Skip to content

Commit f88ec54

Browse files
MarijnS95claude
authored andcommitted
[offloader] Fan a single DXIL library across an RT pipeline's Shaders[]
RayTracing pipelines compile every entry point — raygen, miss, closest-hit, any-hit, intersection, callable — into a single DXIL library via `dxc -T lib_6_x` / `clang-dxc -T lib_6_x`. That's the shape every real DXR app ships: D3D12's CreateStateObject requires a DXIL-library subobject anyway, and the driver fuses entry points across the whole library at link time, so writing one .hlsl file and compiling it once is both idiomatic and the path the framework's `%dxc_target_lib` substitution emits. Compute and raster pipelines stay one-to-one (the existing position- based mapping handles VS+PS, AS+MS+PS, etc.). RT pipelines today need N positional args even though one library blob holds every entry — which the foundational `raygen-roundtrip.test` runs straight into: 3 Shaders[] entries vs 1 input file fails the count check before any GPU work happens. Detect the RT-pipeline-with-one-input shape and copy the library blob into every `Shaders[].Shader` slot via `MemoryBuffer::getMemBufferCopy`. Each entry owns its own buffer copy (DXIL libraries are KBs, no real memory pressure) keeping the existing `unique_ptr<MemoryBuffer>` ownership model intact. Non-RT pipelines still go through the positional path and still enforce the count check. Verified by re-running `raygen-roundtrip.test`'s pipeline.yaml + the DXIL library via Wine + vkd3d-proton with a single .o argument — same 0xBEEF result the prior three-arg invocation produced. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent bfcd3a2 commit f88ec54

1 file changed

Lines changed: 23 additions & 14 deletions

File tree

tools/offloader/offloader.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -113,26 +113,35 @@ static int run() {
113113
YIn >> PipelineDesc;
114114
ExitOnErr(llvm::errorCodeToError(YIn.error()));
115115

116-
// Read in the shaders. Ray tracing PSOs compile every entry point into a
117-
// single library blob, so one input file backs all Shaders[] entries; the
118-
// backend reads the blob from Shaders.front() and uses the rest only for
119-
// their (Stage, Entry) metadata. Other pipelines expect one object file per
120-
// stage.
121-
if (PipelineDesc.isRayTracing()) {
122-
if (InputShader.size() != 1)
123-
ExitOnErr(createStringError(
124-
std::errc::invalid_argument,
125-
"RayTracing pipeline expects a single shader library, %d provided",
126-
InputShader.size()));
127-
PipelineDesc.Shaders.front().Shader = readFile(InputShader[0]);
116+
// Read in the shaders.
117+
//
118+
// RayTracing pipelines compile every entry point — raygen, miss,
119+
// closest-hit, any-hit, intersection, callable — into a single DXIL
120+
// library via `dxc -T lib_6_x` / `clang-dxc -T lib_6_x`. That's the
121+
// shape every real DXR app ships: D3D12's CreateStateObject requires a
122+
// DXIL-library subobject anyway, and the driver fuses entry points
123+
// across the whole library at link time. So when an RT pipeline is
124+
// paired with a single input file, fan that one blob across every
125+
// Shaders[] entry rather than asking the test author to duplicate the
126+
// path N times on the offloader command line.
127+
if (PipelineDesc.isRayTracing() && InputShader.size() == 1 &&
128+
PipelineDesc.Shaders.size() > 1) {
129+
std::unique_ptr<MemoryBuffer> Lib = readFile(InputShader[0]);
130+
const StringRef LibBytes = Lib->getBuffer();
131+
const StringRef LibName = Lib->getBufferIdentifier();
132+
for (size_t I = 0; I < PipelineDesc.Shaders.size(); ++I)
133+
PipelineDesc.Shaders[I].Shader =
134+
MemoryBuffer::getMemBufferCopy(LibBytes, LibName);
128135
} else {
136+
for (size_t I = 0; I < InputShader.size(); ++I) {
137+
PipelineDesc.Shaders[I].Shader = readFile(InputShader[I]);
138+
}
139+
129140
if (InputShader.size() != PipelineDesc.Shaders.size())
130141
ExitOnErr(createStringError(
131142
std::errc::invalid_argument,
132143
"Pipeline description expects %d shader(s) %d provided",
133144
PipelineDesc.Shaders.size(), InputShader.size()));
134-
for (size_t I = 0; I < InputShader.size(); ++I)
135-
PipelineDesc.Shaders[I].Shader = readFile(InputShader[I]);
136145
}
137146

138147
// Try to guess the API by reading the shader binary.

0 commit comments

Comments
 (0)