SPIRV: Add the support of missing image functions #72
This commit is contained in:
parent
d4782c10d4
commit
fc6189197d
@ -95,7 +95,7 @@ protected:
|
|||||||
void makeGlobalInitializers(const glslang::TIntermSequence&);
|
void makeGlobalInitializers(const glslang::TIntermSequence&);
|
||||||
void visitFunctions(const glslang::TIntermSequence&);
|
void visitFunctions(const glslang::TIntermSequence&);
|
||||||
void handleFunctionEntry(const glslang::TIntermAggregate* node);
|
void handleFunctionEntry(const glslang::TIntermAggregate* node);
|
||||||
void translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments);
|
void translateArguments(glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments);
|
||||||
void translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments);
|
void translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments);
|
||||||
spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node);
|
spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node);
|
||||||
spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*);
|
spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*);
|
||||||
@ -177,6 +177,8 @@ spv::StorageClass TranslateStorageClass(const glslang::TType& type)
|
|||||||
else if (type.getQualifier().isUniformOrBuffer()) {
|
else if (type.getQualifier().isUniformOrBuffer()) {
|
||||||
if (type.getBasicType() == glslang::EbtBlock)
|
if (type.getBasicType() == glslang::EbtBlock)
|
||||||
return spv::StorageClassUniform;
|
return spv::StorageClassUniform;
|
||||||
|
else if (type.getBasicType() == glslang::EbtAtomicUint)
|
||||||
|
return spv::StorageClassAtomicCounter;
|
||||||
else
|
else
|
||||||
return spv::StorageClassUniformConstant;
|
return spv::StorageClassUniformConstant;
|
||||||
// TODO: how are we distuingishing between default and non-default non-writable uniforms? Do default uniforms even exist?
|
// TODO: how are we distuingishing between default and non-default non-writable uniforms? Do default uniforms even exist?
|
||||||
@ -343,6 +345,56 @@ spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Translate glslang image layout format to SPIR-V image format.
|
||||||
|
spv::ImageFormat TranslateImageFormat(const glslang::TType& type)
|
||||||
|
{
|
||||||
|
assert(type.getBasicType() == glslang::EbtSampler);
|
||||||
|
|
||||||
|
switch (type.getQualifier().layoutFormat) {
|
||||||
|
case glslang::ElfNone: return spv::ImageFormatUnknown;
|
||||||
|
case glslang::ElfRgba32f: return spv::ImageFormatRgba32f;
|
||||||
|
case glslang::ElfRgba16f: return spv::ImageFormatRgba16f;
|
||||||
|
case glslang::ElfR32f: return spv::ImageFormatR32f;
|
||||||
|
case glslang::ElfRgba8: return spv::ImageFormatRgba8;
|
||||||
|
case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm;
|
||||||
|
case glslang::ElfRg32f: return spv::ImageFormatRg32f;
|
||||||
|
case glslang::ElfRg16f: return spv::ImageFormatRg16f;
|
||||||
|
case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f;
|
||||||
|
case glslang::ElfR16f: return spv::ImageFormatR16f;
|
||||||
|
case glslang::ElfRgba16: return spv::ImageFormatRgba16;
|
||||||
|
case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2;
|
||||||
|
case glslang::ElfRg16: return spv::ImageFormatRg16;
|
||||||
|
case glslang::ElfRg8: return spv::ImageFormatRg8;
|
||||||
|
case glslang::ElfR16: return spv::ImageFormatR16;
|
||||||
|
case glslang::ElfR8: return spv::ImageFormatR8;
|
||||||
|
case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm;
|
||||||
|
case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm;
|
||||||
|
case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm;
|
||||||
|
case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm;
|
||||||
|
case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm;
|
||||||
|
case glslang::ElfRgba32i: return spv::ImageFormatRgba32i;
|
||||||
|
case glslang::ElfRgba16i: return spv::ImageFormatRgba16i;
|
||||||
|
case glslang::ElfRgba8i: return spv::ImageFormatRgba8i;
|
||||||
|
case glslang::ElfR32i: return spv::ImageFormatR32i;
|
||||||
|
case glslang::ElfRg32i: return spv::ImageFormatRg32i;
|
||||||
|
case glslang::ElfRg16i: return spv::ImageFormatRg16i;
|
||||||
|
case glslang::ElfRg8i: return spv::ImageFormatRg8i;
|
||||||
|
case glslang::ElfR16i: return spv::ImageFormatR16i;
|
||||||
|
case glslang::ElfR8i: return spv::ImageFormatR8i;
|
||||||
|
case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui;
|
||||||
|
case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui;
|
||||||
|
case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui;
|
||||||
|
case glslang::ElfR32ui: return spv::ImageFormatR32ui;
|
||||||
|
case glslang::ElfRg32ui: return spv::ImageFormatRg32ui;
|
||||||
|
case glslang::ElfRg16ui: return spv::ImageFormatRg16ui;
|
||||||
|
case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui;
|
||||||
|
case glslang::ElfRg8ui: return spv::ImageFormatRg8ui;
|
||||||
|
case glslang::ElfR16ui: return spv::ImageFormatR16ui;
|
||||||
|
case glslang::ElfR8ui: return spv::ImageFormatR8ui;
|
||||||
|
default: return (spv::ImageFormat)spv::BadValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Implement the TGlslangToSpvTraverser class.
|
// Implement the TGlslangToSpvTraverser class.
|
||||||
//
|
//
|
||||||
@ -680,7 +732,14 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI
|
|||||||
|
|
||||||
builder.clearAccessChain();
|
builder.clearAccessChain();
|
||||||
node->getOperand()->traverse(this);
|
node->getOperand()->traverse(this);
|
||||||
spv::Id operand = builder.accessChainLoad(TranslatePrecisionDecoration(node->getOperand()->getType()));
|
spv::Id operand = spv::NoResult;
|
||||||
|
|
||||||
|
if (node->getOp() == glslang::EOpAtomicCounterIncrement ||
|
||||||
|
node->getOp() == glslang::EOpAtomicCounterDecrement ||
|
||||||
|
node->getOp() == glslang::EOpAtomicCounter)
|
||||||
|
operand = builder.accessChainGetLValue(); // Special case l-value operands
|
||||||
|
else
|
||||||
|
operand = builder.accessChainLoad(TranslatePrecisionDecoration(node->getOperand()->getType()));
|
||||||
|
|
||||||
spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
|
spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
|
||||||
|
|
||||||
@ -764,6 +823,11 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else if (node->getOp() == glslang::EOpImageStore)
|
||||||
|
{
|
||||||
|
// "imageStore" is a special case, which has no result
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
glslang::TOperator binOp = glslang::EOpNull;
|
glslang::TOperator binOp = glslang::EOpNull;
|
||||||
bool reduceComparison = true;
|
bool reduceComparison = true;
|
||||||
@ -901,7 +965,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
|
|||||||
case glslang::EOpConstructStruct:
|
case glslang::EOpConstructStruct:
|
||||||
{
|
{
|
||||||
std::vector<spv::Id> arguments;
|
std::vector<spv::Id> arguments;
|
||||||
translateArguments(node->getSequence(), arguments);
|
translateArguments(*node, arguments);
|
||||||
spv::Id resultTypeId = convertGlslangToSpvType(node->getType());
|
spv::Id resultTypeId = convertGlslangToSpvType(node->getType());
|
||||||
spv::Id constructed;
|
spv::Id constructed;
|
||||||
if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) {
|
if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) {
|
||||||
@ -1361,7 +1425,7 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
|
|||||||
{
|
{
|
||||||
const glslang::TSampler& sampler = type.getSampler();
|
const glslang::TSampler& sampler = type.getSampler();
|
||||||
spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), sampler.shadow, sampler.arrayed, sampler.ms,
|
spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), sampler.shadow, sampler.arrayed, sampler.ms,
|
||||||
sampler.image ? 2 : 1, spv::ImageFormatUnknown); // TODO: translate format, needed for GLSL image ops
|
sampler.image ? 2 : 1, TranslateImageFormat(type));
|
||||||
// OpenGL "textures" need to be combined with a sampler
|
// OpenGL "textures" need to be combined with a sampler
|
||||||
if (! sampler.image)
|
if (! sampler.image)
|
||||||
spvType = builder.makeSampledImageType(spvType);
|
spvType = builder.makeSampledImageType(spvType);
|
||||||
@ -1609,14 +1673,39 @@ void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate
|
|||||||
builder.setBuildPoint(functionBlock);
|
builder.setBuildPoint(functionBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermSequence& glslangArguments, std::vector<spv::Id>& arguments)
|
void TGlslangToSpvTraverser::translateArguments(glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments)
|
||||||
{
|
{
|
||||||
|
const glslang::TIntermSequence& glslangArguments = node.getSequence();
|
||||||
for (int i = 0; i < (int)glslangArguments.size(); ++i) {
|
for (int i = 0; i < (int)glslangArguments.size(); ++i) {
|
||||||
builder.clearAccessChain();
|
builder.clearAccessChain();
|
||||||
glslangArguments[i]->traverse(this);
|
glslangArguments[i]->traverse(this);
|
||||||
|
|
||||||
|
// Special case l-value operands
|
||||||
|
bool lvalue = false;
|
||||||
|
switch (node.getOp()) {
|
||||||
|
case glslang::EOpImageAtomicAdd:
|
||||||
|
case glslang::EOpImageAtomicMin:
|
||||||
|
case glslang::EOpImageAtomicMax:
|
||||||
|
case glslang::EOpImageAtomicAnd:
|
||||||
|
case glslang::EOpImageAtomicOr:
|
||||||
|
case glslang::EOpImageAtomicXor:
|
||||||
|
case glslang::EOpImageAtomicExchange:
|
||||||
|
case glslang::EOpImageAtomicCompSwap:
|
||||||
|
if (i == 0)
|
||||||
|
lvalue = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lvalue) {
|
||||||
|
arguments.push_back(builder.accessChainGetLValue());
|
||||||
|
}
|
||||||
|
else {
|
||||||
arguments.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArguments[i]->getAsTyped()->getType())));
|
arguments.push_back(builder.accessChainLoad(TranslatePrecisionDecoration(glslangArguments[i]->getAsTyped()->getType())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments)
|
void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments)
|
||||||
{
|
{
|
||||||
@ -1627,10 +1716,7 @@ void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std
|
|||||||
|
|
||||||
spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node)
|
spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node)
|
||||||
{
|
{
|
||||||
if (node->isImage()) {
|
if (! node->isImage() && ! node->isTexture()) {
|
||||||
spv::MissingFunctionality("GLSL image function");
|
|
||||||
return spv::NoResult;
|
|
||||||
} else if (! node->isTexture()) {
|
|
||||||
return spv::NoResult;
|
return spv::NoResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1643,7 +1729,7 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO
|
|||||||
: node->getAsUnaryNode()->getOperand()->getAsTyped()->getType().getSampler();
|
: node->getAsUnaryNode()->getOperand()->getAsTyped()->getType().getSampler();
|
||||||
std::vector<spv::Id> arguments;
|
std::vector<spv::Id> arguments;
|
||||||
if (node->getAsAggregate())
|
if (node->getAsAggregate())
|
||||||
translateArguments(node->getAsAggregate()->getSequence(), arguments);
|
translateArguments(*node->getAsAggregate(), arguments);
|
||||||
else
|
else
|
||||||
translateArguments(*node->getAsUnaryNode(), arguments);
|
translateArguments(*node->getAsUnaryNode(), arguments);
|
||||||
spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
|
spv::Decoration precision = TranslatePrecisionDecoration(node->getType());
|
||||||
@ -1675,8 +1761,37 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is no longer a query....
|
// Check for image functions other than queries
|
||||||
|
if (node->isImage()) {
|
||||||
|
if (node->getOp() == glslang::EOpImageLoad) {
|
||||||
|
return builder.createOp(spv::OpImageRead, convertGlslangToSpvType(node->getType()), arguments);
|
||||||
|
}
|
||||||
|
else if (node->getOp() == glslang::EOpImageStore) {
|
||||||
|
builder.createNoResultOp(spv::OpImageWrite, arguments);
|
||||||
|
return spv::NoResult;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Process image atomic operations. GLSL "IMAGE_PARAMS" will involve in constructing an image texel
|
||||||
|
// pointer and this pointer, as the first source operand, is required by SPIR-V atomic operations.
|
||||||
|
std::vector<spv::Id> imageParams;
|
||||||
|
auto opIt = arguments.begin();
|
||||||
|
imageParams.push_back(*(opIt++));
|
||||||
|
imageParams.push_back(*(opIt++));
|
||||||
|
imageParams.push_back(sampler.ms ? *(opIt++) : 0); // For non-MS, the value should be 0
|
||||||
|
|
||||||
|
spv::Id resultTypeId = builder.makePointer(spv::StorageClassImage, convertGlslangToSpvType(node->getType()));
|
||||||
|
spv::Id pointer = builder.createOp(spv::OpImageTexelPointer, resultTypeId, imageParams);
|
||||||
|
|
||||||
|
std::vector<spv::Id> operands;
|
||||||
|
operands.push_back(pointer);
|
||||||
|
for (; opIt != arguments.end(); ++opIt)
|
||||||
|
operands.push_back(*opIt);
|
||||||
|
|
||||||
|
return createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for texture functions other than queries
|
||||||
if (cracked.fetch)
|
if (cracked.fetch)
|
||||||
spv::MissingFunctionality("texel fetch");
|
spv::MissingFunctionality("texel fetch");
|
||||||
if (cracked.gather)
|
if (cracked.gather)
|
||||||
@ -2438,27 +2553,35 @@ spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv
|
|||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case glslang::EOpAtomicAdd:
|
case glslang::EOpAtomicAdd:
|
||||||
|
case glslang::EOpImageAtomicAdd:
|
||||||
opCode = spv::OpAtomicIAdd;
|
opCode = spv::OpAtomicIAdd;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicMin:
|
case glslang::EOpAtomicMin:
|
||||||
opCode = spv::OpAtomicSMin;
|
case glslang::EOpImageAtomicMin:
|
||||||
|
opCode = builder.isSignedType(typeId) ? spv::OpAtomicUMin : spv::OpAtomicSMin;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicMax:
|
case glslang::EOpAtomicMax:
|
||||||
opCode = spv::OpAtomicSMax;
|
case glslang::EOpImageAtomicMax:
|
||||||
|
opCode = builder.isSignedType(typeId) ? spv::OpAtomicUMax : spv::OpAtomicSMax;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicAnd:
|
case glslang::EOpAtomicAnd:
|
||||||
|
case glslang::EOpImageAtomicAnd:
|
||||||
opCode = spv::OpAtomicAnd;
|
opCode = spv::OpAtomicAnd;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicOr:
|
case glslang::EOpAtomicOr:
|
||||||
|
case glslang::EOpImageAtomicOr:
|
||||||
opCode = spv::OpAtomicOr;
|
opCode = spv::OpAtomicOr;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicXor:
|
case glslang::EOpAtomicXor:
|
||||||
|
case glslang::EOpImageAtomicXor:
|
||||||
opCode = spv::OpAtomicXor;
|
opCode = spv::OpAtomicXor;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicExchange:
|
case glslang::EOpAtomicExchange:
|
||||||
|
case glslang::EOpImageAtomicExchange:
|
||||||
opCode = spv::OpAtomicExchange;
|
opCode = spv::OpAtomicExchange;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicCompSwap:
|
case glslang::EOpAtomicCompSwap:
|
||||||
|
case glslang::EOpImageAtomicCompSwap:
|
||||||
opCode = spv::OpAtomicCompareExchange;
|
opCode = spv::OpAtomicCompareExchange;
|
||||||
break;
|
break;
|
||||||
case glslang::EOpAtomicCounterIncrement:
|
case glslang::EOpAtomicCounterIncrement:
|
||||||
|
|||||||
@ -822,6 +822,7 @@ Id Builder::createVariable(StorageClass storageClass, Id type, const char* name)
|
|||||||
case StorageClassWorkgroupLocal:
|
case StorageClassWorkgroupLocal:
|
||||||
case StorageClassPrivateGlobal:
|
case StorageClassPrivateGlobal:
|
||||||
case StorageClassWorkgroupGlobal:
|
case StorageClassWorkgroupGlobal:
|
||||||
|
case StorageClassAtomicCounter:
|
||||||
constantsTypesGlobals.push_back(inst);
|
constantsTypesGlobals.push_back(inst);
|
||||||
module.mapInstruction(inst);
|
module.mapInstruction(inst);
|
||||||
break;
|
break;
|
||||||
@ -975,6 +976,15 @@ void Builder::createNoResultOp(Op opCode, Id operand)
|
|||||||
buildPoint->addInstruction(op);
|
buildPoint->addInstruction(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An opcode that has one operand, no result id, and no type
|
||||||
|
void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
|
||||||
|
{
|
||||||
|
Instruction* op = new Instruction(opCode);
|
||||||
|
for (auto operand : operands)
|
||||||
|
op->addIdOperand(operand);
|
||||||
|
buildPoint->addInstruction(op);
|
||||||
|
}
|
||||||
|
|
||||||
void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
|
void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
|
||||||
{
|
{
|
||||||
Instruction* op = new Instruction(OpControlBarrier);
|
Instruction* op = new Instruction(OpControlBarrier);
|
||||||
|
|||||||
@ -137,6 +137,11 @@ public:
|
|||||||
|
|
||||||
bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
|
bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
|
||||||
unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
|
unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
|
||||||
|
bool isSignedType(Id typeId) const
|
||||||
|
{
|
||||||
|
assert(getTypeClass(typeId) == OpTypeInt);
|
||||||
|
return module.getInstruction(typeId)->getImmediateOperand(1) == 0u;
|
||||||
|
}
|
||||||
|
|
||||||
int getTypeNumColumns(Id typeId) const
|
int getTypeNumColumns(Id typeId) const
|
||||||
{
|
{
|
||||||
@ -241,6 +246,7 @@ public:
|
|||||||
|
|
||||||
void createNoResultOp(Op);
|
void createNoResultOp(Op);
|
||||||
void createNoResultOp(Op, Id operand);
|
void createNoResultOp(Op, Id operand);
|
||||||
|
void createNoResultOp(Op, const std::vector<Id>& operands);
|
||||||
void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
|
void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
|
||||||
void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
|
void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
|
||||||
Id createUnaryOp(Op, Id typeId, Id operand);
|
Id createUnaryOp(Op, Id typeId, Id operand);
|
||||||
|
|||||||
@ -768,7 +768,7 @@ TIntermTyped* TIntermediate::fold(TIntermAggregate* aggrNode)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EOpReflect:
|
case EOpReflect:
|
||||||
// I – 2 * dot(N, I) * N: Arguments are (I, N).
|
// I - 2 * dot(N, I) * N: Arguments are (I, N).
|
||||||
dot = childConstUnions[0].dot(childConstUnions[1]);
|
dot = childConstUnions[0].dot(childConstUnions[1]);
|
||||||
dot *= 2.0;
|
dot *= 2.0;
|
||||||
for (int comp = 0; comp < numComps; ++comp)
|
for (int comp = 0; comp < numComps; ++comp)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user