[spirv] Translate SubpassInput(MS) and their methods (#1013)
This commit is contained in:
Родитель
fa1e7cddd0
Коммит
4cbada6181
|
@ -166,6 +166,44 @@ Descriptors
|
||||||
To specify which Vulkan descriptor a particular resource binds to, use the
|
To specify which Vulkan descriptor a particular resource binds to, use the
|
||||||
``[[vk::binding(X[, Y])]]`` attribute.
|
``[[vk::binding(X[, Y])]]`` attribute.
|
||||||
|
|
||||||
|
Subpass inputs
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Within a Vulkan `rendering pass <https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#renderpass>`_,
|
||||||
|
a subpass can write results to an output target that can then be read by the
|
||||||
|
next subpass as an input subpass. The "Subpass Input" feature regards the
|
||||||
|
ability to read an output target.
|
||||||
|
|
||||||
|
Subpasses are read through two new builtin resource types, available only in
|
||||||
|
pixel shader:
|
||||||
|
|
||||||
|
.. code:: hlsl
|
||||||
|
|
||||||
|
class SubpassInput<T> {
|
||||||
|
T SubpassLoad();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubpassInputMS<T> {
|
||||||
|
T SubpassLoad(int sampleIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
In the above, ``T`` is a scalar or vector type. If omitted, it will defaults to
|
||||||
|
``float4``.
|
||||||
|
|
||||||
|
Subpass inputs are implicitly addressed by the pixel's (x, y, layer) coordinate.
|
||||||
|
These objects support reading the subpass input through the methods as shown
|
||||||
|
in the above.
|
||||||
|
|
||||||
|
A subpass input is selected by using a new attribute ``vk::input_attachment_index``.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code:: hlsl
|
||||||
|
|
||||||
|
[[vk::input_attachment_index(i)]] SubpassInput input;
|
||||||
|
|
||||||
|
An ``vk::input_attachment_index`` of ``i`` selects the ith entry in the input
|
||||||
|
pass list. (See Vulkan API spec for more information.)
|
||||||
|
|
||||||
Push constants
|
Push constants
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -209,6 +247,9 @@ The namespace ``vk`` will be used for all Vulkan attributes:
|
||||||
- ``push_constant``: For marking a variable as the push constant block. Allowed
|
- ``push_constant``: For marking a variable as the push constant block. Allowed
|
||||||
on global variables of struct type. At most one variable can be marked as
|
on global variables of struct type. At most one variable can be marked as
|
||||||
``push_constant`` in a shader.
|
``push_constant`` in a shader.
|
||||||
|
- ``input_attachment_index(X)``: To associate the Xth entry in the input pass
|
||||||
|
list to the annotated object. Only allowed on objects whose type are
|
||||||
|
``SubpassInput`` or ``SubpassInputMS``.
|
||||||
- ``builtin("X")``: For specifying an entity should be translated into a certain
|
- ``builtin("X")``: For specifying an entity should be translated into a certain
|
||||||
Vulkan builtin variable. Allowed on function parameters, function returns,
|
Vulkan builtin variable. Allowed on function parameters, function returns,
|
||||||
and struct fields.
|
and struct fields.
|
||||||
|
|
|
@ -357,6 +357,10 @@ public:
|
||||||
void decorateDSetBinding(uint32_t targetId, uint32_t setNumber,
|
void decorateDSetBinding(uint32_t targetId, uint32_t setNumber,
|
||||||
uint32_t bindingNumber);
|
uint32_t bindingNumber);
|
||||||
|
|
||||||
|
/// \brief Decorates the given target <result-id> with the given input
|
||||||
|
/// attchment index number.
|
||||||
|
void decorateInputAttachmentIndex(uint32_t targetId, uint32_t indexNumber);
|
||||||
|
|
||||||
/// \brief Decorates the given target <result-id> with the given decoration
|
/// \brief Decorates the given target <result-id> with the given decoration
|
||||||
/// (without additional parameters).
|
/// (without additional parameters).
|
||||||
void decorate(uint32_t targetId, spv::Decoration);
|
void decorate(uint32_t targetId, spv::Decoration);
|
||||||
|
|
|
@ -412,6 +412,9 @@ uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) {
|
||||||
resourceVars.emplace_back(id, getResourceCategory(var->getType()), regAttr,
|
resourceVars.emplace_back(id, getResourceCategory(var->getType()), regAttr,
|
||||||
bindingAttr, counterBindingAttr);
|
bindingAttr, counterBindingAttr);
|
||||||
|
|
||||||
|
if (const auto *inputAttachment = var->getAttr<VKInputAttachmentIndexAttr>())
|
||||||
|
theBuilder.decorateInputAttachmentIndex(id, inputAttachment->getIndex());
|
||||||
|
|
||||||
if (isACRWSBuffer) {
|
if (isACRWSBuffer) {
|
||||||
// For {Append|Consume|RW}StructuredBuffer, we need to always create another
|
// For {Append|Consume|RW}StructuredBuffer, we need to always create another
|
||||||
// variable for its associated counter.
|
// variable for its associated counter.
|
||||||
|
@ -874,7 +877,7 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
|
||||||
// We have checked that not all of the stage variables have explicit
|
// We have checked that not all of the stage variables have explicit
|
||||||
// location assignment.
|
// location assignment.
|
||||||
emitError("partial explicit stage %select{output|input}0 location "
|
emitError("partial explicit stage %select{output|input}0 location "
|
||||||
"assignment via [[vk::location(X)]] unsupported",
|
"assignment via vk::location(X) unsupported",
|
||||||
{})
|
{})
|
||||||
<< forInput;
|
<< forInput;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -761,6 +761,11 @@ void ModuleBuilder::decorateDSetBinding(uint32_t targetId, uint32_t setNumber,
|
||||||
d = Decoration::getBinding(theContext, bindingNumber);
|
d = Decoration::getBinding(theContext, bindingNumber);
|
||||||
theModule.addDecoration(d, targetId);
|
theModule.addDecoration(d, targetId);
|
||||||
}
|
}
|
||||||
|
void ModuleBuilder::decorateInputAttachmentIndex(uint32_t targetId,
|
||||||
|
uint32_t indexNumber) {
|
||||||
|
const auto *d = Decoration::getInputAttachmentIndex(theContext, indexNumber);
|
||||||
|
theModule.addDecoration(d, targetId);
|
||||||
|
}
|
||||||
|
|
||||||
void ModuleBuilder::decorateLocation(uint32_t targetId, uint32_t location) {
|
void ModuleBuilder::decorateLocation(uint32_t targetId, uint32_t location) {
|
||||||
const Decoration *d =
|
const Decoration *d =
|
||||||
|
@ -973,10 +978,10 @@ uint32_t ModuleBuilder::getImageType(uint32_t sampledType, spv::Dim dim,
|
||||||
} else {
|
} else {
|
||||||
requireCapability(spv::Capability::Sampled1D);
|
requireCapability(spv::Capability::Sampled1D);
|
||||||
}
|
}
|
||||||
}
|
} else if (dim == spv::Dim::Buffer) {
|
||||||
|
|
||||||
if (dim == spv::Dim::Buffer) {
|
|
||||||
requireCapability(spv::Capability::SampledBuffer);
|
requireCapability(spv::Capability::SampledBuffer);
|
||||||
|
} else if (dim == spv::Dim::SubpassData) {
|
||||||
|
requireCapability(spv::Capability::InputAttachment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isArray && ms) {
|
if (isArray && ms) {
|
||||||
|
|
|
@ -879,7 +879,59 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
|
||||||
theBuilder.endFunction();
|
theBuilder.endFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPIRVEmitter::validateVKAttributes(const NamedDecl *decl) {
|
bool SPIRVEmitter::validateVKAttributes(const NamedDecl *decl) {
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
if (decl->hasAttr<HLSLRowMajorAttr>()) {
|
||||||
|
emitWarning("row_major attribute for stand-alone matrix is not supported",
|
||||||
|
decl->getAttr<HLSLRowMajorAttr>()->getLocation());
|
||||||
|
}
|
||||||
|
if (decl->hasAttr<HLSLColumnMajorAttr>()) {
|
||||||
|
emitWarning(
|
||||||
|
"column_major attribute for stand-alone matrix is not supported",
|
||||||
|
decl->getAttr<HLSLColumnMajorAttr>()->getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto *varDecl = dyn_cast<VarDecl>(decl)) {
|
||||||
|
const auto varType = varDecl->getType();
|
||||||
|
if ((TypeTranslator::isSubpassInput(varType) ||
|
||||||
|
TypeTranslator::isSubpassInputMS(varType)) &&
|
||||||
|
!varDecl->hasAttr<VKInputAttachmentIndexAttr>()) {
|
||||||
|
emitError("missing vk::input_attachment_index attribute",
|
||||||
|
varDecl->getLocation());
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto *iaiAttr = decl->getAttr<VKInputAttachmentIndexAttr>()) {
|
||||||
|
if (!shaderModel.IsPS()) {
|
||||||
|
emitError("SubpassInput(MS) only allowed in pixel shader",
|
||||||
|
decl->getLocation());
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decl->isExternallyVisible()) {
|
||||||
|
emitError("SubpassInput(MS) must be externally visible",
|
||||||
|
decl->getLocation());
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only allow VKInputAttachmentIndexAttr to be attached to global
|
||||||
|
// variables. So it should be fine to cast here.
|
||||||
|
const auto elementType =
|
||||||
|
hlsl::GetHLSLResourceResultType(cast<VarDecl>(decl)->getType());
|
||||||
|
|
||||||
|
if (!TypeTranslator::isScalarType(elementType) &&
|
||||||
|
!TypeTranslator::isVectorType(elementType)) {
|
||||||
|
emitError(
|
||||||
|
"only scalar/vector types allowed as SubpassInput(MS) parameter type",
|
||||||
|
decl->getLocation());
|
||||||
|
// Return directly to avoid further type processing, which will hit
|
||||||
|
// asserts in TypeTranslator.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The frontend will make sure that
|
// The frontend will make sure that
|
||||||
// * vk::push_constant applies to global variables of struct type
|
// * vk::push_constant applies to global variables of struct type
|
||||||
// * vk::binding applies to global variables or cbuffers/tbuffers
|
// * vk::binding applies to global variables or cbuffers/tbuffers
|
||||||
|
@ -903,14 +955,18 @@ void SPIRVEmitter::validateVKAttributes(const NamedDecl *decl) {
|
||||||
emitError("cannot have more than one push constant block", loc);
|
emitError("cannot have more than one push constant block", loc);
|
||||||
emitNote("push constant block previously defined here",
|
emitNote("push constant block previously defined here",
|
||||||
seenPushConstantAt);
|
seenPushConstantAt);
|
||||||
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decl->hasAttr<VKBindingAttr>()) {
|
if (decl->hasAttr<VKBindingAttr>()) {
|
||||||
emitError("'push_constant' attribute cannot be used together with "
|
emitError("vk::push_constant attribute cannot be used together with "
|
||||||
"'binding' attribute",
|
"vk::binding attribute",
|
||||||
loc);
|
loc);
|
||||||
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
|
void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
|
||||||
|
@ -930,7 +986,8 @@ void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
|
||||||
emitWarning("packoffset ignored since not supported", packing->Loc);
|
emitWarning("packoffset ignored since not supported", packing->Loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateVKAttributes(bufferDecl);
|
if (!validateVKAttributes(bufferDecl))
|
||||||
|
return;
|
||||||
(void)declIdMapper.createCTBuffer(bufferDecl);
|
(void)declIdMapper.createCTBuffer(bufferDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,17 +1010,8 @@ void SPIRVEmitter::doRecordDecl(const RecordDecl *recordDecl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
|
void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
|
||||||
validateVKAttributes(decl);
|
if (!validateVKAttributes(decl))
|
||||||
|
return;
|
||||||
if (decl->hasAttr<HLSLRowMajorAttr>()) {
|
|
||||||
emitWarning("row_major attribute for stand-alone matrix is not supported",
|
|
||||||
decl->getAttr<HLSLRowMajorAttr>()->getLocation());
|
|
||||||
}
|
|
||||||
if (decl->hasAttr<HLSLColumnMajorAttr>()) {
|
|
||||||
emitWarning(
|
|
||||||
"column_major attribute for stand-alone matrix is not supported",
|
|
||||||
decl->getAttr<HLSLColumnMajorAttr>()->getLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decl->hasAttr<VKPushConstantAttr>()) {
|
if (decl->hasAttr<VKPushConstantAttr>()) {
|
||||||
// This is a VarDecl for PushConstant block.
|
// This is a VarDecl for PushConstant block.
|
||||||
|
@ -2338,6 +2386,18 @@ uint32_t SPIRVEmitter::processGetSamplePosition(const CXXMemberCallExpr *expr) {
|
||||||
return emitGetSamplePosition(sampleCount, doExpr(expr->getArg(0)));
|
return emitGetSamplePosition(sampleCount, doExpr(expr->getArg(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpirvEvalInfo SPIRVEmitter::processSubpassLoad(const CXXMemberCallExpr *expr) {
|
||||||
|
const auto *object = expr->getImplicitObjectArgument()->IgnoreParens();
|
||||||
|
const uint32_t sample = expr->getNumArgs() == 1 ? doExpr(expr->getArg(0)) : 0;
|
||||||
|
const uint32_t zero = theBuilder.getConstantInt32(0);
|
||||||
|
const uint32_t location = theBuilder.getConstantComposite(
|
||||||
|
theBuilder.getVecType(theBuilder.getInt32Type(), 2), {zero, zero});
|
||||||
|
|
||||||
|
return processBufferTextureLoad(object, location, /*constOffset*/ 0,
|
||||||
|
/*varOffset*/ 0, /*lod*/ sample,
|
||||||
|
/*residencyCode*/ 0);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
||||||
const auto *object = expr->getImplicitObjectArgument();
|
const auto *object = expr->getImplicitObjectArgument();
|
||||||
|
@ -2625,7 +2685,10 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
|
||||||
// The result type of an OpImageFetch must be a vec4 of float or int.
|
// The result type of an OpImageFetch must be a vec4 of float or int.
|
||||||
const auto type = object->getType();
|
const auto type = object->getType();
|
||||||
assert(TypeTranslator::isBuffer(type) || TypeTranslator::isRWBuffer(type) ||
|
assert(TypeTranslator::isBuffer(type) || TypeTranslator::isRWBuffer(type) ||
|
||||||
TypeTranslator::isTexture(type) || TypeTranslator::isRWTexture(type));
|
TypeTranslator::isTexture(type) || TypeTranslator::isRWTexture(type) ||
|
||||||
|
TypeTranslator::isSubpassInput(type) ||
|
||||||
|
TypeTranslator::isSubpassInputMS(type));
|
||||||
|
|
||||||
const bool doFetch =
|
const bool doFetch =
|
||||||
TypeTranslator::isBuffer(type) || TypeTranslator::isTexture(type);
|
TypeTranslator::isBuffer(type) || TypeTranslator::isTexture(type);
|
||||||
|
|
||||||
|
@ -2633,7 +2696,8 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
|
||||||
|
|
||||||
// For Texture2DMS and Texture2DMSArray, Sample must be used rather than Lod.
|
// For Texture2DMS and Texture2DMSArray, Sample must be used rather than Lod.
|
||||||
uint32_t sampleNumber = 0;
|
uint32_t sampleNumber = 0;
|
||||||
if (TypeTranslator::isTextureMS(type)) {
|
if (TypeTranslator::isTextureMS(type) ||
|
||||||
|
TypeTranslator::isSubpassInputMS(type)) {
|
||||||
sampleNumber = lod;
|
sampleNumber = lod;
|
||||||
lod = 0;
|
lod = 0;
|
||||||
}
|
}
|
||||||
|
@ -3351,6 +3415,9 @@ SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
|
||||||
case IntrinsicOp::MOP_GetSamplePosition:
|
case IntrinsicOp::MOP_GetSamplePosition:
|
||||||
retVal = processGetSamplePosition(expr);
|
retVal = processGetSamplePosition(expr);
|
||||||
break;
|
break;
|
||||||
|
case IntrinsicOp::MOP_SubpassLoad:
|
||||||
|
retVal = processSubpassLoad(expr);
|
||||||
|
break;
|
||||||
case IntrinsicOp::MOP_GatherCmpGreen:
|
case IntrinsicOp::MOP_GatherCmpGreen:
|
||||||
case IntrinsicOp::MOP_GatherCmpBlue:
|
case IntrinsicOp::MOP_GatherCmpBlue:
|
||||||
case IntrinsicOp::MOP_GatherCmpAlpha:
|
case IntrinsicOp::MOP_GatherCmpAlpha:
|
||||||
|
|
|
@ -275,8 +275,9 @@ private:
|
||||||
const llvm::SmallVector<uint32_t, 4> &indices);
|
const llvm::SmallVector<uint32_t, 4> &indices);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Validates that vk::* attributes are used correctly.
|
/// Validates that vk::* attributes are used correctly and returns false if
|
||||||
void validateVKAttributes(const NamedDecl *decl);
|
/// errors are found.
|
||||||
|
bool validateVKAttributes(const NamedDecl *decl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Processes the given expr, casts the result into the given bool (vector)
|
/// Processes the given expr, casts the result into the given bool (vector)
|
||||||
|
@ -732,6 +733,10 @@ private:
|
||||||
/// Texture2DMS(Array).
|
/// Texture2DMS(Array).
|
||||||
uint32_t processGetSamplePosition(const CXXMemberCallExpr *);
|
uint32_t processGetSamplePosition(const CXXMemberCallExpr *);
|
||||||
|
|
||||||
|
/// \brief Processes the SubpassLoad intrinsic function call on a
|
||||||
|
/// SubpassInput(MS).
|
||||||
|
SpirvEvalInfo processSubpassLoad(const CXXMemberCallExpr *);
|
||||||
|
|
||||||
/// \brief Generates SPIR-V instructions for the .Append()/.Consume() call on
|
/// \brief Generates SPIR-V instructions for the .Append()/.Consume() call on
|
||||||
/// the given {Append|Consume}StructuredBuffer. Returns the <result-id> of
|
/// the given {Append|Consume}StructuredBuffer. Returns the <result-id> of
|
||||||
/// the loaded value for .Consume; returns zero for .Append().
|
/// the loaded value for .Consume; returns zero for .Append().
|
||||||
|
|
|
@ -613,6 +613,20 @@ bool TypeTranslator::isSampler(QualType type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TypeTranslator::isSubpassInput(QualType type) {
|
||||||
|
if (const auto *rt = type->getAs<RecordType>())
|
||||||
|
return rt->getDecl()->getName() == "SubpassInput";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TypeTranslator::isSubpassInputMS(QualType type) {
|
||||||
|
if (const auto *rt = type->getAs<RecordType>())
|
||||||
|
return rt->getDecl()->getName() == "SubpassInputMS";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeTranslator::isVectorType(QualType type, QualType *elemType,
|
bool TypeTranslator::isVectorType(QualType type, QualType *elemType,
|
||||||
uint32_t *elemCount) {
|
uint32_t *elemCount) {
|
||||||
bool isVec = false;
|
bool isVec = false;
|
||||||
|
@ -999,6 +1013,14 @@ uint32_t TypeTranslator::translateResourceType(QualType type, LayoutRule rule) {
|
||||||
return translateType(hlsl::GetHLSLResourceResultType(type), rule);
|
return translateType(hlsl::GetHLSLResourceResultType(type), rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name == "SubpassInput" || name == "SubpassInputMS") {
|
||||||
|
const auto sampledType = hlsl::GetHLSLResourceResultType(type);
|
||||||
|
return theBuilder.getImageType(
|
||||||
|
translateType(getElementType(sampledType)), spv::Dim::SubpassData,
|
||||||
|
/*depth*/ 0, /*isArray*/ false, /*ms*/ name == "SubpassInputMS",
|
||||||
|
/*sampled*/ 2);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,12 @@ public:
|
||||||
/// \brief Returns true if the given type is an HLSL sampler type.
|
/// \brief Returns true if the given type is an HLSL sampler type.
|
||||||
static bool isSampler(QualType);
|
static bool isSampler(QualType);
|
||||||
|
|
||||||
|
/// \brief Returns true if the given type is SubpassInput.
|
||||||
|
static bool isSubpassInput(QualType);
|
||||||
|
|
||||||
|
/// \brief Returns true if the given type is SubpassInputMS.
|
||||||
|
static bool isSubpassInputMS(QualType);
|
||||||
|
|
||||||
/// \brief Returns true if the given type will be translated into a SPIR-V
|
/// \brief Returns true if the given type will be translated into a SPIR-V
|
||||||
/// scalar type. This includes normal scalar types, vectors of size 1, and
|
/// scalar type. This includes normal scalar types, vectors of size 1, and
|
||||||
/// 1x1 matrices. If scalarType is not nullptr, writes the scalar type to
|
/// 1x1 matrices. If scalarType is not nullptr, writes the scalar type to
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Run: %dxc -T ps_6_0 -E main
|
// Run: %dxc -T vs_6_0 -E main
|
||||||
|
|
||||||
struct S {
|
struct S {
|
||||||
float f;
|
float f;
|
||||||
|
@ -11,4 +11,4 @@ float main() : A {
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: :7:3: error: 'push_constant' attribute cannot be used together with 'binding' attribute
|
// CHECK: :7:3: error: vk::push_constant attribute cannot be used together with vk::binding attribute
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Run: %dxc -T ps_6_0 -E main
|
||||||
|
|
||||||
|
// CHECK: OpDecorate %SI0 InputAttachmentIndex 0
|
||||||
|
// CHECK: OpDecorate %SI1 InputAttachmentIndex 1
|
||||||
|
// CHECK: OpDecorate %SI2 InputAttachmentIndex 2
|
||||||
|
|
||||||
|
// CHECK: OpDecorate %SI1 DescriptorSet 0
|
||||||
|
// CHECK: OpDecorate %SI1 Binding 5
|
||||||
|
|
||||||
|
// CHECK: OpDecorate %SI2 DescriptorSet 3
|
||||||
|
// CHECK: OpDecorate %SI2 Binding 5
|
||||||
|
|
||||||
|
// CHECK: OpDecorate %SI0 DescriptorSet 0
|
||||||
|
// CHECK: OpDecorate %SI0 Binding 0
|
||||||
|
|
||||||
|
[[vk::input_attachment_index(0)]]
|
||||||
|
SubpassInput SI0;
|
||||||
|
|
||||||
|
[[vk::input_attachment_index(1), vk::binding(5)]]
|
||||||
|
SubpassInput SI1;
|
||||||
|
|
||||||
|
[[vk::input_attachment_index(2), vk::binding(5, 3)]]
|
||||||
|
SubpassInput SI2;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Run: %dxc -T ps_6_0 -E main
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
float a;
|
||||||
|
float2 b;
|
||||||
|
float c;
|
||||||
|
};
|
||||||
|
|
||||||
|
SubpassInput SI0; // error
|
||||||
|
|
||||||
|
[[vk::input_attachment_index(0)]]
|
||||||
|
SubpassInput<S> SI1; // error
|
||||||
|
|
||||||
|
[[vk::input_attachment_index(1)]]
|
||||||
|
static SubpassInput SI2; // error
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: :9:14: error: missing vk::input_attachment_index attribute
|
||||||
|
// CHECK: :12:17: error: only scalar/vector types allowed as SubpassInput(MS) parameter type
|
||||||
|
// CHECK: :15:21: error: SubpassInput(MS) must be externally visible
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Run: %dxc -T ps_6_0 -E main
|
||||||
|
|
||||||
|
// CHECK: OpCapability InputAttachment
|
||||||
|
|
||||||
|
// CHECK: %type_subpass_image = OpTypeImage %float SubpassData 0 0 0 2 Unknown
|
||||||
|
// CHECK: %_ptr_UniformConstant_type_subpass_image = OpTypePointer UniformConstant %type_subpass_image
|
||||||
|
|
||||||
|
// CHECK: %type_subpass_image_0 = OpTypeImage %int SubpassData 0 0 0 2 Unknown
|
||||||
|
// CHECK: %_ptr_UniformConstant_type_subpass_image_0 = OpTypePointer UniformConstant %type_subpass_image_0
|
||||||
|
|
||||||
|
// CHECK: %type_subpass_image_1 = OpTypeImage %uint SubpassData 0 0 0 2 Unknown
|
||||||
|
// CHECK: %_ptr_UniformConstant_type_subpass_image_1 = OpTypePointer UniformConstant %type_subpass_image_1
|
||||||
|
|
||||||
|
// CHECK: %type_subpass_image_2 = OpTypeImage %uint SubpassData 0 0 1 2 Unknown
|
||||||
|
// CHECK: %_ptr_UniformConstant_type_subpass_image_2 = OpTypePointer UniformConstant %type_subpass_image_2
|
||||||
|
|
||||||
|
// CHECK: %type_subpass_image_3 = OpTypeImage %float SubpassData 0 0 1 2 Unknown
|
||||||
|
// CHECK: %_ptr_UniformConstant_type_subpass_image_3 = OpTypePointer UniformConstant %type_subpass_image_3
|
||||||
|
|
||||||
|
// CHECK: %type_subpass_image_4 = OpTypeImage %int SubpassData 0 0 1 2 Unknown
|
||||||
|
// CHECK: %_ptr_UniformConstant_type_subpass_image_4 = OpTypePointer UniformConstant %type_subpass_image_4
|
||||||
|
|
||||||
|
// CHECK: [[v2i00:%\d+]] = OpConstantComposite %v2int %int_0 %int_0
|
||||||
|
|
||||||
|
// CHCK: %SI_f4 = OpVariable %_ptr_UniformConstant_type_subpass_image UniformConstant
|
||||||
|
[[vk::input_attachment_index(0)]] SubpassInput SI_f4;
|
||||||
|
// CHCK: %SI_i3 = OpVariable %_ptr_UniformConstant_type_subpass_image_0 UniformConstant
|
||||||
|
[[vk::input_attachment_index(1)]] SubpassInput<int3> SI_i3;
|
||||||
|
// CHCK: %SI_u2 = OpVariable %_ptr_UniformConstant_type_subpass_image_1 UniformConstant
|
||||||
|
[[vk::input_attachment_index(2)]] SubpassInput<uint2> SI_u2;
|
||||||
|
// CHCK: %SI_f1 = OpVariable %_ptr_UniformConstant_type_subpass_image UniformConstant
|
||||||
|
[[vk::input_attachment_index(3)]] SubpassInput<float> SI_f1;
|
||||||
|
|
||||||
|
// CHCK: %SIMS_u4 = OpVariable %_ptr_UniformConstant_type_subpass_image_2 UniformConstant
|
||||||
|
[[vk::input_attachment_index(10)]] SubpassInputMS<uint4> SIMS_u4;
|
||||||
|
// CHCK: %SIMS_f3 = OpVariable %_ptr_UniformConstant_type_subpass_image_3 UniformConstant
|
||||||
|
[[vk::input_attachment_index(11)]] SubpassInputMS<float3> SIMS_f3;
|
||||||
|
// CHCK: %SIMS_i2 = OpVariable %_ptr_UniformConstant_type_subpass_image_4 UniformConstant
|
||||||
|
[[vk::input_attachment_index(12)]] SubpassInputMS<int2> SIMS_i2;
|
||||||
|
// CHCK: %SIMS_u1 = OpVariable %_ptr_UniformConstant_type_subpass_image_2 UniformConstant
|
||||||
|
[[vk::input_attachment_index(13)]] SubpassInputMS<uint> SIMS_u1;
|
||||||
|
|
||||||
|
float4 main() : SV_Target {
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image %SI_f4
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4float [[img]] [[v2i00]] None
|
||||||
|
// CHECK-NEXT: OpStore %v0 [[texel]]
|
||||||
|
float4 v0 = SI_f4.SubpassLoad();
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_0 %SI_i3
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4int [[img]] [[v2i00]] None
|
||||||
|
// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v3int [[texel]] [[texel]] 0 1 2
|
||||||
|
// CHECK-NEXT: OpStore %v1 [[val]]
|
||||||
|
int3 v1 = SI_i3.SubpassLoad();
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_1 %SI_u2
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4uint [[img]] [[v2i00]] None
|
||||||
|
// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v2uint [[texel]] [[texel]] 0 1
|
||||||
|
// CHECK-NEXT: OpStore %v2 [[val]]
|
||||||
|
uint2 v2 = SI_u2.SubpassLoad();
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image %SI_f1
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4float [[img]] [[v2i00]] None
|
||||||
|
// CHECK-NEXT: [[val:%\d+]] = OpCompositeExtract %float [[texel]] 0
|
||||||
|
// CHECK-NEXT: OpStore %v3 [[val]]
|
||||||
|
float v3 = SI_f1.SubpassLoad();
|
||||||
|
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_2 %SIMS_u4
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4uint [[img]] [[v2i00]] Sample %int_1
|
||||||
|
// CHECK-NEXT: OpStore %v10 [[texel]]
|
||||||
|
uint4 v10 = SIMS_u4.SubpassLoad(1);
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_3 %SIMS_f3
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4float [[img]] [[v2i00]] Sample %int_2
|
||||||
|
// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v3float [[texel]] [[texel]] 0 1 2
|
||||||
|
// CHECK-NEXT: OpStore %v11 [[val]]
|
||||||
|
float3 v11 = SIMS_f3.SubpassLoad(2);
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_4 %SIMS_i2
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4int [[img]] [[v2i00]] Sample %int_3
|
||||||
|
// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v2int [[texel]] [[texel]] 0 1
|
||||||
|
// CHECK-NEXT: OpStore %v12 [[val]]
|
||||||
|
int2 v12 = SIMS_i2.SubpassLoad(3);
|
||||||
|
// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_2 %SIMS_u1
|
||||||
|
// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4uint [[img]] [[v2i00]] Sample %int_4
|
||||||
|
// CHECK-NEXT: [[val:%\d+]] = OpCompositeExtract %uint [[texel]] 0
|
||||||
|
// CHECK-NEXT: OpStore %v13 [[val]]
|
||||||
|
uint v13 = SIMS_u1.SubpassLoad(4);
|
||||||
|
|
||||||
|
return v0.x + v1.y + v2.x + v3 + v10.x + v11.y + v12.x + v13;
|
||||||
|
}
|
|
@ -1151,6 +1151,14 @@ TEST_F(FileTest, VulkanLayoutCBufferPackOffset) {
|
||||||
runFileTest("vk.layout.cbuffer.packoffset.hlsl", Expect::Warning);
|
runFileTest("vk.layout.cbuffer.packoffset.hlsl", Expect::Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FileTest, VulkanSubpassInput) { runFileTest("vk.subpass-input.hlsl"); }
|
||||||
|
TEST_F(FileTest, VulkanSubpassInputBinding) {
|
||||||
|
runFileTest("vk.subpass-input.binding.hlsl");
|
||||||
|
}
|
||||||
|
TEST_F(FileTest, VulkanSubpassInputError) {
|
||||||
|
runFileTest("vk.subpass-input.error.hlsl", Expect::Failure);
|
||||||
|
}
|
||||||
|
|
||||||
// HS: for different Patch Constant Functions
|
// HS: for different Patch Constant Functions
|
||||||
TEST_F(FileTest, HullShaderPCFVoid) { runFileTest("hs.pcf.void.hlsl"); }
|
TEST_F(FileTest, HullShaderPCFVoid) { runFileTest("hs.pcf.void.hlsl"); }
|
||||||
TEST_F(FileTest, HullShaderPCFTakesInputPatch) {
|
TEST_F(FileTest, HullShaderPCFTakesInputPatch) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче