diff --git a/tests/shaders/runtime_test_discovery.h b/tests/shaders/runtime_test_discovery.h index ed593e1b41..88a4cba784 100644 --- a/tests/shaders/runtime_test_discovery.h +++ b/tests/shaders/runtime_test_discovery.h @@ -194,28 +194,54 @@ namespace HLSLTestDiscovery inline bool runTest(const TestFunction& test, std::string& errorMsg) { try { - stf::ShaderTestFixture fixture(ShaderTest::GetFixtureDesc()); - auto shaderDir = (ShaderTest::GetExecutableDirectory() / "Shaders").wstring(); - - auto result = fixture.RunTest(stf::ShaderTestFixture::RuntimeTestDesc{ - .CompilationEnv{ .Source = std::filesystem::path(test.filePath), - .CompilationFlags = { L"-I", shaderDir } }, - .TestName = test.name, - .ThreadGroupCount{ 1, 1, 1 } }); - - if (!result) { - // Extract detailed error information from the result - // This includes line numbers, thread IDs, and actual/expected values - std::ostringstream oss; - oss << result; - errorMsg = oss.str(); - - // Also print to stdout for immediate visibility during test runs - std::cout << "\n" - << errorMsg << "\n"; - return false; + auto runWithDevice = [&test, &errorMsg](const stf::GPUDevice::EDeviceType deviceType) { + stf::ShaderTestFixture fixture(ShaderTest::GetFixtureDesc(deviceType)); + auto shaderDir = (ShaderTest::GetExecutableDirectory() / "Shaders").wstring(); + + auto result = fixture.RunTest(stf::ShaderTestFixture::RuntimeTestDesc{ + .CompilationEnv{ .Source = std::filesystem::path(test.filePath), + .CompilationFlags = { L"-I", shaderDir } }, + .TestName = test.name, + .ThreadGroupCount{ 1, 1, 1 } }); + + if (!result) { + // Extract detailed error information from the result + // This includes line numbers, thread IDs, and actual/expected values + std::ostringstream oss; + oss << result; + errorMsg = oss.str(); + + // Also print to stdout for immediate visibility during test runs + std::cout << "\n" + << errorMsg << "\n"; + return false; + } + + return true; + }; + + if (ShaderTest::GetPreferredDevice() == ShaderTest::EPreferredDevice::Hardware) { + return runWithDevice(stf::GPUDevice::EDeviceType::Hardware); + } + + if (ShaderTest::GetPreferredDevice() == ShaderTest::EPreferredDevice::Software) { + return runWithDevice(stf::GPUDevice::EDeviceType::Software); + } + + try { + const bool hardwareResult = runWithDevice(stf::GPUDevice::EDeviceType::Hardware); + ShaderTest::SetPreferredDevice(ShaderTest::EPreferredDevice::Hardware); + return hardwareResult; + } catch (const stf::HrException& e) { + if (e.Error() == E_INVALIDARG) { + std::cout << "\n[ShaderTests] Hardware D3D12 path returned E_INVALIDARG; retrying with software WARP device.\n"; + const bool softwareResult = runWithDevice(stf::GPUDevice::EDeviceType::Software); + ShaderTest::SetPreferredDevice(ShaderTest::EPreferredDevice::Software); + return softwareResult; + } else { + throw; + } } - return true; } catch (const std::exception& e) { errorMsg = e.what(); std::cout << "\nException: " << errorMsg << "\n"; diff --git a/tests/shaders/test_common.h b/tests/shaders/test_common.h index 78f4f5d45c..81f5c8ebe7 100644 --- a/tests/shaders/test_common.h +++ b/tests/shaders/test_common.h @@ -3,6 +3,8 @@ #include #include +#include +#include #ifdef _WIN32 # include @@ -10,6 +12,50 @@ namespace ShaderTest { + enum class EPreferredDevice + { + Undecided, + Hardware, + Software + }; + + inline EPreferredDevice& GetPreferredDeviceState() + { + static EPreferredDevice preferredDevice = EPreferredDevice::Undecided; + return preferredDevice; + } + + inline void SetPreferredDevice(const EPreferredDevice preferredDevice) + { + GetPreferredDeviceState() = preferredDevice; + } + + inline EPreferredDevice GetPreferredDevice() + { + return GetPreferredDeviceState(); + } + + inline stf::GPUDevice::EDeviceType ToGPUDeviceType(const EPreferredDevice preferredDevice) + { + if (preferredDevice == EPreferredDevice::Software) { + return stf::GPUDevice::EDeviceType::Software; + } + + return stf::GPUDevice::EDeviceType::Hardware; + } + + inline stf::GPUDevice::EDeviceType GetPreferredGPUDeviceType() + { + const EPreferredDevice preferredDevice = GetPreferredDevice(); + if (preferredDevice == EPreferredDevice::Undecided) { + throw std::logic_error( + "Preferred GPU device is undecided. Call ShaderTest::SetPreferredDevice(...) " + "or use ShaderTest::GetFixtureDesc(deviceType) with an explicit device."); + } + + return ToGPUDeviceType(preferredDevice); + } + /// Get the directory containing the test executable /// This is portable across different working directories and drive letters inline std::filesystem::path GetExecutableDirectory() @@ -52,15 +98,21 @@ namespace ShaderTest }; } - /// Get standard fixture description for hardware testing - inline stf::ShaderTestFixture::FixtureDesc GetFixtureDesc() + /// Get fixture description for an explicitly selected GPU device type + inline stf::ShaderTestFixture::FixtureDesc GetFixtureDesc(const stf::GPUDevice::EDeviceType deviceType) { return stf::ShaderTestFixture::FixtureDesc{ .Mappings = GetShaderDirectoryMappings(), .GPUDeviceParams{ - .DebugLevel = stf::GPUDevice::EDebugLevel::Off, // Disable debug layer (may conflict with some drivers) - .DeviceType = stf::GPUDevice::EDeviceType::Hardware, // Use real GPU instead of WARP + .DebugLevel = stf::GPUDevice::EDebugLevel::Off, // Disable debug layer (may conflict with some drivers) + .DeviceType = deviceType, .EnableGPUCapture = false } }; } + + /// Get fixture description using the shared preferred device selection + inline stf::ShaderTestFixture::FixtureDesc GetFixtureDesc() + { + return GetFixtureDesc(GetPreferredGPUDeviceType()); + } } diff --git a/tests/shaders/test_helpers_unified.h b/tests/shaders/test_helpers_unified.h index 16a03a8425..f03e952b95 100644 --- a/tests/shaders/test_helpers_unified.h +++ b/tests/shaders/test_helpers_unified.h @@ -28,7 +28,8 @@ #define SHADER_TEST(test_name, tags, shader_path, hlsl_function, x, y, z) \ TEST_CASE(test_name, tags) \ { \ - stf::ShaderTestFixture fixture(ShaderTest::GetFixtureDesc()); \ + stf::ShaderTestFixture fixture( \ + ShaderTest::GetFixtureDesc(stf::GPUDevice::EDeviceType::Hardware)); \ auto shaderDir = (ShaderTest::GetExecutableDirectory() / "Shaders").wstring(); \ auto result = fixture.RunTest(stf::ShaderTestFixture::RuntimeTestDesc{ \ .CompilationEnv{ .Source = std::filesystem::path(shader_path), \ @@ -87,7 +88,7 @@ inline void GenerateShaderTests(const char* shaderPath, const ShaderTestEntry (& for (const auto& entry : entries) { DYNAMIC_SECTION(entry.testName) { - stf::ShaderTestFixture fixture(ShaderTest::GetFixtureDesc()); + stf::ShaderTestFixture fixture(ShaderTest::GetFixtureDesc(stf::GPUDevice::EDeviceType::Hardware)); auto result = fixture.RunTest(stf::ShaderTestFixture::RuntimeTestDesc{ .CompilationEnv{ .Source = std::filesystem::path(shaderPath), .CompilationFlags = { L"-I", shaderDir } },