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) ...@@ -232,23 +232,8 @@ void TIntermSwitch::traverse(TIntermTraverser *it)
if(visit) if(visit)
{ {
it->incrementDepth(this); it->incrementDepth(this);
if(it->rightToLeft) if(it->inVisit)
{ visit = it->visitSwitch(InVisit, this);
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);
}
it->decrementDepth(); it->decrementDepth();
} }
......
...@@ -1775,6 +1775,90 @@ namespace glsl ...@@ -1775,6 +1775,90 @@ namespace glsl
return true; 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) 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); return emit(op, dst, 0, src0, 0, src1, 0, src2, 0, src3, 0, src4, 0);
......
...@@ -258,6 +258,7 @@ namespace glsl ...@@ -258,6 +258,7 @@ namespace glsl
virtual bool visitAggregate(Visit visit, TIntermAggregate*); virtual bool visitAggregate(Visit visit, TIntermAggregate*);
virtual bool visitLoop(Visit visit, TIntermLoop*); virtual bool visitLoop(Visit visit, TIntermLoop*);
virtual bool visitBranch(Visit visit, TIntermBranch*); virtual bool visitBranch(Visit visit, TIntermBranch*);
virtual bool visitSwitch(Visit, TIntermSwitch*);
sw::Shader::Opcode getOpcode(sw::Shader::Opcode op, TIntermTyped *in) const; 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); 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: ...@@ -643,6 +643,7 @@ public:
TIntermSwitch *getAsSwitchNode() { return this; } TIntermSwitch *getAsSwitchNode() { return this; }
TIntermTyped *getInit() { return mInit; }
TIntermAggregate *getStatementList() { return mStatementList; } TIntermAggregate *getStatementList() { return mStatementList; }
void setStatementList(TIntermAggregate *statementList) { mStatementList = statementList; } void setStatementList(TIntermAggregate *statementList) { mStatementList = statementList; }
......
...@@ -307,12 +307,14 @@ namespace sw ...@@ -307,12 +307,14 @@ namespace sw
case Shader::OPCODE_ENDLOOP: ENDLOOP(); break; case Shader::OPCODE_ENDLOOP: ENDLOOP(); break;
case Shader::OPCODE_ENDREP: ENDREP(); break; case Shader::OPCODE_ENDREP: ENDREP(); break;
case Shader::OPCODE_ENDWHILE: ENDWHILE(); break; case Shader::OPCODE_ENDWHILE: ENDWHILE(); break;
case Shader::OPCODE_ENDSWITCH: ENDSWITCH(); break;
case Shader::OPCODE_IF: IF(src0); break; case Shader::OPCODE_IF: IF(src0); break;
case Shader::OPCODE_IFC: IFC(s0, s1, control); break; case Shader::OPCODE_IFC: IFC(s0, s1, control); break;
case Shader::OPCODE_LABEL: LABEL(dst.index); break; case Shader::OPCODE_LABEL: LABEL(dst.index); break;
case Shader::OPCODE_LOOP: LOOP(src1); break; case Shader::OPCODE_LOOP: LOOP(src1); break;
case Shader::OPCODE_REP: REP(src0); break; case Shader::OPCODE_REP: REP(src0); break;
case Shader::OPCODE_WHILE: WHILE(src0); break; case Shader::OPCODE_WHILE: WHILE(src0); break;
case Shader::OPCODE_SWITCH: SWITCH(); break;
case Shader::OPCODE_RET: RET(); break; case Shader::OPCODE_RET: RET(); break;
case Shader::OPCODE_LEAVE: LEAVE(); break; case Shader::OPCODE_LEAVE: LEAVE(); break;
case Shader::OPCODE_CMP: cmp(d, s0, s1, control); break; case Shader::OPCODE_CMP: cmp(d, s0, s1, control); break;
...@@ -1475,6 +1477,19 @@ namespace sw ...@@ -1475,6 +1477,19 @@ namespace sw
whileTest = false; 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) void PixelProgram::IF(const Src &src)
{ {
if(src.type == Shader::PARAMETER_CONSTBOOL) if(src.type == Shader::PARAMETER_CONSTBOOL)
...@@ -1674,6 +1689,20 @@ namespace sw ...@@ -1674,6 +1689,20 @@ namespace sw
breakDepth = 0; 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() void PixelProgram::RET()
{ {
if(currentLabel == -1) if(currentLabel == -1)
......
...@@ -136,6 +136,7 @@ namespace sw ...@@ -136,6 +136,7 @@ namespace sw
void ENDLOOP(); void ENDLOOP();
void ENDREP(); void ENDREP();
void ENDWHILE(); void ENDWHILE();
void ENDSWITCH();
void IF(const Src &src); void IF(const Src &src);
void IFb(const Src &boolRegister); void IFb(const Src &boolRegister);
void IFp(const Src &predicateRegister); void IFp(const Src &predicateRegister);
...@@ -145,6 +146,7 @@ namespace sw ...@@ -145,6 +146,7 @@ namespace sw
void LOOP(const Src &integerRegister); void LOOP(const Src &integerRegister);
void REP(const Src &integerRegister); void REP(const Src &integerRegister);
void WHILE(const Src &temporaryRegister); void WHILE(const Src &temporaryRegister);
void SWITCH();
void RET(); void RET();
void LEAVE(); void LEAVE();
......
...@@ -971,6 +971,8 @@ namespace sw ...@@ -971,6 +971,8 @@ namespace sw
case OPCODE_LEAVE: return "leave"; case OPCODE_LEAVE: return "leave";
case OPCODE_CONTINUE: return "continue"; case OPCODE_CONTINUE: return "continue";
case OPCODE_TEST: return "test"; case OPCODE_TEST: return "test";
case OPCODE_SWITCH: return "switch";
case OPCODE_ENDSWITCH: return "endswitch";
default: default:
ASSERT(false); ASSERT(false);
} }
...@@ -1088,14 +1090,14 @@ namespace sw ...@@ -1088,14 +1090,14 @@ namespace sw
return opcode == OPCODE_BREAK || opcode == OPCODE_BREAKC || opcode == OPCODE_BREAKP; 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 bool Shader::Instruction::isPredicated() const
...@@ -1686,11 +1688,11 @@ namespace sw ...@@ -1686,11 +1688,11 @@ namespace sw
if(breakDepth > 0) 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++; breakDepth++;
} }
else if(instruction[i]->isEndLoop()) else if(instruction[i]->isEndLoopOrSwitch())
{ {
breakDepth--; breakDepth--;
} }
...@@ -1711,11 +1713,11 @@ namespace sw ...@@ -1711,11 +1713,11 @@ namespace sw
if(continueDepth > 0) 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++; continueDepth++;
} }
else if(instruction[i]->isEndLoop()) else if(instruction[i]->isEndLoopOrSwitch())
{ {
continueDepth--; continueDepth--;
} }
......
...@@ -244,6 +244,8 @@ namespace sw ...@@ -244,6 +244,8 @@ namespace sw
OPCODE_LEAVE, // Return before the end of the function OPCODE_LEAVE, // Return before the end of the function
OPCODE_CONTINUE, OPCODE_CONTINUE,
OPCODE_TEST, // Marks the end of the code that can be skipped by 'continue' OPCODE_TEST, // Marks the end of the code that can be skipped by 'continue'
OPCODE_SWITCH,
OPCODE_ENDSWITCH,
// Integer opcodes // Integer opcodes
OPCODE_INEG, OPCODE_INEG,
...@@ -493,8 +495,8 @@ namespace sw ...@@ -493,8 +495,8 @@ namespace sw
bool isBranch() const; bool isBranch() const;
bool isCall() const; bool isCall() const;
bool isBreak() const; bool isBreak() const;
bool isLoop() const; bool isLoopOrSwitch() const;
bool isEndLoop() const; bool isEndLoopOrSwitch() const;
bool isPredicated() const; bool isPredicated() const;
......
...@@ -296,12 +296,14 @@ namespace sw ...@@ -296,12 +296,14 @@ namespace sw
case Shader::OPCODE_ENDLOOP: ENDLOOP(); break; case Shader::OPCODE_ENDLOOP: ENDLOOP(); break;
case Shader::OPCODE_ENDREP: ENDREP(); break; case Shader::OPCODE_ENDREP: ENDREP(); break;
case Shader::OPCODE_ENDWHILE: ENDWHILE(); break; case Shader::OPCODE_ENDWHILE: ENDWHILE(); break;
case Shader::OPCODE_ENDSWITCH: ENDSWITCH(); break;
case Shader::OPCODE_IF: IF(src0); break; case Shader::OPCODE_IF: IF(src0); break;
case Shader::OPCODE_IFC: IFC(s0, s1, control); break; case Shader::OPCODE_IFC: IFC(s0, s1, control); break;
case Shader::OPCODE_LABEL: LABEL(dst.index); break; case Shader::OPCODE_LABEL: LABEL(dst.index); break;
case Shader::OPCODE_LOOP: LOOP(src1); break; case Shader::OPCODE_LOOP: LOOP(src1); break;
case Shader::OPCODE_REP: REP(src0); break; case Shader::OPCODE_REP: REP(src0); break;
case Shader::OPCODE_WHILE: WHILE(src0); break; case Shader::OPCODE_WHILE: WHILE(src0); break;
case Shader::OPCODE_SWITCH: SWITCH(); break;
case Shader::OPCODE_RET: RET(); break; case Shader::OPCODE_RET: RET(); break;
case Shader::OPCODE_LEAVE: LEAVE(); break; case Shader::OPCODE_LEAVE: LEAVE(); break;
case Shader::OPCODE_CMP: cmp(d, s0, s1, control); break; case Shader::OPCODE_CMP: cmp(d, s0, s1, control); break;
...@@ -1256,6 +1258,19 @@ namespace sw ...@@ -1256,6 +1258,19 @@ namespace sw
whileTest = false; 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) void VertexProgram::IF(const Src &src)
{ {
if(src.type == Shader::PARAMETER_CONSTBOOL) if(src.type == Shader::PARAMETER_CONSTBOOL)
...@@ -1456,6 +1471,20 @@ namespace sw ...@@ -1456,6 +1471,20 @@ namespace sw
breakDepth = 0; 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() void VertexProgram::RET()
{ {
if(currentLabel == -1) if(currentLabel == -1)
......
...@@ -93,6 +93,7 @@ namespace sw ...@@ -93,6 +93,7 @@ namespace sw
void ENDLOOP(); void ENDLOOP();
void ENDREP(); void ENDREP();
void ENDWHILE(); void ENDWHILE();
void ENDSWITCH();
void IF(const Src &src); void IF(const Src &src);
void IFb(const Src &boolRegister); void IFb(const Src &boolRegister);
void IFp(const Src &predicateRegister); void IFp(const Src &predicateRegister);
...@@ -102,6 +103,7 @@ namespace sw ...@@ -102,6 +103,7 @@ namespace sw
void LOOP(const Src &integerRegister); void LOOP(const Src &integerRegister);
void REP(const Src &integerRegister); void REP(const Src &integerRegister);
void WHILE(const Src &temporaryRegister); void WHILE(const Src &temporaryRegister);
void SWITCH();
void RET(); void RET();
void LEAVE(); void LEAVE();
void TEXLDL(Vector4f &dst, Vector4f &src, const Src&); 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