diff options
| -rw-r--r-- | slang.h | 1 | ||||
| -rw-r--r-- | source/core/smart-pointer.h | 2 | ||||
| -rw-r--r-- | source/slang/compiled-program.h | 13 | ||||
| -rw-r--r-- | source/slang/compiler.cpp | 28 | ||||
| -rw-r--r-- | source/slang/compiler.h | 8 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 130 | ||||
| -rw-r--r-- | source/slang/lower.cpp | 136 | ||||
| -rw-r--r-- | source/slang/modifier-defs.h | 1 | ||||
| -rw-r--r-- | source/slang/options.cpp | 2 | ||||
| -rw-r--r-- | source/slang/parameter-binding.cpp | 95 | ||||
| -rw-r--r-- | source/slang/reflection.cpp | 11 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.cpp | 29 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 56 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 1 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 1 | ||||
| -rw-r--r-- | source/slang/syntax-visitors.h | 1 | ||||
| -rw-r--r-- | source/slang/type-layout.cpp | 13 | ||||
| -rw-r--r-- | source/slang/type-layout.h | 7 | ||||
| -rw-r--r-- | tests/preprocessor/import.hlsl | 2 | ||||
| -rw-r--r-- | tests/reflection/cross-compile.slang | 23 | ||||
| -rw-r--r-- | tests/reflection/cross-compile.slang.expected | 48 | ||||
| -rw-r--r-- | tests/render/cross-compile0.hlsl | 4 | ||||
| -rw-r--r-- | tests/render/imported-parameters.hlsl | 4 | ||||
| -rw-r--r-- | tests/render/pound-import.hlsl | 4 |
24 files changed, 500 insertions, 120 deletions
@@ -66,6 +66,7 @@ extern "C" enum { SLANG_TARGET_UNKNOWN, + SLANG_TARGET_NONE, SLANG_GLSL, SLANG_GLSL_VULKAN, SLANG_GLSL_VULKAN_ONE_DESC, diff --git a/source/core/smart-pointer.h b/source/core/smart-pointer.h index 19ddde931..fea149e06 100644 --- a/source/core/smart-pointer.h +++ b/source/core/smart-pointer.h @@ -163,7 +163,7 @@ namespace Slang ~RefPtr() { - releaseReference(pointer); + releaseReference((Slang::RefObject*) pointer); } T& operator*() const diff --git a/source/slang/compiled-program.h b/source/slang/compiled-program.h deleted file mode 100644 index 7f5b674a0..000000000 --- a/source/slang/compiled-program.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef BAKER_SL_COMPILED_PROGRAM_H -#define BAKER_SL_COMPILED_PROGRAM_H - -#include "../core/basic.h" -#include "diagnostics.h" -#include "syntax.h" -#include "type-layout.h" - -namespace Slang -{ -} - -#endif
\ No newline at end of file diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp index 19623a7aa..2f6204eb2 100644 --- a/source/slang/compiler.cpp +++ b/source/slang/compiler.cpp @@ -461,6 +461,10 @@ namespace Slang } break; + case CodeGenTarget::None: + // The user requested no output + break; + // Note(tfoley): We currently hit this case when compiling the stdlib case CodeGenTarget::Unknown: break; @@ -520,17 +524,9 @@ namespace Slang void generateOutput( CompileRequest* compileRequest) { - switch (compileRequest->Target) + // Allow for an "extra" target to verride things first. + switch (compileRequest->extraTarget) { - default: - // For most targets, we will do things per-translation-unit - for( auto translationUnit : compileRequest->translationUnits ) - { - TranslationUnitResult translationUnitResult = emitTranslationUnit(translationUnit.Ptr()); - translationUnit->result = translationUnitResult; - } - break; - case CodeGenTarget::ReflectionJSON: { String reflectionJSON = emitReflectionJSON(compileRequest->layout.Ptr()); @@ -538,8 +534,20 @@ namespace Slang // HACK(tfoley): just print it out since that is what people probably expect. // TODO: need a way to control where output gets routed across all possible targets. fprintf(stdout, "%s", reflectionJSON.begin()); + + return; } break; + + default: + break; + } + + // For most targets, we will do things per-translation-unit + for( auto translationUnit : compileRequest->translationUnits ) + { + TranslationUnitResult translationUnitResult = emitTranslationUnit(translationUnit.Ptr()); + translationUnit->result = translationUnitResult; } } } diff --git a/source/slang/compiler.h b/source/slang/compiler.h index b3bcd7ba1..cb7eb6265 100644 --- a/source/slang/compiler.h +++ b/source/slang/compiler.h @@ -3,11 +3,9 @@ #include "../core/basic.h" -#include "compiled-program.h" #include "diagnostics.h" #include "profile.h" #include "syntax.h" -#include "type-layout.h" #include "../../slang.h" @@ -15,6 +13,7 @@ namespace Slang { struct IncludeHandler; class CompileRequest; + class ProgramLayout; enum class CompilerMode { @@ -37,6 +36,7 @@ namespace Slang enum class CodeGenTarget { Unknown = SLANG_TARGET_UNKNOWN, + None = SLANG_TARGET_NONE, GLSL = SLANG_GLSL, GLSL_Vulkan = SLANG_GLSL_VULKAN, GLSL_Vulkan_OneDesc = SLANG_GLSL_VULKAN_ONE_DESC, @@ -170,6 +170,10 @@ namespace Slang // What target language are we compiling to? CodeGenTarget Target = CodeGenTarget::Unknown; + // An "extra" target that might override the first one + // when it comes to deciding output format, etc. + CodeGenTarget extraTarget = CodeGenTarget::Unknown; + // Directories to search for `#include` files or `import`ed modules List<SearchDirectory> searchDirectories; diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index c135090c1..155e1e39a 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -48,6 +48,10 @@ struct SharedEmitContext StructTypeLayout* globalStructLayout; ProgramLayout* programLayout; + + ProgramSyntaxNode* program; + + bool needHackSamplerForTexelFetch = false; }; struct EmitContext @@ -1758,6 +1762,66 @@ struct EmitVisitor } break; + case 'P': + { + // Okay, we need a collosal hack to deal with the fact that GLSL `texelFetch()` + // for Vulkan seems to be completely broken by design. It's signature wants + // a `sampler2D` for consistency with its peers, but the actual SPIR-V operation + // ignores the sampler paart of it, and just used the `texture2D` part. + // + // The HLSL equivalent (e.g., `Texture2D.Load()`) doesn't provide a sampler + // argument, so we seemingly need to conjure one out of thin air. :( + // + // We are going to hack this *hard* for now. + + // Try to find a suitable sampler-type shader parameter in the global scope + // (fingers crossed) + RefPtr<VarDeclBase> samplerVar; + for (auto d : context->shared->program->Members) + { + if (auto varDecl = d.As<VarDeclBase>()) + { + if (auto samplerType = varDecl->Type.type->As<SamplerStateType>()) + { + samplerVar = varDecl; + break; + } + } + } + + if (auto memberExpr = callExpr->FunctionExpr.As<MemberExpressionSyntaxNode>()) + { + auto base = memberExpr->BaseExpression; + if (auto baseTextureType = base->Type->As<TextureType>()) + { + emitGLSLTextureOrTextureSamplerType(baseTextureType, "sampler"); + Emit("("); + EmitExpr(memberExpr->BaseExpression); + Emit(","); + if (samplerVar) + { + EmitDeclRef(makeDeclRef(samplerVar.Ptr())); + } + else + { + Emit("SLANG_hack_samplerForTexelFetch"); + context->shared->needHackSamplerForTexelFetch = true; + } + Emit(")"); + } + else + { + assert(!"unexpected"); + } + + } + else + { + assert(!"unexpected"); + } + } + break; + default: assert(!"unexpected"); break; @@ -2095,6 +2159,11 @@ struct EmitVisitor void EmitStmt(RefPtr<StatementSyntaxNode> stmt) { + // TODO(tfoley): this shouldn't occur, but sometimes + // lowering will get confused by an empty function body... + if (!stmt) + return; + // Try to ensure that debugging can find the right location advanceToSourceLocation(stmt->Position); @@ -2476,6 +2545,9 @@ struct EmitVisitor #define CASE(TYPE, KEYWORD) \ else if(auto mod_##TYPE = mod.As<TYPE>()) Emit(#KEYWORD " ") + #define CASE2(TYPE, HLSL_NAME, GLSL_NAME) \ + else if(auto mod_##TYPE = mod.As<TYPE>()) Emit((context->shared->target == CodeGenTarget::GLSL) ? GLSL_NAME : HLSL_NAME) + CASE(RowMajorLayoutModifier, row_major); CASE(ColumnMajorLayoutModifier, column_major); CASE(HLSLNoInterpolationModifier, nointerpolation); @@ -2502,6 +2574,7 @@ struct EmitVisitor CASE(ConstModifier, const); #undef CASE + #undef CASE2 else if (auto staticModifier = mod.As<HLSLStaticModifier>()) { @@ -2949,7 +3022,8 @@ struct EmitVisitor } void emitGLSLLayoutQualifiers( - RefPtr<VarLayout> layout) + RefPtr<VarLayout> layout, + LayoutResourceKind filter = LayoutResourceKind::None) { if(!layout) return; @@ -2964,6 +3038,13 @@ struct EmitVisitor for( auto info : layout->resourceInfos ) { + // Skip info that doesn't match our filter + if (filter != LayoutResourceKind::None + && filter != info.kind) + { + continue; + } + emitGLSLLayoutQualifier(info); } } @@ -3095,7 +3176,33 @@ struct EmitVisitor return; } - emitGLSLLayoutQualifiers(layout); + + if (context->shared->target == CodeGenTarget::GLSL) + { + if (decl->HasModifier<InModifier>()) + { + emitGLSLLayoutQualifiers(layout, LayoutResourceKind::VertexInput); + } + else if (decl->HasModifier<OutModifier>()) + { + emitGLSLLayoutQualifiers(layout, LayoutResourceKind::FragmentOutput); + } + else + { + emitGLSLLayoutQualifiers(layout); + } + + // If we have a uniform that wasn't tagged `uniform` in GLSL, then fix that here + if (layout + && !decl->HasModifier<HLSLUniformModifier>()) + { + if (layout->FindResourceInfo(LayoutResourceKind::Uniform) + || layout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot)) + { + Emit("uniform "); + } + } + } EmitVarDeclCommon(decl); @@ -3288,6 +3395,9 @@ String emitEntryPoint( // There may be global-scope modifiers that we should emit now visitor.emitGLSLPreprocessorDirectives(translationUnitSyntax); + String prefix = sharedContext.sb.ProduceString(); + sharedContext.sb.Clear(); + switch(target) { case CodeGenTarget::GLSL: @@ -3303,6 +3413,8 @@ String emitEntryPoint( auto lowered = lowerEntryPoint(entryPoint, programLayout, target); + sharedContext.program = lowered.program; + visitor.EmitDeclsInContainer(lowered.program.Ptr()); #if 0 @@ -3324,7 +3436,19 @@ String emitEntryPoint( String code = sharedContext.sb.ProduceString(); - return code; + StringBuilder finalResultBuilder; + finalResultBuilder << prefix; + + if (sharedContext.needHackSamplerForTexelFetch) + { + finalResultBuilder << "layout(set = 0, binding = 0) uniform sampler SLANG_hack_samplerForTexelFetch;\n"; + } + + finalResultBuilder << code; + + String finalResult = finalResultBuilder.ProduceString(); + + return finalResult; } } // namespace Slang diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp index 0bb047791..165812fae 100644 --- a/source/slang/lower.cpp +++ b/source/slang/lower.cpp @@ -1,6 +1,7 @@ // lower.cpp #include "lower.h" +#include "type-layout.h" #include "visitor.h" namespace Slang @@ -193,6 +194,8 @@ public: struct SharedLoweringContext { + CompileRequest* compileRequest; + ProgramLayout* programLayout; // The target we are going to generate code for. @@ -1371,6 +1374,23 @@ struct LoweringVisitor return loweredDecl; } + SourceLanguage getSourceLanguage(ProgramSyntaxNode* moduleDecl) + { + for (auto translationUnit : shared->compileRequest->translationUnits) + { + if (moduleDecl == translationUnit->SyntaxNode) + return translationUnit->sourceLanguage; + } + + for (auto loadedModuleDecl : shared->compileRequest->loadedModulesList) + { + if (moduleDecl == loadedModuleDecl) + return SourceLanguage::Slang; + } + + return SourceLanguage::Unknown; + } + RefPtr<VarDeclBase> visitVariable( Variable* decl) { @@ -1525,38 +1545,93 @@ struct LoweringVisitor type = arrayType; } - // TODO: if we are declaring an SOA-ized array, - // this is where those array dimensions would need - // to be tacked on. + // We need to create a reference to the global-scope declaration + // of the proper GLSL input/output variable. This might + // be a user-defined input/output, or a system-defined `gl_` one. + RefPtr<ExpressionSyntaxNode> globalVarExpr; - RefPtr<Variable> globalVarDecl = new Variable(); - globalVarDecl->Name.Content = info.name; - globalVarDecl->Type.type = type; + // Handle system-value inputs/outputs + assert(varLayout); + auto systemValueSemantic = varLayout->systemValueSemantic; + if (systemValueSemantic.Length() != 0) + { + auto ns = systemValueSemantic.ToLower(); - ensureDeclHasAValidName(globalVarDecl); + if (ns == "sv_target") + { + // Note: we do *not* need to generate some kind of `gl_` + // builtin for fragment-shader outputs: they are just + // ordinary `out` variables, with ordinary `location`s, + // as far as GLSL is concerned. + } + else if (ns == "sv_position") + { + RefPtr<VarExpressionSyntaxNode> globalVarRef = new VarExpressionSyntaxNode(); + globalVarRef->name = "gl_Position"; + globalVarExpr = globalVarRef; + } + else + { + assert(!"unhandled"); + } + } - addMember(shared->loweredProgram, globalVarDecl); + // If we didn't match some kind of builtin input/output, + // then declare a user input/output variable instead + if (!globalVarExpr) + { + RefPtr<Variable> globalVarDecl = new Variable(); + globalVarDecl->Name.Content = info.name; + globalVarDecl->Type.type = type; - // Add the layout information - RefPtr<ComputedLayoutModifier> modifier = new ComputedLayoutModifier(); - modifier->layout = varLayout; - addModifier(globalVarDecl, modifier); + ensureDeclHasAValidName(globalVarDecl); - // Need to generate an assignment in the right direction. + addMember(shared->loweredProgram, globalVarDecl); + + // Add the layout information + RefPtr<ComputedLayoutModifier> modifier = new ComputedLayoutModifier(); + modifier->layout = varLayout; + addModifier(globalVarDecl, modifier); + + // Add appropriate in/out modifier + switch (info.direction) + { + case VaryingParameterDirection::Input: + addModifier(globalVarDecl, new InModifier()); + break; + + case VaryingParameterDirection::Output: + addModifier(globalVarDecl, new OutModifier()); + break; + } + + + RefPtr<VarExpressionSyntaxNode> globalVarRef = new VarExpressionSyntaxNode(); + globalVarRef->Position = globalVarDecl->Position; + globalVarRef->declRef = makeDeclRef(globalVarDecl.Ptr()); + globalVarRef->name = globalVarDecl->getName(); + + globalVarExpr = globalVarRef; + } + + // TODO: if we are declaring an SOA-ized array, + // this is where those array dimensions would need + // to be tacked on. // - // TODO: for now I am just dealing with input: + // That is, this logic should be getting collected into a loop, + // and so we need to have a loop variable we can use to + // index into the two different expressions. + + // Need to generate an assignment in the right direction. switch (info.direction) { case VaryingParameterDirection::Input: - addModifier(globalVarDecl, new InModifier()); - assign(varExpr, globalVarDecl); + assign(varExpr, globalVarExpr); break; case VaryingParameterDirection::Output: - addModifier(globalVarDecl, new OutModifier()); - - assign(globalVarDecl, varExpr); + assign(globalVarExpr, varExpr); break; } } @@ -1683,6 +1758,18 @@ struct LoweringVisitor info.name = name; info.direction = direction; + // Ensure that we don't get name collisions on `inout` variables + switch (direction) + { + case VaryingParameterDirection::Input: + info.name = "SLANG_in_" + name; + break; + + case VaryingParameterDirection::Output: + info.name = "SLANG_out_" + name; + break; + } + lowerShaderParameterToGLSLGLobalsRec( info, localVarDecl->getType(), @@ -1713,10 +1800,14 @@ struct LoweringVisitor if (loweredEntryPointFunc->getName() == "main") loweredEntryPointFunc->Name.Content = "main_"; + RefPtr<BlockStmt> bodyStmt = new BlockStmt(); + bodyStmt->scopeDecl = new ScopeDecl(); + // We will want to generate declarations into the body of our new `main()` LoweringVisitor subVisitor = *this; subVisitor.isBuildingStmt = true; subVisitor.stmtBeingBuilt = nullptr; + subVisitor.parentDecl = bodyStmt->scopeDecl; // The parameters of the entry-point function will be translated to // both a local variable (for passing to/from the entry point func), @@ -1769,7 +1860,7 @@ struct LoweringVisitor { resultVarDecl = new Variable(); resultVarDecl->Position = loweredEntryPointFunc->Position; - resultVarDecl->Name.Content = "_main_result"; + resultVarDecl->Name.Content = "main_result"; resultVarDecl->Type = TypeExp(loweredEntryPointFunc->ReturnType); ensureDeclHasAValidName(resultVarDecl); @@ -1838,7 +1929,9 @@ struct LoweringVisitor VaryingParameterDirection::Output); } - mainDecl->Body = subVisitor.stmtBeingBuilt; + bodyStmt->body = subVisitor.stmtBeingBuilt; + + mainDecl->Body = bodyStmt; // Once we are done building the body, we append our new declaration to the program. @@ -2003,6 +2096,7 @@ LoweredEntryPoint lowerEntryPoint( CodeGenTarget target) { SharedLoweringContext sharedContext; + sharedContext.compileRequest = entryPoint->compileRequest; sharedContext.programLayout = programLayout; sharedContext.target = target; diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h index e50288600..083da79f0 100644 --- a/source/slang/modifier-defs.h +++ b/source/slang/modifier-defs.h @@ -6,7 +6,6 @@ #define SIMPLE_MODIFIER(NAME) \ SIMPLE_SYNTAX_CLASS(NAME##Modifier, Modifier) -SIMPLE_MODIFIER(Uniform); SIMPLE_MODIFIER(In); SIMPLE_MODIFIER(Out); SIMPLE_MODIFIER(Const); diff --git a/source/slang/options.cpp b/source/slang/options.cpp index db949e78d..bb292a4dc 100644 --- a/source/slang/options.cpp +++ b/source/slang/options.cpp @@ -5,6 +5,7 @@ #include "../../slang.h" +#include "compiler.h" #include "profile.h" #include <assert.h> @@ -229,6 +230,7 @@ struct OptionsParser CASE(spirv, SPIRV); CASE(spirv-assembly, SPIRV_ASM); + CASE(none, TARGET_NONE); #undef CASE diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp index 5811fd9fa..0683c7f8c 100644 --- a/source/slang/parameter-binding.cpp +++ b/source/slang/parameter-binding.cpp @@ -137,9 +137,6 @@ struct SharedParameterBindingContext // The program layout we are trying to construct RefPtr<ProgramLayout> programLayout; - // The source language we are trying to use - SourceLanguage sourceLanguage; - // Information on what ranges of "registers" have already // been claimed, for each resource type UsedRanges usedResourceRanges[kLayoutResourceKindCount]; @@ -160,6 +157,9 @@ struct ParameterBindingContext // What stage (if any) are we compiling for? Stage stage; + + // The source language we are trying to use + SourceLanguage sourceLanguage; }; struct LayoutSemanticInfo @@ -398,7 +398,7 @@ getTypeLayoutForGlobalShaderParameter( ParameterBindingContext* context, VarDeclBase* varDecl) { - switch( context->shared->sourceLanguage ) + switch( context->sourceLanguage ) { case SourceLanguage::Slang: case SourceLanguage::HLSL: @@ -788,6 +788,7 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( ParameterBindingContext* context, RefPtr<ExpressionType> type, EntryPointParameterState const& inState, + RefPtr<VarLayout> varLayout, int semanticSlotCount = 1) { EntryPointParameterState state = inState; @@ -817,6 +818,13 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( } } + // Remember the system-value semantic so that we can query it later + if (varLayout) + { + varLayout->systemValueSemantic = semanticName; + varLayout->systemValueSemanticIndex = semanticIndex; + } + // TODO: add some kind of usage information for system input/output } else @@ -847,13 +855,15 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( static RefPtr<TypeLayout> processEntryPointParameter( ParameterBindingContext* context, RefPtr<ExpressionType> type, - EntryPointParameterState const& state); + EntryPointParameterState const& state, + RefPtr<VarLayout> varLayout); static RefPtr<TypeLayout> processEntryPointParameterWithPossibleSemantic( ParameterBindingContext* context, Decl* declForSemantic, RefPtr<ExpressionType> type, - EntryPointParameterState const& state) + EntryPointParameterState const& state, + RefPtr<VarLayout> varLayout) { // If there is no explicit semantic already in effect, *and* we find an explicit // semantic on the associated declaration, then we'll use it. @@ -868,7 +878,7 @@ static RefPtr<TypeLayout> processEntryPointParameterWithPossibleSemantic( subState.optSemanticName = &semanticInfo.name; subState.ioSemanticIndex = &semanticIndex; - processEntryPointParameter(context, type, subState); + processEntryPointParameter(context, type, subState, varLayout); } } @@ -876,29 +886,30 @@ static RefPtr<TypeLayout> processEntryPointParameterWithPossibleSemantic( // *or* we couldn't find an explicit semantic to apply on the given // declaration, so we will just recursive with whatever we have at // the moment. - return processEntryPointParameter(context, type, state); + return processEntryPointParameter(context, type, state, varLayout); } static RefPtr<TypeLayout> processEntryPointParameter( ParameterBindingContext* context, RefPtr<ExpressionType> type, - EntryPointParameterState const& state) + EntryPointParameterState const& state, + RefPtr<VarLayout> varLayout) { // Scalar and vector types are treated as outputs directly if(auto basicType = type->As<BasicExpressionType>()) { - return processSimpleEntryPointParameter(context, basicType, state); + return processSimpleEntryPointParameter(context, basicType, state, varLayout); } else if(auto vectorType = type->As<VectorExpressionType>()) { - return processSimpleEntryPointParameter(context, vectorType, state); + return processSimpleEntryPointParameter(context, vectorType, state, varLayout); } // A matrix is processed as if it was an array of rows else if( auto matrixType = type->As<MatrixExpressionType>() ) { auto rowCount = GetIntVal(matrixType->getRowCount()); - return processSimpleEntryPointParameter(context, matrixType, state, (int) rowCount); + return processSimpleEntryPointParameter(context, matrixType, state, varLayout, (int) rowCount); } else if( auto arrayType = type->As<ArrayExpressionType>() ) { @@ -908,13 +919,13 @@ static RefPtr<TypeLayout> processEntryPointParameter( auto elementCount = (UInt) GetIntVal(arrayType->ArrayLength); // We use the first element to derive the layout for the element type - auto elementTypeLayout = processEntryPointParameter(context, arrayType->BaseType, state); + auto elementTypeLayout = processEntryPointParameter(context, arrayType->BaseType, state, varLayout); // We still walk over subsequent elements to make sure they consume resources // as needed for( UInt ii = 1; ii < elementCount; ++ii ) { - processEntryPointParameter(context, arrayType->BaseType, state); + processEntryPointParameter(context, arrayType->BaseType, state, nullptr); } RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); @@ -946,14 +957,16 @@ static RefPtr<TypeLayout> processEntryPointParameter( // Need to recursively walk the fields of the structure now... for( auto field : GetFields(structDeclRef) ) { + RefPtr<VarLayout> fieldVarLayout = new VarLayout(); + fieldVarLayout->varDecl = field; + auto fieldTypeLayout = processEntryPointParameterWithPossibleSemantic( context, field.getDecl(), GetType(field), - state); + state, + fieldVarLayout); - RefPtr<VarLayout> fieldVarLayout = new VarLayout(); - fieldVarLayout->varDecl = field; fieldVarLayout->typeLayout = fieldTypeLayout; for (auto rr : fieldTypeLayout->resourceInfos) @@ -1045,7 +1058,7 @@ static void collectEntryPointParameters( // We have an entry-point parameter, and need to figure out what to do with it. // TODO: need to handle `uniform`-qualified parameters here - if (paramDecl->HasModifier<UniformModifier>()) + if (paramDecl->HasModifier<HLSLUniformModifier>()) continue; state.directionMask = 0; @@ -1062,14 +1075,16 @@ static void collectEntryPointParameters( state.directionMask |= kEntryPointParameterDirection_Output; } + RefPtr<VarLayout> paramVarLayout = new VarLayout(); + paramVarLayout->varDecl = makeDeclRef(paramDecl.Ptr()); + auto paramTypeLayout = processEntryPointParameterWithPossibleSemantic( context, paramDecl.Ptr(), paramDecl->Type.type, - state); + state, + paramVarLayout); - RefPtr<VarLayout> paramVarLayout = new VarLayout(); - paramVarLayout->varDecl = makeDeclRef(paramDecl.Ptr()); paramVarLayout->typeLayout = paramTypeLayout; for (auto rr : paramTypeLayout->resourceInfos) @@ -1089,13 +1104,15 @@ static void collectEntryPointParameters( { state.directionMask = kEntryPointParameterDirection_Output; + RefPtr<VarLayout> resultLayout = new VarLayout(); + auto resultTypeLayout = processEntryPointParameterWithPossibleSemantic( context, entryPointFuncDecl, resultType, - state); + state, + resultLayout); - RefPtr<VarLayout> resultLayout = new VarLayout(); resultLayout->typeLayout = resultTypeLayout; for (auto rr : resultTypeLayout->resourceInfos) @@ -1167,6 +1184,7 @@ static void collectParameters( for( auto& translationUnit : request->translationUnits ) { context->stage = inferStageForTranslationUnit(translationUnit.Ptr()); + context->sourceLanguage = translationUnit->sourceLanguage; // First look at global-scope parameters collectGlobalScopeParameters(context, translationUnit->SyntaxNode.Ptr()); @@ -1189,31 +1207,13 @@ static void collectParameters( void generateParameterBindings( CompileRequest* request) { - // TODO: infer a language or set of language rules to use based on the - // source files and entry points given - auto language = SourceLanguage::Unknown; - for( auto& translationUnit : request->translationUnits ) - { - auto translationUnitLanguage = translationUnit->sourceLanguage; - if( language == SourceLanguage::Unknown ) - { - language = translationUnitLanguage; - } - else if( language == translationUnitLanguage ) - { - // same language: nothing to do... - } - else - { - // mismatch! - // TODO(tfoley): emit a diagnostic - } - } + // Try to find rules based on the selected code-generation target + auto rules = GetLayoutRulesFamilyImpl(request->Target); - // TODO(tfoley): We should really be picking layout rules - // based on the *target* language, and not the source... - auto rules = GetLayoutRulesFamilyImpl(language); - assert(rules); + // If there was no target, or there are no rules for the target, + // then bail out here. + if (!rules) + return; RefPtr<ProgramLayout> programLayout = new ProgramLayout; @@ -1222,7 +1222,6 @@ void generateParameterBindings( SharedParameterBindingContext sharedContext; sharedContext.defaultLayoutRules = rules; sharedContext.programLayout = programLayout; - sharedContext.sourceLanguage = language; // Create a sub-context to collect parameters that get // declared into the global scope diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp index d62da70c1..eb27df398 100644 --- a/source/slang/reflection.cpp +++ b/source/slang/reflection.cpp @@ -112,7 +112,7 @@ SLANG_API SlangTypeKind spReflectionType_GetKind(SlangReflectionType* inType) { return SLANG_TYPE_KIND_SAMPLER_STATE; } - else if (auto textureType = type->As<TextureType>()) + else if (auto textureType = type->As<TextureTypeBase>()) { return SLANG_TYPE_KIND_RESOURCE; } @@ -326,7 +326,7 @@ SLANG_API SlangResourceShape spReflectionType_GetResourceShape(SlangReflectionTy type = arrayType->BaseType.Ptr(); } - if(auto textureType = type->As<TextureType>()) + if(auto textureType = type->As<TextureTypeBase>()) { return textureType->getShape(); } @@ -363,7 +363,7 @@ SLANG_API SlangResourceAccess spReflectionType_GetResourceAccess(SlangReflection type = arrayType->BaseType.Ptr(); } - if(auto textureType = type->As<TextureType>()) + if(auto textureType = type->As<TextureTypeBase>()) { return textureType->getAccess(); } @@ -385,6 +385,9 @@ SLANG_API SlangResourceAccess spReflectionType_GetResourceAccess(SlangReflection CASE(HLSLByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ); CASE(HLSLRWByteAddressBufferType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); CASE(UntypedBufferResourceType, SLANG_BYTE_ADDRESS_BUFFER, SLANG_RESOURCE_ACCESS_READ); + + // This isn't entirely accurate, but I can live with it for now + CASE(GLSLShaderStorageBufferType, SLANG_STRUCTURED_BUFFER, SLANG_RESOURCE_ACCESS_READ_WRITE); #undef CASE return SLANG_RESOURCE_ACCESS_NONE; @@ -400,7 +403,7 @@ SLANG_API SlangReflectionType* spReflectionType_GetResourceResultType(SlangRefle type = arrayType->BaseType.Ptr(); } - if (auto textureType = type->As<TextureType>()) + if (auto textureType = type->As<TextureTypeBase>()) { return convert(textureType->elementType.Ptr()); } diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp index e2d5829b4..23d66201a 100644 --- a/source/slang/slang-stdlib.cpp +++ b/source/slang/slang-stdlib.cpp @@ -1431,6 +1431,22 @@ namespace Slang { int loadCoordCount = kBaseTextureTypes[tt].coordCount + isArray + (isMultisample?0:1); + // When translating to GLSL, we need to break apart the `location` argument. + // + // TODO: this should realy be handled by having this member actually get lowered! + int glslLoadCoordCount = kBaseTextureTypes[tt].coordCount + isArray; + static const char* kGLSLLoadCoordsSwizzle[] = { "", "", "x", "xy", "xyz", "xyzw" }; + static const char* kGLSLLoadLODSwizzle[] = { "", "", "y", "z", "w", "error" }; + + if (isMultisample) + { + sb << "__intrinsic(glsl, \"texelFetch($P, $0, $1)\")\n"; + } + else + { + sb << "__intrinsic(glsl, \"texelFetch($P, ($0)." << kGLSLLoadCoordsSwizzle[loadCoordCount] << ", ($0)." << kGLSLLoadLODSwizzle[loadCoordCount] << ")\")\n"; + } + sb << "__intrinsic\n"; sb << "T Load("; sb << "int" << loadCoordCount << " location"; if(isMultisample) @@ -1439,6 +1455,15 @@ namespace Slang } sb << ");\n"; + if (isMultisample) + { + sb << "__intrinsic(glsl, \"texelFetchOffset($P, $0, $1, $2)\")\n"; + } + else + { + sb << "__intrinsic(glsl, \"texelFetch($P, ($0)." << kGLSLLoadCoordsSwizzle[loadCoordCount] << ", ($0)." << kGLSLLoadLODSwizzle[loadCoordCount] << ", $1)\")\n"; + } + sb << "__intrinsic\n"; sb << "T Load("; sb << "int" << loadCoordCount << " location"; if(isMultisample) @@ -1470,11 +1495,15 @@ namespace Slang { // `Sample()` + sb << "__intrinsic(glsl, \"texture($p, $1)\")\n"; + sb << "__intrinsic\n"; sb << "T Sample(SamplerState s, "; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location);\n"; if( baseShape != TextureType::ShapeCube ) { + sb << "__intrinsic(glsl, \"textureOffset($p, $1)\")\n"; + sb << "__intrinsic\n"; sb << "T Sample(SamplerState s, "; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index e5bfcb923..af68060cd 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -179,6 +179,30 @@ void CompileRequest::checkAllTranslationUnits() checkTranslationUnit(translationUnit.Ptr()); } } +// Try to infer a single common source language for a request +static SourceLanguage inferSourceLanguage(CompileRequest* request) +{ + SourceLanguage language = SourceLanguage::Unknown; + for (auto& translationUnit : request->translationUnits) + { + // Allow any other language to overide Slang as a choice + if (language == SourceLanguage::Unknown + || language == SourceLanguage::Slang) + { + language = translationUnit->sourceLanguage; + } + else if (language == translationUnit->sourceLanguage) + { + // same language as we currently have, so keep going + } + else + { + // we found a mismatch, so inference fails + return SourceLanguage::Unknown; + } + } + return language; +} int CompileRequest::executeActionsInner() { @@ -197,6 +221,26 @@ int CompileRequest::executeActionsInner() } } + // If no code-generation target was specified, then try to infer one from the source language, + // just to make sure we can do something reasonable when `reflection-json` is specified + if (Target == CodeGenTarget::Unknown) + { + auto language = inferSourceLanguage(this); + switch (language) + { + case SourceLanguage::HLSL: + Target = CodeGenTarget::DXBytecodeAssembly; + break; + + case SourceLanguage::GLSL: + Target = CodeGenTarget::SPIRVAssembly; + break; + + default: + break; + } + } + #if 0 // If we are being asked to do pass-through, then we need to do that here... if (passThrough != PassThroughMode::None) @@ -628,7 +672,17 @@ SLANG_API void spSetCodeGenTarget( SlangCompileRequest* request, int target) { - REQ(request)->Target = (Slang::CodeGenTarget)target; + if (target == SLANG_REFLECTION_JSON) + { + // HACK: We special case this because reflection JSON is actually + // an additional output step that layers on top of an existing + // target + REQ(request)->extraTarget = Slang::CodeGenTarget::ReflectionJSON; + } + else + { + REQ(request)->Target = (Slang::CodeGenTarget)target; + } } SLANG_API void spSetPassThrough( diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index 441bc5e45..17fdea627 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -165,7 +165,6 @@ </ItemGroup> <ItemGroup> <ClInclude Include="..\..\slang.h" /> - <ClInclude Include="compiled-program.h" /> <ClInclude Include="compiler.h" /> <ClInclude Include="decl-defs.h" /> <ClInclude Include="diagnostic-defs.h" /> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 21b533a10..a276ae95b 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -4,7 +4,6 @@ <Natvis Include="slang.natvis" /> </ItemGroup> <ItemGroup> - <ClInclude Include="compiled-program.h" /> <ClInclude Include="compiler.h" /> <ClInclude Include="diagnostic-defs.h" /> <ClInclude Include="diagnostics.h" /> diff --git a/source/slang/syntax-visitors.h b/source/slang/syntax-visitors.h index 85a20a7cf..4ccc7fa0b 100644 --- a/source/slang/syntax-visitors.h +++ b/source/slang/syntax-visitors.h @@ -3,7 +3,6 @@ #include "diagnostics.h" #include "syntax.h" -#include "compiled-program.h" namespace Slang { diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp index 10c7327ce..31c8ed607 100644 --- a/source/slang/type-layout.cpp +++ b/source/slang/type-layout.cpp @@ -543,15 +543,18 @@ LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(LayoutRulesFamily rule) } } -LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(SourceLanguage language) +LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(CodeGenTarget target) { - switch (language) + switch (target) { - case SourceLanguage::Slang: - case SourceLanguage::HLSL: + case CodeGenTarget::HLSL: + case CodeGenTarget::DXBytecode: + case CodeGenTarget::DXBytecodeAssembly: return &kHLSLLayoutRulesFamilyImpl; - case SourceLanguage::GLSL: + case CodeGenTarget::GLSL: + case CodeGenTarget::SPIRV: + case CodeGenTarget::SPIRVAssembly: return &kGLSLLayoutRulesFamilyImpl; default: diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h index ce1f8864d..262a8c3b1 100644 --- a/source/slang/type-layout.h +++ b/source/slang/type-layout.h @@ -2,6 +2,7 @@ #define SLANG_TYPE_LAYOUT_H #include "../core/basic.h" +#include "compiler.h" #include "profile.h" #include "syntax.h" @@ -236,6 +237,10 @@ public: // Additional flags VarLayoutFlags flags = 0; + // System-value semantic (and index) if this is a system value + String systemValueSemantic; + int systemValueSemanticIndex; + // The start register(s) for any resources struct ResourceInfo { @@ -513,7 +518,7 @@ struct LayoutRulesFamilyImpl LayoutRulesImpl* GetLayoutRulesImpl(LayoutRule rule); LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(LayoutRulesFamily rule); -LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(SourceLanguage language); +LayoutRulesFamilyImpl* GetLayoutRulesFamilyImpl(CodeGenTarget target); SimpleLayoutInfo GetLayout(ExpressionType* type, LayoutRulesImpl* rules); diff --git a/tests/preprocessor/import.hlsl b/tests/preprocessor/import.hlsl index 486023678..f0618a667 100644 --- a/tests/preprocessor/import.hlsl +++ b/tests/preprocessor/import.hlsl @@ -1,4 +1,4 @@ -//TEST:SIMPLE:-profile vs_5_0 +//TEST:SIMPLE:-target none -profile vs_5_0 // Confirm that `#import` interacts with preprocessor as expected diff --git a/tests/reflection/cross-compile.slang b/tests/reflection/cross-compile.slang new file mode 100644 index 000000000..12376c82a --- /dev/null +++ b/tests/reflection/cross-compile.slang @@ -0,0 +1,23 @@ +//TEST(smoke):SIMPLE:-profile glsl_fragment -target glsl -target reflection-json + +// Confirm that when targetting GLSL via cross compilation, +// we use the Vulkan layout rules instead of HLSL ones +// +// Specifically, we expect all of our top-level parameters +// to get distinct registers in the same descriptor set, +// rather than all getting register `0` because they +// occupy different names register spaces (as they would in D3D) + +Texture2D t; + +SamplerState s; + +cbuffer C +{ + float3 c; +} + +float4 main() : SV_Target +{ + return 0.0; +}
\ No newline at end of file diff --git a/tests/reflection/cross-compile.slang.expected b/tests/reflection/cross-compile.slang.expected new file mode 100644 index 000000000..3bb730f66 --- /dev/null +++ b/tests/reflection/cross-compile.slang.expected @@ -0,0 +1,48 @@ +result code = 0 +standard error = { +} +standard output = { +{ + "parameters": [ + { + "name": "t", + "binding": {"kind": "descriptorTableSlot", "index": 0}, + "type": { + "kind": "resource", + "baseShape": "texture2D" + } + }, + { + "name": "s", + "binding": {"kind": "descriptorTableSlot", "index": 1}, + "type": { + "kind": "samplerState" + } + }, + { + "name": "C", + "binding": {"kind": "descriptorTableSlot", "index": 2}, + "type": { + "kind": "constantBuffer", + "elementType": { + "kind": "struct", + "fields": [ + { + "name": "c", + "type": { + "kind": "vector", + "elementCount": 3, + "elementType": { + "kind": "scalar", + "scalarType": "float32" + } + }, + "binding": {"kind": "uniform", "offset": 0, "size": 12} + } + ] + } + } + } + ] +} +} diff --git a/tests/render/cross-compile0.hlsl b/tests/render/cross-compile0.hlsl index d300e2bd5..d40f3460f 100644 --- a/tests/render/cross-compile0.hlsl +++ b/tests/render/cross-compile0.hlsl @@ -97,8 +97,8 @@ uniform Uniforms #define ASSEMBLED_VERTEX(QUAL) \ /* */ -#define V2F(QUAL) \ - QUAL vec3 coarse_color; \ +#define V2F(QUAL) \ + layout(location = 0) QUAL vec3 coarse_color; \ /* */ // Vertex Shader diff --git a/tests/render/imported-parameters.hlsl b/tests/render/imported-parameters.hlsl index 605214fc9..99216728e 100644 --- a/tests/render/imported-parameters.hlsl +++ b/tests/render/imported-parameters.hlsl @@ -84,8 +84,8 @@ FragmentStageOutput fragmentMain(FragmentStageInput input) #define ASSEMBLED_VERTEX(QUAL) \ /* */ -#define V2F(QUAL) \ - QUAL vec3 coarse_color; \ +#define V2F(QUAL) \ + layout(location = 0) QUAL vec3 coarse_color; \ /* */ // Vertex Shader diff --git a/tests/render/pound-import.hlsl b/tests/render/pound-import.hlsl index a9b625fb6..07b195966 100644 --- a/tests/render/pound-import.hlsl +++ b/tests/render/pound-import.hlsl @@ -97,8 +97,8 @@ uniform Uniforms #define ASSEMBLED_VERTEX(QUAL) \ /* */ -#define V2F(QUAL) \ - QUAL vec3 coarse_color; \ +#define V2F(QUAL) \ + layout(location = 0) QUAL vec3 coarse_color; \ /* */ // Vertex Shader |
