[spirv] Translate SubpassInput(MS) and their methods (#1013)

This commit is contained in:
Lei Zhang 2018-01-22 10:45:56 -05:00 коммит произвёл GitHub
Родитель fa1e7cddd0
Коммит 4cbada6181
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 321 добавлений и 25 удалений

Просмотреть файл

@ -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) {