diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 915142185d..2373c0ac75 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -22,6 +22,12 @@ The included licenses apply to the following files: Place release notes for the upcoming release below this line and remove this line upon naming the release. Refer to previous for appropriate section names. +#### SPIR-V + +- Added `-fspv-flatten-resource-array-bindings-only`, which reserves one + binding number per array element for arrays of resources without splitting + the array into separate SPIR-V variables/bindings. + ### Upcoming Preview Release These changes apply to experimental preview shader models only and will not be diff --git a/docs/SPIR-V.rst b/docs/SPIR-V.rst index da5e914e3e..ce1cef26fa 100644 --- a/docs/SPIR-V.rst +++ b/docs/SPIR-V.rst @@ -4289,6 +4289,14 @@ codegen for Vulkan: SPIR-V backend. Also note that this requires the optimizer to be able to resolve all array accesses with constant indeces. Therefore, all loops using the resource arrays must be marked with ``[unroll]``. +- ``-fspv-flatten-resource-array-bindings-only``: Reserves one binding number + per array element for arrays of resources, matching the binding numbers + that ``-fspv-flatten-resource-arrays`` would reserve, but keeps each array + as a single SPIR-V variable/binding (with ``descriptorCount`` equal to the + array size) instead of splitting it into one variable per element. Use this + when you want later-declared resources to avoid overlapping the numbers an + array would need under DX, without requiring the array itself to be + split up or unrolled. - ``-fspv-entrypoint-name=``: Specify the SPIR-V entry point name. Defaults to the HLSL entry point name. - ``-fspv-use-legacy-buffer-matrix-order``: Assumes the legacy matrix order (row diff --git a/include/dxc/Support/HLSLOptions.td b/include/dxc/Support/HLSLOptions.td index f35e4809df..babd0726a1 100644 --- a/include/dxc/Support/HLSLOptions.td +++ b/include/dxc/Support/HLSLOptions.td @@ -405,6 +405,8 @@ def fspv_target_env_EQ : Joined<["-"], "fspv-target-env=">, Group, HelpText<"Specify the target environment: vulkan1.0 (default), vulkan1.1, vulkan1.1spirv1.4, vulkan1.2, vulkan1.3, or universal1.5">; def fspv_flatten_resource_arrays: Flag<["-"], "fspv-flatten-resource-arrays">, Group, Flags<[CoreOption, DriverOption]>, HelpText<"Flatten arrays of resources so each array element takes one binding number">; +def fspv_flatten_resource_array_bindings_only: Flag<["-"], "fspv-flatten-resource-array-bindings-only">, Group, Flags<[CoreOption, DriverOption]>, + HelpText<"Reserve one binding number per array element for arrays of resources, without splitting the array into separate variables">; def fspv_reduce_load_size: Flag<["-"], "fspv-reduce-load-size">, Group, Flags<[CoreOption, DriverOption]>, HelpText<"Replaces loads of composite objects to reduce memory pressure for the loads">; def fspv_fix_func_call_arguments: Flag<["-"], "fspv-fix-func-call-arguments">, Group, Flags<[CoreOption, DriverOption, HelpHidden]>, diff --git a/include/dxc/Support/SPIRVOptions.h b/include/dxc/Support/SPIRVOptions.h index 0253caba63..1136097a3a 100644 --- a/include/dxc/Support/SPIRVOptions.h +++ b/include/dxc/Support/SPIRVOptions.h @@ -64,6 +64,7 @@ struct SpirvCodeGenOptions { bool useLegacyBufferMatrixOrder = false; bool useScalarLayout = false; bool flattenResourceArrays = false; + bool flattenResourceArrayBindingsOnly = false; bool reduceLoadSize = false; bool autoShiftBindings = false; bool supportNonzeroBaseInstance = false; diff --git a/lib/DxcSupport/HLSLOptions.cpp b/lib/DxcSupport/HLSLOptions.cpp index 1630243aab..36fe57ce63 100644 --- a/lib/DxcSupport/HLSLOptions.cpp +++ b/lib/DxcSupport/HLSLOptions.cpp @@ -1120,6 +1120,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude, Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false); opts.SpirvOptions.flattenResourceArrays = Args.hasFlag(OPT_fspv_flatten_resource_arrays, OPT_INVALID, false); + opts.SpirvOptions.flattenResourceArrayBindingsOnly = Args.hasFlag( + OPT_fspv_flatten_resource_array_bindings_only, OPT_INVALID, false); opts.SpirvOptions.reduceLoadSize = Args.hasFlag(OPT_fspv_reduce_load_size, OPT_INVALID, false); opts.SpirvOptions.fixFuncCallArguments = @@ -1294,6 +1296,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude, Args.hasFlag(OPT_fspv_use_legacy_buffer_matrix_order, OPT_INVALID, false) || Args.hasFlag(OPT_fspv_flatten_resource_arrays, OPT_INVALID, false) || + Args.hasFlag(OPT_fspv_flatten_resource_array_bindings_only, OPT_INVALID, + false) || Args.hasFlag(OPT_fspv_reduce_load_size, OPT_INVALID, false) || Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false) || Args.hasFlag(OPT_fspv_fix_func_call_arguments, OPT_INVALID, false) || diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 1a1078bf0b..e2d4c74509 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -2646,7 +2646,9 @@ bool DeclResultIdMapper::decorateResourceBindings() { // resources (e.g. array of textures), DX uses one binding number per array // element. We can match this behavior via a command line option. uint32_t numBindingsToUse = 1; - if (spirvOptions.flattenResourceArrays || needsFlatteningCompositeResources) + if (spirvOptions.flattenResourceArrays || + spirvOptions.flattenResourceArrayBindingsOnly || + needsFlatteningCompositeResources) numBindingsToUse = getNumBindingsUsedByResourceType( var.getSpirvInstr()->getAstResultType()); @@ -2736,7 +2738,9 @@ bool DeclResultIdMapper::decorateResourceBindings() { // resources (e.g. array of textures), DX uses one binding number per array // element. We can match this behavior via a command line option. uint32_t numBindingsToUse = 1; - if (spirvOptions.flattenResourceArrays || needsFlatteningCompositeResources) + if (spirvOptions.flattenResourceArrays || + spirvOptions.flattenResourceArrayBindingsOnly || + needsFlatteningCompositeResources) numBindingsToUse = getNumBindingsUsedByResourceType( var.getSpirvInstr()->getAstResultType()); diff --git a/tools/clang/test/CodeGenSPIRV/vk.binding.cl.flatten-array-bindings-only.example1-optimized.hlsl b/tools/clang/test/CodeGenSPIRV/vk.binding.cl.flatten-array-bindings-only.example1-optimized.hlsl new file mode 100644 index 0000000000..4104796242 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.binding.cl.flatten-array-bindings-only.example1-optimized.hlsl @@ -0,0 +1,27 @@ +// RUN: %dxc -T ps_6_0 -E main -fspv-flatten-resource-array-bindings-only -O3 %s -spirv | FileCheck %s + +// The array is NOT split into individual variables (unlike +// -fspv-flatten-resource-arrays): MyTextures/MySamplers stay as single +// array variables, each with a single Binding decoration, but the binding +// numbers reserved for them still account for the full array size. +// CHECK: OpDecorate %MyTextures Binding 0 +// CHECK: OpDecorate %AnotherTexture Binding 5 +// CHECK: OpDecorate %NextTexture Binding 6 +// CHECK: OpDecorate %MySamplers Binding 7 +// CHECK-NOT: MyTextures_0_ +// CHECK-NOT: MySamplers_0_ +Texture2D MyTextures[5] : register(t0); +Texture2D NextTexture; // This is suppose to be t6. +Texture2D AnotherTexture : register(t5); +SamplerState MySamplers[2]; + +[unroll] +float4 main(float2 TexCoord : TexCoord) : SV_Target0 +{ + float4 result = 0; + for (uint i = 0; i < 5; ++i) + result += MyTextures[i].Sample(MySamplers[i < 3 ? 0 : 1], TexCoord); + result += AnotherTexture.Sample(MySamplers[1], TexCoord); + result += NextTexture.Sample(MySamplers[1], TexCoord); + return result; +} diff --git a/tools/clang/test/CodeGenSPIRV/vk.binding.cl.flatten-array-bindings-only.example1.hlsl b/tools/clang/test/CodeGenSPIRV/vk.binding.cl.flatten-array-bindings-only.example1.hlsl new file mode 100644 index 0000000000..8ef5508ffe --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.binding.cl.flatten-array-bindings-only.example1.hlsl @@ -0,0 +1,23 @@ +// RUN: %dxc -T ps_6_0 -E main -fspv-flatten-resource-array-bindings-only -fcgl %s -spirv | FileCheck %s + +// CHECK: OpDecorate %MyTextures Binding 0 +// CHECK: OpDecorate %AnotherTexture Binding 5 +// CHECK: OpDecorate %NextTexture Binding 6 +// CHECK: OpDecorate %MySamplers Binding 7 +Texture2D MyTextures[5] : register(t0); +Texture2D NextTexture; // This is suppose to be t6. +Texture2D AnotherTexture : register(t5); +SamplerState MySamplers[2]; + +float4 main(float2 TexCoord : TexCoord) : SV_Target0 +{ + float4 result = + MyTextures[0].Sample(MySamplers[0], TexCoord) + + MyTextures[1].Sample(MySamplers[0], TexCoord) + + MyTextures[2].Sample(MySamplers[0], TexCoord) + + MyTextures[3].Sample(MySamplers[1], TexCoord) + + MyTextures[4].Sample(MySamplers[1], TexCoord) + + AnotherTexture.Sample(MySamplers[1], TexCoord) + + NextTexture.Sample(MySamplers[1], TexCoord); + return result; +}