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 + 6 files changed, 432 insertions(+) create mode 100644 source/core/slang-render-api-util.cpp create mode 100644 source/core/slang-render-api-util.h (limited to 'source/core') 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); -- cgit v1.2.3