[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
|
||||
``[[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
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -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
|
||||
on global variables of struct type. At most one variable can be marked as
|
||||
``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
|
||||
Vulkan builtin variable. Allowed on function parameters, function returns,
|
||||
and struct fields.
|
||||
|
|
|
@ -357,6 +357,10 @@ public:
|
|||
void decorateDSetBinding(uint32_t targetId, uint32_t setNumber,
|
||||
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
|
||||
/// (without additional parameters).
|
||||
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,
|
||||
bindingAttr, counterBindingAttr);
|
||||
|
||||
if (const auto *inputAttachment = var->getAttr<VKInputAttachmentIndexAttr>())
|
||||
theBuilder.decorateInputAttachmentIndex(id, inputAttachment->getIndex());
|
||||
|
||||
if (isACRWSBuffer) {
|
||||
// For {Append|Consume|RW}StructuredBuffer, we need to always create another
|
||||
// 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
|
||||
// location assignment.
|
||||
emitError("partial explicit stage %select{output|input}0 location "
|
||||
"assignment via [[vk::location(X)]] unsupported",
|
||||
"assignment via vk::location(X) unsupported",
|
||||
{})
|
||||
<< forInput;
|
||||
return false;
|
||||
|
|
|
@ -761,6 +761,11 @@ void ModuleBuilder::decorateDSetBinding(uint32_t targetId, uint32_t setNumber,
|
|||
d = Decoration::getBinding(theContext, bindingNumber);
|
||||
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) {
|
||||
const Decoration *d =
|
||||
|
@ -973,10 +978,10 @@ uint32_t ModuleBuilder::getImageType(uint32_t sampledType, spv::Dim dim,
|
|||
} else {
|
||||
requireCapability(spv::Capability::Sampled1D);
|
||||
}
|
||||
}
|
||||
|
||||
if (dim == spv::Dim::Buffer) {
|
||||
} else if (dim == spv::Dim::Buffer) {
|
||||
requireCapability(spv::Capability::SampledBuffer);
|
||||
} else if (dim == spv::Dim::SubpassData) {
|
||||
requireCapability(spv::Capability::InputAttachment);
|
||||
}
|
||||
|
||||
if (isArray && ms) {
|
||||
|
|
|
@ -879,7 +879,59 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
|
|||
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
|
||||
// * vk::push_constant applies to global variables of struct type
|
||||
// * 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);
|
||||
emitNote("push constant block previously defined here",
|
||||
seenPushConstantAt);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (decl->hasAttr<VKBindingAttr>()) {
|
||||
emitError("'push_constant' attribute cannot be used together with "
|
||||
"'binding' attribute",
|
||||
emitError("vk::push_constant attribute cannot be used together with "
|
||||
"vk::binding attribute",
|
||||
loc);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
|
||||
|
@ -930,7 +986,8 @@ void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
|
|||
emitWarning("packoffset ignored since not supported", packing->Loc);
|
||||
}
|
||||
}
|
||||
validateVKAttributes(bufferDecl);
|
||||
if (!validateVKAttributes(bufferDecl))
|
||||
return;
|
||||
(void)declIdMapper.createCTBuffer(bufferDecl);
|
||||
}
|
||||
|
||||
|
@ -953,17 +1010,8 @@ void SPIRVEmitter::doRecordDecl(const RecordDecl *recordDecl) {
|
|||
}
|
||||
|
||||
void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
|
||||
validateVKAttributes(decl);
|
||||
|
||||
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 (!validateVKAttributes(decl))
|
||||
return;
|
||||
|
||||
if (decl->hasAttr<VKPushConstantAttr>()) {
|
||||
// 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)));
|
||||
}
|
||||
|
||||
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
|
||||
SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
||||
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.
|
||||
const auto type = object->getType();
|
||||
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 =
|
||||
TypeTranslator::isBuffer(type) || TypeTranslator::isTexture(type);
|
||||
|
||||
|
@ -2633,7 +2696,8 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
|
|||
|
||||
// For Texture2DMS and Texture2DMSArray, Sample must be used rather than Lod.
|
||||
uint32_t sampleNumber = 0;
|
||||
if (TypeTranslator::isTextureMS(type)) {
|
||||
if (TypeTranslator::isTextureMS(type) ||
|
||||
TypeTranslator::isSubpassInputMS(type)) {
|
||||
sampleNumber = lod;
|
||||
lod = 0;
|
||||
}
|
||||
|
@ -3351,6 +3415,9 @@ SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
|
|||
case IntrinsicOp::MOP_GetSamplePosition:
|
||||
retVal = processGetSamplePosition(expr);
|
||||
break;
|
||||
case IntrinsicOp::MOP_SubpassLoad:
|
||||
retVal = processSubpassLoad(expr);
|
||||
break;
|
||||
case IntrinsicOp::MOP_GatherCmpGreen:
|
||||
case IntrinsicOp::MOP_GatherCmpBlue:
|
||||
case IntrinsicOp::MOP_GatherCmpAlpha:
|
||||
|
|
|
@ -275,8 +275,9 @@ private:
|
|||
const llvm::SmallVector<uint32_t, 4> &indices);
|
||||
|
||||
private:
|
||||
/// Validates that vk::* attributes are used correctly.
|
||||
void validateVKAttributes(const NamedDecl *decl);
|
||||
/// Validates that vk::* attributes are used correctly and returns false if
|
||||
/// errors are found.
|
||||
bool validateVKAttributes(const NamedDecl *decl);
|
||||
|
||||
private:
|
||||
/// Processes the given expr, casts the result into the given bool (vector)
|
||||
|
@ -732,6 +733,10 @@ private:
|
|||
/// Texture2DMS(Array).
|
||||
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
|
||||
/// the given {Append|Consume}StructuredBuffer. Returns the <result-id> of
|
||||
/// the loaded value for .Consume; returns zero for .Append().
|
||||
|
|
|
@ -613,6 +613,20 @@ bool TypeTranslator::isSampler(QualType type) {
|
|||
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,
|
||||
uint32_t *elemCount) {
|
||||
bool isVec = false;
|
||||
|
@ -999,6 +1013,14 @@ uint32_t TypeTranslator::translateResourceType(QualType type, LayoutRule 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,12 @@ public:
|
|||
/// \brief Returns true if the given type is an HLSL sampler type.
|
||||
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
|
||||
/// scalar type. This includes normal scalar types, vectors of size 1, and
|
||||
/// 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 {
|
||||
float f;
|
||||
|
@ -11,4 +11,4 @@ float main() : A {
|
|||
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);
|
||||
}
|
||||
|
||||
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
|
||||
TEST_F(FileTest, HullShaderPCFVoid) { runFileTest("hs.pcf.void.hlsl"); }
|
||||
TEST_F(FileTest, HullShaderPCFTakesInputPatch) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче