@@ -609,6 +609,9 @@ class MTLComputeEncoder : public offloadtest::ComputeEncoder {
609609 // / the next encoder transition (via endEncodingImpl).
610610 MTL ::AccelerationStructureCommandEncoder *ASEnc = nullptr ;
611611
612+ // / Bound resource heap, read back for IRDispatchRaysArgument::ResDescHeap.
613+ MTLDescriptorHeap *BoundResourceHeap = nullptr ;
614+
612615 // / Accumulated barrier scope from commands recorded since the last barrier.
613616 MTL ::BarrierScope PendingScope = MTL ::BarrierScope(0 );
614617
@@ -665,6 +668,14 @@ class MTLComputeEncoder : public offloadtest::ComputeEncoder {
665668
666669 MTL ::ComputeCommandEncoder *getNative () const { return ComputeEnc; }
667670
671+ llvm::Error bindResourceHeap (MTLDescriptorHeap &Heap) {
672+ if (auto Err = ensureComputeEncoder ())
673+ return Err;
674+ Heap.bind (ComputeEnc);
675+ BoundResourceHeap = &Heap;
676+ return llvm::Error::success ();
677+ }
678+
668679 MTL ::CommandEncoder *getActiveEncoder () const {
669680 if (ComputeEnc)
670681 return ComputeEnc;
@@ -787,40 +798,9 @@ class MTLComputeEncoder : public offloadtest::ComputeEncoder {
787798 // MTL::Device handle (used to allocate scratch and instance buffers).
788799 llvm::Error batchBuildAS (llvm::ArrayRef<ASBuildItem> Items) override ;
789800
790- // Dispatch threads using a raygen compute kernel synthesized by the
791- // irconverter. All bindings (descriptor heap, top-level argument buffer,
792- // IRDispatchRaysArgument at slot 3, visible/intersection function tables,
793- // and the SBT buffer) must already be set on the active compute encoder by
794- // the caller — this method only binds the pipeline state and issues the
795- // dispatch.
796- llvm::Error dispatchRays (const PipelineState &PSO , const ShaderBindingTable &,
797- uint32_t Width, uint32_t Height,
798- uint32_t Depth) override {
799- if (!llvm::isa<MTLRayTracingPipelineState>(&PSO ))
800- return llvm::createStringError (
801- std::errc::invalid_argument,
802- " dispatchRays requires a RayTracing PipelineState." );
803- const auto &RTPSO = llvm::cast<MTLRayTracingPipelineState>(PSO );
804- if (!RTPSO .ComputePipeline )
805- return llvm::createStringError (
806- std::errc::invalid_argument,
807- " RayTracing PipelineState has no compute pipeline state." );
808- if (auto Err = ensureComputeEncoder ())
809- return Err;
810- flushBarrier ();
811- insertDebugSignpost (
812- llvm::formatv (" DispatchRays [{0},{1},{2}]" , Width, Height, Depth)
813- .str ());
814- ComputeEnc->setComputePipelineState (RTPSO .ComputePipeline );
815-
816- // DispatchRays(W, H, D) launches W*H*D rays; tid in the irconverter raygen
817- // kernel is the per-ray index. Pass grid as raw (W, H, D) and let Metal
818- // ceil-divide by ThreadsPerGroup to compute threadgroup count.
819- const MTL ::Size GridSize (Width, Height, Depth);
820- ComputeEnc->dispatchThreads (GridSize, RTPSO .ThreadsPerGroup );
821- addBarrierScope (MTL ::BarrierScopeBuffers | MTL ::BarrierScopeTextures);
822- return llvm::Error::success ();
823- }
801+ llvm::Error dispatchRays (const PipelineState &PSO ,
802+ const ShaderBindingTable &SBT , uint32_t Width,
803+ uint32_t Height, uint32_t Depth) override ;
824804
825805 // / Lazily transition into an AccelerationStructureCommandEncoder; mirrors
826806 // / the existing compute↔blit lazy switch.
@@ -1742,7 +1722,8 @@ class MTLDevice : public offloadtest::Device {
17421722 const auto &PS = llvm::cast<MTLPipelineState>(IS .Pipeline .get ());
17431723 MTLGPUDescriptorHandle Handle = {};
17441724 if (IS .DescHeap ) {
1745- IS .DescHeap ->bind (NativeEncoder);
1725+ if (auto Err = Encoder.bindResourceHeap (*IS .DescHeap ))
1726+ return Err;
17461727 Handle = IS .DescHeap ->getGPUDescriptorHandleForHeapStart ();
17471728 }
17481729
@@ -1772,7 +1753,15 @@ class MTLDevice : public offloadtest::Device {
17721753 NativeEncoder->useResource (llvm::cast<MTLBuffer>(B.get ())->Buf ,
17731754 MTL ::ResourceUsageRead);
17741755
1775- if (auto Err = Encoder.dispatch (*IS .Pipeline .get (),
1756+ if (P.isRayTracing ()) {
1757+ if (auto Err =
1758+ Encoder.dispatchRays (*IS .Pipeline .get (), *IS .SBT .get (),
1759+ P.DispatchParameters .DispatchGroupCount [0 ],
1760+ P.DispatchParameters .DispatchGroupCount [1 ],
1761+ P.DispatchParameters .DispatchGroupCount [2 ]))
1762+ return Err;
1763+ } else if (auto Err =
1764+ Encoder.dispatch (*IS .Pipeline .get (),
17761765 P.DispatchParameters .DispatchGroupCount [0 ],
17771766 P.DispatchParameters .DispatchGroupCount [1 ],
17781767 P.DispatchParameters .DispatchGroupCount [2 ]))
@@ -1781,112 +1770,6 @@ class MTLDevice : public offloadtest::Device {
17811770 return llvm::Error::success ();
17821771 }
17831772
1784- llvm::Error createRayTracingCommands (Pipeline &P, InvocationState &IS ) {
1785- auto EncoderOrErr = IS .CB ->createComputeEncoder ();
1786- if (!EncoderOrErr)
1787- return EncoderOrErr.takeError ();
1788- auto &Encoder = llvm::cast<MTLComputeEncoder>(*EncoderOrErr.get ());
1789- MTL ::ComputeCommandEncoder *NativeEncoder = Encoder.getNative ();
1790-
1791- const auto &RTPSO =
1792- llvm::cast<MTLRayTracingPipelineState>(*IS .Pipeline .get ());
1793- const auto &SBT = llvm::cast<MTLShaderBindingTable>(*IS .SBT .get ());
1794-
1795- // Bind the global descriptor heap + top-level argument buffer the same
1796- // way the compute path does; the raygen kernel and any visible-function
1797- // callees consume them at the same slots (kIRDescriptorHeapBindPoint and
1798- // kIRArgumentBufferBindPoint).
1799- MTLGPUDescriptorHandle Handle = {};
1800- if (IS .DescHeap ) {
1801- IS .DescHeap ->bind (NativeEncoder);
1802- Handle = IS .DescHeap ->getGPUDescriptorHandleForHeapStart ();
1803- }
1804- for (uint32_t Idx = 0u ; Idx < P.Sets .size (); ++Idx) {
1805- RTPSO .ArgBuffer ->setRootDescriptorTable (Idx, Handle);
1806- Handle.addOffset (P.Sets [Idx].Resources .size ());
1807- }
1808- RTPSO .ArgBuffer ->bind (NativeEncoder);
1809-
1810- // Populate the per-dispatch IRDispatchRaysArgument: SBT region addresses
1811- // (RayGen / Miss / HitGroup / Callable), GPU pointers to the global
1812- // root-signature argument buffer + descriptor heaps, plus resource IDs
1813- // for the visible / intersection function tables. The raygen kernel
1814- // reads this struct from the buffer bound at kIRRayDispatchArgumentsBind-
1815- // Point and any visible-function callees inherit it through the same
1816- // pointer.
1817- IRDispatchRaysArgument Args{};
1818- Args.DispatchRaysDesc .RayGenerationShaderRecord = SBT .RayGenRegion ;
1819- Args.DispatchRaysDesc .MissShaderTable = SBT .MissRegion ;
1820- Args.DispatchRaysDesc .HitGroupTable = SBT .HitGroupRegion ;
1821- Args.DispatchRaysDesc .CallableShaderTable = SBT .CallableRegion ;
1822- Args.DispatchRaysDesc .Width = P.DispatchParameters .DispatchGroupCount [0 ];
1823- Args.DispatchRaysDesc .Height = P.DispatchParameters .DispatchGroupCount [1 ];
1824- Args.DispatchRaysDesc .Depth = P.DispatchParameters .DispatchGroupCount [2 ];
1825- Args.GRS = RTPSO .ArgBuffer ->getGPUAddress ();
1826- Args.ResDescHeap =
1827- IS .DescHeap ? IS .DescHeap ->getGPUDescriptorHandleForHeapStart ().Ptr : 0 ;
1828- Args.SmpDescHeap = 0 ;
1829- Args.VisibleFunctionTable =
1830- RTPSO .VFT ? RTPSO .VFT ->gpuResourceID () : MTL ::ResourceID{0 };
1831- Args.IntersectionFunctionTable =
1832- RTPSO .IFT ? RTPSO .IFT ->gpuResourceID () : MTL ::ResourceID{0 };
1833- Args.IntersectionFunctionTables = 0 ;
1834-
1835- const BufferCreateDesc ArgsBufDesc = BufferCreateDesc::uploadBuffer ();
1836- auto ArgsBufOrErr = offloadtest::createBufferWithData (
1837- *IS .CB ->Dev , " MTL Dispatch Rays Arguments" , ArgsBufDesc, &Args,
1838- sizeof (IRDispatchRaysArgument), nullptr , nullptr );
1839- if (!ArgsBufOrErr)
1840- return ArgsBufOrErr.takeError ();
1841-
1842- auto *MTLArgsBuf = llvm::cast<MTLBuffer>(ArgsBufOrErr->get ());
1843- IS .CB ->KeepAliveOwned .push_back (std::move (*ArgsBufOrErr));
1844-
1845- NativeEncoder->setBuffer (MTLArgsBuf->Buf , 0 ,
1846- kIRRayDispatchArgumentsBindPoint );
1847- NativeEncoder->useResource (MTLArgsBuf->Buf , MTL ::ResourceUsageRead);
1848-
1849- // Mark every dispatch-side resource resident: descriptor-table bundles,
1850- // acceleration structures + their irconverter header/contribution
1851- // buffers (so RayQuery/TraceRay can read them), the SBT buffer (the
1852- // raygen kernel dereferences SBT addresses), and the visible /
1853- // intersection function tables.
1854- for (const auto &Table : IS .DescTables )
1855- for (const auto &ResPair : Table.Resources )
1856- for (const auto &ResSet : ResPair.second )
1857- NativeEncoder->useResource (ResSet.Resource .get (),
1858- MTL ::ResourceUsageRead |
1859- MTL ::ResourceUsageWrite);
1860- auto MarkASResident =
1861- [&](std::unique_ptr<offloadtest::AccelerationStructure> &AS ) {
1862- auto *MTLAS = llvm::cast<MetalAccelerationStructure>(AS .get ());
1863- NativeEncoder->useResource (MTLAS ->AccelStruct ,
1864- MTL ::ResourceUsageRead);
1865- };
1866- for (auto &AS : IS .BLASes )
1867- MarkASResident (AS );
1868- for (auto &Entry : IS .TLASes )
1869- MarkASResident (Entry.second );
1870- for (auto &B : IS .ASDescriptorBuffers )
1871- NativeEncoder->useResource (llvm::cast<MTLBuffer>(B.get ())->Buf ,
1872- MTL ::ResourceUsageRead);
1873- if (SBT .Buffer )
1874- NativeEncoder->useResource (SBT .Buffer , MTL ::ResourceUsageRead);
1875- if (RTPSO .VFT )
1876- NativeEncoder->useResource (RTPSO .VFT , MTL ::ResourceUsageRead);
1877- if (RTPSO .IFT )
1878- NativeEncoder->useResource (RTPSO .IFT , MTL ::ResourceUsageRead);
1879-
1880- if (auto Err =
1881- Encoder.dispatchRays (*IS .Pipeline .get (), *IS .SBT .get (),
1882- P.DispatchParameters .DispatchGroupCount [0 ],
1883- P.DispatchParameters .DispatchGroupCount [1 ],
1884- P.DispatchParameters .DispatchGroupCount [2 ]))
1885- return Err;
1886- Encoder.endEncoding ();
1887- return llvm::Error::success ();
1888- }
1889-
18901773 llvm::Error createRenderTarget (Pipeline &P, InvocationState &IS ) {
18911774 if (!P.Bindings .RTargetBufferPtr )
18921775 return llvm::createStringError (
@@ -3094,7 +2977,7 @@ class MTLDevice : public offloadtest::Device {
30942977 IS .SBT = std::move (*SBTOrErr);
30952978 llvm::outs () << " Shader Binding Table created.\n " ;
30962979
3097- if (auto Err = createRayTracingCommands (P, IS ))
2980+ if (auto Err = createComputeCommands (P, IS ))
30982981 return Err;
30992982 }
31002983
@@ -3303,6 +3186,78 @@ llvm::Error MTLComputeEncoder::batchBuildAS(llvm::ArrayRef<ASBuildItem> Items) {
33033186
33043187 return llvm::Error::success ();
33053188}
3189+
3190+ llvm::Error MTLComputeEncoder::dispatchRays (const PipelineState &PSO ,
3191+ const ShaderBindingTable &SBT ,
3192+ uint32_t Width, uint32_t Height,
3193+ uint32_t Depth) {
3194+ if (!llvm::isa<MTLRayTracingPipelineState>(&PSO ))
3195+ return llvm::createStringError (
3196+ std::errc::invalid_argument,
3197+ " dispatchRays requires a RayTracing PipelineState." );
3198+ if (!llvm::isa<MTLShaderBindingTable>(&SBT ))
3199+ return llvm::createStringError (
3200+ std::errc::invalid_argument,
3201+ " dispatchRays requires a Metal ShaderBindingTable." );
3202+ const auto &RTPSO = llvm::cast<MTLRayTracingPipelineState>(PSO );
3203+ const auto &MTLSBT = llvm::cast<MTLShaderBindingTable>(SBT );
3204+ if (!RTPSO .ComputePipeline )
3205+ return llvm::createStringError (
3206+ std::errc::invalid_argument,
3207+ " RayTracing PipelineState has no compute pipeline state." );
3208+ if (auto Err = ensureComputeEncoder ())
3209+ return Err;
3210+ flushBarrier ();
3211+ insertDebugSignpost (
3212+ llvm::formatv (" DispatchRays [{0},{1},{2}]" , Width, Height, Depth).str ());
3213+
3214+ // Per-dispatch ray arguments, consumed at kIRRayDispatchArgumentsBindPoint.
3215+ IRDispatchRaysArgument Args{};
3216+ Args.DispatchRaysDesc .RayGenerationShaderRecord = MTLSBT .RayGenRegion ;
3217+ Args.DispatchRaysDesc .MissShaderTable = MTLSBT .MissRegion ;
3218+ Args.DispatchRaysDesc .HitGroupTable = MTLSBT .HitGroupRegion ;
3219+ Args.DispatchRaysDesc .CallableShaderTable = MTLSBT .CallableRegion ;
3220+ Args.DispatchRaysDesc .Width = Width;
3221+ Args.DispatchRaysDesc .Height = Height;
3222+ Args.DispatchRaysDesc .Depth = Depth;
3223+ Args.GRS = RTPSO .ArgBuffer ->getGPUAddress ();
3224+ Args.ResDescHeap =
3225+ BoundResourceHeap
3226+ ? BoundResourceHeap->getGPUDescriptorHandleForHeapStart ().Ptr
3227+ : 0 ;
3228+ Args.SmpDescHeap = 0 ;
3229+ Args.VisibleFunctionTable =
3230+ RTPSO .VFT ? RTPSO .VFT ->gpuResourceID () : MTL ::ResourceID{0 };
3231+ Args.IntersectionFunctionTable =
3232+ RTPSO .IFT ? RTPSO .IFT ->gpuResourceID () : MTL ::ResourceID{0 };
3233+ Args.IntersectionFunctionTables = 0 ;
3234+
3235+ const BufferCreateDesc ArgsBufDesc = BufferCreateDesc::uploadBuffer ();
3236+ auto ArgsBufOrErr = offloadtest::createBufferWithData (
3237+ *CB ->Dev , " MTL Dispatch Rays Arguments" , ArgsBufDesc, &Args,
3238+ sizeof (IRDispatchRaysArgument), nullptr , nullptr );
3239+ if (!ArgsBufOrErr)
3240+ return ArgsBufOrErr.takeError ();
3241+ auto *MTLArgsBuf = llvm::cast<MTLBuffer>(ArgsBufOrErr->get ());
3242+ CB ->KeepAliveOwned .push_back (std::move (*ArgsBufOrErr));
3243+
3244+ ComputeEnc->setBuffer (MTLArgsBuf->Buf , 0 , kIRRayDispatchArgumentsBindPoint );
3245+ ComputeEnc->useResource (MTLArgsBuf->Buf , MTL ::ResourceUsageRead);
3246+
3247+ if (MTLSBT .Buffer )
3248+ ComputeEnc->useResource (MTLSBT .Buffer , MTL ::ResourceUsageRead);
3249+ if (RTPSO .VFT )
3250+ ComputeEnc->useResource (RTPSO .VFT , MTL ::ResourceUsageRead);
3251+ if (RTPSO .IFT )
3252+ ComputeEnc->useResource (RTPSO .IFT , MTL ::ResourceUsageRead);
3253+
3254+ ComputeEnc->setComputePipelineState (RTPSO .ComputePipeline );
3255+ // dispatchThreads automatically takes care of bounds checking.
3256+ const MTL ::Size GridSize (Width, Height, Depth);
3257+ ComputeEnc->dispatchThreads (GridSize, RTPSO .ThreadsPerGroup );
3258+ addBarrierScope (MTL ::BarrierScopeBuffers | MTL ::BarrierScopeTextures);
3259+ return llvm::Error::success ();
3260+ }
33063261} // namespace
33073262
33083263llvm::Error offloadtest::initializeMetalDevices (
0 commit comments