@@ -330,6 +330,62 @@ struct AssignValsFromLayoutContext
330330 return SLANG_OK;
331331 }
332332
333+ static bool isDescriptorHandleType (const ShaderCursor& cursor)
334+ {
335+ // Descriptor handles in SPIRV/DX12 with bindless are lowered to uint2
336+ // stored in uniform data.
337+ //
338+ // IMPORTANT: We only check for uint2, NOT uint64!
339+ // - uint64 in uniform data = device address (buffer pointer)
340+ // - uint2 in uniform data = descriptor handle (index into bindless heap)
341+ //
342+ // Note: We DON'T check for Kind::Resource in uniform data because:
343+ // - On Metal/CUDA, regular resources appear as pointers in uniform data
344+ // - Those work fine with regular setBinding(), not descriptor handles
345+
346+ auto typeLayout = cursor.getTypeLayout ();
347+
348+ // Check if this parameter is stored in uniform data (not descriptor table)
349+ auto uniformSize = typeLayout->getSize (SLANG_PARAMETER_CATEGORY_UNIFORM);
350+ auto descriptorSlotSize =
351+ typeLayout->getSize (SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
352+
353+ if (uniformSize == 8 && descriptorSlotSize == 0 )
354+ {
355+ auto type = typeLayout->getType ();
356+ auto kind = type->getKind ();
357+
358+ // Check for uint2 (SPIRV/DX12 descriptor handle)
359+ // This is the ONLY pattern we detect as descriptor handles
360+ if (kind == slang::TypeReflection::Kind::Vector && type->getElementCount () == 2 &&
361+ type->getElementType ()->getScalarType () ==
362+ slang::TypeReflection::ScalarType::UInt32)
363+ return true ;
364+ }
365+
366+ return false ;
367+ }
368+
369+ static DescriptorHandleAccess getDescriptorHandleAccess (SlangResourceAccess resourceAccess)
370+ {
371+ // Map Slang resource access to descriptor handle access
372+ // Most write-capable resources map to ReadWrite
373+ switch (resourceAccess)
374+ {
375+ case SLANG_RESOURCE_ACCESS_READ:
376+ return DescriptorHandleAccess::Read;
377+ case SLANG_RESOURCE_ACCESS_READ_WRITE:
378+ case SLANG_RESOURCE_ACCESS_WRITE:
379+ case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
380+ case SLANG_RESOURCE_ACCESS_APPEND:
381+ case SLANG_RESOURCE_ACCESS_CONSUME:
382+ case SLANG_RESOURCE_ACCESS_FEEDBACK:
383+ return DescriptorHandleAccess::ReadWrite;
384+ default :
385+ return DescriptorHandleAccess::ReadWrite;
386+ }
387+ }
388+
333389 SlangResult assignBuffer (ShaderCursor const & dstCursor, ShaderInputLayout::BufferVal* srcVal)
334390 {
335391 const InputBufferDesc& srcBuffer = srcVal->bufferDesc ;
@@ -350,6 +406,38 @@ struct AssignValsFromLayoutContext
350406 device,
351407 bufferResource));
352408
409+ // Keep buffer alive in resource context
410+ resourceContext.resources .add (ComPtr<IResource>(bufferResource.get ()));
411+
412+ // Check if this is a descriptor handle type
413+ if (isDescriptorHandleType (dstCursor))
414+ {
415+ // Try to allocate descriptor handle for bindless access
416+ DescriptorHandle handle;
417+
418+ // Determine access mode from the shader parameter's resource type
419+ auto handleType = dstCursor.getTypeLayout ()->getType ();
420+ auto resourceType = handleType->getElementType ();
421+ auto resourceAccess =
422+ resourceType ? resourceType->getResourceAccess () : SLANG_RESOURCE_ACCESS_READ_WRITE;
423+ DescriptorHandleAccess access = getDescriptorHandleAccess (resourceAccess);
424+
425+ // Try to get descriptor handle - will fail on backends that don't support it
426+ auto result = bufferResource->getDescriptorHandle (
427+ access,
428+ srcBuffer.format ,
429+ BufferRange{0 , bufferSize},
430+ &handle);
431+
432+ if (SLANG_SUCCEEDED (result))
433+ {
434+ SLANG_RETURN_ON_FAIL (dstCursor.setDescriptorHandle (handle));
435+ maybeAddOutput (dstCursor, srcVal, bufferResource);
436+ return SLANG_OK;
437+ }
438+ // If getDescriptorHandle fails, fall through to regular binding
439+ }
440+
353441 if ((dstCursor.getTypeLayout ()->getType ()->getKind () ==
354442 slang::TypeReflection::Kind::Scalar &&
355443 dstCursor.getTypeLayout ()->getType ()->getScalarType () ==
@@ -360,7 +448,6 @@ struct AssignValsFromLayoutContext
360448 // we should write bufferResource as a pointer.
361449 uint64_t addr = bufferResource->getDeviceAddress ();
362450 dstCursor.setData (&addr, sizeof (addr));
363- resourceContext.resources .add (ComPtr<IResource>(bufferResource.get ()));
364451 maybeAddOutput (dstCursor, srcVal, bufferResource);
365452 return SLANG_OK;
366453 }
@@ -405,6 +492,8 @@ struct AssignValsFromLayoutContext
405492
406493 if (counterResource)
407494 {
495+ // Keep counter buffer alive
496+ resourceContext.resources .add (ComPtr<IResource>(counterResource.get ()));
408497 dstCursor.setBinding (Binding (bufferResource, counterResource));
409498 }
410499 else
@@ -430,8 +519,14 @@ struct AssignValsFromLayoutContext
430519 device,
431520 texture));
432521
522+ // Keep texture alive in resource context
523+ resourceContext.resources .add (ComPtr<IResource>(texture.get ()));
524+
433525 auto sampler = _createSampler (device, samplerEntry->samplerDesc );
434526
527+ // Keep sampler alive in resource context
528+ resourceContext.resources .add (ComPtr<IResource>(sampler.get ()));
529+
435530 dstCursor.setBinding (Binding (texture, sampler));
436531 maybeAddOutput (dstCursor, srcVal, texture);
437532
@@ -451,6 +546,36 @@ struct AssignValsFromLayoutContext
451546 device,
452547 texture));
453548
549+ // Keep texture alive in resource context
550+ resourceContext.resources .add (ComPtr<IResource>(texture.get ()));
551+
552+ // Check if this is a descriptor handle type
553+ if (isDescriptorHandleType (dstCursor))
554+ {
555+ // Try to allocate descriptor handle for bindless access
556+ // Get default texture view first, as handles are allocated from views
557+ ComPtr<ITextureView> textureView;
558+ SLANG_RETURN_ON_FAIL (texture->getDefaultView (textureView.writeRef ()));
559+
560+ // Determine access mode from the shader parameter's resource type
561+ auto handleType = dstCursor.getTypeLayout ()->getType ();
562+ auto resourceType = handleType->getElementType ();
563+ auto resourceAccess =
564+ resourceType ? resourceType->getResourceAccess () : SLANG_RESOURCE_ACCESS_READ_WRITE;
565+ DescriptorHandle handle;
566+ DescriptorHandleAccess access = getDescriptorHandleAccess (resourceAccess);
567+
568+ // Try to get descriptor handle - will fail on backends that don't support it
569+ auto result = textureView->getDescriptorHandle (access, &handle);
570+ if (SLANG_SUCCEEDED (result))
571+ {
572+ SLANG_RETURN_ON_FAIL (dstCursor.setDescriptorHandle (handle));
573+ maybeAddOutput (dstCursor, srcVal, texture);
574+ return SLANG_OK;
575+ }
576+ // If getDescriptorHandle fails, fall through to regular binding
577+ }
578+
454579 dstCursor.setBinding (texture);
455580 maybeAddOutput (dstCursor, srcVal, texture);
456581 return SLANG_OK;
@@ -460,6 +585,23 @@ struct AssignValsFromLayoutContext
460585 {
461586 auto sampler = _createSampler (device, srcVal->samplerDesc );
462587
588+ // Keep sampler alive in resource context
589+ resourceContext.resources .add (ComPtr<IResource>(sampler.get ()));
590+
591+ // Check if this is a descriptor handle type
592+ if (isDescriptorHandleType (dstCursor))
593+ {
594+ // Try to allocate descriptor handle for bindless access
595+ DescriptorHandle handle;
596+ auto result = sampler->getDescriptorHandle (&handle);
597+ if (SLANG_SUCCEEDED (result))
598+ {
599+ SLANG_RETURN_ON_FAIL (dstCursor.setDescriptorHandle (handle));
600+ return SLANG_OK;
601+ }
602+ // If getDescriptorHandle fails, fall through to regular binding
603+ }
604+
463605 dstCursor.setBinding (sampler);
464606 return SLANG_OK;
465607 }
0 commit comments