diff options
Diffstat (limited to 'tools/slang-test')
| -rw-r--r-- | tools/slang-test/main.cpp | 153 | ||||
| -rw-r--r-- | tools/slang-test/render-api-util.cpp | 210 | ||||
| -rw-r--r-- | tools/slang-test/render-api-util.h | 61 | ||||
| -rw-r--r-- | tools/slang-test/slang-test.vcxproj | 2 | ||||
| -rw-r--r-- | tools/slang-test/slang-test.vcxproj.filters | 6 |
5 files changed, 423 insertions, 9 deletions
diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp index 0f469e8d9..e67db42e4 100644 --- a/tools/slang-test/main.cpp +++ b/tools/slang-test/main.cpp @@ -4,10 +4,12 @@ #include "../../source/core/token-reader.h" #include "../../source/core/slang-result.h" +#include "../../source/core/slang-string-util.h" using namespace Slang; #include "os.h" +#include "render-api-util.h" #define STB_IMAGE_IMPLEMENTATION #include "external/stb/stb_image.h" @@ -70,6 +72,9 @@ struct Options // Exclude test that match one these categories Dictionary<TestCategory*, TestCategory*> excludeCategories; + + // By default we can test against all apis + int enabledApis = int(RenderApiFlag::AllOf); }; Options options; @@ -188,6 +193,22 @@ Result parseOptions(int* argc, char** argv) options.excludeCategories.Add(category, category); } } + else if (strcmp(arg, "-api") == 0) + { + if (argCursor == argEnd) + { + fprintf(stderr, "error: expected comma separated list of apis '%s'\n", arg); + return SLANG_FAIL; + } + const char* apiList = *argCursor++; + + SlangResult res = RenderApiUtil::parseApiFlags(UnownedStringSlice(apiList), &options.enabledApis); + if (SLANG_FAILED(res)) + { + fprintf(stderr, "error: unable to parse api list '%s'\n", apiList); + return res; + } + } else { fprintf(stderr, "unknown option '%s'\n", arg); @@ -195,6 +216,13 @@ Result parseOptions(int* argc, char** argv) } } + { + // Find out what apis are available + const int availableApis = RenderApiUtil::getAvailableApis(); + // Only allow apis we know are available + options.enabledApis &= availableApis; + } + // any arguments left over were positional arguments argCount = (int)((char**)writeCursor - argv); argCursor = argv; @@ -636,6 +664,44 @@ void maybeDumpOutput( fflush(stderr); } +// Finds the specialized or default path for expected data for a test. +// If neither are found, will return an empty string +String findExpectedPath(const TestInput& input, const char* postFix) +{ + StringBuilder specializedBuf; + + // Try the specialized name first + specializedBuf << input.outputStem; + if (postFix) + { + specializedBuf << postFix; + } + if (File::Exists(specializedBuf)) + { + return specializedBuf; + } + + + // Try the default name + StringBuilder defaultBuf; + defaultBuf.Clear(); + defaultBuf << input.filePath; + if (postFix) + { + defaultBuf << postFix; + } + + if (File::Exists(defaultBuf)) + { + return defaultBuf; + } + + // Couldn't find either + printf("referenceOutput '%s' or '%s' not found.\n", defaultBuf.Buffer(), specializedBuf.Buffer()); + + return ""; +} + TestResult runSimpleTest(TestInput& input) { // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect @@ -1124,12 +1190,19 @@ TestResult runGLSLComparisonTest(TestInput& input) return kTestResult_Pass; } -TestResult runComputeComparisonImpl(TestInput& input, const char * langOption, String referenceOutput) + +TestResult runComputeComparisonImpl(TestInput& input, const char * langOption) { // TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass auto filePath999 = input.filePath; auto outputStem = input.outputStem; + const String referenceOutput = findExpectedPath(input, ".expected.txt"); + if (referenceOutput.Length() <= 0) + { + return kTestResult_Fail; + } + OSProcessSpawner spawner; spawner.pushExecutablePath(String(options.binDir) + "render-test" + osGetExecutableSuffix()); @@ -1211,22 +1284,22 @@ TestResult runComputeComparisonImpl(TestInput& input, const char * langOption, S TestResult runSlangComputeComparisonTest(TestInput& input) { - return runComputeComparisonImpl(input, "-slang -compute", input.outputStem + ".expected.txt"); + return runComputeComparisonImpl(input, "-slang -compute"); } TestResult runSlangComputeComparisonTestEx(TestInput& input) { - return runComputeComparisonImpl(input, "", input.outputStem + ".expected.txt"); + return runComputeComparisonImpl(input, ""); } TestResult runHLSLComputeTest(TestInput& input) { - return runComputeComparisonImpl(input, "-hlsl-rewrite -compute", input.outputStem + ".expected.txt"); + return runComputeComparisonImpl(input, "-hlsl-rewrite -compute"); } TestResult runSlangRenderComputeComparisonTest(TestInput& input) { - return runComputeComparisonImpl(input, "-slang -gcompute", input.outputStem + ".expected.txt"); + return runComputeComparisonImpl(input, "-slang -gcompute"); } TestResult doRenderComparisonTestRun(TestInput& input, char const* langOption, char const* outputKind, String* outOutput) @@ -1411,12 +1484,48 @@ TestResult skipTest(TestInput& /*input*/) return kTestResult_Ignored; } +static bool hasD3D12Option(const TestOptions& testOptions) +{ + return (testOptions.args.IndexOf("-dx12") != UInt(-1) || + testOptions.args.IndexOf("-d3d12") != UInt(-1)); +} + +bool hasD3D12Option(const FileTestList& testList) +{ + const int numTests = int(testList.tests.Count()); + for (int i = 0; i < numTests; i++) + { + if (hasD3D12Option(testList.tests[i])) + { + return true; + } + } + return false; +} + +bool isRenderTest(const String& command) +{ + return command == "COMPARE_COMPUTE" || + command == "COMPARE_COMPUTE_EX" || + command == "HLSL_COMPUTE" || + command == "COMPARE_RENDER_COMPUTE" || + command == "COMPARE_HLSL_RENDER" || + command == "COMPARE_HLSL_CROSS_COMPILE_RENDER" || + command == "COMPARE_HLSL_GLSL_RENDER"; +} + TestResult runTest( String const& filePath, String const& outputStem, TestOptions const& testOptions, FileTestList const& testList) { + // If this is d3d12 test + if (hasD3D12Option(testOptions) && (options.enabledApis & RenderApiFlag::D3D12) == 0) + { + return kTestResult_Ignored; + } + // based on command name, dispatch to an appropriate callback struct TestCommands { @@ -1426,11 +1535,11 @@ TestResult runTest( static const TestCommands kTestCommands[] = { - { "SIMPLE", &runSimpleTest }, - { "REFLECTION", &runReflectionTest }, + { "SIMPLE", &runSimpleTest}, + { "REFLECTION", &runReflectionTest}, #if SLANG_TEST_SUPPORT_HLSL - { "COMPARE_HLSL", &runHLSLComparisonTest }, - { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest }, + { "COMPARE_HLSL", &runHLSLComparisonTest}, + { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest}, { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &runHLSLCrossCompileRenderComparisonTest}, { "COMPARE_HLSL_GLSL_RENDER", &runHLSLAndGLSLRenderComparisonTest }, { "COMPARE_COMPUTE", runSlangComputeComparisonTest}, @@ -1623,6 +1732,9 @@ bool testPassesCategoryMask( return false; } + + + void runTestsOnFile( TestContext* context, String filePath) @@ -1643,6 +1755,29 @@ void runTestsOnFile( return; } + // If dx12 is available synthesize Dx12 test + if ((options.enabledApis & RenderApiFlag::D3D12) != 0) + { + // If doesn't have option generate dx12 options from dx11 + if (!hasD3D12Option(testList)) + { + const int numTests = int(testList.tests.Count()); + for (int i = 0; i < numTests; i++) + { + const TestOptions& testOptions = testList.tests[i]; + // If it's a render test, and there is on d3d option, add one + if (isRenderTest(testOptions.command) && !hasD3D12Option(testOptions)) + { + // Add with -dx12 option + TestOptions testOptionsCopy(testOptions); + testOptionsCopy.args.Add("-dx12"); + + testList.tests.Add(testOptionsCopy); + } + } + } + } + // We have found a test to run! int subTestCount = 0; for( auto& tt : testList.tests ) diff --git a/tools/slang-test/render-api-util.cpp b/tools/slang-test/render-api-util.cpp new file mode 100644 index 000000000..b8697c54b --- /dev/null +++ b/tools/slang-test/render-api-util.cpp @@ -0,0 +1,210 @@ + +#include "render-api-util.h" + +#include "../../source/core/slang-defines.h" + +#include "../../source/core/list.h" +#include "../../source/core/slang-string-util.h" + +/* static */const RenderApiUtil::Info RenderApiUtil::s_infos[] = +{ + { RenderApiType::OpenGl, "gl,ogl,opengl"}, + { RenderApiType::Vulkan, "vk,vulkan"}, + { RenderApiType::D3D12, "dx12,d3d12"}, + { RenderApiType::D3D11, "dx11,d3d11"}, +}; + +static int _calcAvailableApis() +{ + int flags = 0; + for (int i = 0; i < int(RenderApiType::CountOf); i++) + { + if (RenderApiUtil::calcHasApi(RenderApiType(i))) + { + flags |= (1 << i); + } + } + + return flags; +} + +/* static */int RenderApiUtil::getAvailableApis() +{ + static int s_availableApis = _calcAvailableApis(); + return s_availableApis; +} + +/* static */RenderApiType RenderApiUtil::findApiTypeByName(const Slang::UnownedStringSlice& name) +{ + using namespace Slang; + List<UnownedStringSlice> namesList; + for (int j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++) + { + const auto& apiInfo = RenderApiUtil::s_infos[j]; + const UnownedStringSlice names(apiInfo.names); + + if (names.indexOf(',') >= 0) + { + StringUtil::split(names, ',', namesList); + if (namesList.IndexOf(name) != UInt(-1)) + { + return apiInfo.type; + } + } + else if (names == name) + { + return apiInfo.type; + } + } + return RenderApiType::Unknown; +} + +/* static */int RenderApiUtil::findApiFlagsByName(const Slang::UnownedStringSlice& name) +{ + // Special case 'all' + if (name == "all") + { + return int(RenderApiFlag::AllOf); + } + RenderApiType type = findApiTypeByName(name); + return (type == RenderApiType::Unknown) ? 0 : (1 << int(type)); +} + +/* static */Slang::Result RenderApiUtil::parseApiFlags(const Slang::UnownedStringSlice& text, int* apiBitsOut) +{ + using namespace Slang; + + int apiBits = 0; + + List<UnownedStringSlice> slices; + StringUtil::split(text, ',', slices); + + for (int i = 0; i < int(slices.Count()); ++i) + { + UnownedStringSlice slice = slices[i]; + bool add = true; + if (slice.size() <= 0) + { + return SLANG_FAIL; + } + if (slice[0] == '+') + { + // Drop the + + slice = UnownedStringSlice(slice.begin() + 1, slice.end()); + } + else if (slice[0] == '-') + { + add = false; + // Drop the + + slice = UnownedStringSlice(slice.begin() + 1, slice.end()); + } + + // We need to find the bits... + int bits = findApiFlagsByName(slice); + // 0 means an error + if (bits == 0) + { + return SLANG_FAIL; + } + + if (add) + { + apiBits |= bits; + } + else + { + apiBits &= ~bits; + } + } + + *apiBitsOut = apiBits; + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! Platform specific stuff !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +#if SLANG_WINDOWS_FAMILY + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +namespace { // anonymous + +class WinModule +{ +public: + + /// Initialize. Returns the module on success + HMODULE init(const char* name) + { + if (m_module) + { + ::FreeModule(m_module); + m_module = nullptr; + } + return m_module = ::LoadLibraryA(name); + } + + /// convert to HMODULE + SLANG_FORCE_INLINE operator HMODULE() const { return m_module; } + + /// True if loaded + bool isLoaded() const { return m_module != nullptr; } + + explicit WinModule(const char* name) : + m_module(nullptr) + { + init(name); + } + + /// Ctor + WinModule() :m_module(nullptr) {} + /// Dtor + ~WinModule() + { + if (m_module) + { + ::FreeLibrary(m_module); + } + } + +protected: + HMODULE m_module; +}; + +} // anonymous + +#else +#endif + +/* static */bool RenderApiUtil::calcHasApi(RenderApiType type) +{ +#if SLANG_WINDOWS_FAMILY + switch (type) + { + case RenderApiType::OpenGl: return WinModule("opengl32.dll").isLoaded(); + case RenderApiType::Vulkan: return WinModule("vulkan-1.dll").isLoaded(); + case RenderApiType::D3D11: return WinModule("d3d11.dll").isLoaded(); + case RenderApiType::D3D12: return WinModule("d3d12.dll").isLoaded(); + + default: return false; + } + +#else + + switch (type) + { + case RenderApiType::OpenGl: + case RenderApiType::Vulkan: + { + return true; + } + default: return false; + } +#endif + +} diff --git a/tools/slang-test/render-api-util.h b/tools/slang-test/render-api-util.h new file mode 100644 index 000000000..0f941f05d --- /dev/null +++ b/tools/slang-test/render-api-util.h @@ -0,0 +1,61 @@ +#ifndef SLANG_RENDER_API_UTIL_H +#define SLANG_RENDER_API_UTIL_H + +#include "../../source/core/slang-string.h" +#include "../../source/core/slang-result.h" + + +enum class RenderApiType +{ + Unknown = -1, + OpenGl = 0, + Vulkan, + D3D12, + D3D11, + CountOf, +}; + +// Use a struct wrapped Enum instead of enum class, cos we want to be able to manipulate as integrals +struct RenderApiFlag +{ + enum Enum + { + OpenGl = 1 << int(RenderApiType::OpenGl), + Vulkan = 1 << int(RenderApiType::Vulkan), + D3D12 = 1 << int(RenderApiType::D3D12), + D3D11 = 1 << int(RenderApiType::D3D11), + AllOf = (1 << int(RenderApiType::CountOf)) - 1 ///< All bits set + }; +}; + +struct RenderApiUtil +{ + struct Info + { + RenderApiType type; ///< The type + const char* names; ///< Comma separated list of names associated with the type + }; + + /// Returns true if the API is available. + static bool calcHasApi(RenderApiType type); + + /// Returns a combination of RenderApiFlag bits which if set indicates that the API is available. + static int getAvailableApis(); + + /// Returns -1 if unknown + static RenderApiType findApiTypeByName(const Slang::UnownedStringSlice& name); + /// Returns 0 if none found. + static int findApiFlagsByName(const Slang::UnownedStringSlice& name); + + /// Parse api flags string (comma delimited list of api names, or 'all' for all) + /// For example "all,-dx12" would be all apis, except dx12 + static Slang::Result parseApiFlags(const Slang::UnownedStringSlice& text, int* apiBitsOut); + + /// Get information about a render API + static const Info& getInfo(RenderApiType type) { return s_infos[int(type)]; } + + /// Static information about each render api + static const Info s_infos[int(RenderApiType::CountOf)]; +}; + +#endif // SLANG_RENDER_API_UTIL_H
\ No newline at end of file diff --git a/tools/slang-test/slang-test.vcxproj b/tools/slang-test/slang-test.vcxproj index fc4e88493..34d9bed40 100644 --- a/tools/slang-test/slang-test.vcxproj +++ b/tools/slang-test/slang-test.vcxproj @@ -160,9 +160,11 @@ <ItemGroup> <ClCompile Include="main.cpp" /> <ClCompile Include="os.cpp" /> + <ClCompile Include="render-api-util.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="os.h" /> + <ClInclude Include="render-api-util.h" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\source\core\core.vcxproj"> diff --git a/tools/slang-test/slang-test.vcxproj.filters b/tools/slang-test/slang-test.vcxproj.filters index 3549e6046..e9342cfd6 100644 --- a/tools/slang-test/slang-test.vcxproj.filters +++ b/tools/slang-test/slang-test.vcxproj.filters @@ -21,10 +21,16 @@ <ClCompile Include="os.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="render-api-util.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="os.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="render-api-util.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project>
\ No newline at end of file |
