HLSL: matrix and vector truncations for m*v, v*m, m*m

HLSL truncates the vector, or one of the two matrix dimensions if there is a
dimensional mismatch in m*v, v*m, or m*m.

This PR adds that ability.  Conversion constructors are added as required.
This commit is contained in:
LoopDawg 2017-11-22 10:33:34 -07:00
parent 698bf7547a
commit 2e62910633
6 changed files with 523 additions and 0 deletions

View file

@ -5008,6 +5008,12 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct
bool builtIn = false;
int thisDepth = 0;
// For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes,
// and clamp the opposite arg. Since that's complex, we farm it off to a separate method.
// It doesn't naturally fall out of processing an argument at a time in isolation.
if (function->getName() == "mul")
addGenMulArgumentConversion(loc, *function, arguments);
TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr;
// TODO: this needs improvement: there's no way at present to look up a signature in
@ -5169,6 +5175,68 @@ void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& ar
arguments = intermediate.growAggregate(front, arguments);
}
//
// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat. This is a
// situation not well suited to resolution in intrinsic selection, but we can do so here, since we
// can look at both arguments insert explicit shape changes here, if required.
//
void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args)
{
TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr;
if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) {
// It really ought to have two arguments.
error(loc, "expected: mul arguments", "", "");
return;
}
TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
if (arg0->isVector() && arg1->isVector()) {
// For:
// vec * vec: it's handled during intrinsic selection, so while we could do it here,
// we can also ignore it, which is easier.
} else if (arg0->isVector() && arg1->isMatrix()) {
// vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col.
if (arg0->getVectorSize() < arg1->getMatrixCols()) {
// vec is smaller, so truncate larger mat dimension
const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
0, arg0->getVectorSize(), arg1->getMatrixRows());
arg1 = addConstructor(loc, arg1, truncType);
} else if (arg0->getVectorSize() > arg1->getMatrixCols()) {
// vec is larger, so truncate vec to mat size
const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
arg1->getMatrixCols());
arg0 = addConstructor(loc, arg0, truncType);
}
} else if (arg0->isMatrix() && arg1->isVector()) {
// mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col.
if (arg1->getVectorSize() < arg0->getMatrixRows()) {
// vec is smaller, so truncate larger mat dimension
const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
0, arg0->getMatrixCols(), arg1->getVectorSize());
arg0 = addConstructor(loc, arg0, truncType);
} else if (arg1->getVectorSize() > arg0->getMatrixRows()) {
// vec is larger, so truncate vec to mat size
const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
arg0->getMatrixRows());
arg1 = addConstructor(loc, arg1, truncType);
}
} else if (arg0->isMatrix() && arg1->isMatrix()) {
// mat * mat
} else {
// It's something with scalars: we'll just leave it alone.
}
// Put arguments back.
argAggregate->getSequence()[0] = arg0;
argAggregate->getSequence()[1] = arg1;
call[0].type = &arg0->getWritableType();
call[1].type = &arg1->getWritableType();
}
//
// Add any needed implicit conversions for function-call arguments to input parameters.
//
@ -7015,6 +7083,7 @@ void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQuali
}
}
//
// Look up a function name in the symbol table, and make sure it is a function.
//