diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2019-06-19 07:23:49 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-06-19 07:23:49 -0700 |
| commit | 48ae5496516878768d7de241b9b7fbba91fbaa74 (patch) | |
| tree | 0579405dcca82fa4a7296efea5c5e9bc963f7495 /source | |
| parent | 7c9298d8b10b5f4e69e24e3eb933e93e0d92fc37 (diff) | |
Start exposing a new COM-lite API (#987)
* Start exposing a new COM-lite API
This change is mostly about exposing a new API to the Slang compiler that allows more fine-grained control over the compilation flow. The basic concepts in the new API are:
* An `IGlobalSession` is the granularity at which we load/parse the Slang stdlib, and therefore gives applications a way to amortize startup cost for the library across multiple compiles. This is a concept that might be able to go away in a future version of Slang.
* An `ISession` owns all the code that gets loaded/compiled/generated. Any `import`ed modules are shared across everything in a session (we don't re-parse/-check the code when we see another `import` for the same module). Any generic- or interface-based code in the session can be specialized using types from the same session (but not necessarily across sessions).
* An `IModule` is the unit of code loading and scoping. It doesn't expose any API in this change, but would be the right scope for looking up types or entry points by name.
* An `IProgram` is a "linked" combination of modules and entry points from which code can be generated and reflection information queried.
This change re-uses the existing reflection API types, rather than introduce a new API that duplicates that functionality. That will probably change in a future revision.
There are two major pieces of functionality added here that aren't related to the new API:
* We now have an API concept of "entry point groups" which are one or more entry points that are intended to be used together so that they need to have non-overlapping parameters. For now this is being used to handle "hit groups" and local root signatures for ray tracing, but I'm not sure this is a concept we will keep in the long run.
* We have a very special-case (client-application-specific) flag that ascribes special meaning to the `shared` keyword, so that it can be attached to global parameters to indicate that they are actually to be part of the local root signature rather than the global one for DXR.
None of the API design (including naming) here is finalized; the only reason to check in the changes at this point to avoid having a long-running branch that leads to merge pain. Clients should *not* try to depend on the new API just yet, since it is still a work in progress.
* fixup: clang warning
* fixup: try to detect clang C++11 support
* fixup
* fixup
* fixup
* fixup
* fixup: review feedback
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/core.vcxproj | 4 | ||||
| -rw-r--r-- | source/core/slang-writer.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-check.cpp | 300 | ||||
| -rw-r--r-- | source/slang/slang-compiler.cpp | 87 | ||||
| -rw-r--r-- | source/slang/slang-compiler.h | 261 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-diagnostics.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 50 | ||||
| -rw-r--r-- | source/slang/slang-file-system.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 40 | ||||
| -rw-r--r-- | source/slang/slang-parameter-binding.cpp | 276 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 3 | ||||
| -rw-r--r-- | source/slang/slang-reflection.cpp | 131 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.cpp | 91 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.h | 40 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 464 | ||||
| -rw-r--r-- | source/slang/slang.natvis | 12 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 5 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 7 |
19 files changed, 1539 insertions, 248 deletions
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 |
