|
@@ -163,8 +163,9 @@ inline bool isExternalVar(const VarDecl *var) {
|
|
|
// groupshared variables are allowed to be declared as "static". But we still
|
|
|
// need to put them in the Workgroup storage class. That is, when seeing
|
|
|
// "static groupshared", ignore "static".
|
|
|
- return var->hasExternalFormalLinkage() ? !var->isStaticDataMember()
|
|
|
- : var->getAttr<HLSLGroupSharedAttr>();
|
|
|
+ return var->hasExternalFormalLinkage()
|
|
|
+ ? !var->isStaticDataMember()
|
|
|
+ : (var->getAttr<HLSLGroupSharedAttr>() != nullptr);
|
|
|
}
|
|
|
|
|
|
/// Returns the referenced variable's DeclContext if the given expr is
|
|
@@ -181,9 +182,11 @@ const DeclContext *isConstantTextureBufferDeclRef(const Expr *expr) {
|
|
|
|
|
|
/// Returns true if
|
|
|
/// * the given expr is an DeclRefExpr referencing a kind of structured or byte
|
|
|
-/// buffer and it is non-alias one, or
|
|
|
+/// buffer and it is non-alias one, or
|
|
|
/// * the given expr is an CallExpr returning a kind of structured or byte
|
|
|
-/// buffer.
|
|
|
+/// buffer.
|
|
|
+/// * the given expr is an ArraySubscriptExpr referencing a kind of structured
|
|
|
+/// or byte buffer.
|
|
|
///
|
|
|
/// Note: legalization specific code
|
|
|
bool isReferencingNonAliasStructuredOrByteBuffer(const Expr *expr) {
|
|
@@ -195,6 +198,8 @@ bool isReferencingNonAliasStructuredOrByteBuffer(const Expr *expr) {
|
|
|
} else if (const auto *callExpr = dyn_cast<CallExpr>(expr)) {
|
|
|
if (TypeTranslator::isAKindOfStructuredOrByteBuffer(callExpr->getType()))
|
|
|
return true;
|
|
|
+ } else if (const auto *arrSubExpr = dyn_cast<ArraySubscriptExpr>(expr)) {
|
|
|
+ return isReferencingNonAliasStructuredOrByteBuffer(arrSubExpr->getBase());
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
@@ -533,6 +538,37 @@ std::string getFnName(const FunctionDecl *fn) {
|
|
|
return getNamespacePrefix(fn) + classOrStructName + fn->getName().str();
|
|
|
}
|
|
|
|
|
|
+/// Returns the capability required to non-uniformly index into the given type.
|
|
|
+spv::Capability getNonUniformCapability(QualType type) {
|
|
|
+ using spv::Capability;
|
|
|
+
|
|
|
+ if (type->isArrayType()) {
|
|
|
+ return getNonUniformCapability(
|
|
|
+ type->getAsArrayTypeUnsafe()->getElementType());
|
|
|
+ }
|
|
|
+ if (TypeTranslator::isTexture(type) || TypeTranslator::isSampler(type)) {
|
|
|
+ return Capability::SampledImageArrayNonUniformIndexingEXT;
|
|
|
+ }
|
|
|
+ if (TypeTranslator::isRWTexture(type)) {
|
|
|
+ return Capability::StorageImageArrayNonUniformIndexingEXT;
|
|
|
+ }
|
|
|
+ if (TypeTranslator::isBuffer(type)) {
|
|
|
+ return Capability::UniformTexelBufferArrayNonUniformIndexingEXT;
|
|
|
+ }
|
|
|
+ if (TypeTranslator::isRWBuffer(type)) {
|
|
|
+ return Capability::StorageTexelBufferArrayNonUniformIndexingEXT;
|
|
|
+ }
|
|
|
+ if (const auto *recordType = type->getAs<RecordType>()) {
|
|
|
+ const auto name = recordType->getDecl()->getName();
|
|
|
+
|
|
|
+ if (name == "SubpassInput" || name == "SubpassInputMS") {
|
|
|
+ return Capability::InputAttachmentArrayNonUniformIndexingEXT;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Capability::Max;
|
|
|
+}
|
|
|
+
|
|
|
} // namespace
|
|
|
|
|
|
SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci, EmitSPIRVOptions &options)
|
|
@@ -548,7 +584,7 @@ SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci, EmitSPIRVOptions &options)
|
|
|
featureManager, options),
|
|
|
entryFunctionId(0), curFunction(nullptr), curThis(0),
|
|
|
seenPushConstantAt(), isSpecConstantMode(false),
|
|
|
- needsLegalization(false) {
|
|
|
+ foundNonUniformResourceIndex(false), needsLegalization(false) {
|
|
|
if (shaderModel.GetKind() == hlsl::ShaderModel::Kind::Invalid)
|
|
|
emitError("unknown shader module: %0", {}) << shaderModel.GetName();
|
|
|
|
|
@@ -587,11 +623,7 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
|
|
|
workQueue.insert(funcDecl);
|
|
|
}
|
|
|
} else {
|
|
|
- // If ignoring unused resources, defer Decl handling inside
|
|
|
- // TranslationUnit to the time of first referencing.
|
|
|
- if (!spirvOptions.ignoreUnusedResources) {
|
|
|
- doDecl(decl);
|
|
|
- }
|
|
|
+ doDecl(decl);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -749,21 +781,6 @@ void SPIRVEmitter::doStmt(const Stmt *stmt,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-SpirvEvalInfo SPIRVEmitter::doDeclRefExpr(const DeclRefExpr *expr) {
|
|
|
- const auto *decl = expr->getDecl();
|
|
|
- auto id = declIdMapper.getDeclEvalInfo(decl, false);
|
|
|
-
|
|
|
- if (spirvOptions.ignoreUnusedResources && !id) {
|
|
|
- // First time referencing a Decl inside TranslationUnit. Register
|
|
|
- // into DeclResultIdMapper and emit SPIR-V for it and then query
|
|
|
- // again.
|
|
|
- doDecl(decl);
|
|
|
- id = declIdMapper.getDeclEvalInfo(decl);
|
|
|
- }
|
|
|
-
|
|
|
- return id;
|
|
|
-}
|
|
|
-
|
|
|
SpirvEvalInfo SPIRVEmitter::doExpr(const Expr *expr) {
|
|
|
SpirvEvalInfo result(/*id*/ 0);
|
|
|
|
|
@@ -774,7 +791,7 @@ SpirvEvalInfo SPIRVEmitter::doExpr(const Expr *expr) {
|
|
|
expr = expr->IgnoreParens();
|
|
|
|
|
|
if (const auto *declRefExpr = dyn_cast<DeclRefExpr>(expr)) {
|
|
|
- result = doDeclRefExpr(declRefExpr);
|
|
|
+ result = declIdMapper.getDeclEvalInfo(declRefExpr->getDecl());
|
|
|
} else if (const auto *memberExpr = dyn_cast<MemberExpr>(expr)) {
|
|
|
result = doMemberExpr(memberExpr);
|
|
|
} else if (const auto *castExpr = dyn_cast<CastExpr>(expr)) {
|
|
@@ -879,6 +896,12 @@ SpirvEvalInfo SPIRVEmitter::loadIfGLValue(const Expr *expr,
|
|
|
|
|
|
uint32_t loadedId = theBuilder.createLoad(valType, info);
|
|
|
|
|
|
+ // Decorate with NonUniformEXT if loading from a pointer with that property.
|
|
|
+ // We are likely loading an element from the resource array here.
|
|
|
+ if (info.isNonUniform()) {
|
|
|
+ theBuilder.decorate(loadedId, spv::Decoration::NonUniformEXT);
|
|
|
+ }
|
|
|
+
|
|
|
// Special-case: According to the SPIR-V Spec: There is no physical size or
|
|
|
// bit pattern defined for boolean type. Therefore an unsigned integer is used
|
|
|
// to represent booleans when layout is required. In such cases, after loading
|
|
@@ -1190,11 +1213,13 @@ void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
|
|
|
// supported in Vulkan
|
|
|
for (const auto *member : bufferDecl->decls()) {
|
|
|
if (const auto *varMember = dyn_cast<VarDecl>(member)) {
|
|
|
- if (const auto *init = varMember->getInit())
|
|
|
- emitWarning("%select{tbuffer|cbuffer}0 member initializer "
|
|
|
- "ignored since no Vulkan equivalent",
|
|
|
- init->getExprLoc())
|
|
|
- << bufferDecl->isCBuffer() << init->getSourceRange();
|
|
|
+ if (!spirvOptions.noWarnIgnoredFeatures) {
|
|
|
+ if (const auto *init = varMember->getInit())
|
|
|
+ emitWarning("%select{tbuffer|cbuffer}0 member initializer "
|
|
|
+ "ignored since no Vulkan equivalent",
|
|
|
+ init->getExprLoc())
|
|
|
+ << bufferDecl->isCBuffer() << init->getSourceRange();
|
|
|
+ }
|
|
|
|
|
|
// We cannot handle external initialization of column-major matrices now.
|
|
|
if (typeTranslator.isOrContainsNonFpColMajorMatrix(varMember->getType(),
|
|
@@ -1240,11 +1265,16 @@ void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
|
|
|
decl->getLocation());
|
|
|
}
|
|
|
|
|
|
- if (const auto *arrayType =
|
|
|
- astContext.getAsConstantArrayType(decl->getType())) {
|
|
|
- if (TypeTranslator::isAKindOfStructuredOrByteBuffer(
|
|
|
- arrayType->getElementType())) {
|
|
|
- emitError("arrays of structured/byte buffers unsupported",
|
|
|
+ // Reject arrays of RW/append/consume structured buffers. They have assoicated
|
|
|
+ // counters, which are quite nasty to handle.
|
|
|
+ if (decl->getType()->isArrayType()) {
|
|
|
+ auto type = decl->getType();
|
|
|
+ do {
|
|
|
+ type = type->getAsArrayTypeUnsafe()->getElementType();
|
|
|
+ } while (type->isArrayType());
|
|
|
+
|
|
|
+ if (TypeTranslator::isRWAppendConsumeSBuffer(type)) {
|
|
|
+ emitError("arrays of RW/append/consume structured buffers unsupported",
|
|
|
decl->getLocation());
|
|
|
return;
|
|
|
}
|
|
@@ -1890,8 +1920,20 @@ void SPIRVEmitter::doSwitchStmt(const SwitchStmt *switchStmt,
|
|
|
|
|
|
SpirvEvalInfo
|
|
|
SPIRVEmitter::doArraySubscriptExpr(const ArraySubscriptExpr *expr) {
|
|
|
+ // Make sure we don't have previously unhandled NonUniformResourceIndex()
|
|
|
+ assert(!foundNonUniformResourceIndex);
|
|
|
+
|
|
|
llvm::SmallVector<uint32_t, 4> indices;
|
|
|
- auto info = loadIfAliasVarRef(collectArrayStructIndices(expr, &indices));
|
|
|
+ const auto *base = collectArrayStructIndices(expr, &indices);
|
|
|
+ auto info = loadIfAliasVarRef(base);
|
|
|
+
|
|
|
+ if (foundNonUniformResourceIndex) {
|
|
|
+ // Add the necessary capability required for indexing into this kind
|
|
|
+ // of resource
|
|
|
+ theBuilder.requireCapability(getNonUniformCapability(base->getType()));
|
|
|
+ info.setNonUniform(); // Carry forward the NonUniformEXT decoration
|
|
|
+ foundNonUniformResourceIndex = false;
|
|
|
+ }
|
|
|
|
|
|
if (!indices.empty()) {
|
|
|
(void)turnIntoElementPtr(info, expr->getType(), indices);
|
|
@@ -1959,7 +2001,8 @@ SpirvEvalInfo SPIRVEmitter::processCall(const CallExpr *callExpr) {
|
|
|
SpirvEvalInfo objectEvalInfo = 0; // EvalInfo for the object (if exists)
|
|
|
bool needsTempVar = false; // Whether we need temporary variable.
|
|
|
|
|
|
- llvm::SmallVector<uint32_t, 4> params; // Temporary variables
|
|
|
+ llvm::SmallVector<uint32_t, 4> vars; // Variables for function call
|
|
|
+ llvm::SmallVector<bool, 4> isTempVar; // Temporary variable or not
|
|
|
llvm::SmallVector<SpirvEvalInfo, 4> args; // Evaluated arguments
|
|
|
|
|
|
if (const auto *memberCall = dyn_cast<CXXMemberCallExpr>(callExpr)) {
|
|
@@ -1998,7 +2041,8 @@ SpirvEvalInfo SPIRVEmitter::processCall(const CallExpr *callExpr) {
|
|
|
args.push_back(objectId);
|
|
|
// We do not need to create a new temporary variable for the this
|
|
|
// object. Use the evaluated argument.
|
|
|
- params.push_back(args.back());
|
|
|
+ vars.push_back(args.back());
|
|
|
+ isTempVar.push_back(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2010,25 +2054,44 @@ SpirvEvalInfo SPIRVEmitter::processCall(const CallExpr *callExpr) {
|
|
|
auto *arg = callExpr->getArg(i)->IgnoreParenLValueCasts();
|
|
|
const auto *param = callee->getParamDecl(i);
|
|
|
|
|
|
- // We need to create variables for holding the values to be used as
|
|
|
- // arguments. The variables themselves are of pointer types.
|
|
|
- const uint32_t varType =
|
|
|
- declIdMapper.getTypeAndCreateCounterForPotentialAliasVar(param);
|
|
|
- const std::string varName = "param.var." + param->getNameAsString();
|
|
|
- const uint32_t tempVarId = theBuilder.addFnVar(varType, varName);
|
|
|
+ // Get the evaluation info if this argument is referencing some variable
|
|
|
+ // *as a whole*, in which case we can avoid creating the temporary variable
|
|
|
+ // for it if it is Function scope and can act as out parameter.
|
|
|
+ SpirvEvalInfo argInfo = 0;
|
|
|
+ if (const auto *declRefExpr = dyn_cast<DeclRefExpr>(arg)) {
|
|
|
+ argInfo = declIdMapper.getDeclEvalInfo(declRefExpr->getDecl());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (argInfo && argInfo.getStorageClass() == spv::StorageClass::Function &&
|
|
|
+ canActAsOutParmVar(param)) {
|
|
|
+ vars.push_back(argInfo);
|
|
|
+ isTempVar.push_back(false);
|
|
|
+ args.push_back(doExpr(arg));
|
|
|
+ } else {
|
|
|
+ // We need to create variables for holding the values to be used as
|
|
|
+ // arguments. The variables themselves are of pointer types.
|
|
|
+ const uint32_t varType =
|
|
|
+ declIdMapper.getTypeAndCreateCounterForPotentialAliasVar(param);
|
|
|
+ const std::string varName = "param.var." + param->getNameAsString();
|
|
|
+ const uint32_t tempVarId = theBuilder.addFnVar(varType, varName);
|
|
|
|
|
|
- params.push_back(tempVarId);
|
|
|
- args.push_back(doExpr(arg));
|
|
|
+ vars.push_back(tempVarId);
|
|
|
+ isTempVar.push_back(true);
|
|
|
+ args.push_back(doExpr(arg));
|
|
|
|
|
|
- // Update counter variable associated with function parameters
|
|
|
- tryToAssignCounterVar(param, arg);
|
|
|
+ // Update counter variable associated with function parameters
|
|
|
+ tryToAssignCounterVar(param, arg);
|
|
|
|
|
|
- // Manually load the argument here
|
|
|
- const auto rhsVal = loadIfGLValue(arg, args.back());
|
|
|
- // Initialize the temporary variables using the contents of the arguments
|
|
|
- storeValue(tempVarId, rhsVal, param->getType());
|
|
|
+ // Manually load the argument here
|
|
|
+ const auto rhsVal = loadIfGLValue(arg, args.back());
|
|
|
+ // Initialize the temporary variables using the contents of the arguments
|
|
|
+ storeValue(tempVarId, rhsVal, param->getType());
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ assert(vars.size() == isTempVar.size());
|
|
|
+ assert(vars.size() == args.size());
|
|
|
+
|
|
|
// Push the callee into the work queue if it is not there.
|
|
|
if (!workQueue.count(callee)) {
|
|
|
workQueue.insert(callee);
|
|
@@ -2039,26 +2102,25 @@ SpirvEvalInfo SPIRVEmitter::processCall(const CallExpr *callExpr) {
|
|
|
// Get or forward declare the function <result-id>
|
|
|
const uint32_t funcId = declIdMapper.getOrRegisterFnResultId(callee);
|
|
|
|
|
|
- const uint32_t retVal =
|
|
|
- theBuilder.createFunctionCall(retType, funcId, params);
|
|
|
+ const uint32_t retVal = theBuilder.createFunctionCall(retType, funcId, vars);
|
|
|
|
|
|
// If we created a temporary variable for the lvalue object this method is
|
|
|
// invoked upon, we need to copy the contents in the temporary variable back
|
|
|
// to the original object's variable in case there are side effects.
|
|
|
if (needsTempVar && !objectEvalInfo.isRValue()) {
|
|
|
const uint32_t typeId = typeTranslator.translateType(objectType);
|
|
|
- const uint32_t value = theBuilder.createLoad(typeId, params.front());
|
|
|
+ const uint32_t value = theBuilder.createLoad(typeId, vars.front());
|
|
|
storeValue(objectEvalInfo, value, objectType);
|
|
|
}
|
|
|
|
|
|
// Go through all parameters and write those marked as out/inout
|
|
|
for (uint32_t i = 0; i < numParams; ++i) {
|
|
|
const auto *param = callee->getParamDecl(i);
|
|
|
- if (canActAsOutParmVar(param)) {
|
|
|
+ if (isTempVar[i] && canActAsOutParmVar(param)) {
|
|
|
const auto *arg = callExpr->getArg(i);
|
|
|
const uint32_t index = i + isNonStaticMemberCall;
|
|
|
const uint32_t typeId = typeTranslator.translateType(param->getType());
|
|
|
- const uint32_t value = theBuilder.createLoad(typeId, params[index]);
|
|
|
+ const uint32_t value = theBuilder.createLoad(typeId, vars[index]);
|
|
|
|
|
|
processAssignment(arg, value, false, args[index]);
|
|
|
}
|
|
@@ -2717,7 +2779,9 @@ uint32_t SPIRVEmitter::processGetSamplePosition(const CXXMemberCallExpr *expr) {
|
|
|
|
|
|
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 sample = expr->getNumArgs() == 1
|
|
|
+ ? static_cast<uint32_t>(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});
|
|
@@ -2779,16 +2843,10 @@ SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
|
|
// The HLSL methods, however, have overloaded functions which have float
|
|
|
// output arguments. Since the AST naturally won't have casting AST nodes for
|
|
|
// such cases, we'll have to perform the cast ourselves.
|
|
|
- const auto storeToOutputArg = [this](const Expr *outputArg,
|
|
|
- uint32_t toStoreId) {
|
|
|
- const auto outputArgType = outputArg->getType();
|
|
|
- // Perform cast to float if necessary.
|
|
|
- if (isFloatOrVecMatOfFloatType(outputArgType)) {
|
|
|
- toStoreId = theBuilder.createUnaryOp(
|
|
|
- spv::Op::OpConvertUToF, typeTranslator.translateType(outputArgType),
|
|
|
- toStoreId);
|
|
|
- }
|
|
|
- theBuilder.createStore(doExpr(outputArg), toStoreId);
|
|
|
+ const auto storeToOutputArg = [this](const Expr *outputArg, uint32_t id,
|
|
|
+ QualType type) {
|
|
|
+ id = castToType(id, type, outputArg->getType(), outputArg->getExprLoc());
|
|
|
+ theBuilder.createStore(doExpr(outputArg), id);
|
|
|
};
|
|
|
|
|
|
if ((typeName == "Texture1D" && numArgs > 1) ||
|
|
@@ -2815,8 +2873,11 @@ SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
|
|
querySize -= 1;
|
|
|
|
|
|
const uint32_t uintId = theBuilder.getUint32Type();
|
|
|
- const uint32_t resultTypeId =
|
|
|
- querySize == 1 ? uintId : theBuilder.getVecType(uintId, querySize);
|
|
|
+ const QualType resultQualType =
|
|
|
+ querySize == 1
|
|
|
+ ? astContext.UnsignedIntTy
|
|
|
+ : astContext.getExtVectorType(astContext.UnsignedIntTy, querySize);
|
|
|
+ const uint32_t resultTypeId = typeTranslator.translateType(resultQualType);
|
|
|
|
|
|
// Only Texture types use ImageQuerySizeLod.
|
|
|
// TextureMS, RWTexture, Buffers, RWBuffers use ImageQuerySize.
|
|
@@ -2839,7 +2900,7 @@ SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
|
|
|
|
|
if (querySize == 1) {
|
|
|
const uint32_t argIndex = mipLevel ? 1 : 0;
|
|
|
- storeToOutputArg(expr->getArg(argIndex), query);
|
|
|
+ storeToOutputArg(expr->getArg(argIndex), query, resultQualType);
|
|
|
} else {
|
|
|
for (uint32_t i = 0; i < querySize; ++i) {
|
|
|
const uint32_t component =
|
|
@@ -2847,7 +2908,8 @@ SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
|
|
// If the first arg is the mipmap level, we must write the results
|
|
|
// starting from Arg(i+1), not Arg(i).
|
|
|
const uint32_t argIndex = mipLevel ? i + 1 : i;
|
|
|
- storeToOutputArg(expr->getArg(argIndex), component);
|
|
|
+ storeToOutputArg(expr->getArg(argIndex), component,
|
|
|
+ astContext.UnsignedIntTy);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2857,7 +2919,8 @@ SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
|
|
|
numLevels ? spv::Op::OpImageQueryLevels : spv::Op::OpImageQuerySamples;
|
|
|
const uint32_t numLevelsSamplesQuery =
|
|
|
theBuilder.createUnaryOp(opcode, uintId, objectId);
|
|
|
- storeToOutputArg(numLevelsSamplesArg, numLevelsSamplesQuery);
|
|
|
+ storeToOutputArg(numLevelsSamplesArg, numLevelsSamplesQuery,
|
|
|
+ astContext.UnsignedIntTy);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -2873,13 +2936,19 @@ SPIRVEmitter::processTextureLevelOfDetail(const CXXMemberCallExpr *expr) {
|
|
|
// Return type is always a single float (LOD).
|
|
|
assert(expr->getNumArgs() == 2u);
|
|
|
const auto *object = expr->getImplicitObjectArgument();
|
|
|
- const uint32_t objectId = loadIfGLValue(object);
|
|
|
- const uint32_t samplerState = doExpr(expr->getArg(0));
|
|
|
+ const auto objectInfo = loadIfGLValue(object);
|
|
|
+ const auto samplerState = doExpr(expr->getArg(0));
|
|
|
const uint32_t coordinate = doExpr(expr->getArg(1));
|
|
|
const uint32_t sampledImageType = theBuilder.getSampledImageType(
|
|
|
typeTranslator.translateType(object->getType()));
|
|
|
const uint32_t sampledImage = theBuilder.createBinaryOp(
|
|
|
- spv::Op::OpSampledImage, sampledImageType, objectId, samplerState);
|
|
|
+ spv::Op::OpSampledImage, sampledImageType, objectInfo, samplerState);
|
|
|
+
|
|
|
+ if (objectInfo.isNonUniform() || samplerState.isNonUniform()) {
|
|
|
+ // The sampled image will be used to access resource's memory, so we need
|
|
|
+ // to decorate it with NonUniformEXT.
|
|
|
+ theBuilder.decorate(sampledImage, spv::Decoration::NonUniformEXT);
|
|
|
+ }
|
|
|
|
|
|
// The result type of OpImageQueryLod must be a float2.
|
|
|
const uint32_t queryResultType =
|
|
@@ -2933,10 +3002,11 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
|
|
|
// No offset args for TextureCube, 1 or 4 offset args for the rest.
|
|
|
assert(numOffsetArgs == 0 || numOffsetArgs == 1 || numOffsetArgs == 4);
|
|
|
|
|
|
- const uint32_t image = loadIfGLValue(imageExpr);
|
|
|
- const uint32_t sampler = doExpr(expr->getArg(0));
|
|
|
+ const auto image = loadIfGLValue(imageExpr);
|
|
|
+ const auto sampler = doExpr(expr->getArg(0));
|
|
|
const uint32_t coordinate = doExpr(expr->getArg(1));
|
|
|
- const uint32_t compareVal = isCmp ? doExpr(expr->getArg(2)) : 0;
|
|
|
+ const uint32_t compareVal =
|
|
|
+ isCmp ? static_cast<uint32_t>(doExpr(expr->getArg(2))) : 0;
|
|
|
|
|
|
// Handle offsets (if any).
|
|
|
bool needsEmulation = false;
|
|
@@ -2965,7 +3035,10 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
|
|
+ const auto status =
|
|
|
+ hasStatusArg ? static_cast<uint32_t>(doExpr(expr->getArg(numArgs - 1)))
|
|
|
+ : 0;
|
|
|
+ const bool isNonUniform = image.isNonUniform() || sampler.isNonUniform();
|
|
|
|
|
|
if (needsEmulation) {
|
|
|
const auto elemType = typeTranslator.translateType(
|
|
@@ -2975,7 +3048,7 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
|
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
|
varOffset = doExpr(expr->getArg(2 + isCmp + i));
|
|
|
const uint32_t gatherRet = theBuilder.createImageGather(
|
|
|
- retTypeId, imageTypeId, image, sampler, coordinate,
|
|
|
+ retTypeId, imageTypeId, image, sampler, isNonUniform, coordinate,
|
|
|
theBuilder.getConstantInt32(component), compareVal, /*constOffset*/ 0,
|
|
|
varOffset, /*constOffsets*/ 0, /*sampleNumber*/ 0, status);
|
|
|
texels[i] = theBuilder.createCompositeExtract(elemType, gatherRet, {i});
|
|
@@ -2985,7 +3058,7 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
|
|
|
}
|
|
|
|
|
|
return theBuilder.createImageGather(
|
|
|
- retTypeId, imageTypeId, image, sampler, coordinate,
|
|
|
+ retTypeId, imageTypeId, image, sampler, isNonUniform, coordinate,
|
|
|
theBuilder.getConstantInt32(component), compareVal, constOffset,
|
|
|
varOffset, constOffsets, /*sampleNumber*/ 0, status);
|
|
|
}
|
|
@@ -3019,8 +3092,8 @@ uint32_t SPIRVEmitter::processTextureGatherCmp(const CXXMemberCallExpr *expr) {
|
|
|
const bool hasOffsetArg = (numArgs == 5) || (numArgs == 4 && !hasStatusArg);
|
|
|
|
|
|
const auto *imageExpr = expr->getImplicitObjectArgument();
|
|
|
- const uint32_t image = loadIfGLValue(imageExpr);
|
|
|
- const uint32_t sampler = doExpr(expr->getArg(0));
|
|
|
+ const auto image = loadIfGLValue(imageExpr);
|
|
|
+ const auto sampler = doExpr(expr->getArg(0));
|
|
|
const uint32_t coordinate = doExpr(expr->getArg(1));
|
|
|
const uint32_t comparator = doExpr(expr->getArg(2));
|
|
|
uint32_t constOffset = 0, varOffset = 0;
|
|
@@ -3029,11 +3102,14 @@ uint32_t SPIRVEmitter::processTextureGatherCmp(const CXXMemberCallExpr *expr) {
|
|
|
|
|
|
const auto retType = typeTranslator.translateType(callee->getReturnType());
|
|
|
const auto imageType = typeTranslator.translateType(imageExpr->getType());
|
|
|
- const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
|
|
+ const auto status =
|
|
|
+ hasStatusArg ? static_cast<uint32_t>(doExpr(expr->getArg(numArgs - 1)))
|
|
|
+ : 0;
|
|
|
|
|
|
return theBuilder.createImageGather(
|
|
|
- retType, imageType, image, sampler, coordinate, /*component*/ 0,
|
|
|
- comparator, constOffset, varOffset, /*constOffsets*/ 0,
|
|
|
+ retType, imageType, image, sampler,
|
|
|
+ image.isNonUniform() || sampler.isNonUniform(), coordinate,
|
|
|
+ /*component*/ 0, comparator, constOffset, varOffset, /*constOffsets*/ 0,
|
|
|
/*sampleNumber*/ 0, status);
|
|
|
}
|
|
|
|
|
@@ -3051,7 +3127,12 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
|
|
|
const bool doFetch =
|
|
|
TypeTranslator::isBuffer(type) || TypeTranslator::isTexture(type);
|
|
|
|
|
|
- const uint32_t objectId = loadIfGLValue(object);
|
|
|
+ const auto objectInfo = loadIfGLValue(object);
|
|
|
+
|
|
|
+ if (objectInfo.isNonUniform()) {
|
|
|
+ // Decoreate the image handle for OpImageFetch/OpImageRead
|
|
|
+ theBuilder.decorate(objectInfo, spv::Decoration::NonUniformEXT);
|
|
|
+ }
|
|
|
|
|
|
// For Texture2DMS and Texture2DMSArray, Sample must be used rather than Lod.
|
|
|
uint32_t sampleNumber = 0;
|
|
@@ -3080,7 +3161,7 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
|
|
|
// OpImageFetch and OpImageRead can only fetch a vector of 4 elements.
|
|
|
const uint32_t texelTypeId = theBuilder.getVecType(elemTypeId, 4u);
|
|
|
const uint32_t texel = theBuilder.createImageFetchOrRead(
|
|
|
- doFetch, texelTypeId, type, objectId, locationId, lod, constOffset,
|
|
|
+ doFetch, texelTypeId, type, objectInfo, locationId, lod, constOffset,
|
|
|
varOffset, /*constOffsets*/ 0, sampleNumber, residencyCode);
|
|
|
|
|
|
// If the result type is a vec1, vec2, or vec3, some extra processing
|
|
@@ -3795,8 +3876,8 @@ SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
|
|
|
|
|
|
uint32_t SPIRVEmitter::createImageSample(
|
|
|
QualType retType, uint32_t imageType, uint32_t image, uint32_t sampler,
|
|
|
- uint32_t coordinate, uint32_t compareVal, uint32_t bias, uint32_t lod,
|
|
|
- std::pair<uint32_t, uint32_t> grad, uint32_t constOffset,
|
|
|
+ bool isNonUniform, uint32_t coordinate, uint32_t compareVal, uint32_t bias,
|
|
|
+ uint32_t lod, std::pair<uint32_t, uint32_t> grad, uint32_t constOffset,
|
|
|
uint32_t varOffset, uint32_t constOffsets, uint32_t sample, uint32_t minLod,
|
|
|
uint32_t residencyCodeId) {
|
|
|
|
|
@@ -3805,10 +3886,10 @@ uint32_t SPIRVEmitter::createImageSample(
|
|
|
// SampleDref* instructions in SPIR-V always return a scalar.
|
|
|
// They also have the correct type in HLSL.
|
|
|
if (compareVal) {
|
|
|
- return theBuilder.createImageSample(retTypeId, imageType, image, sampler,
|
|
|
- coordinate, compareVal, bias, lod, grad,
|
|
|
- constOffset, varOffset, constOffsets,
|
|
|
- sample, minLod, residencyCodeId);
|
|
|
+ return theBuilder.createImageSample(
|
|
|
+ retTypeId, imageType, image, sampler, isNonUniform, coordinate,
|
|
|
+ compareVal, bias, lod, grad, constOffset, varOffset, constOffsets,
|
|
|
+ sample, minLod, residencyCodeId);
|
|
|
}
|
|
|
|
|
|
// Non-Dref Sample instructions in SPIR-V must always return a vec4.
|
|
@@ -3835,9 +3916,9 @@ uint32_t SPIRVEmitter::createImageSample(
|
|
|
needsLegalization = true;
|
|
|
|
|
|
uint32_t retVal = theBuilder.createImageSample(
|
|
|
- texelTypeId, imageType, image, sampler, coordinate, compareVal, bias, lod,
|
|
|
- grad, constOffset, varOffset, constOffsets, sample, minLod,
|
|
|
- residencyCodeId);
|
|
|
+ texelTypeId, imageType, image, sampler, isNonUniform, coordinate,
|
|
|
+ compareVal, bias, lod, grad, constOffset, varOffset, constOffsets, sample,
|
|
|
+ minLod, residencyCodeId);
|
|
|
|
|
|
// Extract smaller vector from the vec4 result if necessary.
|
|
|
if (texelTypeId != retTypeId) {
|
|
@@ -3886,7 +3967,9 @@ uint32_t SPIRVEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
|
|
|
else if (numArgs > 3 && expr->getArg(3)->getType()->isFloatingType())
|
|
|
clamp = doExpr(expr->getArg(3));
|
|
|
const bool hasClampArg = (clamp != 0);
|
|
|
- const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
|
|
+ const auto status =
|
|
|
+ hasStatusArg ? static_cast<uint32_t>(doExpr(expr->getArg(numArgs - 1)))
|
|
|
+ : 0;
|
|
|
|
|
|
// Subtract 1 for status (if it exists), subtract 1 for clamp (if it exists),
|
|
|
// and subtract 2 for sampler_state and location.
|
|
@@ -3894,24 +3977,26 @@ uint32_t SPIRVEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
|
|
|
|
|
|
const auto *imageExpr = expr->getImplicitObjectArgument();
|
|
|
const uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
|
|
|
- const uint32_t image = loadIfGLValue(imageExpr);
|
|
|
- const uint32_t sampler = doExpr(expr->getArg(0));
|
|
|
+ const auto image = loadIfGLValue(imageExpr);
|
|
|
+ const auto sampler = doExpr(expr->getArg(0));
|
|
|
const uint32_t coordinate = doExpr(expr->getArg(1));
|
|
|
// .Sample()/.Gather() may have a third optional paramter for offset.
|
|
|
uint32_t constOffset = 0, varOffset = 0;
|
|
|
if (hasOffsetArg)
|
|
|
handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
|
|
|
+ const bool isNonUniform = image.isNonUniform() || sampler.isNonUniform();
|
|
|
|
|
|
const auto retType = expr->getDirectCallee()->getReturnType();
|
|
|
const auto retTypeId = typeTranslator.translateType(retType);
|
|
|
if (isSample) {
|
|
|
return createImageSample(
|
|
|
- retType, imageType, image, sampler, coordinate, /*compareVal*/ 0,
|
|
|
- /*bias*/ 0, /*lod*/ 0, std::make_pair(0, 0), constOffset, varOffset,
|
|
|
- /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp, status);
|
|
|
+ retType, imageType, image, sampler, isNonUniform, coordinate,
|
|
|
+ /*compareVal*/ 0, /*bias*/ 0, /*lod*/ 0, std::make_pair(0, 0),
|
|
|
+ constOffset, varOffset, /*constOffsets*/ 0, /*sampleNumber*/ 0,
|
|
|
+ /*minLod*/ clamp, status);
|
|
|
} else {
|
|
|
return theBuilder.createImageGather(
|
|
|
- retTypeId, imageType, image, sampler, coordinate,
|
|
|
+ retTypeId, imageType, image, sampler, isNonUniform, coordinate,
|
|
|
// .Gather() doc says we return four components of red data.
|
|
|
theBuilder.getConstantInt32(0), /*compareVal*/ 0, constOffset,
|
|
|
varOffset, /*constOffsets*/ 0, /*sampleNumber*/ 0, status);
|
|
@@ -3953,7 +4038,9 @@ SPIRVEmitter::processTextureSampleBiasLevel(const CXXMemberCallExpr *expr,
|
|
|
const auto numArgs = expr->getNumArgs();
|
|
|
const bool hasStatusArg =
|
|
|
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
|
|
|
- const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
|
|
+ const auto status =
|
|
|
+ hasStatusArg ? static_cast<uint32_t>(doExpr(expr->getArg(numArgs - 1)))
|
|
|
+ : 0;
|
|
|
|
|
|
uint32_t clamp = 0;
|
|
|
// The .SampleLevel() methods do not take the clamp argument.
|
|
@@ -3971,8 +4058,8 @@ SPIRVEmitter::processTextureSampleBiasLevel(const CXXMemberCallExpr *expr,
|
|
|
|
|
|
const auto *imageExpr = expr->getImplicitObjectArgument();
|
|
|
const uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
|
|
|
- const uint32_t image = loadIfGLValue(imageExpr);
|
|
|
- const uint32_t sampler = doExpr(expr->getArg(0));
|
|
|
+ const auto image = loadIfGLValue(imageExpr);
|
|
|
+ const auto sampler = doExpr(expr->getArg(0));
|
|
|
const uint32_t coordinate = doExpr(expr->getArg(1));
|
|
|
uint32_t lod = 0;
|
|
|
uint32_t bias = 0;
|
|
@@ -3988,10 +4075,11 @@ SPIRVEmitter::processTextureSampleBiasLevel(const CXXMemberCallExpr *expr,
|
|
|
|
|
|
const auto retType = expr->getDirectCallee()->getReturnType();
|
|
|
|
|
|
- return createImageSample(retType, imageType, image, sampler, coordinate,
|
|
|
- /*compareVal*/ 0, bias, lod, std::make_pair(0, 0),
|
|
|
- constOffset, varOffset, /*constOffsets*/ 0,
|
|
|
- /*sampleNumber*/ 0, /*minLod*/ clamp, status);
|
|
|
+ return createImageSample(
|
|
|
+ retType, imageType, image, sampler,
|
|
|
+ image.isNonUniform() || sampler.isNonUniform(), coordinate,
|
|
|
+ /*compareVal*/ 0, bias, lod, std::make_pair(0, 0), constOffset, varOffset,
|
|
|
+ /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp, status);
|
|
|
}
|
|
|
|
|
|
uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
|
|
@@ -4016,7 +4104,9 @@ uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
|
|
|
const auto numArgs = expr->getNumArgs();
|
|
|
const bool hasStatusArg =
|
|
|
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
|
|
|
- const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
|
|
+ const auto status =
|
|
|
+ hasStatusArg ? static_cast<uint32_t>(doExpr(expr->getArg(numArgs - 1)))
|
|
|
+ : 0;
|
|
|
|
|
|
uint32_t clamp = 0;
|
|
|
if (numArgs > 4 && expr->getArg(4)->getType()->isFloatingType())
|
|
@@ -4031,8 +4121,8 @@ uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
|
|
|
|
|
|
const auto *imageExpr = expr->getImplicitObjectArgument();
|
|
|
const uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
|
|
|
- const uint32_t image = loadIfGLValue(imageExpr);
|
|
|
- const uint32_t sampler = doExpr(expr->getArg(0));
|
|
|
+ const auto image = loadIfGLValue(imageExpr);
|
|
|
+ const auto sampler = doExpr(expr->getArg(0));
|
|
|
const uint32_t coordinate = doExpr(expr->getArg(1));
|
|
|
const uint32_t ddx = doExpr(expr->getArg(2));
|
|
|
const uint32_t ddy = doExpr(expr->getArg(3));
|
|
@@ -4043,9 +4133,11 @@ uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
|
|
|
|
|
|
const auto retType = expr->getDirectCallee()->getReturnType();
|
|
|
return createImageSample(
|
|
|
- retType, imageType, image, sampler, coordinate, /*compareVal*/ 0,
|
|
|
- /*bias*/ 0, /*lod*/ 0, std::make_pair(ddx, ddy), constOffset, varOffset,
|
|
|
- /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp, status);
|
|
|
+ retType, imageType, image, sampler,
|
|
|
+ image.isNonUniform() || sampler.isNonUniform(), coordinate,
|
|
|
+ /*compareVal*/ 0, /*bias*/ 0, /*lod*/ 0, std::make_pair(ddx, ddy),
|
|
|
+ constOffset, varOffset, /*constOffsets*/ 0, /*sampleNumber*/ 0,
|
|
|
+ /*minLod*/ clamp, status);
|
|
|
}
|
|
|
|
|
|
uint32_t
|
|
@@ -4098,7 +4190,9 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
|
|
|
const auto numArgs = expr->getNumArgs();
|
|
|
const bool hasStatusArg =
|
|
|
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
|
|
|
- const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
|
|
+ const auto status =
|
|
|
+ hasStatusArg ? static_cast<uint32_t>(doExpr(expr->getArg(numArgs - 1)))
|
|
|
+ : 0;
|
|
|
|
|
|
uint32_t clamp = 0;
|
|
|
// The .SampleCmpLevelZero() methods do not take the clamp argument.
|
|
@@ -4115,8 +4209,8 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
|
|
|
const bool hasOffsetArg = numArgs - hasClampArg - hasStatusArg - 3 > 0;
|
|
|
|
|
|
const auto *imageExpr = expr->getImplicitObjectArgument();
|
|
|
- const uint32_t image = loadIfGLValue(imageExpr);
|
|
|
- const uint32_t sampler = doExpr(expr->getArg(0));
|
|
|
+ const auto image = loadIfGLValue(imageExpr);
|
|
|
+ const auto sampler = doExpr(expr->getArg(0));
|
|
|
const uint32_t coordinate = doExpr(expr->getArg(1));
|
|
|
const uint32_t compareVal = doExpr(expr->getArg(2));
|
|
|
// If offset is present in .SampleCmp(), it will be the fourth argument.
|
|
@@ -4126,21 +4220,13 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
|
|
|
const uint32_t lod = isCmp ? 0 : theBuilder.getConstantFloat32(0);
|
|
|
|
|
|
const auto retType = expr->getDirectCallee()->getReturnType();
|
|
|
- // TODO: Hack. Drivers are expecting the Depth value in OpTypeImage to match
|
|
|
- // the OpImageSample* instruction: Depth=0 for normal sampling, and Depth=1
|
|
|
- // for depth-comparison sampling. That behavior is not what the spec says;
|
|
|
- // Vulkan spec reads "The 'Depth' operand of OpTypeImage is ignored."
|
|
|
- // We always generate OpTypeImage variables with Depth=0. Hack this only
|
|
|
- // depth-comparison sampling code path to use Depth=1 for the OpTypeImage
|
|
|
- // used by OpSampledImage. This causes inconsistent types in SPIR-V, but
|
|
|
- // pleases drivers. Whatever.
|
|
|
- const auto imageType = typeTranslator.translateResourceType(
|
|
|
- imageExpr->getType(), LayoutRule::Void, /*isDepthCmp=*/true);
|
|
|
-
|
|
|
- return createImageSample(retType, imageType, image, sampler, coordinate,
|
|
|
- compareVal, /*bias*/ 0, lod, std::make_pair(0, 0),
|
|
|
- constOffset, varOffset, /*constOffsets*/ 0,
|
|
|
- /*sampleNumber*/ 0, /*minLod*/ clamp, status);
|
|
|
+ const auto imageType = typeTranslator.translateType(imageExpr->getType());
|
|
|
+
|
|
|
+ return createImageSample(
|
|
|
+ retType, imageType, image, sampler,
|
|
|
+ image.isNonUniform() || sampler.isNonUniform(), coordinate, compareVal,
|
|
|
+ /*bias*/ 0, lod, std::make_pair(0, 0), constOffset, varOffset,
|
|
|
+ /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp, status);
|
|
|
}
|
|
|
|
|
|
SpirvEvalInfo
|
|
@@ -4192,7 +4278,9 @@ SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
|
|
|
const bool isTextureMS = TypeTranslator::isTextureMS(objectType);
|
|
|
const bool hasStatusArg =
|
|
|
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
|
|
|
- const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
|
|
+ const auto status =
|
|
|
+ hasStatusArg ? static_cast<uint32_t>(doExpr(expr->getArg(numArgs - 1)))
|
|
|
+ : 0;
|
|
|
|
|
|
if (TypeTranslator::isBuffer(objectType) ||
|
|
|
TypeTranslator::isRWBuffer(objectType) ||
|
|
@@ -4552,19 +4640,21 @@ SpirvEvalInfo SPIRVEmitter::doUnaryOperator(const UnaryOperator *expr) {
|
|
|
|
|
|
// Prefix increment/decrement operator returns a lvalue, while postfix
|
|
|
// increment/decrement returns a rvalue.
|
|
|
- return isPre ? subValue : SpirvEvalInfo(originValue).setRValue();
|
|
|
+ return isPre ? subValue : subValue.setResultId(originValue).setRValue();
|
|
|
}
|
|
|
case UO_Not: {
|
|
|
- const auto valId =
|
|
|
- theBuilder.createUnaryOp(spv::Op::OpNot, subTypeId, subValue);
|
|
|
- return SpirvEvalInfo(valId).setRValue();
|
|
|
+ return subValue
|
|
|
+ .setResultId(
|
|
|
+ theBuilder.createUnaryOp(spv::Op::OpNot, subTypeId, subValue))
|
|
|
+ .setRValue();
|
|
|
}
|
|
|
case UO_LNot: {
|
|
|
// Parsing will do the necessary casting to make sure we are applying the
|
|
|
// ! operator on boolean values.
|
|
|
- const auto valId =
|
|
|
- theBuilder.createUnaryOp(spv::Op::OpLogicalNot, subTypeId, subValue);
|
|
|
- return SpirvEvalInfo(valId).setRValue();
|
|
|
+ return subValue
|
|
|
+ .setResultId(theBuilder.createUnaryOp(spv::Op::OpLogicalNot, subTypeId,
|
|
|
+ subValue))
|
|
|
+ .setRValue();
|
|
|
}
|
|
|
case UO_Plus:
|
|
|
// No need to do anything for the prefix + operator.
|
|
@@ -4573,8 +4663,9 @@ SpirvEvalInfo SPIRVEmitter::doUnaryOperator(const UnaryOperator *expr) {
|
|
|
// SPIR-V have two opcodes for negating values: OpSNegate and OpFNegate.
|
|
|
const spv::Op spvOp = isFloatOrVecOfFloatType(subType) ? spv::Op::OpFNegate
|
|
|
: spv::Op::OpSNegate;
|
|
|
- const auto valId = theBuilder.createUnaryOp(spvOp, subTypeId, subValue);
|
|
|
- return SpirvEvalInfo(valId).setRValue();
|
|
|
+ return subValue
|
|
|
+ .setResultId(theBuilder.createUnaryOp(spvOp, subTypeId, subValue))
|
|
|
+ .setRValue();
|
|
|
}
|
|
|
default:
|
|
|
break;
|
|
@@ -4708,6 +4799,8 @@ SpirvEvalInfo SPIRVEmitter::processAssignment(const Expr *lhs,
|
|
|
const SpirvEvalInfo &rhs,
|
|
|
const bool isCompoundAssignment,
|
|
|
SpirvEvalInfo lhsPtr) {
|
|
|
+ lhs = lhs->IgnoreParenNoopCasts(astContext);
|
|
|
+
|
|
|
// Assigning to vector swizzling should be handled differently.
|
|
|
if (SpirvEvalInfo result = tryToAssignToVectorElements(lhs, rhs))
|
|
|
return result;
|
|
@@ -4734,7 +4827,9 @@ SpirvEvalInfo SPIRVEmitter::processAssignment(const Expr *lhs,
|
|
|
|
|
|
void SPIRVEmitter::storeValue(const SpirvEvalInfo &lhsPtr,
|
|
|
const SpirvEvalInfo &rhsVal,
|
|
|
- const QualType lhsValType) {
|
|
|
+ QualType lhsValType) {
|
|
|
+ if (const auto *refType = lhsValType->getAs<ReferenceType>())
|
|
|
+ lhsValType = refType->getPointeeType();
|
|
|
|
|
|
QualType matElemType = {};
|
|
|
const bool lhsIsMat = typeTranslator.isMxNMatrix(lhsValType, &matElemType);
|
|
@@ -5082,8 +5177,14 @@ SpirvEvalInfo SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
|
|
|
}
|
|
|
|
|
|
auto result = SpirvEvalInfo(valId).setRValue();
|
|
|
+
|
|
|
+ // Propagate RelaxedPrecision
|
|
|
if (lhsVal.isRelaxedPrecision() || rhsVal.isRelaxedPrecision())
|
|
|
result.setRelaxedPrecision();
|
|
|
+ // Propagate NonUniformEXT
|
|
|
+ if (lhsVal.isNonUniform() || rhsVal.isNonUniform())
|
|
|
+ result.setNonUniform();
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
case BO_Assign:
|
|
@@ -5556,8 +5657,9 @@ SPIRVEmitter::tryToAssignToVectorElements(const Expr *lhs,
|
|
|
}
|
|
|
|
|
|
const auto vec1 = doExpr(base);
|
|
|
- const uint32_t vec1Val =
|
|
|
- vec1.isRValue() ? vec1 : theBuilder.createLoad(baseTypeId, vec1);
|
|
|
+ const uint32_t vec1Val = vec1.isRValue()
|
|
|
+ ? static_cast<uint32_t>(vec1)
|
|
|
+ : theBuilder.createLoad(baseTypeId, vec1);
|
|
|
const uint32_t shuffle =
|
|
|
theBuilder.createVectorShuffle(baseTypeId, vec1Val, rhs, selectors);
|
|
|
|
|
@@ -5579,9 +5681,14 @@ SPIRVEmitter::tryToAssignToRWBufferRWTexture(const Expr *lhs,
|
|
|
if (isBufferTextureIndexing(lhsExpr, &baseExpr, &indexExpr)) {
|
|
|
const uint32_t locId = doExpr(indexExpr);
|
|
|
const QualType imageType = baseExpr->getType();
|
|
|
+ const auto baseInfo = doExpr(baseExpr);
|
|
|
const uint32_t imageId = theBuilder.createLoad(
|
|
|
- typeTranslator.translateType(imageType), doExpr(baseExpr));
|
|
|
+ typeTranslator.translateType(imageType), baseInfo);
|
|
|
theBuilder.createImageWrite(imageType, imageId, locId, rhs);
|
|
|
+ if (baseInfo.isNonUniform()) {
|
|
|
+ // Decorate the image handle for OpImageWrite
|
|
|
+ theBuilder.decorate(imageId, spv::Decoration::NonUniformEXT);
|
|
|
+ }
|
|
|
return rhs;
|
|
|
}
|
|
|
return 0;
|
|
@@ -6192,6 +6299,9 @@ SpirvEvalInfo SPIRVEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) {
|
|
|
case hlsl::IntrinsicOp::IOP_InterlockedCompareExchange:
|
|
|
retVal = processIntrinsicInterlockedMethod(callExpr, hlslOpcode);
|
|
|
break;
|
|
|
+ case hlsl::IntrinsicOp::IOP_NonUniformResourceIndex:
|
|
|
+ retVal = processIntrinsicNonUniformResourceIndex(callExpr);
|
|
|
+ break;
|
|
|
case hlsl::IntrinsicOp::IOP_tex1D:
|
|
|
case hlsl::IntrinsicOp::IOP_tex1Dbias:
|
|
|
case hlsl::IntrinsicOp::IOP_tex1Dgrad:
|
|
@@ -6578,6 +6688,11 @@ SPIRVEmitter::processIntrinsicInterlockedMethod(const CallExpr *expr,
|
|
|
}
|
|
|
const auto coordId = doExpr(index);
|
|
|
ptr = theBuilder.createImageTexelPointer(ptrType, baseId, coordId, zero);
|
|
|
+ if (baseId.isNonUniform()) {
|
|
|
+ // Image texel pointer will used to access image memory. Vulkan requires
|
|
|
+ // it to be decorated with NonUniformEXT.
|
|
|
+ theBuilder.decorate(ptr, spv::Decoration::NonUniformEXT);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
if (!ptr)
|
|
@@ -6621,6 +6736,28 @@ SPIRVEmitter::processIntrinsicInterlockedMethod(const CallExpr *expr,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+SpirvEvalInfo
|
|
|
+SPIRVEmitter::processIntrinsicNonUniformResourceIndex(const CallExpr *expr) {
|
|
|
+ foundNonUniformResourceIndex = true;
|
|
|
+ theBuilder.addExtension(Extension::EXT_descriptor_indexing,
|
|
|
+ "NonUniformResourceIndex", expr->getExprLoc());
|
|
|
+ theBuilder.requireCapability(spv::Capability::ShaderNonUniformEXT);
|
|
|
+
|
|
|
+ auto index = doExpr(expr->getArg(0)).setNonUniform();
|
|
|
+ // Decorate the expression in NonUniformResourceIndex() with NonUniformEXT.
|
|
|
+ // Aside from this, we also need to eventually populate the NonUniformEXT
|
|
|
+ // status to the usage of this expression: the "pointer" operand to a memory
|
|
|
+ // access instruction. Vulkan spec has the following rules:
|
|
|
+ //
|
|
|
+ // If an instruction loads from or stores to a resource (including atomics and
|
|
|
+ // image instructions) and the resource descriptor being accessed is not
|
|
|
+ // dynamically uniform, then the operand corresponding to that resource (e.g.
|
|
|
+ // the pointer or sampled image operand) must be decorated with NonUniformEXT.
|
|
|
+ theBuilder.decorate(index, spv::Decoration::NonUniformEXT);
|
|
|
+
|
|
|
+ return index;
|
|
|
+}
|
|
|
+
|
|
|
uint32_t SPIRVEmitter::processIntrinsicMsad4(const CallExpr *callExpr) {
|
|
|
emitWarning("msad4 intrinsic function is emulated using many SPIR-V "
|
|
|
"instructions due to lack of direct SPIR-V equivalent",
|
|
@@ -9143,8 +9280,8 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
|
|
|
declIdMapper.glPerVertex.calculateClipCullDistanceArraySize();
|
|
|
|
|
|
if (!shaderModel.IsCS()) {
|
|
|
- // Generate the gl_PerVertex structs or stand-alone builtins of
|
|
|
- // Position, ClipDistance, and CullDistance.
|
|
|
+ // Generate stand-alone builtins of Position, ClipDistance, and
|
|
|
+ // CullDistance, which belongs to gl_PerVertex.
|
|
|
declIdMapper.glPerVertex.generateVars(inputArraySize, outputArraySize);
|
|
|
}
|
|
|
|
|
@@ -9323,11 +9460,12 @@ bool SPIRVEmitter::processHSEntryPointOutputAndPCF(
|
|
|
// Now create a barrier before calling the Patch Constant Function (PCF).
|
|
|
// Flags are:
|
|
|
// Execution Barrier scope = Workgroup (2)
|
|
|
- // Memory Barrier scope = Device (1)
|
|
|
+ // Memory Barrier scope = Invocation (4)
|
|
|
// Memory Semantics Barrier scope = None (0)
|
|
|
- theBuilder.createBarrier(theBuilder.getConstantUint32(2),
|
|
|
- theBuilder.getConstantUint32(1),
|
|
|
- theBuilder.getConstantUint32(0));
|
|
|
+ const auto constZero = theBuilder.getConstantUint32(0);
|
|
|
+ const auto constFour = theBuilder.getConstantUint32(4);
|
|
|
+ const auto constTwo = theBuilder.getConstantUint32(2);
|
|
|
+ theBuilder.createBarrier(constTwo, constFour, constZero);
|
|
|
|
|
|
// The PCF should be called only once. Therefore, we check the invocationID,
|
|
|
// and we only allow ID 0 to call the PCF.
|