Commit 9aa83a93 by Alexis Hetu Committed by Alexis Hétu

Switch implementation

Implemented switch/case for glsl in OpenGL ES 3.0. For simplicity, it is implemented as a loop without a condition, so break statements work properly like so: begin switch if(...) // 1st case ... else if(...) // other cases ... else // default case ... end switch // Anchor point for break statements All related dEQP tests pass, except 7 tests where vertex shaders contain a switch or a loop within another switch. These 7 failures have only about 5% of bad pixel and seem to be related to an issue with int(floor(...)), since the equivalent tests inside the fragment shader pass. KNOWN ISSUE: If a switch is within a loop and one of the cases contains a "continue" statement, this will not be handled correctly at the moment. There are no dEQP tests for this at the moment, AFAIK. Change-Id: I3ba34ab06a759d07e8520f6a87d75036a5cdaef5 Reviewed-on: https://swiftshader-review.googlesource.com/5272Tested-by: 's avatarAlexis Hétu <sugoi@google.com> Reviewed-by: 's avatarNicolas Capens <capn@google.com>
parent 70085ba5
......@@ -232,23 +232,8 @@ void TIntermSwitch::traverse(TIntermTraverser *it)
if(visit)
{
it->incrementDepth(this);
if(it->rightToLeft)
{
if(mStatementList)
mStatementList->traverse(it);
if(it->inVisit)
visit = it->visitSwitch(InVisit, this);
if(visit)
mInit->traverse(it);
}
else
{
mInit->traverse(it);
if(it->inVisit)
visit = it->visitSwitch(InVisit, this);
if(visit && mStatementList)
mStatementList->traverse(it);
}
if(it->inVisit)
visit = it->visitSwitch(InVisit, this);
it->decrementDepth();
}
......
......@@ -1775,6 +1775,90 @@ namespace glsl
return true;
}
bool OutputASM::visitSwitch(Visit visit, TIntermSwitch *node)
{
if(currentScope != emitScope)
{
return false;
}
TIntermTyped* switchValue = node->getInit();
TIntermAggregate* opList = node->getStatementList();
if(!switchValue || !opList)
{
return false;
}
switchValue->traverse(this);
emit(sw::Shader::OPCODE_SWITCH);
TIntermSequence& sequence = opList->getSequence();
TIntermSequence::iterator it = sequence.begin();
TIntermSequence::iterator defaultIt = sequence.end();
int nbCases = 0;
for(; it != sequence.end(); ++it)
{
TIntermCase* currentCase = (*it)->getAsCaseNode();
if(currentCase)
{
TIntermSequence::iterator caseIt = it;
TIntermTyped* condition = currentCase->getCondition();
if(condition) // non default case
{
if(nbCases != 0)
{
emit(sw::Shader::OPCODE_ELSE);
}
condition->traverse(this);
Temporary result(this);
emitBinary(sw::Shader::OPCODE_EQ, &result, switchValue, condition);
emit(sw::Shader::OPCODE_IF, 0, &result);
nbCases++;
for(++caseIt; caseIt != sequence.end(); ++caseIt)
{
(*caseIt)->traverse(this);
if((*caseIt)->getAsBranchNode()) // Kill, Break, Continue or Return
{
break;
}
}
}
else
{
defaultIt = it; // The default case might not be the last case, keep it for last
}
}
}
// If there's a default case, traverse it here
if(defaultIt != sequence.end())
{
emit(sw::Shader::OPCODE_ELSE);
for(++defaultIt; defaultIt != sequence.end(); ++defaultIt)
{
(*defaultIt)->traverse(this);
if((*defaultIt)->getAsBranchNode()) // Kill, Break, Continue or Return
{
break;
}
}
}
for(int i = 0; i < nbCases; ++i)
{
emit(sw::Shader::OPCODE_ENDIF);
}
emit(sw::Shader::OPCODE_ENDSWITCH);
return false;
}
Instruction *OutputASM::emit(sw::Shader::Opcode op, TIntermTyped *dst, TIntermNode *src0, TIntermNode *src1, TIntermNode *src2, TIntermNode *src3, TIntermNode *src4)
{
return emit(op, dst, 0, src0, 0, src1, 0, src2, 0, src3, 0, src4, 0);
......
......@@ -258,6 +258,7 @@ namespace glsl
virtual bool visitAggregate(Visit visit, TIntermAggregate*);
virtual bool visitLoop(Visit visit, TIntermLoop*);
virtual bool visitBranch(Visit visit, TIntermBranch*);
virtual bool visitSwitch(Visit, TIntermSwitch*);
sw::Shader::Opcode getOpcode(sw::Shader::Opcode op, TIntermTyped *in) const;
Instruction *emit(sw::Shader::Opcode op, TIntermTyped *dst = 0, TIntermNode *src0 = 0, TIntermNode *src1 = 0, TIntermNode *src2 = 0, TIntermNode *src3 = 0, TIntermNode *src4 = 0);
......
......@@ -643,6 +643,7 @@ public:
TIntermSwitch *getAsSwitchNode() { return this; }
TIntermTyped *getInit() { return mInit; }
TIntermAggregate *getStatementList() { return mStatementList; }
void setStatementList(TIntermAggregate *statementList) { mStatementList = statementList; }
......
......@@ -307,12 +307,14 @@ namespace sw
case Shader::OPCODE_ENDLOOP: ENDLOOP(); break;
case Shader::OPCODE_ENDREP: ENDREP(); break;
case Shader::OPCODE_ENDWHILE: ENDWHILE(); break;
case Shader::OPCODE_ENDSWITCH: ENDSWITCH(); break;
case Shader::OPCODE_IF: IF(src0); break;
case Shader::OPCODE_IFC: IFC(s0, s1, control); break;
case Shader::OPCODE_LABEL: LABEL(dst.index); break;
case Shader::OPCODE_LOOP: LOOP(src1); break;
case Shader::OPCODE_REP: REP(src0); break;
case Shader::OPCODE_WHILE: WHILE(src0); break;
case Shader::OPCODE_SWITCH: SWITCH(); break;
case Shader::OPCODE_RET: RET(); break;
case Shader::OPCODE_LEAVE: LEAVE(); break;
case Shader::OPCODE_CMP: cmp(d, s0, s1, control); break;
......@@ -1475,6 +1477,19 @@ namespace sw
whileTest = false;
}
void PixelProgram::ENDSWITCH()
{
loopRepDepth--;
llvm::BasicBlock *endBlock = loopRepEndBlock[loopRepDepth];
Nucleus::createBr(loopRepEndBlock[loopRepDepth]);
Nucleus::setInsertBlock(endBlock);
enableIndex--;
enableBreak = Int4(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
}
void PixelProgram::IF(const Src &src)
{
if(src.type == Shader::PARAMETER_CONSTBOOL)
......@@ -1674,6 +1689,20 @@ namespace sw
breakDepth = 0;
}
void PixelProgram::SWITCH()
{
enableIndex++;
enableStack[enableIndex] = Int4(0xFFFFFFFF);
llvm::BasicBlock *endBlock = Nucleus::createBasicBlock();
loopRepTestBlock[loopRepDepth] = nullptr;
loopRepEndBlock[loopRepDepth] = endBlock;
loopRepDepth++;
breakDepth = 0;
}
void PixelProgram::RET()
{
if(currentLabel == -1)
......
......@@ -136,6 +136,7 @@ namespace sw
void ENDLOOP();
void ENDREP();
void ENDWHILE();
void ENDSWITCH();
void IF(const Src &src);
void IFb(const Src &boolRegister);
void IFp(const Src &predicateRegister);
......@@ -145,6 +146,7 @@ namespace sw
void LOOP(const Src &integerRegister);
void REP(const Src &integerRegister);
void WHILE(const Src &temporaryRegister);
void SWITCH();
void RET();
void LEAVE();
......
......@@ -971,6 +971,8 @@ namespace sw
case OPCODE_LEAVE: return "leave";
case OPCODE_CONTINUE: return "continue";
case OPCODE_TEST: return "test";
case OPCODE_SWITCH: return "switch";
case OPCODE_ENDSWITCH: return "endswitch";
default:
ASSERT(false);
}
......@@ -1088,14 +1090,14 @@ namespace sw
return opcode == OPCODE_BREAK || opcode == OPCODE_BREAKC || opcode == OPCODE_BREAKP;
}
bool Shader::Instruction::isLoop() const
bool Shader::Instruction::isLoopOrSwitch() const
{
return opcode == OPCODE_LOOP || opcode == OPCODE_REP || opcode == OPCODE_WHILE;
return opcode == OPCODE_LOOP || opcode == OPCODE_REP || opcode == OPCODE_WHILE || opcode == OPCODE_SWITCH;
}
bool Shader::Instruction::isEndLoop() const
bool Shader::Instruction::isEndLoopOrSwitch() const
{
return opcode == OPCODE_ENDLOOP || opcode == OPCODE_ENDREP || opcode == OPCODE_ENDWHILE;
return opcode == OPCODE_ENDLOOP || opcode == OPCODE_ENDREP || opcode == OPCODE_ENDWHILE || opcode == OPCODE_ENDSWITCH;;
}
bool Shader::Instruction::isPredicated() const
......@@ -1686,11 +1688,11 @@ namespace sw
if(breakDepth > 0)
{
if(instruction[i]->isLoop()) // Nested loop, don't make the end of it disable the break execution mask
if(instruction[i]->isLoopOrSwitch()) // Nested loop or switch, don't make the end of it disable the break execution mask
{
breakDepth++;
}
else if(instruction[i]->isEndLoop())
else if(instruction[i]->isEndLoopOrSwitch())
{
breakDepth--;
}
......@@ -1711,11 +1713,11 @@ namespace sw
if(continueDepth > 0)
{
if(instruction[i]->isLoop()) // Nested loop, don't make the end of it disable the break execution mask
if(instruction[i]->isLoopOrSwitch()) // Nested loop or switch, don't make the end of it disable the break execution mask
{
continueDepth++;
}
else if(instruction[i]->isEndLoop())
else if(instruction[i]->isEndLoopOrSwitch())
{
continueDepth--;
}
......
......@@ -244,6 +244,8 @@ namespace sw
OPCODE_LEAVE, // Return before the end of the function
OPCODE_CONTINUE,
OPCODE_TEST, // Marks the end of the code that can be skipped by 'continue'
OPCODE_SWITCH,
OPCODE_ENDSWITCH,
// Integer opcodes
OPCODE_INEG,
......@@ -493,8 +495,8 @@ namespace sw
bool isBranch() const;
bool isCall() const;
bool isBreak() const;
bool isLoop() const;
bool isEndLoop() const;
bool isLoopOrSwitch() const;
bool isEndLoopOrSwitch() const;
bool isPredicated() const;
......
......@@ -296,12 +296,14 @@ namespace sw
case Shader::OPCODE_ENDLOOP: ENDLOOP(); break;
case Shader::OPCODE_ENDREP: ENDREP(); break;
case Shader::OPCODE_ENDWHILE: ENDWHILE(); break;
case Shader::OPCODE_ENDSWITCH: ENDSWITCH(); break;
case Shader::OPCODE_IF: IF(src0); break;
case Shader::OPCODE_IFC: IFC(s0, s1, control); break;
case Shader::OPCODE_LABEL: LABEL(dst.index); break;
case Shader::OPCODE_LOOP: LOOP(src1); break;
case Shader::OPCODE_REP: REP(src0); break;
case Shader::OPCODE_WHILE: WHILE(src0); break;
case Shader::OPCODE_SWITCH: SWITCH(); break;
case Shader::OPCODE_RET: RET(); break;
case Shader::OPCODE_LEAVE: LEAVE(); break;
case Shader::OPCODE_CMP: cmp(d, s0, s1, control); break;
......@@ -1256,6 +1258,19 @@ namespace sw
whileTest = false;
}
void VertexProgram::ENDSWITCH()
{
loopRepDepth--;
llvm::BasicBlock *endBlock = loopRepEndBlock[loopRepDepth];
Nucleus::createBr(loopRepEndBlock[loopRepDepth]);
Nucleus::setInsertBlock(endBlock);
enableIndex--;
enableBreak = Int4(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
}
void VertexProgram::IF(const Src &src)
{
if(src.type == Shader::PARAMETER_CONSTBOOL)
......@@ -1456,6 +1471,20 @@ namespace sw
breakDepth = 0;
}
void VertexProgram::SWITCH()
{
enableIndex++;
enableStack[enableIndex] = Int4(0xFFFFFFFF);
llvm::BasicBlock *endBlock = Nucleus::createBasicBlock();
loopRepTestBlock[loopRepDepth] = nullptr;
loopRepEndBlock[loopRepDepth] = endBlock;
loopRepDepth++;
breakDepth = 0;
}
void VertexProgram::RET()
{
if(currentLabel == -1)
......
......@@ -93,6 +93,7 @@ namespace sw
void ENDLOOP();
void ENDREP();
void ENDWHILE();
void ENDSWITCH();
void IF(const Src &src);
void IFb(const Src &boolRegister);
void IFp(const Src &predicateRegister);
......@@ -102,6 +103,7 @@ namespace sw
void LOOP(const Src &integerRegister);
void REP(const Src &integerRegister);
void WHILE(const Src &temporaryRegister);
void SWITCH();
void RET();
void LEAVE();
void TEXLDL(Vector4f &dst, Vector4f &src, const Src&);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment