diff options
26 files changed, 1957 insertions, 332 deletions
diff --git a/.gitignore b/.gitignore index 206dfca5e..09133cdf1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ *.VC.db *.vcxproj.user *.sdf +*.ilk +*.obj bin/ intermediate/ build.*/ diff --git a/slang-com-helper.h b/slang-com-helper.h index 245c800b6..e674ff27f 100644 --- a/slang-com-helper.h +++ b/slang-com-helper.h @@ -70,7 +70,7 @@ For SLANG_IUNKNOWN_QUERY_INTERFACE to work - must have a method 'getInterface' t if not found. */ #define SLANG_IUNKNOWN_QUERY_INTERFACE \ -SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) \ +SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE \ { \ ISlangUnknown* intf = getInterface(uuid); \ if (intf) \ @@ -108,7 +108,7 @@ SLANG_NO_THROW uint32_t SLANG_MCALL release() \ // ------------------------ RefObject IUnknown ----------------------------- #define SLANG_REF_OBJECT_IUNKNOWN_QUERY_INTERFACE \ -SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) \ +SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE \ { \ ISlangUnknown* intf = getInterface(uuid); \ if (intf) \ @@ -120,8 +120,8 @@ SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, voi return SLANG_E_NO_INTERFACE;\ } -#define SLANG_REF_OBJECT_IUNKNOWN_ADD_REF SLANG_NO_THROW uint32_t SLANG_MCALL addRef() { return (uint32_t)addReference(); } -#define SLANG_REF_OBJECT_IUNKNOWN_RELEASE SLANG_NO_THROW uint32_t SLANG_MCALL release() { return (uint32_t)releaseReference(); } +#define SLANG_REF_OBJECT_IUNKNOWN_ADD_REF SLANG_NO_THROW uint32_t SLANG_MCALL addRef() SLANG_OVERRIDE { return (uint32_t)addReference(); } +#define SLANG_REF_OBJECT_IUNKNOWN_RELEASE SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return (uint32_t)releaseReference(); } #define SLANG_REF_OBJECT_IUNKNOWN_ALL \ SLANG_REF_OBJECT_IUNKNOWN_QUERY_INTERFACE \ @@ -298,8 +298,15 @@ convention for interface methods. #ifdef __cplusplus // C++ specific macros +// Clang +#if SLANG_CLANG +# if (__clang_major__*10 + __clang_minor__) >= 33 +# define SLANG_HAS_MOVE_SEMANTICS 1 +# define SLANG_HAS_ENUM_CLASS 1 +# define SLANG_OVERRIDE override +# endif // Gcc -# if SLANG_GCC_FAMILY +#elif SLANG_GCC_FAMILY // Check for C++11 # if (__cplusplus >= 201103L) # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 @@ -1044,8 +1051,7 @@ extern "C" */ typedef struct SlangSession SlangSession; - typedef struct SlangLinkage SlangLinkage; - typedef struct SlangModule SlangModule; + typedef struct SlangProgramLayout SlangProgramLayout; /*! @brief A request for one or more compilation actions to be performed. @@ -1113,20 +1119,6 @@ extern "C" char const* sourcePath, char const* sourceString); - - - SLANG_API SlangLinkage* spCreateLinkage( - SlangSession* session); - - SLANG_API void spDestroyLinkage( - SlangLinkage* linkage); - - SLANG_API SlangModule* spLoadModule( - SlangLinkage* linkage, - char const* moduleName); - - - /*! @brief Create a compile request. */ @@ -1563,11 +1555,17 @@ extern "C" SlangCompileRequest* request, size_t* outSize); - /* Note(tfoley): working on new reflection interface... + /* + Forward declarations of types used in the reflection interface; */ - typedef struct SlangReflection SlangReflection; - typedef struct SlangReflectionEntryPoint SlangReflectionEntryPoint; + typedef struct SlangProgramLayout SlangProgramLayout; + typedef struct SlangEntryPoint SlangEntryPoint; + typedef struct SlangEntryPointLayout SlangEntryPointLayout; + typedef struct SlangEntryPointGroupLayout SlangEntryPointGroupLayout; + +// typedef struct SlangReflection SlangReflection; +// typedef struct SlangReflectionEntryPoint SlangReflectionEntryPoint; typedef struct SlangReflectionModifier SlangReflectionModifier; typedef struct SlangReflectionType SlangReflectionType; typedef struct SlangReflectionTypeLayout SlangReflectionTypeLayout; @@ -1576,6 +1574,12 @@ extern "C" typedef struct SlangReflectionTypeParameter SlangReflectionTypeParameter; typedef struct SlangReflectionUserAttribute SlangReflectionUserAttribute; + /* + Type aliases to maintain backward compatibility. + */ + typedef SlangProgramLayout SlangReflection; + typedef SlangEntryPointLayout SlangReflectionEntryPoint; + // get reflection data from a compilation request SLANG_API SlangReflection* spGetReflection( SlangCompileRequest* request); @@ -1909,6 +1913,9 @@ extern "C" SLANG_API SlangReflectionEntryPoint* spReflection_getEntryPointByIndex(SlangReflection* reflection, SlangUInt index); SLANG_API SlangReflectionEntryPoint* spReflection_findEntryPointByName(SlangReflection* reflection, char const* name); + SLANG_API SlangInt spReflection_getEntryPointGroupCount(SlangReflection* reflection); + SLANG_API SlangEntryPointGroupLayout* spReflection_getEntryPointGroupByIndex(SlangReflection* reflection, SlangInt index); + SLANG_API SlangUInt spReflection_getGlobalConstantBufferBinding(SlangReflection* reflection); SLANG_API size_t spReflection_getGlobalConstantBufferSize(SlangReflection* reflection); @@ -1919,6 +1926,15 @@ extern "C" SlangReflectionType* const* specializationArgs, ISlangBlob** outDiagnostics); + // Entry point group reflection + + SLANG_API SlangInt spEntryPointGroupLayout_getEntryPointCount(SlangEntryPointGroupLayout* group); + SLANG_API SlangReflectionEntryPoint* spEntryPointGroupLayout_getEntryPointByIndex(SlangEntryPointGroupLayout* group, SlangInt index); + SLANG_API SlangReflectionVariableLayout* spEntryPointGroupLayout_getVarLayout(SlangEntryPointGroupLayout* group); + + SLANG_API SlangInt spEntryPointGroupLayout_getParameterCount(SlangEntryPointGroupLayout* group); + SLANG_API SlangReflectionVariableLayout* spEntryPointGroupLayout_getParameterByIndex(SlangEntryPointGroupLayout* group, SlangInt index); + #ifdef __cplusplus } @@ -1926,9 +1942,6 @@ extern "C" /* Helper interfaces for C++ users */ namespace slang { -#define SLANG_SAFE_BOOL(expr) \ - operator bool() const { return expr; } - struct BufferReflection; struct TypeLayoutReflection; struct TypeReflection; @@ -2427,6 +2440,35 @@ namespace slang return 0 != spReflectionEntryPoint_usesAnySampleRateInput((SlangReflectionEntryPoint*) this); } }; + typedef EntryPointReflection EntryPointLayout; + + struct EntryPointGroupLayout + { + SlangInt getEntryPointCount() + { + return spEntryPointGroupLayout_getEntryPointCount((SlangEntryPointGroupLayout*) this); + } + + EntryPointReflection* getEntryPointByIndex(SlangInt index) + { + return (EntryPointReflection*) spEntryPointGroupLayout_getEntryPointByIndex((SlangEntryPointGroupLayout*) this, index); + } + + VariableLayoutReflection* getVarLayout() + { + return (VariableLayoutReflection*) spEntryPointGroupLayout_getVarLayout((SlangEntryPointGroupLayout*) this); + } + + SlangInt getParameterCount() + { + return spEntryPointGroupLayout_getParameterCount((SlangEntryPointGroupLayout*) this); + } + + VariableLayoutReflection* getParameterByIndex(SlangInt index) + { + return (VariableLayoutReflection*) spEntryPointGroupLayout_getParameterByIndex((SlangEntryPointGroupLayout*) this, index); + } + }; struct TypeParameterReflection { @@ -2453,6 +2495,8 @@ namespace slang Default = SLANG_LAYOUT_RULES_DEFAULT, }; + typedef struct ShaderReflection ProgramLayout; + struct ShaderReflection { unsigned getParameterCount() @@ -2480,9 +2524,9 @@ namespace slang return (VariableLayoutReflection*) spReflection_GetParameterByIndex((SlangReflection*) this, index); } - static ShaderReflection* get(SlangCompileRequest* request) + static ProgramLayout* get(SlangCompileRequest* request) { - return (ShaderReflection*) spGetReflection(request); + return (ProgramLayout*) spGetReflection(request); } SlangUInt getEntryPointCount() @@ -2495,6 +2539,16 @@ namespace slang return (EntryPointReflection*) spReflection_getEntryPointByIndex((SlangReflection*) this, index); } + SlangInt getEntryPointGroupCount() + { + return spReflection_getEntryPointGroupCount((SlangReflection*) this); + } + + EntryPointGroupLayout* getEntryPointGroupByIndex(SlangInt index) + { + return (EntryPointGroupLayout*) spReflection_getEntryPointGroupByIndex((SlangReflection*) this, index); + } + SlangUInt getGlobalConstantBufferBinding() { return spReflection_getGlobalConstantBufferBinding((SlangReflection*)this); @@ -2543,8 +2597,311 @@ namespace slang outDiagnostics); } }; + + typedef ISlangBlob IBlob; + + struct IGlobalSession; + struct IModule; + struct IProgram; + struct ISession; + + struct SessionDesc; + struct ProgramDesc; + struct SpecializationArg; + struct TargetDesc; + + /** A global session for interaction with the Slang library. + + An application may create and re-use a single global session across + multiple sessions, in order to amortize startups costs (in current + Slang this is mostly the cost of loading the Slang standard library). + + The global session is currently *not* thread-safe and objects created from + a single global session should only be used from a single thread at + a time. + */ + struct IGlobalSession : public ISlangUnknown + { + public: + /** Create a new session for loading and compiling code. + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL createSession( + SessionDesc const& desc, + ISession** outSession) = 0; + + /** Look up the internal ID of a profile by its `name`. + + Profile IDs are *not* guaranteed to be stable across versions + of the Slang library, so clients are expected to look up + profiles by name at runtime. + */ + virtual SLANG_NO_THROW SlangProfileID SLANG_MCALL findProfile( + char const* name) = 0; + }; + + #define SLANG_UUID_IGlobalSession { 0xc140b5fd, 0xc78, 0x452e, { 0xba, 0x7c, 0x1a, 0x1e, 0x70, 0xc7, 0xf7, 0x1c } }; + + /** Description of a code generation target. + */ + struct TargetDesc + { + /** The target format to generate code for (e.g., SPIR-V, DXIL, etc.) + */ + SlangCompileTarget format = SLANG_TARGET_UNKNOWN; + + /** The compilation profile supported by the target (e.g., "Shader Model 5.1") + */ + SlangProfileID profile = SLANG_PROFILE_UNKNOWN; + + /** Flags for the code generation target. Currently unused. */ + SlangTargetFlags flags = 0; + + /** Default mode to use for floating-point operations on the target. + */ + SlangFloatingPointMode floatingPointMode = SLANG_FLOATING_POINT_MODE_DEFAULT; + + /** Optimization level to use for the target. + */ + SlangOptimizationLevel optimizationLevel = SLANG_OPTIMIZATION_LEVEL_DEFAULT; + }; + + typedef uint32_t SessionFlags; + enum + { + kSessionFlags_None = 0, + + /** Use application-specific policy for semantics of the `shared` keyword. + + This is a legacy/compatibility flag to help an existing Slang client + migrate to new language features, and should *not* be used by other + clients. This feature may be removed in a future release without a + deprecation warning, and this bit may be re-used for another feature. + You have been warned. + */ + kSessionFlag_FalcorCustomSharedKeywordSemantics = 1 << 0, + }; + + struct PreprocessorMacroDesc + { + const char* name; + const char* value; + }; + + struct SessionDesc + { + /** Code generation targets to include in the session. + */ + TargetDesc const* targets = nullptr; + SlangInt targetCount = 0; + + /** Flags to configure the session. + */ + SessionFlags flags = kSessionFlags_None; + + /** Default layout to assume for variables with matrix types. + */ + SlangMatrixLayoutMode defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_ROW_MAJOR; + + /** Paths to use when searching for `#include`d or `import`ed files. + */ + char const* const* searchPaths = nullptr; + SlangInt searchPathCount = 0; + + PreprocessorMacroDesc const* preprocessorMacros = nullptr; + SlangInt preprocessorMacroCount = 0; + + }; + + /** A session provides a scope for code that is loaded. + + A session can be used to load modules of Slang source code, + and to request target-specific compiled binaries and layout + information. + + In order to be able to load code, the session owns a set + of active "search paths" for resolving `#include` directives + and `import` declrations, as well as a set of global + preprocessor definitions that will be used for all code + that gets `import`ed in the session. + + If multiple user shaders are loaded in the same session, + and import the same module (e.g., two source files do `import X`) + then there will only be one copy of `X` loaded within the session. + + In order to be able to generate target code, the session + owns a list of available compilation targets, which specify + code generation options. + + Code loaded and compiled within a session is owned by the session + and will remain resident in memory until the session is released. + Applications wishing to control the memory usage for compiled + and loaded code should use multiple sessions. + */ + struct ISession : public ISlangUnknown + { + public: + /** Get the global session thas was used to create this session. + */ + virtual SLANG_NO_THROW IGlobalSession* SLANG_MCALL getGlobalSession() = 0; + + /** Load a module as it would be by code using `import`. + */ + virtual SLANG_NO_THROW IModule* SLANG_MCALL loadModule( + const char* moduleName, + IBlob** outDiagnostics = nullptr) = 0; + + /** Create a program out of existing compiled items. + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL createProgram( + ProgramDesc const& desc, + IProgram** outProgram) = 0; + + /** Specialize a program based on type arguments. + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL specializeProgram( + IProgram* program, + SlangInt specializationArgCount, + SpecializationArg const* specializationArgs, + IProgram** outSpecializedProgram, + ISlangBlob** outDiagnostics = nullptr) = 0; + + /** Specialize a type based on type arguments. + */ + virtual SLANG_NO_THROW TypeReflection* SLANG_MCALL specializeType( + TypeReflection* type, + SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + ISlangBlob** outDiagnostics = nullptr) = 0; + + + /** Get the layout `type` on the chosen `target`. + */ + virtual SLANG_NO_THROW TypeLayoutReflection* SLANG_MCALL getTypeLayout( + TypeReflection* type, + SlangInt targetIndex = 0, + LayoutRules rules = LayoutRules::Default, + ISlangBlob** outDiagnostics = nullptr) = 0; + + /** Create a request to load/compile front-end code. + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest( + SlangCompileRequest** outCompileRequest) = 0; + }; + + #define SLANG_UUID_ISession { 0x67618701, 0xd116, 0x468f, { 0xab, 0x3b, 0x47, 0x4b, 0xed, 0xce, 0xe, 0x3d } } + + + /** A module is the granularity of shader code compilation and loading. + + In most cases a module corresponds to a single compile "translation unit." + This will often be a single `.slang` or `.hlsl` file and everything it + `#include`s. + + Notably, a module `M` does *not* include the things it `import`s, as these + as distinct modules that `M` depends on. There is a directed graph of + module dependencies, and all modules in the graph must belong to the + same session (`ISession`). + */ + struct IModule : public ISlangUnknown + { + public: + }; + + #define SLANG_UUID_IModule { 0xc720e64, 0x8722, 0x4d31, { 0x89, 0x90, 0x63, 0x8a, 0x98, 0xb1, 0xc2, 0x79 } } + + + /** Argument used for specialization to types/values. + */ + struct SpecializationArg + { + enum class Kind : int32_t + { + Unknown, + Type, + }; + Kind kind; + union + { + TypeReflection* type; + }; + }; + + /** Description of a program to be created. + */ + struct ProgramDesc + { + struct Item + { + enum class Kind : int32_t + { + Program, + Module, + }; + Kind kind; + union + { + IProgram* program; + IModule* module; + }; + }; + + Item const* items; + SlangInt itemCount; + }; + + + /** A program comprises zero or more modules, entry points, etc. that have been linked together. + */ + struct IProgram : public ISlangUnknown + { + public: + /** Get the runtime session that this program belongs to. + */ + virtual SLANG_NO_THROW ISession* SLANG_MCALL getSession() = 0; + + /** Get the layout for this program for the chosen `targetIndex` + */ + virtual SLANG_NO_THROW ProgramLayout* SLANG_MCALL getLayout( + SlangInt targetIndex = 0, + IBlob** outDiagnostics = nullptr) = 0; + + /** Get the compiled code for the entry point at `entryPointIndex` for the chosen `targetIndex` + + If code has not already been generated for the given entry point and target, + then a compilation error may be detected, in which case `outDiagnostics` + (if non-null) will be filled in with a blob of messages diagnosing the error. + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + IBlob** outCode, + IBlob** outDiagnostics = nullptr) = 0; + }; + + #define SLANG_UUID_IProgram { 0x5bc42be8, 0x5c50, 0x4929, { 0x9e, 0x5e, 0xd1, 0x5e, 0x7c, 0x24, 0x1, 0x5f } }; +} + +#define SLANG_API_VERSION 0 + +SLANG_API SlangResult slang_createGlobalSession( + SlangInt apiVersion, + slang::IGlobalSession** outGlobalSession); + +namespace slang +{ + inline SlangResult createGlobalSession( + slang::IGlobalSession** outGlobalSession) + { + return slang_createGlobalSession(SLANG_API_VERSION, outGlobalSession); + } } +/** +*/ +SLANG_API SlangResult spCompileRequest_getProgram( + SlangCompileRequest* request, + slang::IProgram** outProgram); + #endif /* DEPRECATED DEFINITIONS diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index 177cd2011..dd2f3c144 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -231,7 +231,9 @@ <ClCompile Include="windows\slang-win-visual-studio-util.cpp" /> </ItemGroup> <ItemGroup> - <None Include="core.natvis" /> + <Natvis Include="core.natvis"> + <FileType>Document</FileType> + </Natvis> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/source/core/slang-writer.h b/source/core/slang-writer.h index 6e26d6750..cd5737b3d 100644 --- a/source/core/slang-writer.h +++ b/source/core/slang-writer.h @@ -45,7 +45,7 @@ public: // ISlangUnknown SLANG_REF_OBJECT_IUNKNOWN_QUERY_INTERFACE SLANG_REF_OBJECT_IUNKNOWN_ADD_REF - SLANG_NO_THROW uint32_t SLANG_MCALL release() { return (m_flags & WriterFlag::IsStatic) ? (uint32_t)decreaseReference() : (uint32_t)releaseReference(); } + SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return (m_flags & WriterFlag::IsStatic) ? (uint32_t)decreaseReference() : (uint32_t)releaseReference(); } // ISlangWriter - default impl SLANG_NO_THROW virtual void SLANG_MCALL flush() SLANG_OVERRIDE {} diff --git a/source/slang/slang-check.cpp b/source/slang/slang-check.cpp index 90947cf54..83b75b964 100644 --- a/source/slang/slang-check.cpp +++ b/source/slang/slang-check.cpp @@ -433,7 +433,7 @@ namespace Slang Session* getSession() { - return m_linkage->getSession(); + return m_linkage->getSessionImpl(); } public: @@ -9656,6 +9656,111 @@ namespace Slang } } +static bool shouldUseFalcorCustomSharedKeywordSemantics( + EntryPointGroup* entryPointGroup) +{ + if( !entryPointGroup->getLinkageImpl()->m_useFalcorCustomSharedKeywordSemantics ) + return false; + + // As a sanity check, if we are being asked to lay out an + // empty entry-point group, then don't apply the convention. + // + if(entryPointGroup->getEntryPointCount() == 0) + return false; + + // Otherwise we will look at the first entry point in the group, + // and use that to determine whether it looks like we are compiling + // ray-tracing shaders or not. + // + switch( entryPointGroup->getEntryPoint(0)->getStage() ) + { + case Stage::AnyHit: + case Stage::Callable: + case Stage::ClosestHit: + case Stage::Intersection: + case Stage::Miss: + case Stage::RayGeneration: + return true; + + default: + return false; + } +} + + +static bool shouldUseFalcorCustomSharedKeywordSemantics( + Program* program) +{ + if( !program->getLinkageImpl()->m_useFalcorCustomSharedKeywordSemantics ) + return false; + + // As a sanity check, if we are being asked to lay out a program + // with *no* entry points, then we don't apply the convention. + // + if(program->getEntryPointGroupCount() == 0) + return false; + + // Otherwise we let the first entry-point group determine if we should + // apply the policy for the entire program. + // + // Note: this could lead to confusing results if a `Program` mixes + // entry point groups for RT and non-RT pipelines, but that isn't + // a scenario we expect to come up and this whole routine is handling + // "do what I mean" semantics for legacy behavior. + // + return shouldUseFalcorCustomSharedKeywordSemantics(program->getEntryPointGroup(0)); +} + + +void EntryPointGroup::_collectShaderParams(DiagnosticSink* sink) +{ + // If and only if we are in the special mode for Falcor support + // where non-`shared` global shader parameters are actually + // supposed to go into the "local root signature," then we + // will consider such parameters as if they were entry-point + // parameters, attached to the group. + // + if( shouldUseFalcorCustomSharedKeywordSemantics(this) ) + { + for( auto module : getModuleDependencies() ) + { + auto moduleDecl = module->getModuleDecl(); + for( auto globalVar : moduleDecl->getMembersOfType<VarDecl>() ) + { + // Don't consider globals that aren't shader parameters. + // + if(!isGlobalShaderParameter(globalVar)) + continue; + + // Don't consider global shader paramters that were marked + // `shared`, since that is how global-root-signature parameters + // are being specified. + // + if( globalVar->HasModifier<HLSLEffectSharedModifier>() ) + continue; + + auto paramDeclRef = makeDeclRef(globalVar.Ptr()); + + ShaderParamInfo shaderParamInfo; + shaderParamInfo.paramDeclRef = paramDeclRef; + + ExistentialTypeSlots slots; + _collectExistentialSlotsForShaderParam( + shaderParamInfo, + slots, + paramDeclRef); + + if( slots.paramTypes.getCount() != 0 ) + { + sink->diagnose(globalVar, Diagnostics::typeParametersNotAllowedOnEntryPointGlobal, globalVar); + } + + m_shaderParams.add(shaderParamInfo); + } + } + } +} + // Validate that an entry point function conforms to any additional // constraints based on the stage (and profile?) it specifies. void validateEntryPoint( @@ -10334,9 +10439,6 @@ static bool doesParameterMatch( return true; } - - - /// Enumerate the existential-type parameters of a `Program`. /// /// Any parameters found will be added to the list of existential slots on `this`. @@ -10364,9 +10466,34 @@ static bool doesParameterMatch( auto moduleDecl = module->getModuleDecl(); for( auto globalVar : moduleDecl->getMembersOfType<VarDecl>() ) { + // We do not want to consider global variable declarations + // that don't represents shader parameters. This includes + // things like `static` globals and `groupshared` variables. + // if(!isGlobalShaderParameter(globalVar)) continue; + // HACK: In order to support existing policy in the Falcor + // application, we support a custom mode where only + // global variables marked as `shared` should be considered + // as global shader parameters (that go in the "global + // root signature" for DXR). + // + // TODO: Eliminate this special case once all of the client + // application code has been ported to use more general-purpose + // Slang mechanisms (e.g., entry-point `uniform` parameters). + // + if( shouldUseFalcorCustomSharedKeywordSemantics(this) ) + { + if( !globalVar->HasModifier<HLSLEffectSharedModifier>() ) + { + // Skip a non-`shared` global for purposes of enumerating + // shader parameters. + // + continue; + } + } + // This declaration may represent the same logical parameter // as a declaration that came from a different translation unit. // If that is the case, we want to re-use the same `ShaderParamInfo` @@ -10474,7 +10601,13 @@ static bool doesParameterMatch( entryPointReq); if( entryPoint ) { - program->addEntryPoint(entryPoint); + // TODO: We need to implement an explicit policy + // for what should happen if the user specified + // entry points via the command-line (or API), + // but didn't specify any groups (since the current + // compilation API doesn't allow for grouping). + // + program->addEntryPoint(entryPoint, sink); entryPointReq->getTranslationUnit()->entryPoints.add(entryPoint); } } @@ -10540,7 +10673,17 @@ static bool doesParameterMatch( validateEntryPoint(entryPoint, sink); - program->addEntryPoint(entryPoint); + // 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. + // + program->addEntryPoint(entryPoint, sink); translationUnit->entryPoints.add(entryPoint); } } @@ -10636,7 +10779,7 @@ static bool doesParameterMatch( // generic application like `F<A,B,C>` if it were // encountered in the source code. - auto session = linkage->getSession(); + auto session = linkage->getSessionImpl(); auto genericDeclRef = makeDeclRef(genericDecl); // The first pieces is a `VarExpr` that refers to `genericDecl`. @@ -10760,7 +10903,7 @@ static bool doesParameterMatch( List<RefPtr<Expr>> const& args, DiagnosticSink* sink) { - Slang::_specializeExistentialTypeParams(getLinkage(), m_globalExistentialSlots, args, sink); + Slang::_specializeExistentialTypeParams(getLinkageImpl(), m_globalExistentialSlots, args, sink); } Type* Linkage::specializeType( @@ -10799,14 +10942,34 @@ static bool doesParameterMatch( return specializedType; } - /// Specialize a program to global generic arguments - RefPtr<Program> createSpecializedProgram( + // Shared implementation logic for the `_createSpecializedProgram*` entry points. + static RefPtr<Program> _createSpecializedProgramImpl( Linkage* linkage, Program* unspecializedProgram, List<RefPtr<Expr>> const& globalGenericArgs, List<RefPtr<Expr>> const& globalExistentialArgs, DiagnosticSink* sink) { + // TODO: If there are no specialization arguments, + // then the the result of specialization should + // be the same as the input... *but* we are promising + // to return a program without any entry points, + // and that screws things up here. + // + // For now we just carefully avod this early-exit + // if we have any entry points. + // + // Eventually we should try to revise the model so + // that specialization of a program that includes + // entry points can make some kind of sense. + // + if( globalGenericArgs.getCount() == 0 + && globalExistentialArgs.getCount() == 0 + && unspecializedProgram->getEntryPointCount() == 0) + { + return unspecializedProgram; + } + // The given `unspecializedProgram` should be one that // was checked through the front-end, so that now we // only need to check if the given arguments can satisfy @@ -10967,6 +11130,37 @@ static bool doesParameterMatch( specializedProgram->setGlobalGenericSubsitution(globalGenericSubsts); + return specializedProgram; + } + + /// Create a specialized copy of `unspecializedProgram`. + /// + /// The specialized program will include the entry points + /// from the original program (whether thsoe entry points + /// are specialized or not). + /// + static RefPtr<Program> _createSpecializedProgram( + Linkage* linkage, + Program* unspecializedProgram, + List<RefPtr<Expr>> const& globalGenericArgs, + List<RefPtr<Expr>> const& globalExistentialArgs, + DiagnosticSink* sink) + { + auto specializedProgram = _createSpecializedProgramImpl( + linkage, + unspecializedProgram, + globalGenericArgs, + globalExistentialArgs, + sink); + + // We need to ensure that the specialized program has whatever + // entry points (and groups) the unspecialized one had. + // + for( auto entryPointGroup : unspecializedProgram->getEntryPointGroups() ) + { + specializedProgram->addEntryPointGroup(entryPointGroup); + } + // Now deal with the shader parameters and existential arguments // // Note: We should in theory be able to just copy over the shader @@ -10981,6 +11175,61 @@ static bool doesParameterMatch( return specializedProgram; } + SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::specializeProgram( + slang::IProgram* inUnspecializedProgram, + SlangInt specializationArgCount, + slang::SpecializationArg const* specializationArgs, + slang::IProgram** outSpecializedProgram, + ISlangBlob** outDiagnostics) + { + auto unspecializedProgram = asInternal(inUnspecializedProgram); + + if( specializationArgCount == 0 ) + { + *outSpecializedProgram = ComPtr<slang::IProgram>(asExternal(unspecializedProgram)).detach(); + return SLANG_OK; + } + + List<RefPtr<Expr>> globalGenericArgs; + + List<RefPtr<Expr>> globalExistentialArgs; + for( Int ii = 0; ii < specializationArgCount; ++ii ) + { + auto& specializationArg = specializationArgs[ii]; + switch( specializationArg.kind ) + { + case slang::SpecializationArg::Kind::Type: + { + auto typeArg = asInternal(specializationArg.type); + RefPtr<SharedTypeExpr> argExpr = new SharedTypeExpr(); + argExpr->base = TypeExp(typeArg); + argExpr->type = QualType(getTypeType(typeArg)); + + globalExistentialArgs.add(argExpr); + } + break; + + default: + return SLANG_E_INVALID_ARG; + } + } + + DiagnosticSink sink(getSourceManager()); + auto specializedProgram = _createSpecializedProgram( + this, + unspecializedProgram, + globalGenericArgs, + globalExistentialArgs, + &sink); + sink.getBlobIfNeeded(outDiagnostics); + + if(!specializedProgram) + return SLANG_FAIL; + + *outSpecializedProgram = ComPtr<slang::IProgram>(asExternal(specializedProgram)).detach(); + return SLANG_OK; + } + /// Specialize an entry point that was checked by the front-end, based on generic arguments. /// /// If the end-to-end compile request included generic argument strings @@ -11036,6 +11285,7 @@ static bool doesParameterMatch( // global or entry-point generic parameters. // auto unspecializedProgram = endToEndReq->getUnspecializedProgram(); + auto linkage = endToEndReq->getLinkage(); // First, let's parse the generic argument strings that were // provided via the API, so taht we can match them @@ -11058,12 +11308,13 @@ static bool doesParameterMatch( // applying the global generic arguments (if any) to the // unspecialized program. // - auto specializedProgram = createSpecializedProgram( + auto sink = endToEndReq->getSink(); + auto specializedProgram = _createSpecializedProgramImpl( endToEndReq->getLinkage(), unspecializedProgram, globalGenericArgs, globalExistentialArgs, - endToEndReq->getSink()); + sink); // If anything went wrong with the global generic // arguments, then bail out now. @@ -11090,15 +11341,30 @@ static bool doesParameterMatch( endToEndReq->entryPoints.setCount(entryPointCount); } - for( Index ii = 0; ii < entryPointCount; ++ii ) + Index entryPointCounter = 0; + + for( auto unspecializedEntryPointGroup : unspecializedProgram->getEntryPointGroups() ) { - auto unspecializedEntryPoint = unspecializedProgram->getEntryPoint(ii); - auto& entryPointInfo = endToEndReq->entryPoints[ii]; + List<RefPtr<EntryPoint>> specializedEntryPoints; + for( auto unspecializedEntryPoint : unspecializedEntryPointGroup->getEntryPoints() ) + { + Index entryPointIndex = entryPointCounter++; + auto& entryPointInfo = endToEndReq->entryPoints[entryPointIndex]; + + auto specializedEntryPoint = createSpecializedEntryPoint(endToEndReq, unspecializedEntryPoint, entryPointInfo); + specializedEntryPoints.add(specializedEntryPoint); + } - auto specializedEntryPoint = createSpecializedEntryPoint(endToEndReq, unspecializedEntryPoint, entryPointInfo); - specializedProgram->addEntryPoint(specializedEntryPoint); + RefPtr<EntryPointGroup> specializedEntryPointGroup = EntryPointGroup::create(linkage, specializedEntryPoints, endToEndReq->getSink()); + specializedProgram->addEntryPointGroup(specializedEntryPointGroup); } + // Finalize the information for the specialized program, + // now that we have computed its entry point list, etc. + // + specializedProgram->_collectShaderParams(sink); + specializedProgram->_specializeExistentialTypeParams(globalExistentialArgs, sink); + return specializedProgram; } diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 99caed8f0..ff2facc26 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -293,6 +293,31 @@ namespace Slang } // + // EntryPointGroup + // + + RefPtr<EntryPointGroup> EntryPointGroup::create( + Linkage* linkage, + List<RefPtr<EntryPoint>> const& entryPoints, + DiagnosticSink* sink) + { + RefPtr<EntryPointGroup> group = new EntryPointGroup(linkage); + + for( auto entryPoint : entryPoints ) + { + for( auto module : entryPoint->getModuleDependencies() ) + { + group->m_dependencyList.addDependency(module); + } + group->m_entryPoints.add(entryPoint); + } + + group->_collectShaderParams(sink); + + return group; + } + + // Profile Profile::LookUp(char const* name) { @@ -1481,6 +1506,58 @@ SlangResult dissassembleDXILUsingDXC( writeEntryPointResultToStandardOutput(compileRequest, entryPoint, targetReq, result); } + CompileResult& TargetProgram::_createEntryPointResult( + Int entryPointIndex, + BackEndCompileRequest* backEndRequest, + EndToEndCompileRequest* endToEndRequest) + { + // It is possible that entry points goot added to the `Program` + // *after* we created this `TargetProgram`, so there might be + // a request for an entry point that we didn't allocate space for. + // + // TODO: Change the construction logic so that a `Program` is + // constructed all at once rather than incrementally, to avoid + // this problem. + // + if(entryPointIndex >= m_entryPointResults.getCount()) + m_entryPointResults.setCount(entryPointIndex+1); + + auto entryPoint = m_program->getEntryPoint(entryPointIndex); + + auto& result = m_entryPointResults[entryPointIndex]; + result = emitEntryPoint( + backEndRequest, + entryPoint, + entryPointIndex, + m_targetReq, + endToEndRequest); + + return result; + + } + + CompileResult& TargetProgram::getOrCreateEntryPointResult( + Int entryPointIndex, + DiagnosticSink* sink) + { + if(entryPointIndex >= m_entryPointResults.getCount()) + m_entryPointResults.setCount(entryPointIndex+1); + + auto& result = m_entryPointResults[entryPointIndex]; + if( result.format != ResultFormat::None ) + return result; + + RefPtr<BackEndCompileRequest> backEndRequest = new BackEndCompileRequest( + m_program->getLinkageImpl(), + sink, + m_program); + + return _createEntryPointResult( + entryPointIndex, + backEndRequest, + nullptr); + } + void generateOutputForTarget( BackEndCompileRequest* compileReq, TargetRequest* targetReq, @@ -1494,17 +1571,15 @@ SlangResult dissassembleDXILUsingDXC( auto entryPointCount = program->getEntryPointCount(); for(Index ii = 0; ii < entryPointCount; ++ii) { - auto entryPoint = program->getEntryPoint(ii); - CompileResult entryPointResult = emitEntryPoint( - compileReq, - entryPoint, + targetProgram->_createEntryPointResult( ii, - targetReq, + compileReq, endToEndReq); - targetProgram->setEntryPointResult(ii, entryPointResult); } } + + static void _generateOutput( BackEndCompileRequest* compileRequest, EndToEndCompileRequest* endToEndReq) diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 86c6272a9..2b2c35845 100644 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -366,6 +366,55 @@ namespace Slang ModuleDependencyList m_dependencyList; }; + class EntryPointGroup : public RefObject + { + public: + static RefPtr<EntryPointGroup> create( + Linkage* linkage, + List<RefPtr<EntryPoint>> const& entryPoints, + DiagnosticSink* sink); + + Linkage* getLinkageImpl() { return m_linkage; } + + /// Get the number of entry points in the group + Index getEntryPointCount() { return m_entryPoints.getCount(); } + + /// Get the entry point at the given `index`. + RefPtr<EntryPoint> getEntryPoint(Index index) { return m_entryPoints[index]; } + + /// Get the full ist of entry points in the group. + List<RefPtr<EntryPoint>> const& getEntryPoints() { return m_entryPoints; } + + /// Get a list of modules that this entry point group depends on. + /// + /// This will include the dependencies of all of the entry points in the group. + /// + List<RefPtr<Module>> getModuleDependencies() { return m_dependencyList.getModuleList(); } + + /// Get an array of all entry-point-group shader parameters. + List<ShaderParamInfo> const& getShaderParams() { return m_shaderParams; } + + private: + EntryPointGroup(Linkage* linkage) + : m_linkage(linkage) + {} + + void _collectShaderParams(DiagnosticSink* sink); + + Linkage* m_linkage; + List<RefPtr<EntryPoint>> m_entryPoints; + + /// Information about shader parameters to be associated with the entry-point group itself. + /// + /// This list captures parameters that logically belong to the group itself, rather than + /// to any specific entry point in the group. + /// + List<ShaderParamInfo> m_shaderParams; + + /// Modules the entry point group depends on. + ModuleDependencyList m_dependencyList; + }; + enum class PassThroughMode : SlangPassThrough { None = SLANG_PASS_THROUGH_NONE, // don't pass through: use Slang compiler @@ -382,9 +431,13 @@ namespace Slang /// may span multiple Slang source files), and provides access /// to both the AST and IR representations of that code. /// - class Module : public RefObject + class Module : public RefObject, public slang::IModule { public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + + ISlangUnknown* getInterface(const Guid& guid); + /// Create a module (initially empty). Module(Linkage* linkage); @@ -525,6 +578,8 @@ namespace Slang Dictionary<Type*, RefPtr<TypeLayout>> typeLayouts; Dictionary<Type*, RefPtr<TypeLayout>>& getTypeLayouts() { return typeLayouts; } + + TypeLayout* getTypeLayout(Type* type); }; /// Are we generating code for a D3D API? @@ -576,14 +631,54 @@ namespace Slang ComPtr<ISlangBlob> createRawBlob(void const* data, size_t size); /// A context for loading and re-using code modules. - class Linkage : public RefObject + class Linkage : public RefObject, public slang::ISession { public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + + ISlangUnknown* getInterface(const Guid& guid); + + SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL getGlobalSession() override; + SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModule( + const char* moduleName, + slang::IBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW SlangResult SLANG_MCALL createProgram( + slang::ProgramDesc const& desc, + slang::IProgram** outProgram) override; + SLANG_NO_THROW SlangResult SLANG_MCALL specializeProgram( + slang::IProgram* program, + SlangInt specializationArgCount, + slang::SpecializationArg const* specializationArgs, + slang::IProgram** outSpecializedProgram, + ISlangBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL specializeType( + slang::TypeReflection* type, + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + ISlangBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getTypeLayout( + slang::TypeReflection* type, + SlangInt targetIndex = 0, + slang::LayoutRules rules = slang::LayoutRules::Default, + ISlangBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest( + SlangCompileRequest** outCompileRequest) override; + + void addTarget( + slang::TargetDesc const& desc); + SlangResult addSearchPath( + char const* path); + SlangResult addPreprocessorDefine( + char const* name, + char const* value); + SlangResult setMatrixLayoutMode( + SlangMatrixLayoutMode mode); + /// Create an initially-empty linkage Linkage(Session* session); /// Get the parent session for this linkage - Session* getSession() { return m_session; } + Session* getSessionImpl() { return m_session; } // Information on the targets we are being asked to // generate code for. @@ -703,6 +798,8 @@ namespace Slang OptimizationLevel optimizationLevel = OptimizationLevel::Default; + bool m_useFalcorCustomSharedKeywordSemantics = false; + private: Session* m_session = nullptr; @@ -887,9 +984,25 @@ namespace Slang /// to be used togehter so that, e.g., layout can make sure to allocate /// space for the global shader parameters in all referenced modules. /// - class Program : public RefObject + class Program : public RefObject, public slang::IProgram { public: + SLANG_REF_OBJECT_IUNKNOWN_ALL; + ISlangUnknown* getInterface(Guid const& guid); + + SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; + + SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout( + SlangInt targetIndex, + slang::IBlob** outDiagnostics) override; + + SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) override; + + /// Create a new program, initially empty. /// /// All code loaded into the program must come @@ -898,7 +1011,7 @@ namespace Slang Linkage* linkage); /// Get the linkage that this program uses. - Linkage* getLinkage() { return m_linkage; } + Linkage* getLinkageImpl() { return m_linkage; } /// Get the number of entry points added to the program Index getEntryPointCount() { return m_entryPoints.getCount(); } @@ -909,6 +1022,12 @@ namespace Slang /// Get the full ist of entry points on the program. List<RefPtr<EntryPoint>> const& getEntryPoints() { return m_entryPoints; } + + Index getEntryPointGroupCount() { return m_entryPointGroups.getCount(); } + RefPtr<EntryPointGroup> getEntryPointGroup(Index index) { return m_entryPointGroups[index]; } + List<RefPtr<EntryPointGroup>> const& getEntryPointGroups() { return m_entryPointGroups; } + + /// Get the substitution (if any) that represents how global generics are specialized. RefPtr<Substitutions> getGlobalGenericSubstitution() { return m_globalGenericSubst; } @@ -936,7 +1055,13 @@ namespace Slang /// /// This also adds everything the entry point depends on to the list of references. /// - void addEntryPoint(EntryPoint* entryPoint); + void addEntryPoint(EntryPoint* entryPoint, DiagnosticSink* sink); + + /// Add an entry point group to the program + /// + /// This also adds everything the entry point group depends on to the list of references. + /// + void addEntryPointGroup(EntryPointGroup* entryPointGroup); /// Set the global generic argument substitution to use. void setGlobalGenericSubsitution(RefPtr<Substitutions> subst) @@ -1011,6 +1136,9 @@ namespace Slang // Entry points that are part of the program. List<RefPtr<EntryPoint> > m_entryPoints; + // Entry points that are part of the program. + List<RefPtr<EntryPointGroup> > m_entryPointGroups; + // Specializations for global generic parameters (if any) RefPtr<Substitutions> m_globalGenericSubst; @@ -1065,23 +1193,34 @@ namespace Slang /// Get the compiled code for an entry point on the target. /// - /// This routine assumes code generation has already been - /// performed and called `setEntryPointResult`. + /// If this is the first time that code generation has + /// been requested, report any errors that arise during + /// code generation to the given `sink`. + /// + CompileResult& getOrCreateEntryPointResult(Int entryPointIndex, DiagnosticSink* sink); + + /// Get the compiled code for an entry point on the target. + /// + /// This routine assumes that `getOrCreateEntryPointResult` + /// has already been called previously. /// CompileResult& getExistingEntryPointResult(Int entryPointIndex) { return m_entryPointResults[entryPointIndex]; } - // TODO: Need a lazy `getOrCreateEntryPointResult` - /// Set the compiled code for an entry point. + /// Internal helper for `getOrCreateEntryPointResult`. /// - /// Should only be called by code generation. - void setEntryPointResult(Int entryPointIndex, CompileResult const& result) - { - m_entryPointResults[entryPointIndex] = result; - } + /// This is used so that command-line and API-based + /// requests for code can bottleneck through the same place. + /// + /// Shouldn't be called directly by most code. + /// + CompileResult& _createEntryPointResult( + Int entryPointIndex, + BackEndCompileRequest* backEndRequest, + EndToEndCompileRequest* endToEndRequest); private: // The program being compiled or laid out @@ -1143,6 +1282,9 @@ namespace Slang EndToEndCompileRequest( Session* session); + EndToEndCompileRequest( + Linkage* linkage); + // What container format are we being asked to generate? // // Note: This field is unused except by the options-parsing @@ -1224,6 +1366,8 @@ namespace Slang Program* getSpecializedProgram() { return m_specializedProgram; } private: + void init(); + Session* m_session = nullptr; RefPtr<Linkage> m_linkage; DiagnosticSink m_sink; @@ -1277,9 +1421,24 @@ namespace Slang struct TypeCheckingCache; // - class Session + class Session : public RefObject, public slang::IGlobalSession { public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + + ISlangUnknown* getInterface(const Guid& guid); + + /** Create a new linkage. + */ + SLANG_NO_THROW SlangResult SLANG_MCALL createSession( + slang::SessionDesc const& desc, + slang::ISession** outSession) override; + + SLANG_NO_THROW SlangProfileID SLANG_MCALL findProfile( + char const* name) override; + + + enum class SharedLibraryFuncType { Glslang_Compile, @@ -1421,6 +1580,76 @@ namespace Slang RefPtr<Linkage> m_builtinLinkage; }; + +// +// The following functions are utilties to convert between +// matching "external" (public API) and "internal" (implementation) +// types. They are favored over explicit casts because they +// help avoid making incorrect conversions (e.g., when using +// `reinterpret_cast` or C-style casts), and because they +// abstract over the conversion required for each pair of types. +// + +inline slang::IGlobalSession* asExternal(Session* session) +{ + return static_cast<slang::IGlobalSession*>(session); +} + +inline slang::ISession* asExternal(Linkage* linkage) +{ + return static_cast<slang::ISession*>(linkage); +} + +inline Module* asInternal(slang::IModule* module) +{ + return static_cast<Module*>(module); +} + +inline slang::IModule* asExternal(Module* module) +{ + return static_cast<slang::IModule*>(module); +} + +inline Program* asInternal(slang::IProgram* module) +{ + return static_cast<Program*>(module); +} + +inline slang::IProgram* asExternal(Program* module) +{ + return static_cast<slang::IProgram*>(module); +} + +static inline slang::ProgramLayout* asExternal(ProgramLayout* programLayout) +{ + return (slang::ProgramLayout*) programLayout; +} + +inline Type* asInternal(slang::TypeReflection* type) +{ + return reinterpret_cast<Type*>(type); +} + +inline slang::TypeReflection* asExternal(Type* type) +{ + return reinterpret_cast<slang::TypeReflection*>(type); +} + +inline TypeLayout* asInternal(slang::TypeLayoutReflection* type) +{ + return reinterpret_cast<TypeLayout*>(type); +} + +inline slang::TypeLayoutReflection* asExternal(TypeLayout* type) +{ + return reinterpret_cast<slang::TypeLayoutReflection*>(type); +} + +inline SlangCompileRequest* asExternal(EndToEndCompileRequest* request) +{ + return reinterpret_cast<SlangCompileRequest*>(request); +} + } #endif diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index ce4131173..fc9cac3f3 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -409,6 +409,8 @@ DIAGNOSTIC(39016, Error, globalUniformsNotSupported, "'$0' is implicitly a globa DIAGNOSTIC(39017, Error, tooManyShaderRecordConstantBuffers, "can have at most one 'shader record' attributed constant buffer; found $0.") +DIAGNOSTIC(39018, Error, typeParametersNotAllowedOnEntryPointGlobal, "local-root-signature shader parameter '$0' at global scope must not include existential/interface types"); + // // 4xxxx - IL code generation. // diff --git a/source/slang/slang-diagnostics.h b/source/slang/slang-diagnostics.h index e1b9846d7..3d75c577d 100644 --- a/source/slang/slang-diagnostics.h +++ b/source/slang/slang-diagnostics.h @@ -143,7 +143,11 @@ namespace Slang class DiagnosticSink { - public: + public: + DiagnosticSink(SourceManager* sourceManager) + : sourceManager(sourceManager) + {} + struct Flag { enum Enum: uint32_t diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 623c57f2b..aa56bc0b2 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -48,29 +48,37 @@ enum class BuiltInCOp // EntryPointLayout* findEntryPointLayout( - ProgramLayout* programLayout, - EntryPoint* entryPoint) + ProgramLayout* programLayout, + EntryPoint* entryPoint, + EntryPointGroupLayout** outEntryPointGroupLayout = nullptr) { - for( auto entryPointLayout : programLayout->entryPoints ) + for( auto entryPointGroupLayout : programLayout->entryPointGroups ) { - if(entryPointLayout->entryPoint->getName() != entryPoint->getName()) - continue; - - // TODO: We need to be careful about this check, since it relies on - // the profile information in the layout matching that in the request. - // - // What we really seem to want here is some dictionary mapping the - // `EntryPoint` directly to the `EntryPointLayout`, and maybe - // that is precisely what we should build... - // - if(entryPointLayout->profile != entryPoint->getProfile()) - continue; - - // TODO: can't easily filter on translation unit here... - // Ideally the `EntryPoint` should get filled in with a pointer - // the specific function declaration that represents the entry point. - - return entryPointLayout.Ptr(); + for( auto entryPointLayout : entryPointGroupLayout->entryPoints ) + { + if(entryPointLayout->entryPoint->getName() != entryPoint->getName()) + continue; + + // TODO: We need to be careful about this check, since it relies on + // the profile information in the layout matching that in the request. + // + // What we really seem to want here is some dictionary mapping the + // `EntryPoint` directly to the `EntryPointLayout`, and maybe + // that is precisely what we should build... + // + if(entryPointLayout->profile != entryPoint->getProfile()) + continue; + + // TODO: can't easily filter on translation unit here... + // Ideally the `EntryPoint` should get filled in with a pointer + // the specific function declaration that represents the entry point. + + if( outEntryPointGroupLayout ) + { + *outEntryPointGroupLayout = entryPointGroupLayout; + } + return entryPointLayout; + } } return nullptr; diff --git a/source/slang/slang-file-system.h b/source/slang/slang-file-system.h index e89bf45db..1d747d73f 100644 --- a/source/slang/slang-file-system.h +++ b/source/slang/slang-file-system.h @@ -48,7 +48,7 @@ public: const char* path, ISlangBlob** outCanonicalPath) SLANG_OVERRIDE; - virtual SLANG_NO_THROW void SLANG_MCALL clearCache() {} + virtual SLANG_NO_THROW void SLANG_MCALL clearCache() SLANG_OVERRIDE {} /// Get a default instance static ISlangFileSystemExt* getSingleton() { return &s_singleton; } @@ -132,9 +132,9 @@ class CacheFileSystem: public ISlangFileSystemExt, public RefObject virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanonicalPath( const char* path, - ISlangBlob** outCanonicalPath); + ISlangBlob** outCanonicalPath) SLANG_OVERRIDE; - virtual SLANG_NO_THROW void SLANG_MCALL clearCache(); + virtual SLANG_NO_THROW void SLANG_MCALL clearCache() SLANG_OVERRIDE; /// Ctor CacheFileSystem(ISlangFileSystem* fileSystem, UniqueIdentityMode uniqueIdentityMode = UniqueIdentityMode::Default, PathStyle pathStyle = PathStyle::Default); diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 4c1f72adb..07135c148 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -13,8 +13,9 @@ namespace Slang // TODO: maybe arrange so that codegen is driven from the layout layer // instead of the input/request layer. EntryPointLayout* findEntryPointLayout( - ProgramLayout* programLayout, - EntryPoint* EntryPoint); + ProgramLayout* programLayout, + EntryPoint* entryPoint, + EntryPointGroupLayout** outEntryPointGroupLayout); struct IRSpecSymbol : RefObject { @@ -783,9 +784,12 @@ IRFunc* specializeIRForEntryPoint( if( paramIndex < paramLayoutCount ) { auto paramLayout = paramsStructLayout->fields[paramIndex]; + + auto offsetParamLayout = applyOffsetToVarLayout(paramLayout, entryPointLayout->parametersLayout); + context->builder->addLayoutDecoration( pp, - paramLayout); + offsetParamLayout); } else { @@ -1276,6 +1280,33 @@ LinkedIR linkIR( context->globalVarLayouts.AddIfNotExists(mangledName, globalVarLayout); } + EntryPointGroupLayout* entryPointGroupLayout = nullptr; + auto entryPointLayout = findEntryPointLayout(programLayout, entryPoint, &entryPointGroupLayout); + + auto offsetEntryPointLayout = entryPointLayout->getAbsoluteLayout(entryPointGroupLayout); + + // Note: when we are doing the compatibility approach for Falcor, we + // can have global-scope symbols that are actually part of the + // local root signature (entry point group), so we need to make + // sure to apply those layouts appropriately. + auto entryPointGroupStructLayout = getScopeStructLayout(entryPointGroupLayout); + for(auto entry : entryPointGroupStructLayout->mapVarToLayout) + { + if(!entry.Key) + continue; + + auto mangledName = getMangledName(entry.Key); + auto groupVarLayout = entry.Value; + + // We need to "adjust" the layout that was computed for the parameter + // because it will be relative to the start of the entry-point group, + // rather than absolute. + // + auto absoluteVarLayout = groupVarLayout->getAbsoluteLayout(entryPointGroupLayout->parametersLayout); + + context->globalVarLayouts.AddIfNotExists(mangledName, absoluteVarLayout); + } + context->builder->setInsertInto(context->getModule()->getModuleInst()); // for now, clone all unreferenced witness tables @@ -1289,13 +1320,12 @@ LinkedIR linkIR( cloneGlobalValue(context, (IRWitnessTable*)sym.Value->irGlobalValue); } - auto entryPointLayout = findEntryPointLayout(programLayout, entryPoint); // Next, we make sure to clone the global value for // the entry point function itself, and rely on // this step to recursively copy over anything else // it might reference. - auto irEntryPoint = specializeIRForEntryPoint(context, entryPoint, entryPointLayout); + auto irEntryPoint = specializeIRForEntryPoint(context, entryPoint, offsetEntryPointLayout); // HACK: right now the bindings for global generic parameters are coming in // as part of the original IR module, and we need to make sure these get diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 5c4fd24b5..d5efc3515 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -377,6 +377,7 @@ struct SharedParameterBindingContext TargetRequest* getTargetRequest() { return targetRequest; } DiagnosticSink* getSink() { return m_sink; } + Linkage* getLinkage() { return targetRequest->getLinkage(); } }; static DiagnosticSink* getSink(SharedParameterBindingContext* shared) @@ -403,6 +404,8 @@ struct ParameterBindingContext TargetRequest* getTargetRequest() { return shared->getTargetRequest(); } LayoutRulesFamilyImpl* getRulesFamily() { return layoutContext.getRulesFamily(); } + + Linkage* getLinkage() { return shared->getLinkage(); } }; static DiagnosticSink* getSink(ParameterBindingContext* context) @@ -610,7 +613,7 @@ RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter( auto layoutContext = context->layoutContext; auto rules = layoutContext.getRulesFamily(); - if( varDecl->HasModifier<ShaderRecordAttribute>() && as<ConstantBufferType>(type) ) + if(varDecl->HasModifier<ShaderRecordAttribute>() && as<ConstantBufferType>(type)) { return createTypeLayout( layoutContext.with(rules->getShaderRecordConstantBufferRules()), @@ -620,13 +623,20 @@ RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter( // We want to check for a constant-buffer type with a `push_constant` layout // qualifier before we move on to anything else. - if (varDecl->HasModifier<PushConstantAttribute>() && as<ConstantBufferType>(type)) + if( varDecl->HasModifier<PushConstantAttribute>() && as<ConstantBufferType>(type) ) { return createTypeLayout( layoutContext.with(rules->getPushConstantBufferRules()), type); } + // TODO: The cases below for detecting globals that aren't actually + // shader parameters should be redundant now that the semantic + // checking logic is responsible for populating the list of + // parameters on a `Program`. We should be able to clean up + // the code by removing these two cases, and the related null + // pointer checks in the code that calls this. + // HLSL `static` modifier indicates "thread local" if(varDecl->HasModifier<HLSLStaticModifier>()) return nullptr; @@ -1940,6 +1950,45 @@ struct ScopeLayoutBuilder _addParameter(firstVarLayout, parameterInfo); } + + // Add a "simple" parameter that cannot have any user-defined + // register or binding modifiers, so that its layout computation + // can be simplified greatly. + // + void addSimpleParameter( + RefPtr<VarLayout> varLayout) + { + // The main `addParameter` logic will deal with any ordinary/uniform data, + // and with the "pending" part of the layout. + // + addParameter(varLayout); + + // That leaves us to deal with the resource usage that isn't + // handled by `addParameter`. + // + auto paramTypeLayout = varLayout->getTypeLayout(); + for (auto paramTypeResInfo : paramTypeLayout->resourceInfos) + { + // We need to skip ordinary/uniform data because it was + // handled by `addParameter`. + // + if(paramTypeResInfo.kind == LayoutResourceKind::Uniform) + continue; + + // Whatever resources the parameter uses, we need to + // assign the parameter's location/register/binding offset to + // be the sum of everything added so far. + // + auto scopeResInfo = m_structLayout->findOrAddResourceInfo(paramTypeResInfo.kind); + varLayout->findOrAddResourceInfo(paramTypeResInfo.kind)->index = scopeResInfo->count.getFiniteValue(); + + // We then need to add the resources consumed by the parameter + // to those consumed by the scope. + // + scopeResInfo->count += paramTypeResInfo.count; + } + } + RefPtr<VarLayout> endLayout() { // Finish computing the layout for the ordindary data (if any). @@ -2007,7 +2056,7 @@ static ParameterBindingAndKindInfo maybeAllocateConstantBufferBinding( /// Iterate over the parameters of an entry point to compute its requirements. /// -static void collectEntryPointParameters( +static RefPtr<EntryPointLayout> collectEntryPointParameters( ParameterBindingContext* context, EntryPoint* entryPoint, SubstitutionSet typeSubst) @@ -2119,35 +2168,7 @@ static void collectEntryPointParameters( // we need to add its resource usage to that of the entry // point as a whole. // - // Any "ordinary" data (e.g., a `float4x4`) needs to be accounted - // for using the `ScopeLayoutBuilder`, since it will handle - // the details of target-specific `struct` type layout. - // - scopeBuilder.addParameter(paramVarLayout); - - // All of the other resources types will be handled in a - // simpler loop that just increments the relevant counters. - // - for (auto paramTypeResInfo : paramTypeLayout->resourceInfos) - { - // We need to skip ordinary data because it is being - // handled by the `scopeBuilder`. - // - if(paramTypeResInfo.kind == LayoutResourceKind::Uniform) - continue; - - // Whatever resources the parameter uses, we need to - // assign the parameter's location/register/binding offset to - // be the sum of everything added so far. - // - auto entryPointResInfo = paramsStructLayout->findOrAddResourceInfo(paramTypeResInfo.kind); - paramVarLayout->findOrAddResourceInfo(paramTypeResInfo.kind)->index = entryPointResInfo->count.getFiniteValue(); - - // We then need to add the resources consumed by the parameter - // to those consumed by the entry point. - // - entryPointResInfo->count += paramTypeResInfo.count; - } + scopeBuilder.addSimpleParameter(paramVarLayout); } entryPointLayout->parametersLayout = scopeBuilder.endLayout(); @@ -2189,6 +2210,25 @@ static void collectEntryPointParameters( entryPointLayout->resultLayout = resultLayout; } + + return entryPointLayout; +} + + /// Remove resource usage from `typeLayout` that should only be stored per-entry-point. + /// + /// This is used when constructing the layout for an entry point group, to make sure + /// that certain kinds of resource usage from the entry point don't "leak" into + /// the resource usage of the group. + /// +static void removePerEntryPointParameterKinds( + TypeLayout* typeLayout) +{ + typeLayout->removeResourceUsage(LayoutResourceKind::VaryingInput); + typeLayout->removeResourceUsage(LayoutResourceKind::VaryingOutput); + typeLayout->removeResourceUsage(LayoutResourceKind::ShaderRecord); + typeLayout->removeResourceUsage(LayoutResourceKind::HitAttributes); + typeLayout->removeResourceUsage(LayoutResourceKind::ExistentialObjectParam); + typeLayout->removeResourceUsage(LayoutResourceKind::ExistentialTypeParam); } static void collectParameters( @@ -2242,10 +2282,66 @@ static void collectParameters( } // Next consider parameters for entry points - for(auto entryPoint : program->getEntryPoints()) + for( auto entryPointGroup : program->getEntryPointGroups() ) { - context->stage = entryPoint->getStage(); - collectEntryPointParameters(context, entryPoint, globalGenericSubst); + RefPtr<EntryPointGroupLayout> entryPointGroupLayout = new EntryPointGroupLayout(); + entryPointGroupLayout->group = entryPointGroup; + + context->shared->programLayout->entryPointGroups.add(entryPointGroupLayout); + + + ScopeLayoutBuilder scopeBuilder; + scopeBuilder.beginLayout(context); + auto entryPointGroupParamsStructLayout = scopeBuilder.m_structLayout; + + // First lay out any shader parameters that belong to the group + // itself, rather than to its nested entry points. + // + // This ensures that looking up one of the parameters of the + // group by index in its parameters truct will Just Work. + // + for( auto groupParam : entryPointGroup->getShaderParams() ) + { + auto paramDeclRef = groupParam.paramDeclRef; + auto paramType = GetType(paramDeclRef); + + RefPtr<VarLayout> paramVarLayout = new VarLayout(); + paramVarLayout->varDecl = paramDeclRef; + + auto paramTypeLayout = createTypeLayout( + context->layoutContext.with(context->getRulesFamily()->getConstantBufferRules()), + paramType); + paramVarLayout->typeLayout = paramTypeLayout; + + scopeBuilder.addSimpleParameter(paramVarLayout); + } + + for(auto entryPoint : entryPointGroup->getEntryPoints()) + { + // Note: we do not want the entry point group to accumulate + // locations for varying input/output parameters: those + // should be specific to each entry point. + // + // We address this issue by manually removing any + // layout information for the relevant resource kinds + // from the group's layout before adding the parameters + // of any entry point for layout. + // + removePerEntryPointParameterKinds(scopeBuilder.m_structLayout); + + context->stage = entryPoint->getStage(); + auto entryPointLayout = collectEntryPointParameters(context, entryPoint, globalGenericSubst); + + auto entryPointParamsLayout = entryPointLayout->parametersLayout; + auto entryPointParamsTypeLayout = entryPointParamsLayout->typeLayout; + + scopeBuilder.addSimpleParameter(entryPointParamsLayout); + + entryPointGroupLayout->entryPoints.add(entryPointLayout); + } + removePerEntryPointParameterKinds(scopeBuilder.m_structLayout); + + entryPointGroupLayout->parametersLayout = scopeBuilder.endLayout(); } context->entryPointLayout = nullptr; } @@ -2452,44 +2548,6 @@ RefPtr<ProgramLayout> generateParameterBindings( completeBindingsForParameter(&context, parameter); } - // After we have allocated registers/bindings to everything - // in the global scope we will process the parameters - // of each entry point in order. - // - // Note: the effect of the current implementation is to - // allocate non-overlapping registers/bindings between all - // the entry points in the compile request (e.g., if you - // have a vertex and fragment shader being compiled together, - // we will allocate distinct constant buffer registers for - // their uniform parameters). - // - // TODO: We probably need to provide some more nuanced control - // over whether entry points get overlapping or non-overlapping - // bindings. It seems clear that if we were compiling multiple - // compute kernels in one invocation we'd want them to get - // overlapping bindings, because we cannot ever have them bound - // together in a single pipeline state. - // - // Similarly, entry point parameters of DirectX Raytracing (DXR) - // shaders should probably be allowed to overlap by default, - // since those parameters should really go into the "local root signature." - // (Note: there is a bit more subtlety around ray tracing - // shaders that will be assembled into a "hit group") - // - // For now we are just doing the simplest thing, which will be - // appropriate for: - // - // * Compiling a single compute shader in a compile request. - // * Compiling some number of rasterization shader entry points - // in a single request, to be used together. - // * Compiling a single ray-tracing shader in a compile request. - // - for( auto entryPoint : sharedContext.programLayout->entryPoints ) - { - auto entryPointParamsLayout = entryPoint->parametersLayout; - completeBindingsForParameter(&context, entryPointParamsLayout); - } - // Next we need to create a type layout to reflect the information // we have collected, and we will use the `ScopeLayoutBuilder` // to encapsulate the logic that can be shared with the entry-point @@ -2510,17 +2568,75 @@ RefPtr<ProgramLayout> generateParameterBindings( cbInfo->index = globalConstantBufferBinding.index; } - // After we have laid out all the ordinary parameters, - // we need to go through the global scope plus each entry point, - // and "flush" out any pending data that was associated with - // those scopes as part of dealing with interface-type parameters. + // After we have laid out all the ordinary global parameters, + // we need to "flush" out any pending data that was associated with + // the global scope as part of dealing with interface-type parameters. // _allocateBindingsForPendingData(&context, globalScopeVarLayout->pendingVarLayout); - for( auto entryPoint : sharedContext.programLayout->entryPoints ) + + // After we have allocated registers/bindings to everything + // in the global scope we will process the parameters + // of the entry points. + // + // Note: at the moment we are laying out *all* information related to global-scope + // parameters (including pending data from interface-type parameters) before + // anything pertaining to entry points. This is a crucial design choice to + // get right, and we might want to revisit it based on experience. + + // In some cases, a user will want to ensure that all the + // entry points they compile get non-overlapping + // registers/bindings. E.g., if you have a vertex and fragment + // shader being compiled together for Vulkan, you probably want distinct + // bindings for their entry-point `uniform` parametres, so + // that they can be used together. + // + // In other cases, however, a user probably doesn't want us + // to conservatively allocate non-overlapping bindings. + // E.g., if they have a bunch of compute shaders in a single + // file, then they probably want each compute shader to + // compute its parameter layout "from scratch" as if the + // others don't exist. + // + // The way we handle this is by putting the entry points of a + // `Program` into groups, and ensuring that within each group + // we allocate parameters that don't overlap, but we don't + // worry about overlap across groups. + // + for( auto entryPointGroup : sharedContext.programLayout->entryPointGroups ) { - _allocateBindingsForPendingData(&context, entryPoint->parametersLayout->pendingVarLayout); - } + // We save off the allocation state as it was before the entry-point + // group, so that we can restore it to this state after each group. + // + // TODO: We probably ought to wrap all the state relevant to allocation + // of registers/bindings into a single struct/field so that we only + // have one thing to save/restore here even if new state gets added. + // + auto savedGlobalSpaceUsedRangeSets = sharedContext.globalSpaceUsedRangeSets; + auto savedUsedSpaces = sharedContext.usedSpaces; + + // The group will have been allocated a layout that combines the + // usage of all of the contained entry-points, so we just need to + // allocate the entry-point group to be placed after all the global-scope + // parameters. + // + auto entryPointGroupParamsLayout = entryPointGroup->parametersLayout; + completeBindingsForParameter(&context, entryPointGroupParamsLayout); + + _allocateBindingsForPendingData(&context, entryPointGroupParamsLayout->pendingVarLayout); + + // TODO: Should we add the offset information from the group to + // the layout information for each entry point (and thence to its parameters)? + // + // This seems important if we want to allow clients to conveniently + // ignore groups when doing their reflection queries. + // Once we've allocated bindigns for the parameters of entry points + // in the group, we restore the state for tracking what register/bindings + // are used to where it was before. + // + sharedContext.globalSpaceUsedRangeSets = savedGlobalSpaceUsedRangeSets; + sharedContext.usedSpaces = savedUsedSpaces; + } // HACK: we want global parameters to not have to deal with offsetting // by the `VarLayout` stored in `globalScopeVarLayout`, so we will scan diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index aff1d832d..fffbf2c62 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -1709,8 +1709,7 @@ namespace Slang TokenSpan tokenSpan; tokenSpan.mBegin = parser->tokenReader.mCursor; tokenSpan.mEnd = parser->tokenReader.mEnd; - DiagnosticSink newSink; - newSink.sourceManager = parser->sink->sourceManager; + DiagnosticSink newSink(parser->sink->sourceManager); Parser newParser(*parser); newParser.sink = &newSink; auto speculateParseRs = parseGenericApp(&newParser, base); diff --git a/source/slang/slang-reflection.cpp b/source/slang/slang-reflection.cpp index c5428cdeb..3871e2ccc 100644 --- a/source/slang/slang-reflection.cpp +++ b/source/slang/slang-reflection.cpp @@ -86,6 +86,15 @@ static inline SlangReflectionEntryPoint* convert(EntryPointLayout* entryPoint) return (SlangReflectionEntryPoint*) entryPoint; } +static inline EntryPointGroupLayout* convert(SlangEntryPointGroupLayout* entryPointGroup) +{ + return (EntryPointGroupLayout*) entryPointGroup; +} + +static inline SlangEntryPointGroupLayout* convert(EntryPointGroupLayout* entryPointGroup) +{ + return (SlangEntryPointGroupLayout*) entryPointGroup; +} static inline ProgramLayout* convert(SlangReflection* program) { @@ -596,9 +605,9 @@ SLANG_API SlangReflectionType * spReflection_FindTypeByName(SlangReflection * re // TODO: We should extend this API to support getting error messages // when type lookup fails. // - Slang::DiagnosticSink sink; - - sink.sourceManager = programLayout->getTargetReq()->getLinkage()->getSourceManager();; + Slang::DiagnosticSink sink( + programLayout->getTargetReq()->getLinkage()->getSourceManager()); + RefPtr<Type> result = program->getTypeFromString(name, &sink); return (SlangReflectionType*)result.Ptr(); } @@ -611,13 +620,9 @@ SLANG_API SlangReflectionTypeLayout* spReflection_GetTypeLayout( auto context = convert(reflection); auto type = convert(inType); auto targetReq = context->getTargetReq(); - auto layoutContext = getInitialLayoutContextForTarget(targetReq, context); - RefPtr<TypeLayout> result; - if (targetReq->getTypeLayouts().TryGetValue(type, result)) - return (SlangReflectionTypeLayout*)result.Ptr(); - result = createTypeLayout(layoutContext, type); - targetReq->getTypeLayouts()[type] = result; - return (SlangReflectionTypeLayout*)result.Ptr(); + + auto typeLayout = targetReq->getTypeLayout(type); + return convert(typeLayout); } SLANG_API SlangReflectionType* spReflectionType_GetResourceResultType(SlangReflectionType* inType) @@ -1071,11 +1076,32 @@ SLANG_API size_t spReflectionVariableLayout_GetSpace(SlangReflectionVariableLayo space += info->space; } - // Next, deal with any dedicated register-space offset applied to, e.g., a parameter block - if (auto spaceInfo = varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) - { - space += spaceInfo->index; - } + // Note: this code used to try and take a variable with + // an offset for `LayoutResourceKind::RegisterSpace` and + // add it to the space returned, but that isn't going + // to be right in some cases. + // + // Imageine if we have: + // + // struct X { Texture2D y; } + // struct S { Texture2D t; ParmaeterBlock<X> x; } + // + // Texture2D gA; + // S gS; + // + // We expect `gS` to have an offset for `LayoutResourceKind::ShaderResourceView` + // of one (since its texture must come after `gA`), and an offset for + // `LayoutResourceKind::RegisterSpace` of one (since the default space will be + // space zero). It would be incorrect for us to imply that `gS.t` should + // be `t1, space1`, though, because the space offset of `gS` doesn't actually + // apply to `t`. + // + // For now we are punting on this issue and leaving it in the hands of the + // application to determine when a space offset from an "outer" variable should + // apply to the locations of things in an "inner" variable. + // + // There is no policy we can apply locally in this function that + // will Just Work, so the best we can do is try to not lie. return space; } @@ -1392,7 +1418,7 @@ SLANG_API SlangReflectionEntryPoint* spReflection_findEntryPointByName(SlangRefl auto program = convert(inProgram); if(!program) return 0; - // TODO: improve on dumb linear search + // TODO: improve on naive linear search for(auto ep : program->entryPoints) { if(ep->entryPoint->getName()->text == name) @@ -1405,6 +1431,74 @@ SLANG_API SlangReflectionEntryPoint* spReflection_findEntryPointByName(SlangRefl } +SLANG_API SlangInt spReflection_getEntryPointGroupCount(SlangReflection* inProgram) +{ + auto program = convert(inProgram); + if(!program) return 0; + + return program->entryPointGroups.getCount(); +} + +SLANG_API SlangEntryPointGroupLayout* spReflection_getEntryPointGroupByIndex(SlangReflection* inProgram, SlangInt index) +{ + auto program = convert(inProgram); + if(!program) return 0; + + if(index < 0) return nullptr; + if(index >= program->entryPointGroups.getCount()) return nullptr; + + return convert(program->entryPointGroups[(int) index].Ptr()); +} + +SLANG_API SlangInt spEntryPointGroupLayout_getEntryPointCount(SlangEntryPointGroupLayout* inGroup) +{ + auto group = convert(inGroup); + if(!group) return 0; + + return group->entryPoints.getCount(); +} + +SLANG_API SlangReflectionEntryPoint* spEntryPointGroupLayout_getEntryPointByIndex(SlangEntryPointGroupLayout* inGroup, SlangInt index) +{ + auto group = convert(inGroup); + if(!group) return 0; + + if(index < 0) return nullptr; + if(index >= group->entryPoints.getCount()) return nullptr; + + return convert(group->entryPoints[(int) index].Ptr()); +} + +SLANG_API SlangReflectionVariableLayout* spEntryPointGroupLayout_getVarLayout(SlangEntryPointGroupLayout* inGroup) +{ + auto group = convert(inGroup); + if(!group) return 0; + + return convert(group->parametersLayout); +} + +SLANG_API SlangInt spEntryPointGroupLayout_getParameterCount(SlangEntryPointGroupLayout* inGroup) +{ + auto groupLayout = convert(inGroup); + if(!groupLayout) return 0; + + auto& params = groupLayout->group->getShaderParams(); + return params.getCount(); +} + +SLANG_API SlangReflectionVariableLayout* spEntryPointGroupLayout_getParameterByIndex(SlangEntryPointGroupLayout* inGroup, SlangInt index) +{ + auto groupLayout = convert(inGroup); + if(!groupLayout) return nullptr; + + auto& params = groupLayout->group->getShaderParams(); + if(index < 0) return nullptr; + if(index >= params.getCount()) return nullptr; + + return convert(getScopeStructLayout(groupLayout)->fields[index]); +} + + SLANG_API SlangUInt spReflection_getGlobalConstantBufferBinding(SlangReflection* inProgram) { auto program = convert(inProgram); @@ -1437,10 +1531,9 @@ SLANG_API SlangReflectionType* spReflection_specializeType( auto unspecializedType = convert(inType); if(!unspecializedType) return nullptr; - auto linkage = programLayout->getProgram()->getLinkage(); + auto linkage = programLayout->getProgram()->getLinkageImpl(); - DiagnosticSink sink; - sink.sourceManager = linkage->getSourceManager(); + DiagnosticSink sink(linkage->getSourceManager()); auto specializedType = linkage->specializeType(unspecializedType, specializationArgCount, (Type* const*) specializationArgs, &sink); diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 30ab53ca6..c3dbb4966 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -1165,6 +1165,83 @@ RefPtr<TypeLayout> applyOffsetToTypeLayout( return newTypeLayout; } +RefPtr<VarLayout> applyOffsetToVarLayout( + VarLayout* baseLayout, + VarLayout* offsetLayout) +{ + RefPtr<VarLayout> adjustedLayout = new VarLayout(); + adjustedLayout->typeLayout = baseLayout->typeLayout; + adjustedLayout->varDecl = baseLayout->varDecl; + adjustedLayout->flags = baseLayout->flags; + adjustedLayout->semanticName = baseLayout->semanticName; + adjustedLayout->semanticIndex = baseLayout->semanticIndex; + adjustedLayout->systemValueSemantic = baseLayout->systemValueSemantic; + adjustedLayout->systemValueSemanticIndex = baseLayout->systemValueSemanticIndex; + adjustedLayout->stage = baseLayout->stage; + + if( auto basePendingLayout = baseLayout->pendingVarLayout ) + { + if( auto offsetPendingLayout = offsetLayout->pendingVarLayout ) + { + adjustedLayout->pendingVarLayout = applyOffsetToVarLayout( + basePendingLayout, + offsetPendingLayout); + } + } + + for( auto baseResInfo : baseLayout->resourceInfos ) + { + auto adjustedResInfo = baseResInfo; + if( auto offsetResInfo = offsetLayout->FindResourceInfo(baseResInfo.kind) ) + { + adjustedResInfo.index += offsetResInfo->index; + adjustedResInfo.space += offsetResInfo->space; + } + adjustedLayout->resourceInfos.add(adjustedResInfo); + } + + return adjustedLayout; +} + +EntryPointLayout* EntryPointLayout::getAbsoluteLayout( + VarLayout* parentLayout) +{ + SLANG_ASSERT(parentLayout); + + if(m_absoluteLayout) + return m_absoluteLayout; + + RefPtr<EntryPointLayout> adjustedLayout = new EntryPointLayout(); + adjustedLayout->entryPoint = this->entryPoint; + adjustedLayout->flags = this->flags; + adjustedLayout->parametersLayout = this->parametersLayout->getAbsoluteLayout(parentLayout); + adjustedLayout->profile = this->profile; + if( auto baseResultLayout = this->resultLayout ) + { + adjustedLayout->resultLayout = baseResultLayout->getAbsoluteLayout(parentLayout); + } + adjustedLayout->taggedUnionTypeLayouts = this->taggedUnionTypeLayouts; + + m_absoluteLayout = adjustedLayout; + return adjustedLayout; +} + +EntryPointLayout* EntryPointLayout::getAbsoluteLayout(EntryPointGroupLayout* parentGroup) +{ + SLANG_ASSERT(parentGroup); + return getAbsoluteLayout(parentGroup->parametersLayout); +} + + +VarLayout* VarLayout::getAbsoluteLayout(VarLayout* parentAbsoluteLayout) +{ + if( !m_absoluteLayout ) + { + m_absoluteLayout = applyOffsetToVarLayout(this, parentAbsoluteLayout); + } + return m_absoluteLayout; +} + static bool _usesResourceKind(RefPtr<TypeLayout> typeLayout, LayoutResourceKind kind) { auto resInfo = typeLayout->FindResourceInfo(kind); @@ -3180,6 +3257,20 @@ RefPtr<TypeLayout> createTypeLayout( return _createTypeLayout(context, type).layout; } +void TypeLayout::removeResourceUsage(LayoutResourceKind kind) +{ + Int infoCount = resourceInfos.getCount(); + for( Int ii = 0; ii < infoCount; ++ii ) + { + if( resourceInfos[ii].kind == kind ) + { + resourceInfos.removeAt(ii); + return; + } + } +} + + void TypeLayout::addResourceUsageFrom(TypeLayout* otherTypeLayout) { for(auto resInfo : otherTypeLayout->resourceInfos) diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index 97113c77f..e066c2700 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -377,6 +377,8 @@ public: addResourceUsage(info); } + void removeResourceUsage(LayoutResourceKind kind); + void addResourceUsageFrom(TypeLayout* otherTypeLayout); /// "Unwrap" any layers of array-ness from this type layout. @@ -473,6 +475,21 @@ public: } RefPtr<VarLayout> pendingVarLayout; + + + /// Get the "absolute" layout of this variable, if applicable. + /// + /// The `parentAbsoluteLayout` must be the absolute layout + /// of the parent of this variable. + /// + /// The absolute layout will be created once and cached on + /// future accesses; if `parentAbsoluteLayout` is not + /// consistent at different call sites an unexpected + /// layout could be returned. + /// + VarLayout* getAbsoluteLayout(VarLayout* parentAbsoluteLayout); + + RefPtr<VarLayout> m_absoluteLayout; }; // type layout for a variable that has a constant-buffer type @@ -650,6 +667,8 @@ public: StructTypeLayout* getScopeStructLayout( ScopeLayout* programLayout); +class EntryPointGroupLayout; + // Layout information for a single shader entry point // within a program // @@ -682,6 +701,18 @@ public: /// These are any tagged union types used by the generic /// arguments that this entry point is being compiled with. List<RefPtr<TypeLayout>> taggedUnionTypeLayouts; + + EntryPointLayout* getAbsoluteLayout(VarLayout* parentLayout); + EntryPointLayout* getAbsoluteLayout(EntryPointGroupLayout* parentGroup); + + RefPtr<EntryPointLayout> m_absoluteLayout; +}; + +class EntryPointGroupLayout : public ScopeLayout +{ +public: + RefPtr<EntryPointGroup> group; + List<RefPtr<EntryPointLayout>> entryPoints; }; class GenericParamLayout : public Layout @@ -725,6 +756,10 @@ public: // will (eventually) belong there... List<RefPtr<EntryPointLayout>> entryPoints; + // Entry points can also be grouped for layout purposes (e.g., to form + // ray-tracing hit groups), so this array represents those groups + List<RefPtr<EntryPointGroupLayout>> entryPointGroups; + List<RefPtr<GenericParamLayout>> globalGenericParams; Dictionary<String, GenericParamLayout*> globalGenericParamsMap; }; @@ -1113,6 +1148,11 @@ RefPtr<TypeLayout> applyOffsetToTypeLayout( RefPtr<TypeLayout> oldTypeLayout, RefPtr<VarLayout> offsetVarLayout); + /// Create a layout like `baseLayout`, but offset by `offsetLayout` +RefPtr<VarLayout> applyOffsetToVarLayout( + VarLayout* baseLayout, + VarLayout* offsetLayout); + } #endif diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index a3875ef62..b18e4d4d9 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -33,6 +33,12 @@ namespace Slang { +// Allocate static const storage for the various interface IDs that the Slang API needs to expose +static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; +static const Guid IID_ISlangBlob = SLANG_UUID_ISlangBlob; +static const Guid IID_ISession = SLANG_UUID_ISession; +static const Guid IID_IGlobalSession = SLANG_UUID_IGlobalSession; +static const Guid IID_IModule = SLANG_UUID_IModule; Session::Session() { @@ -91,6 +97,55 @@ Session::Session() addBuiltinSource(hlslLanguageScope, "hlsl", getHLSLLibraryCode()); } +ISlangUnknown* Session::getInterface(const Guid& guid) +{ + if(guid == IID_ISlangUnknown || guid == IID_IGlobalSession) + return asExternal(this); + return nullptr; +} + +SLANG_NO_THROW SlangResult SLANG_MCALL Session::createSession( + slang::SessionDesc const& desc, + slang::ISession** outSession) +{ + RefPtr<Linkage> linkage = new Linkage(this); + + Int targetCount = desc.targetCount; + for(Int ii = 0; ii < targetCount; ++ii) + { + linkage->addTarget(desc.targets[ii]); + } + + if(desc.flags & slang::kSessionFlag_FalcorCustomSharedKeywordSemantics) + { + linkage->m_useFalcorCustomSharedKeywordSemantics = true; + } + + linkage->setMatrixLayoutMode(desc.defaultMatrixLayoutMode); + + Int searchPathCount = desc.searchPathCount; + for(Int ii = 0; ii < searchPathCount; ++ii) + { + linkage->addSearchPath(desc.searchPaths[ii]); + } + + Int macroCount = desc.preprocessorMacroCount; + for(Int ii = 0; ii < macroCount; ++ii) + { + auto& macro = desc.preprocessorMacros[ii]; + linkage->addPreprocessorDefine(macro.name, macro.value); + } + + *outSession = asExternal(linkage.detach()); + return SLANG_OK; +} + +SLANG_NO_THROW SlangProfileID SLANG_MCALL Session::findProfile( + char const* name) +{ + return Slang::Profile::LookUp(name).raw; +} + struct IncludeHandlerImpl : IncludeHandler { Linkage* linkage; @@ -330,9 +385,183 @@ Linkage::Linkage(Session* session) setFileSystem(nullptr); } -// Allocate static const storage for the various interface IDs that the Slang API needs to expose -static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; -static const Guid IID_ISlangBlob = SLANG_UUID_ISlangBlob; +ISlangUnknown* Linkage::getInterface(const Guid& guid) +{ + if(guid == IID_ISlangUnknown || guid == IID_ISession) + return asExternal(this); + + return nullptr; +} + +SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL Linkage::getGlobalSession() +{ + return asExternal(getSessionImpl()); +} + +void Linkage::addTarget( + slang::TargetDesc const& desc) +{ + auto targetIndex = addTarget(CodeGenTarget(desc.format)); + auto target = targets[targetIndex]; + + target->floatingPointMode = FloatingPointMode(desc.floatingPointMode); + target->targetFlags = desc.flags; + target->targetProfile = Profile(desc.profile); +} + +#if 0 +SLANG_NO_THROW SlangInt SLANG_MCALL Linkage::getTargetCount() +{ + return targets.getCount(); +} + +SLANG_NO_THROW slang::ITarget* SLANG_MCALL Linkage::getTargetByIndex(SlangInt index) +{ + if(index < 0) return nullptr; + if(index >= targets.getCount()) return nullptr; + return asExternal(targets[index]); +} +#endif + +SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModule( + const char* moduleName, + slang::IBlob** outDiagnostics) +{ + auto name = getNamePool()->getName(moduleName); + + DiagnosticSink sink(getSourceManager()); + auto module = findOrImportModule(name, SourceLoc(), &sink); + sink.getBlobIfNeeded(outDiagnostics); + + return asExternal(module); +} + +SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createProgram( + slang::ProgramDesc const& desc, + slang::IProgram** outProgram) +{ + RefPtr<Program> program = new Program(this); + + auto itemCount = desc.itemCount; + for(SlangInt ii = 0; ii < itemCount; ++ii) + { + auto& item = desc.items[ii]; + switch(item.kind) + { + case slang::ProgramDesc::Item::Kind::Program: + { + Program* existingProgram = asInternal(item.program); + for(auto referencedModule : existingProgram->getModuleDependencies()) + { + program->addReferencedLeafModule(referencedModule); + } + + // TODO: Need to decide whether to include the entry points as well... + } + break; + + case slang::ProgramDesc::Item::Kind::Module: + { + Module* module = asInternal(item.module); + program->addReferencedModule(module); + } + break; + + default: + return SLANG_E_INVALID_ARG; + } + } + + *outProgram = asExternal(program.detach()); + return SLANG_OK; +} + +SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::specializeType( + slang::TypeReflection* inUnspecializedType, + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + ISlangBlob** outDiagnostics) +{ + auto unspecializedType = asInternal(inUnspecializedType); + + List<Type*> typeArgs; + + for(Int ii = 0; ii < specializationArgCount; ++ii) + { + auto& arg = specializationArgs[ii]; + if(arg.kind != slang::SpecializationArg::Kind::Type) + return nullptr; + + typeArgs.add(asInternal(arg.type)); + } + + DiagnosticSink sink(getSourceManager()); + auto specializedType = specializeType(unspecializedType, typeArgs.getCount(), typeArgs.getBuffer(), &sink); + sink.getBlobIfNeeded(outDiagnostics); + + return asExternal(specializedType); +} + +SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL Linkage::getTypeLayout( + slang::TypeReflection* inType, + SlangInt targetIndex, + slang::LayoutRules rules, + ISlangBlob** outDiagnostics) +{ + auto type = asInternal(inType); + + if(targetIndex < 0 || targetIndex >= targets.getCount()) + return nullptr; + + auto target = targets[targetIndex]; + + // TODO: We need a way to pass through the layout rules + // that the user requested (e.g., constant buffers vs. + // structured buffer rules). Right now the API only + // exposes a single case, so this isn't a big deal. + // + SLANG_UNUSED(rules); + + auto typeLayout = target->getTypeLayout(type); + + // TODO: We currently don't have a path for capturing + // errors that occur during layout (e.g., types that + // are invalid because of target-specific layout constraints). + // + SLANG_UNUSED(outDiagnostics); + + return asExternal(typeLayout); +} + +SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createCompileRequest( + SlangCompileRequest** outCompileRequest) +{ + auto compileRequest = new EndToEndCompileRequest(this); + *outCompileRequest = asExternal(compileRequest); + return SLANG_OK; +} + +SlangResult Linkage::addSearchPath( + char const* path) +{ + searchDirectories.searchDirectories.add(Slang::SearchDirectory(path)); + return SLANG_OK; +} + +SlangResult Linkage::addPreprocessorDefine( + char const* name, + char const* value) +{ + preprocessorDefinitions[name] = value; + return SLANG_OK; +} + +SlangResult Linkage::setMatrixLayoutMode( + SlangMatrixLayoutMode mode) +{ + defaultMatrixLayoutMode = MatrixLayoutMode(mode); + return SLANG_OK; +} /** Base class for simple blobs. */ @@ -386,7 +615,7 @@ ComPtr<ISlangBlob> createRawBlob(void const* inData, size_t size) Session* TargetRequest::getSession() { - return linkage->getSession(); + return linkage->getSessionImpl(); } MatrixLayoutMode TargetRequest::getDefaultMatrixLayoutMode() @@ -394,6 +623,29 @@ MatrixLayoutMode TargetRequest::getDefaultMatrixLayoutMode() return linkage->getDefaultMatrixLayoutMode(); } +TypeLayout* TargetRequest::getTypeLayout(Type* type) +{ + // TODO: We are not passing in a `ProgramLayout` here, although one + // is nominally required to establish the global ordering of + // generic type parameters, which might be referenced from field types. + // + // The solution here is to make sure that the reflection data for + // uses of global generic/existential types does *not* include any + // kind of index in that global ordering, and just refers to the + // parameter instead (leaving the user to figure out how that + // maps to the ordering via some API on the program layout). + // + auto layoutContext = getInitialLayoutContextForTarget(this, nullptr); + + RefPtr<TypeLayout> result; + if (getTypeLayouts().TryGetValue(type, result)) + return result.Ptr(); + result = createTypeLayout(layoutContext, type); + getTypeLayouts()[type] = result; + return result.Ptr(); +} + + // // TranslationUnitRequest // @@ -485,8 +737,7 @@ RefPtr<Expr> Linkage::parseTypeString(String typeStr, RefPtr<Scope> scope) Slang::SourceFile* srcFile = localSourceManager.createSourceFileWithString(PathInfo::makeTypeParse(), typeStr); // We'll use a temporary diagnostic sink - DiagnosticSink sink; - sink.sourceManager = &localSourceManager; + DiagnosticSink sink(&localSourceManager); // RAII type to make make sure current SourceManager is restored after parse. // Use RAII - to make sure everything is reset even if an exception is thrown. @@ -521,7 +772,7 @@ RefPtr<Expr> Linkage::parseTypeString(String typeStr, RefPtr<Scope> scope) nullptr); return parseTypeFromSourceFile( - getSession(), + getSessionImpl(), tokens, &sink, scope, getNamePool(), SourceLanguage::Slang); } @@ -552,7 +803,7 @@ Type* Program::getTypeFromString(String typeStr, DiagnosticSink* sink) for(auto module : getModuleDependencies()) scopesToTry.add(module->getModuleDecl()->scope); - auto linkage = getLinkage(); + auto linkage = getLinkageImpl(); for(auto& s : scopesToTry) { RefPtr<Expr> typeExpr = linkage->parseTypeString( @@ -785,13 +1036,15 @@ SlangResult FrontEndCompileRequest::executeActionsInner() if (getSink()->GetErrorCount() != 0) return SLANG_FAIL; - if ((compileFlags & SLANG_COMPILE_FLAG_NO_CODEGEN) == 0) - { - // Generate initial IR for all the translation - // units, if we are in a mode where IR is called for. - generateIR(); - } - + // We always generate IR for all the translation units. + // + // TODO: We may eventually have a mode where we skip + // IR codegen and only produce an AST (e.g., for use when + // debugging problems in the parser or semantic checking), + // but for now there are no cases where not having IR + // makes sense. + // + generateIR(); if (getSink()->GetErrorCount() != 0) return SLANG_FAIL; @@ -819,9 +1072,23 @@ BackEndCompileRequest::BackEndCompileRequest( EndToEndCompileRequest::EndToEndCompileRequest( Session* session) : m_session(session) + , m_sink(nullptr) { m_linkage = new Linkage(session); + init(); +} +EndToEndCompileRequest::EndToEndCompileRequest( + Linkage* linkage) + : m_session(linkage->getSessionImpl()) + , m_linkage(linkage) + , m_sink(nullptr) +{ + init(); +} + +void EndToEndCompileRequest::init() +{ m_sink.sourceManager = m_linkage->getSourceManager(); // Set all the default writers @@ -919,7 +1186,7 @@ SlangResult EndToEndCompileRequest::executeActionsInner() entryPointReq->getName(), entryPointReq->getProfile()); - specializedProgram->addEntryPoint(entryPoint); + specializedProgram->addEntryPoint(entryPoint, getSink()); } } @@ -1353,6 +1620,12 @@ Module::Module(Linkage* linkage) : m_linkage(linkage) {} +ISlangUnknown* Module::getInterface(const Guid& guid) +{ + if(guid == IID_ISlangUnknown || guid == IID_IModule) + return asExternal(this); + return nullptr; +} void Module::addModuleDependency(Module* module) { @@ -1367,10 +1640,69 @@ void Module::addFilePathDependency(String const& path) // Program +static const Guid IID_IProgram = SLANG_UUID_IProgram; + Program::Program(Linkage* linkage) : m_linkage(linkage) {} +ISlangUnknown* Program::getInterface(Guid const& guid) +{ + if(guid == IID_ISlangUnknown + || guid == IID_IProgram) + { + return static_cast<slang::IProgram*>(this); + } + + return nullptr; +} + +SLANG_NO_THROW slang::ISession* SLANG_MCALL Program::getSession() +{ + return m_linkage; +} + +SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL Program::getLayout( + Int targetIndex, + slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkageImpl(); + if(targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return nullptr; + auto target = linkage->targets[targetIndex]; + + DiagnosticSink sink(linkage->getSourceManager()); + auto programLayout = getTargetProgram(target)->getOrCreateLayout(&sink); + sink.getBlobIfNeeded(outDiagnostics); + + return asExternal(programLayout); +} + +SLANG_NO_THROW SlangResult SLANG_MCALL Program::getEntryPointCode( + SlangInt entryPointIndex, + Int targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) +{ + auto linkage = getLinkageImpl(); + if(targetIndex < 0 || targetIndex >= linkage->targets.getCount()) + return SLANG_E_INVALID_ARG; + auto target = linkage->targets[targetIndex]; + + auto targetProgram = getTargetProgram(target); + + DiagnosticSink sink(linkage->getSourceManager()); + auto& entryPointResult = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink); + sink.getBlobIfNeeded(outDiagnostics); + + if(entryPointResult.format == ResultFormat::None ) + return SLANG_FAIL; + + *outCode = entryPointResult.getBlob().detach(); + return SLANG_OK; +} + + void Program::addReferencedModule(Module* module) { m_moduleDependencyList.addDependency(module); @@ -1383,13 +1715,27 @@ void Program::addReferencedLeafModule(Module* module) m_filePathDependencyList.addDependency(module); } -void Program::addEntryPoint(EntryPoint* entryPoint) +void Program::addEntryPoint(EntryPoint* entryPoint, DiagnosticSink* sink) +{ + List<RefPtr<EntryPoint>> entryPoints; + entryPoints.add(entryPoint); + + RefPtr<EntryPointGroup> entryPointGroup = EntryPointGroup::create(getLinkageImpl(), entryPoints, sink); + + addEntryPointGroup(entryPointGroup); +} + +void Program::addEntryPointGroup(EntryPointGroup* entryPointGroup) { - m_entryPoints.add(entryPoint); + m_entryPointGroups.add(entryPointGroup); - for(auto module : entryPoint->getModuleDependencies()) + for(auto entryPoint : entryPointGroup->getEntryPoints()) { - addReferencedModule(module); + m_entryPoints.add(entryPoint); + for(auto module : entryPoint->getModuleDependencies()) + { + addReferencedModule(module); + } } } @@ -1398,7 +1744,7 @@ RefPtr<IRModule> Program::getOrCreateIRModule(DiagnosticSink* sink) if(!m_irModule) { m_irModule = generateIRForProgram( - m_linkage->getSession(), + m_linkage->getSessionImpl(), this, sink); } @@ -1466,7 +1812,7 @@ SlangResult DiagnosticSink::getBlobIfNeeded(ISlangBlob** outBlob) Session* CompileRequestBase::getSession() { - return getLinkage()->getSession(); + return getLinkage()->getSessionImpl(); } static const Slang::Guid IID_ISlangFileSystemExt = SLANG_UUID_ISlangFileSystemExt; @@ -1512,12 +1858,13 @@ void Session::addBuiltinSource( String const& path, String const& source) { - DiagnosticSink sink; + SourceManager* sourceManager = getBuiltinSourceManager(); + + DiagnosticSink sink(sourceManager); RefPtr<FrontEndCompileRequest> compileRequest = new FrontEndCompileRequest( m_builtinLinkage, &sink); - SourceManager* sourceManager = getBuiltinSourceManager(); // Set the source manager on the sink sink.sourceManager = sourceManager; @@ -1611,20 +1958,24 @@ static SlangCompileRequest* convert(Slang::EndToEndCompileRequest* request) static Slang::EndToEndCompileRequest* convert(SlangCompileRequest* request) { return reinterpret_cast<Slang::EndToEndCompileRequest*>(request); } -static SlangLinkage* convert(Slang::Linkage* linkage) -{ return reinterpret_cast<SlangLinkage*>(linkage); } - -static Slang::Linkage* convert(SlangLinkage* linkage) -{ return reinterpret_cast<Slang::Linkage*>(linkage); } - -static SlangModule* convert(Slang::Module* module) -{ return reinterpret_cast<SlangModule*>(module); } - SLANG_API SlangSession* spCreateSession(const char*) { return convert(new Slang::Session()); } +SLANG_API SlangResult slang_createGlobalSession( + SlangInt apiVersion, + slang::IGlobalSession** outGlobalSession) +{ + if(apiVersion != 0) + return SLANG_E_NOT_IMPLEMENTED; + + Slang::Session* globalSession = new Slang::Session(); + Slang::ComPtr<slang::IGlobalSession> result(Slang::asExternal(globalSession)); + *outGlobalSession = result.detach(); + return SLANG_OK; +} + SLANG_API void spDestroySession( SlangSession* session) { @@ -1698,35 +2049,6 @@ SLANG_API SlangResult spSessionCheckPassThroughSupport( return Slang::checkExternalCompilerSupport(s, Slang::PassThroughMode(passThrough)); } - -SLANG_API SlangLinkage* spCreateLinkage( - SlangSession* session) -{ - auto s = convert(session); - auto linkage = new Slang::Linkage(s); - return convert(linkage); -} - -SLANG_API void spDestroyLinkage( - SlangLinkage* linkage) -{ - if(!linkage) return; - auto lnk = convert(linkage); - delete lnk; -} - -SLANG_API SlangModule* spLoadModule( - SlangLinkage* linkage, - char const* moduleName) -{ - if(!linkage) return nullptr; - auto lnk = convert(linkage); - - auto mod = lnk->loadModule(moduleName); - return convert(mod); -} - - SLANG_API SlangCompileRequest* spCreateCompileRequest( SlangSession* session) { @@ -1839,7 +2161,7 @@ SLANG_API void spSetMatrixLayoutMode( { auto req = convert(request); auto linkage = req->getLinkage(); - linkage->defaultMatrixLayoutMode = Slang::MatrixLayoutMode(mode); + linkage->setMatrixLayoutMode(mode); } SLANG_API void spSetTargetMatrixLayoutMode( @@ -1932,7 +2254,7 @@ SLANG_API void spAddSearchPath( { auto req = convert(request); auto linkage = req->getLinkage(); - linkage->searchDirectories.searchDirectories.add(Slang::SearchDirectory(path)); + linkage->addSearchPath(path); } SLANG_API void spAddPreprocessorDefine( @@ -1942,7 +2264,7 @@ SLANG_API void spAddPreprocessorDefine( { auto req = convert(request); auto linkage = req->getLinkage(); - linkage->preprocessorDefinitions[key] = value; + linkage->addPreprocessorDefine(key, value); } SLANG_API char const* spGetDiagnosticOutput( @@ -2391,6 +2713,18 @@ SLANG_API void const* spGetCompileRequestCode( // Reflection API +SLANG_API SlangResult spCompileRequest_getProgram( + SlangCompileRequest* request, + slang::IProgram** outProgram) +{ + if( !request ) return SLANG_ERROR_INVALID_PARAMETER; + auto req = convert(request); + auto program = req->getSpecializedProgram(); + + *outProgram = Slang::ComPtr<slang::IProgram>(program).detach(); + return SLANG_OK; +} + SLANG_API SlangReflection* spGetReflection( SlangCompileRequest* request) { diff --git a/source/slang/slang.natvis b/source/slang/slang.natvis index f19ed925b..0bf32c5e7 100644 --- a/source/slang/slang.natvis +++ b/source/slang/slang.natvis @@ -17,16 +17,10 @@ <ExpandedItem>decl ? ($T1*)(decl) : ($T1*)0</ExpandedItem> <Item Name="[Substitutions]:">"========================="</Item> <LinkedListItems> - <HeadPointer>substitutions.genericSubstitutions.pointer</HeadPointer> + <HeadPointer>substitutions.substitutions.pointer</HeadPointer> <NextPointer>outer.pointer</NextPointer> <ValueNode>this</ValueNode> </LinkedListItems> - <LinkedListItems> - <HeadPointer>substitutions.globalGenParamSubstitutions.pointer</HeadPointer> - <NextPointer>outer.pointer</NextPointer> - <ValueNode>this</ValueNode> - </LinkedListItems> - <Item Name ="thisSubst">substitutions.thisTypeSubstitution</Item> </Expand> </Type> <Type Name="Slang::DeclRefBase"> @@ -66,10 +60,10 @@ <DisplayString>FuncDecl {nameAndLoc}</DisplayString> </Type> <Type Name="Slang::Name"> - <DisplayString>{{name={(char*)(text.buffer.pointer+1), s}}}</DisplayString> + <DisplayString>{{name={(char*)(text.m_buffer.pointer+1), s}}}</DisplayString> </Type> <Type Name="Slang::NameLoc"> - <DisplayString>{{name={(char*)((*name).text.buffer.pointer+1), s} loc={loc.raw}}}</DisplayString> + <DisplayString>{{name={(char*)((*name).text.m_buffer.pointer+1), s} loc={loc.raw}}}</DisplayString> </Type> <Type Name="Slang::IRWitnessTableEntry"> <Expand> diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index 7c6967f70..251b46b3e 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -297,7 +297,10 @@ <ClCompile Include="slang.cpp" /> </ItemGroup> <ItemGroup> - <None Include="slang.natvis" /> + <Natvis Include="..\core\core.natvis" /> + <Natvis Include="slang.natvis"> + <FileType>Document</FileType> + </Natvis> </ItemGroup> <ItemGroup> <CustomBuild Include="core.meta.slang"> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index bd9c2f297..3dd2c4a49 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Header Files"> @@ -394,4 +394,9 @@ <Filter>Source Files</Filter> </CustomBuild> </ItemGroup> + <ItemGroup> + <Natvis Include="..\core\core.natvis"> + <Filter>Source Files</Filter> + </Natvis> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/tests/compute/interface-shader-param-in-struct.slang b/tests/compute/interface-shader-param-in-struct.slang index 2ffc70c36..b033bd31c 100644 --- a/tests/compute/interface-shader-param-in-struct.slang +++ b/tests/compute/interface-shader-param-in-struct.slang @@ -49,6 +49,9 @@ cbuffer C IRandomNumberGenerationStrategy gStrategy; } +//TEST_INPUT: globalExistentialType MyStrategy +//TEST_INPUT:ubuffer(data=[1 2 4 8], stride=4):dxbinding(1),glbinding(1) + struct Stuff { IModifier modifier; @@ -107,21 +110,5 @@ struct MyModifier : IModifier } } -//TEST_INPUT: globalExistentialType MyStrategy //TEST_INPUT: entryPointExistentialType MyModifier - -// The concrete types we plug in for `gStrategy` and `modifier` -// have buffer resources in them, so we need to assign them -// data. The registers/bindings for these parameters will -// always come after all other shader parameters, and their -// relative order will match the relative order of their -// declarations in the global order that Slang uses for -// assigning bindings (all globals before all entry point parameters). -// -// Here's the data for `gStrategy`: -// -//TEST_INPUT:ubuffer(data=[1 2 4 8], stride=4):dxbinding(1),glbinding(1) -// -// Here's the data for `stuff.modifier`: -// //TEST_INPUT:ubuffer(data=[16 32 64 128], stride=4):dxbinding(2),glbinding(3) diff --git a/tests/compute/interface-shader-param3.slang b/tests/compute/interface-shader-param3.slang index 3c8b24be1..f4f45689c 100644 --- a/tests/compute/interface-shader-param3.slang +++ b/tests/compute/interface-shader-param3.slang @@ -45,17 +45,19 @@ int test( //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out RWStructuredBuffer<int> gOutputBuffer; -// Note: while we declare `gStrategy` here, there is no matching -// line providing input data at this point. That is partly to -// work around an apparent bug in the test runner, but it is also -// to reflect the fact that this declaration does not cause a -// constant buffer binding to be allocated, because just from -// the declaration of `gStrategy` we cannot tell whether a -// constant buffer binding is even needed (there might be no -// uniform/ordinary data). -// ConstantBuffer<IRandomNumberGenerationStrategy> gStrategy; +// Note: The current strategy we use for laying out shader +// parameters in the presence of existential/interface types +// is to always put global-scope parameters before any +// entry-point parameters. As a result we need to provide +// the buffer for the specialized version of `gStrategy` +// here, and we will go ahead and provide the concrete +// type argument at the same time. +// +//TEST_INPUT: globalExistentialType MyStrategy +//TEST_INPUT:cbuffer(data=[1 0 0 0], stride=4):dxbinding(0),glbinding(1) + [numthreads(4, 1, 1)] void computeMain( @@ -81,7 +83,7 @@ void computeMain( // // Here's the incantation to make the test runner fill in the constant buffer: // -//TEST_INPUT:cbuffer(data=[256 0 0 0 16 0 0 0], stride=4):dxbinding(0),glbinding(1) +//TEST_INPUT:cbuffer(data=[256 0 0 0 16 0 0 0], stride=4):dxbinding(1),glbinding(2) // // So, the value `256` will be used for `extra` and the value `16` // will be written to the first four bytes of the concrete value @@ -134,16 +136,4 @@ struct MyModifier : IModifier } } -//TEST_INPUT: globalExistentialType MyStrategy //TEST_INPUT: entryPointExistentialType MyModifier - -// Once the concrete types are plugged in, the compiler can -// see that `gStrategy` needs a constant buffer register/binding. -// It will alocate the location for `gStrategy` after all other -// shader parameters, to ensure that different specializations -// of the same shader will agree on the locations for all the -// non-interface-type parameters. -// -//TEST_INPUT:cbuffer(data=[1 0 0 0], stride=4):dxbinding(1),glbinding(2) - - diff --git a/tests/compute/interface-shader-param4.slang b/tests/compute/interface-shader-param4.slang index 07c6951e0..51b6686ef 100644 --- a/tests/compute/interface-shader-param4.slang +++ b/tests/compute/interface-shader-param4.slang @@ -48,6 +48,18 @@ RWStructuredBuffer<int> gOutputBuffer; ConstantBuffer<IRandomNumberGenerationStrategy> gStrategy; +// The concrete types we plug in for `gStrategy` and `modifier` +// have buffer resources in them, so we need to assign them +// data. The registers/bindings for these parameters will +// always come after all other shader parameters in the same +// scope (global or entry-point). +// +// Here's the data for `gStrategy`: +// +//TEST_INPUT: globalExistentialType MyStrategy +//TEST_INPUT:ubuffer(data=[1 2 4 8], stride=4):dxbinding(1),glbinding(1) + + [numthreads(4, 1, 1)] void computeMain( @@ -112,21 +124,7 @@ struct MyModifier : IModifier } } -//TEST_INPUT: globalExistentialType MyStrategy -//TEST_INPUT: entryPointExistentialType MyModifier - -// The concrete types we plug in for `gStrategy` and `modifier` -// have buffer resources in them, so we need to assign them -// data. The registers/bindings for these parameters will -// always come after all other shader parameters, and their -// relative order will match the relative order of their -// declarations in the global order that Slang uses for -// assigning bindings (all globals before all entry point parameters). -// -// Here's the data for `gStrategy`: -// -//TEST_INPUT:ubuffer(data=[1 2 4 8], stride=4):dxbinding(1),glbinding(1) -// // Here's the data for `modifier`: // +//TEST_INPUT: entryPointExistentialType MyModifier //TEST_INPUT:ubuffer(data=[16 32 64 128], stride=4):dxbinding(2),glbinding(3) diff --git a/tests/reflection/parameter-block-explicit-space.slang.expected b/tests/reflection/parameter-block-explicit-space.slang.expected index 04093f4ca..f7944c644 100644 --- a/tests/reflection/parameter-block-explicit-space.slang.expected +++ b/tests/reflection/parameter-block-explicit-space.slang.expected @@ -7,7 +7,7 @@ standard output = { { "name": "a", "bindings": [ - {"kind": "constantBuffer", "space": 2, "index": 0, "count": 0}, + {"kind": "constantBuffer", "index": 0, "count": 0}, {"kind": "registerSpace", "index": 2} ], "type": { @@ -58,7 +58,7 @@ standard output = { { "name": "b", "bindings": [ - {"kind": "constantBuffer", "space": 3, "index": 0, "count": 0}, + {"kind": "constantBuffer", "index": 0, "count": 0}, {"kind": "registerSpace", "index": 3} ], "type": { |
