Skip to content

Commit 94c5570

Browse files
committed
Add DXC tests to SPIRVShaderResourcesTest, based on #734
1 parent 5434a10 commit 94c5570

File tree

2 files changed

+209
-26
lines changed

2 files changed

+209
-26
lines changed

Tests/DiligentCoreTest/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ if(WEBGPU_SUPPORTED)
6464
target_link_libraries(DiligentCoreTest PRIVATE libtint)
6565
endif()
6666

67+
copy_shader_compiler_dlls(DiligentCoreTest DXCOMPILER_FOR_SPIRV YES)
68+
6769
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE} ${SHADERS}})
6870

6971
set_target_properties(DiligentCoreTest

Tests/DiligentCoreTest/src/ShaderTools/SPIRVShaderResourcesTest.cpp

Lines changed: 207 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "SPIRVShaderResources.hpp"
2828
#include "GLSLangUtils.hpp"
29+
#include "DXCompiler.hpp"
2930
#include "DefaultShaderSourceStreamFactory.h"
3031
#include "RefCntAutoPtr.hpp"
3132
#include "EngineMemory.h"
@@ -46,18 +47,26 @@ namespace
4647

4748
class SPIRVShaderResourcesTest : public ::testing::Test
4849
{
50+
public:
51+
static std::unique_ptr<IDXCompiler> DXCompiler;
52+
4953
protected:
5054
static void SetUpTestSuite()
5155
{
5256
GLSLangUtils::InitializeGlslang();
57+
58+
DXCompiler = CreateDXCompiler(DXCompilerTarget::Vulkan, 0, nullptr);
5359
}
5460

5561
static void TearDownTestSuite()
5662
{
5763
GLSLangUtils::FinalizeGlslang();
64+
65+
DXCompiler.reset();
5866
}
5967
};
6068

69+
std::unique_ptr<IDXCompiler> SPIRVShaderResourcesTest::DXCompiler;
6170

6271
struct SPIRVShaderResourceRefAttribs
6372
{
@@ -70,22 +79,61 @@ struct SPIRVShaderResourceRefAttribs
7079
const Uint32 BufferStride;
7180
};
7281

73-
std::vector<unsigned int> LoadSPIRVFromHLSL(const char* FilePath, SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL)
82+
std::vector<unsigned int> LoadSPIRVFromHLSL(const char* FilePath, SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL, bool UseDXC = false)
7483
{
75-
ShaderCreateInfo ShaderCI;
76-
ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
77-
ShaderCI.FilePath = FilePath;
78-
ShaderCI.Desc = {"SPIRV test shader", ShaderType};
79-
ShaderCI.EntryPoint = "main";
84+
std::vector<unsigned int> SPIRV;
8085

81-
RefCntAutoPtr<IShaderSourceInputStreamFactory> pShaderSourceStreamFactory;
82-
CreateDefaultShaderSourceStreamFactory("shaders/SPIRV", &pShaderSourceStreamFactory);
83-
if (!pShaderSourceStreamFactory)
84-
return {};
86+
if (UseDXC)
87+
{
88+
// Use DXC to compile HLSL to SPIR-V.
89+
if (!SPIRVShaderResourcesTest::DXCompiler || !SPIRVShaderResourcesTest::DXCompiler->IsLoaded())
90+
return {};
91+
92+
ShaderCreateInfo ShaderCI;
93+
ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
94+
ShaderCI.FilePath = FilePath;
95+
ShaderCI.Desc = {"SPIRV test shader", ShaderType};
96+
ShaderCI.EntryPoint = "main";
97+
98+
ShaderMacro Macros[] = {{"DXC", "1"}};
99+
ShaderCI.Macros = {Macros, _countof(Macros)};
100+
101+
RefCntAutoPtr<IShaderSourceInputStreamFactory> pShaderSourceStreamFactory;
102+
CreateDefaultShaderSourceStreamFactory("shaders/SPIRV", &pShaderSourceStreamFactory);
103+
if (!pShaderSourceStreamFactory)
104+
return {};
105+
106+
ShaderCI.pShaderSourceStreamFactory = pShaderSourceStreamFactory;
107+
108+
RefCntAutoPtr<IDataBlob> pCompilerOutput;
109+
SPIRVShaderResourcesTest::DXCompiler->Compile(ShaderCI, ShaderVersion{6, 0}, nullptr, nullptr, &SPIRV, &pCompilerOutput);
110+
111+
if (pCompilerOutput && pCompilerOutput->GetSize() > 0)
112+
{
113+
std::string CompilerOutput = static_cast<const char*>(pCompilerOutput->GetConstDataPtr());
114+
LOG_INFO_MESSAGE("DXC compiler output:\n", CompilerOutput);
115+
}
116+
}
117+
else
118+
{
119+
// Use Glslang to compile HLSL to SPIR-V.
120+
ShaderCreateInfo ShaderCI;
121+
ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
122+
ShaderCI.FilePath = FilePath;
123+
ShaderCI.Desc = {"SPIRV test shader", ShaderType};
124+
ShaderCI.EntryPoint = "main";
125+
126+
RefCntAutoPtr<IShaderSourceInputStreamFactory> pShaderSourceStreamFactory;
127+
CreateDefaultShaderSourceStreamFactory("shaders/SPIRV", &pShaderSourceStreamFactory);
128+
if (!pShaderSourceStreamFactory)
129+
return {};
130+
131+
ShaderCI.pShaderSourceStreamFactory = pShaderSourceStreamFactory;
85132

86-
ShaderCI.pShaderSourceStreamFactory = pShaderSourceStreamFactory;
133+
SPIRV = GLSLangUtils::HLSLtoSPIRV(ShaderCI, GLSLangUtils::SpirvVersion::Vk100, nullptr, nullptr);
134+
}
87135

88-
return GLSLangUtils::HLSLtoSPIRV(ShaderCI, GLSLangUtils::SpirvVersion::Vk100, nullptr, nullptr);
136+
return SPIRV;
89137
}
90138

91139
std::vector<unsigned int> LoadSPIRVFromGLSL(const char* FilePath, SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL)
@@ -130,15 +178,11 @@ std::vector<unsigned int> LoadSPIRVFromGLSL(const char* FilePath, SHADER_TYPE Sh
130178
return GLSLangUtils::GLSLtoSPIRV(Attribs);
131179
}
132180

133-
void TestSPIRVResources(const char* FilePath,
134-
const std::vector<SPIRVShaderResourceRefAttribs>& RefResources,
135-
SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL,
136-
const char* CombinedSamplerSuffix = nullptr,
137-
bool IsGLSL = false)
181+
void TestSPIRVResourcesInternal(const char* FilePath,
182+
const std::vector<SPIRVShaderResourceRefAttribs>& RefResources,
183+
const std::vector<unsigned int>& SPIRV,
184+
SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL)
138185
{
139-
const auto SPIRV = IsGLSL ? LoadSPIRVFromGLSL(FilePath, ShaderType) : LoadSPIRVFromHLSL(FilePath, ShaderType);
140-
ASSERT_FALSE(SPIRV.empty()) << "Failed to compile HLSL to SPIRV: " << FilePath;
141-
142186
ShaderDesc ShaderDesc;
143187
ShaderDesc.Name = "SPIRVResources test";
144188
ShaderDesc.ShaderType = ShaderType;
@@ -148,7 +192,7 @@ void TestSPIRVResources(const char* FilePa
148192
GetRawAllocator(),
149193
SPIRV,
150194
ShaderDesc,
151-
CombinedSamplerSuffix,
195+
nullptr,
152196
false, // LoadShaderStageInputs
153197
false, // LoadUniformBufferReflection
154198
EntryPoint};
@@ -184,18 +228,65 @@ void TestSPIRVResources(const char* FilePa
184228
}
185229
}
186230

231+
void TestSPIRVResources(const char* FilePath,
232+
const std::vector<SPIRVShaderResourceRefAttribs>& RefResources,
233+
SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL,
234+
SHADER_SOURCE_LANGUAGE SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL)
235+
{
236+
const auto& SPIRV = (SourceLanguage == SHADER_SOURCE_LANGUAGE_GLSL) ?
237+
LoadSPIRVFromGLSL(FilePath, ShaderType) :
238+
LoadSPIRVFromHLSL(FilePath, ShaderType);
239+
ASSERT_FALSE(SPIRV.empty()) << (SourceLanguage == SHADER_SOURCE_LANGUAGE_GLSL ?
240+
"Failed to compile GLSL to SPIRV with glslang: " :
241+
"Failed to compile HLSL to SPIRV with glslang: ")
242+
<< FilePath;
243+
244+
LOG_INFO_MESSAGE(SourceLanguage == SHADER_SOURCE_LANGUAGE_GLSL ?
245+
"Testing with GLSL->SPIRV with glslang:\n" :
246+
"Testing with HLSL->SPIRV with glslang:\n",
247+
FilePath);
248+
249+
TestSPIRVResourcesInternal(FilePath, RefResources, SPIRV, ShaderType);
250+
}
251+
252+
void TestSPIRVResourcesDXC(const char* FilePath,
253+
const std::vector<SPIRVShaderResourceRefAttribs>& RefResources,
254+
SHADER_TYPE ShaderType = SHADER_TYPE_PIXEL)
255+
{
256+
if (!SPIRVShaderResourcesTest::DXCompiler || !SPIRVShaderResourcesTest::DXCompiler->IsLoaded())
257+
{
258+
LOG_INFO_MESSAGE("HLSL->SPIRV with DXC skipped because DXCompiler is not available\n");
259+
return;
260+
}
261+
262+
const auto& SPIRV_DXC = LoadSPIRVFromHLSL(FilePath, ShaderType, true);
263+
ASSERT_FALSE(SPIRV_DXC.empty()) << "Failed to compile HLSL to SPIRV with DXC: " << FilePath;
264+
265+
LOG_INFO_MESSAGE("Testing with HLSL->SPIRV with DXC:\n", FilePath);
266+
267+
TestSPIRVResourcesInternal(FilePath, RefResources, SPIRV_DXC, ShaderType);
268+
}
269+
187270
using SPIRVResourceType = SPIRVShaderResourceAttribs::ResourceType;
188271

189272
TEST_F(SPIRVShaderResourcesTest, UniformBuffers)
190273
{
191274
TestSPIRVResources("UniformBuffers.psh",
192275
{
193-
// CB0 is optimized away as it's not used in the shader
194276
SPIRVShaderResourceRefAttribs{"CB1", 1, SPIRVResourceType::UniformBuffer, RESOURCE_DIM_BUFFER, 0, 48, 0},
195277
SPIRVShaderResourceRefAttribs{"CB2", 1, SPIRVResourceType::UniformBuffer, RESOURCE_DIM_BUFFER, 0, 16, 0},
196278
});
197279
}
198280

281+
TEST_F(SPIRVShaderResourcesTest, UniformBuffers_DXC)
282+
{
283+
TestSPIRVResourcesDXC("UniformBuffers.psh",
284+
{
285+
SPIRVShaderResourceRefAttribs{"CB1", 1, SPIRVResourceType::UniformBuffer, RESOURCE_DIM_BUFFER, 0, 48, 0},
286+
SPIRVShaderResourceRefAttribs{"CB2", 1, SPIRVResourceType::UniformBuffer, RESOURCE_DIM_BUFFER, 0, 16, 0},
287+
});
288+
}
289+
199290
TEST_F(SPIRVShaderResourcesTest, StorageBuffers)
200291
{
201292
TestSPIRVResources("StorageBuffers.psh",
@@ -209,6 +300,19 @@ TEST_F(SPIRVShaderResourcesTest, StorageBuffers)
209300
});
210301
}
211302

303+
TEST_F(SPIRVShaderResourcesTest, StorageBuffers_DXC)
304+
{
305+
TestSPIRVResourcesDXC("StorageBuffers.psh",
306+
{
307+
// StructuredBuffers have BufferStaticSize=0 (runtime array) and BufferStride is the element size
308+
SPIRVShaderResourceRefAttribs{"g_ROBuffer", 1, SPIRVResourceType::ROStorageBuffer, RESOURCE_DIM_BUFFER, 0, 0, 32},
309+
SPIRVShaderResourceRefAttribs{"g_RWBuffer", 1, SPIRVResourceType::RWStorageBuffer, RESOURCE_DIM_BUFFER, 0, 0, 64},
310+
// ByteAddressBuffers also have BufferStaticSize=0 and BufferStride=4 (uint size)
311+
SPIRVShaderResourceRefAttribs{"g_ROAtomicBuffer", 1, SPIRVResourceType::ROStorageBuffer, RESOURCE_DIM_BUFFER, 0, 0, 4},
312+
SPIRVShaderResourceRefAttribs{"g_RWAtomicBuffer", 1, SPIRVResourceType::RWStorageBuffer, RESOURCE_DIM_BUFFER, 0, 0, 4},
313+
});
314+
}
315+
212316
TEST_F(SPIRVShaderResourcesTest, TexelBuffers)
213317
{
214318
TestSPIRVResources("TexelBuffers.psh",
@@ -218,6 +322,15 @@ TEST_F(SPIRVShaderResourcesTest, TexelBuffers)
218322
});
219323
}
220324

325+
TEST_F(SPIRVShaderResourcesTest, TexelBuffers_DXC)
326+
{
327+
TestSPIRVResourcesDXC("TexelBuffers.psh",
328+
{
329+
SPIRVShaderResourceRefAttribs{"g_UniformTexelBuffer", 1, SPIRVResourceType::UniformTexelBuffer, RESOURCE_DIM_BUFFER, 0, 0, 0},
330+
SPIRVShaderResourceRefAttribs{"g_StorageTexelBuffer", 1, SPIRVResourceType::StorageTexelBuffer, RESOURCE_DIM_BUFFER, 0, 0, 0},
331+
});
332+
}
333+
221334
TEST_F(SPIRVShaderResourcesTest, Textures)
222335
{
223336
TestSPIRVResources("Textures.psh",
@@ -238,6 +351,26 @@ TEST_F(SPIRVShaderResourcesTest, Textures)
238351
});
239352
}
240353

354+
TEST_F(SPIRVShaderResourcesTest, Textures_DXC)
355+
{
356+
TestSPIRVResourcesDXC("Textures.psh",
357+
{
358+
// When textures and samplers are declared separately in HLSL, they are compiled as separate_images
359+
// instead of sampled_images. This is the correct behavior for separate sampler/texture declarations.
360+
SPIRVShaderResourceRefAttribs{"g_SampledImage", 1, SPIRVResourceType::SeparateImage, RESOURCE_DIM_TEX_2D, 0, 0, 0},
361+
SPIRVShaderResourceRefAttribs{"g_SampledImageMS", 1, SPIRVResourceType::SeparateImage, RESOURCE_DIM_TEX_2D, 1, 0, 0},
362+
SPIRVShaderResourceRefAttribs{"g_SampledImage3D", 1, SPIRVResourceType::SeparateImage, RESOURCE_DIM_TEX_3D, 0, 0, 0},
363+
SPIRVShaderResourceRefAttribs{"g_SampledImageCube", 1, SPIRVResourceType::SeparateImage, RESOURCE_DIM_TEX_CUBE, 0, 0, 0},
364+
SPIRVShaderResourceRefAttribs{"g_Sampler", 1, SPIRVResourceType::SeparateSampler, RESOURCE_DIM_UNDEFINED, 0, 0, 0},
365+
SPIRVShaderResourceRefAttribs{"g_SeparateImage", 1, SPIRVResourceType::SeparateImage, RESOURCE_DIM_TEX_2D, 0, 0, 0},
366+
// Combined sampler: g_Texture and g_Texture_sampler
367+
// Note: Even with CombinedSamplerSuffix, SPIRV may still classify them as separate_images
368+
// if they are declared separately. The CombinedSamplerSuffix is mainly used for naming convention.
369+
SPIRVShaderResourceRefAttribs{"g_Texture", 1, SPIRVResourceType::SeparateImage, RESOURCE_DIM_TEX_2D, 0, 0, 0},
370+
SPIRVShaderResourceRefAttribs{"g_Texture_sampler", 1, SPIRVResourceType::SeparateSampler, RESOURCE_DIM_UNDEFINED, 0, 0, 0},
371+
});
372+
}
373+
241374
TEST_F(SPIRVShaderResourcesTest, StorageImages)
242375
{
243376
TestSPIRVResources("StorageImages.psh",
@@ -249,6 +382,17 @@ TEST_F(SPIRVShaderResourcesTest, StorageImages)
249382
});
250383
}
251384

385+
TEST_F(SPIRVShaderResourcesTest, StorageImages_DXC)
386+
{
387+
TestSPIRVResourcesDXC("StorageImages.psh",
388+
{
389+
// Note: HLSL does not support RWTextureCube, so we only test 2D, 2DArray, and 3D storage images
390+
SPIRVShaderResourceRefAttribs{"g_RWImage2D", 1, SPIRVResourceType::StorageImage, RESOURCE_DIM_TEX_2D, 0, 0, 0},
391+
SPIRVShaderResourceRefAttribs{"g_RWImage2DArray", 1, SPIRVResourceType::StorageImage, RESOURCE_DIM_TEX_2D_ARRAY, 0, 0, 0},
392+
SPIRVShaderResourceRefAttribs{"g_RWImage3D", 1, SPIRVResourceType::StorageImage, RESOURCE_DIM_TEX_3D, 0, 0, 0},
393+
});
394+
}
395+
252396
TEST_F(SPIRVShaderResourcesTest, AtomicCounters)
253397
{
254398
// Use GLSL for atomic counters. Note: Vulkan does not support atomic_uint (AtomicCounter storage class).
@@ -260,8 +404,7 @@ TEST_F(SPIRVShaderResourcesTest, AtomicCounters)
260404
SPIRVShaderResourceRefAttribs{"AtomicCounterBuffer", 1, SPIRVResourceType::RWStorageBuffer, RESOURCE_DIM_BUFFER, 0, 4, 0},
261405
},
262406
SHADER_TYPE_PIXEL,
263-
nullptr,
264-
true); // IsGLSL = true
407+
SHADER_SOURCE_LANGUAGE_GLSL);
265408
}
266409

267410
TEST_F(SPIRVShaderResourcesTest, InputAttachments)
@@ -272,6 +415,14 @@ TEST_F(SPIRVShaderResourcesTest, InputAttachments)
272415
});
273416
}
274417

418+
TEST_F(SPIRVShaderResourcesTest, InputAttachments_DXC)
419+
{
420+
TestSPIRVResourcesDXC("InputAttachments.psh",
421+
{
422+
SPIRVShaderResourceRefAttribs{"g_InputAttachment", 1, SPIRVResourceType::InputAttachment, RESOURCE_DIM_UNDEFINED, 0, 0, 0},
423+
});
424+
}
425+
275426
TEST_F(SPIRVShaderResourcesTest, AccelerationStructures)
276427
{
277428
// Use GLSL for acceleration structures since HLSLtoSPIRV doesn't support raytracing shaders
@@ -282,8 +433,7 @@ TEST_F(SPIRVShaderResourcesTest, AccelerationStructures)
282433
SPIRVShaderResourceRefAttribs{"g_AccelStruct", 1, SPIRVResourceType::AccelerationStructure, RESOURCE_DIM_UNDEFINED, 0, 0, 0},
283434
},
284435
SHADER_TYPE_RAY_GEN,
285-
nullptr,
286-
true); // IsGLSL = true
436+
SHADER_SOURCE_LANGUAGE_GLSL); // IsGLSL = true
287437
}
288438

289439
TEST_F(SPIRVShaderResourcesTest, PushConstants)
@@ -297,6 +447,17 @@ TEST_F(SPIRVShaderResourcesTest, PushConstants)
297447
});
298448
}
299449

450+
TEST_F(SPIRVShaderResourcesTest, PushConstants_DXC)
451+
{
452+
// Push constant ArraySize represents the number of 32-bit words, not array elements
453+
// PushConstants struct: float4x4 (16 floats) + float4 (4 floats) + float2 (2 floats) + float (1 float) + uint (1 uint)
454+
// Total: 16 + 4 + 2 + 1 + 1 = 24 floats/uints = 24 * 4 bytes = 96 bytes = 24 words
455+
TestSPIRVResourcesDXC("PushConstants.psh",
456+
{
457+
SPIRVShaderResourceRefAttribs{"PushConstants", 1, SPIRVResourceType::PushConstant, RESOURCE_DIM_BUFFER, 0, 96, 0},
458+
});
459+
}
460+
300461
TEST_F(SPIRVShaderResourcesTest, MixedResources)
301462
{
302463
TestSPIRVResources("MixedResources.psh",
@@ -317,4 +478,24 @@ TEST_F(SPIRVShaderResourcesTest, MixedResources)
317478
});
318479
}
319480

481+
TEST_F(SPIRVShaderResourcesTest, MixedResources_DXC)
482+
{
483+
TestSPIRVResourcesDXC("MixedResources.psh",
484+
{
485+
// UniformBuff: float4x4 (64 bytes) + float4 (16 bytes) = 80 bytes
486+
SPIRVShaderResourceRefAttribs{"UniformBuff", 1, SPIRVResourceType::UniformBuffer, RESOURCE_DIM_BUFFER, 0, 80, 0},
487+
// ROStorageBuff: StructuredBuffer<BufferData> where BufferData = float4[4] = 64 bytes
488+
// StructuredBuffers have BufferStaticSize=0 (runtime array) and BufferStride is the element size
489+
SPIRVShaderResourceRefAttribs{"ROStorageBuff", 1, SPIRVResourceType::ROStorageBuffer, RESOURCE_DIM_BUFFER, 0, 0, 64},
490+
// RWStorageBuff: same as ROStorageBuff
491+
SPIRVShaderResourceRefAttribs{"RWStorageBuff", 1, SPIRVResourceType::RWStorageBuffer, RESOURCE_DIM_BUFFER, 0, 0, 64},
492+
// SampledTex: When Texture2D and SamplerState are declared separately, they are compiled as SeparateImage
493+
SPIRVShaderResourceRefAttribs{"SampledTex", 1, SPIRVResourceType::SeparateImage, RESOURCE_DIM_TEX_2D, 0, 0, 0},
494+
SPIRVShaderResourceRefAttribs{"StorageTex", 1, SPIRVResourceType::StorageImage, RESOURCE_DIM_TEX_2D, 0, 0, 0},
495+
SPIRVShaderResourceRefAttribs{"Sampler", 1, SPIRVResourceType::SeparateSampler, RESOURCE_DIM_UNDEFINED, 0, 0, 0},
496+
// PushConstants: float2 (2 floats) + float (1 float) + uint (1 uint) = 4 words = 16 bytes
497+
SPIRVShaderResourceRefAttribs{"PushConstants", 1, SPIRVResourceType::PushConstant, RESOURCE_DIM_BUFFER, 0, 16, 0},
498+
});
499+
}
500+
320501
} // namespace

0 commit comments

Comments
 (0)