From 0c15582efcec6c7b163ae3a20c04e1aee958d24a Mon Sep 17 00:00:00 2001 From: Yong He Date: Mon, 12 Feb 2024 13:19:35 -0800 Subject: [PATCH 1/7] Fix lowering of static consts in a generic function. (#3573) * Fix lowering of static consts in a generic function. * Fix. * Fix. * Fix lowering of shading rate builtin. --- source/slang/slang-ir-glsl-legalize.cpp | 9 ++- source/slang/slang-lower-to-ir.cpp | 69 ++++++++++--------- .../generics/generic-static-const.slang | 41 +++++++++++ 3 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 tests/language-feature/generics/generic-static-const.slang diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index dd165769c2..0f9e88d206 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -929,7 +929,14 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo( } else if (semanticName == "sv_shadingrate") { - name = "gl_PrimitiveShadingRateEXT"; + if (kind == LayoutResourceKind::VaryingInput) + { + name = "gl_ShadingRateEXT"; + } + else + { + name = "gl_PrimitiveShadingRateEXT"; + } } if( name ) diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index f5d743bb1c..8fef6f5357 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -724,6 +724,31 @@ LoweredValInfo emitDeclRef( DeclRef declRef, IRType* type); + +bool isFunctionVarDecl(VarDeclBase* decl) +{ + // The immediate parent of a function-scope variable + // declaration will be a `ScopeDecl`. + // + // TODO: right now the parent links for scopes are *not* + // set correctly, so we can't just scan up and look + // for a function in the parent chain... + auto parent = decl->parentDecl; + if (as(parent)) + { + return true; + } + return false; +} + +bool isFunctionStaticVarDecl(VarDeclBase* decl) +{ + // Only a variable marked `static` can be static. + if (!decl->findModifier()) + return false; + return isFunctionVarDecl(decl); +} + IRInst* getSimpleVal(IRGenContext* context, LoweredValInfo lowered); int32_t getIntrinsicOp( @@ -7439,25 +7464,22 @@ struct DeclLoweringVisitor : DeclVisitor NestedContext nested(this); auto subBuilder = nested.getBuilder(); auto subContext = nested.getContext(); - IRGeneric* outerGeneric = emitOuterGenerics(subContext, decl, decl); - // TODO(JS): Is this right? - // - // If we *are* in a generic, then outputting this in the (current) generic scope would be correct. - // If we *aren't* we want to go the level above for insertion - // - // Just inserting into the parent doesn't work with a generic that holds a function that has a static const - // variable. - // + IRGeneric* outerGeneric = nullptr; + + // If we are static, then we need to insert the declaration before the parent. // This tries to match the behavior of previous `lowerFunctionStaticConstVarDecl` functionality - if (!outerGeneric && isFunctionStaticVarDecl(decl)) + if (isFunctionStaticVarDecl(decl)) { // We need to insert the constant at a level above // the function being emitted. This will usually // be the global scope, but it might be an outer // generic if we are lowering a generic function. - - subBuilder->setInsertInto(subBuilder->getFunc()->getParent()); + subBuilder->setInsertBefore(subBuilder->getFunc()); + } + else if (!isFunctionVarDecl(decl)) + { + outerGeneric = emitOuterGenerics(subContext, decl, decl); } auto initExpr = decl->initExpr; @@ -7613,27 +7635,6 @@ struct DeclLoweringVisitor : DeclVisitor return globalVal; } - bool isFunctionStaticVarDecl(VarDeclBase* decl) - { - // Only a variable marked `static` can be static. - if(!decl->findModifier()) - return false; - - // The immediate parent of a function-scope variable - // declaration will be a `ScopeDecl`. - // - // TODO: right now the parent links for scopes are *not* - // set correctly, so we can't just scan up and look - // for a function in the parent chain... - auto parent = decl->parentDecl; - if( as(parent) ) - { - return true; - } - - return false; - } - struct NestedContext { IRGenEnv subEnvStorage; @@ -9782,7 +9783,7 @@ bool canDeclLowerToAGeneric(Decl* decl) { if (varDecl->hasModifier() && varDecl->hasModifier()) { - return true; + return !isFunctionVarDecl(varDecl); } } diff --git a/tests/language-feature/generics/generic-static-const.slang b/tests/language-feature/generics/generic-static-const.slang new file mode 100644 index 0000000000..57c82a6876 --- /dev/null +++ b/tests/language-feature/generics/generic-static-const.slang @@ -0,0 +1,41 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -d3d12 -shaderobj -output-using-type -use-dxil +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -vk -shaderobj -output-using-type + +//TEST_INPUT:set outputBuffer = out ubuffer(data=[0 0 0 0], stride=4) +RWStructuredBuffer outputBuffer; + +interface ITestB +{ + static void eval(uint val[N], out uint res[N]); +}; + +struct TestB : ITestB +{ + static void eval(uint val[N], out uint res[N]) + { + static const uint scale = 3 * N; + for (uint i = 0; i < N; i++) + res[i] = scale * val[i]; + } +}; + +[numthreads(1, 1, 1)] +void computeMain(uint3 threadID: SV_DispatchThreadID) +{ + const uint i = threadID.x; + + uint val[4] = { i, i + 1, i + 2, i + 3 }; + uint res[4]; + + TestB test; + test.eval<4>(val, res); + + // CHECK: 0 + outputBuffer[0] = res[0]; + // CHECK: 12 + outputBuffer[1] = res[1]; + // CHECK: 24 + outputBuffer[2] = res[2]; + // CHECK: 36 + outputBuffer[3] = res[3]; +} \ No newline at end of file From 2e35b08d279b4eb37a359941a06970cdea973502 Mon Sep 17 00:00:00 2001 From: Yong He Date: Mon, 12 Feb 2024 14:23:53 -0800 Subject: [PATCH 2/7] Fix #3566. (#3574) --- source/slang/slang-stdlib-textures.cpp | 30 ++++++++++++++++++++------ source/slang/slang-stdlib-textures.h | 3 +++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/source/slang/slang-stdlib-textures.cpp b/source/slang/slang-stdlib-textures.cpp index e6545f8523..85282da920 100644 --- a/source/slang/slang-stdlib-textures.cpp +++ b/source/slang/slang-stdlib-textures.cpp @@ -67,6 +67,7 @@ void TextureTypeInfo::writeFuncBody( const String& glsl, const String& cuda, const String& spirvDefault, + const String& spirvRWDefault, const String& spirvCombined) { BraceScope funcScope{i, sb}; @@ -92,7 +93,13 @@ void TextureTypeInfo::writeFuncBody( if(spirvDefault.getLength() && spirvCombined.getLength()) { sb << i << "case spirv:\n"; - sb << i << "if (isCombined != 0)\n"; + sb << i << "if (access == " << kStdlibResourceAccessReadWrite << ")\n"; + sb << i << "return spirv_asm\n"; + { + BraceScope spirvRWScope{ i, sb, ";\n" }; + sb << spirvRWDefault << "\n"; + } + sb << i << "else if (isCombined != 0)\n"; sb << i << "{\n"; { sb << i << "return spirv_asm\n"; @@ -117,6 +124,7 @@ void TextureTypeInfo::writeFuncWithSig( const String& sig, const String& glsl, const String& spirvDefault, + const String& spirvRWDefault, const String& spirvCombined, const String& cuda, const ReadNoneMode readNoneMode) @@ -126,7 +134,7 @@ void TextureTypeInfo::writeFuncWithSig( sb << i << "[__readNone]\n"; sb << i << "[ForceInline]\n"; sb << i << sig << "\n"; - writeFuncBody(funcName, glsl, cuda, spirvDefault, spirvCombined); + writeFuncBody(funcName, glsl, cuda, spirvDefault, spirvRWDefault, spirvCombined); sb << "\n"; } @@ -136,6 +144,7 @@ void TextureTypeInfo::writeFunc( const String& params, const String& glsl, const String& spirvDefault, + const String& spirvRWDefault, const String& spirvCombined, const String& cuda, const ReadNoneMode readNoneMode) @@ -145,6 +154,7 @@ void TextureTypeInfo::writeFunc( cat(returnType, " ", funcName, "(", params, ")"), glsl, spirvDefault, + spirvRWDefault, spirvCombined, cuda, readNoneMode @@ -294,12 +304,12 @@ void TextureTypeInfo::writeGetDimensionFunctions() } // SPIRV ASM generation - auto generateSpirvAsm = [&](StringBuilder& spirv, UnownedStringSlice imageVar) + auto generateSpirvAsm = [&](StringBuilder& spirv, bool isRW, UnownedStringSlice imageVar) { spirv << "%vecSize:$$uint"; if (sizeDimCount > 1) spirv << sizeDimCount; spirv << " = "; - if (isMultisample) + if (isMultisample || isRW) spirv << "OpImageQuerySize " << imageVar << ";"; else spirv << "OpImageQuerySizeLod " << imageVar <<" $0;"; @@ -380,14 +390,21 @@ void TextureTypeInfo::writeGetDimensionFunctions() { spirvCombined << "OpCapability ImageQuery; "; spirvCombined << "%image:__imageType(this) = OpImage $this; "; - generateSpirvAsm(spirvCombined, toSlice("%image")); + generateSpirvAsm(spirvCombined, false, toSlice("%image")); } StringBuilder spirvDefault; { spirvDefault << "OpCapability ImageQuery; "; - generateSpirvAsm(spirvDefault, toSlice("$this")); + generateSpirvAsm(spirvDefault, false, toSlice("$this")); } + + StringBuilder spirvRWDefault; + { + spirvRWDefault << "OpCapability ImageQuery; "; + generateSpirvAsm(spirvRWDefault, true, toSlice("$this")); + } + sb << " __glsl_version(450)\n"; sb << " __glsl_extension(GL_EXT_samplerless_texture_functions)\n"; writeFunc( @@ -396,6 +413,7 @@ void TextureTypeInfo::writeGetDimensionFunctions() params, glsl, spirvDefault, + spirvRWDefault, spirvCombined, "", ReadNoneMode::Always); diff --git a/source/slang/slang-stdlib-textures.h b/source/slang/slang-stdlib-textures.h index 38ca418622..6c07b1a465 100644 --- a/source/slang/slang-stdlib-textures.h +++ b/source/slang/slang-stdlib-textures.h @@ -67,6 +67,7 @@ struct TextureTypeInfo const String& glsl, const String& cuda, const String& spirvDefault, + const String& spirvRWDefault, const String& spirvCombined ); void writeFuncWithSig( @@ -74,6 +75,7 @@ struct TextureTypeInfo const String& sig, const String& glsl = String{}, const String& spirvDefault = String{}, + const String& spirvRWDefault = String{}, const String& spirvCombined = String{}, const String& cuda = String{}, const ReadNoneMode readNoneMode = ReadNoneMode::Never @@ -84,6 +86,7 @@ struct TextureTypeInfo const String& params, const String& glsl = String{}, const String& spirvDefault = String{}, + const String& spirvRWDefault = String{}, const String& spirvCombined = String{}, const String& cuda = String{}, const ReadNoneMode readNoneMode = ReadNoneMode::Never From 7414252b091c89381a05aea987750666e86b6b35 Mon Sep 17 00:00:00 2001 From: Lukas Lipp <15105596+fknfilewalker@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:29:55 +0100 Subject: [PATCH 3/7] cmake: add options to disable gfx, test, example, ... targets (#3572) * cmake: add options to disable gfx, test and example targets * cmake: enable gfx, test, example targets by default * cmake: use same naming scheme * cmake: option to disable slangd and slangc targets * cmake: option for enabling slang-rt target * cmake: option to deactivate llvm and glslang * cmake: better option text * cmake: remove duplicate slang-llvm option * doc: update docs/building.md --------- Co-authored-by: Yong He --- CMakeLists.txt | 592 +++++++++++++++++++++------------------- docs/building.md | 7 + external/CMakeLists.txt | 24 +- 3 files changed, 329 insertions(+), 294 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f055b01adf..a295e81a43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,14 @@ option(SLANG_EMBED_STDLIB "Build slang with an embedded version of the stdlib") option(SLANG_ENABLE_FULL_IR_VALIDATION "Enable full IR validation (SLOW!)") option(SLANG_ENABLE_ASAN "Enable ASAN (address sanitizer)") +option(SLANG_ENABLE_GFX "Enable gfx targets" ON) +option(SLANG_ENABLE_SLANGD "Enable language server target" ON) +option(SLANG_ENABLE_SLANGC "Enable standalone compiler target" ON) +option(SLANG_ENABLE_SLANGRT "Enable runtime target" ON) +option(SLANG_ENABLE_SLANG_GLSLANG "Enable glslang dependency and slang-glslang wrapper target" ON) +option(SLANG_ENABLE_TESTS "Enable test targets, requires SLANG_ENABLE_GFX, SLANG_ENABLE_SLANGD and SLANG_ENABLE_SLANGRT" ON) +option(SLANG_ENABLE_EXAMPLES "Enable example targets, requires SLANG_ENABLE_GFX" ON) + set(SLANG_GENERATORS_PATH "" CACHE PATH @@ -186,16 +194,18 @@ slang_add_target( INCLUDE_DIRECTORIES_PUBLIC source ) -slang_add_target( - source/slang-rt - SHARED - # This compiles 'core' again with the SLANG_RT_DYNAMIC_EXPORT macro defined - EXTRA_SOURCE_DIRS source/core - USE_EXTRA_WARNINGS - LINK_WITH_PRIVATE miniz lz4_static Threads::Threads ${CMAKE_DL_LIBS} - EXPORT_MACRO_PREFIX SLANG_RT - INSTALL -) +if(SLANG_ENABLE_SLANGRT) + slang_add_target( + source/slang-rt + SHARED + # This compiles 'core' again with the SLANG_RT_DYNAMIC_EXPORT macro defined + EXTRA_SOURCE_DIRS source/core + USE_EXTRA_WARNINGS + LINK_WITH_PRIVATE miniz lz4_static Threads::Threads ${CMAKE_DL_LIBS} + EXPORT_MACRO_PREFIX SLANG_RT + INSTALL + ) +endif() slang_add_target( source/compiler-core @@ -275,38 +285,45 @@ generator( add_subdirectory(prelude) add_subdirectory(source/slang) -slang_add_target( - tools/slangd - EXECUTABLE - LINK_WITH_PRIVATE - core - slang - slang-reflect-headers - slang-capability-defs - Threads::Threads - INSTALL -) -slang_add_target( - source/slangc - EXECUTABLE - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE core slang Threads::Threads - INSTALL -) +if(SLANG_ENABLE_SLANGD) + slang_add_target( + tools/slangd + EXECUTABLE + LINK_WITH_PRIVATE + core + slang + slang-reflect-headers + slang-capability-defs + Threads::Threads + INSTALL + ) +endif() +if(SLANG_ENABLE_SLANGC) + slang_add_target( + source/slangc + EXECUTABLE + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE core slang Threads::Threads + INSTALL + ) +endif() # # Our wrappers for glslang and llvm # -slang_add_target( - source/slang-glslang - MODULE - USE_EXTRA_WARNINGS - LINK_WITH_PRIVATE glslang SPIRV SPIRV-Tools-opt - INSTALL -) -# Our only interface is through what we define in source/slang-glslang, in the -# interests of hygiene, hide anything else we link in. -add_supported_cxx_linker_flags(slang-glslang PRIVATE "-Wl,--exclude-libs,ALL") +if(SLANG_ENABLE_SLANG_GLSLANG) + slang_add_target( + source/slang-glslang + MODULE + USE_EXTRA_WARNINGS + LINK_WITH_PRIVATE glslang SPIRV SPIRV-Tools-opt + INSTALL + ) + # Our only interface is through what we define in source/slang-glslang, in the + # interests of hygiene, hide anything else we link in. + add_supported_cxx_linker_flags(slang-glslang PRIVATE "-Wl,--exclude-libs,ALL") +endif() + if(SLANG_SLANG_LLVM_FLAVOR STREQUAL "FETCH_BINARY") # @@ -416,85 +433,87 @@ elseif(SLANG_SLANG_LLVM_FLAVOR STREQUAL "USE_SYSTEM_LLVM") # pulling in libLLVM.so) endif() -# -# `platform` contains all the platform abstractions for a GUI application. -# -slang_add_target( - tools/platform - SHARED - EXCLUDE_FROM_ALL - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE - core - imgui - $<$:X11::X11> - $<$:-framework Cocoa> - $<$:-framework QuartzCore> - ${CMAKE_DL_LIBS} - EXTRA_COMPILE_DEFINITIONS_PRIVATE - $<$:SLANG_ENABLE_XLIB> - INCLUDE_FROM_PRIVATE gfx - INCLUDE_DIRECTORIES_PUBLIC tools/platform - EXPORT_MACRO_PREFIX SLANG_PLATFORM -) +if(SLANG_ENABLE_GFX) + # + # `platform` contains all the platform abstractions for a GUI application. + # + slang_add_target( + tools/platform + SHARED + EXCLUDE_FROM_ALL + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE + core + imgui + $<$:X11::X11> + $<$:-framework Cocoa> + $<$:-framework QuartzCore> + ${CMAKE_DL_LIBS} + EXTRA_COMPILE_DEFINITIONS_PRIVATE + $<$:SLANG_ENABLE_XLIB> + INCLUDE_FROM_PRIVATE gfx + INCLUDE_DIRECTORIES_PUBLIC tools/platform + EXPORT_MACRO_PREFIX SLANG_PLATFORM + ) -# -# GFX -# -slang_add_target( - tools/gfx - SHARED - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE - core - slang - Vulkan-Headers - $<$:X11::X11> - $<$:CUDA::cudart;CUDA::cuda_driver> - EXTRA_COMPILE_DEFINITIONS_PRIVATE - $<$:GFX_ENABLE_CUDA> - $<$:GFX_OPTIX> - $<$:GFX_NVAPI> - $<$:SLANG_ENABLE_XLIB> - # This is a shared library, so we need to set a preprocessor macro to mark - # exported symbols - EXPORT_MACRO_PREFIX SLANG_GFX - # slang-gfx is in this directory, anything which depends on gfx should include - # this - INCLUDE_DIRECTORIES_PUBLIC . - REQUIRES copy-gfx-slang-modules - INSTALL - FOLDER gfx -) -set(modules_dest_dir $) -add_custom_target( - copy-gfx-slang-modules - COMMAND ${CMAKE_COMMAND} -E make_directory ${modules_dest_dir} - COMMAND - ${CMAKE_COMMAND} -E copy tools/gfx/gfx.slang - ${modules_dest_dir}/gfx.slang - COMMAND - ${CMAKE_COMMAND} -E copy tools/gfx/slang.slang - ${modules_dest_dir}/slang.slang - WORKING_DIRECTORY ${slang_SOURCE_DIR} - VERBATIM -) -set_target_properties(copy-gfx-slang-modules PROPERTIES FOLDER generators) -install( - FILES ${modules_dest_dir}/gfx.slang ${modules_dest_dir}/slang.slang - DESTINATION ${runtime_subdir} -) + # + # GFX + # + slang_add_target( + tools/gfx + SHARED + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE + core + slang + Vulkan-Headers + $<$:X11::X11> + $<$:CUDA::cudart;CUDA::cuda_driver> + EXTRA_COMPILE_DEFINITIONS_PRIVATE + $<$:GFX_ENABLE_CUDA> + $<$:GFX_OPTIX> + $<$:GFX_NVAPI> + $<$:SLANG_ENABLE_XLIB> + # This is a shared library, so we need to set a preprocessor macro to mark + # exported symbols + EXPORT_MACRO_PREFIX SLANG_GFX + # slang-gfx is in this directory, anything which depends on gfx should include + # this + INCLUDE_DIRECTORIES_PUBLIC . + REQUIRES copy-gfx-slang-modules + INSTALL + FOLDER gfx + ) + set(modules_dest_dir $) + add_custom_target( + copy-gfx-slang-modules + COMMAND ${CMAKE_COMMAND} -E make_directory ${modules_dest_dir} + COMMAND + ${CMAKE_COMMAND} -E copy tools/gfx/gfx.slang + ${modules_dest_dir}/gfx.slang + COMMAND + ${CMAKE_COMMAND} -E copy tools/gfx/slang.slang + ${modules_dest_dir}/slang.slang + WORKING_DIRECTORY ${slang_SOURCE_DIR} + VERBATIM + ) + set_target_properties(copy-gfx-slang-modules PROPERTIES FOLDER generators) + install( + FILES ${modules_dest_dir}/gfx.slang ${modules_dest_dir}/slang.slang + DESTINATION ${runtime_subdir} + ) -slang_add_target( - tools/gfx-util - STATIC - LINK_WITH_PRIVATE core - INCLUDE_FROM_PRIVATE gfx - # The headers are included with 'include "gfx-util/blah.h"' which is found - # in the tools directory - INCLUDE_DIRECTORIES_PUBLIC tools - FOLDER gfx -) + slang_add_target( + tools/gfx-util + STATIC + LINK_WITH_PRIVATE core + INCLUDE_FROM_PRIVATE gfx + # The headers are included with 'include "gfx-util/blah.h"' which is found + # in the tools directory + INCLUDE_DIRECTORIES_PUBLIC tools + FOLDER gfx + ) +endif() # # Installing any documentation @@ -508,196 +527,203 @@ endif() # # The test executables and runtime-loaded modules # +if(SLANG_ENABLE_TESTS AND SLANG_ENABLE_GFX AND SLANG_ENABLE_SLANGD AND SLANG_ENABLE_SLANGRT) + slang_add_target( + tools/test-server + EXECUTABLE + EXCLUDE_FROM_ALL + LINK_WITH_PRIVATE core compiler-core slang + FOLDER test + ) + slang_add_target( + tools/test-process + EXECUTABLE + EXCLUDE_FROM_ALL + LINK_WITH_PRIVATE core compiler-core + FOLDER test + ) -slang_add_target( - tools/test-server - EXECUTABLE - EXCLUDE_FROM_ALL - LINK_WITH_PRIVATE core compiler-core slang - FOLDER test -) -slang_add_target( - tools/test-process - EXECUTABLE - EXCLUDE_FROM_ALL - LINK_WITH_PRIVATE core compiler-core - FOLDER test -) - -slang_add_target( - tools/slang-test - EXECUTABLE - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE core compiler-core slang ${CMAKE_DL_LIBS} Threads::Threads - REQUIRES - # Shared libraries dlopened by slang-test - slang-reflection-test - render-test - slang-unit-test - gfx-unit-test - slang-glslang - # Used by some tests when they run - slangd - slang-rt - gfx - test-server - test-process - FOLDER test - DEBUG_DIR ${slang_SOURCE_DIR} -) -if(TARGET slang-llvm) - # Only add a dependency here if slang-llvm exists - # Pending https://gitlab.kitware.com/cmake/cmake/-/issues/19467 to be able - # to use a generator expression in REQUIRES just above. - add_dependencies(slang-test slang-llvm) -endif() -set_property( - DIRECTORY ${slang_SOURCE_DIR} - PROPERTY VS_STARTUP_PROJECT slang-test -) - -slang_add_target( - tools/unit-test - OBJECT - EXCLUDE_FROM_ALL - INCLUDE_FROM_PRIVATE slang - FOLDER test -) - -# These are libraries loaded at runtime from the test executable: -slang_add_target( - tools/gfx-unit-test - MODULE - EXCLUDE_FROM_ALL - EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE core slang unit-test gfx gfx-util platform - OUTPUT_NAME gfx-unit-test-tool - FOLDER test/tools -) -slang_add_target( - tools/slang-unit-test - MODULE - EXCLUDE_FROM_ALL - EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE core compiler-core unit-test slang Threads::Threads - OUTPUT_NAME slang-unit-test-tool - FOLDER test/tools -) -slang_add_target( - tools/slang-reflection-test - MODULE - EXCLUDE_FROM_ALL - EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE core slang Threads::Threads - OUTPUT_NAME slang-reflection-test-tool - FOLDER test/tools -) -slang_add_target( - tools/render-test - MODULE - EXCLUDE_FROM_ALL - EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL - USE_FEWER_WARNINGS - LINK_WITH_PRIVATE - core - compiler-core - slang - gfx - gfx-util - platform - $<$:CUDA::cudart;CUDA::cuda_driver> - EXTRA_COMPILE_DEFINITIONS_PRIVATE - $<$:RENDER_TEST_CUDA> - $<$:RENDER_TEST_OPTIX> - OUTPUT_NAME render-test-tool - FOLDER test/tools -) - -slang_add_target( - tools/slang-profile - EXECUTABLE - EXCLUDE_FROM_ALL - LINK_WITH_PRIVATE core slang - FOLDER test -) - -# -# Examples -# + slang_add_target( + tools/slang-test + EXECUTABLE + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE core compiler-core slang ${CMAKE_DL_LIBS} Threads::Threads + REQUIRES + # Shared libraries dlopened by slang-test + slang-reflection-test + render-test + slang-unit-test + gfx-unit-test + # Used by some tests when they run + slangd + slang-rt + gfx + test-server + test-process + FOLDER test + DEBUG_DIR ${slang_SOURCE_DIR} + ) + if(TARGET slang-glslang) + # Only add a dependency here if slang-glslang exists + add_dependencies(slang-test slang-glslang) + endif() + if(TARGET slang-llvm) + # Only add a dependency here if slang-llvm exists + # Pending https://gitlab.kitware.com/cmake/cmake/-/issues/19467 to be able + # to use a generator expression in REQUIRES just above. + add_dependencies(slang-test slang-llvm) + endif() + set_property( + DIRECTORY ${slang_SOURCE_DIR} + PROPERTY VS_STARTUP_PROJECT slang-test + ) -slang_add_target( - examples/example-base - STATIC - EXCLUDE_FROM_ALL - LINK_WITH_PRIVATE - core - slang - gfx - platform - $<$:CUDA::cudart;CUDA::cuda_driver> - FOLDER examples -) + slang_add_target( + tools/unit-test + OBJECT + EXCLUDE_FROM_ALL + INCLUDE_FROM_PRIVATE slang + FOLDER test + ) -add_custom_target( - all-examples - COMMENT "meta target which depends on all examples" -) -set_target_properties(all-examples PROPERTIES FOLDER examples) -function(example dir) + # These are libraries loaded at runtime from the test executable: slang_add_target( - ${dir} - EXECUTABLE + tools/gfx-unit-test + MODULE + EXCLUDE_FROM_ALL + EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE core slang unit-test gfx gfx-util platform + OUTPUT_NAME gfx-unit-test-tool + FOLDER test/tools + ) + slang_add_target( + tools/slang-unit-test + MODULE + EXCLUDE_FROM_ALL + EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE core compiler-core unit-test slang Threads::Threads + OUTPUT_NAME slang-unit-test-tool + FOLDER test/tools + ) + slang_add_target( + tools/slang-reflection-test + MODULE EXCLUDE_FROM_ALL + EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE core slang Threads::Threads + OUTPUT_NAME slang-reflection-test-tool + FOLDER test/tools + ) + slang_add_target( + tools/render-test + MODULE + EXCLUDE_FROM_ALL + EXTRA_COMPILE_DEFINITIONS_PRIVATE SLANG_SHARED_LIBRARY_TOOL USE_FEWER_WARNINGS LINK_WITH_PRIVATE core - example-base + compiler-core slang gfx gfx-util platform $<$:CUDA::cudart;CUDA::cuda_driver> EXTRA_COMPILE_DEFINITIONS_PRIVATE - $<$:SLANG_ENABLE_XLIB> - REQUIRED_BY all-examples + $<$:RENDER_TEST_CUDA> + $<$:RENDER_TEST_OPTIX> + OUTPUT_NAME render-test-tool + FOLDER test/tools + ) + + slang_add_target( + tools/slang-profile + EXECUTABLE + EXCLUDE_FROM_ALL + LINK_WITH_PRIVATE core slang + FOLDER test + ) +endif() + +if (SLANG_ENABLE_EXAMPLES AND SLANG_ENABLE_GFX) + # + # Examples + # + slang_add_target( + examples/example-base + STATIC + EXCLUDE_FROM_ALL + LINK_WITH_PRIVATE + core + slang + gfx + platform + $<$:CUDA::cudart;CUDA::cuda_driver> FOLDER examples - DEBUG_DIR ${dir} - ${ARGN} ) -endfunction() -example(examples/autodiff-texture WIN32_EXECUTABLE) -example(examples/cpu-com-example ) -example(examples/cpu-hello-world ) -example(examples/gpu-printing ) -example(examples/hello-world LINK_WITH_PRIVATE Vulkan-Headers) -example(examples/model-viewer WIN32_EXECUTABLE) -example(examples/platform-test WIN32_EXECUTABLE) -example(examples/ray-tracing WIN32_EXECUTABLE) -example(examples/ray-tracing-pipeline WIN32_EXECUTABLE) -example(examples/shader-object ) -example(examples/shader-toy WIN32_EXECUTABLE) -example(examples/triangle WIN32_EXECUTABLE) -if(SLANG_ENABLE_AFTERMATH) - example(examples/nv-aftermath-example WIN32_EXECUTABLE) + add_custom_target( + all-examples + COMMENT "meta target which depends on all examples" + ) + set_target_properties(all-examples PROPERTIES FOLDER examples) + function(example dir) + slang_add_target( + ${dir} + EXECUTABLE + EXCLUDE_FROM_ALL + USE_FEWER_WARNINGS + LINK_WITH_PRIVATE + core + example-base + slang + gfx + gfx-util + platform + $<$:CUDA::cudart;CUDA::cuda_driver> + EXTRA_COMPILE_DEFINITIONS_PRIVATE + $<$:SLANG_ENABLE_XLIB> + REQUIRED_BY all-examples + FOLDER examples + DEBUG_DIR ${dir} + ${ARGN} + ) + endfunction() + + example(examples/autodiff-texture WIN32_EXECUTABLE) + example(examples/cpu-com-example ) + example(examples/cpu-hello-world ) + example(examples/gpu-printing ) + example(examples/hello-world LINK_WITH_PRIVATE Vulkan-Headers) + example(examples/model-viewer WIN32_EXECUTABLE) + example(examples/platform-test WIN32_EXECUTABLE) + example(examples/ray-tracing WIN32_EXECUTABLE) + example(examples/ray-tracing-pipeline WIN32_EXECUTABLE) + example(examples/shader-object ) + example(examples/shader-toy WIN32_EXECUTABLE) + example(examples/triangle WIN32_EXECUTABLE) + if(SLANG_ENABLE_AFTERMATH) + example(examples/nv-aftermath-example WIN32_EXECUTABLE) + endif() endif() # # Testing # -include(CTest) -add_test( - NAME slang-test - COMMAND - slang-test -bindir ${slang_SOURCE_DIR}/build/$/${runtime_subdir} - -expected-failure-list ${slang_SOURCE_DIR}/tests/expected-failure.txt - -expected-failure-list - ${slang_SOURCE_DIR}/tests/expected-failure-github.txt - WORKING_DIRECTORY ${slang_SOURCE_DIR} -) +if(SLANG_ENABLE_TESTS) + include(CTest) + add_test( + NAME slang-test + COMMAND + slang-test -bindir ${slang_SOURCE_DIR}/build/$/${runtime_subdir} + -expected-failure-list ${slang_SOURCE_DIR}/tests/expected-failure.txt + -expected-failure-list + ${slang_SOURCE_DIR}/tests/expected-failure-github.txt + WORKING_DIRECTORY ${slang_SOURCE_DIR} + ) +endif() # # Packaging diff --git a/docs/building.md b/docs/building.md index 5c48d0493d..3cac636a11 100644 --- a/docs/building.md +++ b/docs/building.md @@ -253,6 +253,13 @@ See the [documentation on testing](../tools/slang-test/README.md) for more infor | `SLANG_EMBED_STDLIB_SOURCE` | `TRUE` | Embed stdlib source in the binary | | `SLANG_ENABLE_ASAN` | `FALSE` | Enable ASAN (address sanitizer) | | `SLANG_ENABLE_FULL_IR_VALIDATION` | `FALSE` | Enable full IR validation (SLOW!) | +| `SLANG_ENABLE_GFX` | `TRUE` | Enable gfx targets | +| `SLANG_ENABLE_SLANGD` | `TRUE` | Enable language server target | +| `SLANG_ENABLE_SLANGC` | `TRUE` | Enable standalone compiler target | +| `SLANG_ENABLE_SLANGRT` | `TRUE` | Enable runtime target | +| `SLANG_ENABLE_SLANG_GLSLANG` | `TRUE` | Enable glslang dependency and slang-glslang wrapper target | +| `SLANG_ENABLE_TESTS` | `TRUE` | Enable test targets, requires SLANG_ENABLE_GFX, SLANG_ENABLE_SLANGD and SLANG_ENABLE_SLANGRT | +| `SLANG_ENABLE_EXAMPLES` | `TRUE` | Enable example targets, requires SLANG_ENABLE_GFX | | `SLANG_SLANG_LLVM_FLAVOR` | `FETCH_BINARY` | How to set up llvm support | | `SLANG_SLANG_LLVM_BINARY_URL` | System dependent | URL specifying the location of the slang-llvm prebuilt library | | `SLANG_GENERATORS_PATH` | `` | Path to an installed `all-generators` target for cross compilation | diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index e61db99212..a1bb01e69c 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -34,18 +34,20 @@ add_subdirectory(vulkan EXCLUDE_FROM_ALL) # SPIRV-Headers add_subdirectory(spirv-headers EXCLUDE_FROM_ALL) -# SPIRV-Tools -set(SPIRV_TOOLS_BUILD_STATIC ON) -set(SPIRV_WERROR OFF) -set(SPIRV_HEADER_DIR "${CMAKE_CURRENT_LIST_DIR}/spirv-headers/") -set(SPIRV_SKIP_TESTS ON) -add_subdirectory(spirv-tools EXCLUDE_FROM_ALL) +if(SLANG_ENABLE_SLANG_GLSLANG) + # SPIRV-Tools + set(SPIRV_TOOLS_BUILD_STATIC ON) + set(SPIRV_WERROR OFF) + set(SPIRV_HEADER_DIR "${CMAKE_CURRENT_LIST_DIR}/spirv-headers/") + set(SPIRV_SKIP_TESTS ON) + add_subdirectory(spirv-tools EXCLUDE_FROM_ALL) -# glslang -set(SKIP_GLSLANG_INSTALL ON) -set(ENABLE_OPT ON) -set(ENABLE_PCH OFF) -add_subdirectory(glslang EXCLUDE_FROM_ALL) + # glslang + set(SKIP_GLSLANG_INSTALL ON) + set(ENABLE_OPT ON) + set(ENABLE_PCH OFF) + add_subdirectory(glslang EXCLUDE_FROM_ALL) +endif() # imgui add_library(imgui INTERFACE) From 2ced683f10fb82f63a2e2c3d7b5f099c53bb57b0 Mon Sep 17 00:00:00 2001 From: kaizhangNV <149626564+kaizhangNV@users.noreply.github.com> Date: Thu, 15 Feb 2024 00:19:01 -0600 Subject: [PATCH 4/7] Deploy the falcor-perf-test (#3579) * Deploy the falcor-compiler-perf-test Deploy the falcor-compiler-perf-test to falcor-test.yml file. It will download the release pre-build from: https://github.com/shader-slang/falcor-compile-perf-test and run the test executable with newly built slang libraries. It only works on win64 now. Disable running the tests on macos and linux platform. * make a separate yml file to only run falcor-perf-test * Remove falcor-perf-test from dependency Remove falcor-perf-test from dependency, instead download the binary in github action. --------- Co-authored-by: Yong He --- .../workflows/falcor-compiler-perf-test.yml | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/falcor-compiler-perf-test.yml diff --git a/.github/workflows/falcor-compiler-perf-test.yml b/.github/workflows/falcor-compiler-perf-test.yml new file mode 100644 index 0000000000..cfd1837bd4 --- /dev/null +++ b/.github/workflows/falcor-compiler-perf-test.yml @@ -0,0 +1,77 @@ +# This is a basic workflow to help you get started with Actions + +name: Falcor Compiler Perf-Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + build: + runs-on: [Windows, self-hosted, perf] + timeout-minutes: 100 + continue-on-error: true + strategy: + fail-fast: false + matrix: + configuration: ['Release'] + platform: ['x64'] + include: + - platform: x64 + testPlatform: x64 + - platform: x64 + testCategory: full + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: '0' + - name: setup-msbuild + uses: microsoft/setup-msbuild@v1 + - name: build + run: | + .\premake.bat vs2019 --arch=${{matrix.platform}} --deps=true --no-progress=true --enable-cuda=true + + .\make-slang-tag-version.bat + + MSBuild.exe slang.sln -v:m -m -property:Configuration=${{matrix.configuration}} -property:Platform=${{matrix.platform}} -property:WindowsTargetPlatformVersion=10.0.19041.0 -maxcpucount:12 + + - uses: robinraju/release-downloader@v1.9 + id: download + with: + # The source repository path. + # Expected format {owner}/{repo} + # Default: ${{ github.repository }} + repository: "shader-slang/falcor-compile-perf-test" + + # A flag to set the download target as latest release + # The default value is 'false' + latest: true + + # The name of the file to download. + # Use this field only to specify filenames other than tarball or zipball, if any. + # Supports wildcard pattern (eg: '*', '*.deb', '*.zip' etc..) + fileName: "falcor_perf_test-*-win-64.zip" + + # Download the attached zipball (*.zip) + zipBall: true + + # Relative path under $GITHUB_WORKSPACE to place the downloaded file(s) + # It will create the target directory automatically if not present + # eg: out-file-path: "my-downloads" => It will create directory $GITHUB_WORKSPACE/my-downloads + out-file-path: "./falcor-perf-test" + + # Somehow there is a bug in this flag, the executable extracted is not runnable. We have to + # extract ourselves. + extract: false + + - name: run falcor-compiler-perf-test + run: | + $filename = '${{ fromJson(steps.download.outputs.downloaded_files)[0] }}' + Expand-Archive $filename -DestinationPath .\falcor-perf-test + $env:PATH += ";.\bin\windows-${{matrix.testPlatform}}\${{matrix.configuration}}"; + .\falcor-perf-test\bin\Release\falcor_perftest.exe From 5a623ec227726ad1d988a5d91f55f19b62a98e03 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 15 Feb 2024 00:05:51 -0800 Subject: [PATCH 5/7] Support loading serialized modules. (#3588) * Support loading serialized modules. * Fix. * Fix vs solution files * Fix glsl module loading. * C++ fix. * Fix. * Try fix c++ error. * Try fix. * Fix. * Fix. --- .../gfx-unit-test-tool.vcxproj | 5 + .../gfx-unit-test-tool.vcxproj.filters | 15 ++ slang.h | 27 +++ source/slang/slang-ast-base.h | 2 +- source/slang/slang-ast-decl.h | 5 + source/slang/slang-check-decl.cpp | 13 +- source/slang/slang-check-expr.cpp | 8 +- source/slang/slang-check-impl.h | 7 +- source/slang/slang-check-shader.cpp | 52 +---- source/slang/slang-compiler.cpp | 112 ++++++++++ source/slang/slang-compiler.h | 61 ++++++ source/slang/slang-module-library.cpp | 2 +- source/slang/slang-parser.cpp | 5 +- source/slang/slang-serialize-ast-type-info.h | 4 +- source/slang/slang-serialize-container.cpp | 11 +- source/slang/slang-serialize-container.h | 2 +- source/slang/slang-serialize-factory.cpp | 11 +- source/slang/slang-serialize-factory.h | 1 + source/slang/slang-serialize-types.h | 1 + source/slang/slang-serialize.cpp | 167 ++++++++++++++- source/slang/slang-serialize.h | 23 +- source/slang/slang-syntax.h | 11 +- source/slang/slang.cpp | 202 ++++++++++++++---- tools/gfx-unit-test/gfx-test-util.cpp | 62 +++++- tools/gfx-unit-test/gfx-test-util.h | 10 + tools/gfx-unit-test/precompiled-module-2.cpp | 181 ++++++++++++++++ .../precompiled-module-imported.slang | 11 + .../precompiled-module-included.slang | 9 + tools/gfx-unit-test/precompiled-module.cpp | 160 ++++++++++++++ tools/gfx-unit-test/precompiled-module.slang | 14 ++ 30 files changed, 1063 insertions(+), 131 deletions(-) create mode 100644 tools/gfx-unit-test/precompiled-module-2.cpp create mode 100644 tools/gfx-unit-test/precompiled-module-imported.slang create mode 100644 tools/gfx-unit-test/precompiled-module-included.slang create mode 100644 tools/gfx-unit-test/precompiled-module.cpp create mode 100644 tools/gfx-unit-test/precompiled-module.slang diff --git a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj index 335fa8156c..df2f9b4dd5 100644 --- a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj +++ b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj @@ -306,6 +306,8 @@ + + @@ -327,6 +329,9 @@ + + + diff --git a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters index fa5ca30e57..9ab4fb3c42 100644 --- a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters +++ b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters @@ -74,6 +74,12 @@ Source Files + + Source Files + + + Source Files + Source Files @@ -133,6 +139,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + Source Files diff --git a/slang.h b/slang.h index 54466ca042..b9da7df415 100644 --- a/slang.h +++ b/slang.h @@ -4444,6 +4444,17 @@ namespace slang ITypeConformance** outConformance, SlangInt conformanceIdOverride, ISlangBlob** outDiagnostics) = 0; + + /** Load a module from a Slang module blob. + */ + virtual SLANG_NO_THROW IModule* SLANG_MCALL loadModuleFromIRBlob( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics = nullptr) = 0; + + virtual SLANG_NO_THROW SlangInt SLANG_MCALL getLoadedModuleCount() = 0; + virtual SLANG_NO_THROW IModule* SLANG_MCALL getLoadedModule(SlangInt index) = 0; }; #define SLANG_UUID_ISession ISession::getTypeGuid() @@ -4691,6 +4702,22 @@ namespace slang /// Get the name of an entry point defined in the module. virtual SLANG_NO_THROW SlangResult SLANG_MCALL getDefinedEntryPoint(SlangInt32 index, IEntryPoint** outEntryPoint) = 0; + + /// Get a serialized representation of the checked module. + virtual SLANG_NO_THROW SlangResult SLANG_MCALL serialize(ISlangBlob** outSerializedBlob) = 0; + + /// Write the serialized representation of this module to a file. + virtual SLANG_NO_THROW SlangResult SLANG_MCALL writeToFile(char const* fileName) = 0; + + /// Get the name of the module. + virtual SLANG_NO_THROW const char* SLANG_MCALL getName() = 0; + + /// Get the path of the module. + virtual SLANG_NO_THROW const char* SLANG_MCALL getFilePath() = 0; + + /// Get the unique identity of the module. + virtual SLANG_NO_THROW const char* SLANG_MCALL getUniqueIdentity() = 0; + }; #define SLANG_UUID_IModule IModule::getTypeGuid() diff --git a/source/slang/slang-ast-base.h b/source/slang/slang-ast-base.h index e11dbe259c..b16eb5ddf8 100644 --- a/source/slang/slang-ast-base.h +++ b/source/slang/slang-ast-base.h @@ -104,10 +104,10 @@ struct Scope : public NodeBase // but the opposite it allowed. ContainerDecl* containerDecl = nullptr; - SLANG_UNREFLECTED // The parent of this scope (where lookup should go if nothing is found locally) Scope* parent = nullptr; + SLANG_UNREFLECTED // The next sibling of this scope (a peer for lookup) Scope* nextSibling = nullptr; }; diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index 8d598c4744..5d8023eaf1 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -633,4 +633,9 @@ InterfaceDecl* findParentInterfaceDecl(Decl* decl); bool isLocalVar(const Decl* decl); + +// Add a sibling lookup scope for `dest` to refer to `source`. +void addSiblingScopeForContainerDecl(ASTBuilder* builder, ContainerDecl* dest, ContainerDecl* source); +void addSiblingScopeForContainerDecl(ASTBuilder* builder, Scope* destScope, ContainerDecl* source); + } // namespace Slang diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 2a6ee8abce..f1409efe15 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -820,6 +820,11 @@ namespace Slang return as(parentDecl) != nullptr || as(parentDecl) != nullptr; } + bool isUnsafeForceInlineFunc(FunctionDeclBase* funcDecl) + { + return funcDecl->hasModifier(); + } + /// Is `decl` a global shader parameter declaration? bool isGlobalShaderParameter(VarDeclBase* decl) { @@ -7302,7 +7307,7 @@ namespace Slang { // Create a new sub-scope to wire the module // into our lookup chain. - addSiblingScopeForContainerDecl(scope, fileDecl); + addSiblingScopeForContainerDecl(getASTBuilder(), scope, fileDecl); } void SemanticsVisitor::importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl) @@ -7325,7 +7330,7 @@ namespace Slang if (moduleScope->containerDecl != moduleDecl && moduleScope->containerDecl->parentDecl != moduleDecl) continue; - addSiblingScopeForContainerDecl(scope, moduleScope->containerDecl); + addSiblingScopeForContainerDecl(getASTBuilder(), scope, moduleScope->containerDecl); } // Also import any modules from nested `import` declarations @@ -7547,7 +7552,7 @@ namespace Slang if (addedScopes.add(s->containerDecl)) { scopesAdded = true; - addSiblingScopeForContainerDecl(scope, s->containerDecl); + addSiblingScopeForContainerDecl(getASTBuilder(), scope, s->containerDecl); } } }; @@ -7608,7 +7613,7 @@ namespace Slang { ensureDecl(ns, DeclCheckState::ScopesWired); } - addSiblingScopeForContainerDecl(decl, otherNamespace); + addSiblingScopeForContainerDecl(getASTBuilder(), decl, otherNamespace); } } // For file decls, we need to continue searching up in the parent module scope. diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index c04cb73e4f..e9de74d8ef 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -248,14 +248,14 @@ namespace Slang return SourceLoc(); } - void SemanticsVisitor::addSiblingScopeForContainerDecl(ContainerDecl* dest, ContainerDecl* source) + void addSiblingScopeForContainerDecl(ASTBuilder* builder, ContainerDecl* dest, ContainerDecl* source) { - addSiblingScopeForContainerDecl(dest->ownedScope, source); + addSiblingScopeForContainerDecl(builder, dest->ownedScope, source); } - void SemanticsVisitor::addSiblingScopeForContainerDecl(Scope* destScope, ContainerDecl* source) + void addSiblingScopeForContainerDecl(ASTBuilder* builder, Scope* destScope, ContainerDecl* source) { - auto subScope = getASTBuilder()->create(); + auto subScope = builder->create(); subScope->containerDecl = source; subScope->nextSibling = destScope->nextSibling; diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 1808274f33..28ed47c53f 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -17,6 +17,8 @@ namespace Slang bool isGlobalDecl(Decl* decl); + bool isUnsafeForceInlineFunc(FunctionDeclBase* funcDecl); + bool isUniformParameterType(Type* type); Type* checkProperType( @@ -1048,11 +1050,6 @@ namespace Slang Scope* getScope(SyntaxNode* node); - // Add a sibling lookup scope for `dest` to refer to `source`. - void addSiblingScopeForContainerDecl(ContainerDecl* dest, ContainerDecl* source); - void addSiblingScopeForContainerDecl(Scope* destScope, ContainerDecl* source); - - void diagnoseDeprecatedDeclRefUsage(DeclRef declRef, SourceLoc loc, Expr* originalExpr); DeclRef getDefaultDeclRef(Decl* decl) diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp index 2e854554e2..7a39f114ba 100644 --- a/source/slang/slang-check-shader.cpp +++ b/source/slang/slang-check-shader.cpp @@ -905,58 +905,10 @@ namespace Slang // should work for typical HLSL code. // Index translationUnitCount = translationUnits.getCount(); - for(Index tt = 0; tt < translationUnitCount; ++tt) + for (Index tt = 0; tt < translationUnitCount; ++tt) { auto translationUnit = translationUnits[tt]; - for( auto globalDecl : translationUnit->getModuleDecl()->members ) - { - auto maybeFuncDecl = globalDecl; - if( auto genericDecl = as(maybeFuncDecl) ) - { - maybeFuncDecl = genericDecl->inner; - } - - auto funcDecl = as(maybeFuncDecl); - if(!funcDecl) - continue; - - auto entryPointAttr = funcDecl->findModifier(); - if(!entryPointAttr) - continue; - - // We've discovered a valid entry point. It is a function (possibly - // generic) that has a `[shader(...)]` attribute to mark it as an - // entry point. - // - // We will now register that entry point as an `EntryPoint` - // with an appropriately chosen profile. - // - // The profile will only include a stage, so that the profile "family" - // and "version" are left unspecified. Downstream code will need - // to be able to handle this case. - // - Profile profile; - profile.setStage(entryPointAttr->stage); - - RefPtr entryPoint = EntryPoint::create( - linkage, - makeDeclRef(funcDecl), - profile); - - validateEntryPoint(entryPoint, sink); - - // Note: in the case that the user didn't explicitly - // specify entry points and we are instead compiling - // a shader "library," then we do not want to automatically - // combine the entry points into groups in the generated - // `Program`, since that would be slightly too magical. - // - // Instead, each entry point will end up in a singleton - // group, so that its entry-point parameters lay out - // independent of the others. - // - translationUnit->module->_addEntryPoint(entryPoint); - } + translationUnit->getModule()->_discoverEntryPoints(sink); } } } diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index f8ad951086..c77f736bcf 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -2417,4 +2417,116 @@ namespace Slang } return false; } + + SLANG_NO_THROW SlangResult SLANG_MCALL Module::serialize(ISlangBlob** outSerializedBlob) + { + SerialContainerUtil::WriteOptions writeOptions; + writeOptions.sourceManager = getLinkage()->getSourceManager(); + OwnedMemoryStream memoryStream(FileAccess::Write); + SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(this, writeOptions, &memoryStream)); + *outSerializedBlob = RawBlob::create( + memoryStream.getContents().getBuffer(), + (size_t)memoryStream.getContents().getCount()).detach(); + return SLANG_OK; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL Module::writeToFile(char const* fileName) + { + SerialContainerUtil::WriteOptions writeOptions; + writeOptions.sourceManager = getLinkage()->getSourceManager(); + FileStream fileStream; + SLANG_RETURN_ON_FAIL(fileStream.init(fileName, FileMode::Create)); + return SerialContainerUtil::write(this, writeOptions, &fileStream); + } + + SLANG_NO_THROW const char* SLANG_MCALL Module::getName() + { + if (m_name) + return m_name->text.getBuffer(); + return nullptr; + } + + SLANG_NO_THROW const char* SLANG_MCALL Module::getFilePath() + { + if (m_pathInfo.hasFoundPath()) + return m_pathInfo.foundPath.getBuffer(); + return nullptr; + } + + SLANG_NO_THROW const char* SLANG_MCALL Module::getUniqueIdentity() + { + if (m_pathInfo.hasUniqueIdentity()) + return m_pathInfo.getMostUniqueIdentity().getBuffer(); + return nullptr; + } + + void validateEntryPoint( + EntryPoint* entryPoint, + DiagnosticSink* sink); + + void Module::_discoverEntryPoints(DiagnosticSink* sink) + { + for (auto globalDecl : m_moduleDecl->members) + { + auto maybeFuncDecl = globalDecl; + if (auto genericDecl = as(maybeFuncDecl)) + { + maybeFuncDecl = genericDecl->inner; + } + + auto funcDecl = as(maybeFuncDecl); + if (!funcDecl) + continue; + + Profile profile; + + auto entryPointAttr = funcDecl->findModifier(); + if (entryPointAttr) + { + // We've discovered a valid entry point. It is a function (possibly + // generic) that has a `[shader(...)]` attribute to mark it as an + // entry point. + // + // We will now register that entry point as an `EntryPoint` + // with an appropriately chosen profile. + // + // The profile will only include a stage, so that the profile "family" + // and "version" are left unspecified. Downstream code will need + // to be able to handle this case. + // + profile.setStage(entryPointAttr->stage); + } + else + { + // If there isn't a [shader] attribute, look for a [numthreads] attribute + // since that implicitly means a compute shader. + auto numThreadsAttr = funcDecl->findModifier(); + if (numThreadsAttr) + profile.setStage(Stage::Compute); + else + continue; + } + + RefPtr entryPoint = EntryPoint::create( + getLinkage(), + makeDeclRef(funcDecl), + profile); + + validateEntryPoint(entryPoint, sink); + + // Note: in the case that the user didn't explicitly + // specify entry points and we are instead compiling + // a shader "library," then we do not want to automatically + // combine the entry points into groups in the generated + // `Program`, since that would be slightly too magical. + // + // Instead, each entry point will end up in a singleton + // group, so that its entry-point parameters lay out + // independent of the others. + // + _addEntryPoint(entryPoint); + } + } + } + diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 18a91f78ae..d93eff0a62 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1325,6 +1325,22 @@ namespace Slang return Super::getEntryPointHash(entryPointIndex, targetIndex, outHash); } + /// Get a serialized representation of the checked module. + virtual SLANG_NO_THROW SlangResult SLANG_MCALL serialize(ISlangBlob** outSerializedBlob) override; + + /// Write the serialized representation of this module to a file. + virtual SLANG_NO_THROW SlangResult SLANG_MCALL writeToFile(char const* fileName) override; + + /// Get the name of the module. + virtual SLANG_NO_THROW const char* SLANG_MCALL getName() override; + + /// Get the path of the module. + virtual SLANG_NO_THROW const char* SLANG_MCALL getFilePath() override; + + /// Get the unique identity of the module. + virtual SLANG_NO_THROW const char* SLANG_MCALL getUniqueIdentity() override; + + virtual void buildHash(DigestBuilder& builder) SLANG_OVERRIDE; /// Create a module (initially empty). @@ -1354,6 +1370,10 @@ namespace Slang /// void setModuleDecl(ModuleDecl* moduleDecl);// { m_moduleDecl = moduleDecl; } + void setName(String name); + void setName(Name* name) { m_name = name; } + void setPathInfo(PathInfo pathInfo) { m_pathInfo = pathInfo; } + /// Set the IR for this module. /// /// This should only be called once, during creation of the module. @@ -1395,6 +1415,8 @@ namespace Slang /// void _collectShaderParams(); + void _discoverEntryPoints(DiagnosticSink* sink); + class ModuleSpecializationInfo : public SpecializationInfo { public: @@ -1426,6 +1448,9 @@ namespace Slang DiagnosticSink* sink) SLANG_OVERRIDE; private: + Name* m_name = nullptr; + PathInfo m_pathInfo; + // The AST for the module ModuleDecl* m_moduleDecl = nullptr; @@ -1539,6 +1564,13 @@ namespace Slang Dictionary getCombinedPreprocessorDefinitions(); + void setModuleName(Name* name) + { + moduleName = name; + if (module) + module->setName(name); + } + protected: void _addSourceFile(SourceFile* sourceFile); /* Given an artifact, find a PathInfo. @@ -1730,6 +1762,11 @@ namespace Slang /// lookup additional loaded modules. typedef Dictionary LoadedModuleDictionary; + enum ModuleBlobType + { + Source, IR + }; + /// A context for loading and re-using code modules. class Linkage : public RefObject, public slang::ISession { @@ -1742,6 +1779,17 @@ namespace Slang SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModule( const char* moduleName, slang::IBlob** outDiagnostics = nullptr) override; + slang::IModule* loadModuleFromBlob( + const char* moduleName, + const char* path, + slang::IBlob* source, + ModuleBlobType blobType, + slang::IBlob** outDiagnostics = nullptr); + SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromIRBlob( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics = nullptr) override; SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSource( const char* moduleName, const char* path, @@ -1786,6 +1834,8 @@ namespace Slang ISlangBlob** outDiagnostics) override; SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest( SlangCompileRequest** outCompileRequest) override; + virtual SLANG_NO_THROW SlangInt SLANG_MCALL getLoadedModuleCount() override; + virtual SLANG_NO_THROW slang::IModule* SLANG_MCALL getLoadedModule(SlangInt index) override; // Updates the supplied builder with linkage-related information, which includes preprocessor // defines, the compiler version, and other compiler options. This is then merged with the hash @@ -1935,6 +1985,15 @@ namespace Slang ISlangBlob* fileContentsBlob, SourceLoc const& loc, DiagnosticSink* sink, + const LoadedModuleDictionary* additionalLoadedModules, + ModuleBlobType blobType); + + RefPtr loadModuleFromIRBlobImpl( + Name* name, + const PathInfo& filePathInfo, + ISlangBlob* fileContentsBlob, + SourceLoc const& loc, + DiagnosticSink* sink, const LoadedModuleDictionary* additionalLoadedModules); void loadParsedModule( @@ -1952,6 +2011,8 @@ namespace Slang DiagnosticSink* sink, const LoadedModuleDictionary* loadedModules = nullptr); + void prepareDeserializedModule(Module* module, DiagnosticSink* sink); + SourceFile* findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem); struct IncludeResult { diff --git a/source/slang/slang-module-library.cpp b/source/slang/slang-module-library.cpp index b7290008b9..0975d6e8ff 100644 --- a/source/slang/slang-module-library.cpp +++ b/source/slang/slang-module-library.cpp @@ -66,7 +66,7 @@ SlangResult loadModuleLibrary(const Byte* inBytes, size_t bytesCount, EndToEndCo options.linkage = req->getLinkage(); options.sink = req->getSink(); - SLANG_RETURN_ON_FAIL(SerialContainerUtil::read(&riffContainer, options, containerData)); + SLANG_RETURN_ON_FAIL(SerialContainerUtil::read(&riffContainer, options, nullptr, containerData)); for (const auto& module : containerData.modules) { diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 45fa5a1259..477b437269 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -1280,21 +1280,24 @@ namespace Slang Parser* parser, void* /*userData*/) { auto decl = parser->astBuilder->create(); + auto moduleDecl = parser->getCurrentModuleDecl(); if (parser->LookAheadToken(TokenType::Identifier)) { auto nameToken = parser->ReadToken(TokenType::Identifier); decl->nameAndLoc.name = parser->getNamePool()->getName(nameToken.getContent()); decl->nameAndLoc.loc = nameToken.loc; + if (moduleDecl) moduleDecl->nameAndLoc = decl->nameAndLoc; } else if (parser->LookAheadToken(TokenType::StringLiteral)) { auto nameToken = parser->ReadToken(TokenType::StringLiteral); decl->nameAndLoc.name = parser->getNamePool()->getName(getStringLiteralTokenValue(nameToken)); decl->nameAndLoc.loc = nameToken.loc; + if (moduleDecl) moduleDecl->nameAndLoc = decl->nameAndLoc; } else { - if (auto moduleDecl = parser->getCurrentModuleDecl()) + if (moduleDecl) decl->nameAndLoc.name = moduleDecl->getName(); decl->nameAndLoc.loc = parser->tokenReader.peekLoc(); } diff --git a/source/slang/slang-serialize-ast-type-info.h b/source/slang/slang-serialize-ast-type-info.h index f7d8cab081..96c8a438fd 100644 --- a/source/slang/slang-serialize-ast-type-info.h +++ b/source/slang/slang-serialize-ast-type-info.h @@ -52,8 +52,8 @@ inline void serializeValPointerValue(SerialWriter* writer, Val* ptrValue, Serial inline void deserializeValPointerValue(SerialReader* reader, const SerialIndex* inSerial, void* outPtr) { - auto val = reader->getPointer(*(const SerialIndex*)inSerial).dynamicCast(); - *(Val**)outPtr = val; + auto val = reader->getValPointer(*(const SerialIndex*)inSerial); + *(void**)outPtr = val.m_ptr; } template diff --git a/source/slang/slang-serialize-container.cpp b/source/slang/slang-serialize-container.cpp index 175f970c90..6a75064ab3 100644 --- a/source/slang/slang-serialize-container.cpp +++ b/source/slang/slang-serialize-container.cpp @@ -213,7 +213,11 @@ namespace Slang { } ModuleSerialFilter filter(moduleDecl); - SerialWriter writer(serialClasses, &filter); + auto astWriterFlag = SerialWriter::Flag::ZeroInitialize; + if ((options.optionFlags & SerialOptionFlag::ASTFunctionBody) == 0) + astWriterFlag = (SerialWriter::Flag::Enum)(astWriterFlag | SerialWriter::Flag::SkipFunctionBody); + + SerialWriter writer(serialClasses, &filter, astWriterFlag); writer.getExtraObjects().set(sourceLocWriter); @@ -300,7 +304,7 @@ static List& _getCandidateExtensionList( return entry->candidateExtensions; } -/* static */Result SerialContainerUtil::read(RiffContainer* container, const ReadOptions& options, SerialContainerData& out) +/* static */Result SerialContainerUtil::read(RiffContainer* container, const ReadOptions& options, const LoadedModuleDictionary* additionalLoadedModules, SerialContainerData& out) { out.clear(); @@ -441,7 +445,7 @@ static List& _getCandidateExtensionList( NamePool* namePool = linkage->getNamePool(); Name* moduleNameName = namePool->getName(moduleName); - readModule = linkage->findOrImportModule(moduleNameName, SourceLoc::fromRaw(0), options.sink); + readModule = linkage->findOrImportModule(moduleNameName, SourceLoc::fromRaw(0), options.sink, additionalLoadedModules); if (!readModule) { return SLANG_FAIL; @@ -570,7 +574,6 @@ static List& _getCandidateExtensionList( else if (Val* val = dynamicCast(nodeBase)) { val->_setUnique(); - astBuilder->m_cachedNodes.tryGetValueOrAdd(ValKey(val), val); } } } diff --git a/source/slang/slang-serialize-container.h b/source/slang/slang-serialize-container.h index 7cbc97aa25..a2b596a24a 100644 --- a/source/slang/slang-serialize-container.h +++ b/source/slang/slang-serialize-container.h @@ -107,7 +107,7 @@ struct SerialContainerUtil static SlangResult write(const SerialContainerData& data, const WriteOptions& options, RiffContainer* container); /// Read the container into outData - static SlangResult read(RiffContainer* container, const ReadOptions& options, SerialContainerData& outData); + static SlangResult read(RiffContainer* container, const ReadOptions& options, const LoadedModuleDictionary* additionalLoadedModules, SerialContainerData& outData); /// Verify IR serialization static SlangResult verifyIRSerialize(IRModule* module, Session* session, const WriteOptions& options); diff --git a/source/slang/slang-serialize-factory.cpp b/source/slang/slang-serialize-factory.cpp index 351742e601..5eae5e740a 100644 --- a/source/slang/slang-serialize-factory.cpp +++ b/source/slang/slang-serialize-factory.cpp @@ -45,6 +45,11 @@ void* DefaultSerialObjectFactory::create(SerialTypeKind typeKind, SerialSubType return nullptr; } +void* DefaultSerialObjectFactory::getOrCreateVal(ValNodeDesc&& desc) +{ + return m_astBuilder->_getOrCreateImpl(_Move(desc)); +} + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ModuleSerialFilter !!!!!!!!!!!!!!!!!!!!!!!! SerialIndex ModuleSerialFilter::writePointer(SerialWriter* writer, const RefObject* inPtr) @@ -65,12 +70,6 @@ SerialIndex ModuleSerialFilter::writePointer(SerialWriter* writer, const NodeBas NodeBase* ptr = const_cast(inPtr); SLANG_ASSERT(ptr); - // We don't serialize Scope - if (as(ptr)) - { - writer->setPointerIndex(inPtr, SerialIndex(0)); - return SerialIndex(0); - } if (Decl* decl = as(ptr)) { diff --git a/source/slang/slang-serialize-factory.h b/source/slang/slang-serialize-factory.h index 7e51a840d1..caa2f97850 100644 --- a/source/slang/slang-serialize-factory.h +++ b/source/slang/slang-serialize-factory.h @@ -15,6 +15,7 @@ class DefaultSerialObjectFactory : public SerialObjectFactory public: virtual void* create(SerialTypeKind typeKind, SerialSubType subType) SLANG_OVERRIDE; + virtual void* getOrCreateVal(ValNodeDesc&& desc) SLANG_OVERRIDE; DefaultSerialObjectFactory(ASTBuilder* astBuilder) : m_astBuilder(astBuilder) diff --git a/source/slang/slang-serialize-types.h b/source/slang/slang-serialize-types.h index fb0dd2f9d2..7623eb4cf8 100644 --- a/source/slang/slang-serialize-types.h +++ b/source/slang/slang-serialize-types.h @@ -30,6 +30,7 @@ struct SerialOptionFlag SourceLocation = 0x02, ///< If set will output SourceLoc information, that can be reconstructed when read after being stored. ASTModule = 0x04, ///< If set will output AST modules - typically required, but potentially not desired (for example with obsfucation) IRModule = 0x08, ///< If set will output IR modules - typically required + ASTFunctionBody = 0x10, ///< If set will serialize AST function bodies. }; }; typedef SerialOptionFlag::Type SerialOptionFlags; diff --git a/source/slang/slang-serialize.cpp b/source/slang/slang-serialize.cpp index 1f5b6942d7..1c8abb8f95 100644 --- a/source/slang/slang-serialize.cpp +++ b/source/slang/slang-serialize.cpp @@ -3,6 +3,7 @@ #include "slang-ast-base.h" #include "slang-ast-builder.h" +#include "slang-check-impl.h" namespace Slang { @@ -222,6 +223,50 @@ SerialWriter::SerialWriter(SerialClasses* classes, SerialFilter* filter, Flags f m_ptrMap.add(nullptr, 0); } +struct SkipFunctionBodyRAII +{ + FunctionDeclBase* funcDecl = nullptr; + Stmt* oldBody = nullptr; + SkipFunctionBodyRAII(SerialWriter::Flags flags, const SerialClass* serialCls, const void* ptr) + { + if ((flags & SerialWriter::Flag::SkipFunctionBody) == 0) + return; + + if (serialCls->typeKind != SerialTypeKind::NodeBase) + return; + auto cls = serialCls; + while (cls) + { + auto astNodeType = (ASTNodeType)cls->subType; + if (astNodeType == ASTNodeType::FunctionDeclBase) + { + funcDecl = (FunctionDeclBase*)ptr; + break; + } + cls = cls->super; + } + if (funcDecl) + { + oldBody = funcDecl->body; + // We always need to include body of unsafeForceInlineEarly functions + // since they will need to be available at IR lowering time of the + // user module for pre-linking inling. + if (!isUnsafeForceInlineFunc(funcDecl)) + { + funcDecl->body = nullptr; + } + } + + } + ~SkipFunctionBodyRAII() + { + if (funcDecl) + { + funcDecl->body = oldBody; + } + } +}; + SerialIndex SerialWriter::writeObject(const SerialClass* serialCls, const void* ptr) { if (serialCls->flags & SerialClassFlag::DontSerialize) @@ -229,6 +274,16 @@ SerialIndex SerialWriter::writeObject(const SerialClass* serialCls, const void* return SerialIndex(0); } + if (serialCls->typeKind == SerialTypeKind::NodeBase && + ReflectClassInfo::isSubClassOf(serialCls->subType, Val::kReflectClassInfo)) + { + return writeValObject((Val*)ptr); + } + + // If we are skipping function bodies, set the body field to nullptr, and + // restore it after serialization. + SkipFunctionBodyRAII clearFunctionBodyRAII(m_flags, serialCls, ptr); + // This pointer cannot be in the map SLANG_ASSERT(m_ptrMap.tryGetValue(ptr) == nullptr); @@ -279,6 +334,62 @@ SerialIndex SerialWriter::writeObject(const NodeBase* node) return writeObject(serialClass, (const void*)node); } +SerialIndex SerialWriter::writeValObject(const Val* node) +{ + typedef SerialInfo::ValEntry ValEntry; + + size_t size = node->getOperandCount() * sizeof(SerialInfo::SerialValOperand); + ValEntry* nodeEntry = (ValEntry*)m_arena.allocateAligned(sizeof(ValEntry) + size, SerialInfo::MAX_ALIGNMENT); + + nodeEntry->typeKind = SerialTypeKind::NodeBase; + nodeEntry->subType = (SerialSubType)node->astNodeType; + nodeEntry->operandCount = (uint32_t)node->getOperandCount(); + nodeEntry->info = SerialInfo::makeEntryInfo(SerialInfo::MAX_ALIGNMENT); + + // We add before adding fields, so if the fields point to this, the entry will be set + auto index = _add(node, nodeEntry); + + ShortList serializedOperands; + + for (Index i = 0; i < node->getOperandCount(); i++) + { + auto operand = node->m_operands[i]; + switch (operand.kind) + { + case ValNodeOperandKind::ConstantValue: + serializedOperands.add((SerialIndex)0); + break; + case ValNodeOperandKind::ValNode: + case ValNodeOperandKind::ASTNode: + serializedOperands.add(addPointer(operand.values.nodeOperand)); + break; + } + } + + SLANG_ASSERT(serializedOperands.getCount() == node->getOperandCount()); + + auto serialOperands = (SerialInfo::SerialValOperand*)(nodeEntry + 1); + for (Index i = 0; i < node->getOperandCount(); i++) + { + auto serialOperand = serialOperands + i; + auto operand = node->m_operands[i]; + serialOperand->type = (int)operand.kind; + switch (operand.kind) + { + case ValNodeOperandKind::ConstantValue: + serialOperand->payload = operand.values.intOperand; + break; + case ValNodeOperandKind::ValNode: + serialOperand->payload = (uint64_t)serializedOperands[i]; + break; + case ValNodeOperandKind::ASTNode: + serialOperand->payload = (uint64_t)serializedOperands[i]; + break; + } + } + return index; +} + SerialIndex SerialWriter::writeObject(const RefObject* obj) { const SerialRefObject* serialObj = as(obj); @@ -633,6 +744,9 @@ size_t SerialInfo::Entry::calcSize(SerialClasses* serialClasses) const auto serialClass = serialClasses->getSerialClass(typeKind, entry->subType); + if (ReflectClassInfo::isSubClassOf(entry->subType, Val::kReflectClassInfo)) + return sizeof(ValEntry) + static_cast(this)->operandCount * sizeof(SerialValOperand); + // Align by the alignment of the entry size_t alignment = getAlignment(entry->info); size_t size = sizeof(ObjectEntry) + serialClass->size; @@ -722,6 +836,49 @@ SerialPointer SerialReader::getPointer(SerialIndex index) return ptr; } +SerialPointer SerialReader::getValPointer(SerialIndex index) +{ + if (index == SerialIndex(0)) + { + return SerialPointer(); + } + + SLANG_ASSERT(SerialIndexRaw(index) < SerialIndexRaw(m_entries.getCount())); + + SerialPointer& ptr = m_objects[Index(index)]; + + if (ptr.m_ptr) + return ptr; + + const SerialInfo::ValEntry* entry = (SerialInfo::ValEntry*)m_entries[Index(index)]; + ValNodeDesc desc; + desc.type = (ASTNodeType)entry->subType; + auto readPtr = (SerialInfo::SerialValOperand*)(entry + 1); + for (uint32_t i = 0; i < entry->operandCount; i++) + { + auto serialOperand = readPtr[i]; + ValNodeOperand operand; + operand.kind = (ValNodeOperandKind)(serialOperand.type); + switch (operand.kind) + { + case ValNodeOperandKind::ConstantValue: + operand.values.intOperand = serialOperand.payload; + break; + case ValNodeOperandKind::ASTNode: + operand.values.nodeOperand = (NodeBase*)getPointer((SerialIndex)serialOperand.payload).m_ptr; + break; + case ValNodeOperandKind::ValNode: + operand.values.nodeOperand = (Val*)getValPointer((SerialIndex)serialOperand.payload).m_ptr; + break; + } + desc.operands.add(operand); + } + desc.init(); + ptr.m_kind = SerialTypeKind::NodeBase; + ptr.m_ptr = this->m_objectFactory->getOrCreateVal(_Move(desc)); + return ptr; +} + String SerialReader::getString(SerialIndex index) { if (index == SerialIndex(0)) @@ -902,6 +1059,12 @@ SlangResult SerialReader::constructObjects(NamePool* namePool) case SerialTypeKind::NodeBase: { auto objectEntry = static_cast(entry); + + // Don't create object for Vals. + if (objectEntry->typeKind == SerialTypeKind::NodeBase && + ReflectClassInfo::isSubClassOf(objectEntry->subType, Val::kReflectClassInfo)) + break; + void* obj = m_objectFactory->create(objectEntry->typeKind, objectEntry->subType); if (!obj) { @@ -912,7 +1075,7 @@ SlangResult SerialReader::constructObjects(NamePool* namePool) } case SerialTypeKind::Array: { - // Don't need to construct an object, as will be accessed an interpreted by the object that holds it + // Don't need to construct an object, as will be accessed and interpreted by the object that holds it break; } } @@ -944,6 +1107,8 @@ SlangResult SerialReader::deserializeObjects() { return SLANG_FAIL; } + if (ReflectClassInfo::isSubClassOf(objectEntry->subType, Val::kReflectClassInfo)) + continue; const uint8_t* src = (const uint8_t*)(objectEntry + 1); uint8_t* dst = (uint8_t*)dstPtr.m_ptr; diff --git a/source/slang/slang-serialize.h b/source/slang/slang-serialize.h index 3071dc174f..a91ff21e96 100644 --- a/source/slang/slang-serialize.h +++ b/source/slang/slang-serialize.h @@ -27,6 +27,8 @@ docs/design/serialization.md // Predeclare typedef uint32_t SerialSourceLoc; class NodeBase; +class Val; +struct ValNodeDesc; // Pre-declare class SerialClasses; @@ -119,11 +121,23 @@ struct SerialInfo uint32_t _pad0; ///< Necessary, because a node *can* have MAX_ALIGNEMENT }; + struct ValEntry : Entry + { + SerialSubType subType; + uint32_t operandCount; + }; + struct ArrayEntry : Entry { uint16_t elementSize; uint32_t elementCount; }; + + struct SerialValOperand + { + int type; + uint64_t payload; + }; }; typedef uint32_t SerialIndexRaw; @@ -185,6 +199,7 @@ class SerialObjectFactory { public: virtual void* create(SerialTypeKind typeKind, SerialSubType subType) = 0; + virtual void* getOrCreateVal(ValNodeDesc&& desc) = 0; }; class SerialExtraObjects @@ -229,6 +244,8 @@ class SerialReader : public RefObject const void* getArray(SerialIndex index, Index& outCount); SerialPointer getPointer(SerialIndex index); + SerialPointer getValPointer(SerialIndex index); + String getString(SerialIndex index); Name* getName(SerialIndex index); UnownedStringSlice getStringSlice(SerialIndex index); @@ -329,7 +346,10 @@ class SerialWriter : public RefObject /// If set will zero initialize backing memory. This is slower but /// is desirable to make two serializations of the same thing produce the /// identical serialized result. - ZeroInitialize = 0x1 + ZeroInitialize = 0x1, + + /// If set will not serialize function body. + SkipFunctionBody = 0x2, }; }; @@ -342,6 +362,7 @@ class SerialWriter : public RefObject /// Write the object at the pointer SerialIndex writeObject(const NodeBase* ptr); SerialIndex writeObject(const RefObject* ptr); + SerialIndex writeValObject(const Val* ptr); /// Add an array - may need to convert to serialized format template diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h index 6b134e5235..807db9a851 100644 --- a/source/slang/slang-syntax.h +++ b/source/slang/slang-syntax.h @@ -295,12 +295,6 @@ namespace Slang // being in templates, because gcc/clang get angry. // template - void FilteredModifierList::Iterator::operator++() - { - current = adjust(current->next); - } - // - template Modifier* FilteredModifierList::adjust(Modifier* modifier) { Modifier* m = modifier; @@ -315,6 +309,11 @@ namespace Slang } } + template + void FilteredModifierList::Iterator::operator++() + { + current = FilteredModifierList::adjust(current->next); + } // enum class UserDefinedAttributeTargets diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 267d911738..395285b411 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -26,7 +26,7 @@ #include "slang-parser.h" #include "slang-preprocessor.h" #include "slang-type-layout.h" - +# #include "slang-options.h" #include "slang-repro.h" @@ -480,11 +480,12 @@ SlangResult Session::_readBuiltinModule(ISlangFileSystem* fileSystem, Scope* sco // Hmm - don't have a suitable sink yet, so attempt to just not have one options.sink = nullptr; - SLANG_RETURN_ON_FAIL(SerialContainerUtil::read(&riffContainer, options, containerData)); + SLANG_RETURN_ON_FAIL(SerialContainerUtil::read(&riffContainer, options, nullptr, containerData)); for (auto& srcModule : containerData.modules) { RefPtr module(new Module(linkage, srcModule.astBuilder)); + module->setName(moduleName); ModuleDecl* moduleDecl = as(srcModule.astRootNode); // Set the module back reference on the decl @@ -1086,10 +1087,11 @@ SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModule( } } -SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSource( +slang::IModule* Linkage::loadModuleFromBlob( const char* moduleName, const char* path, slang::IBlob* source, + ModuleBlobType blobType, slang::IBlob** outDiagnostics) { SLANG_AST_BUILDER_RAII(getASTBuilder()); @@ -1124,7 +1126,8 @@ SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSource( source, SourceLoc(), &sink, - nullptr); + nullptr, + blobType); sink.getBlobIfNeeded(outDiagnostics); return asExternal(module); @@ -1136,6 +1139,24 @@ SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSource( } } +SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSource( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics) +{ + return loadModuleFromBlob(moduleName, path, source, ModuleBlobType::Source, outDiagnostics); +} + +SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromIRBlob( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics) +{ + return loadModuleFromBlob(moduleName, path, source, ModuleBlobType::IR, outDiagnostics); +} + SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createCompositeComponentType( slang::IComponentType* const* componentTypes, SlangInt componentTypeCount, @@ -1398,6 +1419,18 @@ SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createCompileRequest( return SLANG_OK; } +SLANG_NO_THROW SlangInt SLANG_MCALL Linkage::getLoadedModuleCount() +{ + return loadedModulesList.getCount(); +} + +SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::getLoadedModule(SlangInt index) +{ + if (index >= 0 && index < loadedModulesList.getCount()) + return loadedModulesList[index].get(); + return nullptr; +} + void Linkage::buildHash(DigestBuilder& builder, SlangInt targetIndex) { // Add the Slang compiler version to the hash @@ -2909,8 +2942,7 @@ int FrontEndCompileRequest::addTranslationUnit(SourceLanguage language, Name* mo translationUnit->compileRequest = this; translationUnit->sourceLanguage = SourceLanguage(language); - translationUnit->moduleName = moduleName; - + translationUnit->setModuleName(moduleName); return addTranslationUnit(translationUnit); } @@ -3097,6 +3129,54 @@ void Linkage::loadParsedModule( loadedModulesList.add(loadedModule); } +RefPtr Linkage::loadModuleFromIRBlobImpl( + Name* name, + const PathInfo& filePathInfo, + ISlangBlob* fileContentsBlob, + SourceLoc const& loc, + DiagnosticSink* sink, + const LoadedModuleDictionary* additionalLoadedModules) +{ + RefPtr resultModule = new Module(this, getASTBuilder()); + resultModule->setName(name); + ModuleBeingImportedRAII moduleBeingImported( + this, + resultModule, + name, + loc); + + String mostUniqueIdentity = filePathInfo.getMostUniqueIdentity(); + SLANG_ASSERT(mostUniqueIdentity.getLength() > 0); + + mapPathToLoadedModule.add(mostUniqueIdentity, resultModule); + mapNameToLoadedModules.add(name, resultModule); + + RiffContainer container; + MemoryStreamBase readStream(FileAccess::Read, fileContentsBlob->getBufferPointer(), fileContentsBlob->getBufferSize()); + SLANG_RETURN_NULL_ON_FAIL(RiffUtil::read(&readStream, container)); + SerialContainerUtil::ReadOptions readOptions; + readOptions.linkage = this; + readOptions.astBuilder = getASTBuilder(); + readOptions.session = getSessionImpl(); + readOptions.sharedASTBuilder = getASTBuilder()->getSharedASTBuilder(); + readOptions.sink = sink; + readOptions.sourceManager = getSourceManager(); + readOptions.namePool = getNamePool(); + SerialContainerData containerData; + SLANG_RETURN_NULL_ON_FAIL(SerialContainerUtil::read(&container, readOptions, additionalLoadedModules, containerData)); + if (containerData.modules.getCount() != 1) + return nullptr; + auto moduleEntry = containerData.modules.getFirst(); + resultModule->setIRModule(moduleEntry.irModule); + resultModule->setModuleDecl(as(moduleEntry.astRootNode)); + + prepareDeserializedModule(resultModule, sink); + + loadedModulesList.add(resultModule); + resultModule->setPathInfo(filePathInfo); + return resultModule; +} + Module* Linkage::loadModule(String const& name) { // TODO: We either need to have a diagnostics sink @@ -3129,15 +3209,19 @@ RefPtr Linkage::loadModule( ISlangBlob* sourceBlob, SourceLoc const& srcLoc, DiagnosticSink* sink, - const LoadedModuleDictionary* additionalLoadedModules) + const LoadedModuleDictionary* additionalLoadedModules, + ModuleBlobType blobType) { + if (blobType == ModuleBlobType::IR) + return loadModuleFromIRBlobImpl(name, filePathInfo, sourceBlob, srcLoc, sink, additionalLoadedModules); + RefPtr frontEndReq = new FrontEndCompileRequest(this, nullptr, sink); frontEndReq->additionalLoadedModules = additionalLoadedModules; RefPtr translationUnit = new TranslationUnitRequest(frontEndReq); translationUnit->compileRequest = frontEndReq; - translationUnit->moduleName = name; + translationUnit->setModuleName(name); Stage impliedStage; translationUnit->sourceLanguage = SourceLanguage::Slang; @@ -3216,6 +3300,7 @@ RefPtr Linkage::loadModule( return nullptr; } + module->setPathInfo(filePathInfo); return module; } @@ -3299,7 +3384,6 @@ RefPtr Linkage::findOrImportModule( return previouslyLoadedModule; } - auto fileName = getFileNameFromModuleName(name); // Next, try to find the file of the given name, // using our ordinary include-handling logic. @@ -3310,46 +3394,61 @@ RefPtr Linkage::findOrImportModule( PathInfo pathIncludedFromInfo = getSourceManager()->getPathInfo(loc, SourceLocType::Actual); PathInfo filePathInfo; - ComPtr fileContents; + auto moduleSourceFileName = getFileNameFromModuleName(name); - // We have to load via the found path - as that is how file was originally loaded - if (SLANG_FAILED(includeSystem.findFile(fileName, pathIncludedFromInfo.foundPath, filePathInfo))) + // Look for a precompiled module first, if not exist, load from source. + for (int checkBinaryModule = 1; checkBinaryModule >= 0; checkBinaryModule--) { - if (name && name->text == "glsl") - { - // This is a builtin glsl module, just load it from embedded definition. - fileContents = getSessionImpl()->getGLSLLibraryCode(); - filePathInfo = PathInfo::makeFromString("glsl"); - } + String fileName; + if (checkBinaryModule == 1) + fileName = Path::replaceExt(moduleSourceFileName, "slang-module"); else + fileName = moduleSourceFileName; + + ComPtr fileContents; + + // We have to load via the found path - as that is how file was originally loaded + if (SLANG_FAILED(includeSystem.findFile(fileName, pathIncludedFromInfo.foundPath, filePathInfo))) { - sink->diagnose(loc, Diagnostics::cannotFindFile, fileName); - mapNameToLoadedModules[name] = nullptr; - return nullptr; + if (name && name->text == "glsl") + { + // This is a builtin glsl module, just load it from embedded definition. + fileContents = getSessionImpl()->getGLSLLibraryCode(); + filePathInfo = PathInfo::makeFromString("glsl"); + checkBinaryModule = 0; + } + else + { + continue; + } } - } - // Maybe this was loaded previously at a different relative name? - if (mapPathToLoadedModule.tryGetValue(filePathInfo.getMostUniqueIdentity(), loadedModule)) - return loadedModule; + // Maybe this was loaded previously at a different relative name? + if (mapPathToLoadedModule.tryGetValue(filePathInfo.getMostUniqueIdentity(), loadedModule)) + return loadedModule; - // Try to load it - if( !fileContents && SLANG_FAILED(includeSystem.loadFile(filePathInfo, fileContents))) - { - sink->diagnose(loc, Diagnostics::cannotOpenFile, fileName); - mapNameToLoadedModules[name] = nullptr; - return nullptr; + // Try to load it + if (!fileContents && SLANG_FAILED(includeSystem.loadFile(filePathInfo, fileContents))) + { + continue; + } + + // We've found a file that we can load for the given module, so + // go ahead and perform the module-load action + return loadModule( + name, + filePathInfo, + fileContents, + loc, + sink, + loadedModules, + (checkBinaryModule == 1 ? ModuleBlobType::IR : ModuleBlobType::Source)); } - // We've found a file that we can load for the given module, so - // go ahead and perform the module-load action - return loadModule( - name, - filePathInfo, - fileContents, - loc, - sink, - loadedModules); + // Error: we cannot find the file. + sink->diagnose(loc, Diagnostics::cannotOpenFile, moduleSourceFileName); + mapNameToLoadedModules[name] = nullptr; + return nullptr; } SourceFile* Linkage::findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem) @@ -3567,8 +3666,15 @@ void Module::addFileDependency(SourceFile* sourceFile) void Module::setModuleDecl(ModuleDecl* moduleDecl) { m_moduleDecl = moduleDecl; + moduleDecl->module = this; +} + +void Module::setName(String name) +{ + m_name = getLinkage()->getNamePool()->getName(name); } + RefPtr Module::findEntryPointByName(UnownedStringSlice const& name) { // TODO: We should consider having this function be expanded to be able @@ -4828,6 +4934,22 @@ void Linkage::setFileSystem(ISlangFileSystem* inFileSystem) getSourceManager()->setFileSystemExt(m_fileSystemExt); } +void Linkage::prepareDeserializedModule(Module* module, DiagnosticSink* sink) +{ + module->_collectShaderParams(); + module->_discoverEntryPoints(sink); + + // Hook up fileDecl's scope to module's scope. + auto moduleDecl = module->getModuleDecl(); + for (auto globalDecl : moduleDecl->members) + { + if (auto fileDecl = as(globalDecl)) + { + addSiblingScopeForContainerDecl(m_astBuilder, moduleDecl->ownedScope, fileDecl); + } + } +} + void Linkage::setRequireCacheFileSystem(bool requireCacheFileSystem) { if (requireCacheFileSystem == m_requireCacheFileSystem) diff --git a/tools/gfx-unit-test/gfx-test-util.cpp b/tools/gfx-unit-test/gfx-test-util.cpp index 298283a4ae..748ced5eb9 100644 --- a/tools/gfx-unit-test/gfx-test-util.cpp +++ b/tools/gfx-unit-test/gfx-test-util.cpp @@ -71,6 +71,54 @@ namespace gfx_test return SLANG_OK; } + Slang::Result loadComputeProgram( + gfx::IDevice* device, + slang::ISession* slangSession, + Slang::ComPtr& outShaderProgram, + const char* shaderModuleName, + const char* entryPointName, + slang::ProgramLayout*& slangReflection) + { + Slang::ComPtr diagnosticsBlob; + slang::IModule* module = slangSession->loadModule(shaderModuleName, diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; + + ComPtr computeEntryPoint; + SLANG_RETURN_ON_FAIL( + module->findEntryPointByName(entryPointName, computeEntryPoint.writeRef())); + + Slang::List componentTypes; + componentTypes.add(module); + componentTypes.add(computeEntryPoint); + + Slang::ComPtr composedProgram; + SlangResult result = slangSession->createCompositeComponentType( + componentTypes.getBuffer(), + componentTypes.getCount(), + composedProgram.writeRef(), + diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + SLANG_RETURN_ON_FAIL(result); + + ComPtr linkedProgram; + result = composedProgram->link(linkedProgram.writeRef(), diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + SLANG_RETURN_ON_FAIL(result); + + composedProgram = linkedProgram; + slangReflection = composedProgram->getLayout(); + + gfx::IShaderProgram::Desc programDesc = {}; + programDesc.slangGlobalScope = composedProgram.get(); + + auto shaderProgram = device->createProgram(programDesc); + + outShaderProgram = shaderProgram; + return SLANG_OK; + } + Slang::Result loadComputeProgramFromSource( gfx::IDevice* device, Slang::ComPtr& outShaderProgram, @@ -222,10 +270,7 @@ namespace gfx_test SLANG_IGNORE_TEST } deviceDesc.slang.slangGlobalSession = context->slangGlobalSession; - Slang::List searchPaths; - searchPaths.add(""); - searchPaths.add("../../tools/gfx-unit-test"); - searchPaths.add("tools/gfx-unit-test"); + Slang::List searchPaths = getSlangSearchPaths(); searchPaths.addRange(additionalSearchPaths); deviceDesc.slang.searchPaths = searchPaths.getBuffer(); deviceDesc.slang.searchPathCount = (gfx::GfxCount)searchPaths.getCount(); @@ -253,6 +298,15 @@ namespace gfx_test return device; } + Slang::List getSlangSearchPaths() + { + Slang::List searchPaths; + searchPaths.add(""); + searchPaths.add("../../tools/gfx-unit-test"); + searchPaths.add("tools/gfx-unit-test"); + return searchPaths; + } + #if GFX_ENABLE_RENDERDOC_INTEGRATION RENDERDOC_API_1_1_2* rdoc_api = NULL; void initializeRenderDoc() diff --git a/tools/gfx-unit-test/gfx-test-util.h b/tools/gfx-unit-test/gfx-test-util.h index 501deeae0b..6438304136 100644 --- a/tools/gfx-unit-test/gfx-test-util.h +++ b/tools/gfx-unit-test/gfx-test-util.h @@ -18,6 +18,14 @@ namespace gfx_test const char* entryPointName, slang::ProgramLayout*& slangReflection); + Slang::Result loadComputeProgram( + gfx::IDevice* device, + slang::ISession* slangSession, + Slang::ComPtr& outShaderProgram, + const char* shaderModuleName, + const char* entryPointName, + slang::ProgramLayout*& slangReflection); + Slang::Result loadComputeProgramFromSource( gfx::IDevice* device, Slang::ComPtr& outShaderProgram, @@ -79,6 +87,8 @@ namespace gfx_test Slang::RenderApiFlag::Enum api, Slang::List additionalSearchPaths = {}, gfx::IDevice::ShaderCacheDesc shaderCache = {}); + + Slang::List getSlangSearchPaths(); void initializeRenderDoc(); void renderDocBeginFrame(); diff --git a/tools/gfx-unit-test/precompiled-module-2.cpp b/tools/gfx-unit-test/precompiled-module-2.cpp new file mode 100644 index 0000000000..3da77e05c5 --- /dev/null +++ b/tools/gfx-unit-test/precompiled-module-2.cpp @@ -0,0 +1,181 @@ +#include "tools/unit-test/slang-unit-test.h" + +#include "slang-gfx.h" +#include "gfx-test-util.h" +#include "tools/gfx-util/shader-cursor.h" +#include "source/core/slang-basic.h" +#include "source/core/slang-blob.h" +#include "source/core/slang-memory-file-system.h" +#include "source/core/slang-io.h" + +using namespace gfx; + +namespace gfx_test +{ + // Test that mixing precompiled and non-precompiled modules is working. + + static Slang::Result precompileProgram( + gfx::IDevice* device, + ISlangMutableFileSystem* fileSys, + const char* shaderModuleName) + { + Slang::ComPtr slangSession; + SLANG_RETURN_ON_FAIL(device->getSlangSession(slangSession.writeRef())); + slang::SessionDesc sessionDesc = {}; + auto searchPaths = getSlangSearchPaths(); + sessionDesc.searchPathCount = searchPaths.getCount(); + sessionDesc.searchPaths = searchPaths.getBuffer(); + auto globalSession = slangSession->getGlobalSession(); + globalSession->createSession(sessionDesc, slangSession.writeRef()); + + Slang::ComPtr diagnosticsBlob; + slang::IModule* module = slangSession->loadModule(shaderModuleName, diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; + + // Write loaded modules to memory file system. + for (SlangInt i = 0; i < slangSession->getLoadedModuleCount(); i++) + { + auto module = slangSession->getLoadedModule(i); + auto path = module->getFilePath(); + if (path) + { + auto name = module->getName(); + ComPtr outBlob; + module->serialize(outBlob.writeRef()); + fileSys->saveFileBlob((Slang::String(name) + ".slang-module").getBuffer(), outBlob); + } + } + return SLANG_OK; + } + + void precompiledModule2TestImpl(IDevice* device, UnitTestContext* context) + { + Slang::ComPtr transientHeap; + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096; + GFX_CHECK_CALL_ABORT( + device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); + + // First, load and compile the slang source. + ComPtr memoryFileSystem = ComPtr(new Slang::MemoryFileSystem()); + + ComPtr shaderProgram; + slang::ProgramLayout* slangReflection; + GFX_CHECK_CALL_ABORT(precompileProgram(device, memoryFileSystem.get(), "precompiled-module-imported")); + + // Next, load the precompiled slang program. + Slang::ComPtr slangSession; + device->getSlangSession(slangSession.writeRef()); + slang::SessionDesc sessionDesc = {}; + sessionDesc.targetCount = 1; + slang::TargetDesc targetDesc = {}; + switch (device->getDeviceInfo().deviceType) + { + case gfx::DeviceType::DirectX12: + targetDesc.format = SLANG_DXIL; + targetDesc.profile = device->getSlangSession()->getGlobalSession()->findProfile("sm_6_1"); + break; + case gfx::DeviceType::Vulkan: + targetDesc.format = SLANG_SPIRV; + targetDesc.profile = device->getSlangSession()->getGlobalSession()->findProfile("GLSL_460"); + break; + } + sessionDesc.targets = &targetDesc; + sessionDesc.fileSystem = memoryFileSystem.get(); + auto globalSession = slangSession->getGlobalSession(); + globalSession->createSession(sessionDesc, slangSession.writeRef()); + + const char* moduleSrc = R"( + import "precompiled-module-imported"; + + // Main entry-point. + + using namespace ns; + + [shader("compute")] + [numthreads(4, 1, 1)] + void computeMain( + uint3 sv_dispatchThreadID : SV_DispatchThreadID, + uniform RWStructuredBuffer buffer) + { + buffer[sv_dispatchThreadID.x] = helperFunc() + helperFunc1(); + } + )"; + memoryFileSystem->saveFile("precompiled-module.slang", moduleSrc, strlen(moduleSrc)); + GFX_CHECK_CALL_ABORT(loadComputeProgram(device, slangSession, shaderProgram, "precompiled-module", "computeMain", slangReflection)); + + ComputePipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + ComPtr pipelineState; + GFX_CHECK_CALL_ABORT( + device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); + + const int numberCount = 4; + float initialData[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + IBufferResource::Desc bufferDesc = {}; + bufferDesc.sizeInBytes = numberCount * sizeof(float); + bufferDesc.format = gfx::Format::Unknown; + bufferDesc.elementSize = sizeof(float); + bufferDesc.allowedStates = ResourceStateSet( + ResourceState::ShaderResource, + ResourceState::UnorderedAccess, + ResourceState::CopyDestination, + ResourceState::CopySource); + bufferDesc.defaultState = ResourceState::UnorderedAccess; + bufferDesc.memoryType = MemoryType::DeviceLocal; + + ComPtr numbersBuffer; + GFX_CHECK_CALL_ABORT(device->createBufferResource( + bufferDesc, + (void*)initialData, + numbersBuffer.writeRef())); + + ComPtr bufferView; + IResourceView::Desc viewDesc = {}; + viewDesc.type = IResourceView::Type::UnorderedAccess; + viewDesc.format = Format::Unknown; + GFX_CHECK_CALL_ABORT( + device->createBufferView(numbersBuffer, nullptr, viewDesc, bufferView.writeRef())); + + // We have done all the set up work, now it is time to start recording a command buffer for + // GPU execution. + { + ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics }; + auto queue = device->createCommandQueue(queueDesc); + + auto commandBuffer = transientHeap->createCommandBuffer(); + auto encoder = commandBuffer->encodeComputeCommands(); + + auto rootObject = encoder->bindPipeline(pipelineState); + + ShaderCursor entryPointCursor( + rootObject->getEntryPoint(0)); // get a cursor the the first entry-point. + // Bind buffer view to the entry point. + entryPointCursor.getPath("buffer").setResource(bufferView); + + encoder->dispatchCompute(1, 1, 1); + encoder->endEncoding(); + commandBuffer->close(); + queue->executeCommandBuffer(commandBuffer); + queue->waitOnHost(); + } + + compareComputeResult( + device, + numbersBuffer, + Slang::makeArray(3.0f, 3.0f, 3.0f, 3.0f)); + } + + SLANG_UNIT_TEST(precompiledModule2D3D12) + { + runTestImpl(precompiledModule2TestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); + } + + SLANG_UNIT_TEST(precompiledModule2Vulkan) + { + runTestImpl(precompiledModule2TestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); + } + +} diff --git a/tools/gfx-unit-test/precompiled-module-imported.slang b/tools/gfx-unit-test/precompiled-module-imported.slang new file mode 100644 index 0000000000..5c59e99b0c --- /dev/null +++ b/tools/gfx-unit-test/precompiled-module-imported.slang @@ -0,0 +1,11 @@ +module "precompiled-module-imported"; + +__include "precompiled-module-included.slang"; + +namespace ns +{ + public int helperFunc() + { + return 1; + } +} \ No newline at end of file diff --git a/tools/gfx-unit-test/precompiled-module-included.slang b/tools/gfx-unit-test/precompiled-module-included.slang new file mode 100644 index 0000000000..88d9e57d70 --- /dev/null +++ b/tools/gfx-unit-test/precompiled-module-included.slang @@ -0,0 +1,9 @@ +implementing "precompiled-module-imported"; + +namespace ns +{ + public int helperFunc1() + { + return 2; + } +} \ No newline at end of file diff --git a/tools/gfx-unit-test/precompiled-module.cpp b/tools/gfx-unit-test/precompiled-module.cpp new file mode 100644 index 0000000000..0265751206 --- /dev/null +++ b/tools/gfx-unit-test/precompiled-module.cpp @@ -0,0 +1,160 @@ +#include "tools/unit-test/slang-unit-test.h" + +#include "slang-gfx.h" +#include "gfx-test-util.h" +#include "tools/gfx-util/shader-cursor.h" +#include "source/core/slang-basic.h" +#include "source/core/slang-blob.h" +#include "source/core/slang-memory-file-system.h" + +using namespace gfx; + +namespace gfx_test +{ + static Slang::Result precompileProgram( + gfx::IDevice* device, + ISlangMutableFileSystem* fileSys, + const char* shaderModuleName) + { + Slang::ComPtr slangSession; + SLANG_RETURN_ON_FAIL(device->getSlangSession(slangSession.writeRef())); + slang::SessionDesc sessionDesc = {}; + auto searchPaths = getSlangSearchPaths(); + sessionDesc.searchPathCount = searchPaths.getCount(); + sessionDesc.searchPaths = searchPaths.getBuffer(); + auto globalSession = slangSession->getGlobalSession(); + globalSession->createSession(sessionDesc, slangSession.writeRef()); + + Slang::ComPtr diagnosticsBlob; + slang::IModule* module = slangSession->loadModule(shaderModuleName, diagnosticsBlob.writeRef()); + diagnoseIfNeeded(diagnosticsBlob); + if (!module) + return SLANG_FAIL; + + // Write loaded modules to memory file system. + for (SlangInt i = 0; i < slangSession->getLoadedModuleCount(); i++) + { + auto module = slangSession->getLoadedModule(i); + auto path = module->getFilePath(); + if (path) + { + auto name = module->getName(); + ComPtr outBlob; + module->serialize(outBlob.writeRef()); + fileSys->saveFileBlob((Slang::String(name) + ".slang-module").getBuffer(), outBlob); + } + } + return SLANG_OK; + } + + void precompiledModuleTestImpl(IDevice* device, UnitTestContext* context) + { + Slang::ComPtr transientHeap; + ITransientResourceHeap::Desc transientHeapDesc = {}; + transientHeapDesc.constantBufferSize = 4096; + GFX_CHECK_CALL_ABORT( + device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef())); + + // First, load and compile the slang source. + ComPtr memoryFileSystem = ComPtr(new Slang::MemoryFileSystem()); + + ComPtr shaderProgram; + slang::ProgramLayout* slangReflection; + GFX_CHECK_CALL_ABORT(precompileProgram(device, memoryFileSystem.get(), "precompiled-module")); + + // Next, load the precompiled slang program. + Slang::ComPtr slangSession; + device->getSlangSession(slangSession.writeRef()); + slang::SessionDesc sessionDesc = {}; + sessionDesc.targetCount = 1; + slang::TargetDesc targetDesc = {}; + switch (device->getDeviceInfo().deviceType) + { + case gfx::DeviceType::DirectX12: + targetDesc.format = SLANG_DXIL; + targetDesc.profile = device->getSlangSession()->getGlobalSession()->findProfile("sm_6_1"); + break; + case gfx::DeviceType::Vulkan: + targetDesc.format = SLANG_SPIRV; + targetDesc.profile = device->getSlangSession()->getGlobalSession()->findProfile("GLSL_460"); + break; + } + sessionDesc.targets = &targetDesc; + sessionDesc.fileSystem = memoryFileSystem.get(); + auto globalSession = slangSession->getGlobalSession(); + globalSession->createSession(sessionDesc, slangSession.writeRef()); + GFX_CHECK_CALL_ABORT(loadComputeProgram(device, slangSession, shaderProgram, "precompiled-module", "computeMain", slangReflection)); + + ComputePipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + ComPtr pipelineState; + GFX_CHECK_CALL_ABORT( + device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); + + const int numberCount = 4; + float initialData[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + IBufferResource::Desc bufferDesc = {}; + bufferDesc.sizeInBytes = numberCount * sizeof(float); + bufferDesc.format = gfx::Format::Unknown; + bufferDesc.elementSize = sizeof(float); + bufferDesc.allowedStates = ResourceStateSet( + ResourceState::ShaderResource, + ResourceState::UnorderedAccess, + ResourceState::CopyDestination, + ResourceState::CopySource); + bufferDesc.defaultState = ResourceState::UnorderedAccess; + bufferDesc.memoryType = MemoryType::DeviceLocal; + + ComPtr numbersBuffer; + GFX_CHECK_CALL_ABORT(device->createBufferResource( + bufferDesc, + (void*)initialData, + numbersBuffer.writeRef())); + + ComPtr bufferView; + IResourceView::Desc viewDesc = {}; + viewDesc.type = IResourceView::Type::UnorderedAccess; + viewDesc.format = Format::Unknown; + GFX_CHECK_CALL_ABORT( + device->createBufferView(numbersBuffer, nullptr, viewDesc, bufferView.writeRef())); + + // We have done all the set up work, now it is time to start recording a command buffer for + // GPU execution. + { + ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics }; + auto queue = device->createCommandQueue(queueDesc); + + auto commandBuffer = transientHeap->createCommandBuffer(); + auto encoder = commandBuffer->encodeComputeCommands(); + + auto rootObject = encoder->bindPipeline(pipelineState); + + ShaderCursor entryPointCursor( + rootObject->getEntryPoint(0)); // get a cursor the the first entry-point. + // Bind buffer view to the entry point. + entryPointCursor.getPath("buffer").setResource(bufferView); + + encoder->dispatchCompute(1, 1, 1); + encoder->endEncoding(); + commandBuffer->close(); + queue->executeCommandBuffer(commandBuffer); + queue->waitOnHost(); + } + + compareComputeResult( + device, + numbersBuffer, + Slang::makeArray(3.0f, 3.0f, 3.0f, 3.0f)); + } + + SLANG_UNIT_TEST(precompiledModuleD3D12) + { + runTestImpl(precompiledModuleTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12); + } + + SLANG_UNIT_TEST(precompiledModuleVulkan) + { + runTestImpl(precompiledModuleTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan); + } + +} diff --git a/tools/gfx-unit-test/precompiled-module.slang b/tools/gfx-unit-test/precompiled-module.slang new file mode 100644 index 0000000000..be72314323 --- /dev/null +++ b/tools/gfx-unit-test/precompiled-module.slang @@ -0,0 +1,14 @@ +import "precompiled-module-imported"; + +using namespace ns; + +// Main entry-point. + +[shader("compute")] +[numthreads(4, 1, 1)] +void computeMain( + uint3 sv_dispatchThreadID : SV_DispatchThreadID, + uniform RWStructuredBuffer buffer) +{ + buffer[sv_dispatchThreadID.x] = helperFunc() + helperFunc1(); +} From c639cac500893674240dc3aa6f67ef33b6ae3717 Mon Sep 17 00:00:00 2001 From: tgrimesnv <158093149+tgrimesnv@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:08:46 -0600 Subject: [PATCH 6/7] HLSL texture intrinsic test first draft. (#3583) * HLSL texture intrinsic test first draft. * Some updated to texture-intrinsics.slang * Update dx11 test config in texture-intrinsics.slang * made some edits that shouldn't matter, but commiting once more to be sure * Switch to filecheck-buffer and differing output values per api * Forgot to uncomment one function and updated expected values. * Delete tests/hlsl-intrinsic/texture/texture-intrinsics.slang.expected.txt * Cubemap SampleGrad * Enable Vulkan and have SampleCmpLevelZero ifdefed out for Vulkan --- .../texture/texture-intrinsics.slang | 302 ++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 tests/hlsl-intrinsic/texture/texture-intrinsics.slang diff --git a/tests/hlsl-intrinsic/texture/texture-intrinsics.slang b/tests/hlsl-intrinsic/texture/texture-intrinsics.slang new file mode 100644 index 0000000000..01452ac9d7 --- /dev/null +++ b/tests/hlsl-intrinsic/texture/texture-intrinsics.slang @@ -0,0 +1,302 @@ +//DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -shaderobj -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=DX11):-slang -compute -shaderobj -output-using-type -render-feature hardware-device +//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=DX12):-slang -compute -dx12 -shaderobj -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=DX12CS6):-slang -compute -dx12 -profile cs_6_0 -use-dxil -shaderobj -output-using-type -xslang -DCS60 +//TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=VK):-vk -emit-spirv-directly -compute -shaderobj -output-using-type -render-feature hardware-device -xslang -DVK +//DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-cuda -compute -shaderobj -output-using-type + +//TEST_INPUT: Texture1D(size=4, content = one):name t1D +Texture1D t1D; +//TEST_INPUT: Texture2D(size=4, content = one):name t2D +Texture2D t2D; +//TEST_INPUT: Texture3D(size=4, content = one):name t3D +Texture3D t3D; +//TEST_INPUT: TextureCube(size=4, content = one):name tCube +TextureCube tCube; + +//TEST_INPUT: Texture1D(size=4, content = one, arrayLength=2):name t1DArray +Texture1DArray t1DArray; +//TEST_INPUT: Texture2D(size=4, content = one, arrayLength=2):name t2DArray +Texture2DArray t2DArray; +//TEST_INPUT: TextureCube(size=4, content = one, arrayLength=2):name tCubeArray +TextureCubeArray tCubeArray; + +//TEST_INPUT: Sampler:name samplerState +SamplerState samplerState; +//TEST_INPUT: Sampler:name shadowSampler +SamplerComparisonState shadowSampler; + + +//TEST_INPUT: ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(int3 dispatchThreadID: SV_DispatchThreadID) +{ + int idx = dispatchThreadID.x; + float u = idx * (1.0f / 4); + + float val = 0.0f; + + uint width = 0, height = 0, depth = 0; + float fwidth = 0.0f, fheight = 0.0f, fdepth = 0.0f; + uint numLevels = 0, elements = 0; + float fnumLevels = 0.0f, felements = 0.0f; + + /* +