[spirv] Allow the same binding number for combined image sampler (#729)

Vulkan combined image sampler is logically considered a sampled
image and a sampler bound together.
This commit is contained in:
Lei Zhang 2017-10-23 11:57:40 -07:00 коммит произвёл GitHub
Родитель 946e0b3922
Коммит 5686ccf2c4
7 изменённых файлов: 116 добавлений и 36 удалений

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

@ -678,6 +678,13 @@ If there is no register specification, the corresponding resource will be
assigned to the next available binding number, starting from 0, in descriptor assigned to the next available binding number, starting from 0, in descriptor
set #0. set #0.
Error checking
~~~~~~~~~~~~~~
Trying to reuse the same binding number of the same descriptor set results in
a compiler error, unless we have exactly two resources and one is an image and
the other is a sampler. This is to support the Vulkan combined image sampler.
Summary Summary
~~~~~~~ ~~~~~~~
@ -704,7 +711,7 @@ As an example, for the following code:
[[vk::binding(3)]] [[vk::binding(3)]]
RWBuffer<float4> rwbuffer1 : register(u5, space2); RWBuffer<float4> rwbuffer1 : register(u5, space2);
If we compile with ``-fvk-t-shift 0 10 -fvk-t-shift 1 20``: If we compile with ``-fvk-t-shift 10 0 -fvk-t-shift 20 1``:
- ``rwbuffer1`` will take binding #3 in set #0, since explicit binding - ``rwbuffer1`` will take binding #3 in set #0, since explicit binding
assignment has precedence over the rest. assignment has precedence over the rest.

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

@ -44,6 +44,16 @@ const hlsl::RegisterAssignment *getResourceBinding(const NamedDecl *decl) {
} }
return nullptr; return nullptr;
} }
/// \brief Returns the resource category for the given type.
ResourceVar::Category getResourceCategory(QualType type) {
if (TypeTranslator::isTexture(type) || TypeTranslator::isRWTexture(type))
return ResourceVar::Category::Image;
if (TypeTranslator::isSampler(type))
return ResourceVar::Category::Sampler;
return ResourceVar::Category::Other;
}
} // anonymous namespace } // anonymous namespace
bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl, bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl,
@ -148,7 +158,8 @@ uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) {
const uint32_t id = theBuilder.addModuleVar(varType, storageClass, const uint32_t id = theBuilder.addModuleVar(varType, storageClass,
var->getName(), llvm::None); var->getName(), llvm::None);
astDecls[var] = {id, storageClass, rule}; astDecls[var] = {id, storageClass, rule};
resourceVars.emplace_back(id, getResourceBinding(var), resourceVars.emplace_back(id, getResourceCategory(var->getType()),
getResourceBinding(var),
var->getAttr<VKBindingAttr>()); var->getAttr<VKBindingAttr>());
if (isACSBuffer) { if (isACSBuffer) {
@ -159,7 +170,8 @@ uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) {
const uint32_t counterId = const uint32_t counterId =
theBuilder.addModuleVar(counterType, storageClass, counterName); theBuilder.addModuleVar(counterType, storageClass, counterName);
resourceVars.emplace_back(counterId, nullptr, nullptr); resourceVars.emplace_back(counterId, ResourceVar::Category::Other, nullptr,
nullptr);
counterVars[var] = counterId; counterVars[var] = counterId;
} }
@ -225,7 +237,8 @@ uint32_t DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
astDecls[varDecl] = {bufferVar, spv::StorageClass::Uniform, astDecls[varDecl] = {bufferVar, spv::StorageClass::Uniform,
LayoutRule::GLSLStd140, index++}; LayoutRule::GLSLStd140, index++};
} }
resourceVars.emplace_back(bufferVar, getResourceBinding(decl), resourceVars.emplace_back(bufferVar, ResourceVar::Category::Other,
getResourceBinding(decl),
decl->getAttr<VKBindingAttr>()); decl->getAttr<VKBindingAttr>());
return bufferVar; return bufferVar;
@ -247,7 +260,8 @@ uint32_t DeclResultIdMapper::createCTBuffer(const VarDecl *decl) {
// TODO: std140 rules may not suit tbuffers. // TODO: std140 rules may not suit tbuffers.
astDecls[decl] = {bufferVar, spv::StorageClass::Uniform, astDecls[decl] = {bufferVar, spv::StorageClass::Uniform,
LayoutRule::GLSLStd140}; LayoutRule::GLSLStd140};
resourceVars.emplace_back(bufferVar, getResourceBinding(context), resourceVars.emplace_back(bufferVar, ResourceVar::Category::Other,
getResourceBinding(context),
decl->getAttr<VKBindingAttr>()); decl->getAttr<VKBindingAttr>());
return bufferVar; return bufferVar;
@ -316,27 +330,32 @@ class BindingSet {
public: public:
BindingSet() : nextBinding(0) {} BindingSet() : nextBinding(0) {}
/// Uses the given set and binding number. /// Tries to use the given set and binding number. Returns true if possible,
void useBinding(uint32_t binding, uint32_t set) { /// false otherwise.
bindings[set].insert(binding); bool tryToUseBinding(uint32_t binding, uint32_t set,
ResourceVar::Category category) {
const auto cat = static_cast<uint32_t>(category);
// Note that we will create the entry for binding in bindings[set] here.
// But that should not have bad effects since it defaults to zero.
if ((bindings[set][binding] & cat) == 0) {
bindings[set][binding] |= cat;
return true;
}
return false;
} }
/// Uses the next avaiable binding number in set 0. /// Uses the next avaiable binding number in set 0.
uint32_t useNextBinding() { uint32_t useNextBinding(ResourceVar::Category category) {
auto &set0bindings = bindings[0]; auto &set0bindings = bindings[0];
while (set0bindings.count(nextBinding)) while (set0bindings.count(nextBinding))
nextBinding++; nextBinding++;
set0bindings.insert(nextBinding); set0bindings[nextBinding] = static_cast<uint32_t>(category);
return nextBinding++; return nextBinding++;
} }
/// Returns true if the given set and binding number is already used.
bool isBindingUsed(uint32_t binding, uint32_t set) {
return bindings[set].count(binding);
}
private: private:
std::unordered_map<uint32_t, llvm::SmallSet<uint32_t, 8>> bindings; ///< set number -> (binding number -> resource category)
llvm::DenseMap<uint32_t, llvm::DenseMap<uint32_t, uint32_t>> bindings;
uint32_t nextBinding; ///< Next available binding number in set 0 uint32_t nextBinding; ///< Next available binding number in set 0
}; };
} // namespace } // namespace
@ -498,17 +517,17 @@ bool DeclResultIdMapper::decorateResourceBindings() {
// Process variables with [[vk::binding(...)]] binding assignment // Process variables with [[vk::binding(...)]] binding assignment
for (const auto &var : resourceVars) for (const auto &var : resourceVars)
if (const auto *vkBinding = var.getBinding()) { if (const auto *vkBinding = var.getBinding()) {
const auto cat = var.getCategory();
const auto set = vkBinding->getSet(); const auto set = vkBinding->getSet();
const auto binding = vkBinding->getBinding(); const auto binding = vkBinding->getBinding();
if (bindingSet.isBindingUsed(binding, set)) { if (bindingSet.tryToUseBinding(binding, set, cat)) {
theBuilder.decorateDSetBinding(var.getSpirvId(), set, binding);
} else {
emitError("resource binding #%0 in descriptor set #%1 already assigned", emitError("resource binding #%0 in descriptor set #%1 already assigned",
vkBinding->getLocation()) vkBinding->getLocation())
<< binding << set; << binding << set;
noError = false; noError = false;
} else {
theBuilder.decorateDSetBinding(var.getSpirvId(), set, binding);
bindingSet.useBinding(binding, set);
} }
} }
@ -543,23 +562,24 @@ bool DeclResultIdMapper::decorateResourceBindings() {
llvm_unreachable("unknown register type found"); llvm_unreachable("unknown register type found");
} }
if (bindingSet.isBindingUsed(binding, set)) { const auto cat = var.getCategory();
if (bindingSet.tryToUseBinding(binding, set, cat)) {
theBuilder.decorateDSetBinding(var.getSpirvId(), set, binding);
} else {
emitError( emitError(
"resource binding #%0 in descriptor set #%1 already assigned", "resource binding #%0 in descriptor set #%1 already assigned",
reg->Loc) reg->Loc)
<< binding << set; << binding << set;
noError = false; noError = false;
} else {
theBuilder.decorateDSetBinding(var.getSpirvId(), set, binding);
bindingSet.useBinding(binding, set);
} }
} }
// Process variables with no binding assignment // Process variables with no binding assignment
for (const auto &var : resourceVars) for (const auto &var : resourceVars)
if (!var.getBinding() && !var.getRegister()) if (!var.getBinding() && !var.getRegister())
theBuilder.decorateDSetBinding(var.getSpirvId(), 0, theBuilder.decorateDSetBinding(
bindingSet.useNextBinding()); var.getSpirvId(), 0, bindingSet.useNextBinding(var.getCategory()));
return noError; return noError;
} }

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

@ -85,16 +85,31 @@ private:
class ResourceVar { class ResourceVar {
public: public:
ResourceVar(uint32_t id, const hlsl::RegisterAssignment *r, /// The category of this resource.
///
/// We only care about Vulkan image and sampler types here, since they can be
/// bundled together as a combined image sampler which takes the same binding
/// number. The compiler should allow this case.
///
/// Numbers are assigned to make bit check easiser.
enum class Category : uint32_t {
Image = 1,
Sampler = 2,
Other = 3,
};
ResourceVar(uint32_t id, Category cat, const hlsl::RegisterAssignment *r,
const VKBindingAttr *b) const VKBindingAttr *b)
: varId(id), reg(r), binding(b) {} : varId(id), category(cat), reg(r), binding(b) {}
uint32_t getSpirvId() const { return varId; } uint32_t getSpirvId() const { return varId; }
Category getCategory() const { return category; }
const hlsl::RegisterAssignment *getRegister() const { return reg; } const hlsl::RegisterAssignment *getRegister() const { return reg; }
const VKBindingAttr *getBinding() const { return binding; } const VKBindingAttr *getBinding() const { return binding; }
private: private:
uint32_t varId; ///< <result-id> uint32_t varId; ///< <result-id>
Category category; ///< Resource category
const hlsl::RegisterAssignment *reg; ///< HLSL register assignment const hlsl::RegisterAssignment *reg; ///< HLSL register assignment
const VKBindingAttr *binding; ///< Vulkan binding assignment const VKBindingAttr *binding; ///< Vulkan binding assignment
}; };

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

@ -402,6 +402,15 @@ bool TypeTranslator::isTextureMS(QualType type) {
return false; return false;
} }
bool TypeTranslator::isSampler(QualType type) {
if (const auto *rt = type->getAs<RecordType>()) {
const auto name = rt->getDecl()->getName();
if (name == "SamplerState" || name == "SamplerComparisonState")
return true;
}
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;

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

@ -85,6 +85,9 @@ public:
/// \brief Returns true if the given type is an HLSL RWTexture type. /// \brief Returns true if the given type is an HLSL RWTexture type.
static bool isRWTexture(QualType); static bool isRWTexture(QualType);
/// \brief Returns true if the given type is an HLSL sampler type.
static bool isSampler(QualType);
/// \brief Returns true if the given type is an HLSL OutputPatch type. /// \brief Returns true if the given type is an HLSL OutputPatch type.
static bool isOutputPatch(QualType); static bool isOutputPatch(QualType);

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

@ -6,19 +6,36 @@ SamplerState sampler1 : register(s1, space1);
[[vk::binding(3, 1)]] [[vk::binding(3, 1)]]
SamplerState sampler2 : register(s2); SamplerState sampler2 : register(s2);
[[vk::binding(1)]] // duplicate [[vk::binding(1)]] // reuse - allowed for combined image sampler
Texture2D<float4> texture1; Texture2D<float4> texture1;
[[vk::binding(3, 1)]] // duplicate [[vk::binding(3, 1)]] // reuse - allowed for combined image sampler
Texture3D<float4> texture2 : register(t0, space0); Texture3D<float4> texture2 : register(t0, space0);
[[vk::binding(3, 1)]] // duplicate [[vk::binding(3, 1)]] // reuse - disallowed
Texture3D<float4> texture3 : register(t0, space0); Texture3D<float4> texture3 : register(t0, space0);
[[vk::binding(1)]] // reuse - disallowed
SamplerState sampler3 : register(s1, space1);
struct S { float f; };
[[vk::binding(5)]]
StructuredBuffer<S> buf1;
[[vk::binding(5)]] // reuse - disallowed
SamplerState sampler4;
[[vk::binding(5)]] // reuse - disallowed
Texture2D<float4> texture4;
float4 main() : SV_Target { float4 main() : SV_Target {
return 1.0; return 1.0;
} }
// CHECK: :9:3: error: resource binding #1 in descriptor set #0 already assigned // CHECK-NOT: :9:{{%\d+}}: error: resource binding #1 in descriptor set #0 already assigned
// CHECK: :12:3: error: resource binding #3 in descriptor set #1 already assigned // CHECK-NOT: :12:{{%\d+}}: error: resource binding #3 in descriptor set #1 already assigned
// CHECK: :15:3: error: resource binding #3 in descriptor set #1 already assigned // CHECK: :15:3: error: resource binding #3 in descriptor set #1 already assigned
// CHECK: :18:3: error: resource binding #1 in descriptor set #0 already assigned
// CHECK: :26:3: error: resource binding #5 in descriptor set #0 already assigned
// CHECK: :29:3: error: resource binding #5 in descriptor set #0 already assigned

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

@ -7,13 +7,22 @@ struct S {
ConstantBuffer<S> myCbuffer1 : register(b0); ConstantBuffer<S> myCbuffer1 : register(b0);
ConstantBuffer<S> myCbuffer2 : register(b0, space1); ConstantBuffer<S> myCbuffer2 : register(b0, space1);
RWStructuredBuffer<S> mySBuffer1 : register(u0); // duplicate RWStructuredBuffer<S> mySBuffer1 : register(u0); // reuse - disallowed
RWStructuredBuffer<S> mySBuffer2 : register(u0, space1); // duplicate RWStructuredBuffer<S> mySBuffer2 : register(u0, space1); // reuse - disallowed
RWStructuredBuffer<S> mySBuffer3 : register(u0, space2); RWStructuredBuffer<S> mySBuffer3 : register(u0, space2);
SamplerState mySampler1 : register(s5, space1);
Texture2D myTexture1 : register(t5, space1); // reuse - allowed
Texture2D myTexture2 : register(t6, space6);
[[vk::binding(6, 6)]] // reuse - allowed
SamplerState mySampler2;
float4 main() : SV_Target { float4 main() : SV_Target {
return 1.0; return 1.0;
} }
// CHECK: :10:36: error: resource binding #0 in descriptor set #0 already assigned // CHECK: :10:36: error: resource binding #0 in descriptor set #0 already assigned
// CHECK: :11:36: error: resource binding #0 in descriptor set #1 already assigned // CHECK: :11:36: error: resource binding #0 in descriptor set #1 already assigned
// CHECK-NOT: :15:{{%\d+}}: error: resource binding #5 in descriptor set #1 already assigned
// CHECK-NOT: :18:{{%\d+}}: error: resource binding #6 in descriptor set #6 already assigned