[spirv] Add support for dual-source blending (#1251)
In HLSL, dual-source color blending is enabled only via API; the shader needs no special marks: it only writes SV_Target0 & SV_Target1 like normal. But in Vulkan, to enable dual-source blending, the shader need to mark the two participating output variables with Index = 0 and Index = 1, respectively. So we introduce a new attribute, vk::index(), to let developers to specify the index of an output variable so dual-source blending can be enabled. See Vulkan spec "26.1.2. Dual-Source Blending".
This commit is contained in:
Родитель
c82edd6c5f
Коммит
9b856626d6
|
@ -275,6 +275,8 @@ The namespace ``vk`` will be used for all Vulkan attributes:
|
|||
- ``builtin("X")``: For specifying an entity should be translated into a certain
|
||||
Vulkan builtin variable. Allowed on function parameters, function returns,
|
||||
and struct fields.
|
||||
- ``index(X)``: For specifying the index at a specific pixel shader output
|
||||
location. Used for dual-source blending.
|
||||
|
||||
Only ``vk::`` attributes in the above list are supported. Other attributes will
|
||||
result in warnings and be ignored by the compiler. All C++11 attributes will
|
||||
|
|
|
@ -879,6 +879,14 @@ def VKLocation : InheritableAttr {
|
|||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def VKIndex : InheritableAttr {
|
||||
let Spellings = [CXX11<"vk", "index">];
|
||||
let Subjects = SubjectList<[Function, ParmVar, Field], ErrorDiag>;
|
||||
let Args = [IntArgument<"Number">];
|
||||
let LangOpts = [SPIRV];
|
||||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def VKBinding : InheritableAttr {
|
||||
let Spellings = [CXX11<"vk", "binding">];
|
||||
let Subjects = SubjectList<[GlobalVar, HLSLBuffer], ErrorDiag, "ExpectedGlobalVarOrCTBuffer">;
|
||||
|
|
|
@ -375,6 +375,9 @@ public:
|
|||
/// \brief Decorates the given target <result-id> with the given location.
|
||||
void decorateLocation(uint32_t targetId, uint32_t location);
|
||||
|
||||
/// \brief Decorates the given target <result-id> with the given index.
|
||||
void decorateIndex(uint32_t targetId, uint32_t index);
|
||||
|
||||
/// \brief Decorates the given target <result-id> with the given descriptor
|
||||
/// set and binding number.
|
||||
void decorateDSetBinding(uint32_t targetId, uint32_t setNumber,
|
||||
|
|
|
@ -99,11 +99,11 @@ std::string StageVar::getSemanticStr() const {
|
|||
// Use what is in the source code.
|
||||
// TODO: this looks like a hack to make the current tests happy.
|
||||
// Should consider remove it and fix all tests.
|
||||
if (semanticIndex == 0)
|
||||
return semanticStr;
|
||||
if (semanticInfo.index == 0)
|
||||
return semanticInfo.str;
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << semanticName.str() << semanticIndex;
|
||||
ss << semanticInfo.name.str() << semanticInfo.index;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
@ -181,8 +181,7 @@ bool CounterVarFields::assign(const CounterVarFields &srcFields,
|
|||
return true;
|
||||
}
|
||||
|
||||
DeclResultIdMapper::SemanticInfo
|
||||
DeclResultIdMapper::getStageVarSemantic(const NamedDecl *decl) {
|
||||
SemanticInfo DeclResultIdMapper::getStageVarSemantic(const NamedDecl *decl) {
|
||||
for (auto *annotation : decl->getUnusualAnnotations()) {
|
||||
if (auto *sema = dyn_cast<hlsl::SemanticDecl>(annotation)) {
|
||||
llvm::StringRef semanticStr = sema->SemanticName;
|
||||
|
@ -305,12 +304,12 @@ SpirvEvalInfo DeclResultIdMapper::getDeclEvalInfo(const ValueDecl *decl) {
|
|||
return *info;
|
||||
}
|
||||
|
||||
emitFatalError("found unregistered decl", decl->getLocation())
|
||||
<< decl->getName();
|
||||
emitNote("please file a bug report on "
|
||||
"https://github.com/Microsoft/DirectXShaderCompiler/issues with "
|
||||
"source code if possible",
|
||||
{});
|
||||
emitFatalError("found unregistered decl", decl->getLocation())
|
||||
<< decl->getName();
|
||||
emitNote("please file a bug report on "
|
||||
"https://github.com/Microsoft/DirectXShaderCompiler/issues with "
|
||||
"source code if possible",
|
||||
{});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -813,37 +812,53 @@ namespace {
|
|||
/// the same location.
|
||||
class LocationSet {
|
||||
public:
|
||||
/// Maximum number of indices supported
|
||||
const static uint32_t kMaxIndex = 2;
|
||||
/// Maximum number of locations supported
|
||||
// Typically we won't have that many stage input or output variables.
|
||||
// Using 64 should be fine here.
|
||||
const static uint32_t kMaxLoc = 64;
|
||||
|
||||
LocationSet() : usedLocs(kMaxLoc, false), nextLoc(0) {}
|
||||
LocationSet() {
|
||||
for (uint32_t i = 0; i < kMaxIndex; ++i) {
|
||||
usedLocs[i].resize(kMaxLoc);
|
||||
nextLoc[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Uses the given location.
|
||||
void useLoc(uint32_t loc) { usedLocs.set(loc); }
|
||||
void useLoc(uint32_t loc, uint32_t index = 0) {
|
||||
assert(index < kMaxIndex);
|
||||
usedLocs[index].set(loc);
|
||||
}
|
||||
|
||||
/// Uses the next |count| available location.
|
||||
int useNextLocs(uint32_t count) {
|
||||
while (usedLocs[nextLoc])
|
||||
nextLoc++;
|
||||
int useNextLocs(uint32_t count, uint32_t index = 0) {
|
||||
assert(index < kMaxIndex);
|
||||
auto &locs = usedLocs[index];
|
||||
auto &next = nextLoc[index];
|
||||
while (locs[next])
|
||||
next++;
|
||||
|
||||
int toUse = nextLoc;
|
||||
int toUse = next;
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
assert(!usedLocs[nextLoc]);
|
||||
usedLocs.set(nextLoc++);
|
||||
assert(!locs[next]);
|
||||
locs.set(next++);
|
||||
}
|
||||
|
||||
return toUse;
|
||||
}
|
||||
|
||||
/// Returns true if the given location number is already used.
|
||||
bool isLocUsed(uint32_t loc) { return usedLocs[loc]; }
|
||||
bool isLocUsed(uint32_t loc, uint32_t index = 0) {
|
||||
assert(index < kMaxIndex);
|
||||
return usedLocs[index][loc];
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::SmallBitVector usedLocs; ///< All previously used locations
|
||||
uint32_t nextLoc; ///< Next available location
|
||||
llvm::SmallBitVector usedLocs[kMaxIndex]; ///< All previously used locations
|
||||
uint32_t nextLoc[kMaxIndex]; ///< Next available location
|
||||
};
|
||||
|
||||
/// A class for managing resource bindings to avoid duplicate uses of the same
|
||||
|
@ -926,17 +941,14 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
|
|||
bool noError = true;
|
||||
|
||||
for (const auto &var : stageVars) {
|
||||
// Skip those stage variables we are not handling for this call
|
||||
if (forInput != isInputStorageClass(var))
|
||||
continue;
|
||||
|
||||
// Skip builtins
|
||||
if (var.isSpirvBuitin())
|
||||
// Skip builtins & those stage variables we are not handling for this call
|
||||
if (var.isSpirvBuitin() || forInput != isInputStorageClass(var))
|
||||
continue;
|
||||
|
||||
const auto *attr = var.getLocationAttr();
|
||||
const auto loc = attr->getNumber();
|
||||
const auto attrLoc = attr->getLocation(); // Attr source code location
|
||||
const auto idx = var.getIndexAttr() ? var.getIndexAttr()->getNumber() : 0;
|
||||
|
||||
if (loc >= LocationSet::kMaxLoc) {
|
||||
emitError("stage %select{output|input}0 location #%1 too large",
|
||||
|
@ -946,15 +958,17 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
|
|||
}
|
||||
|
||||
// Make sure the same location is not assigned more than once
|
||||
if (locSet.isLocUsed(loc)) {
|
||||
if (locSet.isLocUsed(loc, idx)) {
|
||||
emitError("stage %select{output|input}0 location #%1 already assigned",
|
||||
attrLoc)
|
||||
<< forInput << loc;
|
||||
noError = false;
|
||||
}
|
||||
locSet.useLoc(loc);
|
||||
locSet.useLoc(loc, idx);
|
||||
|
||||
theBuilder.decorateLocation(var.getSpirvId(), loc);
|
||||
if (var.getIndexAttr())
|
||||
theBuilder.decorateIndex(var.getSpirvId(), idx);
|
||||
}
|
||||
|
||||
return noError;
|
||||
|
@ -964,30 +978,28 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
|
|||
LocationSet locSet;
|
||||
|
||||
for (const auto &var : stageVars) {
|
||||
if (forInput != isInputStorageClass(var))
|
||||
if (var.isSpirvBuitin() || forInput != isInputStorageClass(var))
|
||||
continue;
|
||||
|
||||
if (!var.isSpirvBuitin()) {
|
||||
if (var.getLocationAttr() != nullptr) {
|
||||
// We have checked that not all of the stage variables have explicit
|
||||
// location assignment.
|
||||
emitError("partial explicit stage %select{output|input}0 location "
|
||||
"assignment via vk::location(X) unsupported",
|
||||
{})
|
||||
<< forInput;
|
||||
return false;
|
||||
}
|
||||
if (var.getLocationAttr()) {
|
||||
// We have checked that not all of the stage variables have explicit
|
||||
// location assignment.
|
||||
emitError("partial explicit stage %select{output|input}0 location "
|
||||
"assignment via vk::location(X) unsupported",
|
||||
{})
|
||||
<< forInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only SV_Target, SV_Depth, SV_DepthLessEqual, SV_DepthGreaterEqual,
|
||||
// SV_StencilRef, SV_Coverage are allowed in the pixel shader.
|
||||
// Arbitrary semantics are disallowed in pixel shader.
|
||||
if (var.getSemantic() &&
|
||||
var.getSemantic()->GetKind() == hlsl::Semantic::Kind::Target) {
|
||||
theBuilder.decorateLocation(var.getSpirvId(), var.getSemanticIndex());
|
||||
locSet.useLoc(var.getSemanticIndex());
|
||||
} else {
|
||||
vars.push_back(&var);
|
||||
}
|
||||
const auto &semaInfo = var.getSemanticInfo();
|
||||
|
||||
// We should special rules for SV_Target: the location number comes from the
|
||||
// semantic string index.
|
||||
if (semaInfo.isTarget()) {
|
||||
theBuilder.decorateLocation(var.getSpirvId(), semaInfo.index);
|
||||
locSet.useLoc(semaInfo.index);
|
||||
} else {
|
||||
vars.push_back(&var);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1205,7 +1217,10 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
|
|||
// Found semantic attached directly to this Decl. This means we need to
|
||||
// map this decl to a single stage variable.
|
||||
|
||||
const auto semanticKind = semanticToUse->semantic->GetKind();
|
||||
if (!validateVKAttributes(decl))
|
||||
return false;
|
||||
|
||||
const auto semanticKind = semanticToUse->getKind();
|
||||
|
||||
// Error out when the given semantic is invalid in this shader model
|
||||
if (hlsl::SigPoint::GetInterpretation(semanticKind, sigPoint->GetKind(),
|
||||
|
@ -1298,8 +1313,7 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
|
|||
theBuilder.getConstantUint32(arraySize));
|
||||
|
||||
StageVar stageVar(
|
||||
sigPoint, semanticToUse->str, semanticToUse->semantic,
|
||||
semanticToUse->name, semanticToUse->index, builtinAttr, typeId,
|
||||
sigPoint, *semanticToUse, builtinAttr, typeId,
|
||||
// For HS/DS/GS, we have already stripped the outmost arrayness on type.
|
||||
typeTranslator.getLocationCount(type));
|
||||
const auto name = namePrefix.str() + "." + stageVar.getSemanticStr();
|
||||
|
@ -1311,11 +1325,12 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
|
|||
|
||||
stageVar.setSpirvId(varId);
|
||||
stageVar.setLocationAttr(decl->getAttr<VKLocationAttr>());
|
||||
stageVar.setIndexAttr(decl->getAttr<VKIndexAttr>());
|
||||
stageVars.push_back(stageVar);
|
||||
|
||||
// Emit OpDecorate* instructions to link this stage variable with the HLSL
|
||||
// semantic it is created for
|
||||
theBuilder.decorateHlslSemantic(varId, stageVar.getSemanticStr());
|
||||
theBuilder.decorateHlslSemantic(varId, stageVar.getSemanticInfo().str);
|
||||
|
||||
// We have semantics attached to this decl, which means it must be a
|
||||
// function/parameter/variable. All are DeclaratorDecls.
|
||||
|
@ -1792,9 +1807,8 @@ uint32_t DeclResultIdMapper::getBuiltinVar(spv::BuiltIn builtIn) {
|
|||
hlsl::DxilParamInputQual::In, shaderModel.GetKind(),
|
||||
/*isPatchConstant=*/false));
|
||||
|
||||
StageVar stageVar(sigPoint, /*semaStr=*/"", hlsl::Semantic::GetInvalid(),
|
||||
/*semaName=*/"", /*semaIndex=*/0, /*builtinAttr=*/nullptr,
|
||||
type, /*locCount=*/0);
|
||||
StageVar stageVar(sigPoint, /*semaInfo=*/{}, /*builtinAttr=*/nullptr, type,
|
||||
/*locCount=*/0);
|
||||
|
||||
stageVar.setIsSpirvBuiltin();
|
||||
stageVar.setSpirvId(varId);
|
||||
|
@ -1819,7 +1833,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
|
|||
using spv::BuiltIn;
|
||||
|
||||
const auto sigPoint = stageVar->getSigPoint();
|
||||
const auto semanticKind = stageVar->getSemantic()->GetKind();
|
||||
const auto semanticKind = stageVar->getSemanticInfo().getKind();
|
||||
const auto sigPointKind = sigPoint->GetKind();
|
||||
const uint32_t type = stageVar->getSpirvTypeId();
|
||||
|
||||
|
@ -2194,13 +2208,49 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
|
|||
}
|
||||
default:
|
||||
emitError("semantic %0 unimplemented", srcLoc)
|
||||
<< stageVar->getSemantic()->GetName();
|
||||
<< stageVar->getSemanticStr();
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DeclResultIdMapper::validateVKAttributes(const NamedDecl *decl) {
|
||||
bool success = true;
|
||||
if (const auto *idxAttr = decl->getAttr<VKIndexAttr>()) {
|
||||
if (!shaderModel.IsPS()) {
|
||||
emitError("vk::index only allowed in pixel shader",
|
||||
idxAttr->getLocation());
|
||||
success = false;
|
||||
}
|
||||
|
||||
const auto *locAttr = decl->getAttr<VKLocationAttr>();
|
||||
|
||||
if (!locAttr) {
|
||||
emitError("vk::index should be used together with vk::location for "
|
||||
"dual-source blending",
|
||||
idxAttr->getLocation());
|
||||
success = false;
|
||||
} else {
|
||||
const auto locNumber = locAttr->getNumber();
|
||||
if (locNumber != 0) {
|
||||
emitError("dual-source blending should use vk::location 0",
|
||||
locAttr->getLocation());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
const auto idxNumber = idxAttr->getNumber();
|
||||
if (idxNumber != 0 && idxNumber != 1) {
|
||||
emitError("dual-source blending only accepts 0 or 1 as vk::index",
|
||||
idxAttr->getLocation());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool DeclResultIdMapper::validateVKBuiltins(const NamedDecl *decl,
|
||||
const hlsl::SigPoint *sigPoint) {
|
||||
bool success = true;
|
||||
|
|
|
@ -32,16 +32,29 @@
|
|||
namespace clang {
|
||||
namespace spirv {
|
||||
|
||||
/// A struct containing information about a particular HLSL semantic.
|
||||
struct SemanticInfo {
|
||||
llvm::StringRef str; ///< The original semantic string
|
||||
const hlsl::Semantic *semantic; ///< The unique semantic object
|
||||
llvm::StringRef name; ///< The semantic string without index
|
||||
uint32_t index; ///< The semantic index
|
||||
SourceLocation loc; ///< Source code location
|
||||
|
||||
bool isValid() const { return semantic != nullptr; }
|
||||
|
||||
inline hlsl::Semantic::Kind getKind() const;
|
||||
/// \brief Returns true if this semantic is a SV_Target.
|
||||
inline bool isTarget() const;
|
||||
};
|
||||
|
||||
/// \brief The class containing HLSL and SPIR-V information about a Vulkan stage
|
||||
/// (builtin/input/output) variable.
|
||||
class StageVar {
|
||||
public:
|
||||
inline StageVar(const hlsl::SigPoint *sig, llvm::StringRef semaStr,
|
||||
const hlsl::Semantic *sema, llvm::StringRef semaName,
|
||||
uint32_t semaIndex, const VKBuiltInAttr *builtin,
|
||||
uint32_t type, uint32_t locCount)
|
||||
: sigPoint(sig), semanticStr(semaStr), semantic(sema),
|
||||
semanticName(semaName), semanticIndex(semaIndex), builtinAttr(builtin),
|
||||
inline StageVar(const hlsl::SigPoint *sig, SemanticInfo semaInfo,
|
||||
const VKBuiltInAttr *builtin, uint32_t type,
|
||||
uint32_t locCount)
|
||||
: sigPoint(sig), semanticInfo(std::move(semaInfo)), builtinAttr(builtin),
|
||||
typeId(type), valueId(0), isBuiltin(false),
|
||||
storageClass(spv::StorageClass::Max), location(nullptr),
|
||||
locationCount(locCount) {
|
||||
|
@ -49,7 +62,8 @@ public:
|
|||
}
|
||||
|
||||
const hlsl::SigPoint *getSigPoint() const { return sigPoint; }
|
||||
const hlsl::Semantic *getSemantic() const { return semantic; }
|
||||
const SemanticInfo &getSemanticInfo() const { return semanticInfo; }
|
||||
std::string getSemanticStr() const;
|
||||
|
||||
uint32_t getSpirvTypeId() const { return typeId; }
|
||||
|
||||
|
@ -58,9 +72,6 @@ public:
|
|||
|
||||
const VKBuiltInAttr *getBuiltInAttr() const { return builtinAttr; }
|
||||
|
||||
std::string getSemanticStr() const;
|
||||
uint32_t getSemanticIndex() const { return semanticIndex; }
|
||||
|
||||
bool isSpirvBuitin() const { return isBuiltin; }
|
||||
void setIsSpirvBuiltin() { isBuiltin = true; }
|
||||
|
||||
|
@ -70,20 +81,17 @@ public:
|
|||
const VKLocationAttr *getLocationAttr() const { return location; }
|
||||
void setLocationAttr(const VKLocationAttr *loc) { location = loc; }
|
||||
|
||||
const VKIndexAttr *getIndexAttr() const { return indexAttr; }
|
||||
void setIndexAttr(const VKIndexAttr *idx) { indexAttr = idx; }
|
||||
|
||||
uint32_t getLocationCount() const { return locationCount; }
|
||||
|
||||
private:
|
||||
/// HLSL SigPoint. It uniquely identifies each set of parameters that may be
|
||||
/// input or output for each entry point.
|
||||
const hlsl::SigPoint *sigPoint;
|
||||
/// Original HLSL semantic string in the source code.
|
||||
llvm::StringRef semanticStr;
|
||||
/// HLSL semantic.
|
||||
const hlsl::Semantic *semantic;
|
||||
/// Original HLSL semantic string (without index) in the source code.
|
||||
llvm::StringRef semanticName;
|
||||
/// HLSL semantic index.
|
||||
uint32_t semanticIndex;
|
||||
/// Information about HLSL semantic string.
|
||||
SemanticInfo semanticInfo;
|
||||
/// SPIR-V BuiltIn attribute.
|
||||
const VKBuiltInAttr *builtinAttr;
|
||||
/// SPIR-V <type-id>.
|
||||
|
@ -96,6 +104,8 @@ private:
|
|||
spv::StorageClass storageClass;
|
||||
/// Location assignment if input/output variable.
|
||||
const VKLocationAttr *location;
|
||||
/// Index assignment if PS output variable
|
||||
const VKIndexAttr *indexAttr;
|
||||
/// How many locations this stage variable takes.
|
||||
uint32_t locationCount;
|
||||
};
|
||||
|
@ -514,17 +524,6 @@ private:
|
|||
const DeclContext *decl, uint32_t arraySize, ContextUsageKind usageKind,
|
||||
llvm::StringRef typeName, llvm::StringRef varName);
|
||||
|
||||
/// A struct containing information about a particular HLSL semantic.
|
||||
struct SemanticInfo {
|
||||
llvm::StringRef str; ///< The original semantic string
|
||||
const hlsl::Semantic *semantic; ///< The unique semantic object
|
||||
llvm::StringRef name; ///< The semantic string without index
|
||||
uint32_t index; ///< The semantic index
|
||||
SourceLocation loc; ///< Source code location
|
||||
|
||||
bool isValid() const { return semantic != nullptr; }
|
||||
};
|
||||
|
||||
/// Returns the given decl's HLSL semantic information.
|
||||
static SemanticInfo getStageVarSemantic(const NamedDecl *decl);
|
||||
|
||||
|
@ -565,6 +564,9 @@ private:
|
|||
uint32_t createSpirvStageVar(StageVar *, const NamedDecl *decl,
|
||||
const llvm::StringRef name, SourceLocation);
|
||||
|
||||
/// Returns true if all vk:: attributes usages are valid.
|
||||
bool validateVKAttributes(const NamedDecl *decl);
|
||||
|
||||
/// Returns true if all vk::builtin usages are valid.
|
||||
bool validateVKBuiltins(const NamedDecl *decl,
|
||||
const hlsl::SigPoint *sigPoint);
|
||||
|
@ -711,6 +713,14 @@ public:
|
|||
GlPerVertex glPerVertex;
|
||||
};
|
||||
|
||||
hlsl::Semantic::Kind SemanticInfo::getKind() const {
|
||||
assert(semantic);
|
||||
return semantic->GetKind();
|
||||
}
|
||||
bool SemanticInfo::isTarget() const {
|
||||
return semantic && semantic->GetKind() == hlsl::Semantic::Kind::Target;
|
||||
}
|
||||
|
||||
void CounterIdAliasPair::assign(const CounterIdAliasPair &srcPair,
|
||||
ModuleBuilder &builder,
|
||||
TypeTranslator &translator) const {
|
||||
|
|
|
@ -859,6 +859,11 @@ void ModuleBuilder::decorateLocation(uint32_t targetId, uint32_t location) {
|
|||
theModule.addDecoration(d, targetId);
|
||||
}
|
||||
|
||||
void ModuleBuilder::decorateIndex(uint32_t targetId, uint32_t index) {
|
||||
const Decoration *d = Decoration::getIndex(theContext, index);
|
||||
theModule.addDecoration(d, targetId);
|
||||
}
|
||||
|
||||
void ModuleBuilder::decorateSpecId(uint32_t targetId, uint32_t specId) {
|
||||
const Decoration *d = Decoration::getSpecId(theContext, specId);
|
||||
theModule.addDecoration(d, targetId);
|
||||
|
|
|
@ -10483,6 +10483,10 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
|
|||
declAttr = ::new (S.Context) VKLocationAttr(A.getRange(), S.Context,
|
||||
ValidateAttributeIntArg(S, A), A.getAttributeSpellingListIndex());
|
||||
break;
|
||||
case AttributeList::AT_VKIndex:
|
||||
declAttr = ::new (S.Context) VKIndexAttr(A.getRange(), S.Context,
|
||||
ValidateAttributeIntArg(S, A), A.getAttributeSpellingListIndex());
|
||||
break;
|
||||
case AttributeList::AT_VKBinding:
|
||||
declAttr = ::new (S.Context) VKBindingAttr(A.getRange(), S.Context,
|
||||
ValidateAttributeIntArg(S, A), ValidateAttributeIntArg(S, A, 1),
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Run: %dxc -T ps_6_0 -E main
|
||||
|
||||
struct PSOut {
|
||||
[[vk::location(1), vk::index(5)]] float4 a: SV_Target0;
|
||||
[[vk::location(0), vk::index(1)]] float4 b: SV_Target1;
|
||||
};
|
||||
|
||||
PSOut main() {
|
||||
PSOut o = (PSOut)0;
|
||||
return o;
|
||||
}
|
||||
|
||||
// CHECK: :4:7: error: dual-source blending should use vk::location 0
|
||||
// CHECK: :4:24: error: dual-source blending only accepts 0 or 1 as vk::index
|
|
@ -0,0 +1,10 @@
|
|||
// Run: %dxc -T vs_6_0 -E main
|
||||
|
||||
int main(
|
||||
[[vk::index(1)]] int a : A
|
||||
) : B {
|
||||
return a;
|
||||
}
|
||||
|
||||
// CHECK: :4:7: error: vk::index only allowed in pixel shader
|
||||
// CHECK: :4:7: error: vk::index should be used together with vk::location for dual-source blending
|
|
@ -0,0 +1,16 @@
|
|||
// Run: %dxc -T ps_6_0 -E main
|
||||
|
||||
// CHECK: OpDecorate %out_var_SV_Target0 Location 0
|
||||
// CHECK: OpDecorate %out_var_SV_Target0 Index 0
|
||||
// CHECK: OpDecorate %out_var_SV_Target1 Location 0
|
||||
// CHECK: OpDecorate %out_var_SV_Target1 Index 1
|
||||
|
||||
struct PSOut {
|
||||
[[vk::location(0), vk::index(0)]] float4 a: SV_Target0;
|
||||
[[vk::location(0), vk::index(1)]] float4 b: SV_Target1;
|
||||
};
|
||||
|
||||
PSOut main() {
|
||||
PSOut o = (PSOut)0;
|
||||
return o;
|
||||
}
|
|
@ -482,6 +482,15 @@ TEST_F(FileTest, SemanticInstanceIDPS) {
|
|||
runFileTest("semantic.instance-id.ps.hlsl");
|
||||
}
|
||||
TEST_F(FileTest, SemanticTargetPS) { runFileTest("semantic.target.ps.hlsl"); }
|
||||
TEST_F(FileTest, SemanticTargetDualBlend) {
|
||||
runFileTest("semantic.target.dual-blend.hlsl");
|
||||
}
|
||||
TEST_F(FileTest, SemanticTargetDualBlendError1) {
|
||||
runFileTest("semantic.target.dual-blend.error1.hlsl", Expect::Failure);
|
||||
}
|
||||
TEST_F(FileTest, SemanticTargetDualBlendError2) {
|
||||
runFileTest("semantic.target.dual-blend.error2.hlsl", Expect::Failure);
|
||||
}
|
||||
TEST_F(FileTest, SemanticDepthPS) { runFileTest("semantic.depth.ps.hlsl"); }
|
||||
TEST_F(FileTest, SemanticDepthGreaterEqualPS) {
|
||||
runFileTest("semantic.depth-greater-equal.ps.hlsl");
|
||||
|
@ -1443,9 +1452,7 @@ TEST_F(FileTest, NonFpColMajorError) {
|
|||
TEST_F(FileTest, NamespaceFunctions) {
|
||||
runFileTest("namespace.functions.hlsl");
|
||||
}
|
||||
TEST_F(FileTest, NamespaceGlobals) {
|
||||
runFileTest("namespace.globals.hlsl");
|
||||
}
|
||||
TEST_F(FileTest, NamespaceGlobals) { runFileTest("namespace.globals.hlsl"); }
|
||||
TEST_F(FileTest, NamespaceResources) {
|
||||
runFileTest("namespace.resources.hlsl");
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче