1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-03-13 19:29:14 +01:00

[dxbc] Implemented switch-case instructions

This commit is contained in:
Philip Rebohle 2017-12-30 17:22:36 +01:00
parent 006e1b25e6
commit 5332891748
8 changed files with 281 additions and 27 deletions

View File

@ -34,7 +34,6 @@ namespace dxvk {
const std::string& semanticName,
uint32_t semanticIndex) const {
for (auto e = this->begin(); e != this->end(); e++) {
// TODO case-insensitive compare
if (e->semanticIndex == semanticIndex
&& compareSemanticNames(semanticName, e->semanticName))
return &(*e);

View File

@ -2280,6 +2280,109 @@ namespace dxvk {
}
void DxbcCompiler::emitControlFlowSwitch(const DxbcShaderInstruction& ins) {
// Load the selector as a scalar unsigned integer
const DxbcRegisterValue selector = emitRegisterLoad(
ins.src[0], DxbcRegMask(true, false, false, false));
// Declare switch block. We cannot insert the switch
// instruction itself yet because the number of case
// statements and blocks is unknown at this point.
DxbcCfgBlock block;
block.type = DxbcCfgBlockType::Switch;
block.b_switch.insertPtr = m_module.getInsertionPtr();
block.b_switch.selectorId = selector.id;
block.b_switch.labelBreak = m_module.allocateId();
block.b_switch.labelCase = m_module.allocateId();
block.b_switch.labelDefault = 0;
block.b_switch.labelCases = nullptr;
m_controlFlowBlocks.push_back(block);
// Define the first 'case' label
m_module.opLabel(block.b_switch.labelCase);
}
void DxbcCompiler::emitControlFlowCase(const DxbcShaderInstruction& ins) {
if (m_controlFlowBlocks.size() == 0
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::Switch)
throw DxvkError("DxbcCompiler: 'Case' without 'Switch' found");
// The source operand must be a 32-bit immediate.
if (ins.src[0].type != DxbcOperandType::Imm32)
throw DxvkError("DxbcCompiler: Invalid operand type for 'Case'");
// Use the last label allocated for 'case'. The block starting
// with that label is guaranteed to be empty unless a previous
// 'case' block was not properly closed in the DXBC shader.
DxbcCfgBlockSwitch* block = &m_controlFlowBlocks.back().b_switch;
DxbcSwitchLabel label;
label.desc.literal = ins.src[0].imm.u32_1;
label.desc.labelId = block->labelCase;
label.next = block->labelCases;
block->labelCases = new DxbcSwitchLabel(label);
}
void DxbcCompiler::emitControlFlowDefault(const DxbcShaderInstruction& ins) {
if (m_controlFlowBlocks.size() == 0
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::Switch)
throw DxvkError("DxbcCompiler: 'Default' without 'Switch' found");
// Set the last label allocated for 'case' as the default label.
m_controlFlowBlocks.back().b_switch.labelDefault
= m_controlFlowBlocks.back().b_switch.labelCase;
}
void DxbcCompiler::emitControlFlowEndSwitch(const DxbcShaderInstruction& ins) {
if (m_controlFlowBlocks.size() == 0
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::Switch)
throw DxvkError("DxbcCompiler: 'EndSwitch' without 'Switch' found");
// Remove the block from the stack, it's closed
DxbcCfgBlock block = m_controlFlowBlocks.back();
m_controlFlowBlocks.pop_back();
// If no 'default' label was specified, use the last allocated
// 'case' label. This is guaranteed to be an empty block unless
// a previous 'case' block was not closed properly.
if (block.b_switch.labelDefault == 0)
block.b_switch.labelDefault = block.b_switch.labelCase;
// Close the current 'case' block
m_module.opBranch(block.b_switch.labelBreak);
m_module.opLabel (block.b_switch.labelBreak);
// Insert the 'switch' statement. For that, we need to
// gather all the literal-label pairs for the construct.
m_module.beginInsertion(block.b_switch.insertPtr);
m_module.opSelectionMerge(
block.b_switch.labelBreak,
spv::SelectionControlMaskNone);
// We'll restore the original order of the case labels here
std::vector<SpirvSwitchCaseLabel> jumpTargets;
for (auto i = block.b_switch.labelCases; i != nullptr; i = i->next)
jumpTargets.insert(jumpTargets.begin(), i->desc);
m_module.opSwitch(
block.b_switch.selectorId,
block.b_switch.labelDefault,
jumpTargets.size(),
jumpTargets.data());
m_module.endInsertion();
// Destroy the list of case labels
// FIXME we're leaking memory if compilation fails.
DxbcSwitchLabel* caseLabel = block.b_switch.labelCases;
while (caseLabel != nullptr)
delete std::exchange(caseLabel, caseLabel->next);
}
void DxbcCompiler::emitControlFlowLoop(const DxbcShaderInstruction& ins) {
// Declare the 'loop' block
DxbcCfgBlock block;
@ -2325,25 +2428,42 @@ namespace dxvk {
void DxbcCompiler::emitControlFlowBreak(const DxbcShaderInstruction& ins) {
const bool isBreak = ins.op == DxbcOpcode::Break;
DxbcCfgBlock* loopBlock = cfgFindLoopBlock();
DxbcCfgBlock* cfgBlock = isBreak
? cfgFindBlock({ DxbcCfgBlockType::Loop, DxbcCfgBlockType::Switch })
: cfgFindBlock({ DxbcCfgBlockType::Loop });
if (loopBlock == nullptr)
throw DxvkError("DxbcCompiler: 'Break' or 'Continue' outside 'Loop' found");
if (cfgBlock == nullptr)
throw DxvkError("DxbcCompiler: 'Break' or 'Continue' outside 'Loop' or 'Switch' found");
m_module.opBranch(isBreak
? loopBlock->b_loop.labelBreak
: loopBlock->b_loop.labelContinue);
m_module.opLabel (m_module.allocateId());
if (cfgBlock->type == DxbcCfgBlockType::Loop) {
m_module.opBranch(isBreak
? cfgBlock->b_loop.labelBreak
: cfgBlock->b_loop.labelContinue);
} else /* if (cfgBlock->type == DxbcCfgBlockType::Switch) */ {
m_module.opBranch(cfgBlock->b_switch.labelBreak);
}
// Subsequent instructions assume that there is an open block
const uint32_t labelId = m_module.allocateId();
m_module.opLabel(labelId);
// If this is on the same level as a switch-case construct,
// rather than being nested inside an 'if' statement, close
// the current 'case' block.
if (m_controlFlowBlocks.back().type == DxbcCfgBlockType::Switch)
cfgBlock->b_switch.labelCase = labelId;
}
void DxbcCompiler::emitControlFlowBreakc(const DxbcShaderInstruction& ins) {
const bool isBreak = ins.op == DxbcOpcode::Breakc;
DxbcCfgBlock* loopBlock = cfgFindLoopBlock();
DxbcCfgBlock* cfgBlock = isBreak
? cfgFindBlock({ DxbcCfgBlockType::Loop, DxbcCfgBlockType::Switch })
: cfgFindBlock({ DxbcCfgBlockType::Loop });
if (loopBlock == nullptr)
throw DxvkError("DxbcCompiler: 'Breakc' or 'Continuec' outside 'Loop' found");
if (cfgBlock == nullptr)
throw DxvkError("DxbcCompiler: 'Breakc' or 'Continuec' outside 'Loop' or 'Switch' found");
// Perform zero test on the first component of the condition
const DxbcRegisterValue condition = emitRegisterLoad(
@ -2363,18 +2483,26 @@ namespace dxvk {
zeroTest.id, breakBlock, mergeBlock);
m_module.opLabel(breakBlock);
m_module.opBranch(isBreak
? loopBlock->b_loop.labelBreak
: loopBlock->b_loop.labelContinue);
if (cfgBlock->type == DxbcCfgBlockType::Loop) {
m_module.opBranch(isBreak
? cfgBlock->b_loop.labelBreak
: cfgBlock->b_loop.labelContinue);
} else /* if (cfgBlock->type == DxbcCfgBlockType::Switch) */ {
m_module.opBranch(cfgBlock->b_switch.labelBreak);
}
m_module.opLabel(mergeBlock);
}
void DxbcCompiler::emitControlFlowRet(const DxbcShaderInstruction& ins) {
// TODO implement properly
m_module.opReturn();
m_module.functionEnd();
if (m_controlFlowBlocks.size() == 0)
m_module.functionEnd();
else
m_module.opLabel(m_module.allocateId());
}
@ -2416,6 +2544,18 @@ namespace dxvk {
case DxbcOpcode::EndIf:
return this->emitControlFlowEndIf(ins);
case DxbcOpcode::Switch:
return this->emitControlFlowSwitch(ins);
case DxbcOpcode::Case:
return this->emitControlFlowCase(ins);
case DxbcOpcode::Default:
return this->emitControlFlowDefault(ins);
case DxbcOpcode::EndSwitch:
return this->emitControlFlowEndSwitch(ins);
case DxbcOpcode::Loop:
return this->emitControlFlowLoop(ins);
@ -3714,11 +3854,14 @@ namespace dxvk {
}
DxbcCfgBlock* DxbcCompiler::cfgFindLoopBlock() {
DxbcCfgBlock* DxbcCompiler::cfgFindBlock(
const std::initializer_list<DxbcCfgBlockType>& types) {
for (auto cur = m_controlFlowBlocks.rbegin();
cur != m_controlFlowBlocks.rend(); cur++) {
if (cur->type == DxbcCfgBlockType::Loop)
return &(*cur);
for (auto type : types) {
if (cur->type == type)
return &(*cur);
}
}
return nullptr;

View File

@ -145,7 +145,7 @@ namespace dxvk {
enum class DxbcCfgBlockType : uint32_t {
If, Loop,
If, Loop, Switch,
};
@ -165,12 +165,29 @@ namespace dxvk {
};
struct DxbcSwitchLabel {
SpirvSwitchCaseLabel desc;
DxbcSwitchLabel* next;
};
struct DxbcCfgBlockSwitch {
size_t insertPtr;
uint32_t selectorId;
uint32_t labelBreak;
uint32_t labelCase;
uint32_t labelDefault;
DxbcSwitchLabel* labelCases;
};
struct DxbcCfgBlock {
DxbcCfgBlockType type;
union {
DxbcCfgBlockIf b_if;
DxbcCfgBlockLoop b_loop;
DxbcCfgBlockIf b_if;
DxbcCfgBlockLoop b_loop;
DxbcCfgBlockSwitch b_switch;
};
};
@ -433,6 +450,18 @@ namespace dxvk {
void emitControlFlowEndIf(
const DxbcShaderInstruction& ins);
void emitControlFlowSwitch(
const DxbcShaderInstruction& ins);
void emitControlFlowCase(
const DxbcShaderInstruction& ins);
void emitControlFlowDefault(
const DxbcShaderInstruction& ins);
void emitControlFlowEndSwitch(
const DxbcShaderInstruction& ins);
void emitControlFlowLoop(
const DxbcShaderInstruction& ins);
@ -649,7 +678,8 @@ namespace dxvk {
////////////////
// Misc methods
DxbcCfgBlock* cfgFindLoopBlock();
DxbcCfgBlock* cfgFindBlock(
const std::initializer_list<DxbcCfgBlockType>& types);
DxbcBufferInfo getBufferInfo(
const DxbcRegister& reg);

View File

@ -42,7 +42,7 @@ namespace dxvk {
/* Cut */
{ 0, DxbcInstClass::GeometryEmit },
/* Default */
{ },
{ 0, DxbcInstClass::ControlFlow },
/* DerivRtx */
{ 2, DxbcInstClass::VectorDeriv, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },

View File

@ -9,7 +9,8 @@ namespace dxvk {
SpirvCodeBuffer::~SpirvCodeBuffer() { }
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size, const uint32_t* data) {
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size, const uint32_t* data)
: m_ptr(size) {
m_code.resize(size);
std::memcpy(m_code.data(), data, size * sizeof(uint32_t));
}
@ -28,6 +29,8 @@ namespace dxvk {
m_code.resize(buffer.size() / sizeof(uint32_t));
std::memcpy(reinterpret_cast<char*>(m_code.data()),
buffer.data(), m_code.size() * sizeof(uint32_t));
m_ptr = m_code.size();
}
@ -40,12 +43,14 @@ namespace dxvk {
const uint32_t* src = other.m_code.data();
std::memcpy(dst + size, src, other.size());
m_ptr += other.m_code.size();
}
}
void SpirvCodeBuffer::putWord(uint32_t word) {
m_code.push_back(word);
m_code.insert(m_code.begin() + m_ptr, word);
m_ptr += 1;
}

View File

@ -143,9 +143,46 @@ namespace dxvk {
*/
void store(std::ostream&& stream) const;
/**
* \brief Retrieves current insertion pointer
*
* Sometimes it may be necessay to insert code into the
* middle of the stream rather than appending it. This
* retrieves the current function pointer. Note that the
* pointer will become invalid if any code is inserted
* before the current pointer location.
* \returns Current instruction pointr
*/
size_t getInsertionPtr() const {
return m_ptr;
}
/**
* \brief Sets insertion pointer to a specific value
*
* Sets the insertion pointer to a value that was
* previously retrieved by \ref getInsertionPtr.
* \returns Current instruction pointr
*/
void beginInsertion(size_t ptr) {
m_ptr = ptr;
}
/**
* \brief Sets insertion pointer to the end
*
* After this call, new instructions will be
* appended to the stream. In other words,
* this will restore default behaviour.
*/
void endInsertion() {
m_ptr = m_code.size();
}
private:
std::vector<uint32_t> m_code;
size_t m_ptr = 0;
};

View File

@ -2099,6 +2099,22 @@ namespace dxvk {
m_code.putWord(falseLabel);
}
void SpirvModule::opSwitch(
uint32_t selector,
uint32_t jumpDefault,
uint32_t caseCount,
const SpirvSwitchCaseLabel* caseLabels) {
m_code.putIns (spv::OpSwitch, 3 + 2 * caseCount);
m_code.putWord(selector);
m_code.putWord(jumpDefault);
for (uint32_t i = 0; i < caseCount; i++) {
m_code.putWord(caseLabels[i].literal);
m_code.putWord(caseLabels[i].labelId);
}
}
void SpirvModule::opReturn() {
m_code.putIns (spv::OpReturn, 1);

View File

@ -4,6 +4,11 @@
namespace dxvk {
struct SpirvSwitchCaseLabel {
uint32_t literal = 0;
uint32_t labelId = 0;
};
struct SpirvImageOperands {
uint32_t flags = 0;
uint32_t sLodBias = 0;
@ -34,6 +39,18 @@ namespace dxvk {
SpirvCodeBuffer compile() const;
size_t getInsertionPtr() {
return m_code.getInsertionPtr();
}
void beginInsertion(size_t ptr) {
m_code.beginInsertion(ptr);
}
void endInsertion() {
m_code.endInsertion();
}
uint32_t allocateId();
void enableCapability(
@ -720,6 +737,13 @@ namespace dxvk {
uint32_t trueLabel,
uint32_t falseLabel);
void opSwitch(
uint32_t selector,
uint32_t jumpDefault,
uint32_t caseCount,
const SpirvSwitchCaseLabel* caseLabels);
void opReturn();
void opKill();