From 49ed6b60d662906f290578f802f80b0ead1a2b9d Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Wed, 12 Dec 2018 08:57:48 -0500 Subject: Running tests in slang-test process (#740) * First pass at having an interface to write text to that can be replaced. Simplifed and made more rigerous the interface used to write formatted strings. * Added AppContext to simplify setting up and parsing around of streams. * Added more simplified way to get the std error/out from AppContext. * Work in progress using dll for tools to speed up testing. * First pass at ISlangWriter interface. * Added support for writing VaArgs. Added NullWriter. * Use ISlangWriter for output. * Use ISlangWriter for output - replacing OutputCallback. Make IRDump go to ISlangWriter * SlangWriterTargetType -> SlangWriterChannel Improvements around AppContext * Shared library working with slang-reflection-test. * Dll testing working for render-test. * Include va_list definintion from header. * Fix errors from clang. * Fix typo for linux. * Added -usexes option * Fix typo. * Fix arguments problem on linux. * Fix typo for linux. * Add windows tool shared library projects. * Fix warning from x86 win build. Fix signed warning from slang-test/main.cpp * First attempt at getting premake to work on travis, and run tests. * Try moving build out into script. * Invoke bash scripts so they don't have to be executable. * Drive configuration/tests from env parameters set by travis * Try using source to run travis tests. * Remove the build.linux directory - but doing so will overwrite Makefile. * Made -fno-delete-null-pointer-checks gcc only. * Try to fix warning from -fno-delete-null-pointer-checks * Turn of warnings for unknown switches. * Try to make premake choose the correct tooling. * Disabled missing braces warning. * Disable -Wundefined-var-template on clang. * -Wunused-function disabled for clang. * Fix typo due to SlangBool. * Remove this nullptr tests. * "-Wno-unused-private-field" for clang. * Added "-Wno-undefined-bool-conversion" * Add DominatorList::end fix. * Split scripts into travis_build.sh travis_test.sh * Fix gcc/clang template pre-declaration issue around QualType. * Fix premake to build such that pthread correctly links with slang-glslang --- .travis.yml | 4 +- examples/hello-world/hello-world.vcxproj | 2 +- examples/model-viewer/model-viewer.vcxproj | 2 +- premake5.lua | 82 +++++-- slang.h | 95 ++++++++- slang.sln | 75 +++++-- source/core/core.vcxproj | 4 + source/core/core.vcxproj.filters | 12 ++ source/core/slang-app-context.cpp | 67 ++++++ source/core/slang-app-context.h | 60 ++++++ source/core/slang-string-util.cpp | 39 ++-- source/core/slang-string-util.h | 9 + source/core/slang-string.cpp | 24 +++ source/core/slang-string.h | 8 +- source/core/slang-writer.cpp | 152 +++++++++++++ source/core/slang-writer.h | 145 +++++++++++++ source/slang/check.cpp | 31 +-- source/slang/compiler.cpp | 46 ++-- source/slang/compiler.h | 20 ++ source/slang/diagnostics.cpp | 8 +- source/slang/diagnostics.h | 3 +- source/slang/emit.cpp | 4 +- source/slang/ir.cpp | 20 +- source/slang/ir.h | 4 +- source/slang/lookup.cpp | 16 +- source/slang/lower-to-ir.cpp | 4 +- source/slang/slang.cpp | 65 +++++- source/slang/syntax-base-defs.h | 1 + source/slang/syntax.cpp | 13 +- source/slang/syntax.h | 12 +- source/slang/type-layout.cpp | 5 +- source/slangc/main.cpp | 65 +++--- source/slangc/slangc-shared-library.vcxproj | 178 ++++++++++++++++ .../slangc/slangc-shared-library.vcxproj.filters | 13 ++ tools/render-test/main.cpp | 235 ++++++++++++++------- tools/render-test/options.cpp | 46 ++-- tools/render-test/options.h | 5 +- .../render-test/render-test-shared-library.vcxproj | 210 ++++++++++++++++++ .../render-test-shared-library.vcxproj.filters | 48 +++++ tools/render-test/shader-renderer-util.cpp | 2 +- tools/render-test/slang-support.cpp | 4 +- tools/render-test/slang-support.h | 5 +- tools/slang-reflection-test/main.cpp | 28 ++- .../slang-reflection-test-shared-library.vcxproj | 182 ++++++++++++++++ ...-reflection-test-shared-library.vcxproj.filters | 13 ++ .../slang-reflection-test.vcxproj | 3 + tools/slang-test/main.cpp | 131 ++++++++---- tools/slang-test/os.cpp | 8 +- tools/slang-test/os.h | 5 +- tools/slang-test/test-context.cpp | 30 +++ tools/slang-test/test-context.h | 21 +- travis_build.sh | 13 ++ travis_test.sh | 19 ++ 53 files changed, 1961 insertions(+), 335 deletions(-) create mode 100644 source/core/slang-app-context.cpp create mode 100644 source/core/slang-app-context.h create mode 100644 source/core/slang-writer.cpp create mode 100644 source/core/slang-writer.h create mode 100644 source/slangc/slangc-shared-library.vcxproj create mode 100644 source/slangc/slangc-shared-library.vcxproj.filters create mode 100644 tools/render-test/render-test-shared-library.vcxproj create mode 100644 tools/render-test/render-test-shared-library.vcxproj.filters create mode 100644 tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj create mode 100644 tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj.filters create mode 100644 travis_build.sh create mode 100644 travis_test.sh diff --git a/.travis.yml b/.travis.yml index 6be27eefc..e1ea1a8d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,8 +39,8 @@ matrix: # `./configure && make && make test` which is appropriate # for autoconf, but I don't want to get into that mess.. # -script: make && make test - +script: bash ./travis_build.sh && bash ./travis_test.sh + before_deploy: | export SLANG_OS_NAME=${TRAVIS_OS_NAME} export SLANG_ARCH_NAME=`uname -p` diff --git a/examples/hello-world/hello-world.vcxproj b/examples/hello-world/hello-world.vcxproj index 9efb28688..db6f52a86 100644 --- a/examples/hello-world/hello-world.vcxproj +++ b/examples/hello-world/hello-world.vcxproj @@ -19,7 +19,7 @@ - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA} + {010BE414-ED5B-CF56-16C0-BD18027062C0} true Win32Proj hello-world diff --git a/examples/model-viewer/model-viewer.vcxproj b/examples/model-viewer/model-viewer.vcxproj index ea7ee1521..97896612d 100644 --- a/examples/model-viewer/model-viewer.vcxproj +++ b/examples/model-viewer/model-viewer.vcxproj @@ -19,7 +19,7 @@ - {639B13F2-CF07-CFEC-98FB-664A0427F154} + {2F8724C6-1BC3-2730-84D5-3F277030D04A} true Win32Proj model-viewer diff --git a/premake5.lua b/premake5.lua index 7bf5a34ca..16b599e1a 100644 --- a/premake5.lua +++ b/premake5.lua @@ -115,13 +115,13 @@ workspace "slang" architecture "ARM" filter { "toolset:clang or gcc*" } - buildoptions { "-Wno-unused-parameter", "-Wno-type-limits", "-Wno-sign-compare", "-Wno-unused-variable", "-Wno-reorder", "-Wno-switch", "-Wno-return-type", "-Wno-unused-local-typedefs", "-Wno-parentheses", "-std=c++11", "-fvisibility=hidden", "-fno-delete-null-pointer-checks" } + buildoptions { "-Wno-unused-parameter", "-Wno-type-limits", "-Wno-sign-compare", "-Wno-unused-variable", "-Wno-reorder", "-Wno-switch", "-Wno-return-type", "-Wno-unused-local-typedefs", "-Wno-parentheses", "-std=c++11", "-fvisibility=hidden" , "-fno-delete-null-pointer-checks", "-Wno-ignored-optimization-argument", "-Wno-unknown-warning-option"} filter { "toolset:gcc*"} - buildoptions { "-Wno-nonnull-compare", "-Wno-unused-but-set-variable", "-Wno-implicit-fallthrough" } + buildoptions { "-Wno-nonnull-compare", "-Wno-unused-but-set-variable", "-Wno-implicit-fallthrough" } filter { "toolset:clang" } - buildoptions { "-Wno-deprecated-register", "-Wno-tautological-compare"} + buildoptions { "-Wno-deprecated-register", "-Wno-tautological-compare", "-Wno-missing-braces", "-Wno-undefined-var-template", "-Wno-unused-function", "-Wno-undefined-bool-conversion"} -- When compiling the debug configuration, we want to turn -- optimization off, make sure debug symbols are output, @@ -193,20 +193,15 @@ end -- Next we will define a helper routine that all of our -- projects will bottleneck through. Here `name` is -- the name for the project (and the base name for --- whatever output file it produces), while `baseDir` --- is the parent directory of the project's directory. +-- whatever output file it produces), while `sourceDir` +-- is the directory that holds the source. -- -- E.g., for the `slangc` project, the source code -- is nested in `source/`, so we'd (indirectly) call: -- --- baseSlangProject("slangc", "source") +-- baseSlangProject("slangc", "source/slangc") -- -function baseSlangProject(name, baseDir) - - -- The project directory will be nested inside - -- the base directory using the project's name. - -- - local projectDir = baseDir .. "/" .. name +function baseSlangProject(name, sourceDir) -- Start a new project in premake. This switches -- the "current" project over to the newly created @@ -220,7 +215,7 @@ function baseSlangProject(name, baseDir) -- projects. If we don't have a stable UUID, then the -- output files might have spurious diffs whenever we -- re-run premake generation. - uuid(os.uuid(projectDir)) + uuid(os.uuid(name .. '|' .. sourceDir)) -- Set the location where the project file will be placed. -- We set the project files to reside in their source @@ -233,7 +228,7 @@ function baseSlangProject(name, baseDir) -- it is less relevant to other projects. -- - location(projectDir) + location(sourceDir) if os.target() == "windows" then else @@ -257,7 +252,7 @@ function baseSlangProject(name, baseDir) -- so projects that spread their source over multiple -- directories will need to take more steps. -- - addSourceDir(projectDir) + addSourceDir(sourceDir) -- By default, Premake generates VS project files that -- reflect the directory structure of the source code. @@ -314,7 +309,7 @@ function tool(name) -- Now we invoke our shared project configuration logic, -- specifying that the project lives under the `tools/` path. -- - baseSlangProject(name, "tools") + baseSlangProject(name, "tools/" .. name) -- Finally, we set the project "kind" to produce a console -- application. This is a reasonable default for tools, @@ -339,7 +334,29 @@ function standardProject(name) -- A standard project has its code under `source/` -- - baseSlangProject(name, "source") + baseSlangProject(name, "source/" .. name) +end + +function toolSharedLibrary(name) + group "tool-shared-library" + -- specifying that the project lives under the `tools/` path. + -- + baseSlangProject(name .. "-shared-library", "tools/" .. name) + + defines { "SLANG_SHARED_LIBRARY_TOOL" } + + kind "SharedLib" +end + +function standardSharedLibraryProject(name) + group "tool-shared-library" + -- A standard project has its code under `source/` + -- + baseSlangProject(name .. "-shared-library", "source/".. name) + + defines { "SLANG_SHARED_LIBRARY_TOOL" } + + kind "SharedLib" end -- Finally we have the example programs that show how to use Slang. @@ -349,7 +366,7 @@ function example(name) group "examples" -- They have their source code under `examples//` - baseSlangProject(name, "examples") + baseSlangProject(name, "examples/" .. name) -- By default, all of our examples are GUI applications. One some -- platforms there is no meaningful distinction between GUI and @@ -446,8 +463,15 @@ tool "slang-test" tool "slang-reflection-test" uuid "22C45F4F-FB6B-4535-BED1-D3F5D0C71047" includedirs { "." } - links { "slang" } + links { "slang", "core" } +toolSharedLibrary "slang-reflection-test" + uuid "C5ACCA6E-C04D-4B36-8516-3752B3C13C2F" + + includedirs { "." } + kind "SharedLib" + links { "core", "slang" } + -- -- The most complex testing tool we have is `render-test`, but from -- a build perspective the most interesting thing about it is that for @@ -475,6 +499,20 @@ if os.target() == "windows" then -- dxcompiler.dll, and dxil.dll from the Windows SDK redistributable -- directory into the output directory. postbuildcommands { '"$(SolutionDir)tools\\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/%{cfg.platform:lower()}/" "%{cfg.targetdir}/"'} + + toolSharedLibrary "render-test" + uuid "61F7EB00-7281-4BF3-9470-7C2EA92620C3" + + includedirs { ".", "external", "source", "tools/gfx" } + links { "core", "slang", "gfx" } + + systemversion "10.0.14393.0" + + -- For Windows targets, we want to copy d3dcompiler_47.dll, + -- dxcompiler.dll, and dxil.dll from the Windows SDK redistributable + -- directory into the output directory. + postbuildcommands { '"$(SolutionDir)tools\\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/%{cfg.platform:lower()}/" "%{cfg.targetdir}/"'} + end -- @@ -516,6 +554,11 @@ standardProject "slangc" kind "ConsoleApp" links { "core", "slang" } +standardSharedLibraryProject "slangc" + uuid "644921BF-D228-4EEF-8CDA-11716DB06989" + kind "SharedLib" + links { "core", "slang" } + -- -- TODO: Slang's current `Makefile` build does some careful incantations -- to make sure that the binaries it generates use a "relative `RPATH`" @@ -665,6 +708,7 @@ standardProject "slang-glslang" removefiles { "external/glslang/glslang/OSDependent/Windows/main.cpp" } filter { "system:linux" } + links { "dl", "pthread" } addSourceDir("external/glslang/glslang/OSDependent/Unix") buildoptions{"-fPIC", "-pthread"} diff --git a/slang.h b/slang.h index 8085bf15c..5ea1d13f8 100644 --- a/slang.h +++ b/slang.h @@ -192,19 +192,25 @@ convention for interface methods. #define SLANG_DYNAMIC #endif +#if defined(_MSC_VER) +# define SLANG_DLL_EXPORT __declspec(dllexport) +#else +# define SLANG_DLL_EXPORT __attribute__((__visibility__("default"))) +#endif + #if defined(SLANG_DYNAMIC) - #if defined(_MSC_VER) - #ifdef SLANG_DYNAMIC_EXPORT - #define SLANG_API __declspec(dllexport) - #else - #define SLANG_API __declspec(dllimport) - #endif - #else +# if defined(_MSC_VER) +# ifdef SLANG_DYNAMIC_EXPORT +# define SLANG_API SLANG_DLL_EXPORT +# else +# define SLANG_API __declspec(dllimport) +# endif +# else // TODO: need to consider compiler capabilities -// #ifdef SLANG_DYNAMIC_EXPORT - #define SLANG_API __attribute__((__visibility__("default"))) -// #endif - #endif +//# ifdef SLANG_DYNAMIC_EXPORT +# define SLANG_API SLANG_DLL_EXPORT +//# endif +# endif #endif #ifndef SLANG_API @@ -283,6 +289,13 @@ convention for interface methods. # define SLANG_UINT64(x) (x##ull) #endif + +#ifdef __cplusplus +# define SLANG_EXTERN_C extern "C" +#else +# define SLANG_EXTERN_C +#endif + #ifdef __cplusplus // C++ specific macros // Gcc @@ -339,6 +352,8 @@ convention for interface methods. #include #endif // ! SLANG_NO_STDDEF +#include + #ifdef __cplusplus extern "C" { @@ -354,6 +369,8 @@ extern "C" typedef uint32_t SlangUInt32; typedef intptr_t SlangInt; typedef uintptr_t SlangUInt; + + typedef bool SlangBool; /*! @brief Severity of a diagnostic generated by the compiler. @@ -634,6 +651,8 @@ extern "C" #define SLANG_E_CANNOT_OPEN SLANG_MAKE_CORE_ERROR(4) //! Indicates a file/resource could not be found #define SLANG_E_NOT_FOUND SLANG_MAKE_CORE_ERROR(5) + //! An unhandled internal failure (typically from unhandled exception) +#define SLANG_E_INTERNAL_FAIL SLANG_MAKE_CORE_ERROR(6) /** A "Universally Unique Identifier" (UUID) @@ -810,6 +829,51 @@ extern "C" #define SLANG_UUID_ISlangFileSystemExt { 0x5fb632d2, 0x979d, 0x4481, { 0x9f, 0xee, 0x66, 0x3c, 0x3f, 0x14, 0x49, 0xe1 } } + /* Identifies different types of writer target*/ + typedef unsigned int SlangWriterChannel; + enum + { + SLANG_WRITER_CHANNEL_DIAGNOSTIC, + SLANG_WRITER_CHANNEL_STD_OUTPUT, + SLANG_WRITER_CHANNEL_STD_ERROR, + SLANG_WRITER_CHANNEL_COUNT_OF, + }; + + typedef unsigned int SlangWriterMode; + enum + { + SLANG_WRITER_MODE_TEXT, + SLANG_WRITER_MODE_BINARY, + }; + + /** A stream typically of text, used for outputting diagnostic as well as other information. + */ + struct ISlangWriter : public ISlangUnknown + { + public: + /** Write the formatted string with the used args. If returns SLANG_E_NOT_IMPLEMENTED, will use a default internal implementation, and call write with result + @param format Format string + @param args The arguments + @return SLANG_E_NOT_IMPLEMENTED if not implemented, SLANG_OK on success */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL writeVaList(const char* format, va_list args) = 0; + /** Write text to the writer + @param chars The characters to write out + @param numChars The amount of characters + @returns SLANG_OK on success */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL write(const char* chars, size_t numChars) = 0; + /** Flushes any content to the ouput */ + virtual SLANG_NO_THROW void SLANG_MCALL flush() = 0; + /** Determines if the writer stream is to the console, and can be used to alter the output + @returns Returns true if is a console writer */ + virtual SLANG_NO_THROW SlangBool SLANG_MCALL isConsole() = 0; + /** Set the mode for the writer to use + @param mode The mode to use + @returns SLANG_OK on success */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL setMode(SlangWriterMode mode) = 0; + }; + + #define SLANG_UUID_ISlangWriter { 0xec457f0e, 0x9add, 0x4e6b,{ 0x85, 0x1c, 0xd7, 0xfa, 0x71, 0x6d, 0x15, 0xfd } }; + /*! @brief An instance of the Slang library. */ @@ -984,6 +1048,15 @@ extern "C" SlangDiagnosticCallback callback, void const* userData); + SLANG_API void spSetWriter( + SlangCompileRequest* request, + SlangWriterChannel channel, + ISlangWriter* writer); + + SLANG_API ISlangWriter* spGetWriter( + SlangCompileRequest* request, + SlangWriterChannel channel); + /*! @brief Add a path to use when searching for referenced files. This will be used for both `#include` directives and also for explicit `__import` declarations. diff --git a/slang.sln b/slang.sln index 1556f1963..83e94605a 100644 --- a/slang.sln +++ b/slang.sln @@ -5,9 +5,9 @@ VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello-world", "examples\hello-world\hello-world.vcxproj", "{5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello-world", "examples\hello-world\hello-world.vcxproj", "{010BE414-ED5B-CF56-16C0-BD18027062C0}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "model-viewer", "examples\model-viewer\model-viewer.vcxproj", "{639B13F2-CF07-CFEC-98FB-664A0427F154}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "model-viewer", "examples\model-viewer\model-viewer.vcxproj", "{2F8724C6-1BC3-2730-84D5-3F277030D04A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "source\core\core.vcxproj", "{F9BE7957-8399-899E-0C49-E714FDDD4B65}" EndProject @@ -23,6 +23,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "render-test", "tools\render EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx", "tools\gfx\gfx.vcxproj", "{222F7498-B40C-4F3F-A704-DDEB91A4484A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tool-shared-library", "tool-shared-library", "{3082A9DB-9C44-DD65-E5F4-6BF251F6B543}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-reflection-test-shared-library", "tools\slang-reflection-test\slang-reflection-test-shared-library.vcxproj", "{C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "render-test-shared-library", "tools\render-test\render-test-shared-library.vcxproj", "{61F7EB00-7281-4BF3-9470-7C2EA92620C3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slangc-shared-library", "source\slangc\slangc-shared-library.vcxproj", "{644921BF-D228-4EEF-8CDA-11716DB06989}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slangc", "source\slangc\slangc.vcxproj", "{D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang", "source\slang\slang.vcxproj", "{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}" @@ -40,22 +48,22 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Debug|Win32.ActiveCfg = Debug|Win32 - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Debug|Win32.Build.0 = Debug|Win32 - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Debug|x64.ActiveCfg = Debug|x64 - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Debug|x64.Build.0 = Debug|x64 - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Release|Win32.ActiveCfg = Release|Win32 - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Release|Win32.Build.0 = Release|Win32 - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Release|x64.ActiveCfg = Release|x64 - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Release|x64.Build.0 = Release|x64 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Debug|Win32.ActiveCfg = Debug|Win32 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Debug|Win32.Build.0 = Debug|Win32 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Debug|x64.ActiveCfg = Debug|x64 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Debug|x64.Build.0 = Debug|x64 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Release|Win32.ActiveCfg = Release|Win32 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Release|Win32.Build.0 = Release|Win32 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Release|x64.ActiveCfg = Release|x64 - {639B13F2-CF07-CFEC-98FB-664A0427F154}.Release|x64.Build.0 = Release|x64 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Debug|Win32.ActiveCfg = Debug|Win32 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Debug|Win32.Build.0 = Debug|Win32 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Debug|x64.ActiveCfg = Debug|x64 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Debug|x64.Build.0 = Debug|x64 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Release|Win32.ActiveCfg = Release|Win32 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Release|Win32.Build.0 = Release|Win32 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Release|x64.ActiveCfg = Release|x64 + {010BE414-ED5B-CF56-16C0-BD18027062C0}.Release|x64.Build.0 = Release|x64 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Debug|Win32.ActiveCfg = Debug|Win32 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Debug|Win32.Build.0 = Debug|Win32 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Debug|x64.ActiveCfg = Debug|x64 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Debug|x64.Build.0 = Debug|x64 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Release|Win32.ActiveCfg = Release|Win32 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Release|Win32.Build.0 = Release|Win32 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Release|x64.ActiveCfg = Release|x64 + {2F8724C6-1BC3-2730-84D5-3F277030D04A}.Release|x64.Build.0 = Release|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.ActiveCfg = Debug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.Build.0 = Debug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|x64.ActiveCfg = Debug|x64 @@ -104,6 +112,30 @@ Global {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|Win32.Build.0 = Release|Win32 {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.ActiveCfg = Release|x64 {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.Build.0 = Release|x64 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Debug|Win32.ActiveCfg = Debug|Win32 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Debug|Win32.Build.0 = Debug|Win32 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Debug|x64.ActiveCfg = Debug|x64 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Debug|x64.Build.0 = Debug|x64 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Release|Win32.ActiveCfg = Release|Win32 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Release|Win32.Build.0 = Release|Win32 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Release|x64.ActiveCfg = Release|x64 + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F}.Release|x64.Build.0 = Release|x64 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Debug|Win32.ActiveCfg = Debug|Win32 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Debug|Win32.Build.0 = Debug|Win32 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Debug|x64.ActiveCfg = Debug|x64 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Debug|x64.Build.0 = Debug|x64 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Release|Win32.ActiveCfg = Release|Win32 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Release|Win32.Build.0 = Release|Win32 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Release|x64.ActiveCfg = Release|x64 + {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Release|x64.Build.0 = Release|x64 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Debug|Win32.ActiveCfg = Debug|Win32 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Debug|Win32.Build.0 = Debug|Win32 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Debug|x64.ActiveCfg = Debug|x64 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Debug|x64.Build.0 = Debug|x64 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Release|Win32.ActiveCfg = Release|Win32 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Release|Win32.Build.0 = Release|Win32 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Release|x64.ActiveCfg = Release|x64 + {644921BF-D228-4EEF-8CDA-11716DB06989}.Release|x64.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.ActiveCfg = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.Build.0 = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.ActiveCfg = Debug|x64 @@ -133,12 +165,15 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} - {639B13F2-CF07-CFEC-98FB-664A0427F154} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} + {010BE414-ED5B-CF56-16C0-BD18027062C0} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} + {2F8724C6-1BC3-2730-84D5-3F277030D04A} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} {66174227-8541-41FC-A6DF-4764FC66F78E} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {0C768A18-1D25-4000-9F37-DA5FE99E3B64} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {22C45F4F-FB6B-4535-BED1-D3F5D0C71047} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {96610759-07B9-4EEB-A974-5C634A2E742B} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {222F7498-B40C-4F3F-A704-DDEB91A4484A} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F} = {3082A9DB-9C44-DD65-E5F4-6BF251F6B543} + {61F7EB00-7281-4BF3-9470-7C2EA92620C3} = {3082A9DB-9C44-DD65-E5F4-6BF251F6B543} + {644921BF-D228-4EEF-8CDA-11716DB06989} = {3082A9DB-9C44-DD65-E5F4-6BF251F6B543} EndGlobalSection EndGlobal diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index 97dc19d66..5dad716fb 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -182,6 +182,7 @@ + @@ -194,6 +195,7 @@ + @@ -202,6 +204,7 @@ + @@ -212,6 +215,7 @@ + diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters index b470f9b43..c5c1fc733 100644 --- a/source/core/core.vcxproj.filters +++ b/source/core/core.vcxproj.filters @@ -45,6 +45,9 @@ Header Files + + Header Files + Header Files @@ -81,6 +84,9 @@ Header Files + + Header Files + Header Files @@ -101,6 +107,9 @@ Source Files + + Source Files + Source Files @@ -131,6 +140,9 @@ Source Files + + Source Files + Source Files diff --git a/source/core/slang-app-context.cpp b/source/core/slang-app-context.cpp new file mode 100644 index 000000000..2b3f32cbc --- /dev/null +++ b/source/core/slang-app-context.cpp @@ -0,0 +1,67 @@ + +#include "slang-app-context.h" + +#include "slang-writer.h" + +namespace Slang +{ + +/* static */AppContext* AppContext::s_singleton = nullptr; + + +/* static */AppContext* AppContext::getDefault() +{ + static AppContext* s_context = nullptr; + + if (!s_context) + { + static FileWriter s_stdError(stderr, WriterFlag::IsStatic | WriterFlag::IsUnowned | WriterFlag::AutoFlush); + static FileWriter s_stdOut(stdout, WriterFlag::IsStatic | WriterFlag::IsUnowned | WriterFlag::AutoFlush); + + static AppContext s_contextVar; + s_context = &s_contextVar; + + s_context->setWriter(SLANG_WRITER_CHANNEL_STD_ERROR, &s_stdError); + s_context->setWriter(SLANG_WRITER_CHANNEL_STD_OUTPUT, &s_stdOut); + } + return s_context; +} + +/* static */AppContext* AppContext::initDefault() +{ + AppContext* context = getDefault(); + setSingleton(context); + return context; +} + +/* static */int AppContext::getReturnCode(SlangResult res) +{ + if (SLANG_SUCCEEDED(res)) + { + return 0; + } + else if (res == SLANG_E_INTERNAL_FAIL) + { + return -1; + } + return 1; +} + +void AppContext::setRequestWriters(SlangCompileRequest* request) +{ + for (int i = 0; i < SLANG_WRITER_CHANNEL_COUNT_OF; ++i) + { + if (m_replaceWriterFlags & (1 << i)) + { + spSetWriter(request, SlangWriterChannel(i), m_writers[i]); + } + } +} + +void AppContext::configureRequest(SlangCompileRequest* request) +{ + setRequestWriters(request); +} + +} + diff --git a/source/core/slang-app-context.h b/source/core/slang-app-context.h new file mode 100644 index 000000000..9f1eb306c --- /dev/null +++ b/source/core/slang-app-context.h @@ -0,0 +1,60 @@ +#ifndef SLANG_APP_CONTEXT_H +#define SLANG_APP_CONTEXT_H + +#include "slang-writer.h" +#include "../../slang-com-ptr.h" + +namespace Slang +{ + +#ifdef SLANG_SHARED_LIBRARY_TOOL +# define SLANG_SHARED_LIBRARY_TOOL_API SLANG_EXTERN_C SLANG_DLL_EXPORT +#else +# define SLANG_SHARED_LIBRARY_TOOL_API +#endif + +/* A structure to hold general state shared across an application */ +class AppContext +{ +public: + + ISlangWriter * getWriter(SlangWriterChannel chan) const { return m_writers[chan]; } + void setWriter(SlangWriterChannel chan, ISlangWriter* writer) { m_writers[chan] = writer; } + + /// Make modifications to the request + void configureRequest(SlangCompileRequest* request); + + void setRequestWriters(SlangCompileRequest* request); + + void setReplaceWriterFlagsAll() { setReplaceWriterFlags((1 << SLANG_WRITER_CHANNEL_COUNT_OF) - 1); } + void setReplaceWriterFlags(int flags) { m_replaceWriterFlags = flags; } + int getReplaceWriterFlags() const { return m_replaceWriterFlags; } + + /// Ctor + AppContext() : m_replaceWriterFlags(0) {} + + /// Initialize a default context + static AppContext* initDefault(); + + static AppContext* getDefault(); + + static AppContext* getSingleton() { return s_singleton; } + static void setSingleton(AppContext* context) { s_singleton = context; } + + static WriterHelper getStdError() { return getSingleton()->getWriter(SLANG_WRITER_CHANNEL_STD_ERROR); } + static WriterHelper getStdOut() { return getSingleton()->getWriter(SLANG_WRITER_CHANNEL_STD_OUTPUT); } + static WriterHelper getDiagnostic() { return getSingleton()->getWriter(SLANG_WRITER_CHANNEL_DIAGNOSTIC); } + + static int getReturnCode(SlangResult res); + +protected: + + ComPtr m_writers[SLANG_WRITER_CHANNEL_COUNT_OF]; + int m_replaceWriterFlags; ///< Bit for each writer + + static AppContext* s_singleton; +}; + +} + +#endif diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp index 6d0d896a1..cb5709759 100644 --- a/source/core/slang-string-util.cpp +++ b/source/core/slang-string-util.cpp @@ -39,32 +39,39 @@ static const Guid IID_ISlangBlob = SLANG_UUID_ISlangBlob; } } - -/* static */void StringUtil::append(const char* format, va_list args, StringBuilder& buf) +/* static */size_t StringUtil::calcFormattedSize(const char* format, va_list args) { - int numChars = 0; +#if SLANG_WINDOWS_FAMILY + return _vscprintf(format, args); +#else + return vsnprintf(nullptr, 0, format, args); +#endif +} +/* static */void StringUtil::calcFormatted(const char* format, va_list args, size_t numChars, char* dst) +{ #if SLANG_WINDOWS_FAMILY - numChars = _vscprintf(format, args); + vsnprintf_s(dst, numChars + 1, _TRUNCATE, format, args); #else + vsnprintf(dst, numChars + 1, format, args); +#endif +} + +/* static */void StringUtil::append(const char* format, va_list args, StringBuilder& buf) +{ + // Calculate the size + size_t numChars; { + // Create a copy of args, as will be consumed by calcFormattedSize va_list argsCopy; va_copy(argsCopy, args); - numChars = vsnprintf(nullptr, 0, format, argsCopy); + numChars = calcFormattedSize(format, argsCopy); va_end(argsCopy); } -#endif - - List chars; - chars.SetSize(numChars + 1); - -#if SLANG_WINDOWS_FAMILY - vsnprintf_s(chars.Buffer(), numChars + 1, _TRUNCATE, format, args); -#else - vsnprintf(chars.Buffer(), numChars + 1, format, args); -#endif - buf.Append(chars.Buffer(), numChars); + char* dst = buf.prepareForAppend(numChars + 1); + calcFormatted(format, args, numChars, dst); + buf.appendInPlace(dst, numChars); } /* static */void StringUtil::appendFormat(StringBuilder& buf, const char* format, ...) diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h index b365b6cf5..0579dd057 100644 --- a/source/core/slang-string-util.h +++ b/source/core/slang-string-util.h @@ -41,6 +41,15 @@ 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); + /// 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); + + /// Calculate the formatted string using the specified args. + /// NOTE! The caller *should* assume this will consume the va_list + /// The buffer should be at least calcFormattedSize + 1 bytes. The +1 is needed because a terminating 0 is written. + static void calcFormatted(const char* format, va_list args, size_t numChars, char* dst); + /// Appends formatted string with args into buf static void append(const char* format, va_list args, StringBuilder& buf); diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp index b195e12d5..648249b2c 100644 --- a/source/core/slang-string.cpp +++ b/source/core/slang-string.cpp @@ -253,7 +253,31 @@ namespace Slang ensureUniqueStorageWithCapacity(newLength); return getData() + oldLength; } + void String::appendInPlace(const char* chars, UInt count) + { + SLANG_UNUSED(chars); + + if (count > 0) + { + SLANG_ASSERT(buffer && buffer->isUniquelyReferenced()); + + auto oldLength = getLength(); + auto newLength = oldLength + count; + + char* dst = buffer->getData(); + // Make sure the input buffer is the same one returned from prepareForAppend + SLANG_ASSERT(chars == dst + oldLength); + // It has to fit within the capacity + SLANG_ASSERT(newLength <= buffer->capacity); + + // We just need to modify the length + buffer->length = newLength; + + // And mark with a terminating 0 + dst[newLength] = 0; + } + } void String::append(const char* textBegin, char const* textEnd) { diff --git a/source/core/slang-string.h b/source/core/slang-string.h index fb60ef562..435e2b29b 100644 --- a/source/core/slang-string.h +++ b/source/core/slang-string.h @@ -329,8 +329,7 @@ namespace Slang } void ensureUniqueStorageWithCapacity(UInt capacity); - char* prepareForAppend(UInt count); - + RefPtr buffer; public: @@ -347,6 +346,11 @@ namespace Slang { } + /// Returns a buffer which can hold at least count chars + char* prepareForAppend(UInt count); + /// Append data written to buffer output via 'prepareForAppend' directly written 'inplace' + void appendInPlace(const char* chars, UInt count); + SLANG_FORCE_INLINE StringRepresentation* getStringRepresentation() const { return buffer; } const char * begin() const diff --git a/source/core/slang-writer.cpp b/source/core/slang-writer.cpp new file mode 100644 index 000000000..4c5df0c8a --- /dev/null +++ b/source/core/slang-writer.cpp @@ -0,0 +1,152 @@ +#include "slang-writer.h" + +#include "platform.h" +#include "slang-string-util.h" + +// Includes to allow us to control console +// output when writing assembly dumps. +#include +#ifdef _WIN32 +# include +#else +# include +#endif + +#include + +namespace Slang +{ +static const Guid IID_ISlangWriter = SLANG_UUID_ISlangWriter; +static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; + +/* !!!!!!!!!!!!!!!!!!!!!!!!! WriterHelper !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +SlangResult WriterHelper::print(const char* format, ...) +{ + va_list args; + va_start(args, format); + SlangResult res = m_writer->writeVaList(format, args); + + if (res == SLANG_E_NOT_IMPLEMENTED) + { + StringBuilder builder; + StringUtil::append(format, args, builder); + + // Write if there is anything to write + res = (builder.Length()) ? m_writer->write(builder.Buffer(), builder.Length()) : SLANG_OK; + } + + va_end(args); + return res; +} + +SlangResult WriterHelper::put(const char* text) +{ + return m_writer->write(text, ::strlen(text)); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! BaseWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +ISlangUnknown* BaseWriter::getInterface(const Guid& guid) +{ + return (guid == IID_ISlangUnknown || guid == IID_ISlangWriter) ? static_cast(this) : nullptr; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! CallbackWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +SlangResult CallbackWriter::write(const char* chars, size_t numChars) +{ + if (numChars > 0) + { + // Make sure zero terminated + StringBuilder builder; + builder.Append(chars, numChars); + + m_callback(builder.Buffer(), (void*)m_data); + } + + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! FileWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +FileWriter::~FileWriter() +{ + if ((m_flags & WriterFlag::IsUnowned) == 0) + { + fclose(m_file); + } +} + +SlangResult FileWriter::writeVaList(const char* format, va_list args) +{ + // http://www.cplusplus.com/reference/cstdio/vfprintf/ + ::vfprintf(m_file, format, args); + + if (m_flags & WriterFlag::AutoFlush) + { + ::fflush(m_file); + } + + return SLANG_OK; +} + +SlangResult FileWriter::write(const char* text, size_t numChars) +{ + const size_t numWritten = ::fwrite(text, sizeof(char), numChars, m_file); + if (m_flags & WriterFlag::AutoFlush) + { + ::fflush(m_file); + } + return numChars == numWritten ? SLANG_OK : SLANG_FAIL; +} + +void FileWriter::flush() +{ + ::fflush(m_file); +} + +/* static */bool FileWriter::isConsole(FILE* file) +{ + const int stdoutFileDesc = _fileno(file); + return _isatty(stdoutFileDesc) != 0; +} + +SlangResult FileWriter::setMode(SlangWriterMode mode) +{ + switch (mode) + { + case SLANG_WRITER_MODE_BINARY: + { +#ifdef _WIN32 + int stdoutFileDesc = _fileno(m_file); + _setmode(stdoutFileDesc, _O_BINARY); + return SLANG_OK; +#else + break; +#endif + } + default: break; + } + return SLANG_FAIL; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! StringWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +SlangResult StringWriter::writeVaList(const char* format, va_list args) +{ + StringUtil::append(format, args, *m_builder); + return SLANG_OK; +} + +SlangResult StringWriter::write(const char* chars, size_t numChars) +{ + if (numChars > 0) + { + m_builder->Append(chars, numChars); + } + return SLANG_OK; +} + +} + diff --git a/source/core/slang-writer.h b/source/core/slang-writer.h new file mode 100644 index 000000000..0fa336d6e --- /dev/null +++ b/source/core/slang-writer.h @@ -0,0 +1,145 @@ +#ifndef SLANG_WRITER_H +#define SLANG_WRITER_H + +#include "slang-string.h" + +#include "../../slang-com-helper.h" + +namespace Slang +{ + + +class WriterHelper +{ +public: + SlangResult print(const char* format, ...); + SlangResult put(const char* text); + + SLANG_FORCE_INLINE void flush() { m_writer->flush(); } + + ISlangWriter* getWriter() const { return m_writer; } + + WriterHelper(ISlangWriter* writer) :m_writer(writer) {} + +protected: + ISlangWriter* m_writer; +}; + +struct WriterFlag +{ + enum Enum :uint32_t + { + IsStatic = 0x1, ///< Means non ref counted + IsConsole = 0x2, ///< True if console + IsUnowned = 0x4, ///< True if doesn't own contained type + AutoFlush = 0x8, ///< Automatically flushes after every call + }; +private: + WriterFlag() = delete; +}; +typedef uint32_t WriterFlags; + +class BaseWriter : public ISlangWriter, public RefObject +{ +public: + // ISlangUnknown + SLANG_REF_OBJECT_IUNKNOWN_QUERY_INTERFACE + SLANG_REF_OBJECT_IUNKNOWN_ADD_REF + SLANG_NO_THROW uint32_t SLANG_MCALL release() { return (m_flags & WriterFlag::IsStatic) ? 1 : (uint32_t)releaseReference(); } + + // ISlangWriter - default impl + SLANG_NO_THROW virtual SlangResult SLANG_MCALL writeVaList(const char* format, va_list args) { SLANG_UNUSED(args); SLANG_UNUSED(format); return SLANG_E_NOT_IMPLEMENTED; } + SLANG_NO_THROW virtual void SLANG_MCALL flush() SLANG_OVERRIDE {} + SLANG_NO_THROW virtual bool SLANG_MCALL isConsole() SLANG_OVERRIDE { return (m_flags & WriterFlag::IsConsole) != 0; } + SLANG_NO_THROW virtual SlangResult SLANG_MCALL setMode(SlangWriterMode mode) SLANG_OVERRIDE { SLANG_UNUSED(mode); return SLANG_FAIL; } + + BaseWriter(WriterFlags flags) : + m_flags(flags) + { + } + +protected: + ISlangUnknown * getInterface(const Guid& guid); + WriterFlags m_flags; +}; + +class CallbackWriter : public BaseWriter +{ +public: + typedef BaseWriter Parent; + // ISlangWriter + SLANG_NO_THROW virtual SlangResult SLANG_MCALL write(const char* chars, size_t numChars) SLANG_OVERRIDE; + + CallbackWriter(SlangDiagnosticCallback callback, const void* data, WriterFlags flags) : + Parent(flags), + m_callback(callback), + m_data(data) + {} + +protected: + + SlangDiagnosticCallback m_callback; + const void* m_data; +}; + +class FileWriter : public BaseWriter +{ +public: + typedef BaseWriter Parent; + // ISlangWriter + SLANG_NO_THROW virtual SlangResult SLANG_MCALL writeVaList(const char* format, va_list args) SLANG_OVERRIDE; + SLANG_NO_THROW virtual SlangResult SLANG_MCALL write(const char* chars, size_t numChars) SLANG_OVERRIDE; + SLANG_NO_THROW virtual void SLANG_MCALL flush() SLANG_OVERRIDE; + SLANG_NO_THROW virtual SlangResult SLANG_MCALL setMode(SlangWriterMode mode) SLANG_OVERRIDE; + + static bool isConsole(FILE* file); + static WriterFlags getDefaultFlags(FILE* file) { return isConsole(file) ? WriterFlags(WriterFlag::IsConsole) : 0; } + + /// Ctor + FileWriter(FILE* file, WriterFlags flags) : + Parent(flags | getDefaultFlags(file)), + m_file(file) + {} + + /// Dtor + ~FileWriter(); + +protected: + FILE* m_file; +}; + +class StringWriter : public BaseWriter +{ +public: + typedef BaseWriter Parent; + // ISlangWriter + SLANG_NO_THROW virtual SlangResult SLANG_MCALL writeVaList(const char* format, va_list args) SLANG_OVERRIDE; + SLANG_NO_THROW virtual SlangResult SLANG_MCALL write(const char* chars, size_t numChars) SLANG_OVERRIDE; + + /// Ctor + StringWriter(StringBuilder* builder, WriterFlags flags) : + Parent(flags), + m_builder(builder) + {} + +protected: + StringBuilder* m_builder; +}; + +class NullWriter : public BaseWriter +{ +public: + typedef BaseWriter Parent; + // ISlangWriter + SLANG_NO_THROW virtual SlangResult SLANG_MCALL writeVaList(const char* format, va_list args) SLANG_OVERRIDE { SLANG_UNUSED(format); SLANG_UNUSED(args); return SLANG_OK; } + SLANG_NO_THROW virtual SlangResult SLANG_MCALL write(const char* chars, size_t numChars) SLANG_OVERRIDE { SLANG_UNUSED(chars); SLANG_UNUSED(numChars); return SLANG_OK; } + + /// Ctor + NullWriter(WriterFlags flags) : + Parent(flags) + {} +}; + +} + +#endif // SLANG_TEXT_WRITER_H diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 21e3b894b..4f914ee1f 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1006,6 +1006,15 @@ namespace Slang } } + if (!type) + { + if (outProperType) + { + *outProperType = nullptr; + } + return false; + } + if (auto genericDeclRefType = type->As()) { // We are using a reference to a generic declaration as a concrete @@ -1034,7 +1043,7 @@ namespace Slang } // TODO: this is one place where syntax should get cloned! - if(outProperType) + if (outProperType) args.Add(typeParam->initType.exp); } else if (auto valParam = member.As()) @@ -1050,7 +1059,7 @@ namespace Slang } // TODO: this is one place where syntax should get cloned! - if(outProperType) + if (outProperType) args.Add(valParam->initExpr); } else @@ -1065,15 +1074,13 @@ namespace Slang } return true; } - else + + // default case: we expect this to already be a proper type + if (outProperType) { - // default case: we expect this to already be a proper type - if (outProperType) - { - *outProperType = type; - } - return true; + *outProperType = type; } + return true; } @@ -1147,7 +1154,7 @@ namespace Slang { // TODO: we may want other cases here... - if (auto errorType = expr->type->As()) + if (auto errorType = expr->type.As()) return true; return false; @@ -7229,7 +7236,7 @@ namespace Slang // for anything applicable. AddDeclRefOverloadCandidates(LookupResultItem(declRefExpr->declRef), context); } - else if (auto funcType = funcExprType->As()) + else if (auto funcType = funcExprType.As()) { // TODO(tfoley): deprecate this path... AddFuncOverloadCandidate(funcType, context); @@ -7250,7 +7257,7 @@ namespace Slang AddOverloadCandidates(item, context); } } - else if (auto typeType = funcExprType->As()) + else if (auto typeType = funcExprType.As()) { // If none of the above cases matched, but we are // looking at a type, then I suppose we have diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp index 0f333aa3d..172bc33b9 100644 --- a/source/slang/compiler.cpp +++ b/source/slang/compiler.cpp @@ -862,6 +862,23 @@ SlangResult dissassembleDXILUsingDXC( } } + static void writeOutputFile( + CompileRequest* compileRequest, + ISlangWriter* writer, + String const& path, + void const* data, + size_t size) + { + + if (SLANG_FAILED(writer->write((const char*)data, size))) + { + compileRequest->mSink.diagnose( + SourceLoc(), + Diagnostics::cannotWriteOutputFile, + path); + } + } + static void writeOutputFile( CompileRequest* compileRequest, String const& path, @@ -924,14 +941,10 @@ SlangResult dissassembleDXILUsingDXC( } static void writeOutputToConsole( - CompileRequest*, + ISlangWriter* writer, String const& text) { - fwrite( - text.begin(), - text.end() - text.begin(), - 1, - stdout); + writer->write(text.Buffer(), text.Length()); } static void writeEntryPointResultToStandardOutput( @@ -941,17 +954,19 @@ SlangResult dissassembleDXILUsingDXC( { auto compileRequest = entryPoint->compileRequest; + ISlangWriter* writer = compileRequest->getWriter(WriterChannel::StdOutput); + switch (result.format) { case ResultFormat::Text: - writeOutputToConsole(compileRequest, result.outputString); + writeOutputToConsole(writer, result.outputString); break; case ResultFormat::Binary: { auto& data = result.outputBinary; - int stdoutFileDesc = _fileno(stdout); - if (_isatty(stdoutFileDesc)) + + if (writer->isConsole()) { // Writing to console, so we need to generate text output. @@ -964,7 +979,7 @@ SlangResult dissassembleDXILUsingDXC( dissassembleDXBC(compileRequest, data.begin(), data.end() - data.begin(), assembly); - writeOutputToConsole(compileRequest, assembly); + writeOutputToConsole(writer, assembly); } break; #endif @@ -977,7 +992,7 @@ SlangResult dissassembleDXILUsingDXC( data.begin(), data.end() - data.begin(), assembly); - writeOutputToConsole(compileRequest, assembly); + writeOutputToConsole(writer, assembly); } break; #endif @@ -988,7 +1003,7 @@ SlangResult dissassembleDXILUsingDXC( dissassembleSPIRV(compileRequest, data.begin(), data.end() - data.begin(), assembly); - writeOutputToConsole(compileRequest, assembly); + writeOutputToConsole(writer, assembly); } break; @@ -1000,12 +1015,11 @@ SlangResult dissassembleDXILUsingDXC( else { // Redirecting stdout to a file, so do the usual thing - #ifdef _WIN32 - _setmode(stdoutFileDesc, _O_BINARY); - #endif + writer->setMode(SLANG_WRITER_MODE_BINARY); + writeOutputFile( compileRequest, - stdout, + writer, "stdout", data.begin(), data.end() - data.begin()); diff --git a/source/slang/compiler.h b/source/slang/compiler.h index fbd6b3f15..2a9b60d9b 100644 --- a/source/slang/compiler.h +++ b/source/slang/compiler.h @@ -212,6 +212,20 @@ namespace Slang Precise = SLANG_FLOATING_POINT_MODE_PRECISE, }; + enum class WriterChannel : SlangWriterChannel + { + Diagnostic = SLANG_WRITER_CHANNEL_DIAGNOSTIC, + StdOutput = SLANG_WRITER_CHANNEL_STD_OUTPUT, + StdError = SLANG_WRITER_CHANNEL_STD_ERROR, + CountOf = SLANG_WRITER_CHANNEL_COUNT_OF, + }; + + enum class WriterMode : SlangWriterMode + { + Text = SLANG_WRITER_MODE_TEXT, + Binary = SLANG_WRITER_MODE_BINARY, + }; + // A request to generate output in some target format class TargetRequest : public RefObject { @@ -401,6 +415,12 @@ namespace Slang /// or a wrapped impl that makes fileSystem operate as fileSystemExt ComPtr fileSystemExt; + // For output + ComPtr m_writers[SLANG_WRITER_CHANNEL_COUNT_OF]; + + void setWriter(WriterChannel chan, ISlangWriter* writer); + ISlangWriter* getWriter(WriterChannel chan) const { return m_writers[int(chan)]; } + /// Load a file into memory using the configured file system. /// /// @param path The path to attempt to load from diff --git a/source/slang/diagnostics.cpp b/source/slang/diagnostics.cpp index f1d0b63f9..4000fd967 100644 --- a/source/slang/diagnostics.cpp +++ b/source/slang/diagnostics.cpp @@ -237,13 +237,13 @@ void DiagnosticSink::diagnoseImpl(SourceLoc const& pos, DiagnosticInfo const& in } // Did the client supply a callback for us to use? - if( callback ) + if( writer ) { // If so, pass the error string along to them StringBuilder messageBuilder; formatDiagnostic(this, messageBuilder, diagnostic); - callback(messageBuilder.ProduceString().begin(), callbackUserData); + writer->write(messageBuilder.Buffer(), messageBuilder.Length()); } else { @@ -269,10 +269,10 @@ void DiagnosticSink::diagnoseRaw( } // Did the client supply a callback for us to use? - if( callback ) + if(writer) { // If so, pass the error string along to them - callback(message, callbackUserData); + writer->write(message, ::strlen(message)); } else { diff --git a/source/slang/diagnostics.h b/source/slang/diagnostics.h index 945dc6c73..420b936a3 100644 --- a/source/slang/diagnostics.h +++ b/source/slang/diagnostics.h @@ -143,8 +143,7 @@ namespace Slang // List diagnostics; int errorCount = 0; - SlangDiagnosticCallback callback = nullptr; - void* callbackUserData = nullptr; + ISlangWriter* writer = nullptr; /* void Error(int id, const String & msg, const SourceLoc & pos) diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index d7abbff24..f83ea894a 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -6434,7 +6434,9 @@ String emitEntryPoint( // un-specialized IR. if (translationUnit->compileRequest->shouldDumpIR) { - dumpIR(irModule); + ISlangWriter* writer = translationUnit->compileRequest->getWriter(WriterChannel::StdError); + + dumpIR(irModule, writer); } // Next, we need to ensure that the code we emit for diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index bc33527cf..784c5034d 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -3239,7 +3239,7 @@ namespace Slang dumpIRModule(&context, module); } - void dumpIR(IRGlobalValue* globalVal) + void dumpIR(IRGlobalValue* globalVal, ISlangWriter* writer) { StringBuilder sb; @@ -3249,8 +3249,11 @@ namespace Slang dumpInst(&context, globalVal); - fprintf(stderr, "%s\n", sb.Buffer()); - fflush(stderr); + writer->write(sb.Buffer(), sb.Length()); + char cr[] = "\n"; + writer->write(cr, 1); + + writer->flush(); } String getSlangIRAssembly(IRModule* module) @@ -3260,13 +3263,16 @@ namespace Slang return sb; } - void dumpIR(IRModule* module) + void dumpIR(IRModule* module, ISlangWriter* writer) { String ir = getSlangIRAssembly(module); - fprintf(stderr, "%s\n", ir.Buffer()); - fflush(stderr); - } + writer->write(ir.Buffer(), ir.Length()); + char cr[] = "\n"; + writer->write(cr, 1); + + writer->flush(); + } // // diff --git a/source/slang/ir.h b/source/slang/ir.h index 8183e038a..6fb0ff728 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -1105,8 +1105,8 @@ struct IRModule : RefObject void printSlangIRAssembly(StringBuilder& builder, IRModule* module); String getSlangIRAssembly(IRModule* module); -void dumpIR(IRModule* module); -void dumpIR(IRGlobalValue* globalVal); +void dumpIR(IRModule* module, ISlangWriter* writer); +void dumpIR(IRGlobalValue* globalVal, ISlangWriter* writer); String dumpIRFunc(IRFunc* func); diff --git a/source/slang/lookup.cpp b/source/slang/lookup.cpp index e4ae3c8bb..f74e11016 100644 --- a/source/slang/lookup.cpp +++ b/source/slang/lookup.cpp @@ -177,6 +177,11 @@ void DoMemberLookupImpl( LookupResult& ioResult, BreadcrumbInfo* breadcrumbs) { + if (!baseType) + { + return; + } + // If the type was pointer-like, then dereference it // automatically here. if (auto pointerLikeType = baseType->As()) @@ -482,11 +487,14 @@ void DoLookupImpl( // in the target decl we are extending if (auto extDeclRef = containerDeclRef.As()) { - if (auto targetDeclRef = extDeclRef.getDecl()->targetType->AsDeclRefType()) + if (extDeclRef.getDecl()->targetType) { - if (auto aggDeclRef = targetDeclRef->declRef.As()) + if (auto targetDeclRef = extDeclRef.getDecl()->targetType->AsDeclRefType()) { - containerDeclRef = extDeclRef.Substitute(aggDeclRef); + if (auto aggDeclRef = targetDeclRef->declRef.As()) + { + containerDeclRef = extDeclRef.Substitute(aggDeclRef); + } } } } @@ -703,4 +711,4 @@ LookupResult lookUpMember( return result; } -} \ No newline at end of file +} diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 8af6292e6..2804854b7 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -5608,7 +5608,9 @@ IRModule* generateIRForTranslationUnit( // then we can dump the initial IR for the module here. if(compileRequest->shouldDumpIR) { - dumpIR(module); + ISlangWriter* writer = translationUnit->compileRequest->getWriter(WriterChannel::StdError); + + dumpIR(module, writer); } return module; diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 8fe08884e..b0adb2025 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -13,6 +13,7 @@ #include "../slang/type-layout.h" #include "slang-file-system.h" +#include "../core/slang-writer.h" #include "ir-serialize.h" @@ -305,6 +306,12 @@ CompileRequest::CompileRequest(Session* session) sourceManager->initialize(session->getBuiltinSourceManager()); + // Set all the default writers + for (int i = 0; i < int(WriterChannel::CountOf); ++i) + { + setWriter(WriterChannel(i), nullptr); + } + // Set up the default file system SLANG_ASSERT(fileSystem == nullptr); fileSystemExt = new CacheFileSystem(DefaultFileSystem::getSingleton()); @@ -368,9 +375,38 @@ MatrixLayoutMode TargetRequest::getDefaultMatrixLayoutMode() } - // +static ISlangWriter* _getDefaultWriter(WriterChannel chan) +{ + static FileWriter stdOut(stdout, WriterFlag::IsStatic | WriterFlag::IsUnowned); + static FileWriter stdError(stderr, WriterFlag::IsStatic | WriterFlag::IsUnowned); + static NullWriter nullWriter(WriterFlag::IsStatic | WriterFlag::IsConsole); + + switch (chan) + { + case WriterChannel::StdError: return &stdError; + case WriterChannel::StdOutput: return &stdOut; + case WriterChannel::Diagnostic: return &nullWriter; + default: + { + SLANG_ASSERT(!"Unknown type"); + return &stdError; + } + } +} + +void CompileRequest::setWriter(WriterChannel chan, ISlangWriter* writer) +{ + writer = writer ? writer : _getDefaultWriter(chan); + m_writers[int(chan)] = writer; + + if (chan == WriterChannel::Diagnostic) + { + mSink.writer = writer; + } +} + SlangResult CompileRequest::loadFile(String const& path, ISlangBlob** outBlob) { return fileSystemExt->loadFile(path.Buffer(), outBlob); @@ -1205,7 +1241,6 @@ SLANG_API void spSetFileSystem( } } - SLANG_API void spSetCompileFlags( SlangCompileRequest* request, SlangCompileFlags flags) @@ -1319,11 +1354,33 @@ SLANG_API void spSetDiagnosticCallback( SlangDiagnosticCallback callback, void const* userData) { + using namespace Slang; + if(!request) return; auto req = REQ(request); - req->mSink.callback = callback; - req->mSink.callbackUserData = (void*) userData; + ComPtr writer(new CallbackWriter(callback, userData, WriterFlag::IsConsole)); + req->setWriter(WriterChannel::Diagnostic, writer); +} + +SLANG_API void spSetWriter( + SlangCompileRequest* request, + SlangWriterChannel chan, + ISlangWriter* writer) +{ + if (!request) return; + auto req = REQ(request); + + req->setWriter(Slang::WriterChannel(chan), writer); +} + +SLANG_API ISlangWriter* spGetWriter( + SlangCompileRequest* request, + SlangWriterChannel chan) +{ + if (!request) return nullptr; + auto req = REQ(request); + return req->getWriter(Slang::WriterChannel(chan)); } SLANG_API void spAddSearchPath( diff --git a/source/slang/syntax-base-defs.h b/source/slang/syntax-base-defs.h index acc795d8b..81f03d43f 100644 --- a/source/slang/syntax-base-defs.h +++ b/source/slang/syntax-base-defs.h @@ -22,6 +22,7 @@ ABSTRACT_SYNTAX_CLASS(SyntaxNodeBase, NodeBase) template T* As() { + SLANG_ASSERT(this); return dynamic_cast(this); } ) diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp index c1585a51a..d354057b2 100644 --- a/source/slang/syntax.cpp +++ b/source/slang/syntax.cpp @@ -153,7 +153,8 @@ void Type::accept(IValVisitor* visitor, void* extra) Type* Type::GetCanonicalType() { - if (!this) return nullptr; + SLANG_ASSERT(this); + Type* et = const_cast(this); if (!et->canonicalType) { @@ -1314,7 +1315,7 @@ void Type::accept(IValVisitor* visitor, void* extra) RefPtr GenericSubstitution::applySubstitutionsShallow(SubstitutionSet substSet, RefPtr substOuter, int* ioDiff) { - if (!this) return nullptr; + SLANG_ASSERT(this); int diff = 0; @@ -1366,7 +1367,7 @@ void Type::accept(IValVisitor* visitor, void* extra) RefPtr ThisTypeSubstitution::applySubstitutionsShallow(SubstitutionSet substSet, RefPtr substOuter, int* ioDiff) { - if (!this) return nullptr; + SLANG_ASSERT(this); int diff = 0; @@ -1385,8 +1386,10 @@ void Type::accept(IValVisitor* visitor, void* extra) bool ThisTypeSubstitution::Equals(Substitutions* subst) { + SLANG_ASSERT(this); if (!subst) - return this == nullptr; + return false; + if (auto thisTypeSubst = dynamic_cast(subst)) { return witness->EqualsVal(thisTypeSubst->witness); @@ -1914,7 +1917,7 @@ void Type::accept(IValVisitor* visitor, void* extra) RefPtr Val::Substitute(SubstitutionSet subst) { - if (!this) return nullptr; + SLANG_ASSERT(this); if (!subst) return this; int diff = 0; return SubstituteImpl(subst, &diff); diff --git a/source/slang/syntax.h b/source/slang/syntax.h index 7bd2afc12..5eb40fefb 100644 --- a/source/slang/syntax.h +++ b/source/slang/syntax.h @@ -293,6 +293,9 @@ namespace Slang RefPtr type; bool IsLeftValue; + template + T* As(); + QualType() : IsLeftValue(false) {} @@ -1108,6 +1111,13 @@ namespace Slang #include "object-meta-end.h" + + template + SLANG_FORCE_INLINE T* QualType::As() + { + return type ? type->As() : nullptr; + } + inline RefPtr GetSub(DeclRef const& declRef) { return declRef.Substitute(declRef.getDecl()->sub.Ptr()); @@ -1314,4 +1324,4 @@ namespace Slang RefPtr findInnerMostGenericSubstitution(Substitutions* subst); } // namespace Slang -#endif \ No newline at end of file +#endif diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp index 2d21d7aef..4281829be 100644 --- a/source/slang/type-layout.cpp +++ b/source/slang/type-layout.cpp @@ -1099,12 +1099,13 @@ createParameterGroupTypeLayout( // There are several different cases that need to be handled here, // depending on whether we have a `ParameterBlock`, a `ConstantBuffer`, // or some other kind of parameter group. Furthermore, in the - // `ParameterBlock` case, we need to deal with differnet layout + // `ParameterBlock` case, we need to deal with different layout // rules depending on whether a block should map to a register `space` // in HLSL or not. // Check if we are working with a parameter block... - auto parameterBlockType = parameterGroupType->As(); + auto parameterBlockType = parameterGroupType ? parameterGroupType->As() : nullptr; + // Check if we have a parameter block *and* it should be // allocated into its own register space(s) diff --git a/source/slangc/main.cpp b/source/slangc/main.cpp index ba83a9bdf..e65ce6aa0 100644 --- a/source/slangc/main.cpp +++ b/source/slangc/main.cpp @@ -1,23 +1,24 @@ -// main.cpp +// main.cpp #include "../../slang.h" SLANG_API void spSetCommandLineCompilerMode(SlangCompileRequest* request); #include "../core/slang-io.h" +#include "../core/slang-app-context.h" +#include "../core/slang-writer.h" using namespace Slang; #include -// Try to read an argument for a command-line option. - static void diagnosticCallback( char const* message, void* /*userData*/) { - fputs(message, stderr); - fflush(stderr); + auto stdError = AppContext::getStdError(); + stdError.put(message); + stdError.flush(); } #ifdef _WIN32 @@ -26,14 +27,10 @@ static void diagnosticCallback( #define MAIN main #endif -// Used to identify that compilation was the failure - with a unique 'internal' code -#define SLANG_E_INTERNAL_COMPILE_FAILED SLANG_MAKE_ERROR(SLANG_FACILITY_INTERNAL, 0x7fab) - -static SlangResult innerMain(int argc, char** argv) +SLANG_SHARED_LIBRARY_TOOL_API SlangResult innerMain(AppContext* appContext, SlangSession* session, int argc, const char*const* argv) { - // Parse any command-line options + AppContext::setSingleton(appContext); - SlangSession* session = spCreateSession(nullptr); SlangCompileRequest* compileRequest = spCreateCompileRequest(session); spSetDiagnosticCallback( @@ -43,6 +40,9 @@ static SlangResult innerMain(int argc, char** argv) spSetCommandLineCompilerMode(compileRequest); + // Do any app specific configuration + appContext->configureRequest(compileRequest); + char const* appName = "slangc"; if (argc > 0) appName = argv[0]; @@ -55,47 +55,40 @@ static SlangResult innerMain(int argc, char** argv) } } + SlangResult res = SLANG_OK; + #ifndef _DEBUG try #endif { - // Run the compiler (this will produce any diagnostics through - // our callback above). - if (SLANG_FAILED(spCompile(compileRequest))) - { - // If the compilation failed, then get out of here... - // Turn into an internal Result -> such that return code can be used to vary result to match previous behavior - return SLANG_E_INTERNAL_COMPILE_FAILED; - } - - // Now that we are done, clean up after ourselves - - spDestroyCompileRequest(compileRequest); - spDestroySession(session); + // Run the compiler (this will produce any diagnostics through SLANG_WRITER_TARGET_TYPE_DIAGNOSTIC). + res = spCompile(compileRequest); + // If the compilation failed, then get out of here... + // Turn into an internal Result -> such that return code can be used to vary result to match previous behavior + res = SLANG_FAILED(res) ? SLANG_E_INTERNAL_FAIL : res; } #ifndef _DEBUG catch (Exception & e) { - printf("internal compiler error: %S\n", e.Message.ToWString().begin()); - return SLANG_FAIL; + AppContext::getStdOut().print("internal compiler error: %S\n", e.Message.ToWString().begin()); + res = SLANG_FAIL; } #endif - return SLANG_OK; + + // Now that we are done, clean up after ourselves + spDestroyCompileRequest(compileRequest); + return res; } int MAIN(int argc, char** argv) { - SlangResult res = innerMain(argc, argv); - - if (SLANG_SUCCEEDED(res)) + SlangResult res; { - return 0; - } - else if (res == SLANG_E_INTERNAL_COMPILE_FAILED) - { - return -1; + SlangSession* session = spCreateSession(nullptr); + res = innerMain(AppContext::initDefault(), session, argc, argv); + spDestroySession(session); } - return 1; + return AppContext::getReturnCode(res); } #ifdef _WIN32 diff --git a/source/slangc/slangc-shared-library.vcxproj b/source/slangc/slangc-shared-library.vcxproj new file mode 100644 index 000000000..1e36c1751 --- /dev/null +++ b/source/slangc/slangc-shared-library.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {644921BF-D228-4EEF-8CDA-11716DB06989} + true + Win32Proj + slangc-shared-library + + + + DynamicLibrary + true + Unicode + v140 + + + DynamicLibrary + true + Unicode + v140 + + + DynamicLibrary + false + Unicode + v140 + + + DynamicLibrary + false + Unicode + v140 + + + + + + + + + + + + + + + + + + + true + ..\..\bin\windows-x86\debug\ + ..\..\intermediate\windows-x86\debug\slangc-shared-library\ + slangc-shared-library + .dll + + + true + ..\..\bin\windows-x64\debug\ + ..\..\intermediate\windows-x64\debug\slangc-shared-library\ + slangc-shared-library + .dll + + + false + ..\..\bin\windows-x86\release\ + ..\..\intermediate\windows-x86\release\slangc-shared-library\ + slangc-shared-library + .dll + + + false + ..\..\bin\windows-x64\release\ + ..\..\intermediate\windows-x64\release\slangc-shared-library\ + slangc-shared-library + .dll + + + + NotUsing + Level3 + _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + ..\..\bin\windows-x86\debug\slangc-shared-library.lib + + + + + NotUsing + Level3 + _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + ..\..\bin\windows-x64\debug\slangc-shared-library.lib + + + + + NotUsing + Level3 + NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + ..\..\bin\windows-x86\release\slangc-shared-library.lib + + + + + NotUsing + Level3 + NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + ..\..\bin\windows-x64\release\slangc-shared-library.lib + + + + + + + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} + + + {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} + + + + + + \ No newline at end of file diff --git a/source/slangc/slangc-shared-library.vcxproj.filters b/source/slangc/slangc-shared-library.vcxproj.filters new file mode 100644 index 000000000..e9ae1c092 --- /dev/null +++ b/source/slangc/slangc-shared-library.vcxproj.filters @@ -0,0 +1,13 @@ + + + + + {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} + + + + + Source Files + + + \ No newline at end of file diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp index 93de67907..631085c2b 100644 --- a/tools/render-test/main.cpp +++ b/tools/render-test/main.cpp @@ -17,6 +17,8 @@ #include #include +#include "../../source/core/slang-app-context.h" + #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include @@ -34,6 +36,141 @@ using Slang::Result; int gWindowWidth = 1024; int gWindowHeight = 768; +class Window: public RefObject +{ +public: + SlangResult initialize(int width, int height); + + void show(); + + void* getHandle() const { return m_hwnd; } + + Window() {} + ~Window(); + + static LRESULT CALLBACK windowProc(HWND windowHandle, + UINT message, + WPARAM wParam, + LPARAM lParam); + +protected: + + HINSTANCE m_hinst = nullptr; + HWND m_hwnd = nullptr; +}; + +// +// We use a bare-minimum window procedure to get things up and running. +// + +/* static */LRESULT CALLBACK Window::windowProc( + HWND windowHandle, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + switch (message) + { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + } + + return DefWindowProcW(windowHandle, message, wParam, lParam); +} + +static ATOM _getWindowClassAtom(HINSTANCE hinst) +{ + static ATOM s_windowClassAtom; + + if (s_windowClassAtom) + { + return s_windowClassAtom; + } + WNDCLASSEXW windowClassDesc; + windowClassDesc.cbSize = sizeof(windowClassDesc); + windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + windowClassDesc.lpfnWndProc = &Window::windowProc; + windowClassDesc.cbClsExtra = 0; + windowClassDesc.cbWndExtra = 0; + windowClassDesc.hInstance = hinst; + windowClassDesc.hIcon = 0; + windowClassDesc.hCursor = 0; + windowClassDesc.hbrBackground = 0; + windowClassDesc.lpszMenuName = 0; + windowClassDesc.lpszClassName = L"SlangRenderTest"; + windowClassDesc.hIconSm = 0; + s_windowClassAtom = RegisterClassExW(&windowClassDesc); + + return s_windowClassAtom; +} + +SlangResult Window::initialize(int widthIn, int heightIn) +{ + // Do initial window-creation stuff here, rather than in the renderer-specific files + + m_hinst = GetModuleHandleA(0); + + // First we register a window class. + ATOM windowClassAtom = _getWindowClassAtom(m_hinst); + if (!windowClassAtom) + { + fprintf(stderr, "error: failed to register window class\n"); + return SLANG_FAIL; + } + + // Next, we create a window using that window class. + + // We will create a borderless window since our screen-capture logic in GL + // seems to get thrown off by having to deal with a window frame. + DWORD windowStyle = WS_POPUP; + DWORD windowExtendedStyle = 0; + + RECT windowRect = { 0, 0, widthIn, heightIn }; + AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); + + { + auto width = windowRect.right - windowRect.left; + auto height = windowRect.bottom - windowRect.top; + + LPWSTR windowName = L"Slang Render Test"; + m_hwnd = CreateWindowExW( + windowExtendedStyle, + (LPWSTR)windowClassAtom, + windowName, + windowStyle, + 0, 0, // x, y + width, height, + NULL, // parent + NULL, // menu + m_hinst, + NULL); + } + if (!m_hwnd) + { + fprintf(stderr, "error: failed to create window\n"); + return SLANG_FAIL; + } + + return SLANG_OK; +} + + +void Window::show() +{ + // Once initialization is all complete, we show the window... + int showCommand = SW_SHOW; + ShowWindow(m_hwnd, showCommand); +} + +Window::~Window() +{ + if (m_hwnd) + { + DestroyWindow(m_hwnd); + } +} + // // For the purposes of a small example, we will define the vertex data for a // single triangle directly in the source file. It should be easy to extend @@ -355,88 +492,20 @@ Result RenderTestApp::writeScreen(const char* filename) return PngSerializeUtil::write(filename, surface); } -// -// We use a bare-minimum window procedure to get things up and running. -// +} // namespace renderer_test -static LRESULT CALLBACK windowProc( - HWND windowHandle, - UINT message, - WPARAM wParam, - LPARAM lParam) +SLANG_SHARED_LIBRARY_TOOL_API SlangResult innerMain(Slang::AppContext* appContext, SlangSession* session, int argcIn, const char*const* argvIn) { - switch (message) - { - case WM_CLOSE: - PostQuitMessage(0); - return 0; - } + using namespace renderer_test; + using namespace Slang; - return DefWindowProcW(windowHandle, message, wParam, lParam); -} + AppContext::setSingleton(appContext); -SlangResult innerMain(int argc, char** argv) -{ // Parse command-line options - SLANG_RETURN_ON_FAIL(parseOptions(&argc, argv)); - - // Do initial window-creation stuff here, rather than in the renderer-specific files - - HINSTANCE instance = GetModuleHandleA(0); - int showCommand = SW_SHOW; - - // First we register a window class. - - WNDCLASSEXW windowClassDesc; - windowClassDesc.cbSize = sizeof(windowClassDesc); - windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - windowClassDesc.lpfnWndProc = &windowProc; - windowClassDesc.cbClsExtra = 0; - windowClassDesc.cbWndExtra = 0; - windowClassDesc.hInstance = instance; - windowClassDesc.hIcon = 0; - windowClassDesc.hCursor = 0; - windowClassDesc.hbrBackground = 0; - windowClassDesc.lpszMenuName = 0; - windowClassDesc.lpszClassName = L"HelloWorld"; - windowClassDesc.hIconSm = 0; - ATOM windowClassAtom = RegisterClassExW(&windowClassDesc); - if (!windowClassAtom) - { - fprintf(stderr, "error: failed to register window class\n"); - return SLANG_FAIL; - } + SLANG_RETURN_ON_FAIL(parseOptions(argcIn, argvIn, AppContext::getStdError())); - // Next, we create a window using that window class. - - // We will create a borderless window since our screen-capture logic in GL - // seems to get thrown off by having to deal with a window frame. - DWORD windowStyle = WS_POPUP; - DWORD windowExtendedStyle = 0; - - RECT windowRect = { 0, 0, gWindowWidth, gWindowHeight }; - AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); - - auto width = windowRect.right - windowRect.left; - auto height = windowRect.bottom - windowRect.top; - - LPWSTR windowName = L"Slang Render Test"; - HWND windowHandle = CreateWindowExW( - windowExtendedStyle, - (LPWSTR)windowClassAtom, - windowName, - windowStyle, - 0, 0, // x, y - width, height, - NULL, // parent - NULL, // menu - instance, - NULL); - if (!windowHandle) - { - fprintf(stderr, "error: failed to create window\n"); - return SLANG_FAIL; - } + RefPtr window(new renderer_test::Window); + SLANG_RETURN_ON_FAIL(window->initialize(gWindowWidth, gWindowHeight)); Slang::RefPtr renderer; @@ -500,7 +569,7 @@ SlangResult innerMain(int argc, char** argv) desc.height = gWindowHeight; { - Result res = renderer->initialize(desc, windowHandle); + SlangResult res = renderer->initialize(desc, (HWND)window->getHandle()); if (SLANG_FAILED(res)) { fprintf(stderr, "Unable to initialize renderer\n"); @@ -512,6 +581,8 @@ SlangResult innerMain(int argc, char** argv) shaderCompiler.renderer = renderer; shaderCompiler.target = slangTarget; shaderCompiler.profile = profileName; + shaderCompiler.slangSession = session; + switch (gOptions.inputLanguageID) { case Options::InputLanguageID::Slang: @@ -533,8 +604,7 @@ SlangResult innerMain(int argc, char** argv) SLANG_RETURN_ON_FAIL(app.initialize(renderer, &shaderCompiler)); - // Once initialization is all complete, we show the window... - ShowWindow(windowHandle, showCommand); + window->show(); // ... and enter the event loop: for (;;) @@ -581,7 +651,7 @@ SlangResult innerMain(int argc, char** argv) } else { - Result res = app.writeScreen(gOptions.outputPath); + SlangResult res = app.writeScreen(gOptions.outputPath); if (SLANG_FAILED(res)) { @@ -600,11 +670,12 @@ SlangResult innerMain(int argc, char** argv) return SLANG_OK; } -} // namespace renderer_test int main(int argc, char** argv) { - SlangResult res = renderer_test::innerMain(argc, argv); + SlangSession* session = spCreateSession(nullptr); + SlangResult res = innerMain(Slang::AppContext::initDefault(), session, argc, argv); + spDestroySession(session); return SLANG_FAILED(res) ? 1 : 0; } diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index d99ba355e..bd5640020 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -6,8 +6,12 @@ #include #include +#include "../../source/core/slang-writer.h" + namespace renderer_test { +static const Options gDefaultOptions; + Options gOptions; // Only set it, if the @@ -16,17 +20,20 @@ void setDefaultRendererType(RendererType type) gOptions.rendererType = (gOptions.rendererType == RendererType::Unknown) ? type : gOptions.rendererType; } -SlangResult parseOptions(int* argc, char** argv) +SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper stdError) { + // Reset the options + gOptions = gDefaultOptions; + + List positionalArgs; + typedef Options::ShaderProgramType ShaderProgramType; typedef Options::InputLanguageID InputLanguageID; + //int argCount = argc; - int argCount = *argc; char const* const* argCursor = argv; - char const* const* argEnd = argCursor + argCount; - - char const** writeCursor = (char const**) argv; + char const* const* argEnd = argCursor + argc; // first argument is the application name if( argCursor != argEnd ) @@ -40,7 +47,7 @@ SlangResult parseOptions(int* argc, char** argv) char const* arg = *argCursor++; if( arg[0] != '-' ) { - *writeCursor++ = arg; + positionalArgs.Add(arg); continue; } @@ -48,8 +55,7 @@ SlangResult parseOptions(int* argc, char** argv) { while(argCursor != argEnd) { - char const* arg = *argCursor++; - *writeCursor++ = arg; + positionalArgs.Add(*argCursor++); } break; } @@ -57,7 +63,7 @@ SlangResult parseOptions(int* argc, char** argv) { if( argCursor == argEnd ) { - fprintf(stderr, "expected argument for '%s' option\n", arg); + stdError.print("expected argument for '%s' option\n", arg); return SLANG_FAIL; } gOptions.outputPath = *argCursor++; @@ -98,12 +104,12 @@ SlangResult parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { - fprintf(stderr, "expected argument for '%s' option\n", arg); + stdError.print("expected argument for '%s' option\n", arg); return SLANG_FAIL; } if( gOptions.slangArgCount == Options::kMaxSlangArgs ) { - fprintf(stderr, "maximum number of '%s' options exceeded (%d)\n", arg, Options::kMaxSlangArgs); + stdError.print("maximum number of '%s' options exceeded (%d)\n", arg, Options::kMaxSlangArgs); return SLANG_FAIL; } gOptions.slangArgs[gOptions.slangArgCount++] = *argCursor++; @@ -145,30 +151,26 @@ SlangResult parseOptions(int* argc, char** argv) } else { - fprintf(stderr, "unknown option '%s'\n", arg); + stdError.print("unknown option '%s'\n", arg); return SLANG_FAIL; } } - // any arguments left over were positional arguments - argCount = (int)(writeCursor - (const char**)argv); - argCursor = argv; - argEnd = argCursor + argCount; - + // first positional argument is source shader path - if( argCursor != argEnd ) + if(positionalArgs.Count()) { - gOptions.sourcePath = *argCursor++; + gOptions.sourcePath = positionalArgs[0]; + positionalArgs.RemoveAt(0); } // any remaining arguments represent an error - if(argCursor != argEnd) + if(positionalArgs.Count() != 0) { - fprintf(stderr, "unexpected arguments\n"); + stdError.print("unexpected arguments\n"); return SLANG_FAIL; } - *argc = 0; return SLANG_OK; } diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 76fdf95af..09721def3 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -1,9 +1,10 @@ -// options.h +// options.h #pragma once #include #include "../../slang-com-helper.h" +#include "../../source/core/slang-writer.h" #include "render.h" @@ -52,6 +53,6 @@ struct Options extern Options gOptions; -SlangResult parseOptions(int* argc, char** argv); +SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper stdError); } // renderer_test diff --git a/tools/render-test/render-test-shared-library.vcxproj b/tools/render-test/render-test-shared-library.vcxproj new file mode 100644 index 000000000..359a35b80 --- /dev/null +++ b/tools/render-test/render-test-shared-library.vcxproj @@ -0,0 +1,210 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {61F7EB00-7281-4BF3-9470-7C2EA92620C3} + true + Win32Proj + render-test-shared-library + 10.0.14393.0 + + + + DynamicLibrary + true + Unicode + v140 + + + DynamicLibrary + true + Unicode + v140 + + + DynamicLibrary + false + Unicode + v140 + + + DynamicLibrary + false + Unicode + v140 + + + + + + + + + + + + + + + + + + + true + ..\..\bin\windows-x86\debug\ + ..\..\intermediate\windows-x86\debug\render-test-shared-library\ + render-test-shared-library + .dll + + + true + ..\..\bin\windows-x64\debug\ + ..\..\intermediate\windows-x64\debug\render-test-shared-library\ + render-test-shared-library + .dll + + + false + ..\..\bin\windows-x86\release\ + ..\..\intermediate\windows-x86\release\render-test-shared-library\ + render-test-shared-library + .dll + + + false + ..\..\bin\windows-x64\release\ + ..\..\intermediate\windows-x64\release\render-test-shared-library\ + render-test-shared-library + .dll + + + + NotUsing + Level3 + _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + ..\..\bin\windows-x86\debug\render-test-shared-library.lib + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/debug/" + + + + + NotUsing + Level3 + _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + ..\..\bin\windows-x64\debug\render-test-shared-library.lib + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/debug/" + + + + + NotUsing + Level3 + NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + ..\..\bin\windows-x86\release\render-test-shared-library.lib + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/release/" + + + + + NotUsing + Level3 + NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + ..\..\bin\windows-x64\release\render-test-shared-library.lib + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/release/" + + + + + + + + + + + + + + + + + + + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} + + + {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} + + + {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + + + + + \ No newline at end of file diff --git a/tools/render-test/render-test-shared-library.vcxproj.filters b/tools/render-test/render-test-shared-library.vcxproj.filters new file mode 100644 index 000000000..ff3d52a7e --- /dev/null +++ b/tools/render-test/render-test-shared-library.vcxproj.filters @@ -0,0 +1,48 @@ + + + + + {21EB8090-0D4E-1035-B6D3-48EBA215DCB7} + + + {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/tools/render-test/shader-renderer-util.cpp b/tools/render-test/shader-renderer-util.cpp index f6c0366bb..0d0f8a3a5 100644 --- a/tools/render-test/shader-renderer-util.cpp +++ b/tools/render-test/shader-renderer-util.cpp @@ -185,7 +185,7 @@ static RefPtr _createSamplerState( { if (baseIndex + i != entry.glslBinding[i]) { - assert("Bindings must be contiguous"); + assert(!"Bindings must be contiguous"); break; } } diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index 26e856295..1dc7323a5 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -14,7 +14,6 @@ namespace renderer_test { RefPtr ShaderCompiler::compileProgram( ShaderCompileRequest const& request) { - SlangSession* slangSession = spCreateSession(NULL); SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); spSetCodeGenTarget(slangRequest, target); @@ -173,8 +172,7 @@ RefPtr ShaderCompiler::compileProgram( // owns the memory allocation for the generated text, and will // free it when we destroy the compilation result. spDestroyCompileRequest(slangRequest); - spDestroySession(slangSession); - + return shaderProgram; } diff --git a/tools/render-test/slang-support.h b/tools/render-test/slang-support.h index 03de062d1..a9b8c8871 100644 --- a/tools/render-test/slang-support.h +++ b/tools/render-test/slang-support.h @@ -16,8 +16,9 @@ struct ShaderCompiler SlangSourceLanguage sourceLanguage; SlangPassThrough passThrough; char const* profile; - - RefPtr compileProgram( + SlangSession* slangSession; + + RefPtr compileProgram( ShaderCompileRequest const& request); }; diff --git a/tools/slang-reflection-test/main.cpp b/tools/slang-reflection-test/main.cpp index 872d2ff3a..41a21eee8 100644 --- a/tools/slang-reflection-test/main.cpp +++ b/tools/slang-reflection-test/main.cpp @@ -8,6 +8,8 @@ #include #include +#include "../../source/core/slang-app-context.h" + struct PrettyWriter { bool startOfLine = true; @@ -16,7 +18,7 @@ struct PrettyWriter static void writeRaw(PrettyWriter& writer, char const* begin, char const* end) { - fprintf(stdout, "%.*s", int(end - begin), begin); + Slang::AppContext::getStdOut().print("%.*s", int(end - begin), begin); } static void writeRaw(PrettyWriter& writer, char const* begin) @@ -27,7 +29,7 @@ static void writeRaw(PrettyWriter& writer, char const* begin) static void writeRawChar(PrettyWriter& writer, int c) { char buffer[] = { (char) c, 0 }; - writeRaw(writer, buffer); + writeRaw(writer, buffer, buffer + 1); } static void adjust(PrettyWriter& writer) @@ -77,7 +79,7 @@ static void write(PrettyWriter& writer, char const* text) static void write(PrettyWriter& writer, SlangUInt val) { adjust(writer); - fprintf(stdout, "%llu", (unsigned long long)val); + Slang::AppContext::getStdOut().print("%llu", (unsigned long long)val); } static void emitReflectionVarInfoJSON(PrettyWriter& writer, slang::VariableReflection* var); @@ -881,6 +883,7 @@ void emitReflectionJSON( auto programReflection = (slang::ShaderReflection*) reflection; PrettyWriter writer; + emitReflectionJSON(writer, programReflection); } @@ -889,18 +892,19 @@ static SlangResult maybeDumpDiagnostic(SlangResult res, SlangCompileRequest* req const char* diagnostic; if (SLANG_FAILED(res) && (diagnostic = spGetDiagnosticOutput(request))) { - fputs(diagnostic, stderr); + Slang::AppContext::getStdError().put(diagnostic); } return res; } -static SlangResult innerMain(int argc, char*const*argv) +SLANG_SHARED_LIBRARY_TOOL_API SlangResult innerMain(Slang::AppContext* appContext, SlangSession* session, int argc, const char*const* argv) { - // Parse any command-line options - - SlangSession* session = spCreateSession(nullptr); + Slang::AppContext::setSingleton(appContext); + SlangCompileRequest* request = spCreateCompileRequest(session); + appContext->configureRequest(request); + char const* appName = "slang-reflection-test"; if (argc > 0) appName = argv[0]; @@ -914,8 +918,7 @@ static SlangResult innerMain(int argc, char*const*argv) emitReflectionJSON(reflection); spDestroyCompileRequest(request); - spDestroySession(session); - + return SLANG_OK; } @@ -923,6 +926,9 @@ int main( int argc, char** argv) { - SlangResult res = innerMain(argc, argv); + SlangSession* session = spCreateSession(nullptr); + SlangResult res = innerMain(Slang::AppContext::initDefault(), session, argc, argv); + spDestroySession(session); + return SLANG_FAILED(res) ? 1 : 0; } diff --git a/tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj b/tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj new file mode 100644 index 000000000..48d08b94e --- /dev/null +++ b/tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F} + true + Win32Proj + slang-reflection-test-shared-library + + + + DynamicLibrary + true + Unicode + v140 + + + DynamicLibrary + true + Unicode + v140 + + + DynamicLibrary + false + Unicode + v140 + + + DynamicLibrary + false + Unicode + v140 + + + + + + + + + + + + + + + + + + + true + ..\..\bin\windows-x86\debug\ + ..\..\intermediate\windows-x86\debug\slang-reflection-test-shared-library\ + slang-reflection-test-shared-library + .dll + + + true + ..\..\bin\windows-x64\debug\ + ..\..\intermediate\windows-x64\debug\slang-reflection-test-shared-library\ + slang-reflection-test-shared-library + .dll + + + false + ..\..\bin\windows-x86\release\ + ..\..\intermediate\windows-x86\release\slang-reflection-test-shared-library\ + slang-reflection-test-shared-library + .dll + + + false + ..\..\bin\windows-x64\release\ + ..\..\intermediate\windows-x64\release\slang-reflection-test-shared-library\ + slang-reflection-test-shared-library + .dll + + + + NotUsing + Level3 + _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + ..\..\bin\windows-x86\debug\slang-reflection-test-shared-library.lib + + + + + NotUsing + Level3 + _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + ..\..\bin\windows-x64\debug\slang-reflection-test-shared-library.lib + + + + + NotUsing + Level3 + NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + ..\..\bin\windows-x86\release\slang-reflection-test-shared-library.lib + + + + + NotUsing + Level3 + NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) + ..\..;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + ..\..\bin\windows-x64\release\slang-reflection-test-shared-library.lib + + + + + + + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} + + + {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} + + + + + + \ No newline at end of file diff --git a/tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj.filters b/tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj.filters new file mode 100644 index 000000000..e9ae1c092 --- /dev/null +++ b/tools/slang-reflection-test/slang-reflection-test-shared-library.vcxproj.filters @@ -0,0 +1,13 @@ + + + + + {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} + + + + + Source Files + + + \ No newline at end of file diff --git a/tools/slang-reflection-test/slang-reflection-test.vcxproj b/tools/slang-reflection-test/slang-reflection-test.vcxproj index 78e562401..8a40290e6 100644 --- a/tools/slang-reflection-test/slang-reflection-test.vcxproj +++ b/tools/slang-reflection-test/slang-reflection-test.vcxproj @@ -168,6 +168,9 @@ {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} + diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp index 943e82cef..77005d03b 100644 --- a/tools/slang-test/main.cpp +++ b/tools/slang-test/main.cpp @@ -2,6 +2,7 @@ #include "../../source/core/slang-io.h" #include "../../source/core/token-reader.h" +#include "../../source/core/slang-app-context.h" #include "../../slang-com-helper.h" @@ -95,6 +96,9 @@ struct Options // integration builds. bool dumpOutputOnFailure = false; + // If set, will force using of executables (not shared library) for tests + bool useExes = false; + // kind of output to generate TestOutputMode outputMode = TestOutputMode::Default; @@ -114,6 +118,8 @@ struct Options // Globals +static const Options g_defaultOptions; + Options g_options; Dictionary g_testCategories; TestCategory* g_defaultTestCategory; @@ -125,14 +131,16 @@ TestCategory* findTestCategory(String const& name); /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! Functions !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ -Result parseOptions(int* argc, char** argv) +Result parseOptions(int argc, char** argv, Slang::WriterHelper stdError) { - int argCount = *argc; + g_options = g_defaultOptions; + + List positionalArgs; + + int argCount = argc; char const* const* argCursor = argv; char const* const* argEnd = argCursor + argCount; - char const** writeCursor = (char const**) argv; - // first argument is the application name if( argCursor != argEnd ) { @@ -145,7 +153,7 @@ Result parseOptions(int* argc, char** argv) char const* arg = *argCursor++; if( arg[0] != '-' ) { - *writeCursor++ = arg; + positionalArgs.Add(arg); continue; } @@ -153,8 +161,7 @@ Result parseOptions(int* argc, char** argv) { while(argCursor != argEnd) { - char const* nxtArg = *argCursor++; - *writeCursor++ = nxtArg; + positionalArgs.Add(*argCursor++); } break; } @@ -163,11 +170,15 @@ Result parseOptions(int* argc, char** argv) { if( argCursor == argEnd ) { - fprintf(stderr, "error: expected operand for '%s'\n", arg); + stdError.print("error: expected operand for '%s'\n", arg); return SLANG_FAIL; } g_options.binDir = *argCursor++; } + else if (strcmp(arg, "-useexes") == 0) + { + g_options.useExes = true; + } else if( strcmp(arg, "-v") == 0 ) { g_options.shouldBeVerbose = true; @@ -188,7 +199,7 @@ Result parseOptions(int* argc, char** argv) { if( argCursor == argEnd ) { - fprintf(stderr, "error: expected operand for '%s'\n", arg); + stdError.print("error: expected operand for '%s'\n", arg); return SLANG_FAIL; } argCursor++; @@ -198,7 +209,7 @@ Result parseOptions(int* argc, char** argv) { if( argCursor == argEnd ) { - fprintf(stderr, "error: expected operand for '%s'\n", arg); + stdError.print("error: expected operand for '%s'\n", arg); return SLANG_FAIL; } argCursor++; @@ -230,7 +241,7 @@ Result parseOptions(int* argc, char** argv) { if( argCursor == argEnd ) { - fprintf(stderr, "error: expected operand for '%s'\n", arg); + stdError.print("error: expected operand for '%s'\n", arg); return SLANG_FAIL; } auto category = findTestCategory(*argCursor++); @@ -243,7 +254,7 @@ Result parseOptions(int* argc, char** argv) { if( argCursor == argEnd ) { - fprintf(stderr, "error: expected operand for '%s'\n", arg); + stdError.print("error: expected operand for '%s'\n", arg); return SLANG_FAIL; } auto category = findTestCategory(*argCursor++); @@ -256,7 +267,7 @@ Result parseOptions(int* argc, char** argv) { if (argCursor == argEnd) { - fprintf(stderr, "error: expecting an api expression (eg 'vk+dx12' or '+dx11') '%s'\n", arg); + stdError.print("error: expecting an api expression (eg 'vk+dx12' or '+dx11') '%s'\n", arg); return SLANG_FAIL; } const char* apiList = *argCursor++; @@ -264,7 +275,7 @@ Result parseOptions(int* argc, char** argv) SlangResult res = RenderApiUtil::parseApiFlags(UnownedStringSlice(apiList), g_options.enabledApis, &g_options.enabledApis); if (SLANG_FAILED(res)) { - fprintf(stderr, "error: unable to parse api expression '%s'\n", apiList); + stdError.print("error: unable to parse api expression '%s'\n", apiList); return res; } } @@ -272,7 +283,7 @@ Result parseOptions(int* argc, char** argv) { if (argCursor == argEnd) { - fprintf(stderr, "error: expected an api expression (eg 'vk+dx12' or '+dx11') '%s'\n", arg); + stdError.print("error: expected an api expression (eg 'vk+dx12' or '+dx11') '%s'\n", arg); return SLANG_FAIL; } const char* apiList = *argCursor++; @@ -280,13 +291,13 @@ Result parseOptions(int* argc, char** argv) SlangResult res = RenderApiUtil::parseApiFlags(UnownedStringSlice(apiList), g_options.synthesizedTestApis, &g_options.synthesizedTestApis); if (SLANG_FAILED(res)) { - fprintf(stderr, "error: unable to parse api expression '%s'\n", apiList); + stdError.print("error: unable to parse api expression '%s'\n", apiList); return res; } } else { - fprintf(stderr, "unknown option '%s'\n", arg); + stdError.print("unknown option '%s'\n", arg); return SLANG_FAIL; } } @@ -301,25 +312,21 @@ Result parseOptions(int* argc, char** argv) g_options.synthesizedTestApis &= g_options.enabledApis; } - // any arguments left over were positional arguments - argCount = (int)((char**)writeCursor - argv); - argCursor = argv; - argEnd = argCursor + argCount; - // first positional argument is a "filter" to apply - if( argCursor != argEnd ) + // first positional argument is source shader path + if (positionalArgs.Count()) { - g_options.testPrefix = *argCursor++; + g_options.testPrefix = positionalArgs[0]; + positionalArgs.RemoveAt(0); } // any remaining arguments represent an error - if(argCursor != argEnd) + if (positionalArgs.Count() != 0) { - fprintf(stderr, "unexpected arguments\n"); + stdError.print("unexpected arguments\n"); return SLANG_FAIL; } - *argc = 0; return SLANG_OK; } @@ -638,7 +645,48 @@ OSError spawnAndWait(TestContext* context, const String& testPath, OSProcessSpaw String commandLine = spawner.getCommandLine(); context->messageFormat(TestMessageType::Info, "%s\n", commandLine.begin()); } - + + if (!context->m_useExes) + { + String exeName = Path::GetFileNameWithoutEXT(spawner.executableName_); + + auto func = context->getInnerMainFunc(String(g_options.binDir), exeName); + if (func) + { + StringBuilder stdErrorString; + StringBuilder stdOutString; + + // Say static so not released + StringWriter stdError(&stdErrorString, WriterFlag::IsConsole | WriterFlag::IsStatic); + StringWriter stdOut(&stdOutString, WriterFlag::IsConsole | WriterFlag::IsStatic); + + AppContext appContext; + appContext.setWriter(SLANG_WRITER_CHANNEL_STD_ERROR, &stdError); + appContext.setWriter(SLANG_WRITER_CHANNEL_STD_OUTPUT, &stdOut); + + if (exeName == "slangc") + { + appContext.setWriter(SLANG_WRITER_CHANNEL_DIAGNOSTIC, &stdError); + } + appContext.setReplaceWriterFlagsAll(); + + List args; + args.Add(exeName.Buffer()); + for (int i = 0; i < int(spawner.argumentList_.Count()); ++i) + { + args.Add(spawner.argumentList_[i].Buffer()); + } + + SlangResult res = func(&appContext, context->getSession(), int(args.Count()), args.begin()); + + spawner.standardError_ = stdErrorString; + spawner.standardOutput_ = stdOutString; + spawner.resultCode_ = AppContext::getReturnCode(res); + + return kOSError_None; + } + } + OSError err = spawner.spawnAndWaitForCompletion(); if (err != kOSError_None) { @@ -1214,7 +1262,7 @@ TestResult runGLSLComparisonTest(TestContext* context, TestInput& input) } -TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, const char * langOption) +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 auto filePath999 = input.filePath; @@ -1236,7 +1284,10 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons spawner.pushArgument(arg); } - spawner.pushArgument(langOption); + for (int i = 0; i < int(numLangOpts); ++i) + { + spawner.pushArgument(langOpts[i]); + } spawner.pushArgument("-o"); auto actualOutputFile = outputStem + ".actual.txt"; spawner.pushArgument(actualOutputFile); @@ -1309,22 +1360,25 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons TestResult runSlangComputeComparisonTest(TestContext* context, TestInput& input) { - return runComputeComparisonImpl(context, input, "-slang -compute"); + const char* langOpts[] = { "-slang", "-compute" }; + return runComputeComparisonImpl(context, input, langOpts, SLANG_COUNT_OF(langOpts)); } TestResult runSlangComputeComparisonTestEx(TestContext* context, TestInput& input) { - return runComputeComparisonImpl(context, input, ""); + return runComputeComparisonImpl(context, input, nullptr, 0); } TestResult runHLSLComputeTest(TestContext* context, TestInput& input) { - return runComputeComparisonImpl(context, input, "-hlsl-rewrite -compute"); + const char* langOpts[] = { "--hlsl-rewrite", "-compute" }; + return runComputeComparisonImpl(context, input, langOpts, SLANG_COUNT_OF(langOpts)); } TestResult runSlangRenderComputeComparisonTest(TestContext* context, TestInput& input) { - return runComputeComparisonImpl(context, input, "-slang -gcompute"); + const char* langOpts[] = { "-slang", "-gcompute" }; + return runComputeComparisonImpl(context, input, langOpts, SLANG_COUNT_OF(langOpts)); } TestResult doRenderComparisonTestRun(TestContext* context, TestInput& input, char const* langOption, char const* outputKind, String* outOutput) @@ -1913,9 +1967,11 @@ void runTestsInDirectory( // int main( - int argc, - char** argv) + int argcIn, + char** argvIn) { + AppContext::initDefault(); + // Set up our test categories here auto fullTestCategory = addTestCategory("full", nullptr); @@ -1939,7 +1995,7 @@ int main( // - if (SLANG_FAILED(parseOptions(&argc, argv))) + if (SLANG_FAILED(parseOptions(argcIn, argvIn, AppContext::getStdError()))) { // Return exit code with error return 1; @@ -1968,6 +2024,7 @@ int main( } context.m_dumpOutputOnFailure = g_options.dumpOutputOnFailure; + context.m_useExes = g_options.useExes; context.m_isVerbose = g_options.shouldBeVerbose; { diff --git a/tools/slang-test/os.cpp b/tools/slang-test/os.cpp index 8cb49248c..9ecf50557 100644 --- a/tools/slang-test/os.cpp +++ b/tools/slang-test/os.cpp @@ -222,6 +222,8 @@ void OSProcessSpawner::pushArgument( // TODO(tfoley): handle cases where arguments need some escaping commandLine_.Append(" "); commandLine_.Append(argument); + + argumentList_.Add(argument); } Slang::String OSProcessSpawner::getCommandLine() @@ -480,7 +482,7 @@ void OSProcessSpawner::pushExecutableName( Slang::String executableName) { executableName_ = executableName; - pushArgument(executableName); + arguments_.Add(executableName); isExecutablePath_ = false; } @@ -488,7 +490,7 @@ void OSProcessSpawner::pushExecutablePath( Slang::String executablePath) { executableName_ = executablePath; - pushArgument(executablePath); + arguments_.Add(executablePath); isExecutablePath_ = true; } @@ -496,6 +498,7 @@ void OSProcessSpawner::pushArgument( Slang::String argument) { arguments_.Add(argument); + argumentList_.Add(argument); } Slang::String OSProcessSpawner::getCommandLine() @@ -507,7 +510,6 @@ Slang::String OSProcessSpawner::getCommandLine() { if(ii != 0) sb << " "; sb << arguments_[ii]; - } return sb.ProduceString(); } diff --git a/tools/slang-test/os.h b/tools/slang-test/os.h index 3466f818c..3e6d93942 100644 --- a/tools/slang-test/os.h +++ b/tools/slang-test/os.h @@ -178,8 +178,11 @@ struct OSProcessSpawner Slang::StringBuilder commandLine_; #else Slang::List arguments_; - #endif + + // Only holds the argumements in order + Slang::List argumentList_; + // Is the executable specified by path, rather than just by name? bool isExecutablePath_; }; diff --git a/tools/slang-test/test-context.cpp b/tools/slang-test/test-context.cpp index b94435a8e..efbbb4f65 100644 --- a/tools/slang-test/test-context.cpp +++ b/tools/slang-test/test-context.cpp @@ -82,6 +82,7 @@ TestContext::TestContext() : m_inTest = false; m_dumpOutputOnFailure = false; m_isVerbose = false; + m_useExes = false; m_session = nullptr; } @@ -480,6 +481,35 @@ bool TestContext::didAllSucceed() const return m_passedTestCount == (m_totalTestCount - m_ignoredTestCount); } +TestContext::InnerMainFunc TestContext::getInnerMainFunc(const String& dirPath, const String& name) +{ + { + SharedLibraryTool* tool = m_sharedLibTools.TryGetValue(name); + if (tool) + { + return tool->m_func; + } + } + + StringBuilder sharedLibToolBuilder; + sharedLibToolBuilder.append(name); + sharedLibToolBuilder.append("-shared-library"); + + StringBuilder builder; + SharedLibrary::appendPlatformFileName(sharedLibToolBuilder.getUnownedSlice(), builder); + String path = Path::Combine(dirPath, builder); + + SharedLibraryTool tool = {}; + + if (SLANG_SUCCEEDED(SharedLibrary::loadWithPlatformFilename(path.begin(), tool.m_sharedLibrary))) + { + tool.m_func = (InnerMainFunc)SharedLibrary::findFuncByName(tool.m_sharedLibrary, "innerMain"); + } + + m_sharedLibTools.Add(name, tool); + return tool.m_func; +} + void TestContext::outputSummary() { auto passCount = m_passedTestCount; diff --git a/tools/slang-test/test-context.h b/tools/slang-test/test-context.h index a81473efa..cf047cec7 100644 --- a/tools/slang-test/test-context.h +++ b/tools/slang-test/test-context.h @@ -1,6 +1,10 @@ // test-context.h #include "../../source/core/slang-string-util.h" +#include "../../source/core/platform.h" +#include "../../source/core/slang-app-context.h" +#include "../../source/core/dictionary.h" + #define SLANG_CHECK(x) TestContext::get()->addResultWithLocation((x), #x, __FILE__, __LINE__); @@ -54,6 +58,7 @@ enum class TestMessageType class TestContext { public: + typedef SlangResult(*InnerMainFunc)(Slang::AppContext* appContext, SlangSession* session, int argc, const char*const* argv); struct TestInfo { @@ -124,6 +129,9 @@ class TestContext /// Returns true if all run tests succeeded bool didAllSucceed() const; + /// Get the InnerMain function from a shared library tool + InnerMainFunc getInnerMainFunc(const Slang::String& dirPath, const Slang::String& name); + /// Get the slang session SlangSession* getSession() const { return m_session; } @@ -134,7 +142,6 @@ class TestContext /// Dtor ~TestContext(); - static TestResult combine(TestResult a, TestResult b) { return (a > b) ? a : b; } static TestContext* get() { return s_context; } @@ -155,9 +162,17 @@ class TestContext bool m_dumpOutputOnFailure; bool m_isVerbose; + bool m_useExes; + void outputSummary(); protected: + struct SharedLibraryTool + { + Slang::SharedLibrary::Handle m_sharedLibrary; + InnerMainFunc m_func; + }; + void _addResult(const TestInfo& info); Slang::StringBuilder m_currentMessage; @@ -168,7 +183,9 @@ protected: bool m_inTest; SlangSession* m_session; - + + Slang::Dictionary m_sharedLibTools; + static TestContext* s_context; }; diff --git a/travis_build.sh b/travis_build.sh new file mode 100644 index 000000000..d25958906 --- /dev/null +++ b/travis_build.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# Get premake +wget https://github.com/shader-slang/slang-binaries/blob/master/premake/premake-5.0.0-alpha13/bin/linux-64/premake5?raw=true -O premake5 +chmod u+x premake5 + +# Create the makefile +./premake5 gmake --cc=${CC} + +# Build the configuration +make config=${CONFIGURATION}_x64 + + diff --git a/travis_test.sh b/travis_test.sh new file mode 100644 index 000000000..add6a5d24 --- /dev/null +++ b/travis_test.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# CONFIGURATION=release or debug +# SLANG_TEST_CATEGORY= + +PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCHITECTURE=$(uname -p) + +if [ "${ARCHITECTURE}" == "x86_64" ]; then + ARCHITECTURE="x64" +fi + +TARGET=${PLATFORM}-${ARCHITECTURE} + +OUTPUTDIR=bin/${TARGET}/${CONFIGURATION}/ + +SLANG_TEST=${OUTPUTDIR}slang-test + +${SLANG_TEST} -bindir ${OUTPUTDIR} -travis -category ${SLANG_TEST_CATEGORY} ${SLANG_TEST_FLAGS} -- cgit v1.2.3