From f33aee2be9017934140da9fdfb0dcde15568b3a8 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Thu, 7 Mar 2019 16:31:56 -0500 Subject: Fix problems with synthesized tests and inconsitent render-test command lines (#885) * * Check for inconsistent command line options for renderer * Moved RenderApiUtil into core so can be used in slang-test * Make it use the ShaderdLibrary for API testsing * Added some simplifying functions to StringUtil for spliting/comparisons * Refactored the synthesis of rendering tests so that inconsistent combinations are not produced * Add missing slang-render-api-util.cpp & .h * Stop warning on linux about _canLoadSharedLibrary not being used. --- source/core/core.vcxproj | 2 + source/core/core.vcxproj.filters | 6 + source/core/slang-render-api-util.cpp | 286 ++++++++++++++++++++++++++++ source/core/slang-render-api-util.h | 78 ++++++++ source/core/slang-string-util.cpp | 52 +++++ source/core/slang-string-util.h | 8 + source/slang/slang.vcxproj | 1 + source/slang/slang.vcxproj.filters | 3 + tools/render-test/options.cpp | 106 +++++------ tools/render-test/options.h | 3 + tools/slang-test/options.h | 9 +- tools/slang-test/render-api-util.cpp | 286 ---------------------------- tools/slang-test/render-api-util.h | 64 ------- tools/slang-test/slang-test-main.cpp | 176 ++++++++++------- tools/slang-test/slang-test.vcxproj | 2 - tools/slang-test/slang-test.vcxproj.filters | 6 - 16 files changed, 608 insertions(+), 480 deletions(-) create mode 100644 source/core/slang-render-api-util.cpp create mode 100644 source/core/slang-render-api-util.h delete mode 100644 tools/slang-test/render-api-util.cpp delete mode 100644 tools/slang-test/render-api-util.h diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index bebf7350c..b89e2acff 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -190,6 +190,7 @@ + @@ -211,6 +212,7 @@ + diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters index dd33b61f9..2faaa7137 100644 --- a/source/core/core.vcxproj.filters +++ b/source/core/core.vcxproj.filters @@ -69,6 +69,9 @@ Header Files + + Header Files + Header Files @@ -128,6 +131,9 @@ Source Files + + Source Files + Source Files diff --git a/source/core/slang-render-api-util.cpp b/source/core/slang-render-api-util.cpp new file mode 100644 index 000000000..80f2225d9 --- /dev/null +++ b/source/core/slang-render-api-util.cpp @@ -0,0 +1,286 @@ + +#include "slang-render-api-util.h" + +#include "../../slang.h" + +#include "../../source/core/list.h" +#include "../../source/core/slang-string-util.h" + +#include "platform.h" + +namespace Slang { + +// NOTE! Must keep in same order as RenderApiType and have same amount of entries +/* static */const RenderApiUtil::Info RenderApiUtil::s_infos[] = +{ + { RenderApiType::OpenGl, "gl,ogl,opengl", "glsl,glsl-rewrite,glsl-cross"}, + { RenderApiType::Vulkan, "vk,vulkan", ""}, + { RenderApiType::D3D12, "dx12,d3d12", ""}, + { RenderApiType::D3D11, "dx11,d3d11", "hlsl,hlsl-rewrite,slang"}, +}; + +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; +} + +UnownedStringSlice RenderApiUtil::getApiName(RenderApiType type) +{ + int index = int(type); + if (index < 0 || index >= int(RenderApiType::CountOf)) + { + return UnownedStringSlice(); + } + SLANG_ASSERT(s_infos[index].type == type); + return StringUtil::getAtInSplit(UnownedStringSlice(s_infos[index].names), ',', 0); +} + +/* static */RenderApiType RenderApiUtil::findApiTypeByName(const Slang::UnownedStringSlice& name) +{ + using namespace Slang; + List 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 */ Slang::Result RenderApiUtil::findApiFlagsByName(const Slang::UnownedStringSlice& name, RenderApiFlags* flagsOut) +{ + // Special case 'all' + if (name == "all") + { + *flagsOut = RenderApiFlags(RenderApiFlag::AllOf); + return SLANG_OK; + } + if (name == "none") + { + *flagsOut = RenderApiFlags(0); + return SLANG_OK; + } + RenderApiType type = findApiTypeByName(name); + if (type == RenderApiType::Unknown) + { + return SLANG_FAIL; + } + *flagsOut = RenderApiFlags(1) << int(type); + return SLANG_OK; +} + +static bool isNameStartChar(char c) +{ + return (c >= 'a' && c <='z') || (c >= 'A' && c <= 'Z') || (c == '_'); +} + +static bool isNameNextChar(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c >= '0' && c <= '9'); +} + +namespace { // anonymous +enum class Token +{ + eError, + eOp, + eId, + eEnd, +}; +} + +static Token nextToken(Slang::UnownedStringSlice& textInOut, Slang::UnownedStringSlice& lexemeOut) +{ + using namespace Slang; + if (textInOut.size() <= 0) + { + return Token::eEnd; + } + const char* start = textInOut.begin(); + const char* end = textInOut.end(); + + const char firstChar = start[0]; + if (firstChar == '-' || firstChar == '+') + { + lexemeOut = UnownedStringSlice(start, start + 1); + textInOut = UnownedStringSlice(start + 1, end); + return Token::eOp; + } + + if (!isNameStartChar(firstChar)) + { + lexemeOut = UnownedStringSlice(start, start + 1); + return Token::eError; + } + const char* cur = start + 1; + while (cur < end && isNameNextChar(*cur)) + { + cur++; + } + + lexemeOut = UnownedStringSlice(start, cur); + textInOut = UnownedStringSlice(cur, end); + return Token::eId; +} + +/* static */Slang::Result RenderApiUtil::parseApiFlags(const Slang::UnownedStringSlice& textIn, RenderApiFlags initialFlags, RenderApiFlags* apiFlagsOut) +{ + using namespace Slang; + + UnownedStringSlice text(textIn); + UnownedStringSlice lexeme; + + RenderApiFlags apiFlags = 0; + + switch (nextToken(text, lexeme)) + { + case Token::eOp: + { + // If we start with an op - we use the passed in values as the default + // Rewind back to the start + text = textIn; + apiFlags = initialFlags; + break; + } + case Token::eId: + { + // If we start with an Id - we use that as the starting state + SLANG_RETURN_ON_FAIL(findApiFlagsByName(lexeme, &apiFlags)); + break; + } + default: return SLANG_FAIL; + } + + while (true) + { + // Must have an op followed by an id unless we are at the end + switch (nextToken(text, lexeme)) + { + case Token::eEnd: + { + *apiFlagsOut = apiFlags; + return SLANG_OK; + } + case Token::eOp: break; + default: return SLANG_FAIL; + } + + const char op = lexeme[0]; + if (nextToken(text, lexeme) != Token::eId) + { + return SLANG_FAIL; + } + + RenderApiFlags flags; + SLANG_RETURN_ON_FAIL(findApiFlagsByName(lexeme, &flags)); + + if (op == '+') + { + apiFlags |= flags; + } + else + { + apiFlags &= ~flags; + } + } +} + +/* static */RenderApiType RenderApiUtil::findRenderApiType(const Slang::UnownedStringSlice& text) +{ + using namespace Slang; + for (int j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++) + { + const auto& apiInfo = RenderApiUtil::s_infos[j]; + if (StringUtil::indexOfInSplit(UnownedStringSlice(apiInfo.names), ',', text) >= 0) + { + return apiInfo.type; + } + } + // Didn't find any + return RenderApiType::Unknown; +} + +/* static */RenderApiType RenderApiUtil::findImplicitLanguageRenderApiType(const Slang::UnownedStringSlice& text) +{ + using namespace Slang; + for (int j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++) + { + const auto& apiInfo = RenderApiUtil::s_infos[j]; + if (StringUtil::indexOfInSplit(UnownedStringSlice(apiInfo.languageNames), ',', text) >= 0) + { + return apiInfo.type; + } + } + // Didn't find any + return RenderApiType::Unknown; +} + +#if SLANG_WINDOWS_FAMILY +static bool _canLoadSharedLibrary(const char* libName) +{ + SharedLibrary::Handle handle; + SlangResult res = SharedLibrary::load(libName, handle); + if (SLANG_FAILED(res)) + { + return false; + } + SharedLibrary::unload(handle); + return true; +} +#endif + +/* static */bool RenderApiUtil::calcHasApi(RenderApiType type) +{ +#if SLANG_WINDOWS_FAMILY + switch (type) + { + case RenderApiType::OpenGl: return _canLoadSharedLibrary("opengl32.dll"); + case RenderApiType::Vulkan: return _canLoadSharedLibrary("vulkan-1.dll"); + case RenderApiType::D3D11: return _canLoadSharedLibrary("d3d11.dll"); + case RenderApiType::D3D12: return _canLoadSharedLibrary("d3d12.dll"); + default: break; + } +#elif SLANG_UNIX_FAMILY + // Assume on unix target we have Opengl and Vulkan for now + switch (type) + { + case RenderApiType::OpenGl: + case RenderApiType::Vulkan: + { + return true; + } + default: break; + } +#endif + return false; +} + +} // namespace Slang diff --git a/source/core/slang-render-api-util.h b/source/core/slang-render-api-util.h new file mode 100644 index 000000000..42e88a6ac --- /dev/null +++ b/source/core/slang-render-api-util.h @@ -0,0 +1,78 @@ +#ifndef SLANG_RENDER_API_UTIL_H +#define SLANG_RENDER_API_UTIL_H + +#include "../../source/core/slang-string.h" + +#include "../../slang-com-helper.h" + +namespace Slang +{ + +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 + }; +}; +typedef uint32_t RenderApiFlags; + +struct RenderApiUtil +{ + struct Info + { + RenderApiType type; ///< The type + const char* names; ///< Comma separated list of names associated with the type + const char* languageNames; ///< Comma separated list of target language 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(); + + /// Get the name + static UnownedStringSlice getApiName(RenderApiType type); + + /// Returns RenderApiType::Unknown if not found + static RenderApiType findApiTypeByName(const Slang::UnownedStringSlice& name); + /// FlagsOut will have flag/flags specified by a name if returns with successful result code. + static Slang::Result findApiFlagsByName(const Slang::UnownedStringSlice& name, RenderApiFlags* flagsOut); + + /// Parse api flags string, returning SLANG_OK on success. + /// If first character is + or - the flags will be applied to initialFlags, else initialFlags is ignored. + /// For example "all-dx12" would be all apis, except dx12 + /// -vk would be whatever is in initial flags, but not vulkan. + static Slang::Result parseApiFlags(const Slang::UnownedStringSlice& text, RenderApiFlags initialFlags, RenderApiFlags* apiBitsOut); + + /// Gets the API type from a string, or returns RenderApiType::Unknown if not found + static RenderApiType findRenderApiType(const Slang::UnownedStringSlice& text); + + static RenderApiType findImplicitLanguageRenderApiType(const Slang::UnownedStringSlice& text); + + /// 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)]; +}; + +} // namespace Slang + +#endif // SLANG_RENDER_API_UTIL_H diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp index 5d3e2188d..b7c6780e1 100644 --- a/source/core/slang-string-util.cpp +++ b/source/core/slang-string-util.cpp @@ -39,6 +39,58 @@ static const Guid IID_ISlangBlob = SLANG_UUID_ISlangBlob; } } +/* static */int StringUtil::indexOfInSplit(const UnownedStringSlice& in, char splitChar, const UnownedStringSlice& find) +{ + const char* start = in.begin(); + const char* end = in.end(); + + for (int i = 0; start < end; ++i) + { + // Move cur so it's either at the end or at next split character + const char* cur = start; + while (cur < end && *cur != splitChar) + { + cur++; + } + + // See if we have a match + if (UnownedStringSlice(start, cur) == find) + { + return i; + } + + // Skip the split character, if at end we are okay anyway + start = cur + 1; + } + return -1; +} + +UnownedStringSlice StringUtil::getAtInSplit(const UnownedStringSlice& in, char splitChar, int index) +{ + const char* start = in.begin(); + const char* end = in.end(); + + for (int i = 0; start < end; ++i) + { + // Move cur so it's either at the end or at next split character + const char* cur = start; + while (cur < end && *cur != splitChar) + { + cur++; + } + + if (i == index) + { + return UnownedStringSlice(start, cur); + } + + // Skip the split character, if at end we are okay anyway + start = cur + 1; + } + + return UnownedStringSlice(); +} + /* static */size_t StringUtil::calcFormattedSize(const char* format, va_list args) { #if SLANG_WINDOWS_FAMILY diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h index 3547fd97c..3288899fc 100644 --- a/source/core/slang-string-util.h +++ b/source/core/slang-string-util.h @@ -41,6 +41,14 @@ struct StringUtil /// Slices contents will directly address into in, so contents will only stay valid as long as in does. static void split(const UnownedStringSlice& in, char splitChar, List& slicesOut); + /// Equivalent to doing a split and then finding the index of 'find' on the array + /// Returns -1 if not found + static int indexOfInSplit(const UnownedStringSlice& in, char splitChar, const UnownedStringSlice& find); + + /// Given the entry at the split index specified. + /// Will return slice with begin() == nullptr if not found or input has begin() == nullptr) + static UnownedStringSlice getAtInSplit(const UnownedStringSlice& in, char splitChar, int index); + /// Returns the size in bytes needed to hold the formatted string using the specified args, NOT including a terminating 0 /// NOTE! The caller *should* assume this will consume the va_list (use va_copy to make a copy to be consumed) static size_t calcFormattedSize(const char* format, va_list args); diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index ff37c5e09..40306899f 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -224,6 +224,7 @@ + diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index f9514ce99..5afd32765 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -171,6 +171,9 @@ Header Files + + Header Files + Header Files diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index bd5640020..965a81e27 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -7,6 +7,7 @@ #include #include "../../source/core/slang-writer.h" +#include "../../source/core/slang-render-api-util.h" namespace renderer_test { @@ -14,14 +15,34 @@ static const Options gDefaultOptions; Options gOptions; -// Only set it, if the -void setDefaultRendererType(RendererType type) +static gfx::RendererType _toRenderType(Slang::RenderApiType apiType) { - gOptions.rendererType = (gOptions.rendererType == RendererType::Unknown) ? type : gOptions.rendererType; + using namespace Slang; + switch (apiType) + { + case RenderApiType::D3D11: return gfx::RendererType::DirectX11; + case RenderApiType::D3D12: return gfx::RendererType::DirectX12; + case RenderApiType::OpenGl: return gfx::RendererType::OpenGl; + case RenderApiType::Vulkan: return gfx::RendererType::Vulkan; + default: return gfx::RendererType::Unknown; + } +} + +static SlangResult _setRendererType(RendererType type, const char* arg, Slang::WriterHelper stdError) +{ + if (gOptions.rendererType != RendererType::Unknown) + { + stdError.print("Already has renderer option set. Found '%s'\n", arg); + return SLANG_FAIL; + } + gOptions.rendererType = type; + return SLANG_OK; } SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper stdError) { + using namespace Slang; + // Reset the options gOptions = gDefaultOptions; @@ -68,36 +89,6 @@ SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper s } gOptions.outputPath = *argCursor++; } - else if( strcmp(arg, "-hlsl") == 0 ) - { - setDefaultRendererType( RendererType::DirectX11); - gOptions.inputLanguageID = InputLanguageID::Native; - } - else if( strcmp(arg, "-glsl") == 0 ) - { - setDefaultRendererType( RendererType::OpenGl); - gOptions.inputLanguageID = InputLanguageID::Native; - } - else if( strcmp(arg, "-hlsl-rewrite") == 0 ) - { - setDefaultRendererType( RendererType::DirectX11); - gOptions.inputLanguageID = InputLanguageID::Slang; - } - else if( strcmp(arg, "-glsl-rewrite") == 0 ) - { - setDefaultRendererType(RendererType::OpenGl); - gOptions.inputLanguageID = InputLanguageID::Slang; - } - else if( strcmp(arg, "-slang") == 0 ) - { - setDefaultRendererType( RendererType::DirectX11); - gOptions.inputLanguageID = InputLanguageID::Slang; - } - else if( strcmp(arg, "-glsl-cross") == 0 ) - { - setDefaultRendererType(RendererType::OpenGl); - gOptions.inputLanguageID = InputLanguageID::Slang; - } else if( strcmp(arg, "-xslang") == 0 ) { // This is an option that we want to pass along to Slang @@ -126,37 +117,44 @@ SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper s { gOptions.shaderType = ShaderProgramType::GraphicsCompute; } - else if (strcmp(arg, "-vk") == 0 - || strcmp(arg, "-vulkan") == 0) - { - gOptions.rendererType = RendererType::Vulkan; - } - else if (strcmp(arg, "-d3d12") == 0 - || strcmp(arg, "-dx12") == 0) - { - gOptions.rendererType = RendererType::DirectX12; - } - else if(strcmp(arg, "-gl") == 0) - { - gOptions.rendererType = RendererType::OpenGl; - } - else if (strcmp(arg, "-d3d11") == 0 - || strcmp(arg, "-dx11") == 0) - { - gOptions.rendererType = RendererType::DirectX11; - } else if( strcmp(arg, "-use-dxil") == 0 ) { gOptions.useDXIL = true; } else { + // Lookup + Slang::UnownedStringSlice argSlice(arg); + if (argSlice.size() && argSlice[0] == '-') + { + // Look up the rendering API if set + UnownedStringSlice argName = UnownedStringSlice(argSlice.begin() + 1, argSlice.end()); + RendererType rendererType = _toRenderType(RenderApiUtil::findApiTypeByName(argName)); + + if (rendererType != RendererType::Unknown) + { + gOptions.rendererType = rendererType; + continue; + } + + // Lookup the target language type + RendererType languageRenderType = _toRenderType(RenderApiUtil::findImplicitLanguageRenderApiType(argName)); + if (languageRenderType != RendererType::Unknown) + { + gOptions.targetLanguageRendererType = languageRenderType; + gOptions.inputLanguageID = (argName == "hlsl" || argName == "glsl") ? InputLanguageID::Native : InputLanguageID::Slang; + continue; + } + } + stdError.print("unknown option '%s'\n", arg); return SLANG_FAIL; } } - - + + // If a render option isn't set use defaultRenderType + gOptions.rendererType = (gOptions.rendererType == RendererType::Unknown) ? gOptions.targetLanguageRendererType : gOptions.rendererType; + // first positional argument is source shader path if(positionalArgs.Count()) { diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 09721def3..4f0f900b1 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -42,6 +42,9 @@ struct Options char const* outputPath = nullptr; ShaderProgramType shaderType = ShaderProgramType::Graphics; + /// The renderer type inferred from the target language type. Used if a rendererType is not explicitly set. + RendererType targetLanguageRendererType = RendererType::Unknown; + /// The set render type RendererType rendererType = RendererType::Unknown; InputLanguageID inputLanguageID = InputLanguageID::Slang; diff --git a/tools/slang-test/options.h b/tools/slang-test/options.h index 77460e190..58b8e0fb2 100644 --- a/tools/slang-test/options.h +++ b/tools/slang-test/options.h @@ -6,7 +6,7 @@ #include "../../source/core/dictionary.h" #include "test-reporter.h" -#include "render-api-util.h" +#include "../../source/core/slang-render-api-util.h" #include "../../source/core/smart-pointer.h" // A category that a test can be tagged with @@ -73,7 +73,7 @@ struct Options Slang::Dictionary excludeCategories; // By default we can test against all apis - RenderApiFlags enabledApis = RenderApiFlag::AllOf; + Slang::RenderApiFlags enabledApis = Slang::RenderApiFlag::AllOf; // The subCommand to execute. Will be empty if there is no subCommand Slang::String subCommand; @@ -82,8 +82,9 @@ struct Options Slang::List subCommandArgs; // By default we potentially synthesize test for all - // TODO: Vulkan is disabled by default for now as the majority as vulkan synthesized tests fail - RenderApiFlags synthesizedTestApis = RenderApiFlag::AllOf & ~RenderApiFlag::Vulkan; + // TODO: Vulkan is disabled by default for now as the majority as vulkan synthesized tests + // OpenGL is disabled for now + Slang::RenderApiFlags synthesizedTestApis = Slang::RenderApiFlag::AllOf & ~(Slang::RenderApiFlag::Vulkan | Slang::RenderApiFlag::OpenGl); /// Parse the args, report any errors into stdError, and write the results into optionsOut static SlangResult parse(int argc, char** argv, TestCategorySet* categorySet, Slang::WriterHelper stdError, Options* optionsOut); diff --git a/tools/slang-test/render-api-util.cpp b/tools/slang-test/render-api-util.cpp deleted file mode 100644 index 06a915df8..000000000 --- a/tools/slang-test/render-api-util.cpp +++ /dev/null @@ -1,286 +0,0 @@ - -#include "render-api-util.h" - -#include "../../slang.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 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 */ Slang::Result RenderApiUtil::findApiFlagsByName(const Slang::UnownedStringSlice& name, RenderApiFlags* flagsOut) -{ - // Special case 'all' - if (name == "all") - { - *flagsOut = RenderApiFlags(RenderApiFlag::AllOf); - return SLANG_OK; - } - if (name == "none") - { - *flagsOut = RenderApiFlags(0); - return SLANG_OK; - } - RenderApiType type = findApiTypeByName(name); - if (type == RenderApiType::Unknown) - { - return SLANG_FAIL; - } - *flagsOut = RenderApiFlags(1) << int(type); - return SLANG_OK; -} - -static bool isNameStartChar(char c) -{ - return (c >= 'a' && c <='z') || (c >= 'A' && c <= 'Z') || (c == '_'); -} - -static bool isNameNextChar(char c) -{ - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c >= '0' && c <= '9'); -} - -namespace { // anonymous -enum class Token -{ - eError, - eOp, - eId, - eEnd, -}; -} - -static Token nextToken(Slang::UnownedStringSlice& textInOut, Slang::UnownedStringSlice& lexemeOut) -{ - using namespace Slang; - if (textInOut.size() <= 0) - { - return Token::eEnd; - } - const char* start = textInOut.begin(); - const char* end = textInOut.end(); - - const char firstChar = start[0]; - if (firstChar == '-' || firstChar == '+') - { - lexemeOut = UnownedStringSlice(start, start + 1); - textInOut = UnownedStringSlice(start + 1, end); - return Token::eOp; - } - - if (!isNameStartChar(firstChar)) - { - lexemeOut = UnownedStringSlice(start, start + 1); - return Token::eError; - } - const char* cur = start + 1; - while (cur < end && isNameNextChar(*cur)) - { - cur++; - } - - lexemeOut = UnownedStringSlice(start, cur); - textInOut = UnownedStringSlice(cur, end); - return Token::eId; -} - -/* static */Slang::Result RenderApiUtil::parseApiFlags(const Slang::UnownedStringSlice& textIn, RenderApiFlags initialFlags, RenderApiFlags* apiFlagsOut) -{ - using namespace Slang; - - UnownedStringSlice text(textIn); - UnownedStringSlice lexeme; - - RenderApiFlags apiFlags = 0; - - switch (nextToken(text, lexeme)) - { - case Token::eOp: - { - // If we start with an op - we use the passed in values as the default - // Rewind back to the start - text = textIn; - apiFlags = initialFlags; - break; - } - case Token::eId: - { - // If we start with an Id - we use that as the starting state - SLANG_RETURN_ON_FAIL(findApiFlagsByName(lexeme, &apiFlags)); - break; - } - default: return SLANG_FAIL; - } - - while (true) - { - // Must have an op followed by an id unless we are at the end - switch (nextToken(text, lexeme)) - { - case Token::eEnd: - { - *apiFlagsOut = apiFlags; - return SLANG_OK; - } - case Token::eOp: break; - default: return SLANG_FAIL; - } - - const char op = lexeme[0]; - if (nextToken(text, lexeme) != Token::eId) - { - return SLANG_FAIL; - } - - RenderApiFlags flags; - SLANG_RETURN_ON_FAIL(findApiFlagsByName(lexeme, &flags)); - - if (op == '+') - { - apiFlags |= flags; - } - else - { - apiFlags &= ~flags; - } - } -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! Platform specific stuff !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - - -#if SLANG_WINDOWS_FAMILY - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#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 deleted file mode 100644 index 1c3c81254..000000000 --- a/tools/slang-test/render-api-util.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef SLANG_RENDER_API_UTIL_H -#define SLANG_RENDER_API_UTIL_H - -#include "../../source/core/slang-string.h" - -#include "../../slang-com-helper.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 - }; -}; -typedef uint32_t RenderApiFlags; - -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 RenderApiType::Unknown if not found - static RenderApiType findApiTypeByName(const Slang::UnownedStringSlice& name); - /// FlagsOut will have flag/flags specified by a name if returns with successful result code. - static Slang::Result findApiFlagsByName(const Slang::UnownedStringSlice& name, RenderApiFlags* flagsOut); - - /// Parse api flags string, returning SLANG_OK on success. - /// If first character is + or - the flags will be applied to initialFlags, else initialFlags is ignored. - /// For example "all-dx12" would be all apis, except dx12 - /// -vk would be whatever is in initial flags, but not vulkan. - static Slang::Result parseApiFlags(const Slang::UnownedStringSlice& text, RenderApiFlags initialFlags, RenderApiFlags* 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-main.cpp b/tools/slang-test/slang-test-main.cpp index cbd2974da..285f77a72 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -7,11 +7,12 @@ #include "../../slang-com-helper.h" #include "../../source/core/slang-string-util.h" +#include "../../source/core/slang-byte-encode-util.h" using namespace Slang; #include "os.h" -#include "render-api-util.h" +#include "../../source/core/slang-render-api-util.h" #include "test-context.h" #include "test-reporter.h" #include "options.h" @@ -1437,6 +1438,12 @@ TestResult skipTest(TestContext* /* context */, TestInput& /*input*/) static bool hasRenderOption(RenderApiType apiType, const List& options) { + SLANG_ASSERT(apiType != RenderApiType::Unknown); + if (apiType == RenderApiType::Unknown) + { + return false; + } + const RenderApiUtil::Info& info = RenderApiUtil::getInfo(apiType); List namesList; @@ -1448,25 +1455,12 @@ static bool hasRenderOption(RenderApiType apiType, const List& options) if (option.StartsWith("-")) { const UnownedStringSlice parameter(option.Buffer() + 1, option.Buffer() + option.Length()); - // See if we have a match - for (int j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++) - { - const auto& apiInfo = RenderApiUtil::s_infos[j]; - const UnownedStringSlice names(info.names); + const RenderApiType paramType = RenderApiUtil::findApiTypeByName(parameter); - if (names.indexOf(',') >= 0) - { - StringUtil::split(names, ',', namesList); - - if (namesList.IndexOf(parameter) != UInt(-1)) - { - return true; - } - } - else if (names == parameter) - { - return true; - } + // Found it + if (apiType == paramType) + { + return true; } } } @@ -1656,6 +1650,74 @@ bool testPassesCategoryMask( return false; } +static RenderApiType _findRenderApi(const List& args, bool onlyExplicit) +{ + RenderApiType targetLanguageRenderer = RenderApiType::Unknown; + + for (const auto& arg: args) + { + const UnownedStringSlice argSlice = arg.getUnownedSlice(); + if (argSlice.size() && argSlice[0] == '-' && !argSlice.startsWith(UnownedStringSlice::fromLiteral("--"))) + { + UnownedStringSlice argName(argSlice.begin() + 1, argSlice.end()); + + RenderApiType renderType = RenderApiUtil::findRenderApiType(argName); + if (renderType != RenderApiType::Unknown) + { + return renderType; + } + + if (!onlyExplicit) + { + RenderApiType implicitRenderType = RenderApiUtil::findImplicitLanguageRenderApiType(argName); + if (implicitRenderType != RenderApiType::Unknown) + { + targetLanguageRenderer = implicitRenderType; + } + } + } + } + + return targetLanguageRenderer; +} + +static void _addSythesizedTest(RenderApiType rendererType, const List& renderTests, List& outSynthesizedTests) +{ + for (const auto& test : renderTests) + { + // If doesn't have an explicit render api, add one and add to the synthesized tests + RenderApiType explicitRenderer = _findRenderApi(test.args, true); + + if (explicitRenderer == RenderApiType::Unknown) + { + // Add the explicit parameter + + TestOptions options(test); + + StringBuilder builder; + builder << "-"; + builder << RenderApiUtil::getApiName(rendererType); + + options.args.Add(builder); + + // If the target is vulkan remove the -hlsl option + if (rendererType == RenderApiType::Vulkan) + { + UInt index = options.args.IndexOf("-hlsl"); + if (index != UInt(-1)) + { + options.args.RemoveAt(index); + } + } + + // Add to the tests + outSynthesizedTests.Add(options); + + return; + } + } +} + void runTestsOnFile( TestContext* context, String filePath) @@ -1676,64 +1738,50 @@ void runTestsOnFile( return; } - List synthesizedTests; - - // If dx12 is available synthesize Dx12 test - if ((context->options.synthesizedTestApis & RenderApiFlag::D3D12) != 0) + // If synthesized tests are wanted look into adding them + if (context->options.synthesizedTestApis) { - // If doesn't have option generate dx12 options from dx11 - if (!hasRenderOption(RenderApiType::D3D12, testList)) + List synthesizedTests; + + // Lets find all tests which are render tests + RenderApiFlags apisUsed = 0; + List renderTests; + + const int numTests = int(testList.tests.Count()); + for (int i = 0; i < numTests; i++) { - const int numTests = int(testList.tests.Count()); - for (int i = 0; i < numTests; i++) + const TestOptions& testOptions = testList.tests[i]; + if (isRenderTest(testOptions.command)) { - const TestOptions& testOptions = testList.tests[i]; - // If it's a render test, and there is on d3d option, add one - if (isRenderTest(testOptions.command) && !hasRenderOption(RenderApiType::D3D12, testOptions)) + RenderApiType renderType = _findRenderApi(testOptions.args, false); + if (renderType != RenderApiType::Unknown) { - // Add with -dx12 option - TestOptions testOptionsCopy(testOptions); - testOptionsCopy.args.Add("-dx12"); - - synthesizedTests.Add(testOptionsCopy); + apisUsed |= (1 << int(renderType)); } + renderTests.Add(testOptions); } } - } + // What render options do we want to synthesize + RenderApiFlags missingApis = (~apisUsed) & context->options.synthesizedTestApis; + + // We can only synthesize if if there isn't an explicit render option - // If Vulkan is available synthesize Vulkan test - if ((context->options.synthesizedTestApis & RenderApiFlag::Vulkan) != 0) - { - // If doesn't have option generate dx12 options from dx11 - if (!hasRenderOption(RenderApiType::Vulkan, testList)) + while (missingApis) { - 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) && !isHLSLTest(testOptions.command) && !hasRenderOption(RenderApiType::Vulkan, testOptions)) - { - // Add with -vk option - TestOptions testOptionsCopy(testOptions); - testOptionsCopy.args.Add("-vk"); + const int index = ByteEncodeUtil::calcMsb8(missingApis); + SLANG_ASSERT(index >= 0 && index <= int(RenderApiType::CountOf)); - UInt index = testOptionsCopy.args.IndexOf("-hlsl"); - if (index != UInt(-1)) - { - testOptionsCopy.args.RemoveAt(index); - } + _addSythesizedTest(RenderApiType(index), renderTests, synthesizedTests); - synthesizedTests.Add(testOptionsCopy); - } - } + // Disable the bit + missingApis &= ~(RenderApiFlags(1) << index); } - } - // Add any tests that were synthesized - for (UInt i = 0; i < synthesizedTests.Count(); ++i) - { - testList.tests.Add(synthesizedTests[i]); + // Add any tests that were synthesized + for (UInt i = 0; i < synthesizedTests.Count(); ++i) + { + testList.tests.Add(synthesizedTests[i]); + } } // We have found a test to run! diff --git a/tools/slang-test/slang-test.vcxproj b/tools/slang-test/slang-test.vcxproj index 520ee1759..62b79d627 100644 --- a/tools/slang-test/slang-test.vcxproj +++ b/tools/slang-test/slang-test.vcxproj @@ -164,7 +164,6 @@ - @@ -172,7 +171,6 @@ - diff --git a/tools/slang-test/slang-test.vcxproj.filters b/tools/slang-test/slang-test.vcxproj.filters index be03d5273..e50c56819 100644 --- a/tools/slang-test/slang-test.vcxproj.filters +++ b/tools/slang-test/slang-test.vcxproj.filters @@ -15,9 +15,6 @@ Header Files - - Header Files - Header Files @@ -35,9 +32,6 @@ Source Files - - Source Files - Source Files -- cgit v1.2.3