From 17c6c6044965629a3ae7e8ef004cc0b2ca657c55 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Tue, 4 Feb 2020 15:19:48 -0500 Subject: CUDA/C++ backend improvements (#1198) * WIP with vector float test. * vector-float test working. * Fixed remaing tests broken with init changes. * Improve 64bit-type-support.md * Disable tests broken on CI system for Dx. * WIP: Make type available for comparison. * Moved type conversion into TypeTextUtil. * Add text/type conversions from DownstreamCompiler to TypeTextUtil. * Allow compaison taking into account type. * Removed quantize in vector-float.slang test. --- docs/64bit-type-support.md | 20 +- source/core/core.vcxproj | 2 + source/core/core.vcxproj.filters | 6 + source/core/slang-downstream-compiler.cpp | 105 +------ source/core/slang-downstream-compiler.h | 11 - source/core/slang-type-text-util.cpp | 165 +++++++++++ source/core/slang-type-text-util.h | 38 +++ source/core/slang-writer.cpp | 5 + source/core/slang-writer.h | 1 + source/slang/slang-check.cpp | 4 +- source/slang/slang-emit-cpp.cpp | 327 ++++++++++++++------- source/slang/slang-emit-cpp.h | 3 +- source/slang/slang-emit-cuda.cpp | 16 +- source/slang/slang-hlsl-intrinsic-set.cpp | 34 ++- source/slang/slang-hlsl-intrinsic-set.h | 26 +- source/slang/slang-options.cpp | 10 +- source/slang/slang-state-serialize.cpp | 3 +- tests/bugs/dxbc-double-problem.slang.expected.txt | 1 + .../scalar-double-d3d-intrinsic.slang | 5 +- .../scalar-double-d3d-intrinsic.slang.expected.txt | 1 + .../scalar-double-vk-intrinsic.slang.expected.txt | 1 + .../scalar-int64-literal-problem.slang | 7 +- ...scalar-int64-literal-problem.slang.expected.txt | 8 + tests/hlsl-intrinsic/vector-float.slang | 103 +++++++ .../hlsl-intrinsic/vector-float.slang.expected.txt | 17 ++ tools/render-test/options.cpp | 6 +- tools/render-test/shader-input-layout.cpp | 21 +- tools/render-test/shader-input-layout.h | 1 + tools/slang-test/slang-test-main.cpp | 179 +++++++++-- 29 files changed, 836 insertions(+), 290 deletions(-) create mode 100644 source/core/slang-type-text-util.cpp create mode 100644 source/core/slang-type-text-util.h create mode 100644 tests/hlsl-intrinsic/vector-float.slang create mode 100644 tests/hlsl-intrinsic/vector-float.slang.expected.txt diff --git a/docs/64bit-type-support.md b/docs/64bit-type-support.md index 7c345d4af..f934c97fe 100644 --- a/docs/64bit-type-support.md +++ b/docs/64bit-type-support.md @@ -1,6 +1,22 @@ Slang 64-bit Type Support ========================= +## Summary + +* Not all targets support 64 bit types, or all 64 bit types + * 64 bit integers generally require later APIs/shader models +* When specifying 64 bit literals *always* use the type suffixes (ie `l`, `ull`, `ll`) +* GPU target/s generally do not support all double intrinsics + * Typically missing are trascendentals (sin, cos etc), logarithm and exponental functions + * CUDA is the exception supporting nearly all double intrinsics +* D3D + * D3D targets *appear* to support double intrinsics (like sin, cos, log etc), but behind the scenes they are actually being converted to float + * When using D3D12, it is best to use DXIL if you use double because there are some serious issues around double and DXBC +* VK will produce an error in validation if a double intrinsic is used it does support (which is most of them) + +Overview +======== + The Slang language supports 64 bit built in types. Such as * double @@ -58,8 +74,8 @@ CPU | | Yes | Yes | 1 CUDA | Nvrtx/PTX | Yes | Yes | 1 D3D12 | DXC/DXIL | Yes | Small Subset | 4 Vulkan | GlSlang/Spir-V | Yes | Partial | 2 -D3D11 | FXC/DXBC | Yes | No | 4 -D3D12 | FXC/DXBC | Yes | No | 3, 4 +D3D11 | FXC/DXBC | Yes | Small Subset | 4 +D3D12 | FXC/DXBC | Yes | Small Subset | 3, 4 1) CUDA and CPU support most intrinsics, with the notable exception currently of matrix invert 2) In terms of lack of general intrinsic support, the restriction is described in https://www.khronos.org/registry/spir-v/specs/1.0/GLSL.std.450.html diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index b10bcc683..fe3d45f1b 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -207,6 +207,7 @@ + @@ -238,6 +239,7 @@ + diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters index b296b23af..aeac70769 100644 --- a/source/core/core.vcxproj.filters +++ b/source/core/core.vcxproj.filters @@ -120,6 +120,9 @@ Header Files + + Header Files + Header Files @@ -209,6 +212,9 @@ Source Files + + Source Files + Source Files diff --git a/source/core/slang-downstream-compiler.cpp b/source/core/slang-downstream-compiler.cpp index 2f0cff1a9..0a40092ea 100644 --- a/source/core/slang-downstream-compiler.cpp +++ b/source/core/slang-downstream-compiler.cpp @@ -5,6 +5,8 @@ #include "../../slang-com-helper.h" #include "slang-string-util.h" +#include "slang-type-text-util.h" + #include "slang-io.h" #include "slang-shared-library.h" #include "slang-blob.h" @@ -47,7 +49,7 @@ static DownstreamCompiler::Infos _calcInfos() void DownstreamCompiler::Desc::appendAsText(StringBuilder& out) const { - out << getCompilerTypeAsText(type); + out << TypeTextUtil::asHumanText(type); // Append the version if there is a version if (majorVersion || minorVersion) @@ -74,21 +76,6 @@ void DownstreamCompiler::Desc::appendAsText(StringBuilder& out) const /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompiler !!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ -/* static */UnownedStringSlice DownstreamCompiler::getCompilerTypeAsText(SlangPassThrough type) -{ - switch (type) - { - default: - case SLANG_PASS_THROUGH_NONE: return UnownedStringSlice::fromLiteral("Unknown"); - case SLANG_PASS_THROUGH_VISUAL_STUDIO: return UnownedStringSlice::fromLiteral("Visual Studio"); - case SLANG_PASS_THROUGH_GCC: return UnownedStringSlice::fromLiteral("GCC"); - case SLANG_PASS_THROUGH_CLANG: return UnownedStringSlice::fromLiteral("Clang"); - case SLANG_PASS_THROUGH_NVRTC: return UnownedStringSlice::fromLiteral("NVRTC"); - case SLANG_PASS_THROUGH_FXC: return UnownedStringSlice::fromLiteral("fxc"); - case SLANG_PASS_THROUGH_DXC: return UnownedStringSlice::fromLiteral("dxc"); - case SLANG_PASS_THROUGH_GLSLANG: return UnownedStringSlice::fromLiteral("glslang"); - } -} /* static */bool DownstreamCompiler::canCompile(SlangPassThrough compiler, SlangSourceLanguage sourceLanguage) { @@ -96,92 +83,6 @@ void DownstreamCompiler::Desc::appendAsText(StringBuilder& out) const return (info.sourceLanguageFlags & (SourceLanguageFlags(1) << int(sourceLanguage))) != 0; } -/* static */SlangSourceLanguage DownstreamCompiler::getSourceLanguageFromName(const UnownedStringSlice& text) -{ - if (text == "c" || text == "C") - { - return SLANG_SOURCE_LANGUAGE_C; - } - else if (text == "cpp" || text == "c++" || text == "C++" || text == "cxx") - { - return SLANG_SOURCE_LANGUAGE_CPP; - } - else if (text == "slang") - { - return SLANG_SOURCE_LANGUAGE_SLANG; - } - else if (text == "glsl") - { - return SLANG_SOURCE_LANGUAGE_GLSL; - } - else if (text == "hlsl") - { - return SLANG_SOURCE_LANGUAGE_HLSL; - } - else if (text == "cu" || text == "cuda") - { - return SLANG_SOURCE_LANGUAGE_CUDA; - } - return SLANG_SOURCE_LANGUAGE_UNKNOWN; -} - -#define SLANG_PASS_THROUGH_TYPES(x) \ - x(none, NONE) \ - x(fxc, FXC) \ - x(dxc, DXC) \ - x(glslang, GLSLANG) \ - x(visualstudio, VISUAL_STUDIO) \ - x(clang, CLANG) \ - x(gcc, GCC) \ - x(genericcpp, GENERIC_C_CPP) \ - x(nvrtc, NVRTC) - - - -/* static */SlangPassThrough DownstreamCompiler::getPassThroughFromName(const UnownedStringSlice& slice) -{ -#define SLANG_PASS_THROUGH_NAME_TO_TYPE(x, y) \ - if (slice == UnownedStringSlice::fromLiteral(#x)) return SLANG_PASS_THROUGH_##y; - - SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_NAME_TO_TYPE) - - // Other options - if (slice == "c" || slice == "cpp") - { - return SLANG_PASS_THROUGH_GENERIC_C_CPP; - } - else if (slice == "vs") - { - return SLANG_PASS_THROUGH_VISUAL_STUDIO; - } - - return SLANG_PASS_THROUGH_NONE; -} - -/* static */SlangResult DownstreamCompiler::getPassThroughFromName(const UnownedStringSlice& slice, SlangPassThrough& outPassThrough) -{ - outPassThrough = getPassThroughFromName(slice); - // It could be none on error - if it's not equal to "none" then it msut be an error - if (outPassThrough == SLANG_PASS_THROUGH_NONE && slice != UnownedStringSlice::fromLiteral("none")) - { - return SLANG_FAIL; - } - return SLANG_OK; -} - -/* static */UnownedStringSlice DownstreamCompiler::getPassThroughName(SlangPassThrough passThru) -{ -#define SLANG_PASS_THROUGH_TYPE_TO_NAME(x, y) \ - case SLANG_PASS_THROUGH_##y: return UnownedStringSlice::fromLiteral(#x); - - switch (passThru) - { - SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_TYPE_TO_NAME) - default: break; - } - return UnownedStringSlice::fromLiteral("unknown"); -} - /* static */SlangCompileTarget DownstreamCompiler::getCompileTarget(SlangSourceLanguage sourceLanguage) { switch (sourceLanguage) diff --git a/source/core/slang-downstream-compiler.h b/source/core/slang-downstream-compiler.h index a4238ce38..f9e33ed6c 100644 --- a/source/core/slang-downstream-compiler.h +++ b/source/core/slang-downstream-compiler.h @@ -280,22 +280,11 @@ public: /// Some downstream compilers are backed by a shared library. This allows access to the shared library to access internal functions. virtual ISlangSharedLibrary* getSharedLibrary() { return nullptr; } - /// Return the compiler type as name - static UnownedStringSlice getCompilerTypeAsText(SlangPassThrough type); - /// Get info for a compiler type static const Info& getInfo(SlangPassThrough compiler) { return s_infos.infos[int(compiler)]; } /// True if this compiler can compile the specified language static bool canCompile(SlangPassThrough compiler, SlangSourceLanguage sourceLanguage); - /// Given a source language name returns a source language. Name here is distinct from extension - static SlangSourceLanguage getSourceLanguageFromName(const UnownedStringSlice& text); - /// Given a name returns the pass through - static SlangPassThrough getPassThroughFromName(const UnownedStringSlice& slice); - static SlangResult getPassThroughFromName(const UnownedStringSlice& slice, SlangPassThrough& outPassThrough); - /// Get the compilers name - static UnownedStringSlice getPassThroughName(SlangPassThrough passThru); - /// Given a source language return as the equivalent compile target static SlangCompileTarget getCompileTarget(SlangSourceLanguage sourceLanguage); diff --git a/source/core/slang-type-text-util.cpp b/source/core/slang-type-text-util.cpp new file mode 100644 index 000000000..b9ccc4971 --- /dev/null +++ b/source/core/slang-type-text-util.cpp @@ -0,0 +1,165 @@ + +#include "slang-type-text-util.h" + + +namespace Slang +{ + +#define SLANG_SCALAR_TYPES(x) \ + x(None, none) \ + x(Void, void) \ + x(Bool, bool) \ + x(Float16, half) \ + x(UInt32, uint32_t) \ + x(Int32, int32_t) \ + x(Int64, int64_t) \ + x(UInt64, uint64_t) \ + x(Float32, float) \ + x(Float64, double) + +#define SLANG_PASS_THROUGH_TYPES(x) \ + x(none, NONE) \ + x(fxc, FXC) \ + x(dxc, DXC) \ + x(glslang, GLSLANG) \ + x(visualstudio, VISUAL_STUDIO) \ + x(clang, CLANG) \ + x(gcc, GCC) \ + x(genericcpp, GENERIC_C_CPP) \ + x(nvrtc, NVRTC) + +namespace { // anonymous + +struct ScalarTypeInfo +{ + slang::TypeReflection::ScalarType type; + UnownedStringSlice text; +}; + +static const ScalarTypeInfo s_scalarTypeInfo[] = +{ + #define SLANG_SCALAR_TYPE_INFO(value, text) \ + { slang::TypeReflection::ScalarType::value, UnownedStringSlice::fromLiteral(#text) }, + SLANG_SCALAR_TYPES(SLANG_SCALAR_TYPE_INFO) +}; + +} // anonymous + +/* static */UnownedStringSlice TypeTextUtil::asText(slang::TypeReflection::ScalarType scalarType) +{ + typedef slang::TypeReflection::ScalarType ScalarType; + switch (scalarType) + { +#define SLANG_SCALAR_TYPE_TO_TEXT(value, text) case ScalarType::value: return UnownedStringSlice::fromLiteral(#text); + SLANG_SCALAR_TYPES(SLANG_SCALAR_TYPE_TO_TEXT) + default: break; + } + + return UnownedStringSlice(); +} + +/* static */slang::TypeReflection::ScalarType TypeTextUtil::asScalarType(const UnownedStringSlice& inText) +{ + for (Index i = 0; i < SLANG_COUNT_OF(s_scalarTypeInfo); ++i) + { + const auto& info = s_scalarTypeInfo[i]; + if (info.text == inText) + { + return info.type; + } + } + return slang::TypeReflection::ScalarType::None; +} + +/* static */UnownedStringSlice TypeTextUtil::asHumanText(SlangPassThrough type) +{ + switch (type) + { + default: + case SLANG_PASS_THROUGH_NONE: return UnownedStringSlice::fromLiteral("Unknown"); + case SLANG_PASS_THROUGH_VISUAL_STUDIO: return UnownedStringSlice::fromLiteral("Visual Studio"); + case SLANG_PASS_THROUGH_GCC: return UnownedStringSlice::fromLiteral("GCC"); + case SLANG_PASS_THROUGH_CLANG: return UnownedStringSlice::fromLiteral("Clang"); + case SLANG_PASS_THROUGH_NVRTC: return UnownedStringSlice::fromLiteral("NVRTC"); + case SLANG_PASS_THROUGH_FXC: return UnownedStringSlice::fromLiteral("fxc"); + case SLANG_PASS_THROUGH_DXC: return UnownedStringSlice::fromLiteral("dxc"); + case SLANG_PASS_THROUGH_GLSLANG: return UnownedStringSlice::fromLiteral("glslang"); + } +} + +/* static */SlangSourceLanguage TypeTextUtil::asSourceLanguage(const UnownedStringSlice& text) +{ + if (text == "c" || text == "C") + { + return SLANG_SOURCE_LANGUAGE_C; + } + else if (text == "cpp" || text == "c++" || text == "C++" || text == "cxx") + { + return SLANG_SOURCE_LANGUAGE_CPP; + } + else if (text == "slang") + { + return SLANG_SOURCE_LANGUAGE_SLANG; + } + else if (text == "glsl") + { + return SLANG_SOURCE_LANGUAGE_GLSL; + } + else if (text == "hlsl") + { + return SLANG_SOURCE_LANGUAGE_HLSL; + } + else if (text == "cu" || text == "cuda") + { + return SLANG_SOURCE_LANGUAGE_CUDA; + } + return SLANG_SOURCE_LANGUAGE_UNKNOWN; +} + +/* static */SlangPassThrough TypeTextUtil::asPassThrough(const UnownedStringSlice& slice) +{ +#define SLANG_PASS_THROUGH_NAME_TO_TYPE(x, y) \ + if (slice == UnownedStringSlice::fromLiteral(#x)) return SLANG_PASS_THROUGH_##y; + + SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_NAME_TO_TYPE) + + // Other options + if (slice == "c" || slice == "cpp") + { + return SLANG_PASS_THROUGH_GENERIC_C_CPP; + } + else if (slice == "vs") + { + return SLANG_PASS_THROUGH_VISUAL_STUDIO; + } + + return SLANG_PASS_THROUGH_NONE; +} + +/* static */SlangResult TypeTextUtil::asPassThrough(const UnownedStringSlice& slice, SlangPassThrough& outPassThrough) +{ + outPassThrough = asPassThrough(slice); + // It could be none on error - if it's not equal to "none" then it must be an error + if (outPassThrough == SLANG_PASS_THROUGH_NONE && slice != UnownedStringSlice::fromLiteral("none")) + { + return SLANG_FAIL; + } + return SLANG_OK; +} + +/* static */UnownedStringSlice TypeTextUtil::asText(SlangPassThrough passThru) +{ +#define SLANG_PASS_THROUGH_TYPE_TO_NAME(x, y) \ + case SLANG_PASS_THROUGH_##y: return UnownedStringSlice::fromLiteral(#x); + + switch (passThru) + { + SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_TYPE_TO_NAME) + default: break; + } + return UnownedStringSlice::fromLiteral("unknown"); +} + + +} + diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h new file mode 100644 index 000000000..73c1d9ef0 --- /dev/null +++ b/source/core/slang-type-text-util.h @@ -0,0 +1,38 @@ +#ifndef SLANG_CORE_TYPE_TEXT_UTIL_H +#define SLANG_CORE_TYPE_TEXT_UTIL_H + +#include "../../slang.h" + +#include "slang-string.h" + + +namespace Slang +{ + +/// Utility class to allow conversion of types (such as enums) to and from text types +struct TypeTextUtil +{ + + /// Get the scalar type as text. + static Slang::UnownedStringSlice asText(slang::TypeReflection::ScalarType scalarType); + + // Converts text to scalar type. Returns 'none' if not determined + static slang::TypeReflection::ScalarType asScalarType(const Slang::UnownedStringSlice& text); + + /// As human readable text + static UnownedStringSlice asHumanText(SlangPassThrough type); + + /// Given a source language name returns a source language. Name here is distinct from extension + static SlangSourceLanguage asSourceLanguage(const UnownedStringSlice& text); + + /// Given a name returns the pass through + static SlangPassThrough asPassThrough(const UnownedStringSlice& slice); + static SlangResult asPassThrough(const UnownedStringSlice& slice, SlangPassThrough& outPassThrough); + + /// Get the compilers name + static UnownedStringSlice asText(SlangPassThrough passThru); +}; + +} + +#endif // SLANG_CORE_TYPE_TEXT_UTIL_H diff --git a/source/core/slang-writer.cpp b/source/core/slang-writer.cpp index 2f694aeec..0688bea6c 100644 --- a/source/core/slang-writer.cpp +++ b/source/core/slang-writer.cpp @@ -57,6 +57,11 @@ SlangResult WriterHelper::put(const char* text) return m_writer->write(text, ::strlen(text)); } +SlangResult WriterHelper::put(const UnownedStringSlice& text) +{ + return m_writer->write(text.begin(), text.size()); +} + /* !!!!!!!!!!!!!!!!!!!!!!!!! BaseWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ ISlangUnknown* BaseWriter::getInterface(const Guid& guid) diff --git a/source/core/slang-writer.h b/source/core/slang-writer.h index 759644fde..380823b39 100644 --- a/source/core/slang-writer.h +++ b/source/core/slang-writer.h @@ -16,6 +16,7 @@ class WriterHelper public: SlangResult print(const char* format, ...); SlangResult put(const char* text); + SlangResult put(const UnownedStringSlice& text); SLANG_FORCE_INLINE SlangResult write(const char* chars, size_t numChars) { return m_writer->write(chars, numChars); } SLANG_FORCE_INLINE void flush() { m_writer->flush(); } diff --git a/source/slang/slang-check.cpp b/source/slang/slang-check.cpp index bd7e2ea12..77e3ffc9f 100644 --- a/source/slang/slang-check.cpp +++ b/source/slang/slang-check.cpp @@ -7,6 +7,8 @@ #include "slang-check-impl.h" +#include "../core/slang-type-text-util.h" + namespace Slang { namespace { // anonymous @@ -180,7 +182,7 @@ namespace Slang SlangFuncPtr func = sharedLibrary->findFuncByName(info.name); if (!func) { - UnownedStringSlice compilerName = DownstreamCompiler::getCompilerTypeAsText(SlangPassThrough(info.compilerType)); + UnownedStringSlice compilerName = TypeTextUtil::asText(SlangPassThrough(info.compilerType)); sink->diagnose(SourceLoc(), Diagnostics::failedToFindFunctionForCompiler, info.name, compilerName); return nullptr; } diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 93d1cf2dd..30388479e 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -394,6 +394,23 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S { switch (type->op) { + case kIROp_PtrType: + { + auto ptrType = static_cast(type); + SLANG_RETURN_ON_FAIL(calcTypeName(ptrType->getValueType(), target, out)); + // TODO(JS): It seems although it says it is a pointer, it can actually be output as a reference + // not clear where the ptr aspect is there, as in the definition it is just 'out', implying out + // is somewhere converted to a ptr? + out << "&"; + return SLANG_OK; + } + case kIROp_RefType: + { + auto refType = static_cast(type); + SLANG_RETURN_ON_FAIL(calcTypeName(refType->getValueType(), target, out)); + out << "&"; + return SLANG_OK; + } case kIROp_HalfType: { // Special case half @@ -569,6 +586,21 @@ static IRBasicType* _getElementType(IRType* type) /* static */CPPSourceEmitter::TypeDimension CPPSourceEmitter::_getTypeDimension(IRType* type, bool vecSwap) { + switch (type->op) + { + case kIROp_PtrType: + { + type = static_cast(type)->getValueType(); + break; + } + case kIROp_RefType: + { + type = static_cast(type)->getValueType(); + break; + } + default: break; + } + switch (type->op) { case kIROp_VectorType: @@ -658,7 +690,6 @@ void CPPSourceEmitter::_emitAryDefinition(const HLSLIntrinsic* specOp) } IRType* retType = specOp->returnType; - TypeDimension retDim = _getTypeDimension(retType, false); UnownedStringSlice scalarFuncName(funcName); if (isOperator) @@ -677,15 +708,31 @@ void CPPSourceEmitter::_emitAryDefinition(const HLSLIntrinsic* specOp) writer->emit("\n{\n"); writer->indent(); - emitType(retType); - writer->emit(" r;\n"); + const bool hasReturnType = retType->op != kIROp_VoidType; - for (int i = 0; i < retDim.rowCount; ++i) + TypeDimension calcDim; + if (hasReturnType) { - for (int j = 0; j < retDim.colCount; ++j) + emitType(retType); + writer->emit(" r;\n"); + + calcDim = _getTypeDimension(retType, false); + } + else + { + calcDim = _getTypeDimension(funcType->getParamType(0), false); + } + + for (int i = 0; i < calcDim.rowCount; ++i) + { + for (int j = 0; j < calcDim.colCount; ++j) { - _emitAccess(UnownedStringSlice::fromLiteral("r"), retDim, i, j, writer); - writer->emit(" = "); + if (hasReturnType) + { + _emitAccess(UnownedStringSlice::fromLiteral("r"), calcDim, i, j, writer); + writer->emit(" = "); + } + if (isOperator) { switch (numParams) @@ -727,7 +774,10 @@ void CPPSourceEmitter::_emitAryDefinition(const HLSLIntrinsic* specOp) } } - writer->emit("return r;\n"); + if (hasReturnType) + { + writer->emit("return r;\n"); + } writer->dedent(); writer->emit("}\n\n"); @@ -1066,6 +1116,7 @@ void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcNa writer->emit("}\n\n"); } + void CPPSourceEmitter::_emitConstructConvertDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp) { SourceWriter* writer = getSourceWriter(); @@ -1134,6 +1185,91 @@ void CPPSourceEmitter::_emitConstructConvertDefinition(const UnownedStringSlice& writer->emit("}\n\n"); } +void CPPSourceEmitter::_emitInitDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp) +{ + SourceWriter* writer = getSourceWriter(); + IRFuncType* funcType = specOp->signatureType; + + emitFunctionPreambleImpl(nullptr); + + IRType* retType = specOp->returnType; + + _emitSignature(funcName, specOp); + writer->emit("\n{\n"); + writer->indent(); + + // Use C++ construction + writer->emit("return "); + emitType(retType); + writer->emit("{ "); + + const Index paramCount = Index(funcType->getParamCount()); + + if (IRVectorType* vecType = as(retType)) + { + Index elementCount = Index(GetIntVal(vecType->getElementCount())); + + Index paramIndex = 0; + Index paramSubIndex = 0; + + for (Index i = 0; i < elementCount; ++i) + { + if (i > 0) + { + writer->emit(", "); + } + + if (paramIndex >= paramCount) + { + writer->emit("0"); + } + else + { + IRType* paramType = funcType->getParamType(paramIndex); + + if (IRVectorType* paramVecType = as(paramType)) + { + Index paramElementCount = Index(GetIntVal(paramVecType->getElementCount())); + + writer->emitChar('a' + char(paramIndex)); + writer->emit("."); + writer->emitChar(s_elemNames[paramSubIndex]); + + paramSubIndex ++; + + if (paramSubIndex >= paramElementCount) + { + paramIndex++; + paramSubIndex = 0; + } + } + else + { + writer->emitChar('a' + char(paramIndex)); + paramIndex++; + } + } + } + } + else + { + for (Index i = 0; i < paramCount; ++i) + { + if (i > 0) + { + writer->emit(", "); + } + writer->emitChar('a' + char(i)); + } + } + + writer->emit("};\n"); + + writer->dedent(); + writer->emit("}\n\n"); +} + + void CPPSourceEmitter::_emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp) { SourceWriter* writer = getSourceWriter(); @@ -1246,6 +1382,10 @@ void CPPSourceEmitter::emitSpecializedOperationDefinition(const HLSLIntrinsic* s switch (specOp->op) { + case Op::Init: + { + return _emitInitDefinition(_getFuncName(specOp), specOp); + } case Op::VecMatMul: case Op::Dot: { @@ -1284,6 +1424,11 @@ void CPPSourceEmitter::emitSpecializedOperationDefinition(const HLSLIntrinsic* s { return _emitGetAtDefinition(_getFuncName(specOp), specOp); } + case Op::Swizzle: + { + // Don't have to output anything for swizzle for now + return; + } default: { const auto& info = HLSLIntrinsic::getInfo(specOp->op); @@ -1306,80 +1451,23 @@ void CPPSourceEmitter::emitCall(const HLSLIntrinsic* specOp, IRInst* inst, const SLANG_UNUSED(inOuterPrec); SourceWriter* writer = getSourceWriter(); - - // Getting the name means that this op is registered as used switch (specOp->op) { case Op::Init: { - // For C++ we don't need an init function - // For C we'll need the construct function for the return type - //UnownedStringSlice name = _getFuncName(specOp); - IRType* retType = specOp->returnType; - - switch (retType->op) + if (IRBasicType::isaImpl(retType->op)) { - case kIROp_VectorType: - { - // Get the type name - emitType(retType); - writer->emitChar('{'); - - for (int i = 0; i < numOperands; ++i) - { - if (i > 0) - { - writer->emit(", "); - } - emitOperand(operands[i].get(), getInfo(EmitOp::General)); - } - - writer->emitChar('}'); - break; - } - case kIROp_MatrixType: - { - IRMatrixType* matType = static_cast(retType); - - //int colsCount = int(GetIntVal(matType->getColumnCount())); - int rowsCount = int(GetIntVal(matType->getRowCount())); - - SLANG_ASSERT(rowsCount == numOperands); - - emitType(retType); - writer->emitChar('{'); - - for (int j = 0; j < rowsCount; ++j) - { - if (j > 0) - { - writer->emit(", "); - } - emitOperand(operands[j].get(), getInfo(EmitOp::General)); - } - - writer->emitChar('}'); - break; - } - default: - { - if (IRBasicType::isaImpl(retType->op)) - { - SLANG_ASSERT(numOperands == 1); + SLANG_ASSERT(numOperands == 1); - writer->emit(_getTypeName(retType)); - writer->emitChar('('); - - emitOperand(operands[0].get(), getInfo(EmitOp::General)); + writer->emit(_getTypeName(retType)); + writer->emitChar('('); - writer->emitChar(')'); - break; - } + emitOperand(operands[0].get(), getInfo(EmitOp::General)); - SLANG_ASSERT(!"Not handled"); - } + writer->emitChar(')'); + return; } break; } @@ -1419,56 +1507,55 @@ void CPPSourceEmitter::emitCall(const HLSLIntrinsic* specOp, IRInst* inst, const } writer->emit("}"); - break; + return; } - default: - { - const auto& info = HLSLIntrinsic::getInfo(specOp->op); - // Make sure that the return type is available - bool isOperator = _isOperator(info.funcName); - - UnownedStringSlice funcName = _getFuncName(specOp); + default: break; + } + + { + const auto& info = HLSLIntrinsic::getInfo(specOp->op); + // Make sure that the return type is available + const bool isOperator = _isOperator(info.funcName); + const UnownedStringSlice funcName = _getFuncName(specOp); - switch (specOp->op) + switch (specOp->op) + { + case Op::ConstructFromScalar: { - case Op::ConstructFromScalar: - { - // We need to special case, because this may have come from a swizzle from a built in - // type, in that case the only parameter we want is the first one - numOperands = 1; - break; - } - - default: break; + // We need to special case, because this may have come from a swizzle from a built in + // type, in that case the only parameter we want is the first one + numOperands = 1; + break; } - // add that we want a function - SLANG_ASSERT(info.numOperands < 0 || numOperands == info.numOperands); + default: break; + } - useType(specOp->returnType); + // add that we want a function + SLANG_ASSERT(info.numOperands < 0 || numOperands == info.numOperands); + + useType(specOp->returnType); - if (isOperator) - { - // Just do the default output - defaultEmitInstExpr(inst, inOuterPrec); - } - else - { - writer->emit(funcName); - writer->emitChar('('); + if (isOperator) + { + // Just do the default output + defaultEmitInstExpr(inst, inOuterPrec); + } + else + { + writer->emit(funcName); + writer->emitChar('('); - for (int i = 0; i < numOperands; ++i) + for (int i = 0; i < numOperands; ++i) + { + if (i > 0) { - if (i > 0) - { - writer->emit(", "); - } - emitOperand(operands[i].get(), getInfo(EmitOp::General)); + writer->emit(", "); } - - writer->emitChar(')'); + emitOperand(operands[i].get(), getInfo(EmitOp::General)); } - break; + + writer->emitChar(')'); } } } @@ -1577,6 +1664,12 @@ SlangResult CPPSourceEmitter::calcFuncName(const HLSLIntrinsic* specOp, StringBu outBuilder << "getAt"; return SLANG_OK; } + case Op::Init: + { + outBuilder << "make_"; + SLANG_RETURN_ON_FAIL(calcTypeName(specOp->returnType, CodeGenTarget::CSource, outBuilder)); + return SLANG_OK; + } default: break; } @@ -2061,10 +2154,16 @@ void CPPSourceEmitter::emitPreprocessorDirectivesImpl() } } - // Emit all the intrinsics that were used - for (const auto& keyValue : m_intrinsicNameMap) { - _maybeEmitSpecializedOperationDefinition(keyValue.Key); + List intrinsics; + m_intrinsicSet.getIntrinsics(intrinsics); + + // Emit all the intrinsics that were used + for (auto intrinsic: intrinsics) + { + _maybeEmitSpecializedOperationDefinition(intrinsic); + } + } } diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index 082497510..da8d026ee 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -104,7 +104,8 @@ protected: void _emitConstructConvertDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp); void _emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp); void _emitGetAtDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp); - + void _emitInitDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp); + void _emitSignature(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp); void _emitInOutParamType(IRType* type, String const& name, IRType* valueType); diff --git a/source/slang/slang-emit-cuda.cpp b/source/slang/slang-emit-cuda.cpp index 2d49e606c..019bf8b94 100644 --- a/source/slang/slang-emit-cuda.cpp +++ b/source/slang/slang-emit-cuda.cpp @@ -393,9 +393,9 @@ void CUDASourceEmitter::emitCall(const HLSLIntrinsic* specOp, IRInst* inst, cons IRType* retType = specOp->returnType; - switch (retType->op) + if (IRVectorType* vecType = as(retType)) { - case kIROp_VectorType: + if (numOperands == GetIntVal(vecType->getElementCount())) { // Get the type name writer->emit("make_"); @@ -414,8 +414,8 @@ void CUDASourceEmitter::emitCall(const HLSLIntrinsic* specOp, IRInst* inst, cons writer->emitChar(')'); return; } - default: break; } + // Just use the default break; } default: break; @@ -542,10 +542,14 @@ void CUDASourceEmitter::emitPreprocessorDirectivesImpl() } } - // Emit all the intrinsics that were used - for (const auto& keyValue : m_intrinsicNameMap) { - _maybeEmitSpecializedOperationDefinition(keyValue.Key); + List intrinsics; + m_intrinsicSet.getIntrinsics(intrinsics); + // Emit all the intrinsics that were used + for (auto intrinsic : intrinsics) + { + _maybeEmitSpecializedOperationDefinition(intrinsic); + } } } diff --git a/source/slang/slang-hlsl-intrinsic-set.cpp b/source/slang/slang-hlsl-intrinsic-set.cpp index 1f42836f3..7500e799a 100644 --- a/source/slang/slang-hlsl-intrinsic-set.cpp +++ b/source/slang/slang-hlsl-intrinsic-set.cpp @@ -245,19 +245,37 @@ SlangResult HLSLIntrinsicSet::makeIntrinsic(IRInst* inst, HLSLIntrinsic& out) // If it's constructed from a type conversion calcIntrinsic(Op::ConstructConvert, inst, out); } + return SLANG_OK; } else { - // We only emit as if it has one operand, but we can tell how many it actually has from the return type - calcIntrinsic(Op::Init, inst, 1, out); + // If we are constructing a basic type, we don't need an Op::Init + if (!IRBasicType::isaImpl(dstType->op)) + { + // Emit the 'init' intrinsic + calcIntrinsic(Op::Init, inst, inst->getOperandCount(), out); + return SLANG_OK; + } } - return SLANG_OK; + return SLANG_FAIL; } case kIROp_makeVector: + { + if (inst->getOperandCount() == 1 && as(inst->getOperand(0)->getDataType())) + { + // This is make from scalar + calcIntrinsic(Op::ConstructFromScalar, inst, out); + } + else + { + calcIntrinsic(Op::Init, inst, inst->getOperandCount(), out); + } + return SLANG_OK; + } case kIROp_MakeMatrix: { // We only emit as if it has one operand, but we can tell how many it actually has from the return type - calcIntrinsic(Op::Init, inst, 1, out); + calcIntrinsic(Op::Init, inst, inst->getOperandCount(), out); return SLANG_OK; } case kIROp_swizzle: @@ -346,6 +364,14 @@ SlangResult HLSLIntrinsicSet::makeIntrinsic(IRInst* inst, HLSLIntrinsic& out) return SLANG_FAIL; } +void HLSLIntrinsicSet::getIntrinsics(List& out) const +{ + for (auto& pair : m_intrinsics) + { + out.add(pair.Value); + } +} + HLSLIntrinsic* HLSLIntrinsicSet::add(const HLSLIntrinsic& intrinsic) { // Make sure it's valid(!) diff --git a/source/slang/slang-hlsl-intrinsic-set.h b/source/slang/slang-hlsl-intrinsic-set.h index 17e88fc9a..90ed9368f 100644 --- a/source/slang/slang-hlsl-intrinsic-set.h +++ b/source/slang/slang-hlsl-intrinsic-set.h @@ -146,26 +146,28 @@ struct HLSLIntrinsic bool operator==(const ThisType& rhs) const { return op == rhs.op && returnType == rhs.returnType && signatureType == rhs.signatureType; } bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + static bool isTypeScalar(IRType* type) + { + // Strip off ptr if it's an operand type + if (type->op == kIROp_PtrType) + { + type = as(type->getOperand(0)); + } + // If any are vec or matrix, then we + return !(type->op == kIROp_MatrixType || type->op == kIROp_VectorType); + } + bool isScalar() const { Index paramCount = Index(signatureType->getParamCount()); for (Index i = 0; i < paramCount; ++i) { - IRType* paramType = signatureType->getParamType(i); - - // Strip off ptr if it's an operand type - if (paramType->op == kIROp_PtrType) - { - paramType = as(paramType->getOperand(0)); - } - - // If any are vec or matrix, then we - if (paramType->op == kIROp_MatrixType || paramType->op == kIROp_VectorType) + if (!isTypeScalar(signatureType->getParamType(i))) { return false; } } - return true; + return isTypeScalar(returnType); } int GetHashCode() const { return combineHash(int(op), combineHash(Slang::GetHashCode(returnType), Slang::GetHashCode(signatureType))); } @@ -255,6 +257,8 @@ public: /// Returns the intrinsic constructed if there is one from the inst. If not possible to construct returns nullptr. HLSLIntrinsic* add(IRInst* inst); + void getIntrinsics(List& out) const; + HLSLIntrinsicSet(IRTypeSet* typeSet, HLSLIntrinsicOpLookup* lookup); protected: diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index f9f8db0e2..456c43fa6 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -15,6 +15,8 @@ #include "slang-state-serialize.h" #include "slang-ir-serialize.h" +#include "../core/slang-type-text-util.h" + #include namespace Slang { @@ -708,7 +710,7 @@ struct OptionsParser SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE; - if (SLANG_FAILED(DownstreamCompiler::getPassThroughFromName(name.getUnownedSlice(), passThrough))) + if (SLANG_FAILED(TypeTextUtil::asPassThrough(name.getUnownedSlice(), passThrough))) { sink->diagnose(SourceLoc(), Diagnostics::unknownPassThroughTarget, name); return SLANG_FAIL; @@ -943,7 +945,7 @@ struct OptionsParser String compilerText; SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, compilerText)); - SlangSourceLanguage sourceLanguage = DownstreamCompiler::getSourceLanguageFromName(sourceLanguageText.getUnownedSlice()); + SlangSourceLanguage sourceLanguage = TypeTextUtil::asSourceLanguage(sourceLanguageText.getUnownedSlice()); if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN) { sink->diagnose(SourceLoc(), Diagnostics::unknownSourceLanguage, sourceLanguageText); @@ -951,7 +953,7 @@ struct OptionsParser } SlangPassThrough compiler; - if (SLANG_FAILED(DownstreamCompiler::getPassThroughFromName(compilerText.getUnownedSlice(), compiler))) + if (SLANG_FAILED(TypeTextUtil::asPassThrough(compilerText.getUnownedSlice(), compiler))) { sink->diagnose(SourceLoc(), Diagnostics::unknownPassThroughTarget, compilerText); return SLANG_FAIL; @@ -985,7 +987,7 @@ struct OptionsParser String slice = argStr.subString(1, index - 1); SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE; - if (SLANG_SUCCEEDED(DownstreamCompiler::getPassThroughFromName(slice.getUnownedSlice(), passThrough))) + if (SLANG_SUCCEEDED(TypeTextUtil::asPassThrough(slice.getUnownedSlice(), passThrough))) { session->setDownstreamCompilerPath(passThrough, name.getBuffer()); continue; diff --git a/source/slang/slang-state-serialize.cpp b/source/slang/slang-state-serialize.cpp index b97756965..7b689e762 100644 --- a/source/slang/slang-state-serialize.cpp +++ b/source/slang/slang-state-serialize.cpp @@ -6,6 +6,7 @@ #include "../core/slang-stream.h" #include "../core/slang-math.h" +#include "../core/slang-type-text-util.h" #include "slang-options.h" @@ -1228,7 +1229,7 @@ static SlangResult _calcCommandLine(OffsetBase& base, StateSerializeUtil::Reques default: { cmd.addArg("-pass-through"); - cmd.addArg(DownstreamCompiler::getPassThroughName(SlangPassThrough(requestState->passThroughMode))); + cmd.addArg(TypeTextUtil::asText(SlangPassThrough(requestState->passThroughMode))); break; } } diff --git a/tests/bugs/dxbc-double-problem.slang.expected.txt b/tests/bugs/dxbc-double-problem.slang.expected.txt index 2cffc0e7c..d560391b9 100644 --- a/tests/bugs/dxbc-double-problem.slang.expected.txt +++ b/tests/bugs/dxbc-double-problem.slang.expected.txt @@ -1,3 +1,4 @@ +type: double 0.674277 3.141593 0.017453 diff --git a/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang b/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang index 02bffb7b1..600dfa88d 100644 --- a/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang +++ b/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang @@ -1,8 +1,7 @@ -// It doesn't look like fxc, dxc, vk support double versions of many of the intrinsics, so they are disabled here. - //TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -output-using-type +// On dx11 this causes a crash on CI (not currently not repeatable on test systems) //DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -output-using-type -// Fxc doesn't have double intrinsics available +// This is disabled because Dx12/DXBC doing double writes to a structured buffer can fail //DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -output-using-type //TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil -output-using-type //TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -output-using-type diff --git a/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang.expected.txt b/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang.expected.txt index 60826a847..44f39ae21 100644 --- a/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang.expected.txt +++ b/tests/hlsl-intrinsic/scalar-double-d3d-intrinsic.slang.expected.txt @@ -1,3 +1,4 @@ +type: double 2.850000 2.550000 3.216667 diff --git a/tests/hlsl-intrinsic/scalar-double-vk-intrinsic.slang.expected.txt b/tests/hlsl-intrinsic/scalar-double-vk-intrinsic.slang.expected.txt index 6fd20cd00..b1b17082a 100644 --- a/tests/hlsl-intrinsic/scalar-double-vk-intrinsic.slang.expected.txt +++ b/tests/hlsl-intrinsic/scalar-double-vk-intrinsic.slang.expected.txt @@ -1,3 +1,4 @@ +type: double -105.150000 -46.277573 13.888270 diff --git a/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang b/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang index 71f058dc1..c315e84cd 100644 --- a/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang +++ b/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang @@ -1,12 +1,15 @@ +// Disable the test in general, exists to check the output when the fix is in. + //TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute // No support for int64_t on dx11 (no sm 6.0) //DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute // No support with Dx12 with dxbc. Needs SM6.0 + dxil //DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -profile cs_6_0 -dx12 -use-dxil +//DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -profile cs_6_0 -dx12 -use-dxil // GLSL notices the narrowing of a 64 bit literal into 32 bits and for it this is an error. //DISABLE_TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -//TEST(compute):COMPARE_COMPUTE_EX:-cuda -compute +//DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-cuda -compute + //TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer RWStructuredBuffer outputBuffer; diff --git a/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang.expected.txt b/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang.expected.txt index e69de29bb..6c2c9dabe 100644 --- a/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang.expected.txt +++ b/tests/hlsl-intrinsic/scalar-int64-literal-problem.slang.expected.txt @@ -0,0 +1,8 @@ +7FFFFFFF +FFFFFFFF +0 +FFFFFFFD +80000000 +FFFFFFFB +0 +FFFFFFF9 diff --git a/tests/hlsl-intrinsic/vector-float.slang b/tests/hlsl-intrinsic/vector-float.slang new file mode 100644 index 000000000..2ca98de6c --- /dev/null +++ b/tests/hlsl-intrinsic/vector-float.slang @@ -0,0 +1,103 @@ +//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -use-dxil -output-using-type +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -output-using-type +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-cuda -compute -output-using-type + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int idx = int(dispatchThreadID.x); + + float vf = idx * (1.0f / (4.0f)); + + float3 f = float3(0.1f, vf, vf + 0.2f); + + // Vector specific + float3 nv = normalize(f); + + + // Operate over all values + float3 ft = {}; + + // fmod + ft += float3(int3(((f % 0.11f) * 100) + 0.5)); + + ft += sin(f); + ft += cos(f); + ft += tan(f); + + ft += asin(f); + ft += acos(f); + ft += atan(f); + + ft += atan2(f, 2.0); + +#if 0 + { + // Disabled because not supported on VK (glsl) in vector form + float3 sf, cf; + sincos(f, sf, cf); + + ft += sf; + ft += cf; + } +#endif + +#if 0 + // Disabled because not supported on VK (glsl) in vector form + ft += rcp(1.0 + f); +#endif + + ft += sign(f - 0.5); + + ft += saturate(f * 4 - 2.0); + + ft += sqrt(f); + ft += rsqrt(1.0f + f); + + ft += exp2(f); + ft += exp(f); + + ft += frac(f * 3); + ft += ceil(f * 5 - 3); + + ft += floor(f * 10 - 7.01); + + ft += trunc(f * 7); + + ft += log(f + 10.0); + ft += log2(f * 3 + 2); + + + { + float v[] = { 1, 10, 100, 1000 }; + ft += int3(log10(float3(v[idx] + vf) + 0.5f)); + } + + + ft += abs(f * 4 - 2.0f); + + ft += min(0.5, f); + ft += max(f, 0.75); + + ft += pow(0.5, f); + + ft += smoothstep(0.2, 0.7, f); + ft += lerp(-100, 100, f); + + ft += clamp(f, 0.1, 0.3); + + ft += step(f, 0.5); + + int3 vi = asint(f - f) + idx; + + ft += float3(vi); + + float4 result = float4(ft + nv, 0) + float4(vf); + + outputBuffer[idx] = result; +} \ No newline at end of file diff --git a/tests/hlsl-intrinsic/vector-float.slang.expected.txt b/tests/hlsl-intrinsic/vector-float.slang.expected.txt new file mode 100644 index 000000000..c2a569574 --- /dev/null +++ b/tests/hlsl-intrinsic/vector-float.slang.expected.txt @@ -0,0 +1,17 @@ +type: float +-64.893356 +-98.276619 +-42.604630 +0.000000 +-62.899876 +-34.338215 +9.949981 +0.250000 +-60.725098 +28.410460 +73.591156 +0.500000 +-58.508228 +93.563255 +138.897034 +0.750000 diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index fb433048c..6591941e8 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -11,7 +11,9 @@ #include "../../source/core/slang-list.h" #include "../../source/core/slang-string-util.h" -#include "../../source/core/slang-downstream-compiler.h" +//#include "../../source/core/slang-downstream-compiler.h" + +#include "../../source/core/slang-type-text-util.h" namespace renderer_test { using namespace Slang; @@ -226,7 +228,7 @@ SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper s } UnownedStringSlice sourceLanguageText(*argCursor++); - SlangSourceLanguage sourceLanguage = DownstreamCompiler::getSourceLanguageFromName(sourceLanguageText); + SlangSourceLanguage sourceLanguage = TypeTextUtil::asSourceLanguage(sourceLanguageText); if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN) { stdError.print("error: expecting unknown source language name '%s' for '%s'\n", String(sourceLanguageText).getBuffer(), arg); diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp index 44021c08a..40502a9ec 100644 --- a/tools/render-test/shader-input-layout.cpp +++ b/tools/render-test/shader-input-layout.cpp @@ -3,6 +3,7 @@ #include "shader-input-layout.h" #include "core/slang-token-reader.h" +#include "core/slang-type-text-util.h" #include "render.h" @@ -755,6 +756,14 @@ namespace renderer_test scalarType = elementTypeLayout->getScalarType(); } + if (scalarType != ScalarType::None && scalarType != ScalarType::Void) + { + UnownedStringSlice text = TypeTextUtil::asText(scalarType); + // Write out the type + writer.put("type: "); + writer.put(text); + writer.put("\n"); + } switch (scalarType) { @@ -765,7 +774,6 @@ namespace renderer_test case ScalarType::None: case ScalarType::Void: case ScalarType::Bool: - case ScalarType::Float16: { auto ptr = (const uint32_t*)data; const size_t size = sizeInBytes / sizeof(ptr[0]); @@ -776,6 +784,17 @@ namespace renderer_test } break; } + case ScalarType::Float16: + { + auto ptr = (const uint16_t*)data; + const size_t size = sizeInBytes / sizeof(ptr[0]); + for (size_t i = 0; i < size; ++i) + { + const float v = HalfToFloat(ptr[i]); + writer.print("%f\n", v); + } + break; + } case ScalarType::UInt32: { auto ptr = (const uint32_t*)data; diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h index 5b0527e0f..504b714c5 100644 --- a/tools/render-test/shader-input-layout.h +++ b/tools/render-test/shader-input-layout.h @@ -6,6 +6,7 @@ #include "core/slang-writer.h" + #include "bind-location.h" #include "render.h" diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index a6ad0cff3..85425130d 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -4,6 +4,7 @@ #include "../../source/core/slang-token-reader.h" #include "../../source/core/slang-std-writers.h" #include "../../source/core/slang-hex-dump-util.h" +#include "../../source/core/slang-type-text-util.h" #include "../../slang-com-helper.h" @@ -708,7 +709,7 @@ static SlangResult _extractSlangCTestRequirements(const CommandLine& cmdLine, Te String passThrough; if (SLANG_SUCCEEDED(_extractArg(cmdLine, "-pass-through", passThrough))) { - ioRequirements->addUsedBackEnd(DownstreamCompiler::getPassThroughFromName(passThrough.getUnownedSlice())); + ioRequirements->addUsedBackEnd(TypeTextUtil::asPassThrough(passThrough.getUnownedSlice())); } } @@ -1995,6 +1996,147 @@ TestResult runPerformanceProfile(TestContext* context, TestInput& input) return TestResult::Pass; } + +static double _textToDouble(const UnownedStringSlice& slice) +{ + Index size = Index(slice.size()); + // We have to zero terminate to be able to use atof + const Index maxSize = 80; + char buffer[maxSize + 1]; + + size = (size > maxSize) ? maxSize : size; + + memcpy(buffer, slice.begin(), size); + buffer[size] = 0; + + return atof(buffer); +} + +static bool _areNearlyEqual(double a, double b, double epsilon) +{ + // If they are equal then we are done + if (a == b) + { + return true; + } + + const double absA = abs(a); + const double absB = abs(b); + const double diff = abs(a - b); + + // https://en.wikipedia.org/wiki/Double_precision_floating-point_format + // + const double minNormal = 2.2250738585072014e10-308; + + // Either a or b are very close to being zero, so doing relative comparison isn't really appropriate + if (a == 0.0 || b == 0.0 || (absA + absB < minNormal)) + { + return diff < (epsilon * minNormal); + } + else + { + // Calculate a relative relative error + return diff < epsilon * (absA + absB); + } +} + +static void _calcLines(const UnownedStringSlice& slice, List& outLines) +{ + StringUtil::calcLines(slice, outLines); + + // Remove any trailing empty lines + while (outLines.getCount()) + { + if (outLines.getLast().trim() == UnownedStringSlice()) + { + outLines.removeLast(); + } + else + { + break; + } + } +} + +static SlangResult _compareWithType(const UnownedStringSlice& actual, const UnownedStringSlice& ref, double differenceThreshold = 0.001) +{ + typedef slang::TypeReflection::ScalarType ScalarType; + + ScalarType scalarType = ScalarType::None; + + // We just do straight comparison if there is no type + + List linesActual, linesRef; + + _calcLines(actual, linesActual); + _calcLines(ref, linesRef); + + // If there are more lines in actual, we just ignore them, to keep same behavior as before + if (linesRef.getCount() < linesActual.getCount()) + { + linesActual.setCount(linesRef.getCount()); + } + + if (linesActual.getCount() != linesRef.getCount()) + { + return SLANG_FAIL; + } + + for (Index i = 0; i < linesActual.getCount(); ++i) + { + const UnownedStringSlice lineActual = linesActual[i]; + const UnownedStringSlice lineRef = linesRef[i]; + + if (lineActual.startsWith(UnownedStringSlice::fromLiteral("type:"))) + { + if (lineActual != lineRef) + { + return SLANG_FAIL; + } + // Get the type + List split; + StringUtil::split(lineActual, ':', split); + + if (split.getCount() != 2) + { + return SLANG_FAIL; + } + + scalarType = TypeTextUtil::asScalarType(split[1].trim()); + continue; + } + + switch (scalarType) + { + default: + { + if (lineActual.trim() != lineRef.trim()) + { + return SLANG_FAIL; + } + break; + } + case ScalarType::Float16: + case ScalarType::Float32: + case ScalarType::Float64: + { + + // Compare as double + double valueA = _textToDouble(lineActual); + double valueB = _textToDouble(lineRef); + + if (!_areNearlyEqual(valueA, valueB, differenceThreshold)) + { + return SLANG_FAIL; + } + break; + } + } + } + + return SLANG_OK; +} + TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, const char *const* langOpts, size_t numLangOpts) { // TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass @@ -2035,8 +2177,8 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons return TestResult::Pass; } - const String referenceOutput = findExpectedPath(input, ".expected.txt"); - if (referenceOutput.getLength() <= 0) + const String referenceOutputFile = findExpectedPath(input, ".expected.txt"); + if (referenceOutputFile.getLength() <= 0) { return TestResult::Fail; } @@ -2060,33 +2202,20 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons printf("render-test output:\n%s\n", actualOutput.getBuffer()); return TestResult::Fail; } - if (!File::exists(referenceOutput)) + if (!File::exists(referenceOutputFile)) { - printf("referenceOutput %s not found.\n", referenceOutput.getBuffer()); + printf("referenceOutput %s not found.\n", referenceOutputFile.getBuffer()); return TestResult::Fail; } auto actualOutputContent = File::readAllText(actualOutputFile); - auto actualProgramOutput = Split(actualOutputContent, '\n'); - auto referenceProgramOutput = Split(File::readAllText(referenceOutput), '\n'); - auto printOutput = [&]() - { - context->reporter->messageFormat(TestMessageType::TestFailure, "output mismatch! actual output: {\n%s\n}, \n%s\n", actualOutputContent.getBuffer(), actualOutput.getBuffer()); - }; - if (actualProgramOutput.getCount() < referenceProgramOutput.getCount()) + auto referenceOutputContent = File::readAllText(referenceOutputFile); + + if (SLANG_FAILED(_compareWithType(actualOutputContent.getUnownedSlice(), referenceOutputContent.getUnownedSlice()))) { - printOutput(); - return TestResult::Fail; + context->reporter->messageFormat(TestMessageType::TestFailure, "output mismatch! actual output: {\n%s\n}, \n%s\n", actualOutputContent.getBuffer(), referenceOutputContent.getBuffer()); + return TestResult::Fail; } - for (Index i = 0; i < referenceProgramOutput.getCount(); i++) - { - auto reference = String(referenceProgramOutput[i].trim()); - auto actual = String(actualProgramOutput[i].trim()); - if (actual != reference) - { - printOutput(); - return TestResult::Fail; - } - } + return TestResult::Pass; } @@ -2536,7 +2665,7 @@ static void _calcSynthesizedTests(TestContext* context, RenderApiType synthRende { // const auto& language = srcTest.options.args[index + 1]; - SlangSourceLanguage sourceLanguage = DownstreamCompiler::getSourceLanguageFromName(language.getUnownedSlice()); + SlangSourceLanguage sourceLanguage = TypeTextUtil::asSourceLanguage(language.getUnownedSlice()); bool isCrossCompile = true; -- cgit v1.2.3