summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2024-01-18 16:46:00 -0800
committerGitHub <noreply@github.com>2024-01-18 16:46:00 -0800
commitc5c1a25ab6d0e509e893d737a679ac47949df2f6 (patch)
treee60d4f96ae5105ef19c6b238a4d98467ff58975d /source
parent1a13842f7ece9f3c492a7017509b75eafa903bbf (diff)
Capability def parsing & codegen + disjoint sets (#3451)
* Capability def parsing & codegen + disjoint sets This change adds a capability definition file, and a code generator to produce C++ code that defines the capability enums and necessary data structures around the capabilities. Extends the existing CapabilitySet class to support expressing disjoint sets of capabilities. This sets up for the next change that will enhance our type checking with reasoning of capability requirements. * Fix cmake. * Fix warning. * Fix. * Fix isBetterForTarget to prefer less specialized option. * Fix. * Fix premake. * Fix intrinsic. * Fix vs sln file. --------- Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'source')
-rw-r--r--source/compiler-core/slang-perfect-hash-codegen.cpp81
-rw-r--r--source/compiler-core/slang-perfect-hash-codegen.h9
-rw-r--r--source/compiler-core/slang-perfect-hash.cpp2
-rw-r--r--source/slang/CMakeLists.txt52
-rw-r--r--source/slang/hlsl.meta.slang90
-rw-r--r--source/slang/slang-capabilities.capdef135
-rw-r--r--source/slang/slang-capability-defs.h103
-rw-r--r--source/slang/slang-capability.cpp703
-rw-r--r--source/slang/slang-capability.h141
-rw-r--r--source/slang/slang-compiler.cpp26
-rwxr-xr-xsource/slang/slang-compiler.h4
-rw-r--r--source/slang/slang-doc-markdown-writer.cpp23
-rw-r--r--source/slang/slang-emit-c-like.cpp3
-rw-r--r--source/slang/slang-emit-glsl.cpp18
-rw-r--r--source/slang/slang-emit-spirv.cpp2
-rw-r--r--source/slang/slang-ir-dll-import.cpp2
-rw-r--r--source/slang/slang-ir-glsl-legalize.cpp4
-rw-r--r--source/slang/slang-ir-inst-defs.h4
-rw-r--r--source/slang/slang-ir-insts.h4
-rw-r--r--source/slang/slang-ir-specialize-target-switch.cpp4
-rw-r--r--source/slang/slang-ir.cpp58
-rw-r--r--source/slang/slang-lower-to-ir.cpp8
-rw-r--r--source/slang/slang-options.cpp21
-rw-r--r--source/slang/slang-parser.cpp4
-rw-r--r--source/slang/slang.cpp50
25 files changed, 1070 insertions, 481 deletions
diff --git a/source/compiler-core/slang-perfect-hash-codegen.cpp b/source/compiler-core/slang-perfect-hash-codegen.cpp
new file mode 100644
index 000000000..611b34c54
--- /dev/null
+++ b/source/compiler-core/slang-perfect-hash-codegen.cpp
@@ -0,0 +1,81 @@
+#include "slang-perfect-hash-codegen.h"
+#include "../core/slang-io.h"
+
+namespace Slang
+{
+ SlangResult writeHashFile(
+ String outCppPath,
+ String valueType,
+ const List<String> includes,
+ const HashParams& hashParams,
+ const List<String> values)
+ {
+ StringBuilder sb;
+ StringWriter writer(&sb, WriterFlags(0));
+ WriterHelper w(&writer);
+
+ w.print("// Hash function for %s\n", valueType.getBuffer());
+ w.print("//\n");
+ w.print("// This file was thoughtfully generated by a machine,\n");
+ w.print("// don't even think about modifying it yourself!\n");
+ w.print("//\n");
+ w.print("\n");
+ for (const auto& i : includes)
+ {
+ if (i.getLength())
+ w.print("#include \"%s\"\n", i.getBuffer());
+ }
+ w.print("\n");
+ w.print("\n");
+ w.print("namespace Slang\n");
+ w.print("{\n");
+ w.print("\n");
+
+ w.put(perfectHashToEmbeddableCpp(
+ hashParams,
+ valueType.getUnownedSlice(),
+ (String("lookup") + valueType).getUnownedSlice(),
+ values
+ ).getBuffer());
+
+ w.print("}\n");
+
+ return File::writeAllTextIfChanged(outCppPath, sb.getUnownedSlice());
+ }
+
+ SlangResult writePerfectHashLookupCppFile(String fileName, List<String> opnames, String enumName, String enumerantPrefix, String enumHeaderFile, DiagnosticSink* sink)
+ {
+ HashParams hashParams;
+ auto r = minimalPerfectHash(opnames, hashParams);
+ switch (r)
+ {
+ case HashFindResult::UnavoidableHashCollision:
+ {
+ sink->diagnoseRaw(
+ Severity::Error,
+ "Unable to find a non-overlapping hash function.\n"
+ "The hash function probably has a unavoidable "
+ "collision for some input words\n");
+ return SLANG_FAIL;
+ }
+ case HashFindResult::NonUniqueKeys:
+ {
+ sink->diagnoseRaw(Severity::Error, "Input word list has duplicates\n");
+ return SLANG_FAIL;
+ }
+ case HashFindResult::Success:;
+ }
+
+ List<String> values;
+ values.reserve(hashParams.destTable.getCount());
+ for (const auto& v : hashParams.destTable)
+ values.add(enumerantPrefix + v);
+ return writeHashFile(
+ fileName,
+ enumName,
+ { "../core/slang-common.h", "../core/slang-string.h", enumHeaderFile },
+ hashParams,
+ values);
+ }
+
+}
diff --git a/source/compiler-core/slang-perfect-hash-codegen.h b/source/compiler-core/slang-perfect-hash-codegen.h
new file mode 100644
index 000000000..557f4dba5
--- /dev/null
+++ b/source/compiler-core/slang-perfect-hash-codegen.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "slang-perfect-hash.h"
+#include "slang-diagnostic-sink.h"
+
+namespace Slang
+{
+ SlangResult writePerfectHashLookupCppFile(String fileName, List<String> opnames, String enumName, String enumPrefix, String enumHeaderFile, DiagnosticSink* sink);
+}
diff --git a/source/compiler-core/slang-perfect-hash.cpp b/source/compiler-core/slang-perfect-hash.cpp
index bfe8d4876..d5c4902ed 100644
--- a/source/compiler-core/slang-perfect-hash.cpp
+++ b/source/compiler-core/slang-perfect-hash.cpp
@@ -208,7 +208,7 @@ String perfectHashToEmbeddableCpp(
line("}");
line("");
- return sb;
+ return sb.produceString();
}
}
diff --git a/source/slang/CMakeLists.txt b/source/slang/CMakeLists.txt
index c3ea18433..44785559f 100644
--- a/source/slang/CMakeLists.txt
+++ b/source/slang/CMakeLists.txt
@@ -47,6 +47,54 @@ target_include_directories(
)
#
+# generate capability files
+#
+glob_append(SLANG_CAPABILITY_SOURCE "*.capdef")
+
+set(SLANG_CAPABILITY_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/capability")
+
+# Generate the output file list
+set(SLANG_CAPABILITY_GENERATED_FILES
+ "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-generated-capability-defs.h"
+ "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-generated-capability-defs-impl.h"
+ "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-lookup-capability-defs.cpp"
+)
+add_custom_command(
+ OUTPUT ${SLANG_CAPABILITY_GENERATED_FILES}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${SLANG_CAPABILITY_OUTPUT_DIR}
+ COMMAND
+ slang-capability-generator ${SLANG_CAPABILITY_SOURCE} --target-directory ${SLANG_CAPABILITY_OUTPUT_DIR}
+ DEPENDS ${SLANG_CAPABILITY_SOURCE}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+ VERBATIM
+)
+add_library(
+ slang-capability-defs
+ INTERFACE
+ EXCLUDE_FROM_ALL
+ ${SLANG_CAPABILITY_GENERATED_FILES}
+)
+set_target_properties(slang-capability-defs PROPERTIES FOLDER generated)
+target_include_directories(
+ slang-capability-defs
+ INTERFACE ${SLANG_CAPABILITY_OUTPUT_DIR}
+ INTERFACE ${slang_SOURCE_DIR}/source/slang
+)
+
+add_library(
+ slang-capability-lookup
+ INTERFACE
+ EXCLUDE_FROM_ALL
+ ${SLANG_CAPABILITY_GENERATED_FILES}
+)
+set_target_properties(slang-capability-lookup PROPERTIES FOLDER generated)
+target_sources(
+ slang-capability-lookup
+ INTERFACE
+ "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-lookup-capability-defs.cpp"
+)
+
+#
# generated headers for reflection
#
@@ -118,6 +166,8 @@ slang_add_target(
core
compiler-core
# Bundle the source unconditionally
+ slang-capability-defs
+ slang-capability-lookup
slang-meta-headers
slang-reflect-headers
SPIRV-Headers
@@ -164,6 +214,8 @@ slang_add_target(
core
compiler-core
prelude
+ slang-capability-defs
+ slang-capability-lookup
slang-reflect-headers
SPIRV-Headers
# slang.h is in the project root, so include that directory in the interface
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 2f4064c97..3635069e7 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -4566,8 +4566,8 @@ T GetAttributeAtVertex(T attribute, uint vertexIndex)
{
case hlsl:
__intrinsic_asm "GetAttributeAtVertex";
- case GL_NV_fragment_shader_barycentric:
- case GL_EXT_fragment_shader_barycentric:
+ case _GL_NV_fragment_shader_barycentric:
+ case _GL_EXT_fragment_shader_barycentric:
__intrinsic_asm "$0[$1]";
case spirv:
return spirv_asm {
@@ -4599,8 +4599,8 @@ vector<T,N> GetAttributeAtVertex(vector<T,N> attribute, uint vertexIndex)
{
case hlsl:
__intrinsic_asm "GetAttributeAtVertex";
- case GL_NV_fragment_shader_barycentric:
- case GL_EXT_fragment_shader_barycentric:
+ case _GL_NV_fragment_shader_barycentric:
+ case _GL_EXT_fragment_shader_barycentric:
__intrinsic_asm "$0[$1]";
case spirv:
return spirv_asm {
@@ -4632,8 +4632,8 @@ matrix<T,N,M> GetAttributeAtVertex(matrix<T,N,M> attribute, uint vertexIndex)
{
case hlsl:
__intrinsic_asm "GetAttributeAtVertex";
- case GL_NV_fragment_shader_barycentric:
- case GL_EXT_fragment_shader_barycentric:
+ case _GL_NV_fragment_shader_barycentric:
+ case _GL_EXT_fragment_shader_barycentric:
__intrinsic_asm "$0[$1]";
case spirv:
return spirv_asm {
@@ -9312,8 +9312,8 @@ bool __reportIntersection(float tHit, uint hitKind)
{
__target_switch
{
- case GL_EXT_ray_tracing: __intrinsic_asm "reportIntersectionEXT";
- case GL_NV_ray_tracing: __intrinsic_asm "reportIntersectionNV";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "reportIntersectionEXT";
+ case _GL_NV_ray_tracing: __intrinsic_asm "reportIntersectionNV";
case spirv:
return spirv_asm {
result:$$bool = OpReportIntersectionKHR $tHit $hitKind;
@@ -9339,8 +9339,8 @@ void IgnoreHit()
__target_switch
{
case hlsl: __intrinsic_asm "IgnoreHit";
- case GL_EXT_ray_tracing: __intrinsic_asm "ignoreIntersectionEXT;";
- case GL_NV_ray_tracing: __intrinsic_asm "ignoreIntersectionNV";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "ignoreIntersectionEXT;";
+ case _GL_NV_ray_tracing: __intrinsic_asm "ignoreIntersectionNV";
case cuda: __intrinsic_asm "optixIgnoreIntersection";
case spirv: spirv_asm { OpIgnoreIntersectionKHR; %_ = OpLabel };
}
@@ -9352,8 +9352,8 @@ void AcceptHitAndEndSearch()
__target_switch
{
case hlsl: __intrinsic_asm "AcceptHitAndEndSearch";
- case GL_EXT_ray_tracing: __intrinsic_asm "terminateRayEXT;";
- case GL_NV_ray_tracing: __intrinsic_asm "terminateRayNV";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "terminateRayEXT;";
+ case _GL_NV_ray_tracing: __intrinsic_asm "terminateRayNV";
case cuda: __intrinsic_asm "optixTerminateRay";
case spirv: spirv_asm { OpTerminateRayKHR; %_ = OpLabel };
}
@@ -9371,8 +9371,8 @@ uint3 DispatchRaysIndex()
__target_switch
{
case hlsl: __intrinsic_asm "DispatchRaysIndex";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchIDEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchIDNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchIDEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchIDNV)";
case cuda: __intrinsic_asm "optixGetLaunchIndex";
case spirv:
return spirv_asm {
@@ -9386,8 +9386,8 @@ uint3 DispatchRaysDimensions()
__target_switch
{
case hlsl: __intrinsic_asm "DispatchRaysDimensions";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchSizeEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchSizeNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchSizeEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchSizeNV)";
case cuda: __intrinsic_asm "optixGetLaunchDimensions";
case spirv:
return spirv_asm {
@@ -9403,8 +9403,8 @@ float3 WorldRayOrigin()
__target_switch
{
case hlsl: __intrinsic_asm "WorldRayOrigin";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginNV)";
case cuda: __intrinsic_asm "optixGetWorldRayOrigin";
case spirv:
return spirv_asm {
@@ -9418,8 +9418,8 @@ float3 WorldRayDirection()
__target_switch
{
case hlsl: __intrinsic_asm "WorldRayDirection";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionNV)";
case cuda: __intrinsic_asm "optixGetWorldRayDirection";
case spirv:
return spirv_asm {
@@ -9433,8 +9433,8 @@ float RayTMin()
__target_switch
{
case hlsl: __intrinsic_asm "RayTMin";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTminEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTminNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTminEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTminNV)";
case cuda: __intrinsic_asm "optixGetRayTmin";
case spirv:
return spirv_asm {
@@ -9458,8 +9458,8 @@ float RayTCurrent()
__target_switch
{
case hlsl: __intrinsic_asm "RayTCurrent";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTmaxEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTmaxNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTmaxEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTmaxNV)";
case cuda: __intrinsic_asm "optixGetRayTmax";
case spirv:
return spirv_asm {
@@ -9473,8 +9473,8 @@ uint RayFlags()
__target_switch
{
case hlsl: __intrinsic_asm "RayFlags";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsNV)";
case cuda: __intrinsic_asm "optixGetRayFlags";
case spirv:
return spirv_asm {
@@ -9490,7 +9490,7 @@ uint InstanceIndex()
__target_switch
{
case hlsl: __intrinsic_asm "InstanceIndex";
- case __glslRayTracing: __intrinsic_asm "(gl_InstanceID)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_InstanceID)";
case cuda: __intrinsic_asm "optixGetInstanceIndex";
case spirv:
return spirv_asm {
@@ -9504,8 +9504,8 @@ uint InstanceID()
__target_switch
{
case hlsl: __intrinsic_asm "InstanceID";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexNV)";
case cuda: __intrinsic_asm "optixGetInstanceId";
case spirv:
return spirv_asm {
@@ -9519,7 +9519,7 @@ uint PrimitiveIndex()
__target_switch
{
case hlsl: __intrinsic_asm "PrimitiveIndex";
- case __glslRayTracing: __intrinsic_asm "(gl_PrimitiveID)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_PrimitiveID)";
case cuda: __intrinsic_asm "optixGetPrimitiveIndex";
case spirv:
return spirv_asm {
@@ -9533,8 +9533,8 @@ float3 ObjectRayOrigin()
__target_switch
{
case hlsl: __intrinsic_asm "ObjectRayOrigin";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginNV)";
case cuda: __intrinsic_asm "optixGetObjectRayOrigin";
case spirv:
return spirv_asm {
@@ -9548,8 +9548,8 @@ float3 ObjectRayDirection()
__target_switch
{
case hlsl: __intrinsic_asm "ObjectRayDirection";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionNV)";
case cuda: __intrinsic_asm "optixGetObjectRayDirection";
case spirv:
return spirv_asm {
@@ -9565,8 +9565,8 @@ float3x4 ObjectToWorld3x4()
__target_switch
{
case hlsl: __intrinsic_asm "ObjectToWorld3x4";
- case GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldNV)";
case spirv:
return spirv_asm {
%mat:$$float4x3 = OpLoad builtin(ObjectToWorldKHR:float4x3);
@@ -9580,8 +9580,8 @@ float3x4 WorldToObject3x4()
__target_switch
{
case hlsl: __intrinsic_asm "WorldToObject3x4";
- case GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectNV)";
case spirv:
return spirv_asm {
%mat:$$float4x3 = OpLoad builtin(WorldToObjectKHR:float4x3);
@@ -9595,8 +9595,8 @@ float4x3 ObjectToWorld4x3()
__target_switch
{
case hlsl: __intrinsic_asm "ObjectToWorld4x3";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldNV)";
case spirv:
return spirv_asm {
result:$$float4x3 = OpLoad builtin(ObjectToWorldKHR:float4x3);
@@ -9609,8 +9609,8 @@ float4x3 WorldToObject4x3()
__target_switch
{
case hlsl: __intrinsic_asm "WorldToObject4x3";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldToObjectEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldToObjectNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldToObjectEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldToObjectNV)";
case spirv:
return spirv_asm {
result:$$float4x3 = OpLoad builtin(WorldToObjectKHR:float4x3);
@@ -9656,8 +9656,8 @@ uint HitKind()
__target_switch
{
case hlsl: __intrinsic_asm "HitKind";
- case GL_EXT_ray_tracing: __intrinsic_asm "(gl_HitKindEXT)";
- case GL_NV_ray_tracing: __intrinsic_asm "(gl_HitKindNV)";
+ case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_HitKindEXT)";
+ case _GL_NV_ray_tracing: __intrinsic_asm "(gl_HitKindNV)";
case cuda: __intrinsic_asm "optixGetHitKind";
case spirv:
return spirv_asm {
@@ -9848,7 +9848,7 @@ extension __TextureImpl<T,__Shape2D, 1, 0, 0, $(kStdlibResourceAccessFeedback),
//
// Get the index of the geometry that was hit in an intersection, any-hit, or closest-hit shader
-__target_intrinsic(GL_EXT_ray_tracing, "(gl_GeometryIndexEXT)")
+__glsl_extension(GL_EXT_ray_tracing)
uint GeometryIndex()
{
__target_switch
diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef
new file mode 100644
index 000000000..7e5931edf
--- /dev/null
+++ b/source/slang/slang-capabilities.capdef
@@ -0,0 +1,135 @@
+// This file is the single source of truth for all capabilities
+// supported by the Slang language.
+//
+// This file will be parsed and processed by the slang-capability-generator
+// tool during the build process to produce slang-generated-capability-defs.h
+// and slang-generated-capability-defs-impl.h files that constitute the final
+// C++ source of the compiler. New capabilties should be added by editing
+// this file instead of the generated .h files.
+//
+// A capability atom represent a basic unit that characterizes a single code-gen target or
+// a platform-specific API/feature, e.g. _GL_EXT_ray_tracing represents the GLSL ray tracing
+// extension, and `glsl` represents the GLSL code gen target.
+//
+// A capability name is defined by a unique disjunction of conjunction of capability atoms.
+// For example, `raytracing` is a name that expands to
+// glsl + _GL_EXT_ray_tracing | spirv_1_4 + _GL_EXT_ray_tracing | hlsl + _sm_6_4
+// which means it requires the `_GL_EXT_ray_tracing` extension when generating code for glsl
+// or spirv, and requires sm_6_4 when generating hlsl.
+//
+// There are three types of capability definitions:
+// - `def`: this will introduce an new capability atom. If there is an inheritance clause,
+// the capability name will expand to all inherited atoms plus the newly introduced atom.
+// - `abstract`: an abstract capability does not introduce an actual atom, but it defines
+// an implicit conflict group such that two capabilities inheriting the same abstract
+// capability cannot be satisfied simultaneously. When used in an expression, `abstract`
+// capability also expands into a disjunction (or) of all inherited capabilities.
+// - `alias`: this defines an alias and does not introduce actual atoms.
+
+// Several capabilities represent the target formats in which we generate code.
+// Because we can only generate code in one format at a time, all of these are
+// marked as conflicting with one another along the `TargetFormat` axis.
+//
+// Note: We are only including here the source code formats we initially generate
+// code in and not the formats that code might be translated into "downstream."
+// Trying to figure out how to integrate both kinds of formats into our capability
+// system will be an interesting challenge (e.g., can we compile code for `hlsl+spirv`
+// and for `glsl+spirv` or even just for `spirv`, and how should all of those impact
+// overloading).
+//
+def textualTarget;
+
+abstract target;
+def hlsl : target + textualTarget;
+def glsl : target + textualTarget;
+def c : target + textualTarget;
+def cpp : target + textualTarget;
+def cuda : target + textualTarget;
+
+// We have multiple capabilities for the various SPIR-V versions,
+// arranged so that they inherit from one another to represent which versions
+// provide a super-set of the features of earlier ones (e.g., SPIR-V 1.4 is
+// expressed as inheriting from SPIR-V 1.3).
+//
+def spirv_1_0 : target;
+def spirv_1_1 : spirv_1_0;
+def spirv_1_2 : spirv_1_1;
+def spirv_1_3 : spirv_1_2;
+def spirv_1_4 : spirv_1_3;
+def spirv_1_5 : spirv_1_4;
+def spirv_1_6 : spirv_1_5;
+alias spirv = spirv_1_0;
+alias spirv_latest = spirv_1_6;
+
+// Capabilities that stand for target spirv version for GLSL backend.
+// These are not compilation targets.
+def glsl_spirv_1_0;
+def glsl_spirv_1_1 : glsl_spirv_1_0;
+def glsl_spirv_1_2 : glsl_spirv_1_1;
+def glsl_spirv_1_3 : glsl_spirv_1_2;
+def glsl_spirv_1_4 : glsl_spirv_1_3;
+def glsl_spirv_1_5 : glsl_spirv_1_4;
+def glsl_spirv_1_6 : glsl_spirv_1_5;
+
+abstract stage;
+def vertex : stage;
+def fragment : stage;
+alias pixel = fragment;
+def compute : stage;
+def hull : stage;
+def domain : stage;
+def geometry : stage;
+def raygen : stage;
+def intersection : stage;
+def anyhit : stage;
+def closesthit: stage;
+def miss : stage;
+def mesh : stage;
+
+def _sm_5_1 : hlsl;
+def _sm_6_0 : _sm_5_1;
+def _sm_6_1 : _sm_6_0;
+def _sm_6_2 : _sm_6_1;
+def _sm_6_3 : _sm_6_2;
+def _sm_6_4 : _sm_6_3;
+def _sm_6_5 : _sm_6_4;
+def _sm_6_6 : _sm_6_5;
+def _sm_6_7 : _sm_6_6;
+
+// The following capabilities all pertain to how ray tracing shaders are translated
+// to GLSL, where there are two different extensions that can provide the core
+// functionality of `TraceRay` and the related operations.
+//
+// The two extensions are expressed as distinct capabilities that both are marked
+// as conflicting on the `RayTracingExtension` axis, so that a compilation target
+// cannot have both enabled at once.
+//
+// The `GL_EXT_ray_tracing` extension should be favored, so it has a rank of `1`
+// instead of `0`, which means that when comparing overloads that require these
+// extensions, the `EXT` extension will be favored over the `NV` extension, if
+// all other factors are equal.
+//
+def _GL_EXT_ray_tracing : glsl + glsl_spirv_1_4 = 1;
+def _GL_NV_ray_tracing : _GL_EXT_ray_tracing;
+def _SPV_KHR_ray_tracing : spirv_1_4;
+alias GL_NV_ray_tracing = _GL_NV_ray_tracing | _SPV_KHR_ray_tracing | _sm_6_4 | cuda;
+alias GL_EXT_ray_tracing = _GL_EXT_ray_tracing | _SPV_KHR_ray_tracing | _sm_6_4 | cuda;
+alias raytracing = GL_EXT_ray_tracing;
+
+def _GL_EXT_fragment_shader_barycentric : glsl + fragment;
+def _GL_NV_fragment_shader_barycentric : _GL_EXT_fragment_shader_barycentric;
+def _SPV_KHR_fragment_shader_barycentric : spirv_1_0 + fragment;
+alias GL_NV_fragment_shader_barycentric = _GL_NV_fragment_shader_barycentric | _SPV_KHR_fragment_shader_barycentric | hlsl + fragment;
+alias GL_EXT_fragment_shader_barycentric = _GL_EXT_fragment_shader_barycentric | _SPV_KHR_fragment_shader_barycentric | hlsl + fragment;
+
+// TODO: define what SM means for all supported targets.
+
+alias sm_5_1 = _sm_5_1;
+alias sm_6_0 = _sm_6_0;
+alias sm_6_1 = _sm_6_1;
+alias sm_6_2 = _sm_6_2;
+alias sm_6_3 = _sm_6_3;
+alias sm_6_4 = _sm_6_4 | raytracing;
+alias sm_6_5 = _sm_6_5;
+alias sm_6_6 = _sm_6_6;
+alias sm_6_7 = _sm_6_7;
diff --git a/source/slang/slang-capability-defs.h b/source/slang/slang-capability-defs.h
deleted file mode 100644
index c523d9cda..000000000
--- a/source/slang/slang-capability-defs.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// slang-capability-defs.h
-
-// This file uses macros to define the capability "atoms" that
-// are used by the `CapabilitySet` implementation.
-//
-// Any file that `#include`s this file is required to set
-// the `SLANG_CAPABILITY_ATOM` macro before including it.
-//
-#ifndef SLANG_CAPABILITY_ATOM
-#error Must define SLANG_CAPABILITY_ATOM before including.
-#endif
-//
-// It is not necessary to `#undef` the macro in the client
-// file, because this file will `#undef` it at the end.
-
-// Our representation allows each capability atom to define
-// a number of other base atoms that it "inherits" from.
-//
-// Different atoms will need different numbers of bases,
-// so we will define a few different macros that wrap
-// `SLANG_CAPABILITY_ATOM` and let us handle the cases
-// more conveniently.
-//
-// TODO: There is probably a way to handle this with
-// variadic macros.
-//
-#define SLANG_CAPABILITY_ATOM4(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2, BASE3) \
- SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2, BASE3)
-
-#define SLANG_CAPABILITY_ATOM3(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2) \
- SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2, Invalid)
-
-#define SLANG_CAPABILITY_ATOM2(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1) \
- SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, Invalid, Invalid)
-
-#define SLANG_CAPABILITY_ATOM1(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0) \
- SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, Invalid, Invalid, Invalid)
-
-#define SLANG_CAPABILITY_ATOM0(ENUMERATOR, NAME, KIND, CONFLICTS, RANK) \
- SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, Invalid, Invalid, Invalid, Invalid)
-
-// Several capabilities represent the target formats in which we generate code.
-// Because we can only generate code in one format at a time, all of these are
-// marked as conflicting with one another along the `TargetFormat` axis.
-//
-// Note: We are only including here the source code formats we initially generate
-// code in and not the formats that code might be translated into "downstream."
-// Trying to figure out how to integrate both kinds of formats into our capability
-// system will be an interesting challenge (e.g., can we compile code for `hlsl+spirv`
-// and for `glsl+spirv` or even just for `spirv`, and how should all of those impact
-// overloading).
-//
-SLANG_CAPABILITY_ATOM0(TEXTUAL_SOURCE, textual_source, Abstract,None,0)
-SLANG_CAPABILITY_ATOM1(HLSL, hlsl, Concrete,TargetFormat,0,TEXTUAL_SOURCE)
-SLANG_CAPABILITY_ATOM1(GLSL, glsl, Concrete,TargetFormat,0,TEXTUAL_SOURCE)
-SLANG_CAPABILITY_ATOM1(C, c, Concrete,TargetFormat,0,TEXTUAL_SOURCE)
-SLANG_CAPABILITY_ATOM1(CPP, cpp, Concrete,TargetFormat,0,TEXTUAL_SOURCE)
-SLANG_CAPABILITY_ATOM1(CUDA, cuda, Concrete,TargetFormat,0,TEXTUAL_SOURCE)
-
-SLANG_CAPABILITY_ATOM0(SPIRV_DIRECT, spirv, Concrete, TargetFormat, 0)
-
-// We have multiple capabilities for the various SPIR-V versions,
-// arranged so that they inherit from one another to represent which versions
-// provide a super-set of the features of earlier ones (e.g., SPIR-V 1.4 is
-// expressed as inheriting from SPIR-V 1.3).
-//
-SLANG_CAPABILITY_ATOM1(SPIRV, __spirv, Abstract,None,0, GLSL)
-SLANG_CAPABILITY_ATOM1(SPIRV_1_0, spirv_1_0, Concrete,None,0, SPIRV)
-SLANG_CAPABILITY_ATOM1(SPIRV_1_1, spirv_1_1, Concrete,None,0, SPIRV_1_0)
-SLANG_CAPABILITY_ATOM1(SPIRV_1_2, spirv_1_2, Concrete,None,0, SPIRV_1_1)
-SLANG_CAPABILITY_ATOM1(SPIRV_1_3, spirv_1_3, Concrete,None,0, SPIRV_1_2)
-SLANG_CAPABILITY_ATOM1(SPIRV_1_4, spirv_1_4, Concrete,None,0, SPIRV_1_3)
-SLANG_CAPABILITY_ATOM1(SPIRV_1_5, spirv_1_5, Concrete,None,0, SPIRV_1_4)
-
-// The following capabilities all pertain to how ray tracing shaders are translated
-// to GLSL, where there are two different extensions that can provide the core
-// functionality of `TraceRay` and the related operations.
-//
-// The two extensions are expressed as distinct capabilities that both are marked
-// as conflicting on the `RayTracingExtension` axis, so that a compilation target
-// cannot have both enabled at once.
-//
-// The `GL_EXT_ray_tracing` extension should be favored, so it has a rank of `1`
-// instead of `0`, which means that when comparing overloads that require these
-// extensions, the `EXT` extension will be favored over the `NV` extension, if
-// all other factors are equal.
-//
-SLANG_CAPABILITY_ATOM1(GLSLRayTracing, __glslRayTracing, Abstract,None,0, GLSL)
-SLANG_CAPABILITY_ATOM1(GL_NV_ray_tracing, GL_NV_ray_tracing, Concrete,RayTracingExtension,0, GLSLRayTracing)
-SLANG_CAPABILITY_ATOM2(GL_EXT_ray_tracing, GL_EXT_ray_tracing, Concrete,RayTracingExtension,1, GLSLRayTracing, SPIRV_1_4)
-
-SLANG_CAPABILITY_ATOM1(GLSLFragmentShaderBarycentric, __glslFragmentShaderBarycentric, Abstract, None, 0, GLSL)
-SLANG_CAPABILITY_ATOM1(GL_NV_fragment_shader_barycentric, GL_NV_fragment_shader_barycentric, Concrete, FragmentShaderBarycentricExtension, 0, GLSLFragmentShaderBarycentric)
-SLANG_CAPABILITY_ATOM1(GL_EXT_fragment_shader_barycentric, GL_EXT_fragment_shader_barycentric, Concrete, FragmentShaderBarycentricExtension, 1, GLSLFragmentShaderBarycentric)
-
-
-#undef SLANG_CAPABILITY_ATOM0
-#undef SLANG_CAPABILITY_ATOM1
-#undef SLANG_CAPABILITY_ATOM2
-#undef SLANG_CAPABILITY_ATOM3
-#undef SLANG_CAPABILITY_ATOM4
-
-#undef SLANG_CAPABILITY_ATOM
diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp
index 28dea8d23..a3f8157e7 100644
--- a/source/slang/slang-capability.cpp
+++ b/source/slang/slang-capability.cpp
@@ -14,7 +14,7 @@ namespace Slang
// We are going to divide capability atoms into a few categories.
//
-enum class CapabilityAtomFlavor : int32_t
+enum class CapabilityNameFlavor : int32_t
{
// A concrete capability atom is something that a target
// can directly support, where the presence of the feature
@@ -42,102 +42,68 @@ enum class CapabilityAtomFlavor : int32_t
Alias,
};
-// Certain capability atoms will conflict with one another,
-// such that a concrete target should never be able to support
-// both.
-//
-// It is possible in theory to define "conflicting" capabilities
-// in terms of the inheritance graph, but that makes checking
-// for conflicts more difficult.
-//
-// Instead, we are going to allow each capability to define a
-// mask to indicate group(s) of conflicting capabilities it
-// belongs to. Two different capability atoms that have
-// overlapping masks will be considered to conflict.
-//
-enum class CapabilityAtomConflictMask : uint32_t
-{
- // By default, most capability atoms do not conflict with one another.
- None = 0,
-
- // Capability atoms that reprsent target code generation formats always conflict.
- // (e.g., you cannot generate both HLSL and C++ output at once)
- TargetFormat = 1 << 0,
-
- // Capability atoms that represent GLSL ray tracing extensions conflict with
- // one another (we only want to use one such extension at a time).
- RayTracingExtension = 1 << 1,
-
- // Capability atoms that represent GLSL fragment shader barycentric extensions conflict with
- // one another (we only want to use one such extension at a time).
- FragmentShaderBarycentricExtension = 1 << 2,
-};
-
-// For simplicity in building up our data structure representing
-// all capability atoms, we will limit the number of bases that
-// a capability atom is allowed to inherit from.
-//
-static const int kCapabilityAtom_MaxBases = 4;
-
// The macros in the `slang-capability-defs.h` file will be used
// to fill out a `static const` array of information about each
// capability atom.
//
struct CapabilityAtomInfo
{
- /// The API-/language-exposed name of the capability.
- char const* name;
+ /// The API-/language-exposed name of the capability.
+ char const* name;
- /// Flavor of atom: concrete, abstract, or alias
- CapabilityAtomFlavor flavor;
+ /// Flavor of atom: concrete, abstract, or alias
+ CapabilityNameFlavor flavor;
- /// A mask to indicate which other categories of atoms this one conflicts with
- CapabilityAtomConflictMask conflictMask;
+ /// If the atom is a direct descendent of an abstract base, keep that for reference here.
+ CapabilityName abstractBase;
- /// Ranking to use when deciding if this atom is a "better" one to select.
+ /// Ranking to use when deciding if this atom is a "better" one to select.
uint32_t rank;
- /// Base atoms this one "inherits" from (terminated with `Invalid` if not all entries used).
- CapabilityAtom bases[kCapabilityAtom_MaxBases];
+ /// Canonical representation in the form of disjunction-of-conjunction of atoms.
+ ArrayView<ArrayView<CapabilityName>> canonicalRepresentation;
};
-//
-static const CapabilityAtomInfo kCapabilityAtoms[Int(CapabilityAtom::Count)] =
-{
- { "invalid", CapabilityAtomFlavor::Concrete, CapabilityAtomConflictMask::None, 0, { CapabilityAtom::Invalid, CapabilityAtom::Invalid, CapabilityAtom::Invalid, CapabilityAtom::Invalid } },
-#define SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, FLAVOR, CONFLICT, RANK, BASE0, BASE1, BASE2, BASE3) \
- { #NAME, CapabilityAtomFlavor::FLAVOR, CapabilityAtomConflictMask::CONFLICT, RANK, { CapabilityAtom::BASE0, CapabilityAtom::BASE1, CapabilityAtom::BASE2, CapabilityAtom::BASE3 } },
-#include "slang-capability-defs.h"
-};
+#include "slang-generated-capability-defs-impl.h"
- /// Get the extended information structure for the given capability `atom`
+static CapabilityAtom asAtom(CapabilityName name)
+{
+ SLANG_ASSERT((CapabilityAtom)name < CapabilityAtom::Count);
+ return (CapabilityAtom)name;
+}
+
+/// Get the extended information structure for the given capability `atom`
+static CapabilityAtomInfo const& _getInfo(CapabilityName atom)
+{
+ SLANG_ASSERT(Int(atom) < Int(CapabilityName::Count));
+ return kCapabilityNameInfos[Int(atom)];
+}
static CapabilityAtomInfo const& _getInfo(CapabilityAtom atom)
{
SLANG_ASSERT(Int(atom) < Int(CapabilityAtom::Count));
- return kCapabilityAtoms[Int(atom)];
+ return kCapabilityNameInfos[Int(atom)];
}
-void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames)
+void getCapabilityNames(List<UnownedStringSlice>& ioNames)
{
- ioNames.setCount(Count(CapabilityAtom::Count));
- for (Index i = 0; i < Count(CapabilityAtom::Count); ++i)
+ ioNames.reserve(Count(CapabilityName::Count));
+ for (Index i = 0; i < Count(CapabilityName::Count); ++i)
{
- ioNames[i] = UnownedStringSlice(_getInfo(CapabilityAtom(i)).name);
+ if (_getInfo(CapabilityName(i)).flavor != CapabilityNameFlavor::Abstract)
+ {
+ ioNames.add(UnownedStringSlice(_getInfo(CapabilityName(i)).name));
+ }
}
}
-CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name)
+bool lookupCapabilityName(const UnownedStringSlice& str, CapabilityName& value);
+
+CapabilityName findCapabilityName(UnownedStringSlice const& name)
{
- // For now we are implementing a linear search over the
- // array of capability atoms to perform name lookup.
- //
- for( Index i = 0; i < Index(CapabilityAtom::Count); ++i )
- {
- auto& capInfo = _getInfo(CapabilityAtom(i));
- if(name == UnownedTerminatedStringSlice(capInfo.name))
- return CapabilityAtom(i);
- }
- return CapabilityAtom::Invalid;
+ CapabilityName result;
+ if (!lookupCapabilityName(name, result))
+ return CapabilityName::Invalid;
+ return result;
}
bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base)
@@ -147,29 +113,22 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base)
return true;
}
- const auto& info = kCapabilityAtoms[Index(atom)];
-
- for (auto cur : info.bases)
+ const auto& info = kCapabilityNameInfos[Index(atom)];
+ for (auto cur : info.canonicalRepresentation)
{
- if (cur == CapabilityAtom::Invalid)
- {
- return false;
- }
-
- if (isCapabilityDerivedFrom(cur, base))
- {
- return true;
- }
+ for (auto cbase : cur)
+ if (asAtom(cbase) == base)
+ return true;
}
return false;
}
//
-// CapabilitySet
+// CapabilityConjunctionSet
//
-// The current design choice in `CapabilitySet` is that it stores
+// The current design choice in `CapabilityConjunctionSet` is that it stores
// an expanded, deduplicated, and sorted list of the capability
// atoms in the set. "Expanded" here means that it includes the
// transitive closure of the inheritance graph of those atoms.
@@ -179,90 +138,64 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base)
// binary searches to efficiently detect whether an atom
// is present in a set.
-CapabilitySet::CapabilitySet()
+CapabilityConjunctionSet::CapabilityConjunctionSet()
{}
-CapabilitySet::CapabilitySet(Int atomCount, CapabilityAtom const* atoms)
+CapabilityConjunctionSet::CapabilityConjunctionSet(Int atomCount, CapabilityAtom const* atoms)
{
_init(atomCount, atoms);
}
-CapabilitySet::CapabilitySet(CapabilityAtom atom)
+CapabilityConjunctionSet::CapabilityConjunctionSet(CapabilityAtom atom)
{
_init(1, &atom);
}
-CapabilitySet::CapabilitySet(List<CapabilityAtom> const& atoms)
+CapabilityConjunctionSet::CapabilityConjunctionSet(List<CapabilityAtom> const& atoms)
{
_init(atoms.getCount(), atoms.getBuffer());
}
-CapabilitySet CapabilitySet::makeEmpty()
+CapabilityConjunctionSet CapabilityConjunctionSet::makeEmpty()
{
- return CapabilitySet();
+ return CapabilityConjunctionSet();
}
-CapabilitySet CapabilitySet::makeInvalid()
+CapabilityConjunctionSet CapabilityConjunctionSet::makeInvalid()
{
// An invalid capability set will always be a singleton
// set of the `Invalid` atom, and we will construct
// the set directly rather than use the more expensive
// logic in `_init()`.
//
- CapabilitySet result;
+ CapabilityConjunctionSet result;
result.m_expandedAtoms.add(CapabilityAtom::Invalid);
return result;
}
- /// Helper routine for `CapabilitySet::_init`.
- ///
- /// Recursively add all atoms implied by `atom` to `ioExpandedAtoms`.
- ///
-static void _addAtomsRec(
- CapabilityAtom atom,
- HashSet<CapabilityAtom>& ioExpandedAtoms)
+void CapabilityConjunctionSet::_init(Int atomCount, CapabilityAtom const* atoms)
{
- auto& atomInfo = _getInfo(atom);
-
- // The first step is to add `atom` itself, *unless*
- // it is an alias, because an alias shouldn't impact
- // whether one set is considered a subset/superset of
- // another.
- //
- if(atomInfo.flavor != CapabilityAtomFlavor::Alias)
- {
- ioExpandedAtoms.add(atom);
- }
-
- // Next we add all the atoms transitively implied by `atom`.
- //
- for(auto baseAtom : atomInfo.bases)
- {
- // Note: the list of `bases` is a fixed-size array, but
- // can be terminated with `Invalid` to indicate that
- // not all of the entries are being used.
- //
- // If we see the sentinel, then we know we are at the end
- // of the list.
- //
- if(baseAtom == CapabilityAtom::Invalid)
- break;
-
- _addAtomsRec(baseAtom, ioExpandedAtoms);
- }
-}
-
-void CapabilitySet::_init(Int atomCount, CapabilityAtom const* atoms)
-{
- // In order to fill in the expanded and deduplicated
- // set of atoms, we will use an explicit hash set
- // and then recursively walk the tree of atoms and
- // their bases.
+ // We will use an explicit hash set to deduplicate input atoms.
//
HashSet<CapabilityAtom> expandedAtomsSet;
for(Int i = 0; i < atomCount; ++i)
{
- _addAtomsRec(atoms[i], expandedAtomsSet);
+ if (expandedAtomsSet.add(atoms[i]))
+ {
+ auto& info = _getInfo(atoms[i]);
+
+ // Add the base items that this atom implies.
+ if (info.canonicalRepresentation.getCount() == 1)
+ {
+ // The atom must have only one conjunction.
+ SLANG_ASSERT(info.canonicalRepresentation.getCount() == 1);
+
+ for (auto base : info.canonicalRepresentation[0])
+ {
+ expandedAtomsSet.add(asAtom(base));
+ }
+ }
+ }
}
// We can then translate the set of atoms into a list,
@@ -276,7 +209,7 @@ void CapabilitySet::_init(Int atomCount, CapabilityAtom const* atoms)
m_expandedAtoms.sort();
}
-void CapabilitySet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const
+void CapabilityConjunctionSet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const
{
// A "compacted" list of atoms is one that starts with
// the "expanded" list and removes any atoms that are
@@ -294,13 +227,18 @@ void CapabilitySet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const
for( auto atom : m_expandedAtoms )
{
auto& atomInfo = _getInfo(atom);
- for(auto baseAtom : atomInfo.bases)
+ if (atomInfo.canonicalRepresentation.getCount() != 1)
{
- // Note: dealing with possible early termination of the `bases` list.
- if(baseAtom == CapabilityAtom::Invalid)
- break;
+ // If the atom is not a single conjunction, skip.
+ continue;
+ }
+ for(auto baseAtom : atomInfo.canonicalRepresentation[0])
+ {
+ // Note: don't add atom itself into redundant set.
+ if(asAtom(baseAtom) == atom)
+ continue;
- redundantAtomsSet.add(baseAtom);
+ redundantAtomsSet.add(asAtom(baseAtom));
}
}
@@ -318,7 +256,7 @@ void CapabilitySet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const
}
}
-bool CapabilitySet::isEmpty() const
+bool CapabilityConjunctionSet::isEmpty() const
{
// Checking if a capability set is empty is trivial in any representation;
// all we need to know is if it has zero atoms in its definition.
@@ -326,7 +264,7 @@ bool CapabilitySet::isEmpty() const
return m_expandedAtoms.getCount() == 0;
}
-bool CapabilitySet::isInvalid() const
+bool CapabilityConjunctionSet::isInvalid() const
{
// We will assume here that there is only one canonical representation of
// an invalid capability set, which is a singleton set of the `Invalid`
@@ -342,32 +280,47 @@ bool CapabilitySet::isInvalid() const
return m_expandedAtoms[0] == CapabilityAtom::Invalid;
}
-bool CapabilitySet::isIncompatibleWith(CapabilityAtom that) const
+bool CapabilityConjunctionSet::isIncompatibleWith(CapabilityAtom that) const
{
// Checking for incompatibility is complicated, and it is best
// to only implement it for full (expanded) sets.
//
- return isIncompatibleWith(CapabilitySet(that));
+ return isIncompatibleWith(CapabilityConjunctionSet(that));
}
-uint32_t CapabilitySet::_calcConflictMask() const
+static UIntSet _calcConflictMask(CapabilityAtom atom)
+{
+ UIntSet mask;
+ auto abstractBase = _getInfo(atom).abstractBase;
+ if (abstractBase != CapabilityName::Invalid)
+ {
+ mask.add((UInt)abstractBase);
+ }
+ return mask;
+}
+
+static UIntSet _calcConflictMask(const CapabilityConjunctionSet& set)
{
// Given a capbility set, we want to compute the mask representing
// all groups of features for which it holds a potentially-conflicting atom.
//
- uint32_t mask = 0;
- for( auto atom : m_expandedAtoms )
+ UIntSet mask;
+ for (auto atom : set.getExpandedAtoms())
{
- mask |= uint32_t(_getInfo(atom).conflictMask);
+ auto abstractBase = _getInfo(atom).abstractBase;
+ if (abstractBase != CapabilityName::Invalid)
+ {
+ mask.add((UInt)abstractBase);
+ }
}
return mask;
}
-bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const
+bool CapabilityConjunctionSet::isIncompatibleWith(CapabilityConjunctionSet const& that) const
{
// The `this` and `that` sets are incompatible if there exists
// an atom A in `this` and an atom `B` in `that` such that
- // A and B are not equal, but the two have overlapping "conflict mask."
+ // A and B are not equal, but the two have overlapping "conflict group."
//
// Equivalently, we can say that the two are in conflict if
//
@@ -382,8 +335,8 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const
// We start by identifying the OR of the conflict masks for
// all features in `this` and `that`.
//
- uint32_t thisMask = this->_calcConflictMask();
- uint32_t thatMask = that._calcConflictMask();
+ UIntSet thisMask = _calcConflictMask(*this);
+ UIntSet thatMask = _calcConflictMask(that);
// Note: there is a possible early-exit opportunity here if
// `thisMask` and `thatMask` have no overlap: there could
@@ -422,8 +375,8 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const
// include something with an overlapping mask
// (we don't know what at this point in the code).
//
- auto thisAtomMask = uint32_t(_getInfo(thisAtom).conflictMask);
- if(thisAtomMask & thatMask)
+ auto thisConflictMask = Slang::_calcConflictMask(thisAtom);
+ if(UIntSet::hasIntersection(thisConflictMask, thatMask))
return true;
thisIndex++;
}
@@ -435,8 +388,8 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const
//
// The logic here is the mirror image of the case above.
//
- auto thatAtomMask = uint32_t(_getInfo(thatAtom).conflictMask);
- if(thatAtomMask & thisMask)
+ auto thatConflictMask = Slang::_calcConflictMask(thatAtom);
+ if(UIntSet::hasIntersection(thatConflictMask, thisMask))
return true;
thatIndex++;
}
@@ -445,7 +398,7 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const
return false;
}
-bool CapabilitySet::implies(CapabilitySet const& that) const
+bool CapabilityConjunctionSet::implies(CapabilityConjunctionSet const& that) const
{
// One capability set implies another if it is a super-set
// of the other one. Think of it this way: if your target
@@ -541,37 +494,19 @@ struct CapabilityAtomComparator
}
};
-bool CapabilitySet::implies(CapabilityAtom atom) const
+bool CapabilityConjunctionSet::implies(CapabilityAtom atom) const
{
- // The common case here is when `atom` is not an alias.
+ // Every non-alias atom that `this` implies should
+ // be presented in the `m_expandedAtoms` list.
//
- if( _getInfo(atom).flavor != CapabilityAtomFlavor::Alias )
- {
- // Every non-alias atom that `this` implies should
- // be presented in the `m_expandedAtoms` list.
- //
- // Because the list is sorted, we can find out whether
- // it contains `atom` with a binary search.
- //
- Index result = m_expandedAtoms.binarySearch(atom, CapabilityAtomComparator());
- return result >= 0;
- }
- else
- {
- // In the case where `atom` is an alias, then it won't
- // appear in the expanded list, and we need to check
- // whether `this` set implies everything that `atom`
- // transitively inherits from.
- //
- // The simplest way to do that is to expand `atom`
- // into the full capability set it stands for and
- // check that.
- //
- return implies(CapabilitySet(atom));
- }
+ // Because the list is sorted, we can find out whether
+ // it contains `atom` with a binary search.
+ //
+ Index result = m_expandedAtoms.binarySearch(atom, CapabilityAtomComparator());
+ return result >= 0;
}
-Int CapabilitySet::countIntersectionWith(CapabilitySet const& that) const
+Int CapabilityConjunctionSet::countIntersectionWith(CapabilityConjunctionSet const& that) const
{
// The goal of this subroutine is to count the number of
// elements in the intersection of `this` and `that`,
@@ -626,9 +561,9 @@ Int CapabilitySet::countIntersectionWith(CapabilitySet const& that) const
return intersectionCount;
}
-bool CapabilitySet::isBetterForTarget(
- CapabilitySet const& existingCaps,
- CapabilitySet const& targetCaps)
+bool CapabilityConjunctionSet::isBetterForTarget(
+ CapabilityConjunctionSet const& existingCaps,
+ CapabilityConjunctionSet const& targetCaps) const
{
auto& candidateCaps = *this;
@@ -720,14 +655,12 @@ bool CapabilitySet::isBetterForTarget(
// All preceding factors being equal, we prefer
// a candidate that is strictly more specialized than the other.
//
- // TODO: This logic has the negative effect of always preferring
- // to enable optional features even if they aren't necessary.
- // It would prefer the set {glsl, optionalFeature} over the set
- // {glsl}, even though we might argue that a default implementaton
- // that works without any optional features is "obviously" what
- // the user means if they didn't enable those features.
+ // We want to avoid choosing the candidate that uses
+ // optional features if they aren't necessary.
+ // For example, the set {glsl, optionalFeature} should not be preferred
+ // over the set {glsl}, if optionalFeature isn't requested explictly.
//
- // TODO: The right answer is possibly that we want to partition
+ // The solution here is that we want to partition
// `candidateCaps` and `existingCaps` into two parts: their
// intersection with `targetCaps` and their difference with it.
//
@@ -736,8 +669,28 @@ bool CapabilitySet::isBetterForTarget(
// part we'd actually wnat to favor a definition that is less
// specialized.
//
- if(candidateCaps.implies(existingCaps)) return true;
- if(existingCaps.implies(candidateCaps)) return true;
+ CapabilityConjunctionSet candidateCapsIntersection;
+ CapabilityConjunctionSet candidateCapsDifference;
+ for (auto atom : candidateCaps.m_expandedAtoms)
+ {
+ if (targetCaps.implies(atom))
+ candidateCapsIntersection.m_expandedAtoms.add(atom);
+ else
+ candidateCapsDifference.m_expandedAtoms.add(atom);
+ }
+ CapabilityConjunctionSet existingCapsIntersection;
+ CapabilityConjunctionSet existingCapsDifference;
+ for (auto atom : existingCaps.m_expandedAtoms)
+ {
+ if (targetCaps.implies(atom))
+ existingCapsIntersection.m_expandedAtoms.add(atom);
+ else
+ existingCapsDifference.m_expandedAtoms.add(atom);
+ }
+ auto scoreCandidate = candidateCapsIntersection.m_expandedAtoms.getCount() - candidateCapsDifference.m_expandedAtoms.getCount();
+ auto scoreExisting = existingCapsIntersection.m_expandedAtoms.getCount() - existingCapsDifference.m_expandedAtoms.getCount();
+ if (scoreCandidate != scoreExisting)
+ return scoreCandidate > scoreExisting;
// At this point we have the problem that neither candidate
// appears to be "obviously" better for the target, but we
@@ -747,18 +700,15 @@ bool CapabilitySet::isBetterForTarget(
// different from the other, and see if anything in either case
// has a ranking that should make it be preferred.
//
- // TODO: This should probably *not* be considering anything that
- // is implied/supported by the target.
- //
- auto candidateScore = candidateCaps._calcDifferenceScoreWith(existingCaps);
- auto existingScore = existingCaps._calcDifferenceScoreWith(candidateCaps);
+ auto candidateScore = candidateCapsDifference._calcDifferenceScoreWith(existingCapsDifference);
+ auto existingScore = existingCapsDifference._calcDifferenceScoreWith(candidateCapsDifference);
if(candidateScore != existingScore)
return candidateScore > existingScore;
return false;
}
-uint32_t CapabilitySet::_calcDifferenceScoreWith(CapabilitySet const& that) const
+uint32_t CapabilityConjunctionSet::_calcDifferenceScoreWith(CapabilityConjunctionSet const& that) const
{
uint32_t score = 0;
@@ -811,12 +761,345 @@ uint32_t CapabilitySet::_calcDifferenceScoreWith(CapabilitySet const& that) cons
}
-bool CapabilitySet::operator==(CapabilitySet const& other) const
+bool CapabilityConjunctionSet::operator==(CapabilityConjunctionSet const& other) const
+{
+ return m_expandedAtoms == other.m_expandedAtoms;
+}
+
+bool CapabilityConjunctionSet::operator<(CapabilityConjunctionSet const& that) const
+{
+ for (Index i = 0; i < Math::Min(m_expandedAtoms.getCount(), that.m_expandedAtoms.getCount()); i++)
+ {
+ if (m_expandedAtoms[i] < that.m_expandedAtoms[i])
+ return true;
+ else if (m_expandedAtoms[i] > that.m_expandedAtoms[i])
+ return false;
+ }
+ return m_expandedAtoms.getCount() < that.m_expandedAtoms.getCount();
+}
+
+
+CapabilitySet::CapabilitySet()
+{}
+
+CapabilitySet::CapabilitySet(Int atomCount, CapabilityName const* atoms)
+{
+ for (Int i = 0; i < atomCount; i++)
+ addCapability(atoms[i]);
+}
+
+CapabilitySet::CapabilitySet(CapabilityName atom)
+{
+ auto info = _getInfo(atom);
+ for (auto conjunction : info.canonicalRepresentation)
+ {
+ CapabilityConjunctionSet set;
+ for (auto atomName : conjunction)
+ set.getExpandedAtoms().add(asAtom(atomName));
+ m_conjunctions.add(_Move(set));
+ }
+}
+
+CapabilitySet::CapabilitySet(List<CapabilityName> const& atoms)
+{
+ for (auto atom : atoms)
+ addCapability(atom);
+}
+
+CapabilitySet CapabilitySet::makeEmpty()
+{
+ return CapabilitySet();
+}
+
+CapabilitySet CapabilitySet::makeInvalid()
+{
+ // An invalid capability set will always be a singleton
+ // set of the `Invalid` atom, and we will construct
+ // the set directly rather than use the more expensive
+ // logic in `_init()`.
+ //
+ CapabilitySet result;
+ result.m_conjunctions.add(CapabilityConjunctionSet(CapabilityAtom::Invalid));
+ return result;
+}
+
+void CapabilitySet::addCapability(CapabilityName name)
+{
+ join(CapabilitySet(name));
+}
+
+bool CapabilitySet::isEmpty() const
+{
+ return m_conjunctions.getCount() == 0;
+}
+
+bool CapabilitySet::isInvalid() const
+{
+ return m_conjunctions.getCount() == 1 && m_conjunctions[0].isInvalid();
+}
+
+bool CapabilitySet::isIncompatibleWith(CapabilityAtom other) const
+{
+ if (isEmpty())
+ return false;
+
+ // If all conjunctions are incompatible with the atom, then we are incompatible.
+ for (auto& c : m_conjunctions)
+ if (!c.isIncompatibleWith(other))
+ return false;
+ return true;
+}
+
+bool CapabilitySet::isIncompatibleWith(CapabilityName other) const
+{
+ if (isEmpty())
+ return false;
+ auto otherSet = CapabilitySet(other);
+ return isIncompatibleWith(otherSet);
+}
+
+bool CapabilitySet::isIncompatibleWith(CapabilityConjunctionSet const& other) const
{
- // TODO: We should be able to implement this more efficiently
- // by scanning over the two sets in tandem.
+ if (isEmpty())
+ return false;
+
+ // If all conjunctions are incompatible with the atom, then we are incompatible.
+ for (auto& c : m_conjunctions)
+ if (!c.isIncompatibleWith(other))
+ return false;
+ return true;
+}
+
+bool CapabilitySet::isIncompatibleWith(CapabilitySet const& other) const
+{
+ if (isEmpty())
+ return false;
+ if (other.isEmpty())
+ return false;
+
+ // If all conjunctions in other are incompatible with the this set, then we are incompatible.
+ for (auto& oc : other.m_conjunctions)
+ for (auto& c : m_conjunctions)
+ if (!c.isIncompatibleWith(oc))
+ return false;
+ return true;
+}
+
+bool CapabilitySet::implies(CapabilityAtom atom) const
+{
+ if (isEmpty())
+ return false;
+
+ for (auto& c : m_conjunctions)
+ if (c.implies(atom))
+ return true;
+
+ return false;
+}
+
+bool CapabilitySet::implies(const CapabilityConjunctionSet& set) const
+{
+ if (isEmpty())
+ return false;
+
+ for (auto& c : m_conjunctions)
+ if (c.implies(set))
+ return true;
- return this->implies(other) && other.implies(*this);
+ return false;
+}
+
+bool CapabilitySet::implies(CapabilitySet const& other) const
+{
+ // x implies (c | d) only if (x implies c) and (x implies d).
+ if (other.isEmpty())
+ return true;
+ for (auto& c : other.m_conjunctions)
+ if (!implies(c))
+ return false;
+ return true;
+}
+
+bool CapabilitySet::operator==(CapabilitySet const& that) const
+{
+ return m_conjunctions == that.m_conjunctions;
+}
+
+void CapabilitySet::calcCompactedAtoms(List<List<CapabilityAtom>>& outAtoms) const
+{
+ for (auto& c : m_conjunctions)
+ {
+ List<CapabilityAtom> atoms;
+ c.calcCompactedAtoms(atoms);
+ outAtoms.add(atoms);
+ }
+}
+
+void CapabilitySet::join(const CapabilitySet& other)
+{
+ if (isEmpty() || other.isInvalid())
+ {
+ *this = other;
+ return;
+ }
+ if (isInvalid())
+ return;
+ if (other.isEmpty())
+ return;
+
+ List<CapabilityConjunctionSet> resultSet;
+ for (auto& thatConjunction : other.m_conjunctions)
+ {
+ for (auto& thisConjunction : m_conjunctions)
+ {
+ if (thisConjunction.isIncompatibleWith(thatConjunction))
+ continue;
+
+ CapabilityConjunctionSet conjunction;
+ CapabilityConjunctionSet *conjunctionToAdd = nullptr;
+
+ // Add atoms from thatConjunction that are not existant in thisConjunction.
+ for (auto atom : thatConjunction.getExpandedAtoms())
+ {
+ if (thisConjunction.getExpandedAtoms().binarySearch(atom, CapabilityAtomComparator()) == -1)
+ {
+ conjunction.getExpandedAtoms().add(atom);
+ }
+ }
+
+ if (conjunction.getExpandedAtoms().getCount() != 0)
+ {
+ // If we find any capabilities in thatConjunction that is missing from thisConjunction,
+ // create a new ConjunctionSet that contains atoms from both, and add it to the disjunction set.
+ conjunction.getExpandedAtoms().addRange(thisConjunction.getExpandedAtoms());
+ conjunction.getExpandedAtoms().sort();
+ conjunctionToAdd = &conjunction;
+ }
+ else
+ {
+ // Otherwise, thisConjunction implies thatConjunction, so we just add thisConjunction to resultSet.
+ conjunctionToAdd = &thisConjunction;
+ }
+ // We add conjunctionToAdd to resultSet only if it does not imply any existing conjunctions.
+ // For example, if `resultSet` is (a), and conjunctionToAdd is (ab), then we don't want to add the conjunction
+ // to form (a | ab) because that would reduce to (a).
+ bool skipAdd = false;
+ for (auto& c : resultSet)
+ {
+ if (conjunctionToAdd->implies(c))
+ {
+ skipAdd = true;
+ break;
+ }
+ }
+ if (!skipAdd)
+ {
+ // Once we added the new conjunction, any existing conjunctions that implies the new one can be
+ // removed.
+ // For example, if resultSet was (ab), and we are adding (a), the result should be just (a).
+ for (Index i = 0; i < resultSet.getCount();)
+ {
+ if (resultSet[i].implies(*conjunctionToAdd))
+ {
+ resultSet.fastRemoveAt(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+ resultSet.add(*conjunctionToAdd);
+ }
+ }
+ }
+ m_conjunctions = _Move(resultSet);
+
+ // Make sure conjunctions are sorted so equality tests are trivial.
+ m_conjunctions.sort();
+}
+
+bool CapabilitySet::isBetterForTarget(CapabilitySet const& that, CapabilitySet const& targetCaps) const
+{
+ if (targetCaps.isIncompatibleWith(*this))
+ return false;
+ if (targetCaps.isIncompatibleWith(that))
+ return true;
+
+ ArrayView<CapabilityConjunctionSet> thisSets = m_conjunctions.getArrayView();
+ ArrayView<CapabilityConjunctionSet> thatSets = that.m_conjunctions.getArrayView();
+ CapabilityConjunctionSet emtpySet = CapabilityConjunctionSet::makeEmpty();
+
+ if (isEmpty())
+ thisSets = makeArrayViewSingle(emtpySet);
+ if (that.isEmpty())
+ thatSets = makeArrayViewSingle(emtpySet);
+
+ // It is hard to think about what it means exactly to compare a general disjunction set to another with regard
+ // to a target that itself is also a disjunction set.
+ // Instead of trying to find a meaning for the general case, we just want to extend the logic
+ // for conjunction sets to disjunction sets in a way that common situations are handled correctly.
+ // Note that when we reach here, most of these sets are likely to contain only one conjunction, so
+ // we just need to make sure the more general logic here yields correct result for that case.
+ //
+ // Right now, we define betterness for disjunctions as follows:
+ // A capability set X is determined to be better for a target T than capability set Y,
+ // if we find a conjunction A in X and a conjunction B in Y and a conjunction C in T such that
+ // A is better then B for target C.
+ //
+ struct ViableConjunctionIndex
+ {
+ Index index;
+ UIntSet targetConjunctionIndices;
+ };
+ auto getViableConjunction = [&](ArrayView<CapabilityConjunctionSet> set, List<ViableConjunctionIndex>& outList)
+ {
+ for (Index i = 0; i < set.getCount(); i++)
+ {
+ auto& conjunction = set[i];
+ ViableConjunctionIndex viableConjunction;
+ viableConjunction.index = i;
+ for (Index j = 0; j < targetCaps.m_conjunctions.getCount(); j++)
+ {
+ auto& targetConjunction = targetCaps.m_conjunctions[j];
+ if (conjunction.isIncompatibleWith(targetConjunction))
+ continue;
+ viableConjunction.targetConjunctionIndices.add(j);
+ }
+ if (!viableConjunction.targetConjunctionIndices.isEmpty())
+ {
+ outList.add(viableConjunction);
+ }
+ }
+ };
+ List<ViableConjunctionIndex> viableConjunctionsThis;
+ List<ViableConjunctionIndex> viableConjunctionsThat;
+
+ getViableConjunction(thisSets, viableConjunctionsThis);
+ getViableConjunction(thatSets, viableConjunctionsThat);
+
+ for (auto& thisConjunctionIndex : viableConjunctionsThis)
+ {
+ auto& thisConjunction = thisSets[thisConjunctionIndex.index];
+ for (auto& thatConjunctionIndex : viableConjunctionsThat)
+ {
+ auto& thatConjunction = thatSets[thatConjunctionIndex.index];
+ UIntSet intersection = thisConjunctionIndex.targetConjunctionIndices;
+ intersection.intersectWith(thatConjunctionIndex.targetConjunctionIndices);
+ if (!intersection.isEmpty())
+ {
+ for (Index targetConjunctionIndex = 0; targetConjunctionIndex < targetCaps.m_conjunctions.getCount(); targetConjunctionIndex++)
+ {
+ if (!intersection.contains((UInt)targetConjunctionIndex))
+ continue;
+ if (thisConjunction.isBetterForTarget(thatConjunction, targetCaps.m_conjunctions[targetConjunctionIndex]))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
}
}
diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h
index 55c5044ed..e686ff5a9 100644
--- a/source/slang/slang-capability.h
+++ b/source/slang/slang-capability.h
@@ -25,22 +25,7 @@ namespace Slang
// the `CapabilityAtom` enumeration, which is generated from declarations
// in the `slang-capability-defs.h` file.
//
-enum class CapabilityAtom : int32_t
-{
- // The "invalid" capability represents an atomic feature that no
- // platform can/will ever support. If we ever determine that a
- // function needs the invalid capability, it would be reasonable
- // to report that situation as an error.
- //
- Invalid = 0,
-
-#define SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, FLAVOR, CONFLICT, RANK, BASE0, BASE1, BASE2, BASE3) \
- ENUMERATOR,
-
-#include "slang-capability-defs.h"
-
- Count,
-};
+#include "slang-generated-capability-defs.h"
// Once we have a universe of suitable capability atoms, we can define
// the capabilities of a target as simply the set of all atomic capabilities
@@ -62,26 +47,31 @@ enum class CapabilityAtom : int32_t
// In all cases, we represent a set of capabilities with `CapabilitySet`.
/// A set of capabilities, representing features that are either supported or required
-struct CapabilitySet
+struct CapabilityConjunctionSet
{
public:
/// Default-construct an empty capability set
- CapabilitySet();
+ CapabilityConjunctionSet();
+
+ CapabilityConjunctionSet(CapabilityConjunctionSet const& other) = default;
+ CapabilityConjunctionSet& operator=(CapabilityConjunctionSet const& other) = default;
+ CapabilityConjunctionSet(CapabilityConjunctionSet&& other) = default;
+ CapabilityConjunctionSet& operator=(CapabilityConjunctionSet&& other) = default;
/// Construct a capability set from an explicit list of atomic capabilities
- CapabilitySet(Int atomCount, CapabilityAtom const* atoms);
+ CapabilityConjunctionSet(Int atomCount, CapabilityAtom const* atoms);
/// Construct a capability set from an explicit list of atomic capabilities
- explicit CapabilitySet(List<CapabilityAtom> const& atoms);
+ explicit CapabilityConjunctionSet(List<CapabilityAtom> const& atoms);
/// Construct a singleton set from a single atomic capability
- explicit CapabilitySet(CapabilityAtom atom);
+ explicit CapabilityConjunctionSet(CapabilityAtom atom);
/// Make an empty capability set
- static CapabilitySet makeEmpty();
+ static CapabilityConjunctionSet makeEmpty();
/// Make an invalid capability set (such that no target could ever support it)
- static CapabilitySet makeInvalid();
+ static CapabilityConjunctionSet makeInvalid();
/// Is this capability set empty (such that any target supports it)?
bool isEmpty() const;
@@ -105,7 +95,7 @@ public:
bool isIncompatibleWith(CapabilityAtom other) const;
/// Is this capability set incompatible with the given `other` atomic capability.
- bool isIncompatibleWith(CapabilitySet const& other) const;
+ bool isIncompatibleWith(CapabilityConjunctionSet const& other) const;
// One capability set A "implies" another set B if a target that
// supports A must also support all of B.
@@ -116,7 +106,7 @@ public:
// and not get hung up on the set theory.
/// Does this capability set imply all the capabilities in `other`?
- bool implies(CapabilitySet const& other) const;
+ bool implies(CapabilityConjunctionSet const& other) const;
/// Does this capability set imply the atomic capability `other`?
bool implies(CapabilityAtom other) const;
@@ -124,23 +114,24 @@ public:
// A capability set is equal to another if each implies the other.
/// Are these two capability sets equal?
- bool operator==(CapabilitySet const& that) const;
+ bool operator==(CapabilityConjunctionSet const& that) const;
+ bool operator<(CapabilityConjunctionSet const& that) const;
/// Get access to the raw atomic capabilities that define this set.
List<CapabilityAtom> const& getExpandedAtoms() const { return m_expandedAtoms; }
+ List<CapabilityAtom>& getExpandedAtoms() { return m_expandedAtoms; }
/// Calculate a list of "compacted" atoms, which excludes any atoms from the expanded list that are implies by another item in the list.
void calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const;
- Int countIntersectionWith(CapabilitySet const& that) const;
+ Int countIntersectionWith(CapabilityConjunctionSet const& that) const;
- bool isBetterForTarget(CapabilitySet const& that, CapabilitySet const& targetCaps);
+ bool isBetterForTarget(CapabilityConjunctionSet const& that, CapabilityConjunctionSet const& targetCaps) const;
private:
void _init(Int atomCount, CapabilityAtom const* atoms);
- uint32_t _calcConflictMask() const;
- uint32_t _calcDifferenceScoreWith(CapabilitySet const& other) const;
+ uint32_t _calcDifferenceScoreWith(CapabilityConjunctionSet const& other) const;
// The underlying representation we use is a sorted and deduplicated
// list of all the (non-alias) atoms that are present in the set.
@@ -151,6 +142,92 @@ private:
};
/// Are the `left` and `right` capability sets unequal?
+inline bool operator!=(CapabilityConjunctionSet const& left, CapabilityConjunctionSet const& right)
+{
+ return !(left == right);
+}
+
+struct CapabilitySet
+{
+public:
+ /// Default-construct an empty capability set
+ CapabilitySet();
+
+ CapabilitySet(CapabilitySet const& other) = default;
+ CapabilitySet& operator=(CapabilitySet const& other) = default;
+ CapabilitySet(CapabilitySet&& other) = default;
+ CapabilitySet& operator=(CapabilitySet&& other) = default;
+
+ /// Construct a capability set from an explicit list of atomic capabilities
+ CapabilitySet(Int atomCount, CapabilityName const* atoms);
+
+ /// Construct a capability set from an explicit list of atomic capabilities
+ explicit CapabilitySet(List<CapabilityName> const& atoms);
+
+ /// Construct a singleton set from a single atomic capability
+ explicit CapabilitySet(CapabilityName atom);
+
+ /// Construct a singleton set from conjunctions
+ explicit CapabilitySet(const List<CapabilityConjunctionSet>& conjunctions);
+
+ /// Make an empty capability set
+ static CapabilitySet makeEmpty();
+
+ /// Make an invalid capability set (such that no target could ever support it)
+ static CapabilitySet makeInvalid();
+
+ /// Is this capability set empty (such that any target supports it)?
+ bool isEmpty() const;
+
+ /// Is this capability set invalid (such that no target could support it)?
+ bool isInvalid() const;
+
+ /// Is this capability set incompatible with the given `other` set.
+ bool isIncompatibleWith(CapabilityAtom other) const;
+
+ /// Is this capability set incompatible with the given `other` set.
+ bool isIncompatibleWith(CapabilityName other) const;
+
+ /// Is this capability set incompatible with the given `other` atomic capability.
+ bool isIncompatibleWith(CapabilityConjunctionSet const& other) const;
+
+ /// Is this capability set incompatible with the given `other` atomic capability.
+ bool isIncompatibleWith(CapabilitySet const& other) const;
+
+ /// Does this capability set imply all the capabilities in `other`?
+ bool implies(CapabilitySet const& other) const;
+
+ /// Does this capability set imply all the capabilities in `other`?
+ bool implies(CapabilityConjunctionSet const& other) const;
+
+ /// Does this capability set imply the atomic capability `other`?
+ bool implies(CapabilityAtom other) const;
+
+ /// Join two capability sets to form (this & other).
+ void join(const CapabilitySet& other);
+
+ /// Are these two capability sets equal?
+ bool operator==(CapabilitySet const& that) const;
+
+ /// Get access to the raw atomic capabilities that define this set.
+ List<CapabilityConjunctionSet>& getExpandedAtoms() { return m_conjunctions; }
+ const List<CapabilityConjunctionSet>& getExpandedAtoms() const { return m_conjunctions; }
+
+
+ /// Calculate a list of "compacted" atoms, which excludes any atoms from the expanded list that are implies by another item in the list.
+ void calcCompactedAtoms(List<List<CapabilityAtom>>& outAtoms) const;
+
+ bool isBetterForTarget(CapabilitySet const& that, CapabilitySet const& targetCaps) const;
+
+private:
+ // The underlying representation we use is a list of conjunctions.
+ //
+ List<CapabilityConjunctionSet> m_conjunctions;
+
+ void addCapability(CapabilityName name);
+};
+
+/// Are the `left` and `right` capability sets unequal?
inline bool operator!=(CapabilitySet const& left, CapabilitySet const& right)
{
return !(left == right);
@@ -160,9 +237,9 @@ inline bool operator!=(CapabilitySet const& left, CapabilitySet const& right)
bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base);
/// Find a capability atom with the given `name`, or return CapabilityAtom::Invalid.
-CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name);
+CapabilityName findCapabilityName(UnownedStringSlice const& name);
/// Gets the capability names.
-void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames);
+void getCapabilityNames(List<UnownedStringSlice>& ioNames);
}
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index c2bb515f6..491655643 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -584,19 +584,23 @@ namespace Slang
GLSLExtensionTracker* extensionTracker,
CapabilitySet const& caps)
{
- for( auto atom : caps.getExpandedAtoms() )
+ for( auto conjunctions : caps.getExpandedAtoms() )
{
- switch( atom )
+ for (auto atom : conjunctions.getExpandedAtoms())
{
- default:
- break;
-
- case CapabilityAtom::SPIRV_1_0: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 0)); break;
- case CapabilityAtom::SPIRV_1_1: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 1)); break;
- case CapabilityAtom::SPIRV_1_2: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 2)); break;
- case CapabilityAtom::SPIRV_1_3: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 3)); break;
- case CapabilityAtom::SPIRV_1_4: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 4)); break;
- case CapabilityAtom::SPIRV_1_5: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 5)); break;
+ switch (atom)
+ {
+ default:
+ break;
+
+ case CapabilityAtom::glsl_spirv_1_0: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 0)); break;
+ case CapabilityAtom::glsl_spirv_1_1: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 1)); break;
+ case CapabilityAtom::glsl_spirv_1_2: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 2)); break;
+ case CapabilityAtom::glsl_spirv_1_3: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 3)); break;
+ case CapabilityAtom::glsl_spirv_1_4: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 4)); break;
+ case CapabilityAtom::glsl_spirv_1_5: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 5)); break;
+ case CapabilityAtom::glsl_spirv_1_6: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 6)); break;
+ }
}
}
}
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 8a56df3af..a95b5a6bc 100755
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -1620,7 +1620,7 @@ namespace Slang
forceGLSLScalarBufferLayout = value;
}
- void addCapability(CapabilityAtom capability);
+ void addCapability(CapabilityName capability);
bool shouldEmitSPIRVDirectly()
{
@@ -1666,7 +1666,7 @@ namespace Slang
SlangTargetFlags targetFlags = kDefaultTargetFlags;
Slang::Profile targetProfile = Slang::Profile();
FloatingPointMode floatingPointMode = FloatingPointMode::Default;
- List<CapabilityAtom> rawCapabilities;
+ List<CapabilityName> rawCapabilities;
CapabilitySet cookedCapabilities;
LineDirectiveMode lineDirectiveMode = LineDirectiveMode::Default;
bool dumpIntermediates = false;
diff --git a/source/slang/slang-doc-markdown-writer.cpp b/source/slang/slang-doc-markdown-writer.cpp
index de7695d18..77b239437 100644
--- a/source/slang/slang-doc-markdown-writer.cpp
+++ b/source/slang/slang-doc-markdown-writer.cpp
@@ -432,20 +432,21 @@ static DocMarkdownWriter::Requirement _getRequirementFromTargetToken(const Token
return Requirement{CodeGenTarget::SPIRV, UnownedStringSlice("")};
}
- const CapabilityAtom targetCap = findCapabilityAtom(targetName);
+ const CapabilityAtom targetCap = (CapabilityAtom)findCapabilityName(targetName);
if (targetCap == CapabilityAtom::Invalid)
{
return Requirement{ CodeGenTarget::None, String() };
}
+ SLANG_ASSERT(targetCap < CapabilityAtom::Count);
static const CapabilityAtom rootAtoms[] =
{
- CapabilityAtom::GLSL,
- CapabilityAtom::HLSL,
- CapabilityAtom::CUDA,
- CapabilityAtom::CPP,
- CapabilityAtom::C,
+ CapabilityAtom::glsl,
+ CapabilityAtom::hlsl,
+ CapabilityAtom::cuda,
+ CapabilityAtom::cpp,
+ CapabilityAtom::c,
};
for (auto rootAtom : rootAtoms)
@@ -458,23 +459,23 @@ static DocMarkdownWriter::Requirement _getRequirementFromTargetToken(const Token
}
}
- if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::GLSL))
+ if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::glsl))
{
return Requirement{CodeGenTarget::GLSL, targetName};
}
- else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::HLSL))
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::hlsl))
{
return Requirement{ CodeGenTarget::HLSL, targetName };
}
- else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::CUDA))
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::cuda))
{
return Requirement{ CodeGenTarget::CUDASource, targetName };
}
- else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::CPP))
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::cpp))
{
return Requirement{ CodeGenTarget::CPPSource, targetName };
}
- else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::C))
+ else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::c))
{
return Requirement{ CodeGenTarget::CSource, targetName };
}
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index ba5606791..8aa550a03 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -1214,7 +1214,8 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst)
case kIROp_IntLit:
case kIROp_FloatLit:
case kIROp_BoolLit:
- case kIROp_CapabilitySet:
+ case kIROp_CapabilityConjunction:
+ case kIROp_CapabilityDisjunction:
return true;
// Always fold these in, because their results
diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp
index 0fc54ca1f..bff82acf3 100644
--- a/source/slang/slang-emit-glsl.cpp
+++ b/source/slang/slang-emit-glsl.cpp
@@ -70,7 +70,7 @@ void GLSLSourceEmitter::_requireRayTracing()
// the user has explicitly opted in to the `GL_NV_ray_tracing` extension
// we will use that one instead.
//
- if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) )
+ if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) )
{
m_glslExtensionTracker->requireExtension(UnownedStringSlice::fromLiteral("GL_NV_ray_tracing"));
}
@@ -92,7 +92,7 @@ void GLSLSourceEmitter::_requireFragmentShaderBarycentric()
// the user has explicitly opted in to the `GL_NV_fragment_shader_barycentric` extension
// we will use that one instead.
- if( getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric) )
+ if( getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric) )
{
m_glslExtensionTracker->requireExtension(UnownedStringSlice::fromLiteral("GL_NV_fragment_shader_barycentric"));
}
@@ -657,7 +657,7 @@ bool GLSLSourceEmitter::_emitGLSLLayoutQualifierWithBindingKinds(LayoutResourceK
m_writer->emit("layout(push_constant)\n");
break;
case LayoutResourceKind::ShaderRecord:
- if (getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing))
+ if (getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing))
{
m_writer->emit("layout(shaderRecordNV)\n");
}
@@ -1398,7 +1398,7 @@ void GLSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout)
case LayoutResourceKind::RayPayload:
{
- if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) )
+ if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) )
{
m_writer->emit("rayPayloadInNV ");
}
@@ -1411,7 +1411,7 @@ void GLSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout)
case LayoutResourceKind::CallablePayload:
{
- if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) )
+ if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) )
{
m_writer->emit("callableDataInNV ");
}
@@ -1424,7 +1424,7 @@ void GLSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout)
case LayoutResourceKind::HitAttributes:
{
- if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) )
+ if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) )
{
m_writer->emit("hitAttributeNV ");
}
@@ -2421,7 +2421,7 @@ void GLSLSourceEmitter::emitSimpleTypeImpl(IRType* type)
// `GL_NV_ray_tracing` using target capabilities, and will always default
// to `GL_EXT_ray_tracing` otherwise.
//
- if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) )
+ if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) )
{
// If the user has explicitly opted in to `GL_NV_ray_tracing`,
// then we don't need to explicitly request the extentsion again.
@@ -2509,7 +2509,7 @@ bool GLSLSourceEmitter::_maybeEmitInterpolationModifierText(IRInterpolationMode
if( stage == Stage::Fragment && isInput)
{
_requireFragmentShaderBarycentric();
- if (getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric))
+ if (getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric))
{
m_writer->emit("pervertexNV ");
}
@@ -2657,7 +2657,7 @@ void GLSLSourceEmitter::emitVarDecorationsImpl(IRInst* varDecl)
// Special case hitObjectAttribute as is only NV currently
if (decoration->getOp() == kIROp_VulkanHitObjectAttributesDecoration ||
- getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing))
+ getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing))
{
m_writer->emit(toSlice("NV"));
}
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index aa277cfba..07f8a4cd2 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -3161,7 +3161,7 @@ struct SPIRVEmitContext
}
else if (semanticName == "sv_barycentrics")
{
- if (m_targetRequest->getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric))
+ if (m_targetRequest->getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric))
{
requireSPIRVCapability(SpvCapabilityFragmentBarycentricNV);
ensureExtensionDeclaration(UnownedStringSlice("SPV_NV_fragment_shader_barycentric"));
diff --git a/source/slang/slang-ir-dll-import.cpp b/source/slang/slang-ir-dll-import.cpp
index 2da89ad3e..fbb473ea2 100644
--- a/source/slang/slang-ir-dll-import.cpp
+++ b/source/slang/slang-ir-dll-import.cpp
@@ -28,7 +28,7 @@ struct DllImportContext
auto funcType = builder.getFuncType(paramCount, paramTypes, resultType);
builder.setDataType(result, funcType);
builder.addTargetIntrinsicDecoration(
- result, CapabilitySet(CapabilityAtom::CPP), targetIntrinsic);
+ result, CapabilitySet(CapabilityName::cpp), targetIntrinsic);
return result;
}
diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp
index 7bd4f77a3..4d2b12bc4 100644
--- a/source/slang/slang-ir-glsl-legalize.cpp
+++ b/source/slang/slang-ir-glsl-legalize.cpp
@@ -914,7 +914,7 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo(
else if (semanticName == "sv_barycentrics")
{
context->requireGLSLVersion(ProfileVersion::GLSL_450);
- if (codeGenContext->getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric))
+ if (codeGenContext->getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric))
{
context->requireGLSLExtension(UnownedStringSlice::fromLiteral("GL_NV_fragment_shader_barycentric"));
name = "gl_BaryCoordNV";
@@ -2281,7 +2281,7 @@ static void legalizeMeshOutputParam(
auto arrayName = isPerPrimitive ? "gl_MeshPrimitivesEXT" : "gl_MeshVerticesEXT";
builder->addTargetIntrinsicDecoration(
meshOutputBlockType,
- CapabilitySet(CapabilityAtom::GLSL),
+ CapabilitySet(CapabilityName::glsl),
UnownedStringSlice(typeName));
builder->addImportDecoration(blockParam, UnownedStringSlice(arrayName));
if(isPerPrimitive)
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index 99f73b025..453fbf16d 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -282,7 +282,9 @@ INST(Block, block, 0, PARENT)
INST(VoidLit, void_constant, 0, 0)
INST_RANGE(Constant, BoolLit, VoidLit)
-INST(CapabilitySet, capabilitySet, 0, HOISTABLE)
+INST(CapabilityConjunction, capabilityConjunction, 0, HOISTABLE)
+INST(CapabilityDisjunction, capabilityDisjunction, 0, HOISTABLE)
+INST_RANGE(CapabilitySet, CapabilityConjunction, CapabilityDisjunction)
INST(undefined, undefined, 0, 0)
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 4953a5287..2a7852c1e 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -20,7 +20,7 @@ class Decl;
struct IRCapabilitySet : IRInst
{
- IR_LEAF_ISA(CapabilitySet);
+ IR_PARENT_ISA(CapabilitySet);
CapabilitySet getCaps();
};
@@ -4688,7 +4688,7 @@ IRTargetSpecificDecoration* findBestTargetDecoration(
IRTargetSpecificDecoration* findBestTargetDecoration(
IRInst* val,
- CapabilityAtom targetCapabilityAtom);
+ CapabilityName targetCapabilityAtom);
bool findTargetIntrinsicDefinition(IRInst* callee, CapabilitySet const& targetCaps, UnownedStringSlice& outDefinition);
diff --git a/source/slang/slang-ir-specialize-target-switch.cpp b/source/slang/slang-ir-specialize-target-switch.cpp
index fcd5ca10a..f4cb6bfa7 100644
--- a/source/slang/slang-ir-specialize-target-switch.cpp
+++ b/source/slang/slang-ir-specialize-target-switch.cpp
@@ -18,11 +18,11 @@ namespace Slang
IRBlock* targetBlock = nullptr;
for (UInt i = 0; i < targetSwitch->getCaseCount(); i++)
{
- auto cap = (CapabilityAtom)getIntVal(targetSwitch->getCaseValue(i));
+ auto cap = (CapabilityName)getIntVal(targetSwitch->getCaseValue(i));
if (target->getTargetCaps().isIncompatibleWith(cap))
continue;
CapabilitySet capSet;
- if (cap == CapabilityAtom::Invalid)
+ if (cap == CapabilityName::Invalid)
capSet = CapabilitySet::makeEmpty();
else
capSet = CapabilitySet(cap);
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index f9a1276fe..1dbb21a0d 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -287,16 +287,36 @@ namespace Slang
CapabilitySet IRCapabilitySet::getCaps()
{
- List<CapabilityAtom> atoms;
-
- Index count = (Index) getOperandCount();
- for(Index i = 0; i < count; ++i)
+ switch (getOp())
{
- auto operand = cast<IRIntLit>(getOperand(i));
- atoms.add(CapabilityAtom(operand->getValue()));
- }
+ case kIROp_CapabilityConjunction:
+ {
+ List<CapabilityName> atoms;
+
+ Index count = (Index)getOperandCount();
+ for (Index i = 0; i < count; ++i)
+ {
+ auto operand = cast<IRIntLit>(getOperand(i));
+ atoms.add(CapabilityName(operand->getValue()));
+ }
- return CapabilitySet(atoms.getCount(), atoms.getBuffer());
+ return CapabilitySet(atoms.getCount(), atoms.getBuffer());
+ }
+ break;
+ case kIROp_CapabilityDisjunction:
+ {
+ CapabilitySet result;
+ Index count = (Index) getOperandCount();
+ for (Index i = 0; i < count; ++i)
+ {
+ auto operand = cast<IRCapabilitySet>(getOperand(i));
+ result.getExpandedAtoms().addRange(operand->getCaps().getExpandedAtoms());
+ }
+ return result;
+ }
+ break;
+ }
+ return CapabilitySet();
}
@@ -2396,17 +2416,21 @@ namespace Slang
// be a minimal list of atoms such that they will produce
// the same `CapabilitySet` when expanded.
- List<CapabilityAtom> compactedAtoms;
+ List<List<CapabilityAtom>> compactedAtoms;
caps.calcCompactedAtoms(compactedAtoms);
-
- List<IRInst*> args;
- for( auto atom : compactedAtoms )
+ List<IRInst*> conjunctions;
+ for( auto atomConjunction : compactedAtoms )
{
- args.add(getIntValue(capabilityAtomType, Int(atom)));
+ List<IRInst*> args;
+ for (auto atom : atomConjunction)
+ args.add(getIntValue(capabilityAtomType, Int(atom)));
+ auto conjunctionInst = createIntrinsicInst(
+ capabilitySetType, kIROp_CapabilityConjunction, args.getCount(), args.getBuffer());
+ conjunctions.add(conjunctionInst);
}
-
- return createIntrinsicInst(
- capabilitySetType, kIROp_CapabilitySet, args.getCount(), args.getBuffer());
+ if (conjunctions.getCount() == 1)
+ return conjunctions[0];
+ return createIntrinsicInst(capabilitySetType, kIROp_CapabilityDisjunction, conjunctions.getCount(), conjunctions.getBuffer());
}
static void canonicalizeInstOperands(IRBuilder& builder, IROp op, ArrayView<IRInst*> operands)
@@ -8004,7 +8028,7 @@ namespace Slang
IRTargetSpecificDecoration* findBestTargetDecoration(
IRInst* val,
- CapabilityAtom targetCapabilityAtom)
+ CapabilityName targetCapabilityAtom)
{
return findBestTargetDecoration(val, CapabilitySet(targetCapabilityAtom));
}
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 4ee4efc16..f624b0baa 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -8724,7 +8724,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// target, and we need to reflect that at the IR level.
auto targetName = targetMod->targetToken.getContent();
- auto targetCap = findCapabilityAtom(targetName);
+ auto targetCap = findCapabilityName(targetName);
getBuilder()->addTargetDecoration(inst, CapabilitySet(targetCap));
}
@@ -8777,8 +8777,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
}
else
{
- CapabilityAtom targetCap = findCapabilityAtom(targetName);
- SLANG_ASSERT(targetCap != CapabilityAtom::Invalid);
+ CapabilityName targetCap = findCapabilityName(targetName);
+ SLANG_ASSERT(targetCap != CapabilityName::Invalid);
targetCaps = CapabilitySet(targetCap);
}
@@ -8923,7 +8923,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
getBuilder()->addTargetIntrinsicDecoration(
irInst,
- CapabilitySet(CapabilityAtom::TEXTUAL_SOURCE),
+ CapabilitySet(CapabilityName::textualTarget),
definition.getUnownedSlice()
);
}
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 9830fb18c..7c442a9d2 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -307,7 +307,7 @@ void initCommandOptions(CommandOptions& options)
"code accordingly.");
List<UnownedStringSlice> names;
- getCapabilityAtomNames(names);
+ getCapabilityNames(names);
// We'll just add to keep the list more simple...
options.addValue("spirv_1_{ 0,1,2,3,4,5 }", "minimum supported SPIR - V version");
@@ -315,11 +315,12 @@ void initCommandOptions(CommandOptions& options)
for (auto name : names)
{
if (name.startsWith("__") ||
- name.startsWith("spirv_1_"))
+ name.startsWith("spirv_1_") ||
+ name.startsWith("_"))
{
continue;
}
- else if (name.startsWith("GL_"))
+ else if (name.startsWith("GL_") || name.startsWith("SPV_") || name.startsWith("GLSL_"))
{
// We'll assume it is an extension..
StringBuilder buf;
@@ -764,7 +765,7 @@ struct OptionsParser
int targetID = -1;
FloatingPointMode floatingPointMode = FloatingPointMode::Default;
bool forceGLSLScalarLayout = false;
- List<CapabilityAtom> capabilityAtoms;
+ List<CapabilityName> capabilityAtoms;
// State for tracking command-line errors
bool conflictingProfilesSet = false;
@@ -796,7 +797,7 @@ struct OptionsParser
RawTarget* getCurrentTarget();
void setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion);
- void addCapabilityAtom(RawTarget* rawTarget, CapabilityAtom atom);
+ void addCapabilityAtom(RawTarget* rawTarget, CapabilityName atom);
void setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode);
@@ -1161,7 +1162,7 @@ void OptionsParser::setProfileVersion(RawTarget* rawTarget, ProfileVersion profi
rawTarget->profileVersion = profileVersion;
}
-void OptionsParser::addCapabilityAtom(RawTarget* rawTarget, CapabilityAtom atom)
+void OptionsParser::addCapabilityAtom(RawTarget* rawTarget, CapabilityName atom)
{
rawTarget->capabilityAtoms.add(atom);
}
@@ -1755,8 +1756,8 @@ SlangResult OptionsParser::_parseProfile(const CommandLineArg& arg)
for (Index i = 1; i < sliceCount; ++i)
{
UnownedStringSlice atomName = slices[i];
- CapabilityAtom atom = findCapabilityAtom(atomName);
- if (atom == CapabilityAtom::Invalid)
+ CapabilityName atom = findCapabilityName(atomName);
+ if (atom == CapabilityName::Invalid)
{
m_sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName);
return SLANG_FAIL;
@@ -2119,8 +2120,8 @@ SlangResult OptionsParser::_parse(
for (Index i = 0; i < sliceCount; ++i)
{
UnownedStringSlice atomName = slices[i];
- CapabilityAtom atom = findCapabilityAtom(atomName);
- if (atom == CapabilityAtom::Invalid)
+ CapabilityName atom = findCapabilityName(atomName);
+ if (atom == CapabilityName::Invalid)
{
m_sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName);
return SLANG_FAIL;
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 62ab71430..909a8eb72 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -4868,8 +4868,8 @@ namespace Slang
for (auto caseName : caseNames)
{
TargetCaseStmt* targetCase = parser->astBuilder->create<TargetCaseStmt>();
- auto cap = findCapabilityAtom(caseName.getContent());
- if (caseName.getContent().getLength() && cap == CapabilityAtom::Invalid)
+ auto cap = findCapabilityName(caseName.getContent());
+ if (caseName.getContent().getLength() && cap == CapabilityName::Invalid)
{
parser->sink->diagnose(caseName.loc, Diagnostics::unknownTargetName, caseName.getContent());
}
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 7bc73fd2c..8eb75c119 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -661,7 +661,7 @@ SLANG_NO_THROW SlangProfileID SLANG_MCALL Session::findProfile(
SLANG_NO_THROW SlangCapabilityID SLANG_MCALL Session::findCapability(
char const* name)
{
- return SlangCapabilityID(Slang::findCapabilityAtom(UnownedTerminatedStringSlice(name)));
+ return SlangCapabilityID(Slang::findCapabilityName(UnownedTerminatedStringSlice(name)));
}
SLANG_NO_THROW void SLANG_MCALL Session::setDownstreamCompilerPath(
@@ -1439,9 +1439,12 @@ void Linkage::buildHash(DigestBuilder<SHA1>& builder, SlangInt targetIndex)
builder.append(targetReq->shouldTrackLiveness());
auto cookedCapabilities = targetReq->getTargetCaps().getExpandedAtoms();
- for (auto& capability : cookedCapabilities)
+ builder.append(cookedCapabilities.getCount());
+ for (auto& capabilityConjunction : cookedCapabilities)
{
- builder.append(capability);
+ builder.append(capabilityConjunction.getExpandedAtoms().getCount());
+ for (auto atom : capabilityConjunction.getExpandedAtoms())
+ builder.append(atom);
}
const PassThroughMode passThroughMode = getDownstreamCompilerRequiredForTarget(targetReq->getTarget());
@@ -1523,7 +1526,7 @@ void TargetRequest::setHLSLToVulkanLayoutOptions(HLSLToVulkanLayoutOptions* opts
}
}
-void TargetRequest::addCapability(CapabilityAtom capability)
+void TargetRequest::addCapability(CapabilityName capability)
{
rawCapabilities.add(capability);
cookedCapabilities = CapabilitySet::makeEmpty();
@@ -1553,23 +1556,26 @@ CapabilitySet TargetRequest::getTargetCaps()
// atoms, so that most of the information about what operations
// are available where can be directly encoded on the declarations.
- List<CapabilityAtom> atoms;
+ List<CapabilityName> atoms;
+ bool isGLSLTarget = false;
switch(format)
{
case CodeGenTarget::GLSL:
case CodeGenTarget::GLSL_Vulkan:
case CodeGenTarget::GLSL_Vulkan_OneDesc:
- atoms.add(CapabilityAtom::GLSL);
+ isGLSLTarget = true;
+ atoms.add(CapabilityName::glsl);
break;
case CodeGenTarget::SPIRV:
case CodeGenTarget::SPIRVAssembly:
if (targetFlags & SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY)
{
- atoms.add(CapabilityAtom::SPIRV_DIRECT);
+ atoms.add(CapabilityName::spirv_1_5);
}
else
{
- atoms.add(CapabilityAtom::GLSL);
+ isGLSLTarget = true;
+ atoms.add(CapabilityName::glsl);
}
break;
@@ -1578,11 +1584,11 @@ CapabilitySet TargetRequest::getTargetCaps()
case CodeGenTarget::DXBytecodeAssembly:
case CodeGenTarget::DXIL:
case CodeGenTarget::DXILAssembly:
- atoms.add(CapabilityAtom::HLSL);
+ atoms.add(CapabilityName::hlsl);
break;
case CodeGenTarget::CSource:
- atoms.add(CapabilityAtom::C);
+ atoms.add(CapabilityName::c);
break;
case CodeGenTarget::CPPSource:
@@ -1591,19 +1597,35 @@ CapabilitySet TargetRequest::getTargetCaps()
case CodeGenTarget::ShaderSharedLibrary:
case CodeGenTarget::HostHostCallable:
case CodeGenTarget::ShaderHostCallable:
- atoms.add(CapabilityAtom::CPP);
+ atoms.add(CapabilityName::cpp);
break;
case CodeGenTarget::CUDASource:
case CodeGenTarget::PTX:
- atoms.add(CapabilityAtom::CUDA);
+ atoms.add(CapabilityName::cuda);
break;
default:
break;
}
- for(auto atom : rawCapabilities)
+
+ CapabilitySet latestSpirvCapSet = CapabilitySet(CapabilityName::spirv_latest);
+ CapabilityName latestSpirvAtom = (CapabilityName)latestSpirvCapSet.getExpandedAtoms()[0].getExpandedAtoms().getLast();
+ for (auto atom : rawCapabilities)
+ {
+ if (isGLSLTarget)
+ {
+ // If we are emitting GLSL code, we need to
+ // translate all spirv_*_* capabilities to
+ // glsl_spirv_*_* instead.
+ //
+ if (atom >= CapabilityName::spirv_1_0 && atom <= latestSpirvAtom)
+ {
+ atom = (CapabilityName)((Int)CapabilityName::glsl_spirv_1_0 + ((Int)atom - (Int)CapabilityName::spirv_1_0));
+ }
+ }
atoms.add(atom);
+ }
cookedCapabilities = CapabilitySet(atoms);
return cookedCapabilities;
@@ -5088,7 +5110,7 @@ SlangResult EndToEndCompileRequest::addTargetCapability(SlangInt targetIndex, Sl
auto& targets = getLinkage()->targets;
if(targetIndex < 0 || targetIndex >= targets.getCount())
return SLANG_E_INVALID_ARG;
- targets[targetIndex]->addCapability(CapabilityAtom(capability));
+ targets[targetIndex]->addCapability(CapabilityName(capability));
return SLANG_OK;
}