diff options
| -rw-r--r-- | source/slang/check.cpp | 176 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 2 | ||||
| -rw-r--r-- | source/slang/core.meta.slang.h | 2 | ||||
| -rw-r--r-- | source/slang/diagnostic-defs.h | 13 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 153 | ||||
| -rw-r--r-- | source/slang/ir-inst-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/ir-insts.h | 12 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 13 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 57 | ||||
| -rw-r--r-- | source/slang/modifier-defs.h | 6 | ||||
| -rw-r--r-- | tests/render/tess.hlsl | 78 |
11 files changed, 496 insertions, 18 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 046587d0f..1fa7f9b01 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1845,6 +1845,38 @@ namespace Slang return Stage::Unknown; } + bool hasIntArgs(Attribute* attr, int numArgs) + { + if (int(attr->args.Count()) != numArgs) + { + return false; + } + for (int i = 0; i < numArgs; ++i) + { + if (!attr->args[i]->As<IntegerLiteralExpr>()) + { + return false; + } + } + return true; + } + + bool hasStringArgs(Attribute* attr, int numArgs) + { + if (int(attr->args.Count()) != numArgs) + { + return false; + } + for (int i = 0; i < numArgs; ++i) + { + if (!attr->args[i]->As<StringLiteralExpr>()) + { + return false; + } + } + return true; + } + bool validateAttribute(RefPtr<Attribute> attr) { if(auto numThreadsAttr = attr.As<NumThreadsAttribute>()) @@ -1898,7 +1930,27 @@ namespace Slang entryPointAttr->stage = stage; } - else + else if ((attr.As<DomainAttribute>()) || + (attr.As<MaxTessFactorAttribute>()) || + (attr.As<OutputTopologyAttribute>()) || + (attr.As<PartitioningAttribute>()) || + (attr.As<PatchConstantFuncAttribute>())) + { + // Let it go thru iff single string attribute + if (!hasStringArgs(attr, 1)) + { + getSink()->diagnose(attr, Diagnostics::expectedSingleStringArg, attr->name); + } + } + else if (attr.As<OutputControlPointsAttribute>()) + { + // Let it go thru iff single integral attribute + if (!hasIntArgs(attr, 1)) + { + getSink()->diagnose(attr, Diagnostics::expectedSingleIntArg, attr->name); + } + } + else { if(attr->args.Count() == 0) { @@ -8231,18 +8283,89 @@ namespace Slang return type; } + + FuncDecl* findFunctionDeclByName(EntryPointRequest* entryPoint, Name* name) + { + auto translationUnit = entryPoint->getTranslationUnit(); + auto sink = &entryPoint->compileRequest->mSink; + auto translationUnitSyntax = translationUnit->SyntaxNode; + + // Make sure we've got a query-able member dictionary + buildMemberDictionary(translationUnitSyntax); + + // We will look up any global-scope declarations in the translation + // unit that match the name of our entry point. + Decl* firstDeclWithName = nullptr; + if (!translationUnitSyntax->memberDictionary.TryGetValue(name, firstDeclWithName)) + { + // If there doesn't appear to be any such declaration, then we are done. + + sink->diagnose(translationUnitSyntax, Diagnostics::entryPointFunctionNotFound, name); + + return nullptr; + } + + // We found at least one global-scope declaration with the right name, + // but (1) it might not be a function, and (2) there might be + // more than one function. + // + // We'll walk the linked list of declarations with the same name, + // to see what we find. Along the way we'll keep track of the + // first function declaration we find, if any: + FuncDecl* entryPointFuncDecl = nullptr; + for (auto ee = firstDeclWithName; ee; ee = ee->nextInContainerWithSameName) + { + // Is this declaration a function? + if (auto funcDecl = dynamic_cast<FuncDecl*>(ee)) + { + // Skip non-primary declarations, so that + // we don't give an error when an entry + // point is forward-declared. + if (!isPrimaryDecl(funcDecl)) + continue; + + // is this the first one we've seen? + if (!entryPointFuncDecl) + { + // If so, this is a candidate to be + // the entry point function. + entryPointFuncDecl = funcDecl; + } + else + { + // Uh-oh! We've already seen a function declaration with this + // name before, so the whole thing is ambiguous. We need + // to diagnose and bail out. + + sink->diagnose(translationUnitSyntax, Diagnostics::ambiguousEntryPoint, name); + + // List all of the declarations that the user *might* mean + for (auto ff = firstDeclWithName; ff; ff = ff->nextInContainerWithSameName) + { + if (auto candidate = dynamic_cast<FuncDecl*>(ff)) + { + sink->diagnose(candidate, Diagnostics::entryPointCandidate, candidate->getName()); + } + } + + // Bail out. + return nullptr; + } + } + } + + return entryPointFuncDecl; + } + // Validate that an entry point function conforms to any additional // constraints based on the stage (and profile?) it specifies. void validateEntryPoint( - EntryPointRequest* /*entryPoint*/) + EntryPointRequest* entryPoint) { - // TODO: We currently don't do any checking here, but this is the + // TODO: We currently do minimal checking here, but this is the // right place to perform the following validation checks: // - // * Does the entry point specify all of the attributes required - // by the chosen stage (e.g., a `[domain(...)]` attribute for] - // a hull shader. - // + // * Are the function input/output parameters and result type // all valid for the chosen stage? (e.g., there shouldn't be // an `OutputStream<X>` type in a vertex shader signature) @@ -8263,6 +8386,43 @@ namespace Slang // `Texture2D.Sample`, then that should produce an error because // that function is specific to the fragment profile/stage. // + + if (entryPoint->getStage() == Stage::Hull) + { + auto translationUnit = entryPoint->getTranslationUnit(); + auto sink = &entryPoint->compileRequest->mSink; + auto translationUnitSyntax = translationUnit->SyntaxNode; + + auto attr = entryPoint->decl->FindModifier<PatchConstantFuncAttribute>(); + + if (attr) + { + if (attr->args.Count() != 1) + { + sink->diagnose(translationUnitSyntax, Diagnostics::badlyDefinedPatchConstantFunc, entryPoint->name); + return; + } + + Expr* expr = attr->args[0]; + StringLiteralExpr* stringLit = expr->As<StringLiteralExpr>(); + + if (!stringLit) + { + sink->diagnose(translationUnitSyntax, Diagnostics::badlyDefinedPatchConstantFunc, entryPoint->name); + return; + } + + Name* name = entryPoint->compileRequest->getNamePool()->getName(stringLit->value); + FuncDecl* funcDecl = findFunctionDeclByName(entryPoint, name); + if (!funcDecl) + { + sink->diagnose(translationUnitSyntax, Diagnostics::attributeFunctionNotFound, name, "patchconstantfunc"); + return; + } + + attr->patchConstantFuncDecl = funcDecl; + } + } } // Given an `EntryPointRequest` specified via API or command line options, @@ -8445,7 +8605,7 @@ namespace Slang // set to `Batman` to know whether the setting for `B` is valid. In this limit // the constraints can be mutually recursive (so `A : IMentor<B>`). // - // The only way to check things corectly is to validate each conformance under + // The only way to check things correctly is to validate each conformance under // a set of assumptions (substitutions) that includes all the type substitutions, // and possibly also all the other constraints *except* the one to be validated. // diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index a29cf7a27..ab46e5676 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1136,7 +1136,7 @@ __attributeTarget(FuncDecl) attribute_syntax [outputcontrolpoints(count: int)] : OutputControlPointsAttribute; __attributeTarget(FuncDecl) -attribute_syntax [outputtopology(topology)] : OuptutTopologyAttribute; +attribute_syntax [outputtopology(topology)] : OutputTopologyAttribute; __attributeTarget(FuncDecl) attribute_syntax [partitioning(mode)] : PartitioningAttribute; diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h index b4bef2397..d3edba93d 100644 --- a/source/slang/core.meta.slang.h +++ b/source/slang/core.meta.slang.h @@ -1156,7 +1156,7 @@ SLANG_RAW("__attributeTarget(FuncDecl)\n") SLANG_RAW("attribute_syntax [outputcontrolpoints(count: int)] : OutputControlPointsAttribute;\n") SLANG_RAW("\n") SLANG_RAW("__attributeTarget(FuncDecl)\n") -SLANG_RAW("attribute_syntax [outputtopology(topology)] : OuptutTopologyAttribute;\n") +SLANG_RAW("attribute_syntax [outputtopology(topology)] : OutputTopologyAttribute;\n") SLANG_RAW("\n") SLANG_RAW("__attributeTarget(FuncDecl)\n") SLANG_RAW("attribute_syntax [partitioning(mode)] : PartitioningAttribute;\n") diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h index b6f755489..8dd2f3b0b 100644 --- a/source/slang/diagnostic-defs.h +++ b/source/slang/diagnostic-defs.h @@ -206,10 +206,20 @@ DIAGNOSTIC(33071, Error, expectedAStringLiteral, "expected a string literal") // Attributes DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'") DIAGNOSTIC(31001, Error, attributeArgumentCountMismatch, "attribute '$0' expects $1 arguments ($2 provided)") -DIAGNOSTIC(31001, Error, attributeNotApplicable, "attribute '$0' is not valid here") +DIAGNOSTIC(31002, Error, attributeNotApplicable, "attribute '$0' is not valid here") + +DIAGNOSTIC(31003, Error, badlyDefinedPatchConstantFunc, "hull shader '$0' has has badly defined 'patchconstantfunc' attribute."); + +DIAGNOSTIC(31004, Error, expectedSingleIntArg, "attribute '$0' expects a single int argument") +DIAGNOSTIC(31005, Error, expectedSingleStringArg, "attribute '$0' expects a single string argument") + +DIAGNOSTIC(31006, Error, attributeFunctionNotFound, "Could not find function '$0' for attribute'$1'") + DIAGNOSTIC(31100, Error, unknownStageName, "unknown stage name '$0'") + + // Enums DIAGNOSTIC(32000, Error, invalidEnumTagType, "invalid tag type for 'enum': '$0'") @@ -265,7 +275,6 @@ DIAGNOSTIC(39999, Error, invalidFloatingPOintLiteralSuffix, "invalid suffix '$0' - DIAGNOSTIC(38000, Error, entryPointFunctionNotFound, "no function found matching entry point name '$0'") DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one function matches entry point name '$0'") DIAGNOSTIC(38002, Note, entryPointCandidate, "see candidate declaration for entry point '$0'") diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 1b8de806a..1a56c1d4d 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -3654,6 +3654,12 @@ struct EmitVisitor } break; + case kIROp_NotePatchConstantFunc: + { + // No-op + break; + } + case kIROp_Var: { auto ptrType = cast<IRPtrType>(inst->getDataType()); @@ -4129,7 +4135,98 @@ struct EmitVisitor } } + + + IRInst* findFirstInst(IRFunc* irFunc, IROp op) + { + for (auto block : irFunc->getBlocks()) + { + for (auto inst : block->getChildren()) + { + if (inst->op == op) + { + return inst; + } + } + } + return nullptr; + } + + void emitAttributeSingleString(const char* name, FuncDecl* entryPoint, Attribute* attrib) + { + assert(attrib); + + attrib->args.Count(); + if (attrib->args.Count() != 1) + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), entryPoint->loc, "Attribute expects single parameter"); + return; + } + + Expr* expr = attrib->args[0]; + + auto stringLitExpr = expr->As<StringLiteralExpr>(); + if (!stringLitExpr) + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), entryPoint->loc, "Attribute parameter expecting to be a string "); + return; + } + + emit("["); + emit(name); + emit("(\""); + emit(stringLitExpr->value); + emit("\")]\n"); + } + + void emitAttributeSingleInt(const char* name, FuncDecl* entryPoint, Attribute* attrib) + { + assert(attrib); + + attrib->args.Count(); + if (attrib->args.Count() != 1) + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), entryPoint->loc, "Attribute expects single parameter"); + return; + } + + Expr* expr = attrib->args[0]; + + auto intLitExpr = expr->As<IntegerLiteralExpr>(); + if (!intLitExpr) + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), entryPoint->loc, "Attribute expects an int"); + return; + } + + emit("["); + emit(name); + emit("("); + emit(intLitExpr->value); + emit(")]\n"); + } + + void emitFuncDeclPatchConstantFuncAttribute(IRFunc* irFunc, FuncDecl* entryPoint, PatchConstantFuncAttribute* attrib) + { + SLANG_UNUSED(attrib); + + auto irPatchFunc = static_cast<IRNotePatchConstantFunc*>(findFirstInst(irFunc, kIROp_NotePatchConstantFunc)); + assert(irPatchFunc); + if (!irPatchFunc) + { + SLANG_DIAGNOSE_UNEXPECTED(getSink(), entryPoint->loc, "Unable to find NotePatchConstantFunc instruction"); + return; + } + + const String irName = getIRName(irPatchFunc->getFunc()); + + emit("[patchconstantfunc(\""); + emit(irName); + emit("\")]\n"); + } + void emitIREntryPointAttributes_HLSL( + IRFunc* irFunc, EmitContext* ctx, EntryPointLayout* entryPointLayout) { @@ -4199,8 +4296,54 @@ struct EmitVisitor Emit(attrib->value); emit(")]\n"); } + break; + } + case Stage::Domain: + { + FuncDecl* entryPoint = entryPointLayout->entryPoint; + /* [domain("isoline")] */ + if (auto attrib = entryPoint->FindModifier<DomainAttribute>()) + { + emitAttributeSingleString("domain", entryPoint, attrib); + } + + break; + } + case Stage::Hull: + { + // Lists these are only attributes for hull shader + // https://docs.microsoft.com/en-us/windows/desktop/direct3d11/direct3d-11-advanced-stages-hull-shader-design + + FuncDecl* entryPoint = entryPointLayout->entryPoint; + + /* [domain("isoline")] */ + if (auto attrib = entryPoint->FindModifier<DomainAttribute>()) + { + emitAttributeSingleString("domain", entryPoint, attrib); + } + /* [domain("partitioning")] */ + if (auto attrib = entryPoint->FindModifier<PartitioningAttribute>()) + { + emitAttributeSingleString("partitioning", entryPoint, attrib); + } + /* [outputtopology("line")] */ + if (auto attrib = entryPoint->FindModifier<OutputTopologyAttribute>()) + { + emitAttributeSingleString("outputtopology", entryPoint, attrib); + } + /* [outputcontrolpoints(4)] */ + if (auto attrib = entryPoint->FindModifier<OutputControlPointsAttribute>()) + { + emitAttributeSingleInt("outputcontrolpoints", entryPoint, attrib); + } + /* [patchconstantfunc("HSConst")] */ + if (auto attrib = entryPoint->FindModifier<PatchConstantFuncAttribute>()) + { + emitFuncDeclPatchConstantFuncAttribute(irFunc, entryPoint, attrib); + } + + break; } - break; // TODO: There are other stages that will need this kind of handling. default: break; @@ -4208,6 +4351,7 @@ struct EmitVisitor } void emitIREntryPointAttributes_GLSL( + IRFunc* /*irFunc*/, EmitContext* /*ctx*/, EntryPointLayout* entryPointLayout) { @@ -4310,17 +4454,18 @@ struct EmitVisitor } void emitIREntryPointAttributes( + IRFunc* irFunc, EmitContext* ctx, EntryPointLayout* entryPointLayout) { switch(getTarget(ctx)) { case CodeGenTarget::HLSL: - emitIREntryPointAttributes_HLSL(ctx, entryPointLayout); + emitIREntryPointAttributes_HLSL(irFunc, ctx, entryPointLayout); break; case CodeGenTarget::GLSL: - emitIREntryPointAttributes_GLSL(ctx, entryPointLayout); + emitIREntryPointAttributes_GLSL(irFunc, ctx, entryPointLayout); break; } } @@ -4390,7 +4535,7 @@ struct EmitVisitor auto entryPointLayout = asEntryPoint(func); if (entryPointLayout) { - emitIREntryPointAttributes(ctx, entryPointLayout); + emitIREntryPointAttributes(func, ctx, entryPointLayout); } auto name = getIRFuncName(func); diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h index 0b7d15fcc..69cbe9e9a 100644 --- a/source/slang/ir-inst-defs.h +++ b/source/slang/ir-inst-defs.h @@ -353,6 +353,8 @@ INST(SampleGrad, sampleGrad, 4, 0) INST(GroupMemoryBarrierWithGroupSync, GroupMemoryBarrierWithGroupSync, 0, 0) +INST(NotePatchConstantFunc, notePatchConstantFunc, 1, 0) + PSEUDO_INST(Pos) PSEUDO_INST(PreInc) diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h index 880856b36..e7ca08c6d 100644 --- a/source/slang/ir-insts.h +++ b/source/slang/ir-insts.h @@ -381,6 +381,13 @@ struct IRSwizzledStore : IRInst IR_LEAF_ISA(SwizzledStore) }; + +struct IRNotePatchConstantFunc: IRInst +{ + IRInst* getFunc() { return getOperand(0); } + IR_LEAF_ISA(NotePatchConstantFunc) +}; + // An IR `var` instruction conceptually represents // a stack allocation of some memory. struct IRVar : IRInst @@ -689,11 +696,16 @@ struct IRBuilder IRBlock* createBlock(); IRBlock* emitBlock(); + + IRParam* createParam( IRType* type); IRParam* emitParam( IRType* type); + IRNotePatchConstantFunc* emitNotePatchConstantFunc( + IRInst* func); + IRVar* emitVar( IRType* type); diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index 2a8885b0e..83735d6df 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -1898,6 +1898,19 @@ namespace Slang return inst; } + IRNotePatchConstantFunc* IRBuilder::emitNotePatchConstantFunc( + IRInst* func) + { + auto inst = createInst<IRNotePatchConstantFunc>( + this, + kIROp_NotePatchConstantFunc, + nullptr, + func); + + addInst(inst); + return inst; + } + IRInst* IRBuilder::emitFieldExtract( IRType* type, IRInst* base, diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 27325ee38..07cb508b8 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -1142,6 +1142,27 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower &irElementType); } + IRType* lowerGenericIntrinsicType(DeclRefType* type, Type* elementType, IntVal* count) + { + auto intrinsicTypeModifier = type->declRef.getDecl()->FindModifier<IntrinsicTypeModifier>(); + SLANG_ASSERT(intrinsicTypeModifier); + IROp op = IROp(intrinsicTypeModifier->irOp); + IRInst* irElementType = lowerType(context, elementType); + + IRInst* irCount = lowerSimpleVal(context, count); + + IRInst* const operands[2] = + { + irElementType, + irCount, + }; + + return getBuilder()->getType( + op, + SLANG_COUNT_OF(operands), + operands); + } + IRType* visitResourceType(ResourceType* type) { return lowerGenericIntrinsicType(type, type->elementType); @@ -1162,6 +1183,14 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower return lowerSimpleIntrinsicType(type); } + IRType* visitHLSLPatchType(HLSLPatchType* type) + { + Type* elementType = type->getElementType(); + IntVal* count = type->getElementCount(); + + return lowerGenericIntrinsicType(type, elementType, count); + } + // We do not expect to encounter the following types in ASTs that have // passed front-end semantic checking. #define UNEXPECTED_CASE(NAME) IRType* visit##NAME(NAME*) { SLANG_UNEXPECTED(#NAME); UNREACHABLE_RETURN(nullptr); } @@ -4765,6 +4794,34 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // the setter body. } + { + + auto attr = decl->FindModifier<PatchConstantFuncAttribute>(); + + // I needed to test for patchConstantFuncDecl here + // because it is only set if validateEntryPoint is called with Hull as the required stage + // If I just build domain shader, and then the attribute exists, but patchConstantFuncDecl is not set + // and thus leads to a crash. + if (attr && attr->patchConstantFuncDecl) + { + // We need to lower the function + FuncDecl* patchConstantFunc = attr->patchConstantFuncDecl; + assert(patchConstantFunc); + + // Convert the patch constant function into IRInst + IRInst* irPatchConstantFunc = getSimpleVal(context, ensureDecl(subContext, patchConstantFunc)); + + // Emit the note patch constant func + subContext->irBuilder->emitIntrinsicInst( + nullptr, + kIROp_NotePatchConstantFunc, + 1, + &irPatchConstantFunc); + + } + } + + // Lower body lowerStmt(subContext, decl->Body); diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h index 8ccdc75b1..f0443c501 100644 --- a/source/slang/modifier-defs.h +++ b/source/slang/modifier-defs.h @@ -335,9 +335,11 @@ SIMPLE_SYNTAX_CLASS(CallAttribute, Attribute) // `[call]` SIMPLE_SYNTAX_CLASS(MaxTessFactorAttribute, Attribute) SIMPLE_SYNTAX_CLASS(OutputControlPointsAttribute, Attribute) -SIMPLE_SYNTAX_CLASS(OuptutTopologyAttribute, Attribute) +SIMPLE_SYNTAX_CLASS(OutputTopologyAttribute, Attribute) SIMPLE_SYNTAX_CLASS(PartitioningAttribute, Attribute) -SIMPLE_SYNTAX_CLASS(PatchConstantFuncAttribute, Attribute) +SYNTAX_CLASS(PatchConstantFuncAttribute, Attribute) + FIELD(RefPtr<FuncDecl>, patchConstantFuncDecl) +END_SYNTAX_CLASS() SIMPLE_SYNTAX_CLASS(DomainAttribute, Attribute) SIMPLE_SYNTAX_CLASS(EarlyDepthStencilAttribute, Attribute) diff --git a/tests/render/tess.hlsl b/tests/render/tess.hlsl new file mode 100644 index 000000000..1cd01ef33 --- /dev/null +++ b/tests/render/tess.hlsl @@ -0,0 +1,78 @@ +//TEST:COMPARE_HLSL: -target dxbc-assembly -profile hs_5_1 -entry HS -profile ds_5_1 -entry DS + +// tests/render/tess.hlsl + +// -profile vs_5_1 -o my-shader.vs.dxbc -entry VS -profile ps_5_1 -o my-shader.ps.dxbc -entry PS + +struct IA_OUTPUT +{ + float3 cpoint : CPOINT; +}; + +struct VS_OUTPUT +{ + float3 cpoint : CPOINT; +}; + +struct HS_CONSTANT_OUTPUT +{ + float edges[2] : SV_TessFactor; +}; + +struct HS_OUTPUT +{ + float3 cpoint : CPOINT; +}; + +struct DS_OUTPUT +{ + float4 position : SV_Position; +}; + +VS_OUTPUT VS(IA_OUTPUT input) +{ + VS_OUTPUT output; + output.cpoint = input.cpoint; + return output; +} + +HS_CONSTANT_OUTPUT HSConst() +{ + HS_CONSTANT_OUTPUT output; + + output.edges[0] = 1.0f; // Detail factor + output.edges[1] = 64.0f; // Density factor + + return output; +} + +[domain("isoline")] +[partitioning("integer")] +[outputtopology("line")] +[outputcontrolpoints(4)] +[patchconstantfunc("HSConst")] +HS_OUTPUT HS(InputPatch<VS_OUTPUT, 4> ip, uint id : SV_OutputControlPointID) +{ + HS_OUTPUT output; + output.cpoint = ip[id].cpoint; + return output; +} + +[domain("isoline")] +DS_OUTPUT DS(HS_CONSTANT_OUTPUT input, OutputPatch<HS_OUTPUT, 4> op, float2 uv : SV_DomainLocation) +{ + DS_OUTPUT output; + + float t = uv.x; + + float3 pos = pow(1.0f - t, 3.0f) * op[0].cpoint + 3.0f * pow(1.0f - t, 2.0f) * t * op[1].cpoint + 3.0f * (1.0f - t) * pow(t, 2.0f) * op[2].cpoint + pow(t, 3.0f) * op[3].cpoint; + + output.position = float4(pos, 1.0f); + + return output; +} + +float4 PS(DS_OUTPUT input) : SV_Target0 +{ + return float4(0.0f, 0.0f, 0.0f, 1.0f); +}
\ No newline at end of file |
