Skip to content

Add opt-in unified ArrayStride for SPIR-V resource descriptor heaps#11723

Merged
jkwak-work merged 12 commits into
masterfrom
fix/issue-11718
Jun 27, 2026
Merged

Add opt-in unified ArrayStride for SPIR-V resource descriptor heaps#11723
jkwak-work merged 12 commits into
masterfrom
fix/issue-11718

Conversation

@nv-slang-bot

@nv-slang-bot nv-slang-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Motivation

Fixes #11718.

VK_EXT_descriptor_heap / spvDescriptorHeapEXT packs every resource descriptor
(images and buffers) into one runtime array — the "resource heap" — and a separate
array for samplers. The host allocator stores each heap slot at a fixed stride:
for the resource heap, the spec (§"Shader Model 6.6 - SamplerHeap and ResourceHeap")
defines that stride as the canonical maximum

max(sizeof(image descriptor), sizeof(buffer descriptor))

so that index * stride lands on the same byte offset regardless of which descriptor
category a given access happens to read.

Slang previously emitted a per-type ArrayStrideIdEXT derived only from the descriptor
types a single shader referenced. That is wrong for a shared heap: an image-only
shader and a buffer-only shader, indexing the same host-packed heap, would advertise
two different strides and mis-index each other's descriptors. The motivating case:

[shader("compute")][numthreads(1,1,1)]
void computeMain(
    DescriptorHandle<Texture2D<float4>>        tex,
    DescriptorHandle<ConstantBuffer<MyStruct>> cbuffer,
    RWStructuredBuffer<uint>                   output)
{
    float4 texel = tex.Load(int3(0, 0, 0));
    output[0] = (uint)texel.x + (*cbuffer).value;
}

Compiled with -spirv-unified-descriptor-heap-stride, both resource arrays here — and a
buffer-only shader sharing the heap — must advertise the identical image-vs-buffer max.

Proposed solution

Add an opt-in option, -spirv-unified-descriptor-heap-stride, that decorates every
image/buffer resource descriptor-heap runtime array with one shared, fixed ArrayStrideIdEXT:
the canonical max(sizeof(image descriptor), sizeof(buffer descriptor)). (Acceleration-structure
heap entries are unaffected — they keep their existing literal uint64 stride.)

The stride is not adaptive to the shader's contents. Because the host heap layout is
independent of any one shader, the emitted stride is always the full image-vs-buffer max —
even for a shader that touches only a buffer. A buffer-only shader still emits the
placeholder image descriptor type and the full Select chain, so two shaders over the same
heap can never disagree.

OpConstantSizeOfEXT is device-defined (the descriptor size is decided by the driver,
not the compiler), so the maximum cannot be a compile-time literal. It is expressed
symbolically as max(a, b) = Select(UGreaterThan(a, b), a, b) using OpSpecConstantOp,
whose result is a constant <id> — a requirement for ArrayStrideIdEXT.
(OpExtInst GLSL.std.450 UMax will not do: its result is not a constant <id>.)

The unified stride is built lazily and memoized the first time a resource array needs it
(getUnifiedResourceHeapStride()), so the constant <id> is emitted before the first
array type — ArrayStrideIdEXT requires the stride <id> to precede the array it decorates.
There is no separate finalize pass and no array reordering.

Samplers are out of scope. The spec uses two heaps — samplers are never co-located with
resources — so the unified resource-heap stride folds only image-vs-buffer. Sampler arrays
(OpTypeSampler element type) are excluded from the fold and keep their existing
-spirv-sampler-heap-stride behavior.

Mutual exclusivity. A non-zero -spirv-resource-heap-stride and the unified maximum
stride are mutually exclusive — the explicit literal and the device-defined max are different
contracts for the same array, so combining them is a hard error (E57006,
SpirvConflictingDescriptorHeapStrideOptions), not a silent "one wins." An explicit
-spirv-resource-heap-stride 0 selects the same default OpConstantSizeOfEXT path the unified
option modifies and is therefore accepted (not a conflict).

Placeholder image descriptor type — deviation from the proposal

The issue's literal proposal sketches the placeholder image descriptor as
OpTypeImage %void 2D 2 0 0 0 Unknown. That is invalid SPIR-V for Vulkan:

  • VUID-StandaloneSpirv-OpTypeImage-04656 — the sampled type must be a 32-bit int,
    64-bit int, or 32-bit float scalar; %void is rejected.
  • VUID-StandaloneSpirv-OpTypeImage-04657Sampled must be 1 or 2, never 0.

A descriptor's device-defined size is its heap-slot footprint and is independent of the
image's sampled type and Sampled flag, so we use a spec-valid placeholder
OpTypeImage %float 2D 2 0 0 1 Unknown (sampled type float, Sampled = 1; depth operand
stays Unknown = 2). Both entry points of the regression test then pass static SPIR-V
validation (see below). cc @csyonghe — flagging the placeholder deviation explicitly.

Change summary

File Change
include/slang.h Append SPIRVUnifiedDescriptorHeapStride = 153 before the CountOf sentinel (additive, ABI-safe); doc notes mutual exclusivity with a non-zero -spirv-resource-heap-stride.
source/slang/slang-options.cpp Register the boolean CLI option -spirv-unified-descriptor-heap-stride and add its case to the boolean-option parse switch.
source/slang/slang-emit-spirv.cpp getUnifiedResourceHeapStride() lazily emits the fixed image-vs-buffer Select max (memoized). getDescriptorRuntimeArrayType() decorates resource arrays with that shared ArrayStrideIdEXT when the option is on; sampler arrays are excluded. diagnoseConflictingDescriptorHeapStrideOptions() raises E57006 when the unified option and a non-zero -spirv-resource-heap-stride are both set.
source/slang/slang-diagnostics.lua Define SpirvConflictingDescriptorHeapStrideOptions (E57006).
tests/spirv/descriptor-heap-unified-stride.slang Four variants: UNIFIED (shared max, two arrays one stride id), DEFAULT (control — per-type size-of, no Select), CONFLICT (diagnostic — unified option + a non-zero -spirv-resource-heap-strideE57006), SINGLE (buffer-only shader still emits the full image-vs-buffer max, proving non-adaptivity).

Concepts and vocabulary

  • Resource heap vs sampler heapspvDescriptorHeapEXT uses two separate runtime
    arrays. The unified stride applies only to the resource heap (images + buffers).
  • ArrayStrideIdEXT — decorates a runtime array with a constant <id> byte stride; that
    <id> must be defined before the array type in the module.
  • OpConstantSizeOfEXT — a device-defined (opaque, driver-decided) constant giving a
    descriptor type's heap-slot size. Not a compile-time literal.
  • Fixed / non-adaptive stride — the emitted stride depends only on the spec's canonical
    image-vs-buffer max, never on which descriptor types the shader happens to reference.

Process report

  • include/slang.h — new options need a CompilerOptionName enumerator. Appended
    SPIRVUnifiedDescriptorHeapStride = 153 immediately before CountOf with an explicit
    value (never mid-enum), so existing ABI offsets are unchanged. pr: non-breaking.

  • source/slang/slang-options.cpp — the option is a pure boolean (presence = on), so it
    is registered with a nullptr value-spec and routed through the existing boolean-option
    parse case alongside TraceCoverageBoolean / SkipSPIRVValidation. No new parsing
    machinery.

  • source/slang/slang-emit-spirv.cpp

    • getUnifiedResourceHeapStride() builds, in ConstantsAndTypes, the placeholder image
      descriptor type (OpTypeImage %float 2D 2 0 0 1 Unknown), the buffer descriptor type
      (ensureDescriptorHeapBufferDescriptorType(SpvStorageClassUniform)), their two
      OpConstantSizeOfEXT, OpSpecConstantOp OpUGreaterThan, and OpSpecConstantOp OpSelect.
      It is memoized on m_unifiedResourceHeapStride and emitted lazily so the stride <id>
      precedes the first decorated array (the ArrayStrideIdEXT ordering rule).
    • getDescriptorRuntimeArrayType() gates the fold on
      arrayStride == 0 && isResourceElement && isUnifiedResourceHeapStrideEnabled(), where
      isResourceElement = descriptorElementType->opcode != SpvOpTypeSampler. When folding, it
      skips the per-type OpConstantSizeOfEXT and decorates with the shared
      emitOpDecorateArrayStrideIdEXT; otherwise it keeps the prior per-type / literal path.
      Input-shape check: the per-array descriptor element type is the correct, principled
      input — the fold is a property of the heap, not of a malformed type, so it lives at
      the array-construction site rather than masking anything upstream.
    • diagnoseConflictingDescriptorHeapStrideOptions() is called once in emitSPIRVFromIR
      (after the forward-declared-pointers loop) and raises E57006 when the unified option
      and a non-zero -spirv-resource-heap-stride are both set. It fires on the CLI
      combination even when the module declares no resource heap (the user supplied
      contradictory intent), and emits no instructions.
  • source/slang/slang-diagnostics.luaSpirvConflictingDescriptorHeapStrideOptions
    (E57006) is a location-less hard error: "an explicit resource heap stride and the unified
    maximum stride are mutually exclusive." It is only emitted when -spirv-resource-heap-stride
    is non-zero (an explicit 0 is the default path and is accepted), so the "explicit resource
    heap stride" it names is always a non-zero one.

  • tests/spirv/descriptor-heap-unified-stride.slang — the SINGLE variant is the key
    guardrail: a buffer-only shader compiled with the flag still emits the placeholder
    OpTypeImage and the full Select chain, demonstrating the stride is fixed, not adaptive.

Validation

Built slang-test (Debug); descriptor-heap-unified-stride.slang passes all 4 static variants
(UNIFIED, DEFAULT, CONFLICT, SINGLE).

Static SPIR-V validation of both unified-stride entry points, under
SLANG_RUN_SPIRV_VALIDATION=1, exits 0:

$ SLANG_RUN_SPIRV_VALIDATION=1 slangc tests/spirv/descriptor-heap-unified-stride.slang \
      -target spirv -capability spvDescriptorHeapEXT -spirv-unified-descriptor-heap-stride \
      -entry computeMain   -stage compute -o main.spv      # EXIT=0
$ SLANG_RUN_SPIRV_VALIDATION=1 slangc tests/spirv/descriptor-heap-unified-stride.slang \
      -target spirv -capability spvDescriptorHeapEXT -spirv-unified-descriptor-heap-stride \
      -entry computeSingle -stage compute -o single.spv    # EXIT=0

Caveat: static spirv-val confirms the module is well-formed, but the device driver is the
final arbiter of the device-defined descriptor sizes and the resulting stride — this opt-in path
has not been exercised end-to-end on hardware. The runtime stack (slang-rhi) does not yet support
VK_EXT_descriptor_heap, so a runtime COMPARE_COMPUTE is not meaningful for this path today.
The two-heap assumption was confirmed by @chaoticbob on this PR.

🤖 Generated by an automated Slang coworker — may be inaccurate. A human maintainer should verify.

@nv-slang-bot nv-slang-bot Bot added the pr: non-breaking PRs without breaking changes label Jun 24, 2026
@jkwak-work jkwak-work self-assigned this Jun 24, 2026
Comment thread tests/spirv/descriptor-heap-unified-stride.slang Outdated
Comment thread tests/spirv/descriptor-heap-unified-stride.slang Outdated
@jkwak-work jkwak-work marked this pull request as ready for review June 24, 2026 05:47
@jkwak-work jkwak-work requested a review from a team as a code owner June 24, 2026 05:47
@jkwak-work jkwak-work requested review from bmillsNV and removed request for a team June 24, 2026 05:47
Comment thread source/slang/slang-emit-spirv.cpp

@jkwak-work jkwak-work left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nv-slang-bot , Fix the format workflow problem.

Comment thread source/slang/slang-emit-spirv.cpp Outdated
Comment thread source/slang/slang-emit-spirv.cpp Outdated
Comment thread tests/spirv/descriptor-heap-unified-stride.slang
Comment thread source/slang/slang-emit-spirv.cpp Outdated
nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
- Simplify the getUnifiedResourceHeapStride and conflict-diagnose doc comments
  (drop the spirv-val error-case detail) per review; this also clears the Check
  Formatting failure, since the comments are now shorter.
- Remove the redundant call-site comment in emitSPIRVFromIR.
- Add tests/spirv/descriptor-heap-unified-stride-runtime.slang: a -vk bindless
  COMPARE_COMPUTE exercising descriptor-handle resources at runtime.
- Tighten the option doc/help wording to image and buffer descriptor sizes.
@nv-slang-bot

nv-slang-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review @jkwak-work — addressed in c2dc734:

  • getUnifiedResourceHeapStride comment too verbose / error cases (r3469658009): trimmed to the what/why and dropped the spirv-val VUID and %void%float detail; that rationale now lives only in the PR description (flagged there for @csyonghe).
  • Conflict-diagnose comment too verbose (r3469632834): cut to three lines.
  • Redundant call-site comment (r3469637449): removed — the function's own comment covers it.
  • Runtime COMPARE_COMPUTE test (r3469644313): added tests/spirv/descriptor-heap-unified-stride-runtime.slang, a -vk bindless COMPARE_COMPUTE. It's a separate file because COMPARE_COMPUTE(compute) runs the entry named computeMain by convention, and in the static-stride file computeMain is the texture+cbuffer entry. The spvDescriptorHeapEXT unified-stride path itself needs VK_EXT_descriptor_heap driver support, so it stays statically verified; this runtime test exercises the descriptor-handle path through the bindless render feature.
  • Format workflow: the shorter comments resolve the Check Formatting failure (verified locally; CI is re-running on the new commit).

🤖 Generated by an automated Slang coworker — may be inaccurate. A human maintainer should verify.

nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
Remove tests/spirv/descriptor-heap-unified-stride-runtime.slang: @jkwak-work
retracted the runtime-test request because the runtime stack (slang-rhi) does
not yet support VK_EXT_descriptor_heap, so a COMPARE_COMPUTE cannot exercise the
unified-stride path. The feature stays statically verified.
jkwak-work
jkwak-work previously approved these changes Jun 24, 2026

@jkwak-work jkwak-work left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good to me.
And I think the code properly addressed what Yong concerned about.

@jkwak-work jkwak-work left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nv-slang-bot , don't we need to update the documents for the new option?

nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
Add the new option to the three user-facing doc surfaces, matching the wording
of the sibling -spirv-resource-heap-stride / -spirv-sampler-heap-stride options:

- docs/user-guide/03-convenience-features.md: prose paragraph in the
  spvDescriptorHeapEXT section explaining the shared image-vs-buffer-max stride,
  the default-path-only scope, mutual exclusivity with -spirv-resource-heap-stride,
  and that the sampler heap and acceleration-structure entries are unaffected.
- docs/user-guide/08-compiling.md: SPIRVUnifiedDescriptorHeapStride row in the
  compilation-API options table.
- docs/command-line-slangc-reference.md: regenerated via `slangc -help-style
  markdown -h`, adding the generated -spirv-unified-descriptor-heap-stride block.
nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
…stride (#11723)

diagnoseConflictingDescriptorHeapStrideOptions() raises E57006 only when
SPIRVResourceHeapStride != 0; an explicit `-spirv-resource-heap-stride 0` selects
the same default OpConstantSizeOfEXT path that the unified option modifies and is
accepted. Correct the option help text, the slang.h enumerator doc comment, and
all three user-facing doc surfaces to say the unified option is mutually exclusive
with a non-zero -spirv-resource-heap-stride (combining the two is the error),
rather than overstating that supplying the resource-heap-stride option at all
conflicts.
nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
…11723)

Match the comment to the actual condition: the conflict is between a non-zero
explicit -spirv-resource-heap-stride and the unified stride; an explicit stride
of 0 selects the same default OpConstantSizeOfEXT path the unified option
modifies and is not a conflict.
nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
Second internal comment that overstated the conflict: clarify that unified stride
only affects the auto path (when -spirv-resource-heap-stride is 0), the conflict
is with a non-zero -spirv-resource-heap-stride, and an explicit 0 selects that
same auto path and is not a conflict.
nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
Comment-only: the CONFLICT variant uses -spirv-resource-heap-stride 64, and the
conflict is specifically with a non-zero explicit stride (explicit 0 selects the
same default path and is accepted). Test directives are unchanged.
@nv-slang-bot

nv-slang-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@jkwak-work Done — documented -spirv-unified-descriptor-heap-stride across all three user-facing surfaces (commit 14f5d62):

  • docs/user-guide/03-convenience-features.md — prose in the spvDescriptorHeapEXT section (shared image-vs-buffer-max stride, default-OpConstantSizeOfEXT-path-only scope, sampler heap + acceleration-structure entries unaffected).
  • docs/user-guide/08-compiling.md — a SPIRVUnifiedDescriptorHeapStride row in the compilation-API options table.
  • docs/command-line-slangc-reference.md — regenerated via slangc -help-style markdown -h, adding the option's block.

While writing the docs I also tightened the conflict wording everywhere (option help text, slang.h doc, both internal comments, and the test comment): the conflict is specifically with a non-zero -spirv-resource-heap-stridediagnoseConflictingDescriptorHeapStrideOptions() gates on SPIRVResourceHeapStride != 0, so an explicit -spirv-resource-heap-stride 0 selects the same default path the unified option modifies and is accepted. CI re-dispatched on the latest commit.

🤖 Generated by an automated Slang coworker — may be inaccurate. A human maintainer should verify.

Comment thread source/slang/slang-emit-spirv.cpp
nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
)

Per review (@jkwak-work): diagnose the `-spirv-unified-descriptor-heap-stride` /
non-zero `-spirv-resource-heap-stride` mutual exclusivity in OptionsParser's post-parse
validation (alongside ConflictingProfilesSpecifiedForTarget / ConflictingStagesForEntryPoint),
reading both from linkage->m_optionSet, so E57006 fires as soon as the options are parsed --
before any SPIR-V emission and regardless of whether the module declares a resource heap.
An explicit `-spirv-resource-heap-stride 0` selects the default OpConstantSizeOfEXT path
the unified option modifies and is accepted.

Keep diagnoseConflictingDescriptorHeapStrideOptions() at emit time as the compile-API
safety net: SPIRVResourceHeapStride / SPIRVUnifiedDescriptorHeapStride are public API
options, and entries set directly through the compile API do not flow through OptionsParser,
so without the emit-time check that path would silently honor the explicit stride. The two
do not double-diagnose: an E57006 at parse time aborts before emission, so the emit-time
check only fires for the API path.

CONFLICT test variant still produces error[E57006]; build + 4/4 static tests + spirv-val
EXIT=0 for both entries green.
nv-slang-bot Bot added a commit that referenced this pull request Jun 24, 2026
clang-format 17.0.6 reflows the two doc comments touched in c71ee1b (the
isUnifiedResourceHeapStrideEnabled and diagnoseConflictingDescriptorHeapStrideOptions
comments). Match its wrapping exactly; comment text unchanged.
jkwak-work
jkwak-work previously approved these changes Jun 24, 2026
csyonghe
csyonghe previously approved these changes Jun 25, 2026
@csyonghe csyonghe added this pull request to the merge queue Jun 25, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Jun 26, 2026
@jkwak-work jkwak-work added this pull request to the merge queue Jun 26, 2026
nv-slang-bot Bot added 12 commits June 27, 2026 04:11
Add the SPIRVUnifiedDescriptorHeapStride option
(-spirv-unified-descriptor-heap-stride): when set, every resource
descriptor-heap runtime array advertises one shared ArrayStride equal to
the maximum of every per-type OpConstantSizeOfEXT, built as an
OpSpecConstantOp Select/UGreaterThan chain and applied via OpDecorateId
ArrayStrideIdEXT. A heap shared by multiple descriptor types then maps a
descriptor handle to the same byte offset regardless of which type's
runtime array is indexed.

The deferred maximum is emitted in a finalize pass and each participating
array is moved after it so the shared stride id precedes the target array
type, satisfying the spirv-tools ArrayStrideIdEXT operand-ordering rule.
Explicit -spirv-resource-heap-stride still wins by construction; samplers
(separate heap) and acceleration structures (non-zero stride) are excluded.

Fixes #11718.
Rework the opt-in unified ArrayStride so it is the canonical fixed
sequence max(sizeof(image descriptor), sizeof(buffer descriptor)),
emitted regardless of which resource descriptor types a shader actually
references. The host packs one resource heap at this device-defined
stride, so two shaders that share the heap must compute an identical
stride or they would mis-index; an adaptive (per-shader) stride breaks
that invariant. Samplers live in a separate heap, so the resource-heap
maximum folds only image vs buffer.

- getUnifiedResourceHeapStride() memoizes the fixed Select chain
  max(a,b)=Select(UGreaterThan(a,b),a,b) over OpConstantSizeOfEXT of a
  placeholder image and a buffer descriptor; emitted lazily before the
  first resource array so its <id> precedes the ArrayStrideIdEXT use.
- The placeholder image uses %float / Sampled=1 rather than the
  proposal's %void / Sampled=0: a descriptor's device-defined size is
  its heap-slot size, independent of the image's sampled type/flag, and
  %void/Sampled=0 fails Vulkan spirv-val (VUID-StandaloneSpirv-
  OpTypeImage-04656/04657).
- Diagnose combining -spirv-unified-descriptor-heap-stride with an
  explicit -spirv-resource-heap-stride as a hard error (E57006); the two
  are mutually exclusive.
- Simplify the getUnifiedResourceHeapStride and conflict-diagnose doc comments
  (drop the spirv-val error-case detail) per review; this also clears the Check
  Formatting failure, since the comments are now shorter.
- Remove the redundant call-site comment in emitSPIRVFromIR.
- Add tests/spirv/descriptor-heap-unified-stride-runtime.slang: a -vk bindless
  COMPARE_COMPUTE exercising descriptor-handle resources at runtime.
- Tighten the option doc/help wording to image and buffer descriptor sizes.
Remove tests/spirv/descriptor-heap-unified-stride-runtime.slang: @jkwak-work
retracted the runtime-test request because the runtime stack (slang-rhi) does
not yet support VK_EXT_descriptor_heap, so a COMPARE_COMPUTE cannot exercise the
unified-stride path. The feature stays statically verified.
Add the new option to the three user-facing doc surfaces, matching the wording
of the sibling -spirv-resource-heap-stride / -spirv-sampler-heap-stride options:

- docs/user-guide/03-convenience-features.md: prose paragraph in the
  spvDescriptorHeapEXT section explaining the shared image-vs-buffer-max stride,
  the default-path-only scope, mutual exclusivity with -spirv-resource-heap-stride,
  and that the sampler heap and acceleration-structure entries are unaffected.
- docs/user-guide/08-compiling.md: SPIRVUnifiedDescriptorHeapStride row in the
  compilation-API options table.
- docs/command-line-slangc-reference.md: regenerated via `slangc -help-style
  markdown -h`, adding the generated -spirv-unified-descriptor-heap-stride block.
…stride (#11723)

diagnoseConflictingDescriptorHeapStrideOptions() raises E57006 only when
SPIRVResourceHeapStride != 0; an explicit `-spirv-resource-heap-stride 0` selects
the same default OpConstantSizeOfEXT path that the unified option modifies and is
accepted. Correct the option help text, the slang.h enumerator doc comment, and
all three user-facing doc surfaces to say the unified option is mutually exclusive
with a non-zero -spirv-resource-heap-stride (combining the two is the error),
rather than overstating that supplying the resource-heap-stride option at all
conflicts.
…11723)

Match the comment to the actual condition: the conflict is between a non-zero
explicit -spirv-resource-heap-stride and the unified stride; an explicit stride
of 0 selects the same default OpConstantSizeOfEXT path the unified option
modifies and is not a conflict.
Second internal comment that overstated the conflict: clarify that unified stride
only affects the auto path (when -spirv-resource-heap-stride is 0), the conflict
is with a non-zero -spirv-resource-heap-stride, and an explicit 0 selects that
same auto path and is not a conflict.
Comment-only: the CONFLICT variant uses -spirv-resource-heap-stride 64, and the
conflict is specifically with a non-zero explicit stride (explicit 0 selects the
same default path and is accepted). Test directives are unchanged.
)

Per review (@jkwak-work): diagnose the `-spirv-unified-descriptor-heap-stride` /
non-zero `-spirv-resource-heap-stride` mutual exclusivity in OptionsParser's post-parse
validation (alongside ConflictingProfilesSpecifiedForTarget / ConflictingStagesForEntryPoint),
reading both from linkage->m_optionSet, so E57006 fires as soon as the options are parsed --
before any SPIR-V emission and regardless of whether the module declares a resource heap.
An explicit `-spirv-resource-heap-stride 0` selects the default OpConstantSizeOfEXT path
the unified option modifies and is accepted.

Keep diagnoseConflictingDescriptorHeapStrideOptions() at emit time as the compile-API
safety net: SPIRVResourceHeapStride / SPIRVUnifiedDescriptorHeapStride are public API
options, and entries set directly through the compile API do not flow through OptionsParser,
so without the emit-time check that path would silently honor the explicit stride. The two
do not double-diagnose: an E57006 at parse time aborts before emission, so the emit-time
check only fires for the API path.

CONFLICT test variant still produces error[E57006]; build + 4/4 static tests + spirv-val
EXIT=0 for both entries green.
clang-format 17.0.6 reflows the two doc comments touched in c71ee1b (the
isUnifiedResourceHeapStrideEnabled and diagnoseConflictingDescriptorHeapStrideOptions
comments). Match its wrapping exactly; comment text unchanged.
At merge time this PR collided with the concurrently-queued #11556, which
appends CompilerVersion = 153 to CompilerOptionName. Both PRs branched when
the enum tail was TraceCoverageBoolean = 152 and each took the next free value
153, producing a `duplicate case value` build break in the OptionKind switch
(slang-options.cpp) once combined in a merge group.

Yield 153 to #11556's CompilerVersion and append this option at 154 instead.
Append-only, so no existing enumerator value shifts (ABI-safe). The two now
hold distinct values regardless of merge order.
@nv-slang-bot nv-slang-bot Bot removed this pull request from the merge queue due to a manual request Jun 27, 2026
@nv-slang-bot nv-slang-bot Bot dismissed stale reviews from csyonghe and jkwak-work via 3a48114 June 27, 2026 04:35
@nv-slang-bot nv-slang-bot Bot force-pushed the fix/issue-11718 branch from 5d6a68e to 3a48114 Compare June 27, 2026 04:35
@nv-slang-bot

nv-slang-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current master and renumbered to clear a merge-time enum collision.

This PR's head CI was green on its own, but it was evicted from the merge queue by a duplicate case value build break that only appears in the merge group: the concurrently-queued #11556 (getDownstreamCompilerVersion) appends CompilerOptionName::CompilerVersion = 153, and this PR independently took the next free value 153 for SPIRVUnifiedDescriptorHeapStride — so the two OptionKind switch cases collide once batched together.

Fix: yield 153 to #11556 and append SPIRVUnifiedDescriptorHeapStride at 154 instead. Append-only, so no existing enumerator value shifts (ABI-safe); the two now hold distinct values in any merge order. Rebased onto master @ 838d6b40e1; slangc builds clean locally with no duplicate case value.

🤖 Generated by an automated Slang coworker — may be inaccurate. A human maintainer should verify.

@nv-slang-bot

nv-slang-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@jkwak-work — courtesy heads-up: the force-push for the rebase auto-dismissed your prior approval, so this PR is back at REVIEW_REQUIRED and can't re-enter the merge queue without a fresh approval.

The only change since you approved is the verified SPIRVUnifiedDescriptorHeapStride 153 → 154 deconfliction (append-only, so no existing enumerator value shifts — ABI-safe), which resolves the merge-time duplicate case value collision with #11556's CompilerOptionName::CompilerVersion = 153. Head CI is green — no duplicate case value. Re-approval would let it re-enter the queue whenever convenient; no rush.

🤖 Generated by an automated Slang coworker — may be inaccurate. A human maintainer should verify.

@jkwak-work jkwak-work enabled auto-merge June 27, 2026 11:16

@jkwak-work jkwak-work left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

@jkwak-work jkwak-work added this pull request to the merge queue Jun 27, 2026
Merged via the queue into master with commit 4832b0f Jun 27, 2026
116 of 118 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr: non-breaking PRs without breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for unified stride mode for resource descriptor heap

5 participants