summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
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;
}