2017-10-16 17:50:09 +02:00
|
|
|
#include "dxbc_compiler.h"
|
|
|
|
|
|
|
|
namespace dxvk {
|
2018-07-30 20:52:42 +02:00
|
|
|
|
|
|
|
constexpr uint32_t Icb_BindingSlotId = 14;
|
|
|
|
constexpr uint32_t Icb_MaxBakedDwords = 16;
|
2017-10-16 17:50:09 +02:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
constexpr uint32_t PerVertex_Position = 0;
|
2017-12-21 16:00:36 +01:00
|
|
|
constexpr uint32_t PerVertex_CullDist = 1;
|
|
|
|
constexpr uint32_t PerVertex_ClipDist = 2;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-14 12:53:53 +01:00
|
|
|
DxbcCompiler::DxbcCompiler(
|
2018-04-15 21:00:08 +02:00
|
|
|
const std::string& fileName,
|
2018-06-23 17:14:35 +02:00
|
|
|
const DxbcModuleInfo& moduleInfo,
|
2018-10-08 09:34:56 +02:00
|
|
|
const DxbcProgramInfo& programInfo,
|
2017-12-07 16:29:34 +01:00
|
|
|
const Rc<DxbcIsgn>& isgn,
|
2018-03-23 01:04:04 +01:00
|
|
|
const Rc<DxbcIsgn>& osgn,
|
|
|
|
const DxbcAnalysisInfo& analysis)
|
2018-10-08 09:34:56 +02:00
|
|
|
: m_moduleInfo (moduleInfo),
|
|
|
|
m_programInfo(programInfo),
|
|
|
|
m_isgn (isgn),
|
|
|
|
m_osgn (osgn),
|
|
|
|
m_analysis (&analysis) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// Declare an entry point ID. We'll need it during the
|
|
|
|
// initialization phase where the execution mode is set.
|
|
|
|
m_entryPointId = m_module.allocateId();
|
|
|
|
|
2018-04-15 21:00:08 +02:00
|
|
|
// Set the shader name so that we recognize it in renderdoc
|
|
|
|
m_module.setDebugSource(
|
|
|
|
spv::SourceLanguageUnknown, 0,
|
|
|
|
m_module.addDebugString(fileName.c_str()),
|
2018-05-02 13:07:26 +02:00
|
|
|
nullptr);
|
2018-04-15 21:00:08 +02:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Set the memory model. This is the same for all shaders.
|
|
|
|
m_module.setMemoryModel(
|
|
|
|
spv::AddressingModelLogical,
|
|
|
|
spv::MemoryModelGLSL450);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Make sure our interface registers are clear
|
|
|
|
for (uint32_t i = 0; i < DxbcMaxInterfaceRegs; i++) {
|
2018-07-01 12:44:37 +02:00
|
|
|
m_vRegs.at(i) = DxbcRegisterPointer { };
|
|
|
|
m_oRegs.at(i) = DxbcRegisterPointer { };
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
2018-05-26 14:54:05 +02:00
|
|
|
// Clear spec constants
|
|
|
|
for (uint32_t i = 0; i < m_specConstants.size(); i++) {
|
|
|
|
m_specConstants.at(i) = DxbcRegisterValue {
|
|
|
|
DxbcVectorType { DxbcScalarType::Uint32, 0 },
|
|
|
|
0 };
|
|
|
|
}
|
|
|
|
|
2018-02-01 18:07:24 +01:00
|
|
|
this->emitInit();
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-10-16 17:50:09 +02:00
|
|
|
|
|
|
|
|
2017-12-14 12:53:53 +01:00
|
|
|
DxbcCompiler::~DxbcCompiler() {
|
2017-10-16 17:50:09 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::processInstruction(const DxbcShaderInstruction& ins) {
|
2017-12-18 11:53:28 +01:00
|
|
|
switch (ins.opClass) {
|
|
|
|
case DxbcInstClass::Declaration:
|
|
|
|
return this->emitDcl(ins);
|
2017-12-19 17:41:23 +01:00
|
|
|
|
|
|
|
case DxbcInstClass::CustomData:
|
|
|
|
return this->emitCustomData(ins);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcInstClass::Atomic:
|
|
|
|
return this->emitAtomic(ins);
|
|
|
|
|
2018-01-11 17:11:51 +01:00
|
|
|
case DxbcInstClass::AtomicCounter:
|
|
|
|
return this->emitAtomicCounter(ins);
|
|
|
|
|
2017-12-29 00:51:31 +01:00
|
|
|
case DxbcInstClass::Barrier:
|
|
|
|
return this->emitBarrier(ins);
|
|
|
|
|
2017-12-30 03:44:19 +01:00
|
|
|
case DxbcInstClass::BitExtract:
|
|
|
|
return this->emitBitExtract(ins);
|
|
|
|
|
|
|
|
case DxbcInstClass::BitInsert:
|
|
|
|
return this->emitBitInsert(ins);
|
|
|
|
|
2018-05-12 01:39:23 +02:00
|
|
|
case DxbcInstClass::BitScan:
|
|
|
|
return this->emitBitScan(ins);
|
|
|
|
|
2017-12-30 01:26:37 +01:00
|
|
|
case DxbcInstClass::BufferQuery:
|
|
|
|
return this->emitBufferQuery(ins);
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcInstClass::BufferLoad:
|
|
|
|
return this->emitBufferLoad(ins);
|
|
|
|
|
|
|
|
case DxbcInstClass::BufferStore:
|
|
|
|
return this->emitBufferStore(ins);
|
|
|
|
|
2017-12-30 13:18:31 +01:00
|
|
|
case DxbcInstClass::ConvertFloat16:
|
|
|
|
return this->emitConvertFloat16(ins);
|
|
|
|
|
2018-06-07 14:32:56 +02:00
|
|
|
case DxbcInstClass::ConvertFloat64:
|
|
|
|
return this->emitConvertFloat64(ins);
|
|
|
|
|
2017-12-30 13:18:31 +01:00
|
|
|
case DxbcInstClass::ControlFlow:
|
|
|
|
return this->emitControlFlow(ins);
|
|
|
|
|
|
|
|
case DxbcInstClass::GeometryEmit:
|
|
|
|
return this->emitGeometryEmit(ins);
|
2018-02-08 10:17:59 +01:00
|
|
|
|
2018-03-01 09:26:17 +01:00
|
|
|
case DxbcInstClass::HullShaderPhase:
|
|
|
|
return this->emitHullShaderPhase(ins);
|
|
|
|
|
2018-03-01 12:08:06 +01:00
|
|
|
case DxbcInstClass::HullShaderInstCnt:
|
|
|
|
return this->emitHullShaderInstCnt(ins);
|
|
|
|
|
2018-02-26 16:46:34 +01:00
|
|
|
case DxbcInstClass::Interpolate:
|
|
|
|
return this->emitInterpolate(ins);
|
|
|
|
|
2018-04-11 01:49:39 +02:00
|
|
|
case DxbcInstClass::NoOperation:
|
|
|
|
return;
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
case DxbcInstClass::TextureQuery:
|
|
|
|
return this->emitTextureQuery(ins);
|
|
|
|
|
2018-02-04 22:41:23 +01:00
|
|
|
case DxbcInstClass::TextureQueryLod:
|
|
|
|
return this->emitTextureQueryLod(ins);
|
|
|
|
|
2018-02-04 19:30:39 +01:00
|
|
|
case DxbcInstClass::TextureQueryMs:
|
|
|
|
return this->emitTextureQueryMs(ins);
|
|
|
|
|
2018-03-12 12:25:10 +01:00
|
|
|
case DxbcInstClass::TextureQueryMsPos:
|
|
|
|
return this->emitTextureQueryMsPos(ins);
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
case DxbcInstClass::TextureFetch:
|
|
|
|
return this->emitTextureFetch(ins);
|
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
case DxbcInstClass::TextureGather:
|
|
|
|
return this->emitTextureGather(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::TextureSample:
|
2017-12-27 01:37:15 +01:00
|
|
|
return this->emitTextureSample(ins);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
case DxbcInstClass::TypedUavLoad:
|
|
|
|
return this->emitTypedUavLoad(ins);
|
|
|
|
|
|
|
|
case DxbcInstClass::TypedUavStore:
|
|
|
|
return this->emitTypedUavStore(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorAlu:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorAlu(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
case DxbcInstClass::VectorCmov:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorCmov(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorCmp:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorCmp(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2017-12-19 20:26:05 +01:00
|
|
|
case DxbcInstClass::VectorDeriv:
|
|
|
|
return this->emitVectorDeriv(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorDot:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorDot(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcInstClass::VectorIdiv:
|
|
|
|
return this->emitVectorIdiv(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorImul:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorImul(ins);
|
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
case DxbcInstClass::VectorShift:
|
|
|
|
return this->emitVectorShift(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorSinCos:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorSinCos(ins);
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(
|
2017-12-18 11:53:28 +01:00
|
|
|
str::format("DxbcCompiler: Unhandled opcode class: ",
|
2017-12-18 00:46:44 +01:00
|
|
|
ins.op));
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
}
|
2018-07-25 22:45:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::processXfbPassthrough() {
|
|
|
|
m_module.setExecutionMode (m_entryPointId, spv::ExecutionModeInputPoints);
|
|
|
|
m_module.setExecutionMode (m_entryPointId, spv::ExecutionModeOutputPoints);
|
|
|
|
m_module.setOutputVertices(m_entryPointId, 1);
|
|
|
|
m_module.setInvocations (m_entryPointId, 1);
|
|
|
|
|
|
|
|
for (auto e = m_isgn->begin(); e != m_isgn->end(); e++) {
|
|
|
|
emitDclInput(e->registerId, 1,
|
|
|
|
e->componentMask, e->systemValue,
|
|
|
|
DxbcInterpolationMode::Undefined);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out which streams to enable
|
|
|
|
uint32_t streamMask = 0;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < m_xfbVars.size(); i++)
|
|
|
|
streamMask |= 1u << m_xfbVars[i].streamId;
|
|
|
|
|
|
|
|
for (uint32_t mask = streamMask; mask != 0; mask &= mask - 1) {
|
|
|
|
const uint32_t streamId = bit::tzcnt(mask);
|
|
|
|
|
|
|
|
emitXfbOutputSetup(streamId, true);
|
|
|
|
m_module.opEmitVertex(m_module.constu32(streamId));
|
|
|
|
}
|
|
|
|
|
|
|
|
// End the main function
|
|
|
|
emitFunctionEnd();
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
|
2017-12-14 12:53:53 +01:00
|
|
|
Rc<DxvkShader> DxbcCompiler::finalize() {
|
2017-12-18 00:46:44 +01:00
|
|
|
// Depending on the shader type, this will prepare
|
|
|
|
// input registers, call various shader functions
|
|
|
|
// and write back the output registers.
|
2018-10-08 09:34:56 +02:00
|
|
|
switch (m_programInfo.type()) {
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcProgramType::VertexShader: this->emitVsFinalize(); break;
|
2018-03-01 07:00:54 +01:00
|
|
|
case DxbcProgramType::HullShader: this->emitHsFinalize(); break;
|
|
|
|
case DxbcProgramType::DomainShader: this->emitDsFinalize(); break;
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcProgramType::GeometryShader: this->emitGsFinalize(); break;
|
|
|
|
case DxbcProgramType::PixelShader: this->emitPsFinalize(); break;
|
2017-12-21 17:27:40 +01:00
|
|
|
case DxbcProgramType::ComputeShader: this->emitCsFinalize(); break;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Declare the entry point, we now have all the
|
|
|
|
// information we need, including the interfaces
|
|
|
|
m_module.addEntryPoint(m_entryPointId,
|
2018-10-08 09:34:56 +02:00
|
|
|
m_programInfo.executionModel(), "main",
|
2017-12-13 15:32:54 +01:00
|
|
|
m_entryPointInterfaces.size(),
|
|
|
|
m_entryPointInterfaces.data());
|
|
|
|
m_module.setDebugName(m_entryPointId, "main");
|
2018-06-23 20:19:46 +02:00
|
|
|
|
|
|
|
DxvkShaderOptions shaderOptions = { };
|
|
|
|
|
|
|
|
if (m_moduleInfo.xfb != nullptr)
|
|
|
|
shaderOptions.rasterizedStream = m_moduleInfo.xfb->rasterizedStream;
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Create the shader module object
|
|
|
|
return new DxvkShader(
|
2018-10-08 09:34:56 +02:00
|
|
|
m_programInfo.shaderStage(),
|
2017-12-13 15:32:54 +01:00
|
|
|
m_resourceSlots.size(),
|
|
|
|
m_resourceSlots.data(),
|
2018-01-12 14:25:26 +01:00
|
|
|
m_interfaceSlots,
|
2018-07-30 20:15:19 +02:00
|
|
|
m_module.compile(),
|
2018-06-23 20:19:46 +02:00
|
|
|
shaderOptions,
|
2018-07-30 20:52:42 +02:00
|
|
|
std::move(m_immConstData));
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
void DxbcCompiler::emitDcl(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::DclGlobalFlags:
|
|
|
|
return this->emitDclGlobalFlags(ins);
|
|
|
|
|
2018-02-08 10:17:59 +01:00
|
|
|
case DxbcOpcode::DclIndexRange:
|
|
|
|
return; // not needed for anything
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcOpcode::DclTemps:
|
|
|
|
return this->emitDclTemps(ins);
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
case DxbcOpcode::DclIndexableTemp:
|
|
|
|
return this->emitDclIndexableTemp(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcOpcode::DclInput:
|
|
|
|
case DxbcOpcode::DclInputSgv:
|
|
|
|
case DxbcOpcode::DclInputSiv:
|
|
|
|
case DxbcOpcode::DclInputPs:
|
|
|
|
case DxbcOpcode::DclInputPsSgv:
|
|
|
|
case DxbcOpcode::DclInputPsSiv:
|
|
|
|
case DxbcOpcode::DclOutput:
|
|
|
|
case DxbcOpcode::DclOutputSgv:
|
|
|
|
case DxbcOpcode::DclOutputSiv:
|
|
|
|
return this->emitDclInterfaceReg(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclConstantBuffer:
|
|
|
|
return this->emitDclConstantBuffer(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclSampler:
|
|
|
|
return this->emitDclSampler(ins);
|
2018-02-04 22:59:15 +01:00
|
|
|
|
|
|
|
case DxbcOpcode::DclStream:
|
|
|
|
return this->emitDclStream(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
case DxbcOpcode::DclUavTyped:
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcOpcode::DclResource:
|
2017-12-28 16:03:17 +01:00
|
|
|
return this->emitDclResourceTyped(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclUavRaw:
|
|
|
|
case DxbcOpcode::DclResourceRaw:
|
|
|
|
case DxbcOpcode::DclUavStructured:
|
|
|
|
case DxbcOpcode::DclResourceStructured:
|
|
|
|
return this->emitDclResourceRawStructured(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcOpcode::DclThreadGroupSharedMemoryRaw:
|
|
|
|
case DxbcOpcode::DclThreadGroupSharedMemoryStructured:
|
|
|
|
return this->emitDclThreadGroupSharedMemory(ins);
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcOpcode::DclGsInputPrimitive:
|
|
|
|
return this->emitDclGsInputPrimitive(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclGsOutputPrimitiveTopology:
|
|
|
|
return this->emitDclGsOutputTopology(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclMaxOutputVertexCount:
|
|
|
|
return this->emitDclMaxOutputVertexCount(ins);
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2018-03-01 12:47:24 +01:00
|
|
|
case DxbcOpcode::DclInputControlPointCount:
|
|
|
|
return this->emitDclInputControlPointCount(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclOutputControlPointCount:
|
|
|
|
return this->emitDclOutputControlPointCount(ins);
|
|
|
|
|
2018-03-10 15:02:27 +01:00
|
|
|
case DxbcOpcode::DclHsMaxTessFactor:
|
|
|
|
return this->emitDclHsMaxTessFactor(ins);
|
|
|
|
|
2018-03-01 14:36:17 +01:00
|
|
|
case DxbcOpcode::DclTessDomain:
|
|
|
|
return this->emitDclTessDomain(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclTessPartitioning:
|
|
|
|
return this->emitDclTessPartitioning(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclTessOutputPrimitive:
|
|
|
|
return this->emitDclTessOutputPrimitive(ins);
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcOpcode::DclThreadGroup:
|
|
|
|
return this->emitDclThreadGroup(ins);
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2018-05-22 19:36:53 +02:00
|
|
|
case DxbcOpcode::DclGsInstanceCount:
|
|
|
|
return this->emitDclGsInstanceCount(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
default:
|
|
|
|
Logger::warn(
|
|
|
|
str::format("DxbcCompiler: Unhandled opcode: ",
|
|
|
|
ins.op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclGlobalFlags(const DxbcShaderInstruction& ins) {
|
2018-04-10 08:01:54 +02:00
|
|
|
const DxbcGlobalFlags flags = ins.controls.globalFlags();
|
2018-02-08 10:26:46 +01:00
|
|
|
|
|
|
|
if (flags.test(DxbcGlobalFlag::EarlyFragmentTests))
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeEarlyFragmentTests);
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclTemps(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_temps has one operand:
|
|
|
|
// (imm0) Number of temp registers
|
|
|
|
const uint32_t oldCount = m_rRegs.size();
|
|
|
|
const uint32_t newCount = ins.imm[0].u32;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
if (newCount > oldCount) {
|
|
|
|
m_rRegs.resize(newCount);
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterInfo info;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = 4;
|
|
|
|
info.type.alength = 0;
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
for (uint32_t i = oldCount; i < newCount; i++) {
|
|
|
|
const uint32_t varId = this->emitNewVariable(info);
|
|
|
|
m_module.setDebugName(varId, str::format("r", i).c_str());
|
|
|
|
m_rRegs.at(i) = varId;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
void DxbcCompiler::emitDclIndexableTemp(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_indexable_temps has three operands:
|
|
|
|
// (imm0) Array register index (x#)
|
|
|
|
// (imm1) Number of vectors stored in the array
|
|
|
|
// (imm2) Component count of each individual vector
|
|
|
|
DxbcRegisterInfo info;
|
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = ins.imm[2].u32;
|
|
|
|
info.type.alength = ins.imm[1].u32;
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
|
|
|
|
|
|
|
const uint32_t regId = ins.imm[0].u32;
|
|
|
|
|
|
|
|
if (regId >= m_xRegs.size())
|
|
|
|
m_xRegs.resize(regId + 1);
|
|
|
|
|
|
|
|
m_xRegs.at(regId).ccount = info.type.ccount;
|
|
|
|
m_xRegs.at(regId).varId = emitNewVariable(info);
|
2018-01-05 16:46:17 +01:00
|
|
|
|
|
|
|
m_module.setDebugName(m_xRegs.at(regId).varId,
|
|
|
|
str::format("x", regId).c_str());
|
2017-12-20 22:50:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclInterfaceReg(const DxbcShaderInstruction& ins) {
|
2017-12-19 12:58:40 +01:00
|
|
|
switch (ins.dst[0].type) {
|
2018-09-13 18:25:32 +02:00
|
|
|
case DxbcOperandType::InputControlPoint:
|
2018-10-08 09:34:56 +02:00
|
|
|
if (m_programInfo.type() != DxbcProgramType::HullShader)
|
2018-09-13 18:25:32 +02:00
|
|
|
break;
|
|
|
|
/* fall through */
|
|
|
|
|
2017-12-19 12:58:40 +01:00
|
|
|
case DxbcOperandType::Input:
|
|
|
|
case DxbcOperandType::Output: {
|
|
|
|
// dcl_input and dcl_output instructions
|
|
|
|
// have the following operands:
|
|
|
|
// (dst0) The register to declare
|
|
|
|
// (imm0) The system value (optional)
|
|
|
|
uint32_t regDim = 0;
|
|
|
|
uint32_t regIdx = 0;
|
|
|
|
|
|
|
|
// In the vertex and fragment shader stage, the
|
|
|
|
// operand indices will have the following format:
|
|
|
|
// (0) Register index
|
|
|
|
//
|
|
|
|
// In other stages, the input and output registers
|
|
|
|
// may be declared as arrays of a fixed size:
|
|
|
|
// (0) Array length
|
|
|
|
// (1) Register index
|
|
|
|
if (ins.dst[0].idxDim == 2) {
|
|
|
|
regDim = ins.dst[0].idx[0].offset;
|
|
|
|
regIdx = ins.dst[0].idx[1].offset;
|
|
|
|
} else if (ins.dst[0].idxDim == 1) {
|
|
|
|
regIdx = ins.dst[0].idx[0].offset;
|
|
|
|
} else {
|
|
|
|
Logger::err(str::format(
|
|
|
|
"DxbcCompiler: ", ins.op,
|
|
|
|
": Invalid index dimension"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This declaration may map an output register to a system
|
|
|
|
// value. If that is the case, the system value type will
|
|
|
|
// be stored in the second operand.
|
|
|
|
const bool hasSv =
|
|
|
|
ins.op == DxbcOpcode::DclInputSgv
|
2018-03-05 16:14:46 +01:00
|
|
|
|| ins.op == DxbcOpcode::DclInputSiv
|
|
|
|
|| ins.op == DxbcOpcode::DclInputPsSgv
|
|
|
|
|| ins.op == DxbcOpcode::DclInputPsSiv
|
|
|
|
|| ins.op == DxbcOpcode::DclOutputSgv
|
|
|
|
|| ins.op == DxbcOpcode::DclOutputSiv;
|
2017-12-19 12:58:40 +01:00
|
|
|
|
|
|
|
DxbcSystemValue sv = DxbcSystemValue::None;
|
|
|
|
|
|
|
|
if (hasSv)
|
|
|
|
sv = static_cast<DxbcSystemValue>(ins.imm[0].u32);
|
|
|
|
|
|
|
|
// In the pixel shader, inputs are declared with an
|
|
|
|
// interpolation mode that is part of the op token.
|
|
|
|
const bool hasInterpolationMode =
|
|
|
|
ins.op == DxbcOpcode::DclInputPs
|
2018-03-05 16:14:46 +01:00
|
|
|
|| ins.op == DxbcOpcode::DclInputPsSiv;
|
2017-12-19 12:58:40 +01:00
|
|
|
|
|
|
|
DxbcInterpolationMode im = DxbcInterpolationMode::Undefined;
|
|
|
|
|
|
|
|
if (hasInterpolationMode)
|
2018-04-10 08:01:54 +02:00
|
|
|
im = ins.controls.interpolation();
|
2017-12-19 12:58:40 +01:00
|
|
|
|
|
|
|
// Declare the actual input/output variable
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::DclInput:
|
|
|
|
case DxbcOpcode::DclInputSgv:
|
|
|
|
case DxbcOpcode::DclInputSiv:
|
|
|
|
case DxbcOpcode::DclInputPs:
|
|
|
|
case DxbcOpcode::DclInputPsSgv:
|
|
|
|
case DxbcOpcode::DclInputPsSiv:
|
|
|
|
this->emitDclInput(regIdx, regDim, ins.dst[0].mask, sv, im);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DclOutput:
|
|
|
|
case DxbcOpcode::DclOutputSgv:
|
|
|
|
case DxbcOpcode::DclOutputSiv:
|
|
|
|
this->emitDclOutput(regIdx, regDim, ins.dst[0].mask, sv, im);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::err(str::format(
|
|
|
|
"DxbcCompiler: Unexpected opcode: ",
|
|
|
|
ins.op));
|
|
|
|
}
|
|
|
|
} break;
|
2017-12-29 00:51:31 +01:00
|
|
|
|
|
|
|
case DxbcOperandType::InputThreadId: {
|
|
|
|
m_cs.builtinGlobalInvocationId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInGlobalInvocationId,
|
|
|
|
"vThreadId");
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOperandType::InputThreadGroupId: {
|
|
|
|
m_cs.builtinWorkgroupId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInWorkgroupId,
|
|
|
|
"vThreadGroupId");
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOperandType::InputThreadIdInGroup: {
|
|
|
|
m_cs.builtinLocalInvocationId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInLocalInvocationId,
|
|
|
|
"vThreadIdInGroup");
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOperandType::InputThreadIndexInGroup: {
|
|
|
|
m_cs.builtinLocalInvocationIndex = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInLocalInvocationIndex,
|
|
|
|
"vThreadIndexInGroup");
|
|
|
|
} break;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2018-02-26 14:23:41 +01:00
|
|
|
case DxbcOperandType::InputCoverageMask: {
|
|
|
|
m_ps.builtinSampleMaskIn = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 1 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInSampleMask,
|
|
|
|
"vCoverage");
|
|
|
|
} break;
|
|
|
|
|
2018-01-29 10:54:36 +01:00
|
|
|
case DxbcOperandType::OutputCoverageMask: {
|
|
|
|
m_ps.builtinSampleMaskOut = emitNewBuiltinVariable({
|
2018-02-26 14:23:41 +01:00
|
|
|
{ DxbcScalarType::Uint32, 1, 1 },
|
2018-01-29 10:54:36 +01:00
|
|
|
spv::StorageClassOutput },
|
|
|
|
spv::BuiltInSampleMask,
|
2018-02-26 14:23:41 +01:00
|
|
|
"oMask");
|
2018-01-29 10:54:36 +01:00
|
|
|
} break;
|
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
case DxbcOperandType::OutputDepth: {
|
|
|
|
m_module.setExecutionMode(m_entryPointId,
|
|
|
|
spv::ExecutionModeDepthReplacing);
|
|
|
|
m_ps.builtinDepth = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Float32, 1, 0 },
|
|
|
|
spv::StorageClassOutput },
|
|
|
|
spv::BuiltInFragDepth,
|
|
|
|
"oDepth");
|
|
|
|
} break;
|
|
|
|
|
2018-02-08 10:28:27 +01:00
|
|
|
case DxbcOperandType::OutputDepthGe: {
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeDepthReplacing);
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeDepthGreater);
|
|
|
|
m_ps.builtinDepth = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Float32, 1, 0 },
|
|
|
|
spv::StorageClassOutput },
|
|
|
|
spv::BuiltInFragDepth,
|
|
|
|
"oDepthGe");
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOperandType::OutputDepthLe: {
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeDepthReplacing);
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeDepthLess);
|
|
|
|
m_ps.builtinDepth = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Float32, 1, 0 },
|
|
|
|
spv::StorageClassOutput },
|
|
|
|
spv::BuiltInFragDepth,
|
|
|
|
"oDepthLe");
|
|
|
|
} break;
|
|
|
|
|
2018-04-08 18:25:44 +02:00
|
|
|
case DxbcOperandType::InputPrimitiveId: {
|
|
|
|
m_primitiveIdIn = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInPrimitiveId,
|
|
|
|
"vPrim");
|
|
|
|
} break;
|
|
|
|
|
2018-03-06 16:47:35 +01:00
|
|
|
case DxbcOperandType::InputDomainPoint: {
|
|
|
|
m_ds.builtinTessCoord = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Float32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInTessCoord,
|
|
|
|
"vDomain");
|
|
|
|
} break;
|
|
|
|
|
2018-03-01 09:26:17 +01:00
|
|
|
case DxbcOperandType::InputForkInstanceId:
|
|
|
|
case DxbcOperandType::InputJoinInstanceId: {
|
2018-03-06 18:29:20 +01:00
|
|
|
auto phase = this->getCurrentHsForkJoinPhase();
|
|
|
|
|
|
|
|
phase->instanceIdPtr = m_module.newVar(
|
|
|
|
m_module.defPointerType(
|
|
|
|
m_module.defIntType(32, 0),
|
|
|
|
spv::StorageClassFunction),
|
|
|
|
spv::StorageClassFunction);
|
|
|
|
|
|
|
|
m_module.opStore(phase->instanceIdPtr, phase->instanceId);
|
|
|
|
m_module.setDebugName(phase->instanceIdPtr,
|
|
|
|
ins.dst[0].type == DxbcOperandType::InputForkInstanceId
|
|
|
|
? "vForkInstanceId" : "vJoinInstanceId");
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOperandType::OutputControlPointId: {
|
|
|
|
// This system value map to the invocation
|
|
|
|
// ID, which has been declared already.
|
2018-03-01 09:26:17 +01:00
|
|
|
} break;
|
|
|
|
|
2018-03-06 16:47:35 +01:00
|
|
|
case DxbcOperandType::InputPatchConstant:
|
|
|
|
case DxbcOperandType::OutputControlPoint: {
|
|
|
|
// These have been declared as global input and
|
|
|
|
// output arrays, so there's nothing left to do.
|
|
|
|
} break;
|
|
|
|
|
2018-05-22 19:36:53 +02:00
|
|
|
case DxbcOperandType::InputGsInstanceId: {
|
|
|
|
m_gs.builtinInvocationId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInInvocationId,
|
|
|
|
"vInstanceID");
|
|
|
|
} break;
|
|
|
|
|
2017-11-13 00:22:52 +01:00
|
|
|
default:
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::err(str::format(
|
2017-12-19 12:58:40 +01:00
|
|
|
"DxbcCompiler: Unsupported operand type declaration: ",
|
|
|
|
ins.dst[0].type));
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclInput(
|
|
|
|
uint32_t regIdx,
|
|
|
|
uint32_t regDim,
|
|
|
|
DxbcRegMask regMask,
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcInterpolationMode im) {
|
|
|
|
// Avoid declaring the same variable multiple times.
|
|
|
|
// This may happen when multiple system values are
|
|
|
|
// mapped to different parts of the same register.
|
2018-07-01 12:44:37 +02:00
|
|
|
if (m_vRegs.at(regIdx).id == 0 && sv == DxbcSystemValue::None) {
|
2018-01-01 23:31:01 +01:00
|
|
|
const DxbcVectorType regType = getInputRegType(regIdx);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterInfo info;
|
2018-01-01 23:31:01 +01:00
|
|
|
info.type.ctype = regType.ctype;
|
|
|
|
info.type.ccount = regType.ccount;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.alength = regDim;
|
2017-12-18 00:46:44 +01:00
|
|
|
info.sclass = spv::StorageClassInput;
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
const uint32_t varId = emitNewVariable(info);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
m_module.decorateLocation(varId, regIdx);
|
|
|
|
m_module.setDebugName(varId, str::format("v", regIdx).c_str());
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
|
|
|
|
2018-07-01 12:44:37 +02:00
|
|
|
m_vRegs.at(regIdx) = { regType, varId };
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
// Interpolation mode, used in pixel shaders
|
|
|
|
if (im == DxbcInterpolationMode::Constant)
|
|
|
|
m_module.decorate(varId, spv::DecorationFlat);
|
|
|
|
|
|
|
|
if (im == DxbcInterpolationMode::LinearCentroid
|
|
|
|
|| im == DxbcInterpolationMode::LinearNoPerspectiveCentroid)
|
|
|
|
m_module.decorate(varId, spv::DecorationCentroid);
|
|
|
|
|
|
|
|
if (im == DxbcInterpolationMode::LinearNoPerspective
|
|
|
|
|| im == DxbcInterpolationMode::LinearNoPerspectiveCentroid
|
|
|
|
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample)
|
|
|
|
m_module.decorate(varId, spv::DecorationNoPerspective);
|
|
|
|
|
|
|
|
if (im == DxbcInterpolationMode::LinearSample
|
2018-04-08 21:06:58 +02:00
|
|
|
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample) {
|
|
|
|
m_module.enableCapability(spv::CapabilitySampleRateShading);
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.decorate(varId, spv::DecorationSample);
|
2018-04-08 21:06:58 +02:00
|
|
|
}
|
2018-01-12 14:25:26 +01:00
|
|
|
|
|
|
|
// Declare the input slot as defined
|
|
|
|
m_interfaceSlots.inputSlots |= 1u << regIdx;
|
2017-12-21 12:37:20 +01:00
|
|
|
} else if (sv != DxbcSystemValue::None) {
|
|
|
|
// Add a new system value mapping if needed
|
2018-03-24 11:29:07 +01:00
|
|
|
bool skipSv = sv == DxbcSystemValue::ClipDistance
|
2018-03-24 00:32:22 +01:00
|
|
|
|| sv == DxbcSystemValue::CullDistance;
|
|
|
|
|
2018-03-24 11:29:07 +01:00
|
|
|
if (!skipSv)
|
2018-03-24 00:32:22 +01:00
|
|
|
m_vMappings.push_back({ regIdx, regMask, sv });
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
2017-10-16 17:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclOutput(
|
|
|
|
uint32_t regIdx,
|
|
|
|
uint32_t regDim,
|
|
|
|
DxbcRegMask regMask,
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcInterpolationMode im) {
|
2018-03-24 11:29:07 +01:00
|
|
|
// Add a new system value mapping if needed. Clip
|
|
|
|
// and cull distances are handled separately.
|
|
|
|
if (sv != DxbcSystemValue::None
|
|
|
|
&& sv != DxbcSystemValue::ClipDistance
|
|
|
|
&& sv != DxbcSystemValue::CullDistance)
|
2018-03-06 14:00:03 +01:00
|
|
|
m_oMappings.push_back({ regIdx, regMask, sv });
|
|
|
|
|
2018-10-08 09:34:56 +02:00
|
|
|
if (m_programInfo.type() == DxbcProgramType::HullShader) {
|
2018-09-13 14:54:54 +02:00
|
|
|
// Hull shaders don't use standard outputs
|
|
|
|
if (getCurrentHsForkJoinPhase() != nullptr)
|
|
|
|
m_hs.outputPerPatchMask |= 1 << regIdx;
|
|
|
|
} else if (m_oRegs.at(regIdx).id == 0) {
|
|
|
|
// Avoid declaring the same variable multiple times.
|
|
|
|
// This may happen when multiple system values are
|
|
|
|
// mapped to different parts of the same register.
|
2018-04-18 21:14:34 +02:00
|
|
|
const DxbcVectorType regType = getOutputRegType(regIdx);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterInfo info;
|
2018-04-18 21:14:34 +02:00
|
|
|
info.type.ctype = regType.ctype;
|
|
|
|
info.type.ccount = regType.ccount;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.alength = regDim;
|
2017-12-18 00:46:44 +01:00
|
|
|
info.sclass = spv::StorageClassOutput;
|
2018-06-24 01:07:48 +02:00
|
|
|
|
|
|
|
// In xfb mode, we set up the actual
|
|
|
|
// output vars when emitting a vertex
|
|
|
|
if (m_moduleInfo.xfb != nullptr)
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
const uint32_t varId = this->emitNewVariable(info);
|
|
|
|
m_module.setDebugName(varId, str::format("o", regIdx).c_str());
|
2018-06-24 01:07:48 +02:00
|
|
|
|
|
|
|
if (info.sclass == spv::StorageClassOutput) {
|
|
|
|
m_module.decorateLocation(varId, regIdx);
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2018-07-01 12:44:37 +02:00
|
|
|
m_oRegs.at(regIdx) = { regType, varId };
|
2018-01-12 14:25:26 +01:00
|
|
|
|
|
|
|
// Declare the output slot as defined
|
|
|
|
m_interfaceSlots.outputSlots |= 1u << regIdx;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclConstantBuffer(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_constant_buffer has one operand with two indices:
|
|
|
|
// (0) Constant buffer register ID (cb#)
|
|
|
|
// (1) Number of constants in the buffer
|
|
|
|
const uint32_t bufferId = ins.dst[0].idx[0].offset;
|
|
|
|
const uint32_t elementCount = ins.dst[0].idx[1].offset;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2018-07-30 22:08:01 +02:00
|
|
|
this->emitDclConstantBufferVar(bufferId, elementCount,
|
|
|
|
str::format("cb", bufferId).c_str());
|
2018-07-30 20:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclConstantBufferVar(
|
|
|
|
uint32_t regIdx,
|
2018-07-30 22:08:01 +02:00
|
|
|
uint32_t numConstants,
|
|
|
|
const char* name) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// Uniform buffer data is stored as a fixed-size array
|
|
|
|
// of 4x32-bit vectors. SPIR-V requires explicit strides.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t arrayType = m_module.defArrayTypeUnique(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Float32, 4 }),
|
2018-07-30 20:52:42 +02:00
|
|
|
m_module.constu32(numConstants));
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.decorateArrayStride(arrayType, 16);
|
|
|
|
|
|
|
|
// SPIR-V requires us to put that array into a
|
|
|
|
// struct and decorate that struct as a block.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType);
|
2018-01-02 20:20:52 +01:00
|
|
|
|
|
|
|
m_module.decorateBlock (structType);
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.memberDecorateOffset(structType, 0, 0);
|
2018-01-02 20:20:52 +01:00
|
|
|
|
2018-07-30 22:08:01 +02:00
|
|
|
m_module.setDebugName (structType, str::format("struct_", name).c_str());
|
2018-01-02 20:20:52 +01:00
|
|
|
m_module.setDebugMemberName (structType, 0, "m");
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Variable that we'll use to access the buffer
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t varId = m_module.newVar(
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.defPointerType(structType, spv::StorageClassUniform),
|
|
|
|
spv::StorageClassUniform);
|
|
|
|
|
2018-07-30 22:08:01 +02:00
|
|
|
m_module.setDebugName(varId, name);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Compute the DXVK binding slot index for the buffer.
|
|
|
|
// D3D11 needs to bind the actual buffers to this slot.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
2018-10-08 09:34:56 +02:00
|
|
|
m_programInfo.type(), DxbcBindingType::ConstantBuffer,
|
2018-07-30 20:52:42 +02:00
|
|
|
regIdx);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
m_module.decorateDescriptorSet(varId, 0);
|
|
|
|
m_module.decorateBinding(varId, bindingId);
|
|
|
|
|
2018-01-10 13:44:04 +01:00
|
|
|
// Declare a specialization constant which will
|
|
|
|
// store whether or not the resource is bound.
|
|
|
|
const uint32_t specConstId = m_module.specConstBool(true);
|
|
|
|
m_module.decorateSpecId(specConstId, bindingId);
|
|
|
|
m_module.setDebugName(specConstId,
|
2018-07-30 22:08:01 +02:00
|
|
|
str::format(name, "_bound").c_str());
|
2018-01-10 13:44:04 +01:00
|
|
|
|
|
|
|
DxbcConstantBuffer buf;
|
|
|
|
buf.varId = varId;
|
|
|
|
buf.specId = specConstId;
|
2018-07-30 20:52:42 +02:00
|
|
|
buf.size = numConstants;
|
|
|
|
m_constantBuffers.at(regIdx) = buf;
|
2018-01-10 13:44:04 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Store descriptor info for the shader interface
|
|
|
|
DxvkResourceSlot resource;
|
|
|
|
resource.slot = bindingId;
|
|
|
|
resource.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
2018-01-09 20:35:29 +01:00
|
|
|
resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
2017-12-13 15:32:54 +01:00
|
|
|
m_resourceSlots.push_back(resource);
|
2017-12-08 17:08:26 +01:00
|
|
|
}
|
2018-07-30 20:52:42 +02:00
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclSampler(const DxbcShaderInstruction& ins) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// dclSampler takes one operand:
|
2017-12-18 00:46:44 +01:00
|
|
|
// (dst0) The sampler register to declare
|
|
|
|
const uint32_t samplerId = ins.dst[0].idx[0].offset;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// The sampler type is opaque, but we still have to
|
|
|
|
// define a pointer and a variable in oder to use it
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t samplerType = m_module.defSamplerType();
|
|
|
|
const uint32_t samplerPtrType = m_module.defPointerType(
|
2017-12-13 15:32:54 +01:00
|
|
|
samplerType, spv::StorageClassUniformConstant);
|
2017-11-13 00:22:52 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Define the sampler variable
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t varId = m_module.newVar(samplerPtrType,
|
2017-12-13 15:32:54 +01:00
|
|
|
spv::StorageClassUniformConstant);
|
|
|
|
m_module.setDebugName(varId,
|
|
|
|
str::format("s", samplerId).c_str());
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
m_samplers.at(samplerId).varId = varId;
|
|
|
|
m_samplers.at(samplerId).typeId = samplerType;
|
|
|
|
|
|
|
|
// Compute binding slot index for the sampler
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
2018-10-08 09:34:56 +02:00
|
|
|
m_programInfo.type(), DxbcBindingType::ImageSampler, samplerId);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
m_module.decorateDescriptorSet(varId, 0);
|
|
|
|
m_module.decorateBinding(varId, bindingId);
|
|
|
|
|
|
|
|
// Store descriptor info for the shader interface
|
|
|
|
DxvkResourceSlot resource;
|
|
|
|
resource.slot = bindingId;
|
|
|
|
resource.type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
2018-01-09 20:35:29 +01:00
|
|
|
resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
2017-12-13 15:32:54 +01:00
|
|
|
m_resourceSlots.push_back(resource);
|
2017-11-13 00:22:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-04 22:59:15 +01:00
|
|
|
void DxbcCompiler::emitDclStream(const DxbcShaderInstruction& ins) {
|
2018-06-23 23:46:24 +02:00
|
|
|
if (ins.dst[0].idx[0].offset != 0 && m_moduleInfo.xfb == nullptr)
|
2018-02-04 22:59:15 +01:00
|
|
|
Logger::err("Dxbc: Multiple streams not supported");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitDclResourceTyped(const DxbcShaderInstruction& ins) {
|
2017-12-10 10:34:18 +01:00
|
|
|
// dclResource takes two operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The resource register ID
|
|
|
|
// (imm0) The resource return type
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
2017-12-10 10:34:18 +01:00
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
// We also handle unordered access views here
|
|
|
|
const bool isUav = ins.op == DxbcOpcode::DclUavTyped;
|
|
|
|
|
|
|
|
if (isUav) {
|
2018-06-23 17:14:35 +02:00
|
|
|
if (m_moduleInfo.options.test(DxbcOption::UseStorageImageReadWithoutFormat))
|
2018-03-21 12:47:53 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityStorageImageReadWithoutFormat);
|
2018-01-01 17:14:06 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityStorageImageWriteWithoutFormat);
|
|
|
|
}
|
|
|
|
|
2017-12-10 10:34:18 +01:00
|
|
|
// Defines the type of the resource (texture2D, ...)
|
2018-04-10 08:01:54 +02:00
|
|
|
const DxbcResourceDim resourceType = ins.controls.resourceDim();
|
2017-12-10 10:34:18 +01:00
|
|
|
|
|
|
|
// Defines the type of a read operation. DXBC has the ability
|
|
|
|
// to define four different types whereas SPIR-V only allows
|
|
|
|
// one, but in practice this should not be much of a problem.
|
|
|
|
auto xType = static_cast<DxbcResourceReturnType>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 0, 3));
|
2017-12-10 10:34:18 +01:00
|
|
|
auto yType = static_cast<DxbcResourceReturnType>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 4, 7));
|
2017-12-10 10:34:18 +01:00
|
|
|
auto zType = static_cast<DxbcResourceReturnType>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 8, 11));
|
2017-12-10 10:34:18 +01:00
|
|
|
auto wType = static_cast<DxbcResourceReturnType>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 12, 15));
|
2017-12-10 10:34:18 +01:00
|
|
|
|
|
|
|
if ((xType != yType) || (xType != zType) || (xType != wType))
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::warn("DxbcCompiler: dcl_resource: Ignoring resource return types");
|
2017-12-10 10:34:18 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Declare the actual sampled type
|
2017-12-20 13:41:04 +01:00
|
|
|
const DxbcScalarType sampledType = [xType] {
|
|
|
|
switch (xType) {
|
2018-03-24 14:21:13 +01:00
|
|
|
// FIXME is this correct? There's no documentation about it
|
|
|
|
case DxbcResourceReturnType::Mixed: return DxbcScalarType::Uint32;
|
2018-03-21 15:08:53 +01:00
|
|
|
// FIXME do we have to manually clamp writes to SNORM/UNORM resources?
|
|
|
|
case DxbcResourceReturnType::Snorm: return DxbcScalarType::Float32;
|
|
|
|
case DxbcResourceReturnType::Unorm: return DxbcScalarType::Float32;
|
2017-12-20 13:41:04 +01:00
|
|
|
case DxbcResourceReturnType::Float: return DxbcScalarType::Float32;
|
|
|
|
case DxbcResourceReturnType::Sint: return DxbcScalarType::Sint32;
|
|
|
|
case DxbcResourceReturnType::Uint: return DxbcScalarType::Uint32;
|
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Invalid sampled type: ", xType));
|
|
|
|
}
|
|
|
|
}();
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Declare the resource type
|
2018-04-23 00:46:27 +02:00
|
|
|
const uint32_t sampledTypeId = getScalarTypeId(sampledType);
|
|
|
|
const DxbcImageInfo typeInfo = getResourceType(resourceType, isUav);
|
2017-12-20 13:41:04 +01:00
|
|
|
|
2017-12-24 13:33:22 +01:00
|
|
|
// Declare additional capabilities if necessary
|
|
|
|
switch (resourceType) {
|
|
|
|
case DxbcResourceDim::Buffer: m_module.enableCapability(spv::CapabilityImageBuffer); break;
|
|
|
|
case DxbcResourceDim::Texture1D: m_module.enableCapability(spv::CapabilityImage1D); break;
|
|
|
|
case DxbcResourceDim::Texture1DArr: m_module.enableCapability(spv::CapabilityImage1D); break;
|
|
|
|
case DxbcResourceDim::TextureCubeArr: m_module.enableCapability(spv::CapabilityImageCubeArray); break;
|
2018-04-22 18:27:01 +02:00
|
|
|
case DxbcResourceDim::Texture2DMs: m_module.enableCapability(spv::CapabilityImageMSArray); break;
|
2017-12-24 13:33:22 +01:00
|
|
|
case DxbcResourceDim::Texture2DMsArr: m_module.enableCapability(spv::CapabilityImageMSArray); break;
|
|
|
|
default: break; // No additional capabilities required
|
|
|
|
}
|
|
|
|
|
2018-03-23 01:04:04 +01:00
|
|
|
// If the read-without-format capability is not set and this
|
|
|
|
// image is access via a typed load, or if atomic operations
|
|
|
|
// are used,, we must define the image format explicitly.
|
|
|
|
spv::ImageFormat imageFormat = spv::ImageFormatUnknown;
|
|
|
|
|
|
|
|
if (isUav) {
|
|
|
|
if ((m_analysis->uavInfos[registerId].accessAtomicOp)
|
|
|
|
|| (m_analysis->uavInfos[registerId].accessTypedLoad
|
2018-06-23 17:14:35 +02:00
|
|
|
&& !m_moduleInfo.options.test(DxbcOption::UseStorageImageReadWithoutFormat)))
|
2018-03-23 01:04:04 +01:00
|
|
|
imageFormat = getScalarImageFormat(sampledType);
|
|
|
|
}
|
|
|
|
|
2018-01-03 02:37:44 +01:00
|
|
|
// We do not know whether the image is going to be used as
|
|
|
|
// a color image or a depth image yet, but we can pick the
|
|
|
|
// correct type when creating a sampled image object.
|
2018-01-02 16:57:37 +01:00
|
|
|
const uint32_t imageTypeId = m_module.defImageType(sampledTypeId,
|
2018-01-03 02:37:44 +01:00
|
|
|
typeInfo.dim, 0, typeInfo.array, typeInfo.ms, typeInfo.sampled,
|
2018-03-23 01:04:04 +01:00
|
|
|
imageFormat);
|
2017-12-20 13:41:04 +01:00
|
|
|
|
|
|
|
// We'll declare the texture variable with the color type
|
|
|
|
// and decide which one to use when the texture is sampled.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t resourcePtrType = m_module.defPointerType(
|
2018-01-02 16:57:37 +01:00
|
|
|
imageTypeId, spv::StorageClassUniformConstant);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t varId = m_module.newVar(resourcePtrType,
|
2017-12-13 15:32:54 +01:00
|
|
|
spv::StorageClassUniformConstant);
|
|
|
|
|
|
|
|
m_module.setDebugName(varId,
|
2018-01-01 17:14:06 +01:00
|
|
|
str::format(isUav ? "u" : "t", registerId).c_str());
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2018-01-10 13:44:04 +01:00
|
|
|
// Compute the DXVK binding slot index for the resource.
|
|
|
|
// D3D11 needs to bind the actual resource to this slot.
|
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
2018-10-08 09:34:56 +02:00
|
|
|
m_programInfo.type(), isUav
|
2018-01-10 13:44:04 +01:00
|
|
|
? DxbcBindingType::UnorderedAccessView
|
|
|
|
: DxbcBindingType::ShaderResource,
|
|
|
|
registerId);
|
|
|
|
|
|
|
|
m_module.decorateDescriptorSet(varId, 0);
|
|
|
|
m_module.decorateBinding(varId, bindingId);
|
|
|
|
|
2018-04-10 08:01:54 +02:00
|
|
|
if (ins.controls.uavFlags().test(DxbcUavFlag::GloballyCoherent))
|
2018-03-31 16:41:19 +02:00
|
|
|
m_module.decorate(varId, spv::DecorationCoherent);
|
|
|
|
|
2018-06-28 03:42:11 +02:00
|
|
|
// On GPUs which don't support storageImageReadWithoutFormat,
|
|
|
|
// we have to decorate untyped UAVs as write-only
|
|
|
|
if (isUav && imageFormat == spv::ImageFormatUnknown
|
|
|
|
&& !m_moduleInfo.options.test(DxbcOption::UseStorageImageReadWithoutFormat))
|
|
|
|
m_module.decorate(varId, spv::DecorationNonReadable);
|
|
|
|
|
2018-01-10 13:44:04 +01:00
|
|
|
// Declare a specialization constant which will
|
|
|
|
// store whether or not the resource is bound.
|
|
|
|
const uint32_t specConstId = m_module.specConstBool(true);
|
|
|
|
m_module.decorateSpecId(specConstId, bindingId);
|
|
|
|
m_module.setDebugName(specConstId,
|
|
|
|
str::format(isUav ? "u" : "t", registerId, "_bound").c_str());
|
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
if (isUav) {
|
|
|
|
DxbcUav uav;
|
|
|
|
uav.type = DxbcResourceType::Typed;
|
|
|
|
uav.imageInfo = typeInfo;
|
|
|
|
uav.varId = varId;
|
2018-01-11 17:11:51 +01:00
|
|
|
uav.ctrId = 0;
|
2018-01-10 13:44:04 +01:00
|
|
|
uav.specId = specConstId;
|
2018-01-01 17:14:06 +01:00
|
|
|
uav.sampledType = sampledType;
|
|
|
|
uav.sampledTypeId = sampledTypeId;
|
2018-01-02 16:57:37 +01:00
|
|
|
uav.imageTypeId = imageTypeId;
|
2018-01-01 17:14:06 +01:00
|
|
|
uav.structStride = 0;
|
|
|
|
m_uavs.at(registerId) = uav;
|
|
|
|
} else {
|
|
|
|
DxbcShaderResource res;
|
|
|
|
res.type = DxbcResourceType::Typed;
|
|
|
|
res.imageInfo = typeInfo;
|
|
|
|
res.varId = varId;
|
2018-01-10 13:44:04 +01:00
|
|
|
res.specId = specConstId;
|
2018-01-01 17:14:06 +01:00
|
|
|
res.sampledType = sampledType;
|
|
|
|
res.sampledTypeId = sampledTypeId;
|
2018-01-02 16:57:37 +01:00
|
|
|
res.imageTypeId = imageTypeId;
|
2018-01-31 11:25:49 +01:00
|
|
|
res.colorTypeId = imageTypeId;
|
2018-02-02 21:42:04 +01:00
|
|
|
res.depthTypeId = 0;
|
2018-01-01 17:14:06 +01:00
|
|
|
res.structStride = 0;
|
2018-02-02 21:42:04 +01:00
|
|
|
|
2018-02-28 06:51:13 +01:00
|
|
|
if ((sampledType == DxbcScalarType::Float32)
|
|
|
|
&& (resourceType == DxbcResourceDim::Texture2D
|
|
|
|
|| resourceType == DxbcResourceDim::Texture2DArr
|
|
|
|
|| resourceType == DxbcResourceDim::TextureCube
|
|
|
|
|| resourceType == DxbcResourceDim::TextureCubeArr)) {
|
2018-02-02 21:42:04 +01:00
|
|
|
res.depthTypeId = m_module.defImageType(sampledTypeId,
|
|
|
|
typeInfo.dim, 1, typeInfo.array, typeInfo.ms, typeInfo.sampled,
|
|
|
|
spv::ImageFormatUnknown);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_textures.at(registerId) = res;
|
2018-01-01 17:14:06 +01:00
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Store descriptor info for the shader interface
|
|
|
|
DxvkResourceSlot resource;
|
|
|
|
resource.slot = bindingId;
|
2018-04-23 00:46:27 +02:00
|
|
|
resource.view = typeInfo.vtype;
|
2018-01-01 17:14:06 +01:00
|
|
|
|
|
|
|
if (isUav) {
|
|
|
|
resource.type = resourceType == DxbcResourceDim::Buffer
|
|
|
|
? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
|
|
|
|
: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
|
|
} else {
|
|
|
|
resource.type = resourceType == DxbcResourceDim::Buffer
|
|
|
|
? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
|
|
|
|
: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
|
|
}
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
m_resourceSlots.push_back(resource);
|
2017-12-11 14:36:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitDclResourceRawStructured(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_resource_raw and dcl_uav_raw take one argument:
|
|
|
|
// (dst0) The resource register ID
|
|
|
|
// dcl_resource_structured and dcl_uav_structured take two arguments:
|
|
|
|
// (dst0) The resource register ID
|
|
|
|
// (imm0) Structure stride, in bytes
|
|
|
|
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
|
|
|
|
|
|
|
const bool isUav = ins.op == DxbcOpcode::DclUavRaw
|
|
|
|
|| ins.op == DxbcOpcode::DclUavStructured;
|
|
|
|
|
|
|
|
const bool isStructured = ins.op == DxbcOpcode::DclUavStructured
|
|
|
|
|| ins.op == DxbcOpcode::DclResourceStructured;
|
|
|
|
|
|
|
|
// Structured and raw buffers are represented as
|
|
|
|
// texel buffers consisting of 32-bit integers.
|
|
|
|
m_module.enableCapability(spv::CapabilityImageBuffer);
|
|
|
|
|
|
|
|
const DxbcScalarType sampledType = DxbcScalarType::Uint32;
|
|
|
|
const uint32_t sampledTypeId = getScalarTypeId(sampledType);
|
|
|
|
|
2018-05-24 12:07:03 +02:00
|
|
|
const DxbcImageInfo typeInfo = { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_MAX_ENUM };
|
2017-12-28 16:03:17 +01:00
|
|
|
|
|
|
|
// Declare the resource type
|
|
|
|
const uint32_t resTypeId = m_module.defImageType(sampledTypeId,
|
|
|
|
typeInfo.dim, 0, typeInfo.array, typeInfo.ms, typeInfo.sampled,
|
|
|
|
spv::ImageFormatR32ui);
|
|
|
|
|
|
|
|
const uint32_t varId = m_module.newVar(
|
|
|
|
m_module.defPointerType(resTypeId, spv::StorageClassUniformConstant),
|
|
|
|
spv::StorageClassUniformConstant);
|
|
|
|
|
|
|
|
m_module.setDebugName(varId,
|
|
|
|
str::format(isUav ? "u" : "t", registerId).c_str());
|
|
|
|
|
|
|
|
// Write back resource info
|
|
|
|
const DxbcResourceType resType = isStructured
|
|
|
|
? DxbcResourceType::Structured
|
|
|
|
: DxbcResourceType::Raw;
|
|
|
|
|
|
|
|
const uint32_t resStride = isStructured
|
|
|
|
? ins.imm[0].u32
|
|
|
|
: 0;
|
|
|
|
|
2018-01-10 13:44:04 +01:00
|
|
|
// Compute the DXVK binding slot index for the resource.
|
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
2018-10-08 09:34:56 +02:00
|
|
|
m_programInfo.type(), isUav
|
2018-01-10 13:44:04 +01:00
|
|
|
? DxbcBindingType::UnorderedAccessView
|
|
|
|
: DxbcBindingType::ShaderResource,
|
|
|
|
registerId);
|
|
|
|
|
|
|
|
m_module.decorateDescriptorSet(varId, 0);
|
|
|
|
m_module.decorateBinding(varId, bindingId);
|
|
|
|
|
2018-04-10 08:01:54 +02:00
|
|
|
if (ins.controls.uavFlags().test(DxbcUavFlag::GloballyCoherent))
|
2018-03-31 16:41:19 +02:00
|
|
|
m_module.decorate(varId, spv::DecorationCoherent);
|
|
|
|
|
2018-01-10 13:44:04 +01:00
|
|
|
// Declare a specialization constant which will
|
|
|
|
// store whether or not the resource is bound.
|
|
|
|
const uint32_t specConstId = m_module.specConstBool(true);
|
|
|
|
m_module.decorateSpecId(specConstId, bindingId);
|
|
|
|
m_module.setDebugName(specConstId,
|
|
|
|
str::format(isUav ? "u" : "t", registerId, "_bound").c_str());
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
if (isUav) {
|
|
|
|
DxbcUav uav;
|
|
|
|
uav.type = resType;
|
|
|
|
uav.imageInfo = typeInfo;
|
|
|
|
uav.varId = varId;
|
2018-01-11 17:11:51 +01:00
|
|
|
uav.ctrId = 0;
|
2018-01-10 13:44:04 +01:00
|
|
|
uav.specId = specConstId;
|
2017-12-28 16:03:17 +01:00
|
|
|
uav.sampledType = sampledType;
|
|
|
|
uav.sampledTypeId = sampledTypeId;
|
|
|
|
uav.imageTypeId = resTypeId;
|
|
|
|
uav.structStride = resStride;
|
|
|
|
m_uavs.at(registerId) = uav;
|
|
|
|
} else {
|
|
|
|
DxbcShaderResource res;
|
|
|
|
res.type = resType;
|
|
|
|
res.imageInfo = typeInfo;
|
|
|
|
res.varId = varId;
|
2018-01-10 13:44:04 +01:00
|
|
|
res.specId = specConstId;
|
2017-12-28 16:03:17 +01:00
|
|
|
res.sampledType = sampledType;
|
|
|
|
res.sampledTypeId = sampledTypeId;
|
2018-01-02 16:57:37 +01:00
|
|
|
res.imageTypeId = resTypeId;
|
2017-12-28 16:03:17 +01:00
|
|
|
res.colorTypeId = resTypeId;
|
2018-02-02 21:42:04 +01:00
|
|
|
res.depthTypeId = 0;
|
2017-12-28 16:03:17 +01:00
|
|
|
res.structStride = resStride;
|
|
|
|
m_textures.at(registerId) = res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store descriptor info for the shader interface
|
|
|
|
DxvkResourceSlot resource;
|
|
|
|
resource.slot = bindingId;
|
|
|
|
resource.type = isUav
|
|
|
|
? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
|
|
|
|
: VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
2018-01-09 20:35:29 +01:00
|
|
|
resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
2017-12-28 16:03:17 +01:00
|
|
|
m_resourceSlots.push_back(resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclThreadGroupSharedMemory(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_tgsm_raw takes two arguments:
|
|
|
|
// (dst0) The resource register ID
|
2018-03-09 22:01:19 +01:00
|
|
|
// (imm0) Block size, in bytes
|
2017-12-28 16:03:17 +01:00
|
|
|
// dcl_tgsm_structured takes three arguments:
|
|
|
|
// (dst0) The resource register ID
|
|
|
|
// (imm0) Structure stride, in bytes
|
2017-12-29 00:51:31 +01:00
|
|
|
// (imm1) Structure count
|
|
|
|
const bool isStructured = ins.op == DxbcOpcode::DclThreadGroupSharedMemoryStructured;
|
|
|
|
|
|
|
|
const uint32_t regId = ins.dst[0].idx[0].offset;
|
|
|
|
|
|
|
|
if (regId >= m_gRegs.size())
|
|
|
|
m_gRegs.resize(regId + 1);
|
|
|
|
|
|
|
|
const uint32_t elementStride = isStructured ? ins.imm[0].u32 : 0;
|
|
|
|
const uint32_t elementCount = isStructured ? ins.imm[1].u32 : ins.imm[0].u32;
|
|
|
|
|
|
|
|
DxbcRegisterInfo varInfo;
|
|
|
|
varInfo.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
varInfo.type.ccount = 1;
|
|
|
|
varInfo.type.alength = isStructured
|
|
|
|
? elementCount * elementStride / 4
|
2018-03-09 22:01:19 +01:00
|
|
|
: elementCount / 4;
|
2017-12-29 00:51:31 +01:00
|
|
|
varInfo.sclass = spv::StorageClassWorkgroup;
|
|
|
|
|
|
|
|
m_gRegs[regId].type = isStructured
|
|
|
|
? DxbcResourceType::Structured
|
|
|
|
: DxbcResourceType::Raw;
|
|
|
|
m_gRegs[regId].elementStride = elementStride;
|
|
|
|
m_gRegs[regId].elementCount = elementCount;
|
|
|
|
m_gRegs[regId].varId = emitNewVariable(varInfo);
|
|
|
|
|
|
|
|
m_module.setDebugName(m_gRegs[regId].varId,
|
|
|
|
str::format("g", regId).c_str());
|
2017-12-28 16:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitDclGsInputPrimitive(const DxbcShaderInstruction& ins) {
|
|
|
|
// The input primitive type is stored within in the
|
|
|
|
// control bits of the opcode token. In SPIR-V, we
|
|
|
|
// have to define an execution mode.
|
|
|
|
const spv::ExecutionMode mode = [&] {
|
2018-04-10 08:01:54 +02:00
|
|
|
switch (ins.controls.primitive()) {
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcPrimitive::Point: return spv::ExecutionModeInputPoints;
|
|
|
|
case DxbcPrimitive::Line: return spv::ExecutionModeInputLines;
|
|
|
|
case DxbcPrimitive::Triangle: return spv::ExecutionModeTriangles;
|
|
|
|
case DxbcPrimitive::LineAdj: return spv::ExecutionModeInputLinesAdjacency;
|
|
|
|
case DxbcPrimitive::TriangleAdj: return spv::ExecutionModeInputTrianglesAdjacency;
|
|
|
|
default: throw DxvkError("DxbcCompiler: Unsupported primitive type");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
2018-04-10 08:01:54 +02:00
|
|
|
m_gs.inputPrimitive = ins.controls.primitive();
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.setExecutionMode(m_entryPointId, mode);
|
2017-12-21 12:37:20 +01:00
|
|
|
|
2018-03-24 16:23:31 +01:00
|
|
|
const uint32_t vertexCount
|
|
|
|
= primitiveVertexCount(m_gs.inputPrimitive);
|
|
|
|
|
|
|
|
emitDclInputArray(vertexCount);
|
|
|
|
emitDclInputPerVertex(vertexCount, "gs_vertex_in");
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclGsOutputTopology(const DxbcShaderInstruction& ins) {
|
|
|
|
// The input primitive topology is stored within in the
|
|
|
|
// control bits of the opcode token. In SPIR-V, we have
|
|
|
|
// to define an execution mode.
|
|
|
|
const spv::ExecutionMode mode = [&] {
|
2018-04-10 08:01:54 +02:00
|
|
|
switch (ins.controls.primitiveTopology()) {
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcPrimitiveTopology::PointList: return spv::ExecutionModeOutputPoints;
|
|
|
|
case DxbcPrimitiveTopology::LineStrip: return spv::ExecutionModeOutputLineStrip;
|
|
|
|
case DxbcPrimitiveTopology::TriangleStrip: return spv::ExecutionModeOutputTriangleStrip;
|
|
|
|
default: throw DxvkError("DxbcCompiler: Unsupported primitive topology");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
m_module.setExecutionMode(m_entryPointId, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclMaxOutputVertexCount(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_max_output_vertex_count has one operand:
|
|
|
|
// (imm0) The maximum number of vertices
|
|
|
|
m_gs.outputVertexCount = ins.imm[0].u32;
|
2018-03-01 14:36:17 +01:00
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.setOutputVertices(m_entryPointId, m_gs.outputVertexCount);
|
2018-03-01 12:47:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclInputControlPointCount(const DxbcShaderInstruction& ins) {
|
2018-03-01 14:36:17 +01:00
|
|
|
// dcl_input_control_points has the control point
|
|
|
|
// count embedded within the opcode token.
|
2018-10-08 09:34:56 +02:00
|
|
|
if (m_programInfo.type() == DxbcProgramType::HullShader) {
|
2018-04-10 08:01:54 +02:00
|
|
|
m_hs.vertexCountIn = ins.controls.controlPointCount();
|
2018-03-06 16:47:35 +01:00
|
|
|
|
|
|
|
emitDclInputArray(m_hs.vertexCountIn);
|
|
|
|
} else {
|
2018-04-10 08:01:54 +02:00
|
|
|
m_ds.vertexCountIn = ins.controls.controlPointCount();
|
2018-03-06 16:47:35 +01:00
|
|
|
|
|
|
|
m_ds.inputPerPatch = emitTessInterfacePerPatch (spv::StorageClassInput);
|
|
|
|
m_ds.inputPerVertex = emitTessInterfacePerVertex(spv::StorageClassInput, m_ds.vertexCountIn);
|
|
|
|
}
|
2018-03-01 12:47:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclOutputControlPointCount(const DxbcShaderInstruction& ins) {
|
2018-03-01 14:36:17 +01:00
|
|
|
// dcl_output_control_points has the control point
|
|
|
|
// count embedded within the opcode token.
|
2018-04-10 08:01:54 +02:00
|
|
|
m_hs.vertexCountOut = ins.controls.controlPointCount();
|
2018-03-01 14:36:17 +01:00
|
|
|
|
2018-09-13 14:54:54 +02:00
|
|
|
m_hs.outputPerPatch = emitTessInterfacePerPatch(spv::StorageClassPrivate);
|
2018-03-06 14:00:03 +01:00
|
|
|
m_hs.outputPerVertex = emitTessInterfacePerVertex(spv::StorageClassOutput, m_hs.vertexCountOut);
|
|
|
|
|
2018-04-10 08:01:54 +02:00
|
|
|
m_module.setOutputVertices(m_entryPointId, m_hs.vertexCountOut);
|
2018-03-01 14:36:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-10 15:02:27 +01:00
|
|
|
void DxbcCompiler::emitDclHsMaxTessFactor(const DxbcShaderInstruction& ins) {
|
|
|
|
m_hs.maxTessFactor = ins.imm[0].f32;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-01 14:36:17 +01:00
|
|
|
void DxbcCompiler::emitDclTessDomain(const DxbcShaderInstruction& ins) {
|
|
|
|
const spv::ExecutionMode executionMode = [&] {
|
2018-04-10 08:01:54 +02:00
|
|
|
switch (ins.controls.tessDomain()) {
|
2018-03-01 14:36:17 +01:00
|
|
|
case DxbcTessDomain::Isolines: return spv::ExecutionModeIsolines;
|
|
|
|
case DxbcTessDomain::Triangles: return spv::ExecutionModeTriangles;
|
|
|
|
case DxbcTessDomain::Quads: return spv::ExecutionModeQuads;
|
|
|
|
default: throw DxvkError("Dxbc: Invalid tess domain");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
m_module.setExecutionMode(m_entryPointId, executionMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclTessPartitioning(const DxbcShaderInstruction& ins) {
|
|
|
|
const spv::ExecutionMode executionMode = [&] {
|
2018-04-10 08:01:54 +02:00
|
|
|
switch (ins.controls.tessPartitioning()) {
|
2018-03-01 14:36:17 +01:00
|
|
|
case DxbcTessPartitioning::Pow2:
|
|
|
|
case DxbcTessPartitioning::Integer: return spv::ExecutionModeSpacingEqual;
|
|
|
|
case DxbcTessPartitioning::FractOdd: return spv::ExecutionModeSpacingFractionalOdd;
|
|
|
|
case DxbcTessPartitioning::FractEven: return spv::ExecutionModeSpacingFractionalEven;
|
|
|
|
default: throw DxvkError("Dxbc: Invalid tess partitioning");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
m_module.setExecutionMode(m_entryPointId, executionMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclTessOutputPrimitive(const DxbcShaderInstruction& ins) {
|
2018-04-10 08:01:54 +02:00
|
|
|
switch (ins.controls.tessOutputPrimitive()) {
|
2018-03-01 14:36:17 +01:00
|
|
|
case DxbcTessOutputPrimitive::Point:
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModePointMode);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcTessOutputPrimitive::Line:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcTessOutputPrimitive::TriangleCw:
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeVertexOrderCw);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcTessOutputPrimitive::TriangleCcw:
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeVertexOrderCcw);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError("Dxbc: Invalid tess output primitive");
|
|
|
|
}
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitDclThreadGroup(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_thread_group has three operands:
|
|
|
|
// (imm0) Number of threads in X dimension
|
|
|
|
// (imm1) Number of threads in Y dimension
|
|
|
|
// (imm2) Number of threads in Z dimension
|
|
|
|
m_module.setLocalSize(m_entryPointId,
|
|
|
|
ins.imm[0].u32, ins.imm[1].u32, ins.imm[2].u32);
|
|
|
|
}
|
2018-01-11 17:11:51 +01:00
|
|
|
|
|
|
|
|
2018-05-22 19:36:53 +02:00
|
|
|
void DxbcCompiler::emitDclGsInstanceCount(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_gs_instance_count has one operand:
|
|
|
|
// (imm0) Number of geometry shader invocations
|
|
|
|
m_module.setInvocations(m_entryPointId, ins.imm[0].u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-11 17:11:51 +01:00
|
|
|
uint32_t DxbcCompiler::emitDclUavCounter(uint32_t regId) {
|
|
|
|
// Declare a structure type which holds the UAV counter
|
|
|
|
if (m_uavCtrStructType == 0) {
|
|
|
|
const uint32_t t_u32 = m_module.defIntType(32, 0);
|
|
|
|
const uint32_t t_struct = m_module.defStructTypeUnique(1, &t_u32);
|
|
|
|
|
|
|
|
m_module.decorate(t_struct, spv::DecorationBufferBlock);
|
|
|
|
m_module.memberDecorateOffset(t_struct, 0, 0);
|
|
|
|
|
|
|
|
m_module.setDebugName (t_struct, "uav_meta");
|
|
|
|
m_module.setDebugMemberName(t_struct, 0, "ctr");
|
|
|
|
|
|
|
|
m_uavCtrStructType = t_struct;
|
|
|
|
m_uavCtrPointerType = m_module.defPointerType(
|
|
|
|
t_struct, spv::StorageClassUniform);
|
|
|
|
}
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2018-01-11 17:11:51 +01:00
|
|
|
// Declare the buffer variable
|
|
|
|
const uint32_t varId = m_module.newVar(
|
|
|
|
m_uavCtrPointerType, spv::StorageClassUniform);
|
|
|
|
|
|
|
|
m_module.setDebugName(varId,
|
|
|
|
str::format("u", regId, "_meta").c_str());
|
|
|
|
|
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
2018-10-08 09:34:56 +02:00
|
|
|
m_programInfo.type(), DxbcBindingType::UavCounter,
|
2018-01-11 17:11:51 +01:00
|
|
|
regId);
|
|
|
|
|
|
|
|
m_module.decorateDescriptorSet(varId, 0);
|
|
|
|
m_module.decorateBinding(varId, bindingId);
|
|
|
|
|
|
|
|
// Declare the storage buffer binding
|
|
|
|
DxvkResourceSlot resource;
|
|
|
|
resource.slot = bindingId;
|
|
|
|
resource.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
|
|
resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
|
|
|
m_resourceSlots.push_back(resource);
|
|
|
|
|
|
|
|
return varId;
|
|
|
|
}
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2017-12-19 17:41:23 +01:00
|
|
|
void DxbcCompiler::emitDclImmediateConstantBuffer(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_immConstBuf != 0)
|
|
|
|
throw DxvkError("DxbcCompiler: Immediate constant buffer already declared");
|
|
|
|
|
|
|
|
if ((ins.customDataSize & 0x3) != 0)
|
|
|
|
throw DxvkError("DxbcCompiler: Immediate constant buffer size not a multiple of four DWORDs");
|
|
|
|
|
2018-07-30 20:52:42 +02:00
|
|
|
if (ins.customDataSize <= Icb_MaxBakedDwords) {
|
|
|
|
this->emitDclImmediateConstantBufferBaked(
|
|
|
|
ins.customDataSize, ins.customData);
|
|
|
|
} else {
|
|
|
|
this->emitDclImmediateConstantBufferUbo(
|
|
|
|
ins.customDataSize, ins.customData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclImmediateConstantBufferBaked(
|
|
|
|
uint32_t dwordCount,
|
|
|
|
const uint32_t* dwordArray) {
|
2017-12-19 17:41:23 +01:00
|
|
|
// Declare individual vector constants as 4x32-bit vectors
|
|
|
|
std::array<uint32_t, 4096> vectorIds;
|
|
|
|
|
|
|
|
DxbcVectorType vecType;
|
|
|
|
vecType.ctype = DxbcScalarType::Uint32;
|
|
|
|
vecType.ccount = 4;
|
|
|
|
|
|
|
|
const uint32_t vectorTypeId = getVectorTypeId(vecType);
|
2018-07-30 20:52:42 +02:00
|
|
|
const uint32_t vectorCount = dwordCount / 4;
|
2017-12-19 17:41:23 +01:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < vectorCount; i++) {
|
|
|
|
std::array<uint32_t, 4> scalarIds = {
|
2018-07-30 20:52:42 +02:00
|
|
|
m_module.constu32(dwordArray[4 * i + 0]),
|
|
|
|
m_module.constu32(dwordArray[4 * i + 1]),
|
|
|
|
m_module.constu32(dwordArray[4 * i + 2]),
|
|
|
|
m_module.constu32(dwordArray[4 * i + 3]),
|
2017-12-19 17:41:23 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
vectorIds.at(i) = m_module.constComposite(
|
|
|
|
vectorTypeId, scalarIds.size(), scalarIds.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Declare the array that contains all the vectors
|
|
|
|
DxbcArrayType arrInfo;
|
|
|
|
arrInfo.ctype = DxbcScalarType::Uint32;
|
|
|
|
arrInfo.ccount = 4;
|
|
|
|
arrInfo.alength = vectorCount;
|
|
|
|
|
|
|
|
const uint32_t arrayTypeId = getArrayTypeId(arrInfo);
|
|
|
|
const uint32_t arrayId = m_module.constComposite(
|
|
|
|
arrayTypeId, vectorCount, vectorIds.data());
|
|
|
|
|
|
|
|
// Declare the variable that will hold the constant
|
|
|
|
// data and initialize it with the constant array.
|
|
|
|
const uint32_t pointerTypeId = m_module.defPointerType(
|
|
|
|
arrayTypeId, spv::StorageClassPrivate);
|
|
|
|
|
|
|
|
m_immConstBuf = m_module.newVarInit(
|
|
|
|
pointerTypeId, spv::StorageClassPrivate,
|
|
|
|
arrayId);
|
|
|
|
m_module.setDebugName(m_immConstBuf, "icb");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-30 20:52:42 +02:00
|
|
|
void DxbcCompiler::emitDclImmediateConstantBufferUbo(
|
|
|
|
uint32_t dwordCount,
|
|
|
|
const uint32_t* dwordArray) {
|
2018-07-30 22:08:01 +02:00
|
|
|
this->emitDclConstantBufferVar(Icb_BindingSlotId, dwordCount / 4, "icb");
|
2018-07-30 20:52:42 +02:00
|
|
|
m_immConstData = DxvkShaderConstData(dwordCount, dwordArray);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-19 17:41:23 +01:00
|
|
|
void DxbcCompiler::emitCustomData(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.customDataType) {
|
|
|
|
case DxbcCustomDataClass::ImmConstBuf:
|
|
|
|
return emitDclImmediateConstantBuffer(ins);
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unsupported custom data block: ",
|
|
|
|
ins.customDataType));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorAlu(const DxbcShaderInstruction& ins) {
|
|
|
|
std::array<DxbcRegisterValue, DxbcMaxOperandCount> src;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
for (uint32_t i = 0; i < ins.srcCount; i++)
|
|
|
|
src.at(i) = emitRegisterLoad(ins.src[i], ins.dst[0].mask);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue dst;
|
|
|
|
dst.type.ctype = ins.dst[0].dataType;
|
2018-03-23 19:48:07 +01:00
|
|
|
dst.type.ccount = ins.dst[0].mask.popCount();
|
2018-06-07 15:05:06 +02:00
|
|
|
|
|
|
|
if (isDoubleType(ins.dst[0].dataType))
|
|
|
|
dst.type.ccount /= 2;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t typeId = getVectorTypeId(dst.type);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
switch (ins.op) {
|
2017-12-19 00:45:31 +01:00
|
|
|
/////////////////////
|
|
|
|
// Move instructions
|
|
|
|
case DxbcOpcode::Mov:
|
2018-06-07 15:05:06 +02:00
|
|
|
case DxbcOpcode::DMov:
|
2017-12-19 00:45:31 +01:00
|
|
|
dst.id = src.at(0).id;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/////////////////////////////////////
|
|
|
|
// ALU operations on float32 numbers
|
2017-12-13 15:32:54 +01:00
|
|
|
case DxbcOpcode::Add:
|
2018-06-07 15:05:06 +02:00
|
|
|
case DxbcOpcode::DAdd:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opFAdd(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-17 01:36:41 +01:00
|
|
|
case DxbcOpcode::Div:
|
2018-08-15 19:51:07 +02:00
|
|
|
case DxbcOpcode::DDiv:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opFDiv(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Exp:
|
|
|
|
dst.id = m_module.opExp2(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-29 19:26:59 +01:00
|
|
|
case DxbcOpcode::Frc:
|
|
|
|
dst.id = m_module.opFract(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcOpcode::Log:
|
|
|
|
dst.id = m_module.opLog2(
|
|
|
|
typeId, src.at(0).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
case DxbcOpcode::Mad:
|
2018-08-15 19:51:07 +02:00
|
|
|
case DxbcOpcode::DFma:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opFFma(typeId,
|
|
|
|
src.at(0).id, src.at(1).id, src.at(2).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-13 16:35:01 +01:00
|
|
|
case DxbcOpcode::Max:
|
2018-06-07 15:05:06 +02:00
|
|
|
case DxbcOpcode::DMax:
|
2018-06-12 22:13:53 +02:00
|
|
|
dst.id = m_module.opNMax(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
2017-12-13 16:35:01 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Min:
|
2018-06-07 15:05:06 +02:00
|
|
|
case DxbcOpcode::DMin:
|
2018-06-12 22:13:53 +02:00
|
|
|
dst.id = m_module.opNMin(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
2017-12-18 00:46:44 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Mul:
|
2018-06-07 15:05:06 +02:00
|
|
|
case DxbcOpcode::DMul:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opFMul(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
2017-12-13 16:35:01 +01:00
|
|
|
break;
|
|
|
|
|
2018-08-15 19:51:07 +02:00
|
|
|
case DxbcOpcode::Rcp:
|
2017-12-30 03:44:19 +01:00
|
|
|
dst.id = m_module.opFDiv(typeId,
|
2018-01-08 22:26:45 +01:00
|
|
|
emitBuildConstVecf32(
|
|
|
|
1.0f, 1.0f, 1.0f, 1.0f,
|
|
|
|
ins.dst[0].mask).id,
|
2017-12-30 03:44:19 +01:00
|
|
|
src.at(0).id);
|
2018-08-15 19:51:07 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DRcp:
|
|
|
|
dst.id = m_module.opFDiv(typeId,
|
|
|
|
emitBuildConstVecf64(1.0, 1.0,
|
|
|
|
ins.dst[0].mask).id,
|
|
|
|
src.at(0).id);
|
|
|
|
break;
|
2017-12-30 03:44:19 +01:00
|
|
|
|
2017-12-29 19:26:59 +01:00
|
|
|
case DxbcOpcode::RoundNe:
|
|
|
|
dst.id = m_module.opRoundEven(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::RoundNi:
|
|
|
|
dst.id = m_module.opFloor(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::RoundPi:
|
|
|
|
dst.id = m_module.opCeil(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::RoundZ:
|
|
|
|
dst.id = m_module.opTrunc(
|
2017-12-18 00:46:44 +01:00
|
|
|
typeId, src.at(0).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Rsq:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opInverseSqrt(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-29 19:26:59 +01:00
|
|
|
case DxbcOpcode::Sqrt:
|
|
|
|
dst.id = m_module.opSqrt(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-19 00:45:31 +01:00
|
|
|
/////////////////////////////////////
|
|
|
|
// ALU operations on signed integers
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcOpcode::IAdd:
|
|
|
|
dst.id = m_module.opIAdd(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::IMad:
|
2017-12-20 23:50:39 +01:00
|
|
|
case DxbcOpcode::UMad:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opIAdd(typeId,
|
|
|
|
m_module.opIMul(typeId,
|
|
|
|
src.at(0).id, src.at(1).id),
|
|
|
|
src.at(2).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcOpcode::IMax:
|
|
|
|
dst.id = m_module.opSMax(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::IMin:
|
|
|
|
dst.id = m_module.opSMin(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::INeg:
|
|
|
|
dst.id = m_module.opSNegate(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
2017-12-19 00:45:31 +01:00
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
///////////////////////////////////////
|
|
|
|
// ALU operations on unsigned integers
|
|
|
|
case DxbcOpcode::UMax:
|
|
|
|
dst.id = m_module.opUMax(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::UMin:
|
|
|
|
dst.id = m_module.opUMin(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-19 00:45:31 +01:00
|
|
|
///////////////////////////////////////
|
|
|
|
// Bit operations on unsigned integers
|
|
|
|
case DxbcOpcode::And:
|
|
|
|
dst.id = m_module.opBitwiseAnd(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Not:
|
|
|
|
dst.id = m_module.opNot(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-19 00:45:31 +01:00
|
|
|
case DxbcOpcode::Or:
|
|
|
|
dst.id = m_module.opBitwiseOr(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Xor:
|
|
|
|
dst.id = m_module.opBitwiseXor(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
2018-02-04 13:14:23 +01:00
|
|
|
case DxbcOpcode::CountBits:
|
|
|
|
dst.id = m_module.opBitCount(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2018-02-15 09:41:48 +01:00
|
|
|
case DxbcOpcode::BfRev:
|
|
|
|
dst.id = m_module.opBitReverse(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-19 18:12:18 +01:00
|
|
|
///////////////////////////
|
|
|
|
// Conversion instructions
|
|
|
|
case DxbcOpcode::ItoF:
|
|
|
|
dst.id = m_module.opConvertStoF(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::UtoF:
|
|
|
|
dst.id = m_module.opConvertUtoF(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::FtoI:
|
|
|
|
dst.id = m_module.opConvertFtoS(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::FtoU:
|
|
|
|
dst.id = m_module.opConvertFtoU(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
default:
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-12-11 14:36:35 +01:00
|
|
|
|
2018-04-20 23:32:51 +02:00
|
|
|
if (ins.controls.precise())
|
|
|
|
m_module.decorate(dst.id, spv::DecorationNoContraction);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Store computed value
|
|
|
|
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], dst);
|
2017-11-17 11:41:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorCmov(const DxbcShaderInstruction& ins) {
|
2018-01-17 21:47:18 +01:00
|
|
|
// movc and swapc have the following operands:
|
|
|
|
// (dst0) The first destination register
|
|
|
|
// (dst1) The second destination register (swapc only)
|
2017-12-18 00:46:44 +01:00
|
|
|
// (src0) The condition vector
|
2018-01-17 21:47:18 +01:00
|
|
|
// (src1) Vector to select from if the condition is not 0
|
|
|
|
// (src2) Vector to select from if the condition is 0
|
2017-12-18 00:46:44 +01:00
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
const DxbcRegisterValue selectTrue = emitRegisterLoad(ins.src[1], ins.dst[0].mask);
|
|
|
|
const DxbcRegisterValue selectFalse = emitRegisterLoad(ins.src[2], ins.dst[0].mask);
|
2017-12-17 01:36:41 +01:00
|
|
|
|
2018-03-23 19:48:07 +01:00
|
|
|
const uint32_t componentCount = ins.dst[0].mask.popCount();
|
2017-12-17 01:36:41 +01:00
|
|
|
|
|
|
|
// We'll compare against a vector of zeroes to generate a
|
|
|
|
// boolean vector, which in turn will be used by OpSelect
|
|
|
|
uint32_t zeroType = m_module.defIntType(32, 0);
|
|
|
|
uint32_t boolType = m_module.defBoolType();
|
|
|
|
|
|
|
|
uint32_t zero = m_module.constu32(0);
|
|
|
|
|
|
|
|
if (componentCount > 1) {
|
|
|
|
zeroType = m_module.defVectorType(zeroType, componentCount);
|
|
|
|
boolType = m_module.defVectorType(boolType, componentCount);
|
|
|
|
|
|
|
|
const std::array<uint32_t, 4> zeroVec = { zero, zero, zero, zero };
|
|
|
|
zero = m_module.constComposite(zeroType, componentCount, zeroVec.data());
|
|
|
|
}
|
|
|
|
|
2018-01-17 21:47:18 +01:00
|
|
|
// In case of swapc, the second destination operand receives
|
|
|
|
// the output that a cmov instruction would normally get
|
|
|
|
const uint32_t trueIndex = ins.op == DxbcOpcode::Swapc ? 1 : 0;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < ins.dstCount; i++) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = ins.dst[i].dataType;
|
|
|
|
result.type.ccount = componentCount;
|
|
|
|
result.id = m_module.opSelect(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
m_module.opINotEqual(boolType, condition.id, zero),
|
|
|
|
i == trueIndex ? selectTrue.id : selectFalse.id,
|
|
|
|
i != trueIndex ? selectTrue.id : selectFalse.id);
|
|
|
|
|
|
|
|
result = emitDstOperandModifiers(result, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[i], result);
|
|
|
|
}
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorCmp(const DxbcShaderInstruction& ins) {
|
2017-12-17 01:36:41 +01:00
|
|
|
// Compare instructions have three operands:
|
2017-12-18 00:46:44 +01:00
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) The first vector to compare
|
|
|
|
// (src1) The second vector to compare
|
2018-06-07 15:21:30 +02:00
|
|
|
uint32_t componentCount = ins.dst[0].mask.popCount();
|
|
|
|
|
|
|
|
// For 64-bit operations, we'll return a 32-bit
|
|
|
|
// vector, so we have to adjust the read mask
|
|
|
|
DxbcRegMask srcMask = ins.dst[0].mask;
|
|
|
|
|
|
|
|
if (isDoubleType(ins.src[0].dataType)) {
|
|
|
|
srcMask = DxbcRegMask(
|
|
|
|
componentCount > 0, componentCount > 0,
|
|
|
|
componentCount > 1, componentCount > 1);
|
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const std::array<DxbcRegisterValue, 2> src = {
|
2018-06-07 15:21:30 +02:00
|
|
|
emitRegisterLoad(ins.src[0], srcMask),
|
|
|
|
emitRegisterLoad(ins.src[1], srcMask),
|
2017-12-18 00:46:44 +01:00
|
|
|
};
|
2017-12-17 01:36:41 +01:00
|
|
|
|
|
|
|
// Condition, which is a boolean vector used
|
|
|
|
// to select between the ~0u and 0u vectors.
|
|
|
|
uint32_t condition = 0;
|
|
|
|
uint32_t conditionType = m_module.defBoolType();
|
|
|
|
|
|
|
|
if (componentCount > 1)
|
|
|
|
conditionType = m_module.defVectorType(conditionType, componentCount);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
switch (ins.op) {
|
2017-12-17 01:36:41 +01:00
|
|
|
case DxbcOpcode::Eq:
|
2018-06-07 15:21:30 +02:00
|
|
|
case DxbcOpcode::DEq:
|
2017-12-17 01:36:41 +01:00
|
|
|
condition = m_module.opFOrdEqual(
|
2017-12-18 00:46:44 +01:00
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Ge:
|
2018-06-07 15:21:30 +02:00
|
|
|
case DxbcOpcode::DGe:
|
2017-12-17 01:36:41 +01:00
|
|
|
condition = m_module.opFOrdGreaterThanEqual(
|
2017-12-18 00:46:44 +01:00
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Lt:
|
2018-06-07 15:21:30 +02:00
|
|
|
case DxbcOpcode::DLt:
|
2017-12-17 01:36:41 +01:00
|
|
|
condition = m_module.opFOrdLessThan(
|
2017-12-18 00:46:44 +01:00
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Ne:
|
2018-06-07 15:21:30 +02:00
|
|
|
case DxbcOpcode::DNe:
|
2017-12-17 01:36:41 +01:00
|
|
|
condition = m_module.opFOrdNotEqual(
|
2017-12-18 00:46:44 +01:00
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::IEq:
|
|
|
|
condition = m_module.opIEqual(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::IGe:
|
|
|
|
condition = m_module.opSGreaterThanEqual(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::ILt:
|
|
|
|
condition = m_module.opSLessThan(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::INe:
|
|
|
|
condition = m_module.opINotEqual(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
case DxbcOpcode::UGe:
|
|
|
|
condition = m_module.opUGreaterThanEqual(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::ULt:
|
|
|
|
condition = m_module.opULessThan(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-17 01:36:41 +01:00
|
|
|
default:
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate constant vectors for selection
|
|
|
|
uint32_t sFalse = m_module.constu32( 0u);
|
|
|
|
uint32_t sTrue = m_module.constu32(~0u);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = componentCount;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
2017-12-17 01:36:41 +01:00
|
|
|
|
|
|
|
if (componentCount > 1) {
|
|
|
|
const std::array<uint32_t, 4> vFalse = { sFalse, sFalse, sFalse, sFalse };
|
|
|
|
const std::array<uint32_t, 4> vTrue = { sTrue, sTrue, sTrue, sTrue };
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
sFalse = m_module.constComposite(typeId, componentCount, vFalse.data());
|
|
|
|
sTrue = m_module.constComposite(typeId, componentCount, vTrue .data());
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Perform component-wise mask selection
|
|
|
|
// based on the condition evaluated above.
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opSelect(
|
|
|
|
typeId, condition, sTrue, sFalse);
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-19 20:26:05 +01:00
|
|
|
void DxbcCompiler::emitVectorDeriv(const DxbcShaderInstruction& ins) {
|
|
|
|
// Derivative instructions have two operands:
|
|
|
|
// (dst0) Destination register for the derivative
|
|
|
|
// (src0) The operand to compute the derivative of
|
|
|
|
DxbcRegisterValue value = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::DerivRtx:
|
|
|
|
value.id = m_module.opDpdx(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRty:
|
|
|
|
value.id = m_module.opDpdy(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtxCoarse:
|
|
|
|
value.id = m_module.opDpdxCoarse(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtyCoarse:
|
|
|
|
value.id = m_module.opDpdyCoarse(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtxFine:
|
|
|
|
value.id = m_module.opDpdxFine(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtyFine:
|
|
|
|
value.id = m_module.opDpdyFine(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = emitDstOperandModifiers(value, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorDot(const DxbcShaderInstruction& ins) {
|
|
|
|
const DxbcRegMask srcMask(true,
|
|
|
|
ins.op >= DxbcOpcode::Dp2,
|
|
|
|
ins.op >= DxbcOpcode::Dp3,
|
|
|
|
ins.op >= DxbcOpcode::Dp4);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const std::array<DxbcRegisterValue, 2> src = {
|
|
|
|
emitRegisterLoad(ins.src[0], srcMask),
|
|
|
|
emitRegisterLoad(ins.src[1], srcMask),
|
|
|
|
};
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue dst;
|
|
|
|
dst.type.ctype = ins.dst[0].dataType;
|
|
|
|
dst.type.ccount = 1;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opDot(
|
|
|
|
getVectorTypeId(dst.type),
|
|
|
|
src.at(0).id,
|
|
|
|
src.at(1).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2018-04-20 23:32:51 +02:00
|
|
|
if (ins.controls.precise())
|
|
|
|
m_module.decorate(dst.id, spv::DecorationNoContraction);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], dst);
|
2017-12-09 01:49:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitVectorIdiv(const DxbcShaderInstruction& ins) {
|
|
|
|
// udiv has four operands:
|
|
|
|
// (dst0) Quotient destination register
|
|
|
|
// (dst1) Remainder destination register
|
|
|
|
// (src0) The first vector to compare
|
|
|
|
// (src1) The second vector to compare
|
|
|
|
if (ins.dst[0].type == DxbcOperandType::Null
|
|
|
|
&& ins.dst[1].type == DxbcOperandType::Null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// FIXME support this if applications require it
|
|
|
|
if (ins.dst[0].type != DxbcOperandType::Null
|
|
|
|
&& ins.dst[1].type != DxbcOperandType::Null
|
|
|
|
&& ins.dst[0].mask != ins.dst[1].mask) {
|
2017-12-30 13:18:31 +01:00
|
|
|
Logger::warn("DxbcCompiler: Idiv with different destination masks not supported");
|
2017-12-18 16:41:05 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load source operands as integers with the
|
|
|
|
// mask of one non-NULL destination operand
|
|
|
|
const DxbcRegMask srcMask =
|
|
|
|
ins.dst[0].type != DxbcOperandType::Null
|
|
|
|
? ins.dst[0].mask
|
|
|
|
: ins.dst[1].mask;
|
|
|
|
|
|
|
|
const std::array<DxbcRegisterValue, 2> src = {
|
|
|
|
emitRegisterLoad(ins.src[0], srcMask),
|
|
|
|
emitRegisterLoad(ins.src[1], srcMask),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Compute results only if the destination
|
|
|
|
// operands are not NULL.
|
|
|
|
if (ins.dst[0].type != DxbcOperandType::Null) {
|
|
|
|
DxbcRegisterValue quotient;
|
|
|
|
quotient.type.ctype = ins.dst[0].dataType;
|
2018-03-23 19:48:07 +01:00
|
|
|
quotient.type.ccount = ins.dst[0].mask.popCount();
|
2017-12-18 16:41:05 +01:00
|
|
|
|
|
|
|
quotient.id = m_module.opUDiv(
|
|
|
|
getVectorTypeId(quotient.type),
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
|
|
|
|
quotient = emitDstOperandModifiers(quotient, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], quotient);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ins.dst[1].type != DxbcOperandType::Null) {
|
|
|
|
DxbcRegisterValue remainder;
|
|
|
|
remainder.type.ctype = ins.dst[1].dataType;
|
2018-03-23 19:48:07 +01:00
|
|
|
remainder.type.ccount = ins.dst[1].mask.popCount();
|
2017-12-18 16:41:05 +01:00
|
|
|
|
|
|
|
remainder.id = m_module.opUMod(
|
|
|
|
getVectorTypeId(remainder.type),
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
|
|
|
|
remainder = emitDstOperandModifiers(remainder, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[1], remainder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorImul(const DxbcShaderInstruction& ins) {
|
|
|
|
// imul and umul have four operands:
|
|
|
|
// (dst0) High destination register
|
|
|
|
// (dst1) Low destination register
|
|
|
|
// (src0) The first vector to compare
|
|
|
|
// (src1) The second vector to compare
|
|
|
|
if (ins.dst[0].type == DxbcOperandType::Null) {
|
|
|
|
if (ins.dst[1].type == DxbcOperandType::Null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If dst0 is NULL, this instruction behaves just
|
2017-12-18 16:41:05 +01:00
|
|
|
// like any other three-operand ALU instruction
|
2017-12-18 00:46:44 +01:00
|
|
|
const std::array<DxbcRegisterValue, 2> src = {
|
|
|
|
emitRegisterLoad(ins.src[0], ins.dst[1].mask),
|
|
|
|
emitRegisterLoad(ins.src[1], ins.dst[1].mask),
|
|
|
|
};
|
2017-12-13 16:35:01 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = ins.dst[1].dataType;
|
2018-03-23 19:48:07 +01:00
|
|
|
result.type.ccount = ins.dst[1].mask.popCount();
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opIMul(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
src.at(0).id, src.at(1).id);
|
2017-12-13 16:35:01 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
result = emitDstOperandModifiers(result, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[1], result);
|
|
|
|
} else {
|
|
|
|
// TODO implement this
|
|
|
|
Logger::warn("DxbcCompiler: Extended Imul not yet supported");
|
2017-12-13 16:35:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
void DxbcCompiler::emitVectorShift(const DxbcShaderInstruction& ins) {
|
|
|
|
// Shift operations have three operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) The register to shift
|
|
|
|
// (src1) The shift amount (scalar)
|
2018-01-17 05:35:41 +01:00
|
|
|
DxbcRegisterValue shiftReg = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
DxbcRegisterValue countReg = emitRegisterLoad(ins.src[1], ins.dst[0].mask);
|
2018-09-03 01:01:44 +02:00
|
|
|
|
|
|
|
if (ins.src[1].type != DxbcOperandType::Imm32)
|
|
|
|
countReg = emitRegisterMaskBits(countReg, 0x1F);
|
2018-01-17 05:35:41 +01:00
|
|
|
|
|
|
|
if (countReg.type.ccount == 1)
|
|
|
|
countReg = emitRegisterExtend(countReg, shiftReg.type.ccount);
|
2017-12-20 23:50:39 +01:00
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = ins.dst[0].dataType;
|
2018-03-23 19:48:07 +01:00
|
|
|
result.type.ccount = ins.dst[0].mask.popCount();
|
2017-12-20 23:50:39 +01:00
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::IShl:
|
|
|
|
result.id = m_module.opShiftLeftLogical(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
shiftReg.id, countReg.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::IShr:
|
|
|
|
result.id = m_module.opShiftRightArithmetic(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
shiftReg.id, countReg.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::UShr:
|
|
|
|
result.id = m_module.opShiftRightLogical(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
shiftReg.id, countReg.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = emitDstOperandModifiers(result, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorSinCos(const DxbcShaderInstruction& ins) {
|
|
|
|
// sincos has three operands:
|
|
|
|
// (dst0) Destination register for sin(x)
|
|
|
|
// (dst1) Destination register for cos(x)
|
|
|
|
// (src0) Source operand x
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Load source operand as 32-bit float vector.
|
|
|
|
const DxbcRegisterValue srcValue = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, true, true, true));
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Either output may be DxbcOperandType::Null, in
|
|
|
|
// which case we don't have to generate any code.
|
|
|
|
if (ins.dst[0].type != DxbcOperandType::Null) {
|
|
|
|
const DxbcRegisterValue sinInput =
|
|
|
|
emitRegisterExtract(srcValue, ins.dst[0].mask);
|
|
|
|
|
|
|
|
DxbcRegisterValue sin;
|
|
|
|
sin.type = sinInput.type;
|
|
|
|
sin.id = m_module.opSin(
|
|
|
|
getVectorTypeId(sin.type),
|
|
|
|
sinInput.id);
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], sin);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ins.dst[1].type != DxbcOperandType::Null) {
|
|
|
|
const DxbcRegisterValue cosInput =
|
|
|
|
emitRegisterExtract(srcValue, ins.dst[1].mask);
|
|
|
|
|
|
|
|
DxbcRegisterValue cos;
|
|
|
|
cos.type = cosInput.type;
|
2018-01-02 12:07:49 +01:00
|
|
|
cos.id = m_module.opCos(
|
2017-12-18 00:46:44 +01:00
|
|
|
getVectorTypeId(cos.type),
|
|
|
|
cosInput.id);
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[1], cos);
|
|
|
|
}
|
2017-12-08 17:08:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitGeometryEmit(const DxbcShaderInstruction& ins) {
|
2018-06-23 23:46:24 +02:00
|
|
|
// In xfb mode we might have multiple streams, so
|
|
|
|
// we have to figure out which stream to write to
|
|
|
|
uint32_t streamId = 0;
|
|
|
|
uint32_t streamVar = 0;
|
|
|
|
|
|
|
|
if (m_moduleInfo.xfb != nullptr) {
|
|
|
|
streamId = ins.dstCount > 0 ? ins.dst[0].idx[0].offset : 0;
|
|
|
|
streamVar = m_module.constu32(streamId);
|
|
|
|
}
|
|
|
|
|
2018-06-23 17:34:50 +02:00
|
|
|
// Checking the negation is easier for EmitThenCut/EmitThenCutStream
|
|
|
|
bool doEmit = ins.op != DxbcOpcode::Cut && ins.op != DxbcOpcode::CutStream;
|
|
|
|
bool doCut = ins.op != DxbcOpcode::Emit && ins.op != DxbcOpcode::EmitStream;
|
|
|
|
|
|
|
|
if (doEmit) {
|
|
|
|
emitOutputSetup();
|
|
|
|
emitClipCullStore(DxbcSystemValue::ClipDistance, m_clipDistances);
|
|
|
|
emitClipCullStore(DxbcSystemValue::CullDistance, m_cullDistances);
|
2018-07-25 22:45:23 +02:00
|
|
|
emitXfbOutputSetup(streamId, false);
|
2018-06-23 23:46:24 +02:00
|
|
|
m_module.opEmitVertex(streamVar);
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
2018-06-23 17:34:50 +02:00
|
|
|
|
|
|
|
if (doCut)
|
2018-06-23 23:46:24 +02:00
|
|
|
m_module.opEndPrimitive(streamVar);
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitAtomic(const DxbcShaderInstruction& ins) {
|
2018-01-02 12:07:49 +01:00
|
|
|
// atomic_* operations have the following operands:
|
|
|
|
// (dst0) Destination u# or g# register
|
|
|
|
// (src0) Index into the texture or buffer
|
|
|
|
// (src1) The source value for the operation
|
|
|
|
// (src2) Second source operand (optional)
|
|
|
|
// imm_atomic_* operations have the following operands:
|
|
|
|
// (dst0) Register that receives the result
|
|
|
|
// (dst1) Destination u# or g# register
|
|
|
|
// (srcX) As above
|
2018-05-26 19:00:22 +02:00
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(ins.dst[ins.dstCount - 1]);
|
|
|
|
|
2018-01-02 12:07:49 +01:00
|
|
|
const bool isImm = ins.dstCount == 2;
|
|
|
|
const bool isUav = ins.dst[ins.dstCount - 1].type == DxbcOperandType::UnorderedAccessView;
|
|
|
|
|
2018-05-26 19:00:22 +02:00
|
|
|
// Perform atomic operations on UAVs only if the UAV
|
|
|
|
// is bound and if there is nothing else stopping us.
|
|
|
|
DxbcConditional cond;
|
|
|
|
|
|
|
|
if (isUav) {
|
2018-05-26 19:25:20 +02:00
|
|
|
uint32_t writeTest = emitUavWriteTest(bufferInfo);
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
cond.labelIf = m_module.allocateId();
|
|
|
|
cond.labelEnd = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
|
|
|
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
|
|
|
|
|
|
|
m_module.opLabel(cond.labelIf);
|
|
|
|
}
|
|
|
|
|
2018-03-21 12:11:18 +01:00
|
|
|
// Retrieve destination pointer for the atomic operation>
|
2018-01-02 12:07:49 +01:00
|
|
|
const DxbcRegisterPointer pointer = emitGetAtomicPointer(
|
|
|
|
ins.dst[ins.dstCount - 1], ins.src[0]);
|
|
|
|
|
|
|
|
// Load source values
|
|
|
|
std::array<DxbcRegisterValue, 2> src;
|
|
|
|
|
|
|
|
for (uint32_t i = 1; i < ins.srcCount; i++) {
|
2018-03-30 17:31:48 +02:00
|
|
|
src[i - 1] = emitRegisterBitcast(
|
|
|
|
emitRegisterLoad(ins.src[i], DxbcRegMask(true, false, false, false)),
|
|
|
|
pointer.type.ctype);
|
2018-01-02 12:07:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Define memory scope and semantics based on the operands
|
2018-03-31 16:41:19 +02:00
|
|
|
uint32_t scope = 0;
|
|
|
|
uint32_t semantics = 0;
|
2018-01-02 12:07:49 +01:00
|
|
|
|
2018-03-31 16:41:19 +02:00
|
|
|
if (isUav) {
|
|
|
|
scope = spv::ScopeDevice;
|
|
|
|
semantics = spv::MemorySemanticsImageMemoryMask
|
|
|
|
| spv::MemorySemanticsAcquireReleaseMask;
|
|
|
|
} else {
|
|
|
|
scope = spv::ScopeWorkgroup;
|
|
|
|
semantics = spv::MemorySemanticsWorkgroupMemoryMask
|
|
|
|
| spv::MemorySemanticsAcquireReleaseMask;
|
|
|
|
}
|
2018-01-02 12:07:49 +01:00
|
|
|
|
2018-03-31 16:41:19 +02:00
|
|
|
const uint32_t scopeId = m_module.constu32(scope);
|
2018-01-02 12:07:49 +01:00
|
|
|
const uint32_t semanticsId = m_module.constu32(semantics);
|
|
|
|
|
|
|
|
// Perform the atomic operation on the given pointer
|
|
|
|
DxbcRegisterValue value;
|
2018-03-30 17:31:48 +02:00
|
|
|
value.type = pointer.type;
|
|
|
|
value.id = 0;
|
2018-01-02 12:07:49 +01:00
|
|
|
|
|
|
|
// The result type, which is a scalar integer
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
|
|
|
|
2017-12-30 13:18:31 +01:00
|
|
|
switch (ins.op) {
|
2018-04-04 13:59:43 +02:00
|
|
|
case DxbcOpcode::AtomicCmpStore:
|
|
|
|
case DxbcOpcode::ImmAtomicCmpExch:
|
|
|
|
value.id = m_module.opAtomicCompareExchange(
|
|
|
|
typeId, pointer.id, scopeId, semanticsId,
|
|
|
|
m_module.constu32(spv::MemorySemanticsMaskNone),
|
|
|
|
src[1].id, src[0].id);
|
|
|
|
break;
|
|
|
|
|
2018-01-02 12:07:49 +01:00
|
|
|
case DxbcOpcode::ImmAtomicExch:
|
|
|
|
value.id = m_module.opAtomicExchange(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::AtomicIAdd:
|
|
|
|
case DxbcOpcode::ImmAtomicIAdd:
|
|
|
|
value.id = m_module.opAtomicIAdd(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::AtomicAnd:
|
|
|
|
case DxbcOpcode::ImmAtomicAnd:
|
|
|
|
value.id = m_module.opAtomicAnd(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::AtomicOr:
|
|
|
|
case DxbcOpcode::ImmAtomicOr:
|
|
|
|
value.id = m_module.opAtomicOr(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::AtomicXor:
|
|
|
|
case DxbcOpcode::ImmAtomicXor:
|
|
|
|
value.id = m_module.opAtomicXor(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
2018-02-21 03:49:06 +01:00
|
|
|
case DxbcOpcode::AtomicIMin:
|
2018-04-02 10:58:26 +02:00
|
|
|
case DxbcOpcode::ImmAtomicIMin:
|
2018-02-21 03:49:06 +01:00
|
|
|
value.id = m_module.opAtomicSMin(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::AtomicIMax:
|
2018-04-02 10:58:26 +02:00
|
|
|
case DxbcOpcode::ImmAtomicIMax:
|
2018-02-21 03:49:06 +01:00
|
|
|
value.id = m_module.opAtomicSMax(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
2018-01-02 12:07:49 +01:00
|
|
|
case DxbcOpcode::AtomicUMin:
|
2018-04-02 10:58:26 +02:00
|
|
|
case DxbcOpcode::ImmAtomicUMin:
|
2018-01-02 12:07:49 +01:00
|
|
|
value.id = m_module.opAtomicUMin(typeId,
|
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::AtomicUMax:
|
2018-04-02 10:58:26 +02:00
|
|
|
case DxbcOpcode::ImmAtomicUMax:
|
2018-01-05 19:48:45 +01:00
|
|
|
value.id = m_module.opAtomicUMax(typeId,
|
2018-01-02 12:07:49 +01:00
|
|
|
pointer.id, scopeId, semanticsId,
|
|
|
|
src[0].id);
|
|
|
|
break;
|
|
|
|
|
2017-12-30 13:18:31 +01:00
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
2018-01-02 12:07:49 +01:00
|
|
|
return;
|
2017-12-30 13:18:31 +01:00
|
|
|
}
|
2018-01-02 12:07:49 +01:00
|
|
|
|
|
|
|
// Write back the result to the destination
|
|
|
|
// register if this is an imm_atomic_* opcode.
|
|
|
|
if (isImm)
|
|
|
|
emitRegisterStore(ins.dst[0], value);
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
// End conditional block
|
|
|
|
if (isUav) {
|
|
|
|
m_module.opBranch(cond.labelEnd);
|
|
|
|
m_module.opLabel (cond.labelEnd);
|
|
|
|
}
|
2018-01-11 17:11:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitAtomicCounter(const DxbcShaderInstruction& ins) {
|
|
|
|
// imm_atomic_alloc and imm_atomic_consume have the following operands:
|
|
|
|
// (dst0) The register that will hold the old counter value
|
|
|
|
// (dst1) The UAV whose counter is going to be modified
|
2018-05-26 19:00:22 +02:00
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(ins.dst[1]);
|
|
|
|
|
2018-01-11 17:11:51 +01:00
|
|
|
const uint32_t registerId = ins.dst[1].idx[0].offset;
|
|
|
|
|
|
|
|
if (m_uavs.at(registerId).ctrId == 0)
|
|
|
|
m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId);
|
|
|
|
|
2018-05-26 19:00:22 +02:00
|
|
|
// Only perform the operation if the UAV is bound
|
2018-05-26 19:25:20 +02:00
|
|
|
uint32_t writeTest = emitUavWriteTest(bufferInfo);
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
DxbcConditional cond;
|
|
|
|
cond.labelIf = m_module.allocateId();
|
|
|
|
cond.labelEnd = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
|
|
|
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
|
|
|
|
|
|
|
m_module.opLabel(cond.labelIf);
|
|
|
|
|
2018-01-11 17:11:51 +01:00
|
|
|
// Get a pointer to the atomic counter in question
|
|
|
|
DxbcRegisterInfo ptrType;
|
|
|
|
ptrType.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
ptrType.type.ccount = 1;
|
|
|
|
ptrType.type.alength = 0;
|
|
|
|
ptrType.sclass = spv::StorageClassUniform;
|
|
|
|
|
|
|
|
const uint32_t zeroId = m_module.consti32(0);
|
|
|
|
const uint32_t ptrId = m_module.opAccessChain(
|
|
|
|
getPointerTypeId(ptrType),
|
|
|
|
m_uavs.at(registerId).ctrId,
|
|
|
|
1, &zeroId);
|
|
|
|
|
|
|
|
// Define memory scope and semantics based on the operands
|
|
|
|
uint32_t scope = spv::ScopeDevice;
|
|
|
|
uint32_t semantics = spv::MemorySemanticsUniformMemoryMask
|
|
|
|
| spv::MemorySemanticsAcquireReleaseMask;
|
|
|
|
|
|
|
|
const uint32_t scopeId = m_module.constu32(scope);
|
|
|
|
const uint32_t semanticsId = m_module.constu32(semantics);
|
|
|
|
|
|
|
|
// Compute the result value
|
|
|
|
DxbcRegisterValue value;
|
|
|
|
value.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
value.type.ccount = 1;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::ImmAtomicAlloc:
|
|
|
|
value.id = m_module.opAtomicIAdd(typeId, ptrId,
|
|
|
|
scopeId, semanticsId, m_module.constu32(1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::ImmAtomicConsume:
|
|
|
|
value.id = m_module.opAtomicISub(typeId, ptrId,
|
|
|
|
scopeId, semanticsId, m_module.constu32(1));
|
2018-06-11 20:17:29 +02:00
|
|
|
value.id = m_module.opISub(typeId, value.id,
|
|
|
|
m_module.constu32(1));
|
2018-01-11 17:11:51 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the result
|
|
|
|
emitRegisterStore(ins.dst[0], value);
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
// End conditional block
|
|
|
|
m_module.opBranch(cond.labelEnd);
|
|
|
|
m_module.opLabel (cond.labelEnd);
|
2017-12-28 16:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-29 00:51:31 +01:00
|
|
|
void DxbcCompiler::emitBarrier(const DxbcShaderInstruction& ins) {
|
|
|
|
// sync takes no operands. Instead, the synchronization
|
|
|
|
// scope is defined by the operand control bits.
|
2018-04-10 08:01:54 +02:00
|
|
|
const DxbcSyncFlags flags = ins.controls.syncFlags();
|
2017-12-29 00:51:31 +01:00
|
|
|
|
2018-03-30 17:59:57 +02:00
|
|
|
uint32_t executionScope = spv::ScopeInvocation;
|
|
|
|
uint32_t memoryScope = spv::ScopeInvocation;
|
2017-12-29 00:51:31 +01:00
|
|
|
uint32_t memorySemantics = 0;
|
|
|
|
|
|
|
|
if (flags.test(DxbcSyncFlag::ThreadsInGroup))
|
|
|
|
executionScope = spv::ScopeWorkgroup;
|
|
|
|
|
|
|
|
if (flags.test(DxbcSyncFlag::ThreadGroupSharedMemory)) {
|
|
|
|
memoryScope = spv::ScopeWorkgroup;
|
2018-03-30 17:59:57 +02:00
|
|
|
memorySemantics |= spv::MemorySemanticsWorkgroupMemoryMask
|
|
|
|
| spv::MemorySemanticsAcquireReleaseMask;
|
2017-12-29 00:51:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flags.test(DxbcSyncFlag::UavMemoryGroup)) {
|
|
|
|
memoryScope = spv::ScopeWorkgroup;
|
2018-03-30 17:59:57 +02:00
|
|
|
memorySemantics |= spv::MemorySemanticsImageMemoryMask
|
|
|
|
| spv::MemorySemanticsUniformMemoryMask
|
|
|
|
| spv::MemorySemanticsAcquireReleaseMask;
|
2017-12-29 00:51:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flags.test(DxbcSyncFlag::UavMemoryGlobal)) {
|
|
|
|
memoryScope = spv::ScopeDevice;
|
2018-03-30 17:59:57 +02:00
|
|
|
memorySemantics |= spv::MemorySemanticsImageMemoryMask
|
|
|
|
| spv::MemorySemanticsUniformMemoryMask
|
|
|
|
| spv::MemorySemanticsAcquireReleaseMask;
|
2017-12-29 00:51:31 +01:00
|
|
|
}
|
|
|
|
|
2018-03-30 17:59:57 +02:00
|
|
|
if (executionScope != spv::ScopeInvocation) {
|
2017-12-29 00:51:31 +01:00
|
|
|
m_module.opControlBarrier(
|
|
|
|
m_module.constu32(executionScope),
|
|
|
|
m_module.constu32(memoryScope),
|
|
|
|
m_module.constu32(memorySemantics));
|
2018-08-17 10:17:27 +02:00
|
|
|
} else if (memoryScope != spv::ScopeInvocation) {
|
|
|
|
m_module.opMemoryBarrier(
|
|
|
|
m_module.constu32(memoryScope),
|
|
|
|
m_module.constu32(memorySemantics));
|
|
|
|
} else {
|
|
|
|
Logger::warn("DxbcCompiler: sync instruction has no effect");
|
2017-12-29 00:51:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-30 03:44:19 +01:00
|
|
|
void DxbcCompiler::emitBitExtract(const DxbcShaderInstruction& ins) {
|
|
|
|
// ibfe and ubfe take the following arguments:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Number of bits to extact
|
|
|
|
// (src1) Offset of the bits to extract
|
|
|
|
// (src2) Register to extract bits from
|
|
|
|
const bool isSigned = ins.op == DxbcOpcode::IBfe;
|
|
|
|
|
2018-09-03 01:01:44 +02:00
|
|
|
DxbcRegisterValue bitCnt = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
DxbcRegisterValue bitOfs = emitRegisterLoad(ins.src[1], ins.dst[0].mask);
|
|
|
|
|
|
|
|
if (ins.src[0].type != DxbcOperandType::Imm32)
|
|
|
|
bitCnt = emitRegisterMaskBits(bitCnt, 0x1F);
|
|
|
|
|
|
|
|
if (ins.src[1].type != DxbcOperandType::Imm32)
|
|
|
|
bitOfs = emitRegisterMaskBits(bitOfs, 0x1F);
|
2017-12-30 03:44:19 +01:00
|
|
|
|
|
|
|
const DxbcRegisterValue src = emitRegisterLoad(ins.src[2], ins.dst[0].mask);
|
|
|
|
|
2018-01-09 15:39:41 +01:00
|
|
|
const uint32_t componentCount = src.type.ccount;
|
|
|
|
std::array<uint32_t, 4> componentIds = {{ 0, 0, 0, 0 }};
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < componentCount; i++) {
|
|
|
|
const DxbcRegisterValue currBitCnt = emitRegisterExtract(bitCnt, DxbcRegMask::select(i));
|
|
|
|
const DxbcRegisterValue currBitOfs = emitRegisterExtract(bitOfs, DxbcRegMask::select(i));
|
|
|
|
const DxbcRegisterValue currSrc = emitRegisterExtract(src, DxbcRegMask::select(i));
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(currSrc.type);
|
|
|
|
|
|
|
|
componentIds[i] = isSigned
|
|
|
|
? m_module.opBitFieldSExtract(typeId, currSrc.id, currBitOfs.id, currBitCnt.id)
|
|
|
|
: m_module.opBitFieldUExtract(typeId, currSrc.id, currBitOfs.id, currBitCnt.id);
|
|
|
|
}
|
|
|
|
|
2017-12-30 03:44:19 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type = src.type;
|
2018-01-09 15:39:41 +01:00
|
|
|
result.id = componentCount > 1
|
|
|
|
? m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
componentCount, componentIds.data())
|
|
|
|
: componentIds[0];
|
2017-12-30 03:44:19 +01:00
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitBitInsert(const DxbcShaderInstruction& ins) {
|
|
|
|
// ibfe and ubfe take the following arguments:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Number of bits to extact
|
|
|
|
// (src1) Offset of the bits to extract
|
|
|
|
// (src2) Register to take bits from
|
|
|
|
// (src3) Register to replace bits in
|
2018-09-03 01:01:44 +02:00
|
|
|
DxbcRegisterValue bitCnt = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
DxbcRegisterValue bitOfs = emitRegisterLoad(ins.src[1], ins.dst[0].mask);
|
|
|
|
|
|
|
|
if (ins.src[0].type != DxbcOperandType::Imm32)
|
|
|
|
bitCnt = emitRegisterMaskBits(bitCnt, 0x1F);
|
|
|
|
|
|
|
|
if (ins.src[1].type != DxbcOperandType::Imm32)
|
|
|
|
bitOfs = emitRegisterMaskBits(bitOfs, 0x1F);
|
2017-12-30 03:44:19 +01:00
|
|
|
|
|
|
|
const DxbcRegisterValue insert = emitRegisterLoad(ins.src[2], ins.dst[0].mask);
|
|
|
|
const DxbcRegisterValue base = emitRegisterLoad(ins.src[3], ins.dst[0].mask);
|
|
|
|
|
2018-01-09 15:39:41 +01:00
|
|
|
const uint32_t componentCount = base.type.ccount;
|
|
|
|
std::array<uint32_t, 4> componentIds = {{ 0, 0, 0, 0 }};
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < componentCount; i++) {
|
|
|
|
const DxbcRegisterValue currBitCnt = emitRegisterExtract(bitCnt, DxbcRegMask::select(i));
|
|
|
|
const DxbcRegisterValue currBitOfs = emitRegisterExtract(bitOfs, DxbcRegMask::select(i));
|
|
|
|
const DxbcRegisterValue currInsert = emitRegisterExtract(insert, DxbcRegMask::select(i));
|
|
|
|
const DxbcRegisterValue currBase = emitRegisterExtract(base, DxbcRegMask::select(i));
|
|
|
|
|
|
|
|
componentIds[i] = m_module.opBitFieldInsert(
|
|
|
|
getVectorTypeId(currBase.type),
|
|
|
|
currBase.id, currInsert.id,
|
|
|
|
currBitOfs.id, currBitCnt.id);
|
|
|
|
}
|
|
|
|
|
2017-12-30 03:44:19 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type = base.type;
|
2018-01-09 15:39:41 +01:00
|
|
|
result.id = componentCount > 1
|
|
|
|
? m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
componentCount, componentIds.data())
|
|
|
|
: componentIds[0];
|
2017-12-30 03:44:19 +01:00
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
2018-05-12 01:39:23 +02:00
|
|
|
|
|
|
|
void DxbcCompiler::emitBitScan(const DxbcShaderInstruction& ins) {
|
|
|
|
// firstbit(lo|hi|shi) have two operands:
|
|
|
|
// (dst0) The destination operant
|
|
|
|
// (src0) Source operand to scan
|
|
|
|
DxbcRegisterValue src = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
|
|
|
|
DxbcRegisterValue dst;
|
|
|
|
dst.type.ctype = ins.dst[0].dataType;
|
|
|
|
dst.type.ccount = ins.dst[0].mask.popCount();
|
|
|
|
|
|
|
|
// Result type, should be an unsigned integer
|
|
|
|
const uint32_t typeId = getVectorTypeId(dst.type);
|
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::FirstBitLo: dst.id = m_module.opFindILsb(typeId, src.id); break;
|
|
|
|
case DxbcOpcode::FirstBitHi: dst.id = m_module.opFindUMsb(typeId, src.id); break;
|
|
|
|
case DxbcOpcode::FirstBitShi: dst.id = m_module.opFindSMsb(typeId, src.id); break;
|
|
|
|
default: Logger::warn(str::format("DxbcCompiler: Unhandled instruction: ", ins.op)); return;
|
|
|
|
}
|
2017-12-30 03:44:19 +01:00
|
|
|
|
2018-05-12 01:39:23 +02:00
|
|
|
// The 'Hi' variants are counted from the MSB in DXBC
|
|
|
|
// rather than the LSB, so we have to invert the number
|
|
|
|
if (ins.op == DxbcOpcode::FirstBitHi
|
|
|
|
|| ins.op == DxbcOpcode::FirstBitShi) {
|
|
|
|
dst.id = m_module.opSelect(typeId,
|
|
|
|
m_module.opINotEqual(m_module.defBoolType(),
|
|
|
|
dst.id, m_module.constu32(0xFFFFFFFF)),
|
|
|
|
m_module.opISub(typeId, m_module.constu32(31), dst.id),
|
|
|
|
m_module.constu32(0xFFFFFFFF));
|
|
|
|
}
|
|
|
|
|
|
|
|
// No modifiers are supported
|
|
|
|
emitRegisterStore(ins.dst[0], dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-30 01:26:37 +01:00
|
|
|
void DxbcCompiler::emitBufferQuery(const DxbcShaderInstruction& ins) {
|
|
|
|
// bufinfo takes two arguments
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) The buffer register to query
|
2018-01-10 18:58:17 +01:00
|
|
|
// TODO Check if resource is bound
|
2017-12-30 01:26:37 +01:00
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(ins.src[0]);
|
|
|
|
|
|
|
|
// We'll store this as a scalar unsigned integer
|
2018-01-02 16:57:37 +01:00
|
|
|
DxbcRegisterValue result = emitQueryTexelBufferSize(ins.src[0]);
|
2017-12-30 01:26:37 +01:00
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
|
|
|
|
2018-01-02 16:57:37 +01:00
|
|
|
// Adjust returned size if this is a raw or structured
|
|
|
|
// buffer, as emitQueryTexelBufferSize only returns the
|
|
|
|
// number of typed elements in the buffer.
|
2017-12-30 01:26:37 +01:00
|
|
|
if (bufferInfo.type == DxbcResourceType::Raw) {
|
|
|
|
result.id = m_module.opIMul(typeId,
|
|
|
|
result.id, m_module.constu32(4));
|
|
|
|
} else if (bufferInfo.type == DxbcResourceType::Structured) {
|
|
|
|
result.id = m_module.opUDiv(typeId, result.id,
|
|
|
|
m_module.constu32(bufferInfo.stride / 4));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the result. The scalar will be extended to a
|
|
|
|
// vector if the write mask consists of more than one
|
|
|
|
// component, which is the desired behaviour.
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
2018-01-10 13:44:04 +01:00
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitBufferLoad(const DxbcShaderInstruction& ins) {
|
|
|
|
// ld_raw takes three arguments:
|
|
|
|
// (dst0) Destination register
|
|
|
|
// (src0) Byte offset
|
|
|
|
// (src1) Source register
|
|
|
|
// ld_structured takes four arguments:
|
|
|
|
// (dst0) Destination register
|
|
|
|
// (src0) Structure index
|
|
|
|
// (src1) Byte offset
|
|
|
|
// (src2) Source register
|
2018-01-10 18:58:17 +01:00
|
|
|
// TODO Check if resource is bound
|
2017-12-28 16:03:17 +01:00
|
|
|
const bool isStructured = ins.op == DxbcOpcode::LdStructured;
|
|
|
|
|
|
|
|
// Source register. The exact way we access
|
|
|
|
// the data depends on the register type.
|
|
|
|
const DxbcRegister& dstReg = ins.dst[0];
|
|
|
|
const DxbcRegister& srcReg = isStructured ? ins.src[2] : ins.src[1];
|
|
|
|
|
|
|
|
// Retrieve common info about the buffer
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(srcReg);
|
|
|
|
|
|
|
|
// Compute element index
|
|
|
|
const DxbcRegisterValue elementIndex = isStructured
|
|
|
|
? emitCalcBufferIndexStructured(
|
|
|
|
emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)),
|
|
|
|
emitRegisterLoad(ins.src[1], DxbcRegMask(true, false, false, false)),
|
|
|
|
bufferInfo.stride)
|
|
|
|
: emitCalcBufferIndexRaw(
|
|
|
|
emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)));
|
|
|
|
|
|
|
|
emitRegisterStore(dstReg,
|
|
|
|
emitRawBufferLoad(srcReg, elementIndex, dstReg.mask));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitBufferStore(const DxbcShaderInstruction& ins) {
|
2017-12-28 18:37:02 +01:00
|
|
|
// store_raw takes three arguments:
|
|
|
|
// (dst0) Destination register
|
|
|
|
// (src0) Byte offset
|
|
|
|
// (src1) Source register
|
|
|
|
// store_structured takes four arguments:
|
|
|
|
// (dst0) Destination register
|
|
|
|
// (src0) Structure index
|
|
|
|
// (src1) Byte offset
|
|
|
|
// (src2) Source register
|
2018-01-10 18:58:17 +01:00
|
|
|
// TODO Check if resource is bound
|
2017-12-28 18:37:02 +01:00
|
|
|
const bool isStructured = ins.op == DxbcOpcode::StoreStructured;
|
|
|
|
|
|
|
|
// Source register. The exact way we access
|
|
|
|
// the data depends on the register type.
|
|
|
|
const DxbcRegister& dstReg = ins.dst[0];
|
|
|
|
const DxbcRegister& srcReg = isStructured ? ins.src[2] : ins.src[1];
|
|
|
|
|
|
|
|
// Retrieve common info about the buffer
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(dstReg);
|
|
|
|
|
|
|
|
// Compute element index
|
|
|
|
const DxbcRegisterValue elementIndex = isStructured
|
|
|
|
? emitCalcBufferIndexStructured(
|
|
|
|
emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)),
|
|
|
|
emitRegisterLoad(ins.src[1], DxbcRegMask(true, false, false, false)),
|
|
|
|
bufferInfo.stride)
|
|
|
|
: emitCalcBufferIndexRaw(
|
|
|
|
emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)));
|
|
|
|
|
|
|
|
emitRawBufferStore(dstReg, elementIndex,
|
|
|
|
emitRegisterLoad(srcReg, dstReg.mask));
|
2017-12-28 16:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-30 13:18:31 +01:00
|
|
|
void DxbcCompiler::emitConvertFloat16(const DxbcShaderInstruction& ins) {
|
|
|
|
// f32tof16 takes two operands:
|
|
|
|
// (dst0) Destination register as a uint32 vector
|
|
|
|
// (src0) Source register as a float32 vector
|
|
|
|
// f16tof32 takes two operands:
|
|
|
|
// (dst0) Destination register as a float32 vector
|
|
|
|
// (src0) Source register as a uint32 vector
|
|
|
|
const DxbcRegisterValue src = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
|
|
|
|
// We handle both packing and unpacking here
|
|
|
|
const bool isPack = ins.op == DxbcOpcode::F32toF16;
|
|
|
|
|
|
|
|
// The conversion instructions do not map very well to the
|
|
|
|
// SPIR-V pack instructions, which operate on 2D vectors.
|
|
|
|
std::array<uint32_t, 4> scalarIds = {{ 0, 0, 0, 0 }};
|
|
|
|
|
2018-01-10 09:16:42 +01:00
|
|
|
const uint32_t componentCount = src.type.ccount;
|
2017-12-30 13:18:31 +01:00
|
|
|
|
|
|
|
// These types are used in both pack and unpack operations
|
|
|
|
const uint32_t t_u32 = getVectorTypeId({ DxbcScalarType::Uint32, 1 });
|
|
|
|
const uint32_t t_f32 = getVectorTypeId({ DxbcScalarType::Float32, 1 });
|
|
|
|
const uint32_t t_f32v2 = getVectorTypeId({ DxbcScalarType::Float32, 2 });
|
|
|
|
|
|
|
|
// Constant zero-bit pattern, used for packing
|
|
|
|
const uint32_t zerof32 = isPack ? m_module.constf32(0.0f) : 0;
|
|
|
|
|
2018-01-10 09:16:42 +01:00
|
|
|
for (uint32_t i = 0; i < componentCount; i++) {
|
|
|
|
const DxbcRegisterValue componentValue
|
|
|
|
= emitRegisterExtract(src, DxbcRegMask::select(i));
|
|
|
|
|
|
|
|
if (isPack) { // f32tof16
|
|
|
|
const std::array<uint32_t, 2> packIds =
|
|
|
|
{{ componentValue.id, zerof32 }};
|
2017-12-30 13:18:31 +01:00
|
|
|
|
2018-01-10 09:16:42 +01:00
|
|
|
scalarIds[i] = m_module.opPackHalf2x16(t_u32,
|
|
|
|
m_module.opCompositeConstruct(t_f32v2, packIds.size(), packIds.data()));
|
|
|
|
} else { // f16tof32
|
|
|
|
const uint32_t zeroIndex = 0;
|
2017-12-30 13:18:31 +01:00
|
|
|
|
2018-01-10 09:16:42 +01:00
|
|
|
scalarIds[i] = m_module.opCompositeExtract(t_f32,
|
|
|
|
m_module.opUnpackHalf2x16(t_f32v2, componentValue.id),
|
|
|
|
1, &zeroIndex);
|
2017-12-30 13:18:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store result in the destination register
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = ins.dst[0].dataType;
|
2018-01-10 09:16:42 +01:00
|
|
|
result.type.ccount = componentCount;
|
|
|
|
result.id = componentCount > 1
|
2017-12-30 13:18:31 +01:00
|
|
|
? m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
2018-01-10 09:16:42 +01:00
|
|
|
componentCount, scalarIds.data())
|
|
|
|
: scalarIds[0];
|
2017-12-30 13:18:31 +01:00
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
2018-06-07 14:32:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitConvertFloat64(const DxbcShaderInstruction& ins) {
|
|
|
|
// ftod and dtof take the following operands:
|
|
|
|
// (dst0) Destination operand
|
|
|
|
// (src0) Number to convert
|
|
|
|
uint32_t dstBits = ins.dst[0].mask.popCount();
|
|
|
|
|
|
|
|
DxbcRegMask srcMask = isDoubleType(ins.dst[0].dataType)
|
2018-06-07 16:02:59 +02:00
|
|
|
? DxbcRegMask(dstBits >= 2, dstBits >= 4, false, false)
|
2018-06-07 14:32:56 +02:00
|
|
|
: DxbcRegMask(dstBits >= 1, dstBits >= 1, dstBits >= 2, dstBits >= 2);
|
|
|
|
|
|
|
|
// Perform actual conversion, destination modifiers are not applied
|
|
|
|
DxbcRegisterValue val = emitRegisterLoad(ins.src[0], srcMask);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = ins.dst[0].dataType;
|
|
|
|
result.type.ccount = val.type.ccount;
|
2018-08-15 20:10:45 +02:00
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::DtoF:
|
|
|
|
case DxbcOpcode::FtoD:
|
|
|
|
result.id = m_module.opFConvert(
|
|
|
|
getVectorTypeId(result.type), val.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DtoI:
|
|
|
|
result.id = m_module.opConvertFtoS(
|
|
|
|
getVectorTypeId(result.type), val.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DtoU:
|
|
|
|
result.id = m_module.opConvertFtoU(
|
|
|
|
getVectorTypeId(result.type), val.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::ItoD:
|
|
|
|
result.id = m_module.opConvertStoF(
|
|
|
|
getVectorTypeId(result.type), val.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::UtoD:
|
|
|
|
result.id = m_module.opConvertUtoF(
|
|
|
|
getVectorTypeId(result.type), val.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format("DxbcCompiler: Unhandled instruction: ", ins.op));
|
|
|
|
return;
|
|
|
|
}
|
2018-06-07 14:32:56 +02:00
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
2017-12-30 13:18:31 +01:00
|
|
|
|
|
|
|
|
2018-03-01 12:08:06 +01:00
|
|
|
void DxbcCompiler::emitHullShaderInstCnt(const DxbcShaderInstruction& ins) {
|
|
|
|
this->getCurrentHsForkJoinPhase()->instanceCount = ins.imm[0].u32;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-01 09:26:17 +01:00
|
|
|
void DxbcCompiler::emitHullShaderPhase(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::HsDecls: {
|
|
|
|
if (m_hs.currPhaseType != DxbcCompilerHsPhase::None)
|
|
|
|
Logger::err("DXBC: HsDecls not the first phase in hull shader");
|
|
|
|
|
|
|
|
m_hs.currPhaseType = DxbcCompilerHsPhase::Decl;
|
|
|
|
} break;
|
|
|
|
|
2018-03-05 17:23:00 +01:00
|
|
|
case DxbcOpcode::HsControlPointPhase: {
|
|
|
|
m_hs.cpPhase = this->emitNewHullShaderControlPointPhase();
|
|
|
|
|
|
|
|
m_hs.currPhaseType = DxbcCompilerHsPhase::ControlPoint;
|
|
|
|
m_hs.currPhaseId = 0;
|
|
|
|
|
|
|
|
m_module.setDebugName(m_hs.cpPhase.functionId, "hs_control_point");
|
|
|
|
} break;
|
|
|
|
|
2018-03-01 09:26:17 +01:00
|
|
|
case DxbcOpcode::HsForkPhase: {
|
|
|
|
auto phase = this->emitNewHullShaderForkJoinPhase();
|
|
|
|
m_hs.forkPhases.push_back(phase);
|
|
|
|
|
|
|
|
m_hs.currPhaseType = DxbcCompilerHsPhase::Fork;
|
|
|
|
m_hs.currPhaseId = m_hs.forkPhases.size() - 1;
|
|
|
|
|
|
|
|
m_module.setDebugName(phase.functionId,
|
|
|
|
str::format("hs_fork_", m_hs.currPhaseId).c_str());
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOpcode::HsJoinPhase: {
|
|
|
|
auto phase = this->emitNewHullShaderForkJoinPhase();
|
|
|
|
m_hs.joinPhases.push_back(phase);
|
|
|
|
|
|
|
|
m_hs.currPhaseType = DxbcCompilerHsPhase::Join;
|
|
|
|
m_hs.currPhaseId = m_hs.joinPhases.size() - 1;
|
|
|
|
|
|
|
|
m_module.setDebugName(phase.functionId,
|
|
|
|
str::format("hs_join_", m_hs.currPhaseId).c_str());
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-26 16:46:34 +01:00
|
|
|
void DxbcCompiler::emitInterpolate(const DxbcShaderInstruction& ins) {
|
|
|
|
// The SPIR-V instructions operate on input variable pointers,
|
|
|
|
// which are all declared as four-component float vectors.
|
|
|
|
const uint32_t registerId = ins.src[0].idx[0].offset;
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float32;
|
|
|
|
result.type.ccount = 4;
|
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::EvalCentroid: {
|
|
|
|
result.id = m_module.opInterpolateAtCentroid(
|
|
|
|
getVectorTypeId(result.type),
|
2018-07-01 12:44:37 +02:00
|
|
|
m_vRegs.at(registerId).id);
|
2018-02-26 16:46:34 +01:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOpcode::EvalSampleIndex: {
|
|
|
|
const DxbcRegisterValue sampleIndex = emitRegisterLoad(
|
|
|
|
ins.src[1], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
result.id = m_module.opInterpolateAtSample(
|
|
|
|
getVectorTypeId(result.type),
|
2018-07-01 12:44:37 +02:00
|
|
|
m_vRegs.at(registerId).id,
|
2018-02-26 16:46:34 +01:00
|
|
|
sampleIndex.id);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOpcode::EvalSnapped: {
|
|
|
|
const DxbcRegisterValue offset = emitRegisterLoad(
|
|
|
|
ins.src[1], DxbcRegMask(true, true, false, false));
|
|
|
|
|
|
|
|
result.id = m_module.opInterpolateAtOffset(
|
|
|
|
getVectorTypeId(result.type),
|
2018-07-01 12:44:37 +02:00
|
|
|
m_vRegs.at(registerId).id,
|
2018-02-26 16:46:34 +01:00
|
|
|
offset.id);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
ins.src[0].swizzle, ins.dst[0].mask);
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
void DxbcCompiler::emitTextureQuery(const DxbcShaderInstruction& ins) {
|
|
|
|
// resinfo has three operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Resource LOD to query
|
|
|
|
// (src1) Resource to query
|
2018-01-10 18:58:17 +01:00
|
|
|
// TODO Check if resource is bound
|
2018-01-11 21:39:17 +01:00
|
|
|
const DxbcBufferInfo resourceInfo = getBufferInfo(ins.src[1]);
|
2018-04-10 08:01:54 +02:00
|
|
|
const DxbcResinfoType resinfoType = ins.controls.resinfoType();
|
2017-12-27 01:37:15 +01:00
|
|
|
|
|
|
|
// Read the exact LOD for the image query
|
|
|
|
const DxbcRegisterValue mipLod = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
const DxbcScalarType returnType = resinfoType == DxbcResinfoType::Uint
|
|
|
|
? DxbcScalarType::Uint32 : DxbcScalarType::Float32;
|
|
|
|
|
2018-02-04 18:08:18 +01:00
|
|
|
// Query the size of the selected mip level, as well as the
|
|
|
|
// total number of mip levels. We will have to combine the
|
|
|
|
// result into a four-component vector later.
|
|
|
|
DxbcRegisterValue imageSize = emitQueryTextureSize(ins.src[1], mipLod);
|
|
|
|
DxbcRegisterValue imageLevels = emitQueryTextureLods(ins.src[1]);
|
2017-12-27 01:37:15 +01:00
|
|
|
|
|
|
|
// Convert intermediates to the requested type
|
|
|
|
if (returnType == DxbcScalarType::Float32) {
|
|
|
|
imageSize.type.ctype = DxbcScalarType::Float32;
|
|
|
|
imageSize.id = m_module.opConvertUtoF(
|
|
|
|
getVectorTypeId(imageSize.type),
|
|
|
|
imageSize.id);
|
|
|
|
|
|
|
|
imageLevels.type.ctype = DxbcScalarType::Float32;
|
|
|
|
imageLevels.id = m_module.opConvertUtoF(
|
|
|
|
getVectorTypeId(imageLevels.type),
|
|
|
|
imageLevels.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the selected return type is rcpFloat, we need
|
|
|
|
// to compute the reciprocal of the image dimensions,
|
|
|
|
// but not the array size, so we need to separate it.
|
2018-02-04 18:08:18 +01:00
|
|
|
const uint32_t imageCoordDim = imageSize.type.ccount;
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
DxbcRegisterValue imageLayers;
|
|
|
|
imageLayers.type = imageSize.type;
|
|
|
|
imageLayers.id = 0;
|
|
|
|
|
2018-01-11 21:39:17 +01:00
|
|
|
if (resinfoType == DxbcResinfoType::RcpFloat && resourceInfo.image.array) {
|
2018-02-04 18:08:18 +01:00
|
|
|
imageLayers = emitRegisterExtract(imageSize, DxbcRegMask::select(imageCoordDim - 1));
|
|
|
|
imageSize = emitRegisterExtract(imageSize, DxbcRegMask::firstN(imageCoordDim - 1));
|
2017-12-27 01:37:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (resinfoType == DxbcResinfoType::RcpFloat) {
|
2018-03-12 12:25:10 +01:00
|
|
|
imageSize.id = m_module.opFDiv(
|
|
|
|
getVectorTypeId(imageSize.type),
|
2018-04-22 16:43:16 +02:00
|
|
|
emitBuildConstVecf32(1.0f, 1.0f, 1.0f, 1.0f,
|
|
|
|
DxbcRegMask::firstN(imageSize.type.ccount)).id,
|
2017-12-27 01:37:15 +01:00
|
|
|
imageSize.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Concatenate result vectors and scalars to form a
|
|
|
|
// 4D vector. Unused components will be set to zero.
|
|
|
|
std::array<uint32_t, 4> vectorIds = { imageSize.id, 0, 0, 0 };
|
|
|
|
uint32_t numVectorIds = 1;
|
|
|
|
|
|
|
|
if (imageLayers.id != 0)
|
|
|
|
vectorIds[numVectorIds++] = imageLayers.id;
|
|
|
|
|
2018-02-04 18:08:18 +01:00
|
|
|
if (imageCoordDim < 3) {
|
2017-12-27 01:37:15 +01:00
|
|
|
const uint32_t zero = returnType == DxbcScalarType::Uint32
|
|
|
|
? m_module.constu32(0)
|
|
|
|
: m_module.constf32(0.0f);
|
|
|
|
|
2018-02-04 18:08:18 +01:00
|
|
|
for (uint32_t i = imageCoordDim; i < 3; i++)
|
2017-12-27 01:37:15 +01:00
|
|
|
vectorIds[numVectorIds++] = zero;
|
|
|
|
}
|
|
|
|
|
|
|
|
vectorIds[numVectorIds++] = imageLevels.id;
|
|
|
|
|
|
|
|
// Create the actual result vector
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = returnType;
|
|
|
|
result.type.ccount = 4;
|
|
|
|
result.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
numVectorIds, vectorIds.data());
|
|
|
|
|
|
|
|
// Swizzle components using the resource swizzle
|
|
|
|
// and the destination operand's write mask
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
ins.src[1].swizzle, ins.dst[0].mask);
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-04 22:41:23 +01:00
|
|
|
void DxbcCompiler::emitTextureQueryLod(const DxbcShaderInstruction& ins) {
|
|
|
|
// All sample instructions have at least these operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Texture coordinates
|
|
|
|
// (src1) The texture itself
|
|
|
|
// (src2) The sampler object
|
|
|
|
const DxbcRegister& texCoordReg = ins.src[0];
|
|
|
|
const DxbcRegister& textureReg = ins.src[1];
|
|
|
|
const DxbcRegister& samplerReg = ins.src[2];
|
|
|
|
|
|
|
|
// Texture and sampler register IDs
|
|
|
|
const uint32_t textureId = textureReg.idx[0].offset;
|
|
|
|
const uint32_t samplerId = samplerReg.idx[0].offset;
|
|
|
|
|
2018-04-21 18:09:29 +02:00
|
|
|
// Load texture coordinates
|
2018-04-22 14:08:01 +02:00
|
|
|
const DxbcRegisterValue coord = emitRegisterLoad(texCoordReg,
|
|
|
|
DxbcRegMask::firstN(getTexLayerDim(m_textures.at(textureId).imageInfo)));
|
2018-02-04 22:41:23 +01:00
|
|
|
|
|
|
|
// Query the LOD. The result is a two-dimensional float32
|
|
|
|
// vector containing the mip level and virtual LOD numbers.
|
|
|
|
const uint32_t sampledImageId = emitLoadSampledImage(
|
|
|
|
m_textures.at(textureId), m_samplers.at(samplerId), false);
|
|
|
|
|
|
|
|
const uint32_t queriedLodId = m_module.opImageQueryLod(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Float32, 2 }),
|
|
|
|
sampledImageId, coord.id);
|
|
|
|
|
|
|
|
// Build the result array vector by filling up
|
|
|
|
// the remaining two components with zeroes.
|
|
|
|
const uint32_t zero = m_module.constf32(0.0f);
|
|
|
|
const std::array<uint32_t, 3> resultIds
|
|
|
|
= {{ queriedLodId, zero, zero }};
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type = DxbcVectorType { DxbcScalarType::Float32, 4 };
|
|
|
|
result.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
resultIds.size(), resultIds.data());
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-04 19:30:39 +01:00
|
|
|
void DxbcCompiler::emitTextureQueryMs(const DxbcShaderInstruction& ins) {
|
|
|
|
// sampleinfo has two operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Resource to query
|
|
|
|
// TODO Check if resource is bound
|
|
|
|
DxbcRegisterValue sampleCount = emitQueryTextureSamples(ins.src[0]);
|
|
|
|
|
2018-04-10 08:01:54 +02:00
|
|
|
if (ins.controls.returnType() != DxbcInstructionReturnType::Uint) {
|
2018-04-09 22:52:59 +02:00
|
|
|
sampleCount.type = { DxbcScalarType::Float32, 1 };
|
2018-02-04 19:30:39 +01:00
|
|
|
sampleCount.id = m_module.opConvertUtoF(
|
|
|
|
getVectorTypeId(sampleCount.type),
|
|
|
|
sampleCount.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], sampleCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-12 12:25:10 +01:00
|
|
|
void DxbcCompiler::emitTextureQueryMsPos(const DxbcShaderInstruction& ins) {
|
|
|
|
// samplepos has three operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Resource to query
|
|
|
|
// (src1) Sample index
|
|
|
|
// TODO Check if resource is bound
|
|
|
|
if (m_samplePositions == 0)
|
|
|
|
m_samplePositions = emitSamplePosArray();
|
|
|
|
|
|
|
|
// The lookup index is qual to the sample count plus the
|
|
|
|
// sample index, or 0 if the resource cannot be queried.
|
|
|
|
DxbcRegisterValue sampleCount = emitQueryTextureSamples(ins.src[0]);
|
|
|
|
DxbcRegisterValue sampleIndex = emitRegisterLoad(
|
|
|
|
ins.src[1], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
uint32_t lookupIndex = m_module.opIAdd(
|
|
|
|
getVectorTypeId(sampleCount.type),
|
|
|
|
sampleCount.id, sampleIndex.id);
|
|
|
|
|
|
|
|
// Validate the parameters
|
|
|
|
uint32_t sampleCountValid = m_module.opULessThanEqual(
|
|
|
|
m_module.defBoolType(),
|
|
|
|
sampleCount.id,
|
|
|
|
m_module.constu32(16));
|
|
|
|
|
|
|
|
uint32_t sampleIndexValid = m_module.opULessThan(
|
|
|
|
m_module.defBoolType(),
|
|
|
|
sampleIndex.id,
|
|
|
|
sampleCount.id);
|
|
|
|
|
|
|
|
// If the lookup cannot be performed, set the lookup
|
|
|
|
// index to zero, which will return a zero vector.
|
|
|
|
lookupIndex = m_module.opSelect(
|
|
|
|
getVectorTypeId(sampleCount.type),
|
|
|
|
m_module.opLogicalAnd(
|
|
|
|
m_module.defBoolType(),
|
|
|
|
sampleCountValid,
|
|
|
|
sampleIndexValid),
|
|
|
|
lookupIndex,
|
|
|
|
m_module.constu32(0));
|
|
|
|
|
|
|
|
// Load sample pos vector and write the masked
|
|
|
|
// components to the destination register.
|
|
|
|
DxbcRegisterPointer samplePos;
|
|
|
|
samplePos.type.ctype = DxbcScalarType::Float32;
|
|
|
|
samplePos.type.ccount = 4;
|
|
|
|
samplePos.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(samplePos.type),
|
|
|
|
spv::StorageClassPrivate),
|
|
|
|
m_samplePositions, 1, &lookupIndex);
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0],
|
|
|
|
emitRegisterSwizzle(
|
|
|
|
emitValueLoad(samplePos),
|
|
|
|
ins.src[0].swizzle,
|
|
|
|
ins.dst[0].mask));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
void DxbcCompiler::emitTextureFetch(const DxbcShaderInstruction& ins) {
|
|
|
|
// ld has three operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Source address
|
|
|
|
// (src1) Source texture
|
2018-01-16 22:39:30 +01:00
|
|
|
// ld2dms has four operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Source address
|
|
|
|
// (src1) Source texture
|
|
|
|
// (src2) Sample number
|
2017-12-27 01:37:15 +01:00
|
|
|
const uint32_t textureId = ins.src[1].idx[0].offset;
|
|
|
|
|
|
|
|
// Image type, which stores the image dimensions etc.
|
2018-04-21 18:09:29 +02:00
|
|
|
const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo;
|
|
|
|
const uint32_t imageLayerDim = getTexLayerDim(imageType);
|
2017-12-27 01:37:15 +01:00
|
|
|
|
|
|
|
// Load the texture coordinates. The last component
|
|
|
|
// contains the LOD if the resource is an image.
|
2018-01-02 16:57:37 +01:00
|
|
|
const DxbcRegisterValue address = emitRegisterLoad(
|
2017-12-27 01:37:15 +01:00
|
|
|
ins.src[0], DxbcRegMask(true, true, true, true));
|
|
|
|
|
|
|
|
// Additional image operands. This will store
|
|
|
|
// the LOD and the address offset if present.
|
|
|
|
SpirvImageOperands imageOperands;
|
|
|
|
|
|
|
|
if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) {
|
|
|
|
const std::array<uint32_t, 3> offsetIds = {
|
|
|
|
imageLayerDim >= 1 ? m_module.consti32(ins.sampleControls.u) : 0,
|
|
|
|
imageLayerDim >= 2 ? m_module.consti32(ins.sampleControls.v) : 0,
|
|
|
|
imageLayerDim >= 3 ? m_module.consti32(ins.sampleControls.w) : 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
imageOperands.flags |= spv::ImageOperandsConstOffsetMask;
|
|
|
|
imageOperands.sConstOffset = m_module.constComposite(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }),
|
|
|
|
imageLayerDim, offsetIds.data());
|
|
|
|
}
|
|
|
|
|
2018-01-19 09:09:38 +01:00
|
|
|
// The LOD is not present when reading from
|
|
|
|
// a buffer or from a multisample texture.
|
|
|
|
if (imageType.dim != spv::DimBuffer && imageType.ms == 0) {
|
2018-01-16 22:39:30 +01:00
|
|
|
DxbcRegisterValue imageLod = emitRegisterExtract(
|
|
|
|
address, DxbcRegMask(false, false, false, true));
|
2018-01-02 16:57:37 +01:00
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
imageOperands.flags |= spv::ImageOperandsLodMask;
|
2018-01-02 16:57:37 +01:00
|
|
|
imageOperands.sLod = imageLod.id;
|
2017-12-27 01:37:15 +01:00
|
|
|
}
|
|
|
|
|
2018-01-19 09:09:38 +01:00
|
|
|
// The ld2ms instruction has a sample index, but we
|
|
|
|
// are only allowed to set it for multisample views
|
2018-01-16 22:39:30 +01:00
|
|
|
if (ins.op == DxbcOpcode::LdMs && imageType.ms == 1) {
|
|
|
|
DxbcRegisterValue sampleId = emitRegisterLoad(
|
|
|
|
ins.src[2], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
imageOperands.flags |= spv::ImageOperandsSampleMask;
|
|
|
|
imageOperands.sSampleId = sampleId.id;
|
|
|
|
}
|
|
|
|
|
2018-01-02 16:57:37 +01:00
|
|
|
// Extract coordinates from address
|
2018-04-21 18:09:29 +02:00
|
|
|
const DxbcRegisterValue coord = emitCalcTexCoord(address, imageType);
|
2018-01-02 16:57:37 +01:00
|
|
|
|
2018-01-10 18:58:17 +01:00
|
|
|
// Fetch texels only if the resource is actually bound
|
|
|
|
const uint32_t labelMerge = m_module.allocateId();
|
|
|
|
const uint32_t labelBound = m_module.allocateId();
|
|
|
|
const uint32_t labelUnbound = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(labelMerge, spv::SelectionControlMaskNone);
|
|
|
|
m_module.opBranchConditional(m_textures.at(textureId).specId, labelBound, labelUnbound);
|
|
|
|
m_module.opLabel(labelBound);
|
|
|
|
|
|
|
|
// Reading a typed image or buffer view
|
|
|
|
// always returns a four-component vector.
|
2018-01-03 02:37:44 +01:00
|
|
|
const uint32_t imageId = m_module.opLoad(
|
|
|
|
m_textures.at(textureId).imageTypeId,
|
|
|
|
m_textures.at(textureId).varId);
|
2017-12-27 01:37:15 +01:00
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = m_textures.at(textureId).sampledType;
|
|
|
|
result.type.ccount = 4;
|
2018-01-03 02:37:44 +01:00
|
|
|
result.id = m_module.opImageFetch(
|
2017-12-27 01:37:15 +01:00
|
|
|
getVectorTypeId(result.type), imageId,
|
2018-01-02 16:57:37 +01:00
|
|
|
coord.id, imageOperands);
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
// Swizzle components using the texture swizzle
|
|
|
|
// and the destination operand's write mask
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
ins.src[1].swizzle, ins.dst[0].mask);
|
2018-03-06 14:00:03 +01:00
|
|
|
|
2018-01-10 18:58:17 +01:00
|
|
|
// If the texture is not bound, return zeroes
|
|
|
|
m_module.opBranch(labelMerge);
|
|
|
|
m_module.opLabel(labelUnbound);
|
|
|
|
|
|
|
|
DxbcRegisterValue zeroes = [&] {
|
|
|
|
switch (result.type.ctype) {
|
|
|
|
case DxbcScalarType::Float32: return emitBuildConstVecf32(0.0f, 0.0f, 0.0f, 0.0f, ins.dst[0].mask);
|
|
|
|
case DxbcScalarType::Uint32: return emitBuildConstVecu32(0u, 0u, 0u, 0u, ins.dst[0].mask);
|
|
|
|
case DxbcScalarType::Sint32: return emitBuildConstVeci32(0, 0, 0, 0, ins.dst[0].mask);
|
|
|
|
default: throw DxvkError("DxbcCompiler: Invalid scalar type");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
m_module.opBranch(labelMerge);
|
|
|
|
m_module.opLabel(labelMerge);
|
|
|
|
|
|
|
|
// Merge the result with a phi function
|
|
|
|
const std::array<SpirvPhiLabel, 2> phiLabels = {{
|
|
|
|
{ result.id, labelBound },
|
|
|
|
{ zeroes.id, labelUnbound },
|
|
|
|
}};
|
|
|
|
|
|
|
|
DxbcRegisterValue mergedResult;
|
|
|
|
mergedResult.type = result.type;
|
|
|
|
mergedResult.id = m_module.opPhi(
|
|
|
|
getVectorTypeId(mergedResult.type),
|
|
|
|
phiLabels.size(), phiLabels.data());
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], mergedResult);
|
2017-12-27 01:37:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
void DxbcCompiler::emitTextureGather(const DxbcShaderInstruction& ins) {
|
|
|
|
// Gather4 takes the following operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Texture coordinates
|
|
|
|
// (src1) The texture itself
|
|
|
|
// (src2) The sampler, with a component selector
|
|
|
|
// Gather4C takes the following additional operand:
|
|
|
|
// (src3) The depth reference value
|
2018-02-04 17:40:02 +01:00
|
|
|
// The Gather4Po variants take an additional operand
|
|
|
|
// which defines an extended constant offset.
|
2018-01-17 02:12:29 +01:00
|
|
|
// TODO reduce code duplication by moving some common code
|
|
|
|
// in both sample() and gather() into separate methods
|
2018-02-04 17:40:02 +01:00
|
|
|
const bool isExtendedGather = ins.op == DxbcOpcode::Gather4Po
|
|
|
|
|| ins.op == DxbcOpcode::Gather4PoC;
|
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
const DxbcRegister& texCoordReg = ins.src[0];
|
2018-02-04 17:40:02 +01:00
|
|
|
const DxbcRegister& textureReg = ins.src[1 + isExtendedGather];
|
|
|
|
const DxbcRegister& samplerReg = ins.src[2 + isExtendedGather];
|
2018-01-17 02:12:29 +01:00
|
|
|
|
|
|
|
// Texture and sampler register IDs
|
|
|
|
const uint32_t textureId = textureReg.idx[0].offset;
|
|
|
|
const uint32_t samplerId = samplerReg.idx[0].offset;
|
|
|
|
|
|
|
|
// Image type, which stores the image dimensions etc.
|
|
|
|
const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo;
|
|
|
|
const uint32_t imageLayerDim = getTexLayerDim(imageType);
|
|
|
|
|
|
|
|
// Load the texture coordinates. SPIR-V allows these
|
|
|
|
// to be float4 even if not all components are used.
|
2018-04-21 18:09:29 +02:00
|
|
|
DxbcRegisterValue coord = emitLoadTexCoord(texCoordReg, imageType);
|
2018-01-17 02:12:29 +01:00
|
|
|
|
|
|
|
// Load reference value for depth-compare operations
|
2018-02-04 17:40:02 +01:00
|
|
|
const bool isDepthCompare = ins.op == DxbcOpcode::Gather4C
|
|
|
|
|| ins.op == DxbcOpcode::Gather4PoC;
|
2018-01-17 02:12:29 +01:00
|
|
|
|
|
|
|
const DxbcRegisterValue referenceValue = isDepthCompare
|
2018-02-04 17:40:02 +01:00
|
|
|
? emitRegisterLoad(ins.src[3 + isExtendedGather],
|
|
|
|
DxbcRegMask(true, false, false, false))
|
2018-01-17 02:12:29 +01:00
|
|
|
: DxbcRegisterValue();
|
|
|
|
|
|
|
|
// Determine the sampled image type based on the opcode.
|
|
|
|
const uint32_t sampledImageType = isDepthCompare
|
|
|
|
? m_module.defSampledImageType(m_textures.at(textureId).depthTypeId)
|
|
|
|
: m_module.defSampledImageType(m_textures.at(textureId).colorTypeId);
|
|
|
|
|
|
|
|
// Accumulate additional image operands.
|
|
|
|
SpirvImageOperands imageOperands;
|
|
|
|
|
2018-02-04 17:40:02 +01:00
|
|
|
if (isExtendedGather) {
|
|
|
|
m_module.enableCapability(spv::CapabilityImageGatherExtended);
|
|
|
|
|
|
|
|
DxbcRegisterValue gatherOffset = emitRegisterLoad(
|
|
|
|
ins.src[1], DxbcRegMask::firstN(imageLayerDim));
|
|
|
|
|
|
|
|
imageOperands.flags |= spv::ImageOperandsOffsetMask;
|
|
|
|
imageOperands.gOffset = gatherOffset.id;
|
|
|
|
} else if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) {
|
2018-01-17 02:12:29 +01:00
|
|
|
const std::array<uint32_t, 3> offsetIds = {
|
|
|
|
imageLayerDim >= 1 ? m_module.consti32(ins.sampleControls.u) : 0,
|
|
|
|
imageLayerDim >= 2 ? m_module.consti32(ins.sampleControls.v) : 0,
|
|
|
|
imageLayerDim >= 3 ? m_module.consti32(ins.sampleControls.w) : 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
imageOperands.flags |= spv::ImageOperandsConstOffsetMask;
|
|
|
|
imageOperands.sConstOffset = m_module.constComposite(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }),
|
|
|
|
imageLayerDim, offsetIds.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combine the texture and the sampler into a sampled image
|
|
|
|
const uint32_t sampledImageId = m_module.opSampledImage(
|
|
|
|
sampledImageType,
|
|
|
|
m_module.opLoad(
|
|
|
|
m_textures.at(textureId).imageTypeId,
|
|
|
|
m_textures.at(textureId).varId),
|
|
|
|
m_module.opLoad(
|
|
|
|
m_samplers.at(samplerId).typeId,
|
|
|
|
m_samplers.at(samplerId).varId));
|
|
|
|
|
|
|
|
// Gathering texels always returns a four-component
|
|
|
|
// vector, even for the depth-compare variants.
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = m_textures.at(textureId).sampledType;
|
|
|
|
result.type.ccount = 4;
|
2018-02-04 19:30:39 +01:00
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
switch (ins.op) {
|
|
|
|
// Simple image gather operation
|
2018-02-04 19:30:39 +01:00
|
|
|
case DxbcOpcode::Gather4:
|
|
|
|
case DxbcOpcode::Gather4Po: {
|
2018-01-17 02:12:29 +01:00
|
|
|
result.id = m_module.opImageGather(
|
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
2018-03-08 07:50:37 +01:00
|
|
|
m_module.consti32(samplerReg.swizzle[0]),
|
2018-01-17 02:12:29 +01:00
|
|
|
imageOperands);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
// Depth-compare operation
|
2018-02-04 19:30:39 +01:00
|
|
|
case DxbcOpcode::Gather4C:
|
|
|
|
case DxbcOpcode::Gather4PoC: {
|
2018-01-17 02:12:29 +01:00
|
|
|
result.id = m_module.opImageDrefGather(
|
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
referenceValue.id, imageOperands);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Swizzle components using the texture swizzle
|
|
|
|
// and the destination operand's write mask
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
textureReg.swizzle, ins.dst[0].mask);
|
|
|
|
|
2018-01-27 19:31:08 +01:00
|
|
|
emitRegisterStore(ins.dst[0], result);
|
2018-01-17 02:12:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
void DxbcCompiler::emitTextureSample(const DxbcShaderInstruction& ins) {
|
2017-12-20 00:16:49 +01:00
|
|
|
// All sample instructions have at least these operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Texture coordinates
|
|
|
|
// (src1) The texture itself
|
|
|
|
// (src2) The sampler object
|
2017-12-18 00:46:44 +01:00
|
|
|
const DxbcRegister& texCoordReg = ins.src[0];
|
|
|
|
const DxbcRegister& textureReg = ins.src[1];
|
|
|
|
const DxbcRegister& samplerReg = ins.src[2];
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Texture and sampler register IDs
|
|
|
|
const uint32_t textureId = textureReg.idx[0].offset;
|
|
|
|
const uint32_t samplerId = samplerReg.idx[0].offset;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Image type, which stores the image dimensions etc.
|
|
|
|
const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo;
|
2018-04-21 18:09:29 +02:00
|
|
|
const uint32_t imageLayerDim = getTexLayerDim(imageType);
|
2017-12-20 23:50:39 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Load the texture coordinates. SPIR-V allows these
|
|
|
|
// to be float4 even if not all components are used.
|
2018-04-21 18:09:29 +02:00
|
|
|
DxbcRegisterValue coord = emitLoadTexCoord(texCoordReg, imageType);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
// Load reference value for depth-compare operations
|
2017-12-20 13:41:04 +01:00
|
|
|
const bool isDepthCompare = ins.op == DxbcOpcode::SampleC
|
|
|
|
|| ins.op == DxbcOpcode::SampleClz;
|
|
|
|
|
|
|
|
const DxbcRegisterValue referenceValue = isDepthCompare
|
|
|
|
? emitRegisterLoad(ins.src[3], DxbcRegMask(true, false, false, false))
|
|
|
|
: DxbcRegisterValue();
|
2017-12-20 00:16:49 +01:00
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
// Load explicit gradients for sample operations that require them
|
2017-12-21 17:14:11 +01:00
|
|
|
const bool hasExplicitGradients = ins.op == DxbcOpcode::SampleD;
|
2017-12-20 23:50:39 +01:00
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
const DxbcRegisterValue explicitGradientX = hasExplicitGradients
|
2018-04-21 18:09:29 +02:00
|
|
|
? emitRegisterLoad(ins.src[3], DxbcRegMask::firstN(imageLayerDim))
|
2017-12-20 23:50:39 +01:00
|
|
|
: DxbcRegisterValue();
|
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
const DxbcRegisterValue explicitGradientY = hasExplicitGradients
|
2018-04-21 18:09:29 +02:00
|
|
|
? emitRegisterLoad(ins.src[4], DxbcRegMask::firstN(imageLayerDim))
|
2017-12-20 23:50:39 +01:00
|
|
|
: DxbcRegisterValue();
|
|
|
|
|
2018-01-20 10:21:27 +01:00
|
|
|
// LOD for certain sample operations
|
|
|
|
const bool hasLod = ins.op == DxbcOpcode::SampleL
|
|
|
|
|| ins.op == DxbcOpcode::SampleB;
|
2017-12-21 17:14:11 +01:00
|
|
|
|
2018-01-20 10:21:27 +01:00
|
|
|
const DxbcRegisterValue lod = hasLod
|
2017-12-21 17:14:11 +01:00
|
|
|
? emitRegisterLoad(ins.src[3], DxbcRegMask(true, false, false, false))
|
|
|
|
: DxbcRegisterValue();
|
|
|
|
|
2017-12-20 20:21:44 +01:00
|
|
|
// Accumulate additional image operands. These are
|
|
|
|
// not part of the actual operand token in SPIR-V.
|
|
|
|
SpirvImageOperands imageOperands;
|
2017-12-21 12:37:20 +01:00
|
|
|
|
|
|
|
if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) {
|
|
|
|
const std::array<uint32_t, 3> offsetIds = {
|
2017-12-21 16:00:36 +01:00
|
|
|
imageLayerDim >= 1 ? m_module.consti32(ins.sampleControls.u) : 0,
|
|
|
|
imageLayerDim >= 2 ? m_module.consti32(ins.sampleControls.v) : 0,
|
|
|
|
imageLayerDim >= 3 ? m_module.consti32(ins.sampleControls.w) : 0,
|
2017-12-21 12:37:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
imageOperands.flags |= spv::ImageOperandsConstOffsetMask;
|
|
|
|
imageOperands.sConstOffset = m_module.constComposite(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }),
|
|
|
|
imageLayerDim, offsetIds.data());
|
|
|
|
}
|
2017-12-20 20:21:44 +01:00
|
|
|
|
2018-01-10 18:58:17 +01:00
|
|
|
// Combine the texture and the sampler into a sampled image
|
2018-02-04 22:41:23 +01:00
|
|
|
const uint32_t sampledImageId = emitLoadSampledImage(
|
|
|
|
m_textures.at(textureId), m_samplers.at(samplerId),
|
|
|
|
isDepthCompare);
|
2018-01-10 18:58:17 +01:00
|
|
|
|
2017-12-20 13:41:04 +01:00
|
|
|
// Sampling an image always returns a four-component
|
|
|
|
// vector, whereas depth-compare ops return a scalar.
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
2017-12-20 13:41:04 +01:00
|
|
|
result.type.ctype = m_textures.at(textureId).sampledType;
|
|
|
|
result.type.ccount = isDepthCompare ? 1 : 4;
|
2017-12-20 00:16:49 +01:00
|
|
|
|
|
|
|
switch (ins.op) {
|
2017-12-20 20:21:44 +01:00
|
|
|
// Simple image sample operation
|
2017-12-20 00:16:49 +01:00
|
|
|
case DxbcOpcode::Sample: {
|
|
|
|
result.id = m_module.opImageSampleImplicitLod(
|
|
|
|
getVectorTypeId(result.type),
|
2017-12-20 20:21:44 +01:00
|
|
|
sampledImageId, coord.id,
|
|
|
|
imageOperands);
|
2017-12-20 00:16:49 +01:00
|
|
|
} break;
|
|
|
|
|
2017-12-20 20:21:44 +01:00
|
|
|
// Depth-compare operation
|
2017-12-20 14:54:24 +01:00
|
|
|
case DxbcOpcode::SampleC: {
|
|
|
|
result.id = m_module.opImageSampleDrefImplicitLod(
|
2017-12-20 20:21:44 +01:00
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
referenceValue.id, imageOperands);
|
2017-12-20 14:54:24 +01:00
|
|
|
} break;
|
|
|
|
|
2017-12-20 20:21:44 +01:00
|
|
|
// Depth-compare operation on mip level zero
|
2017-12-20 00:16:49 +01:00
|
|
|
case DxbcOpcode::SampleClz: {
|
2017-12-20 20:21:44 +01:00
|
|
|
imageOperands.flags |= spv::ImageOperandsLodMask;
|
|
|
|
imageOperands.sLod = m_module.constf32(0.0f);
|
|
|
|
|
2017-12-20 00:16:49 +01:00
|
|
|
result.id = m_module.opImageSampleDrefExplicitLod(
|
2017-12-20 20:21:44 +01:00
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
referenceValue.id, imageOperands);
|
2017-12-20 00:16:49 +01:00
|
|
|
} break;
|
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
// Sample operation with explicit gradients
|
|
|
|
case DxbcOpcode::SampleD: {
|
|
|
|
imageOperands.flags |= spv::ImageOperandsGradMask;
|
|
|
|
imageOperands.sGradX = explicitGradientX.id;
|
|
|
|
imageOperands.sGradY = explicitGradientY.id;
|
|
|
|
|
|
|
|
result.id = m_module.opImageSampleExplicitLod(
|
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
imageOperands);
|
|
|
|
} break;
|
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
// Sample operation with explicit LOD
|
|
|
|
case DxbcOpcode::SampleL: {
|
|
|
|
imageOperands.flags |= spv::ImageOperandsLodMask;
|
2018-01-20 10:21:27 +01:00
|
|
|
imageOperands.sLod = lod.id;
|
2017-12-21 17:14:11 +01:00
|
|
|
|
|
|
|
result.id = m_module.opImageSampleExplicitLod(
|
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
imageOperands);
|
|
|
|
} break;
|
|
|
|
|
2018-01-20 10:21:27 +01:00
|
|
|
// Sample operation with LOD bias
|
|
|
|
case DxbcOpcode::SampleB: {
|
|
|
|
imageOperands.flags |= spv::ImageOperandsBiasMask;
|
|
|
|
imageOperands.sLodBias = lod.id;
|
|
|
|
|
|
|
|
result.id = m_module.opImageSampleImplicitLod(
|
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
imageOperands);
|
|
|
|
} break;
|
|
|
|
|
2017-12-20 00:16:49 +01:00
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
// Swizzle components using the texture swizzle
|
|
|
|
// and the destination operand's write mask
|
2017-12-20 00:16:49 +01:00
|
|
|
if (result.type.ccount != 1) {
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
textureReg.swizzle, ins.dst[0].mask);
|
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2018-01-27 19:31:08 +01:00
|
|
|
emitRegisterStore(ins.dst[0], result);
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
void DxbcCompiler::emitTypedUavLoad(const DxbcShaderInstruction& ins) {
|
|
|
|
// load_uav_typed has three operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) The texture or buffer coordinates
|
|
|
|
// (src1) The UAV to load from
|
|
|
|
const uint32_t registerId = ins.src[1].idx[0].offset;
|
|
|
|
const DxbcUav uavInfo = m_uavs.at(registerId);
|
|
|
|
|
|
|
|
// Load texture coordinates
|
2018-04-21 18:09:29 +02:00
|
|
|
DxbcRegisterValue texCoord = emitLoadTexCoord(
|
|
|
|
ins.src[0], uavInfo.imageInfo);
|
2018-01-01 17:14:06 +01:00
|
|
|
|
|
|
|
// Load source value from the UAV
|
|
|
|
DxbcRegisterValue uavValue;
|
|
|
|
uavValue.type.ctype = uavInfo.sampledType;
|
|
|
|
uavValue.type.ccount = 4;
|
|
|
|
uavValue.id = m_module.opImageRead(
|
|
|
|
getVectorTypeId(uavValue.type),
|
|
|
|
m_module.opLoad(uavInfo.imageTypeId, uavInfo.varId),
|
|
|
|
texCoord.id, SpirvImageOperands());
|
|
|
|
|
|
|
|
// Apply component swizzle and mask
|
|
|
|
uavValue = emitRegisterSwizzle(uavValue,
|
|
|
|
ins.src[1].swizzle, ins.dst[0].mask);
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], uavValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitTypedUavStore(const DxbcShaderInstruction& ins) {
|
|
|
|
// store_uav_typed has three operands:
|
|
|
|
// (dst0) The destination UAV
|
|
|
|
// (src0) The texture or buffer coordinates
|
|
|
|
// (src1) The value to store
|
2018-05-26 19:25:20 +02:00
|
|
|
const DxbcBufferInfo uavInfo = getBufferInfo(ins.dst[0]);
|
2018-01-01 17:14:06 +01:00
|
|
|
|
2018-05-26 19:00:22 +02:00
|
|
|
// Execute write op only if the UAV is bound
|
2018-05-26 19:25:20 +02:00
|
|
|
uint32_t writeTest = emitUavWriteTest(uavInfo);
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
DxbcConditional cond;
|
|
|
|
cond.labelIf = m_module.allocateId();
|
|
|
|
cond.labelEnd = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge (cond.labelEnd, spv::SelectionControlMaskNone);
|
|
|
|
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
|
|
|
|
|
|
|
m_module.opLabel(cond.labelIf);
|
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
// Load texture coordinates
|
2018-05-26 19:25:20 +02:00
|
|
|
DxbcRegisterValue texCoord = emitLoadTexCoord(ins.src[0], uavInfo.image);
|
2018-01-01 17:14:06 +01:00
|
|
|
|
|
|
|
// Load the value that will be written to the image. We'll
|
|
|
|
// have to cast it to the component type of the image.
|
|
|
|
const DxbcRegisterValue texValue = emitRegisterBitcast(
|
|
|
|
emitRegisterLoad(ins.src[1], DxbcRegMask(true, true, true, true)),
|
2018-05-26 19:25:20 +02:00
|
|
|
uavInfo.stype);
|
2018-01-01 17:14:06 +01:00
|
|
|
|
|
|
|
// Write the given value to the image
|
|
|
|
m_module.opImageWrite(
|
2018-05-26 19:25:20 +02:00
|
|
|
m_module.opLoad(uavInfo.typeId, uavInfo.varId),
|
2018-01-01 17:14:06 +01:00
|
|
|
texCoord.id, texValue.id, SpirvImageOperands());
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
// End conditional block
|
|
|
|
m_module.opBranch(cond.labelEnd);
|
|
|
|
m_module.opLabel (cond.labelEnd);
|
2018-01-01 17:14:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
void DxbcCompiler::emitControlFlowIf(const DxbcShaderInstruction& ins) {
|
|
|
|
// Load the first component of the condition
|
|
|
|
// operand and perform a zero test on it.
|
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
// Declare the 'if' block. We do not know if there
|
|
|
|
// will be an 'else' block or not, so we'll assume
|
|
|
|
// that there is one and leave it empty otherwise.
|
|
|
|
DxbcCfgBlock block;
|
|
|
|
block.type = DxbcCfgBlockType::If;
|
2018-05-18 22:26:16 +02:00
|
|
|
block.b_if.ztestId = emitRegisterZeroTest(condition, ins.controls.zeroTest()).id;
|
2017-12-18 11:53:28 +01:00
|
|
|
block.b_if.labelIf = m_module.allocateId();
|
2018-05-18 22:26:16 +02:00
|
|
|
block.b_if.labelElse = 0;
|
2017-12-18 11:53:28 +01:00
|
|
|
block.b_if.labelEnd = m_module.allocateId();
|
2018-05-18 22:26:16 +02:00
|
|
|
block.b_if.headerPtr = m_module.getInsertionPtr();
|
2017-12-18 11:53:28 +01:00
|
|
|
m_controlFlowBlocks.push_back(block);
|
|
|
|
|
2018-05-18 22:26:16 +02:00
|
|
|
// We'll insert the branch instruction when closing
|
|
|
|
// the block, since we don't know whether or not an
|
|
|
|
// else block is needed right now.
|
2017-12-18 11:53:28 +01:00
|
|
|
m_module.opLabel(block.b_if.labelIf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowElse(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_controlFlowBlocks.size() == 0
|
|
|
|
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::If
|
2018-05-18 22:26:16 +02:00
|
|
|
|| m_controlFlowBlocks.back().b_if.labelElse != 0)
|
2017-12-18 11:53:28 +01:00
|
|
|
throw DxvkError("DxbcCompiler: 'Else' without 'If' found");
|
|
|
|
|
|
|
|
// Set the 'Else' flag so that we do
|
|
|
|
// not insert a dummy block on 'EndIf'
|
|
|
|
DxbcCfgBlock& block = m_controlFlowBlocks.back();
|
2018-05-18 22:26:16 +02:00
|
|
|
block.b_if.labelElse = m_module.allocateId();
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
// Close the 'If' block by branching to
|
|
|
|
// the merge block we declared earlier
|
|
|
|
m_module.opBranch(block.b_if.labelEnd);
|
|
|
|
m_module.opLabel (block.b_if.labelElse);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowEndIf(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_controlFlowBlocks.size() == 0
|
|
|
|
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::If)
|
|
|
|
throw DxvkError("DxbcCompiler: 'EndIf' without 'If' found");
|
|
|
|
|
|
|
|
// Remove the block from the stack, it's closed
|
2018-05-18 22:26:16 +02:00
|
|
|
DxbcCfgBlock block = m_controlFlowBlocks.back();
|
2017-12-18 11:53:28 +01:00
|
|
|
m_controlFlowBlocks.pop_back();
|
|
|
|
|
2018-05-18 22:26:16 +02:00
|
|
|
// Write out the 'if' header
|
|
|
|
m_module.beginInsertion(block.b_if.headerPtr);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2018-05-18 22:26:16 +02:00
|
|
|
m_module.opSelectionMerge(
|
|
|
|
block.b_if.labelEnd,
|
|
|
|
spv::SelectionControlMaskNone);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2018-05-18 22:26:16 +02:00
|
|
|
m_module.opBranchConditional(
|
|
|
|
block.b_if.ztestId,
|
|
|
|
block.b_if.labelIf,
|
|
|
|
block.b_if.labelElse != 0
|
|
|
|
? block.b_if.labelElse
|
|
|
|
: block.b_if.labelEnd);
|
|
|
|
|
|
|
|
m_module.endInsertion();
|
|
|
|
|
|
|
|
// End the active 'if' or 'else' block
|
|
|
|
m_module.opBranch(block.b_if.labelEnd);
|
|
|
|
m_module.opLabel (block.b_if.labelEnd);
|
2017-12-18 11:53:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
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;
|
2018-08-10 03:31:35 +02:00
|
|
|
label.next = block->labelCases;
|
2017-12-30 17:22:36 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
void DxbcCompiler::emitControlFlowLoop(const DxbcShaderInstruction& ins) {
|
|
|
|
// Declare the 'loop' block
|
|
|
|
DxbcCfgBlock block;
|
|
|
|
block.type = DxbcCfgBlockType::Loop;
|
|
|
|
block.b_loop.labelHeader = m_module.allocateId();
|
|
|
|
block.b_loop.labelBegin = m_module.allocateId();
|
|
|
|
block.b_loop.labelContinue = m_module.allocateId();
|
|
|
|
block.b_loop.labelBreak = m_module.allocateId();
|
|
|
|
m_controlFlowBlocks.push_back(block);
|
|
|
|
|
|
|
|
m_module.opBranch(block.b_loop.labelHeader);
|
|
|
|
m_module.opLabel (block.b_loop.labelHeader);
|
|
|
|
|
|
|
|
m_module.opLoopMerge(
|
|
|
|
block.b_loop.labelBreak,
|
|
|
|
block.b_loop.labelContinue,
|
|
|
|
spv::LoopControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranch(block.b_loop.labelBegin);
|
|
|
|
m_module.opLabel (block.b_loop.labelBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowEndLoop(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_controlFlowBlocks.size() == 0
|
|
|
|
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::Loop)
|
|
|
|
throw DxvkError("DxbcCompiler: 'EndLoop' without 'Loop' found");
|
|
|
|
|
|
|
|
// Remove the block from the stack, it's closed
|
|
|
|
const DxbcCfgBlock block = m_controlFlowBlocks.back();
|
|
|
|
m_controlFlowBlocks.pop_back();
|
|
|
|
|
|
|
|
// Declare the continue block
|
|
|
|
m_module.opBranch(block.b_loop.labelContinue);
|
|
|
|
m_module.opLabel (block.b_loop.labelContinue);
|
|
|
|
|
|
|
|
// Declare the merge block
|
|
|
|
m_module.opBranch(block.b_loop.labelHeader);
|
|
|
|
m_module.opLabel (block.b_loop.labelBreak);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-29 22:54:25 +01:00
|
|
|
void DxbcCompiler::emitControlFlowBreak(const DxbcShaderInstruction& ins) {
|
|
|
|
const bool isBreak = ins.op == DxbcOpcode::Break;
|
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
DxbcCfgBlock* cfgBlock = isBreak
|
|
|
|
? cfgFindBlock({ DxbcCfgBlockType::Loop, DxbcCfgBlockType::Switch })
|
|
|
|
: cfgFindBlock({ DxbcCfgBlockType::Loop });
|
|
|
|
|
|
|
|
if (cfgBlock == nullptr)
|
|
|
|
throw DxvkError("DxbcCompiler: 'Break' or 'Continue' outside 'Loop' or 'Switch' found");
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2017-12-29 22:54:25 +01:00
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
// Subsequent instructions assume that there is an open block
|
|
|
|
const uint32_t labelId = m_module.allocateId();
|
|
|
|
m_module.opLabel(labelId);
|
2017-12-29 22:54:25 +01:00
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
// 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;
|
2017-12-29 22:54:25 +01:00
|
|
|
}
|
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
void DxbcCompiler::emitControlFlowBreakc(const DxbcShaderInstruction& ins) {
|
2017-12-29 22:54:25 +01:00
|
|
|
const bool isBreak = ins.op == DxbcOpcode::Breakc;
|
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
DxbcCfgBlock* cfgBlock = isBreak
|
|
|
|
? cfgFindBlock({ DxbcCfgBlockType::Loop, DxbcCfgBlockType::Switch })
|
|
|
|
: cfgFindBlock({ DxbcCfgBlockType::Loop });
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
if (cfgBlock == nullptr)
|
|
|
|
throw DxvkError("DxbcCompiler: 'Breakc' or 'Continuec' outside 'Loop' or 'Switch' found");
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
// Perform zero test on the first component of the condition
|
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
2018-04-10 08:01:54 +02:00
|
|
|
condition, ins.controls.zeroTest());
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
// We basically have to wrap this into an 'if' block
|
|
|
|
const uint32_t breakBlock = m_module.allocateId();
|
|
|
|
const uint32_t mergeBlock = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(mergeBlock,
|
|
|
|
spv::SelectionControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranchConditional(
|
|
|
|
zeroTest.id, breakBlock, mergeBlock);
|
|
|
|
|
|
|
|
m_module.opLabel(breakBlock);
|
2017-12-30 17:22:36 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
m_module.opLabel(mergeBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowRet(const DxbcShaderInstruction& ins) {
|
2018-08-10 03:31:35 +02:00
|
|
|
if (m_controlFlowBlocks.size() != 0) {
|
|
|
|
uint32_t labelId = m_module.allocateId();
|
2018-08-27 12:47:52 +02:00
|
|
|
|
|
|
|
m_module.opReturn();
|
2018-08-10 03:31:35 +02:00
|
|
|
m_module.opLabel(labelId);
|
|
|
|
|
|
|
|
// return can be used in place of break to terminate a case block
|
|
|
|
if (m_controlFlowBlocks.back().type == DxbcCfgBlockType::Switch)
|
|
|
|
m_controlFlowBlocks.back().b_switch.labelCase = labelId;
|
|
|
|
} else {
|
|
|
|
// Last instruction in the current function
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2018-08-10 03:31:35 +02:00
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
2018-03-10 17:04:58 +03:00
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowRetc(const DxbcShaderInstruction& ins) {
|
|
|
|
// Perform zero test on the first component of the condition
|
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
2018-04-10 08:01:54 +02:00
|
|
|
condition, ins.controls.zeroTest());
|
2018-03-10 17:04:58 +03:00
|
|
|
|
|
|
|
// We basically have to wrap this into an 'if' block
|
|
|
|
const uint32_t returnLabel = m_module.allocateId();
|
|
|
|
const uint32_t continueLabel = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(continueLabel,
|
|
|
|
spv::SelectionControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranchConditional(
|
|
|
|
zeroTest.id, returnLabel, continueLabel);
|
|
|
|
|
|
|
|
m_module.opLabel(returnLabel);
|
|
|
|
m_module.opReturn();
|
|
|
|
|
|
|
|
m_module.opLabel(continueLabel);
|
|
|
|
}
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitControlFlowDiscard(const DxbcShaderInstruction& ins) {
|
|
|
|
// Discard actually has an operand that determines
|
|
|
|
// whether or not the fragment should be discarded
|
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
2018-04-10 08:01:54 +02:00
|
|
|
condition, ins.controls.zeroTest());
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2018-05-26 19:25:20 +02:00
|
|
|
if (m_ps.killState == 0) {
|
|
|
|
DxbcConditional cond;
|
|
|
|
cond.labelIf = m_module.allocateId();
|
|
|
|
cond.labelEnd = m_module.allocateId();
|
|
|
|
|
2018-05-31 14:20:12 +02:00
|
|
|
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
2018-05-26 19:25:20 +02:00
|
|
|
m_module.opBranchConditional(zeroTest.id, cond.labelIf, cond.labelEnd);
|
|
|
|
|
|
|
|
// OpKill terminates the block
|
|
|
|
m_module.opLabel(cond.labelIf);
|
|
|
|
m_module.opKill();
|
|
|
|
|
|
|
|
m_module.opLabel(cond.labelEnd);
|
|
|
|
} else {
|
|
|
|
uint32_t typeId = m_module.defBoolType();
|
|
|
|
|
|
|
|
uint32_t killState = m_module.opLoad (typeId, m_ps.killState);
|
|
|
|
killState = m_module.opLogicalOr(typeId, killState, zeroTest.id);
|
|
|
|
m_module.opStore(m_ps.killState, killState);
|
|
|
|
}
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
void DxbcCompiler::emitControlFlow(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::If:
|
|
|
|
return this->emitControlFlowIf(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::Else:
|
|
|
|
return this->emitControlFlowElse(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::EndIf:
|
|
|
|
return this->emitControlFlowEndIf(ins);
|
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
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);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcOpcode::Loop:
|
|
|
|
return this->emitControlFlowLoop(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::EndLoop:
|
|
|
|
return this->emitControlFlowEndLoop(ins);
|
|
|
|
|
2017-12-29 22:54:25 +01:00
|
|
|
case DxbcOpcode::Break:
|
|
|
|
case DxbcOpcode::Continue:
|
|
|
|
return this->emitControlFlowBreak(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcOpcode::Breakc:
|
2017-12-29 22:54:25 +01:00
|
|
|
case DxbcOpcode::Continuec:
|
2017-12-18 11:53:28 +01:00
|
|
|
return this->emitControlFlowBreakc(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::Ret:
|
|
|
|
return this->emitControlFlowRet(ins);
|
2018-03-10 17:04:58 +03:00
|
|
|
|
|
|
|
case DxbcOpcode::Retc:
|
|
|
|
return this->emitControlFlowRetc(ins);
|
2017-12-18 16:41:05 +01:00
|
|
|
|
|
|
|
case DxbcOpcode::Discard:
|
|
|
|
return this->emitControlFlowDiscard(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-30 03:44:19 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitBuildConstVecf32(
|
2018-01-08 22:26:45 +01:00
|
|
|
float x,
|
|
|
|
float y,
|
|
|
|
float z,
|
|
|
|
float w,
|
2017-12-30 03:44:19 +01:00
|
|
|
const DxbcRegMask& writeMask) {
|
2018-01-10 18:58:17 +01:00
|
|
|
// TODO refactor these functions into one single template
|
2017-12-30 03:44:19 +01:00
|
|
|
std::array<uint32_t, 4> ids = { 0, 0, 0, 0 };
|
|
|
|
uint32_t componentIndex = 0;
|
|
|
|
|
2018-01-08 22:26:45 +01:00
|
|
|
if (writeMask[0]) ids[componentIndex++] = m_module.constf32(x);
|
|
|
|
if (writeMask[1]) ids[componentIndex++] = m_module.constf32(y);
|
|
|
|
if (writeMask[2]) ids[componentIndex++] = m_module.constf32(z);
|
|
|
|
if (writeMask[3]) ids[componentIndex++] = m_module.constf32(w);
|
2017-12-30 03:44:19 +01:00
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float32;
|
|
|
|
result.type.ccount = componentIndex;
|
|
|
|
result.id = componentIndex > 1
|
|
|
|
? m_module.constComposite(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
componentIndex, ids.data())
|
|
|
|
: ids[0];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-10 18:58:17 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitBuildConstVecu32(
|
|
|
|
uint32_t x,
|
|
|
|
uint32_t y,
|
|
|
|
uint32_t z,
|
|
|
|
uint32_t w,
|
|
|
|
const DxbcRegMask& writeMask) {
|
|
|
|
std::array<uint32_t, 4> ids = { 0, 0, 0, 0 };
|
|
|
|
uint32_t componentIndex = 0;
|
|
|
|
|
|
|
|
if (writeMask[0]) ids[componentIndex++] = m_module.constu32(x);
|
|
|
|
if (writeMask[1]) ids[componentIndex++] = m_module.constu32(y);
|
|
|
|
if (writeMask[2]) ids[componentIndex++] = m_module.constu32(z);
|
|
|
|
if (writeMask[3]) ids[componentIndex++] = m_module.constu32(w);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = componentIndex;
|
|
|
|
result.id = componentIndex > 1
|
|
|
|
? m_module.constComposite(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
componentIndex, ids.data())
|
|
|
|
: ids[0];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitBuildConstVeci32(
|
|
|
|
int32_t x,
|
|
|
|
int32_t y,
|
|
|
|
int32_t z,
|
|
|
|
int32_t w,
|
|
|
|
const DxbcRegMask& writeMask) {
|
|
|
|
std::array<uint32_t, 4> ids = { 0, 0, 0, 0 };
|
|
|
|
uint32_t componentIndex = 0;
|
|
|
|
|
|
|
|
if (writeMask[0]) ids[componentIndex++] = m_module.consti32(x);
|
|
|
|
if (writeMask[1]) ids[componentIndex++] = m_module.consti32(y);
|
|
|
|
if (writeMask[2]) ids[componentIndex++] = m_module.consti32(z);
|
|
|
|
if (writeMask[3]) ids[componentIndex++] = m_module.consti32(w);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Sint32;
|
|
|
|
result.type.ccount = componentIndex;
|
|
|
|
result.id = componentIndex > 1
|
|
|
|
? m_module.constComposite(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
componentIndex, ids.data())
|
|
|
|
: ids[0];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-15 19:51:07 +02:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitBuildConstVecf64(
|
|
|
|
double xy,
|
|
|
|
double zw,
|
|
|
|
const DxbcRegMask& writeMask) {
|
|
|
|
std::array<uint32_t, 2> ids = { 0, 0 };
|
|
|
|
uint32_t componentIndex = 0;
|
|
|
|
|
|
|
|
if (writeMask[0] && writeMask[1]) ids[componentIndex++] = m_module.constf64(xy);
|
|
|
|
if (writeMask[2] && writeMask[3]) ids[componentIndex++] = m_module.constf64(zw);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float64;
|
|
|
|
result.type.ccount = componentIndex;
|
|
|
|
result.id = componentIndex > 1
|
|
|
|
? m_module.constComposite(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
componentIndex, ids.data())
|
|
|
|
: ids[0];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterBitcast(
|
|
|
|
DxbcRegisterValue srcValue,
|
|
|
|
DxbcScalarType dstType) {
|
2018-06-07 14:32:56 +02:00
|
|
|
DxbcScalarType srcType = srcValue.type.ctype;
|
|
|
|
|
|
|
|
if (srcType == dstType)
|
2017-12-18 00:46:44 +01:00
|
|
|
return srcValue;
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = dstType;
|
|
|
|
result.type.ccount = srcValue.type.ccount;
|
2018-06-07 14:32:56 +02:00
|
|
|
|
|
|
|
if (isDoubleType(srcType)) result.type.ccount *= 2;
|
|
|
|
if (isDoubleType(dstType)) result.type.ccount /= 2;
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opBitcast(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
srcValue.id);
|
|
|
|
return result;
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterSwizzle(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcRegSwizzle swizzle,
|
|
|
|
DxbcRegMask writeMask) {
|
2018-01-08 22:26:45 +01:00
|
|
|
if (value.type.ccount == 1)
|
2018-03-23 19:48:07 +01:00
|
|
|
return emitRegisterExtend(value, writeMask.popCount());
|
2018-01-08 22:26:45 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
std::array<uint32_t, 4> indices;
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
uint32_t dstIndex = 0;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-29 00:51:31 +01:00
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
2017-12-18 00:46:44 +01:00
|
|
|
if (writeMask[i])
|
2017-12-13 15:32:54 +01:00
|
|
|
indices[dstIndex++] = swizzle[i];
|
|
|
|
}
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// If the swizzle combined with the mask can be reduced
|
|
|
|
// to a no-op, we don't need to insert any instructions.
|
2017-12-18 00:46:44 +01:00
|
|
|
bool isIdentitySwizzle = dstIndex == value.type.ccount;
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++)
|
|
|
|
isIdentitySwizzle &= indices[i] == i;
|
|
|
|
|
|
|
|
if (isIdentitySwizzle)
|
2017-12-18 00:46:44 +01:00
|
|
|
return value;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Use OpCompositeExtract if the resulting vector contains
|
|
|
|
// only one component, and OpVectorShuffle if it is a vector.
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = value.type.ctype;
|
|
|
|
result.type.ccount = dstIndex;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
if (dstIndex == 1) {
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opCompositeExtract(
|
|
|
|
typeId, value.id, 1, indices.data());
|
2017-12-13 15:32:54 +01:00
|
|
|
} else {
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opVectorShuffle(
|
|
|
|
typeId, value.id, value.id,
|
2017-12-13 15:32:54 +01:00
|
|
|
dstIndex, indices.data());
|
|
|
|
}
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
return result;
|
2017-12-10 12:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterExtract(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcRegMask mask) {
|
|
|
|
return emitRegisterSwizzle(value,
|
|
|
|
DxbcRegSwizzle(0, 1, 2, 3), mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterInsert(
|
|
|
|
DxbcRegisterValue dstValue,
|
|
|
|
DxbcRegisterValue srcValue,
|
|
|
|
DxbcRegMask srcMask) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type = dstValue.type;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
|
|
|
|
2018-03-23 19:48:07 +01:00
|
|
|
if (srcMask.popCount() == 0) {
|
2017-12-18 00:46:44 +01:00
|
|
|
// Nothing to do if the insertion mask is empty
|
|
|
|
result.id = dstValue.id;
|
|
|
|
} else if (dstValue.type.ccount == 1) {
|
|
|
|
// Both values are scalar, so the first component
|
|
|
|
// of the write mask decides which one to take.
|
|
|
|
result.id = srcMask[0] ? srcValue.id : dstValue.id;
|
|
|
|
} else if (srcValue.type.ccount == 1) {
|
|
|
|
// The source value is scalar. Since OpVectorShuffle
|
|
|
|
// requires both arguments to be vectors, we have to
|
|
|
|
// use OpCompositeInsert to modify the vector instead.
|
|
|
|
const uint32_t componentId = srcMask.firstSet();
|
|
|
|
|
|
|
|
result.id = m_module.opCompositeInsert(typeId,
|
|
|
|
srcValue.id, dstValue.id, 1, &componentId);
|
|
|
|
} else {
|
|
|
|
// Both arguments are vectors. We can determine which
|
|
|
|
// components to take from which vector and use the
|
|
|
|
// OpVectorShuffle instruction.
|
|
|
|
std::array<uint32_t, 4> components;
|
|
|
|
uint32_t srcComponentId = dstValue.type.ccount;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < dstValue.type.ccount; i++)
|
|
|
|
components.at(i) = srcMask[i] ? srcComponentId++ : i;
|
|
|
|
|
|
|
|
result.id = m_module.opVectorShuffle(
|
|
|
|
typeId, dstValue.id, srcValue.id,
|
|
|
|
dstValue.type.ccount, components.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-01 10:11:15 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterConcat(
|
|
|
|
DxbcRegisterValue value1,
|
|
|
|
DxbcRegisterValue value2) {
|
|
|
|
std::array<uint32_t, 2> ids =
|
|
|
|
{{ value1.id, value2.id }};
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = value1.type.ctype;
|
|
|
|
result.type.ccount = value1.type.ccount + value2.type.ccount;
|
|
|
|
result.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
ids.size(), ids.data());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterExtend(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
uint32_t size) {
|
2017-12-13 15:32:54 +01:00
|
|
|
if (size == 1)
|
2017-12-18 00:46:44 +01:00
|
|
|
return value;
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2018-03-01 10:11:15 +01:00
|
|
|
std::array<uint32_t, 4> ids = {{
|
2017-12-18 00:46:44 +01:00
|
|
|
value.id, value.id,
|
|
|
|
value.id, value.id,
|
2018-03-01 10:11:15 +01:00
|
|
|
}};
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = value.type.ctype;
|
|
|
|
result.type.ccount = size;
|
|
|
|
result.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
size, ids.data());
|
2017-12-13 15:32:54 +01:00
|
|
|
return result;
|
2017-12-08 17:08:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterAbsolute(
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
switch (value.type.ctype) {
|
|
|
|
case DxbcScalarType::Float32: value.id = m_module.opFAbs(typeId, value.id); break;
|
|
|
|
case DxbcScalarType::Sint32: value.id = m_module.opSAbs(typeId, value.id); break;
|
|
|
|
default: Logger::warn("DxbcCompiler: Cannot get absolute value for given type");
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterNegate(
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
switch (value.type.ctype) {
|
|
|
|
case DxbcScalarType::Float32: value.id = m_module.opFNegate(typeId, value.id); break;
|
2018-06-07 15:05:06 +02:00
|
|
|
case DxbcScalarType::Float64: value.id = m_module.opFNegate(typeId, value.id); break;
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcScalarType::Sint32: value.id = m_module.opSNegate(typeId, value.id); break;
|
2018-06-07 15:05:06 +02:00
|
|
|
case DxbcScalarType::Sint64: value.id = m_module.opSNegate(typeId, value.id); break;
|
2017-12-18 00:46:44 +01:00
|
|
|
default: Logger::warn("DxbcCompiler: Cannot negate given type");
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterZeroTest(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcZeroTest test) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Bool;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
|
|
|
|
const uint32_t zeroId = m_module.constu32(0u);
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
|
|
|
|
|
|
|
result.id = test == DxbcZeroTest::TestZ
|
|
|
|
? m_module.opIEqual (typeId, value.id, zeroId)
|
|
|
|
: m_module.opINotEqual(typeId, value.id, zeroId);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-03 01:01:44 +02:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterMaskBits(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
uint32_t mask) {
|
|
|
|
DxbcRegisterValue maskVector = emitBuildConstVecu32(
|
|
|
|
mask, mask, mask, mask, DxbcRegMask::firstN(value.type.ccount));
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type = value.type;
|
|
|
|
result.id = m_module.opBitwiseAnd(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
value.id, maskVector.id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitSrcOperandModifiers(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcRegModifiers modifiers) {
|
|
|
|
if (modifiers.test(DxbcRegModifier::Abs))
|
|
|
|
value = emitRegisterAbsolute(value);
|
|
|
|
|
|
|
|
if (modifiers.test(DxbcRegModifier::Neg))
|
|
|
|
value = emitRegisterNegate(value);
|
|
|
|
return value;
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitDstOperandModifiers(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcOpModifiers modifiers) {
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
|
|
|
|
|
|
|
if (value.type.ctype == DxbcScalarType::Float32) {
|
|
|
|
// Saturating only makes sense on floats
|
|
|
|
if (modifiers.saturate) {
|
2018-01-08 22:26:45 +01:00
|
|
|
const DxbcRegMask mask = DxbcRegMask::firstN(value.type.ccount);
|
|
|
|
const DxbcRegisterValue vec0 = emitBuildConstVecf32(0.0f, 0.0f, 0.0f, 0.0f, mask);
|
|
|
|
const DxbcRegisterValue vec1 = emitBuildConstVecf32(1.0f, 1.0f, 1.0f, 1.0f, mask);
|
|
|
|
|
2018-06-12 22:13:53 +02:00
|
|
|
value.id = m_module.opNClamp(typeId, value.id, vec0.id, vec1.id);
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-25 22:45:23 +02:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitArrayAccess(
|
|
|
|
DxbcRegisterPointer pointer,
|
|
|
|
spv::StorageClass sclass,
|
|
|
|
uint32_t index) {
|
|
|
|
uint32_t ptrTypeId = m_module.defPointerType(
|
|
|
|
getVectorTypeId(pointer.type), sclass);
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type = pointer.type;
|
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
ptrTypeId, pointer.id, 1, &index);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-04 22:41:23 +01:00
|
|
|
uint32_t DxbcCompiler::emitLoadSampledImage(
|
|
|
|
const DxbcShaderResource& textureResource,
|
|
|
|
const DxbcSampler& samplerResource,
|
|
|
|
bool isDepthCompare) {
|
|
|
|
const uint32_t sampledImageType = isDepthCompare
|
|
|
|
? m_module.defSampledImageType(textureResource.depthTypeId)
|
|
|
|
: m_module.defSampledImageType(textureResource.colorTypeId);
|
|
|
|
|
|
|
|
return m_module.opSampledImage(sampledImageType,
|
|
|
|
m_module.opLoad(textureResource.imageTypeId, textureResource.varId),
|
|
|
|
m_module.opLoad(samplerResource.typeId, samplerResource.varId));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetTempPtr(
|
2017-12-20 22:50:05 +01:00
|
|
|
const DxbcRegister& operand) {
|
2017-12-18 00:46:44 +01:00
|
|
|
// r# regs are indexed as follows:
|
|
|
|
// (0) register index (immediate)
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float32;
|
|
|
|
result.type.ccount = 4;
|
|
|
|
result.id = m_rRegs.at(operand.idx[0].offset);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetIndexableTempPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
// x# regs are indexed as follows:
|
|
|
|
// (0) register index (immediate)
|
|
|
|
// (1) element index (relative)
|
|
|
|
const uint32_t regId = operand.idx[0].offset;
|
|
|
|
|
|
|
|
const DxbcRegisterValue vectorId
|
|
|
|
= emitIndexLoad(operand.idx[1]);
|
|
|
|
|
|
|
|
DxbcRegisterInfo info;
|
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = m_xRegs[regId].ccount;
|
|
|
|
info.type.alength = 0;
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = info.type.ctype;
|
|
|
|
result.type.ccount = info.type.ccount;
|
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
getPointerTypeId(info),
|
|
|
|
m_xRegs.at(regId).varId,
|
|
|
|
1, &vectorId.id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetInputPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
// In the vertex and pixel stages,
|
|
|
|
// v# regs are indexed as follows:
|
|
|
|
// (0) register index (relative)
|
|
|
|
//
|
|
|
|
// In the tessellation and geometry
|
|
|
|
// stages, the index has two dimensions:
|
|
|
|
// (0) vertex index (relative)
|
|
|
|
// (1) register index (relative)
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float32;
|
|
|
|
result.type.ccount = 4;
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2018-03-06 14:00:03 +01:00
|
|
|
std::array<uint32_t, 2> indices = {{ 0, 0 }};
|
2017-12-21 12:37:20 +01:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < operand.idxDim; i++)
|
|
|
|
indices.at(i) = emitIndexLoad(operand.idx[i]).id;
|
2018-03-06 16:47:35 +01:00
|
|
|
|
|
|
|
// Pick the input array depending on
|
|
|
|
// the program type and operand type
|
|
|
|
struct InputArray {
|
|
|
|
uint32_t id;
|
|
|
|
spv::StorageClass sclass;
|
|
|
|
};
|
|
|
|
|
|
|
|
const InputArray array = [&] () -> InputArray {
|
|
|
|
switch (operand.type) {
|
|
|
|
case DxbcOperandType::InputControlPoint:
|
2018-10-08 09:34:56 +02:00
|
|
|
return m_programInfo.type() == DxbcProgramType::HullShader
|
2018-03-07 09:52:24 +01:00
|
|
|
? InputArray { m_vArray, spv::StorageClassPrivate }
|
|
|
|
: InputArray { m_ds.inputPerVertex, spv::StorageClassInput };
|
2018-03-06 16:47:35 +01:00
|
|
|
case DxbcOperandType::InputPatchConstant:
|
2018-10-08 09:34:56 +02:00
|
|
|
return m_programInfo.type() == DxbcProgramType::HullShader
|
2018-09-13 14:54:54 +02:00
|
|
|
? InputArray { m_hs.outputPerPatch, spv::StorageClassPrivate }
|
|
|
|
: InputArray { m_ds.inputPerPatch, spv::StorageClassInput };
|
2018-03-07 09:52:24 +01:00
|
|
|
case DxbcOperandType::OutputControlPoint:
|
|
|
|
return InputArray { m_hs.outputPerVertex, spv::StorageClassOutput };
|
2018-03-06 16:47:35 +01:00
|
|
|
default:
|
|
|
|
return { m_vArray, spv::StorageClassPrivate };
|
|
|
|
}
|
|
|
|
}();
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
DxbcRegisterInfo info;
|
|
|
|
info.type.ctype = result.type.ctype;
|
|
|
|
info.type.ccount = result.type.ccount;
|
|
|
|
info.type.alength = 0;
|
2018-03-06 16:47:35 +01:00
|
|
|
info.sclass = array.sclass;
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
result.id = m_module.opAccessChain(
|
2018-03-06 16:47:35 +01:00
|
|
|
getPointerTypeId(info), array.id,
|
2017-12-21 12:37:20 +01:00
|
|
|
operand.idxDim, indices.data());
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetOutputPtr(
|
|
|
|
const DxbcRegister& operand) {
|
2018-10-08 09:34:56 +02:00
|
|
|
if (m_programInfo.type() == DxbcProgramType::HullShader) {
|
2018-03-06 14:00:03 +01:00
|
|
|
// Hull shaders are special in that they have two sets of
|
|
|
|
// output registers, one for per-patch values and one for
|
|
|
|
// per-vertex values.
|
2018-07-01 12:44:37 +02:00
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float32;
|
|
|
|
result.type.ccount = 4;
|
2018-03-06 14:00:03 +01:00
|
|
|
|
2018-07-01 12:44:37 +02:00
|
|
|
uint32_t registerId = emitIndexLoad(operand.idx[0]).id;
|
2018-09-13 14:54:54 +02:00
|
|
|
|
2018-07-01 12:44:37 +02:00
|
|
|
if (m_hs.currPhaseType == DxbcCompilerHsPhase::ControlPoint) {
|
|
|
|
std::array<uint32_t, 2> indices = {{
|
|
|
|
m_module.opLoad(m_module.defIntType(32, 0), m_hs.builtinInvocationId),
|
|
|
|
registerId,
|
|
|
|
}};
|
|
|
|
|
2018-09-13 14:54:54 +02:00
|
|
|
uint32_t ptrTypeId = m_module.defPointerType(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
spv::StorageClassOutput);
|
|
|
|
|
2018-07-01 12:44:37 +02:00
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
ptrTypeId, m_hs.outputPerVertex,
|
|
|
|
indices.size(), indices.data());
|
|
|
|
} else {
|
2018-09-13 14:54:54 +02:00
|
|
|
uint32_t ptrTypeId = m_module.defPointerType(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
spv::StorageClassPrivate);
|
|
|
|
|
2018-07-01 12:44:37 +02:00
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
ptrTypeId, m_hs.outputPerPatch,
|
|
|
|
1, ®isterId);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
// Regular shaders have their output
|
|
|
|
// registers set up at declaration time
|
|
|
|
return m_oRegs.at(operand.idx[0].offset);
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-02 18:09:59 +02:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetConstBufPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
// Constant buffers take a two-dimensional index:
|
|
|
|
// (0) register index (immediate)
|
|
|
|
// (1) constant offset (relative)
|
|
|
|
DxbcRegisterInfo info;
|
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = 4;
|
|
|
|
info.type.alength = 0;
|
|
|
|
info.sclass = spv::StorageClassUniform;
|
|
|
|
|
|
|
|
const uint32_t regId = operand.idx[0].offset;
|
|
|
|
const DxbcRegisterValue constId = emitIndexLoad(operand.idx[1]);
|
|
|
|
|
|
|
|
const uint32_t ptrTypeId = getPointerTypeId(info);
|
|
|
|
|
|
|
|
const std::array<uint32_t, 2> indices =
|
|
|
|
{{ m_module.consti32(0), constId.id }};
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = info.type.ctype;
|
|
|
|
result.type.ccount = info.type.ccount;
|
|
|
|
result.id = m_module.opAccessChain(ptrTypeId,
|
|
|
|
m_constantBuffers.at(regId).varId,
|
|
|
|
indices.size(), indices.data());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-19 17:41:23 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetImmConstBufPtr(
|
|
|
|
const DxbcRegister& operand) {
|
2018-07-30 20:52:42 +02:00
|
|
|
const DxbcRegisterValue constId
|
|
|
|
= emitIndexLoad(operand.idx[0]);
|
|
|
|
|
|
|
|
if (m_immConstBuf != 0) {
|
2018-07-30 21:08:52 +02:00
|
|
|
DxbcRegisterInfo ptrInfo;
|
|
|
|
ptrInfo.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
ptrInfo.type.ccount = 4;
|
|
|
|
ptrInfo.type.alength = 0;
|
2018-07-30 20:52:42 +02:00
|
|
|
ptrInfo.sclass = spv::StorageClassPrivate;
|
|
|
|
|
2018-07-30 21:08:52 +02:00
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = ptrInfo.type.ctype;
|
|
|
|
result.type.ccount = ptrInfo.type.ccount;
|
2018-07-30 20:52:42 +02:00
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
getPointerTypeId(ptrInfo),
|
|
|
|
m_immConstBuf, 1, &constId.id);
|
|
|
|
return result;
|
|
|
|
} else if (m_constantBuffers.at(Icb_BindingSlotId).varId != 0) {
|
|
|
|
const std::array<uint32_t, 2> indices =
|
|
|
|
{{ m_module.consti32(0), constId.id }};
|
|
|
|
|
2018-07-30 21:08:52 +02:00
|
|
|
DxbcRegisterInfo ptrInfo;
|
|
|
|
ptrInfo.type.ctype = DxbcScalarType::Float32;
|
|
|
|
ptrInfo.type.ccount = 4;
|
|
|
|
ptrInfo.type.alength = 0;
|
2018-07-30 20:52:42 +02:00
|
|
|
ptrInfo.sclass = spv::StorageClassUniform;
|
|
|
|
|
2018-07-30 21:08:52 +02:00
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = ptrInfo.type.ctype;
|
|
|
|
result.type.ccount = ptrInfo.type.ccount;
|
2018-07-30 20:52:42 +02:00
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
getPointerTypeId(ptrInfo),
|
|
|
|
m_constantBuffers.at(Icb_BindingSlotId).varId,
|
|
|
|
indices.size(), indices.data());
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
throw DxvkError("DxbcCompiler: Immediate constant buffer not defined");
|
|
|
|
}
|
2017-12-19 17:41:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetOperandPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
switch (operand.type) {
|
|
|
|
case DxbcOperandType::Temp:
|
|
|
|
return emitGetTempPtr(operand);
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
case DxbcOperandType::IndexableTemp:
|
|
|
|
return emitGetIndexableTempPtr(operand);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcOperandType::Input:
|
2018-03-06 16:47:35 +01:00
|
|
|
case DxbcOperandType::InputControlPoint:
|
|
|
|
case DxbcOperandType::InputPatchConstant:
|
2018-03-07 09:52:24 +01:00
|
|
|
case DxbcOperandType::OutputControlPoint:
|
2017-12-18 00:46:44 +01:00
|
|
|
return emitGetInputPtr(operand);
|
|
|
|
|
|
|
|
case DxbcOperandType::Output:
|
|
|
|
return emitGetOutputPtr(operand);
|
|
|
|
|
2018-06-02 18:09:59 +02:00
|
|
|
case DxbcOperandType::ConstantBuffer:
|
|
|
|
return emitGetConstBufPtr(operand);
|
|
|
|
|
2017-12-19 17:41:23 +01:00
|
|
|
case DxbcOperandType::ImmediateConstantBuffer:
|
|
|
|
return emitGetImmConstBufPtr(operand);
|
|
|
|
|
2017-12-29 00:51:31 +01:00
|
|
|
case DxbcOperandType::InputThreadId:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 3 },
|
|
|
|
m_cs.builtinGlobalInvocationId };
|
|
|
|
|
|
|
|
case DxbcOperandType::InputThreadGroupId:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 3 },
|
|
|
|
m_cs.builtinWorkgroupId };
|
|
|
|
|
|
|
|
case DxbcOperandType::InputThreadIdInGroup:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 3 },
|
|
|
|
m_cs.builtinLocalInvocationId };
|
|
|
|
|
|
|
|
case DxbcOperandType::InputThreadIndexInGroup:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 1 },
|
|
|
|
m_cs.builtinLocalInvocationIndex };
|
2018-01-17 02:12:29 +01:00
|
|
|
|
2018-02-26 14:23:41 +01:00
|
|
|
case DxbcOperandType::InputCoverageMask: {
|
|
|
|
const std::array<uint32_t, 1> indices
|
|
|
|
= {{ m_module.constu32(0) }};
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
spv::StorageClassInput),
|
|
|
|
m_ps.builtinSampleMaskIn,
|
|
|
|
indices.size(), indices.data());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DxbcOperandType::OutputCoverageMask: {
|
|
|
|
const std::array<uint32_t, 1> indices
|
|
|
|
= {{ m_module.constu32(0) }};
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
spv::StorageClassOutput),
|
|
|
|
m_ps.builtinSampleMaskOut,
|
|
|
|
indices.size(), indices.data());
|
|
|
|
return result;
|
|
|
|
}
|
2018-01-29 10:54:36 +01:00
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
case DxbcOperandType::OutputDepth:
|
2018-02-08 10:28:27 +01:00
|
|
|
case DxbcOperandType::OutputDepthGe:
|
|
|
|
case DxbcOperandType::OutputDepthLe:
|
2018-01-17 02:12:29 +01:00
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Float32, 1 },
|
|
|
|
m_ps.builtinDepth };
|
2018-03-01 09:26:17 +01:00
|
|
|
|
2018-04-08 18:25:44 +02:00
|
|
|
case DxbcOperandType::InputPrimitiveId:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 1 },
|
|
|
|
m_primitiveIdIn };
|
|
|
|
|
2018-03-06 16:47:35 +01:00
|
|
|
case DxbcOperandType::InputDomainPoint:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Float32, 3 },
|
|
|
|
m_ds.builtinTessCoord };
|
|
|
|
|
2018-03-05 17:23:00 +01:00
|
|
|
case DxbcOperandType::OutputControlPointId:
|
2018-03-06 18:29:20 +01:00
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 1 },
|
|
|
|
m_hs.builtinInvocationId };
|
|
|
|
|
2018-03-01 09:26:17 +01:00
|
|
|
case DxbcOperandType::InputForkInstanceId:
|
|
|
|
case DxbcOperandType::InputJoinInstanceId:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 1 },
|
2018-03-06 18:29:20 +01:00
|
|
|
getCurrentHsForkJoinPhase()->instanceIdPtr };
|
2018-05-22 19:36:53 +02:00
|
|
|
|
|
|
|
case DxbcOperandType::InputGsInstanceId:
|
|
|
|
return DxbcRegisterPointer {
|
|
|
|
{ DxbcScalarType::Uint32, 1 },
|
|
|
|
m_gs.builtinInvocationId };
|
2017-12-29 00:51:31 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled operand type: ",
|
|
|
|
operand.type));
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-02 12:07:49 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetAtomicPointer(
|
|
|
|
const DxbcRegister& operand,
|
|
|
|
const DxbcRegister& address) {
|
|
|
|
// Query information about the resource itself
|
|
|
|
const uint32_t registerId = operand.idx[0].offset;
|
|
|
|
const DxbcBufferInfo resourceInfo = getBufferInfo(operand);
|
|
|
|
|
|
|
|
// For UAVs and shared memory, different methods
|
|
|
|
// of obtaining the final pointer are used.
|
|
|
|
const bool isUav = operand.type == DxbcOperandType::UnorderedAccessView;
|
|
|
|
|
|
|
|
// Compute the actual address into the resource
|
|
|
|
const DxbcRegisterValue addressValue = [&] {
|
|
|
|
switch (resourceInfo.type) {
|
|
|
|
case DxbcResourceType::Raw:
|
|
|
|
return emitCalcBufferIndexRaw(emitRegisterLoad(
|
|
|
|
address, DxbcRegMask(true, false, false, false)));
|
|
|
|
|
|
|
|
case DxbcResourceType::Structured: {
|
|
|
|
const DxbcRegisterValue addressComponents = emitRegisterLoad(
|
|
|
|
address, DxbcRegMask(true, true, false, false));
|
|
|
|
|
|
|
|
return emitCalcBufferIndexStructured(
|
|
|
|
emitRegisterExtract(addressComponents, DxbcRegMask(true, false, false, false)),
|
|
|
|
emitRegisterExtract(addressComponents, DxbcRegMask(false, true, false, false)),
|
|
|
|
resourceInfo.stride);
|
|
|
|
};
|
|
|
|
|
|
|
|
case DxbcResourceType::Typed: {
|
|
|
|
if (!isUav)
|
|
|
|
throw DxvkError("DxbcCompiler: TGSM cannot be typed");
|
|
|
|
|
2018-04-21 18:09:29 +02:00
|
|
|
return emitLoadTexCoord(address,
|
|
|
|
m_uavs.at(registerId).imageInfo);
|
2018-01-02 12:07:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError("DxbcCompiler: Unhandled resource type");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
// Compute the actual pointer
|
|
|
|
DxbcRegisterPointer result;
|
2018-03-30 17:31:48 +02:00
|
|
|
result.type.ctype = resourceInfo.stype;
|
2018-01-02 12:07:49 +01:00
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = isUav
|
|
|
|
? m_module.opImageTexelPointer(
|
|
|
|
m_module.defPointerType(getVectorTypeId(result.type), spv::StorageClassImage),
|
|
|
|
m_uavs.at(registerId).varId, addressValue.id, m_module.constu32(0))
|
|
|
|
: m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(getVectorTypeId(result.type), spv::StorageClassWorkgroup),
|
|
|
|
m_gRegs.at(registerId).varId, 1, &addressValue.id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRawBufferLoad(
|
|
|
|
const DxbcRegister& operand,
|
|
|
|
DxbcRegisterValue elementIndex,
|
|
|
|
DxbcRegMask writeMask) {
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(operand);
|
|
|
|
|
|
|
|
// Shared memory is the only type of buffer that
|
|
|
|
// is not accessed through a texel buffer view
|
2018-09-06 21:44:53 +02:00
|
|
|
bool isTgsm = operand.type == DxbcOperandType::ThreadGroupSharedMemory;
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2018-09-06 21:44:53 +02:00
|
|
|
// Common types and IDs used while loading the data
|
|
|
|
uint32_t bufferId = isTgsm ? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId);
|
|
|
|
|
|
|
|
uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 });
|
|
|
|
uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 });
|
2017-12-28 16:03:17 +01:00
|
|
|
|
|
|
|
// Since all data is represented as a sequence of 32-bit
|
|
|
|
// integers, we have to load each component individually.
|
2018-09-06 21:44:53 +02:00
|
|
|
std::array<uint32_t, 4> ccomps = { 0, 0, 0, 0 };
|
|
|
|
std::array<uint32_t, 4> scomps = { 0, 0, 0, 0 };
|
|
|
|
uint32_t scount = 0;
|
2017-12-28 16:03:17 +01:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
2018-09-06 21:44:53 +02:00
|
|
|
uint32_t sindex = operand.swizzle[i];
|
|
|
|
|
|
|
|
if (!writeMask[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ccomps[sindex] == 0) {
|
|
|
|
uint32_t elementIndexAdjusted = m_module.opIAdd(
|
|
|
|
getVectorTypeId(elementIndex.type), elementIndex.id,
|
|
|
|
m_module.consti32(sindex));
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2018-09-06 21:44:53 +02:00
|
|
|
// Load requested component from the buffer
|
|
|
|
uint32_t zero = 0;
|
|
|
|
|
|
|
|
switch (operand.type) {
|
|
|
|
case DxbcOperandType::Resource:
|
|
|
|
ccomps[sindex] = m_module.opCompositeExtract(scalarTypeId,
|
|
|
|
m_module.opImageFetch(vectorTypeId,
|
|
|
|
bufferId, elementIndexAdjusted,
|
|
|
|
SpirvImageOperands()), 1, &zero);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOperandType::UnorderedAccessView:
|
|
|
|
ccomps[sindex] = m_module.opCompositeExtract(scalarTypeId,
|
|
|
|
m_module.opImageRead(vectorTypeId,
|
|
|
|
bufferId, elementIndexAdjusted,
|
|
|
|
SpirvImageOperands()), 1, &zero);
|
|
|
|
break;
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2018-09-06 21:44:53 +02:00
|
|
|
case DxbcOperandType::ThreadGroupSharedMemory:
|
|
|
|
ccomps[sindex] = m_module.opLoad(scalarTypeId,
|
|
|
|
m_module.opAccessChain(bufferInfo.typeId,
|
|
|
|
bufferInfo.varId, 1, &elementIndexAdjusted));
|
|
|
|
break;
|
2017-12-28 16:03:17 +01:00
|
|
|
|
2018-09-06 21:44:53 +02:00
|
|
|
default:
|
|
|
|
throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw load");
|
2017-12-28 16:03:17 +01:00
|
|
|
}
|
2018-09-06 21:44:53 +02:00
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
}
|
|
|
|
}
|
2018-09-06 21:44:53 +02:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
|
|
uint32_t sindex = operand.swizzle[i];
|
|
|
|
|
|
|
|
if (writeMask[i])
|
|
|
|
scomps[scount++] = ccomps[sindex];
|
|
|
|
}
|
2017-12-28 16:03:17 +01:00
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
2018-09-06 21:44:53 +02:00
|
|
|
result.type.ccount = scount;
|
|
|
|
result.id = scomps[0];
|
|
|
|
|
|
|
|
if (scount > 1) {
|
|
|
|
result.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
scount, scomps.data());
|
|
|
|
}
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitRawBufferStore(
|
|
|
|
const DxbcRegister& operand,
|
|
|
|
DxbcRegisterValue elementIndex,
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(operand);
|
2017-12-28 18:37:02 +01:00
|
|
|
|
|
|
|
// Cast source value to the expected data type
|
|
|
|
value = emitRegisterBitcast(value, DxbcScalarType::Uint32);
|
|
|
|
|
2018-05-26 19:00:22 +02:00
|
|
|
// Thread Group Shared Memory is not accessed through a texel buffer view
|
|
|
|
const bool isUav = operand.type == DxbcOperandType::UnorderedAccessView;
|
2017-12-28 18:37:02 +01:00
|
|
|
|
2018-05-26 19:00:22 +02:00
|
|
|
// Perform UAV writes only if the UAV is bound and if there
|
|
|
|
// is nothing else preventing us from writing to it.
|
|
|
|
DxbcConditional cond;
|
|
|
|
|
|
|
|
if (isUav) {
|
2018-05-26 19:25:20 +02:00
|
|
|
uint32_t writeTest = emitUavWriteTest(bufferInfo);
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
cond.labelIf = m_module.allocateId();
|
|
|
|
cond.labelEnd = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
|
|
|
m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd);
|
|
|
|
|
|
|
|
m_module.opLabel(cond.labelIf);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform the actual write operation
|
|
|
|
const uint32_t bufferId = isUav ? m_module.opLoad(bufferInfo.typeId, bufferInfo.varId) : 0;
|
2017-12-28 18:37:02 +01:00
|
|
|
|
2017-12-29 12:11:19 +01:00
|
|
|
const uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 });
|
|
|
|
const uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 });
|
2017-12-28 18:37:02 +01:00
|
|
|
|
|
|
|
uint32_t srcComponentIndex = 0;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
|
|
if (operand.mask[i]) {
|
|
|
|
const uint32_t srcComponentId = value.type.ccount > 1
|
|
|
|
? m_module.opCompositeExtract(scalarTypeId,
|
|
|
|
value.id, 1, &srcComponentIndex)
|
|
|
|
: value.id;
|
|
|
|
|
|
|
|
// Add the component offset to the element index
|
|
|
|
const uint32_t elementIndexAdjusted = i != 0
|
|
|
|
? m_module.opIAdd(getVectorTypeId(elementIndex.type),
|
|
|
|
elementIndex.id, m_module.consti32(i))
|
|
|
|
: elementIndex.id;
|
|
|
|
|
|
|
|
switch (operand.type) {
|
2017-12-29 12:11:19 +01:00
|
|
|
case DxbcOperandType::UnorderedAccessView: {
|
|
|
|
const std::array<uint32_t, 4> srcVectorIds = {
|
|
|
|
srcComponentId, srcComponentId,
|
|
|
|
srcComponentId, srcComponentId,
|
|
|
|
};
|
|
|
|
|
2017-12-28 18:37:02 +01:00
|
|
|
m_module.opImageWrite(
|
|
|
|
bufferId, elementIndexAdjusted,
|
2017-12-29 12:11:19 +01:00
|
|
|
m_module.opCompositeConstruct(vectorTypeId,
|
|
|
|
4, srcVectorIds.data()),
|
2017-12-28 18:37:02 +01:00
|
|
|
SpirvImageOperands());
|
2017-12-29 12:11:19 +01:00
|
|
|
} break;
|
2017-12-28 18:37:02 +01:00
|
|
|
|
2017-12-29 00:51:31 +01:00
|
|
|
case DxbcOperandType::ThreadGroupSharedMemory:
|
|
|
|
m_module.opStore(
|
|
|
|
m_module.opAccessChain(bufferInfo.typeId,
|
|
|
|
bufferInfo.varId, 1, &elementIndexAdjusted),
|
|
|
|
srcComponentId);
|
|
|
|
break;
|
|
|
|
|
2017-12-28 18:37:02 +01:00
|
|
|
default:
|
|
|
|
throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw store");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write next component
|
|
|
|
srcComponentIndex += 1;
|
|
|
|
}
|
|
|
|
}
|
2018-05-26 19:00:22 +02:00
|
|
|
|
|
|
|
// End conditional block
|
|
|
|
if (isUav) {
|
|
|
|
m_module.opBranch(cond.labelEnd);
|
|
|
|
m_module.opLabel (cond.labelEnd);
|
|
|
|
}
|
2017-12-28 16:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-02 16:57:37 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitQueryTexelBufferSize(
|
|
|
|
const DxbcRegister& resource) {
|
|
|
|
// Load the texel buffer object. This cannot be used with
|
|
|
|
// constant buffers or any other type of resource.
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(resource);
|
|
|
|
|
|
|
|
const uint32_t bufferId = m_module.opLoad(
|
|
|
|
bufferInfo.typeId, bufferInfo.varId);
|
|
|
|
|
|
|
|
// We'll store this as a scalar unsigned integer
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opImageQuerySize(
|
|
|
|
getVectorTypeId(result.type), bufferId);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitQueryTextureLods(
|
|
|
|
const DxbcRegister& resource) {
|
|
|
|
const DxbcBufferInfo info = getBufferInfo(resource);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
2018-02-17 07:33:42 +01:00
|
|
|
|
|
|
|
if (info.image.sampled == 1) {
|
|
|
|
result.id = m_module.opImageQueryLevels(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
m_module.opLoad(info.typeId, info.varId));
|
|
|
|
} else {
|
|
|
|
// Report one LOD in case of UAVs
|
|
|
|
result.id = m_module.constu32(1);
|
|
|
|
}
|
2018-01-02 16:57:37 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-04 19:30:39 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitQueryTextureSamples(
|
|
|
|
const DxbcRegister& resource) {
|
2018-05-26 14:54:05 +02:00
|
|
|
if (resource.type == DxbcOperandType::Rasterizer) {
|
|
|
|
// SPIR-V has no gl_NumSamples equivalent, so we have
|
|
|
|
// to work around it using a specialization constant
|
|
|
|
return getSpecConstant(DxvkSpecConstantId::RasterizerSampleCount);
|
|
|
|
} else {
|
|
|
|
DxbcBufferInfo info = getBufferInfo(resource);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opImageQuerySamples(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
m_module.opLoad(info.typeId, info.varId));
|
|
|
|
return result;
|
|
|
|
}
|
2018-02-04 19:30:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-02 16:57:37 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitQueryTextureSize(
|
|
|
|
const DxbcRegister& resource,
|
|
|
|
DxbcRegisterValue lod) {
|
|
|
|
const DxbcBufferInfo info = getBufferInfo(resource);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
2018-04-12 13:57:15 +02:00
|
|
|
result.type.ccount = getTexSizeDim(info.image);
|
2018-02-04 18:08:18 +01:00
|
|
|
|
2018-02-17 07:33:42 +01:00
|
|
|
if (info.image.ms == 0 && info.image.sampled == 1) {
|
2018-02-04 18:08:18 +01:00
|
|
|
result.id = m_module.opImageQuerySizeLod(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
m_module.opLoad(info.typeId, info.varId),
|
|
|
|
lod.id);
|
|
|
|
} else {
|
|
|
|
result.id = m_module.opImageQuerySize(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
m_module.opLoad(info.typeId, info.varId));
|
|
|
|
}
|
2018-04-22 08:13:14 +02:00
|
|
|
|
2018-01-02 16:57:37 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitCalcBufferIndexStructured(
|
|
|
|
DxbcRegisterValue structId,
|
|
|
|
DxbcRegisterValue structOffset,
|
|
|
|
uint32_t structStride) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Sint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
|
|
|
|
2017-12-28 18:37:02 +01:00
|
|
|
result.id = m_module.opIAdd(typeId,
|
2018-02-04 18:08:18 +01:00
|
|
|
m_module.opIMul(typeId, structId.id, m_module.consti32(structStride / 4)),
|
|
|
|
m_module.opSDiv(typeId, structOffset.id, m_module.consti32(4)));
|
2017-12-28 16:03:17 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitCalcBufferIndexRaw(
|
|
|
|
DxbcRegisterValue byteOffset) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Sint32;
|
|
|
|
result.type.ccount = 1;
|
2017-12-28 18:37:02 +01:00
|
|
|
result.id = m_module.opSDiv(
|
2017-12-28 16:03:17 +01:00
|
|
|
getVectorTypeId(result.type),
|
|
|
|
byteOffset.id,
|
2017-12-28 18:37:02 +01:00
|
|
|
m_module.consti32(4));
|
2017-12-28 16:03:17 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-21 18:09:29 +02:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitCalcTexCoord(
|
|
|
|
DxbcRegisterValue coordVector,
|
|
|
|
const DxbcImageInfo& imageInfo) {
|
|
|
|
const uint32_t dim = getTexCoordDim(imageInfo);
|
|
|
|
|
|
|
|
if (dim != coordVector.type.ccount) {
|
|
|
|
coordVector = emitRegisterExtract(
|
|
|
|
coordVector, DxbcRegMask::firstN(dim));
|
|
|
|
}
|
|
|
|
|
|
|
|
return coordVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitLoadTexCoord(
|
|
|
|
const DxbcRegister& coordReg,
|
|
|
|
const DxbcImageInfo& imageInfo) {
|
|
|
|
return emitCalcTexCoord(emitRegisterLoad(coordReg,
|
|
|
|
DxbcRegMask(true, true, true, true)), imageInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitIndexLoad(
|
|
|
|
DxbcRegIndex index) {
|
|
|
|
if (index.relReg != nullptr) {
|
|
|
|
DxbcRegisterValue result = emitRegisterLoad(
|
|
|
|
*index.relReg, DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
if (index.offset != 0) {
|
|
|
|
result.id = m_module.opIAdd(
|
|
|
|
getVectorTypeId(result.type), result.id,
|
|
|
|
m_module.consti32(index.offset));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Sint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.consti32(index.offset);
|
|
|
|
return result;
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitValueLoad(
|
|
|
|
DxbcRegisterPointer ptr) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type = ptr.type;
|
|
|
|
result.id = m_module.opLoad(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
ptr.id);
|
2017-12-13 15:32:54 +01:00
|
|
|
return result;
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitValueStore(
|
|
|
|
DxbcRegisterPointer ptr,
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcRegMask writeMask) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// If the component types are not compatible,
|
|
|
|
// we need to bit-cast the source variable.
|
2017-12-18 00:46:44 +01:00
|
|
|
if (value.type.ctype != ptr.type.ctype)
|
|
|
|
value = emitRegisterBitcast(value, ptr.type.ctype);
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// If the source value consists of only one component,
|
|
|
|
// it is stored in all components of the destination.
|
|
|
|
if (value.type.ccount == 1)
|
2018-03-23 19:48:07 +01:00
|
|
|
value = emitRegisterExtend(value, writeMask.popCount());
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2018-03-23 19:48:07 +01:00
|
|
|
if (ptr.type.ccount == writeMask.popCount()) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// Simple case: We write to the entire register
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opStore(ptr.id, value.id);
|
2017-11-16 01:30:17 +01:00
|
|
|
} else {
|
2017-12-13 15:32:54 +01:00
|
|
|
// We only write to part of the destination
|
|
|
|
// register, so we need to load and modify it
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue tmp = emitValueLoad(ptr);
|
|
|
|
tmp = emitRegisterInsert(tmp, value, writeMask);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opStore(ptr.id, tmp.id);
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-10 18:58:17 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterLoadRaw(
|
|
|
|
const DxbcRegister& reg) {
|
2018-06-02 18:09:59 +02:00
|
|
|
return emitValueLoad(emitGetOperandPtr(reg));
|
2018-01-10 18:58:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterLoad(
|
|
|
|
const DxbcRegister& reg,
|
|
|
|
DxbcRegMask writeMask) {
|
2018-06-07 15:05:06 +02:00
|
|
|
if (reg.type == DxbcOperandType::Imm32
|
|
|
|
|| reg.type == DxbcOperandType::Imm64) {
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
|
|
|
|
if (reg.componentCount == DxbcComponentCount::Component1) {
|
|
|
|
// Create one single u32 constant
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.constu32(reg.imm.u32_1);
|
|
|
|
} else if (reg.componentCount == DxbcComponentCount::Component4) {
|
2017-12-18 18:02:15 +01:00
|
|
|
// Create a u32 vector with as many components as needed
|
|
|
|
std::array<uint32_t, 4> indices;
|
|
|
|
uint32_t indexId = 0;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < indices.size(); i++) {
|
|
|
|
if (writeMask[i]) {
|
|
|
|
indices.at(indexId++) =
|
|
|
|
m_module.constu32(reg.imm.u32_4[i]);
|
|
|
|
}
|
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
2018-03-23 19:48:07 +01:00
|
|
|
result.type.ccount = writeMask.popCount();
|
2017-12-18 18:02:15 +01:00
|
|
|
result.id = indices.at(0);
|
|
|
|
|
|
|
|
if (indexId > 1) {
|
|
|
|
result.id = m_module.constComposite(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
result.type.ccount, indices.data());
|
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
} else {
|
|
|
|
// Something went horribly wrong in the decoder or the shader is broken
|
|
|
|
throw DxvkError("DxbcCompiler: Invalid component count for immediate operand");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cast constants to the requested type
|
|
|
|
return emitRegisterBitcast(result, reg.dataType);
|
|
|
|
} else {
|
|
|
|
// Load operand from the operand pointer
|
2018-01-10 18:58:17 +01:00
|
|
|
DxbcRegisterValue result = emitRegisterLoadRaw(reg);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
// Apply operand swizzle to the operand value
|
|
|
|
result = emitRegisterSwizzle(result, reg.swizzle, writeMask);
|
|
|
|
|
|
|
|
// Cast it to the requested type. We need to do
|
|
|
|
// this after the swizzling for 64-bit types.
|
|
|
|
result = emitRegisterBitcast(result, reg.dataType);
|
|
|
|
|
|
|
|
// Apply operand modifiers
|
|
|
|
result = emitSrcOperandModifiers(result, reg.modifiers);
|
|
|
|
return result;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitRegisterStore(
|
|
|
|
const DxbcRegister& reg,
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
emitValueStore(emitGetOperandPtr(reg), value, reg.mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-26 14:54:05 +02:00
|
|
|
DxbcRegisterValue DxbcCompiler::getSpecConstant(DxvkSpecConstantId specId) {
|
|
|
|
const uint32_t specIdOffset = uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin);
|
|
|
|
|
|
|
|
// Look up spec constant in the array
|
|
|
|
DxbcRegisterValue value = m_specConstants.at(specIdOffset);
|
|
|
|
|
|
|
|
if (value.id != 0)
|
|
|
|
return value;
|
|
|
|
|
|
|
|
// Declare a new specialization constant if needed
|
|
|
|
DxbcSpecConstant info = getSpecConstantProperties(specId);
|
|
|
|
|
|
|
|
value.type.ctype = info.ctype;
|
|
|
|
value.type.ccount = info.ccount;
|
|
|
|
value.id = m_module.specConst32(
|
|
|
|
getVectorTypeId(value.type),
|
|
|
|
info.value);
|
|
|
|
|
|
|
|
m_module.decorateSpecId(value.id, uint32_t(specId));
|
|
|
|
m_module.setDebugName(value.id, info.name);
|
|
|
|
|
|
|
|
m_specConstants.at(specIdOffset) = value;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcSpecConstant DxbcCompiler::getSpecConstantProperties(DxvkSpecConstantId specId) {
|
|
|
|
static const std::array<DxbcSpecConstant,
|
|
|
|
uint32_t(DxvkSpecConstantId::SpecConstantIdMax) -
|
|
|
|
uint32_t(DxvkSpecConstantId::SpecConstantIdMin) + 1> s_specConstants = {{
|
|
|
|
{ DxbcScalarType::Uint32, 1, 1, "RasterizerSampleCount" },
|
|
|
|
}};
|
|
|
|
|
|
|
|
return s_specConstants.at(uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
void DxbcCompiler::emitInputSetup() {
|
|
|
|
// Copy all defined v# registers into the input array
|
|
|
|
const uint32_t vecTypeId = m_module.defVectorType(m_module.defFloatType(32), 4);
|
|
|
|
const uint32_t ptrTypeId = m_module.defPointerType(vecTypeId, spv::StorageClassPrivate);
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
for (uint32_t i = 0; i < m_vRegs.size(); i++) {
|
2018-07-01 12:44:37 +02:00
|
|
|
if (m_vRegs.at(i).id != 0) {
|
2017-12-21 16:00:36 +01:00
|
|
|
const uint32_t registerId = m_module.consti32(i);
|
2018-07-01 15:24:21 +02:00
|
|
|
|
|
|
|
DxbcRegisterPointer srcPtr = m_vRegs.at(i);
|
|
|
|
DxbcRegisterValue srcValue = emitRegisterBitcast(
|
|
|
|
emitValueLoad(srcPtr), DxbcScalarType::Float32);
|
|
|
|
|
|
|
|
DxbcRegisterPointer dstPtr;
|
|
|
|
dstPtr.type = { DxbcScalarType::Float32, 4 };
|
|
|
|
dstPtr.id = m_module.opAccessChain(
|
|
|
|
ptrTypeId, m_vArray, 1, ®isterId);
|
2018-01-07 21:04:23 +01:00
|
|
|
|
2018-07-01 15:24:21 +02:00
|
|
|
emitValueStore(dstPtr, srcValue, DxbcRegMask::firstN(srcValue.type.ccount));
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Copy all system value registers into the array,
|
|
|
|
// preserving any previously written contents.
|
2017-12-21 16:00:36 +01:00
|
|
|
for (const DxbcSvMapping& map : m_vMappings) {
|
|
|
|
const uint32_t registerId = m_module.consti32(map.regId);
|
|
|
|
|
|
|
|
const DxbcRegisterValue value = [&] {
|
2018-10-08 09:34:56 +02:00
|
|
|
switch (m_programInfo.type()) {
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcProgramType::VertexShader: return emitVsSystemValueLoad(map.sv, map.regMask);
|
|
|
|
case DxbcProgramType::PixelShader: return emitPsSystemValueLoad(map.sv, map.regMask);
|
2018-10-08 09:34:56 +02:00
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Unexpected stage: ", m_programInfo.type()));
|
2017-12-21 16:00:36 +01:00
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
DxbcRegisterPointer inputReg;
|
|
|
|
inputReg.type.ctype = DxbcScalarType::Float32;
|
|
|
|
inputReg.type.ccount = 4;
|
|
|
|
inputReg.id = m_module.opAccessChain(
|
|
|
|
ptrTypeId, m_vArray, 1, ®isterId);
|
|
|
|
emitValueStore(inputReg, value, map.regMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitInputSetup(uint32_t vertexCount) {
|
|
|
|
// Copy all defined v# registers into the input array. Note
|
|
|
|
// that the outer index of the array is the vertex index.
|
|
|
|
const uint32_t vecTypeId = m_module.defVectorType(m_module.defFloatType(32), 4);
|
|
|
|
const uint32_t dstPtrTypeId = m_module.defPointerType(vecTypeId, spv::StorageClassPrivate);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < m_vRegs.size(); i++) {
|
2018-07-01 12:44:37 +02:00
|
|
|
if (m_vRegs.at(i).id != 0) {
|
2017-12-21 16:00:36 +01:00
|
|
|
const uint32_t registerId = m_module.consti32(i);
|
|
|
|
|
|
|
|
for (uint32_t v = 0; v < vertexCount; v++) {
|
2018-01-07 21:04:23 +01:00
|
|
|
std::array<uint32_t, 2> indices
|
|
|
|
= {{ m_module.consti32(v), registerId }};
|
|
|
|
|
2018-07-01 15:24:21 +02:00
|
|
|
DxbcRegisterPointer srcPtr;
|
|
|
|
srcPtr.type = m_vRegs.at(i).type;
|
|
|
|
srcPtr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(getVectorTypeId(srcPtr.type), spv::StorageClassInput),
|
|
|
|
m_vRegs.at(i).id, 1, indices.data());
|
|
|
|
|
|
|
|
DxbcRegisterValue srcValue = emitRegisterBitcast(
|
|
|
|
emitValueLoad(srcPtr), DxbcScalarType::Float32);
|
2017-12-21 16:00:36 +01:00
|
|
|
|
2018-07-01 15:24:21 +02:00
|
|
|
DxbcRegisterPointer dstPtr;
|
|
|
|
dstPtr.type = { DxbcScalarType::Float32, 4 };
|
|
|
|
dstPtr.id = m_module.opAccessChain(
|
|
|
|
dstPtrTypeId, m_vArray, 2, indices.data());
|
|
|
|
|
|
|
|
emitValueStore(dstPtr, srcValue, DxbcRegMask::firstN(srcValue.type.ccount));
|
2017-12-21 16:00:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy all system value registers into the array,
|
|
|
|
// preserving any previously written contents.
|
|
|
|
for (const DxbcSvMapping& map : m_vMappings) {
|
|
|
|
const uint32_t registerId = m_module.consti32(map.regId);
|
|
|
|
|
|
|
|
for (uint32_t v = 0; v < vertexCount; v++) {
|
|
|
|
const DxbcRegisterValue value = [&] {
|
2018-10-08 09:34:56 +02:00
|
|
|
switch (m_programInfo.type()) {
|
2017-12-21 16:00:36 +01:00
|
|
|
case DxbcProgramType::GeometryShader: return emitGsSystemValueLoad(map.sv, map.regMask, v);
|
2018-10-08 09:34:56 +02:00
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Unexpected stage: ", m_programInfo.type()));
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
2017-12-21 16:00:36 +01:00
|
|
|
}();
|
|
|
|
|
|
|
|
std::array<uint32_t, 2> indices = {
|
|
|
|
m_module.consti32(v), registerId,
|
|
|
|
};
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
DxbcRegisterPointer inputReg;
|
|
|
|
inputReg.type.ctype = DxbcScalarType::Float32;
|
|
|
|
inputReg.type.ccount = 4;
|
|
|
|
inputReg.id = m_module.opAccessChain(dstPtrTypeId,
|
|
|
|
m_vArray, indices.size(), indices.data());
|
|
|
|
emitValueStore(inputReg, value, map.regMask);
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
void DxbcCompiler::emitOutputSetup() {
|
2017-12-18 16:41:05 +01:00
|
|
|
for (const DxbcSvMapping& svMapping : m_oMappings) {
|
2018-07-01 12:44:37 +02:00
|
|
|
DxbcRegisterPointer outputReg = m_oRegs.at(svMapping.regId);
|
2017-12-21 16:00:36 +01:00
|
|
|
|
2018-10-08 09:34:56 +02:00
|
|
|
if (m_programInfo.type() == DxbcProgramType::HullShader) {
|
2018-03-06 14:49:11 +01:00
|
|
|
uint32_t registerIndex = m_module.constu32(svMapping.regId);
|
|
|
|
|
2018-07-01 15:24:21 +02:00
|
|
|
outputReg.type = { DxbcScalarType::Float32, 4 };
|
2018-03-06 14:49:11 +01:00
|
|
|
outputReg.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(outputReg.type),
|
2018-09-13 14:54:54 +02:00
|
|
|
spv::StorageClassPrivate),
|
2018-03-06 14:49:11 +01:00
|
|
|
m_hs.outputPerPatch,
|
|
|
|
1, ®isterIndex);
|
|
|
|
}
|
|
|
|
|
2018-02-04 23:09:07 +01:00
|
|
|
auto sv = svMapping.sv;
|
|
|
|
auto mask = svMapping.regMask;
|
|
|
|
auto value = emitValueLoad(outputReg);
|
|
|
|
|
2018-10-08 09:34:56 +02:00
|
|
|
switch (m_programInfo.type()) {
|
2018-02-04 23:09:07 +01:00
|
|
|
case DxbcProgramType::VertexShader: emitVsSystemValueStore(sv, mask, value); break;
|
|
|
|
case DxbcProgramType::GeometryShader: emitGsSystemValueStore(sv, mask, value); break;
|
2018-03-06 14:49:11 +01:00
|
|
|
case DxbcProgramType::HullShader: emitHsSystemValueStore(sv, mask, value); break;
|
2018-03-06 16:47:35 +01:00
|
|
|
case DxbcProgramType::DomainShader: emitDsSystemValueStore(sv, mask, value); break;
|
2018-04-18 21:14:34 +02:00
|
|
|
case DxbcProgramType::PixelShader: emitPsSystemValueStore(sv, mask, value); break;
|
2018-03-06 16:47:35 +01:00
|
|
|
case DxbcProgramType::ComputeShader: break;
|
2018-02-04 23:09:07 +01:00
|
|
|
}
|
2017-12-21 16:00:36 +01:00
|
|
|
}
|
|
|
|
}
|
2018-09-01 17:56:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitOutputMapping() {
|
|
|
|
// For pixel shaders, we need to swizzle the
|
|
|
|
// output vectors using some spec constants.
|
|
|
|
for (uint32_t i = 0; i < m_oRegs.size(); i++) {
|
|
|
|
if (m_oRegs[i].id == 0 || m_oRegs[i].type.ccount < 2)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DxbcRegisterValue vector = emitValueLoad(m_oRegs[i]);
|
|
|
|
|
|
|
|
uint32_t specTypeId = getScalarTypeId(DxbcScalarType::Uint32);
|
|
|
|
uint32_t compTypeId = getScalarTypeId(vector.type.ctype);
|
|
|
|
|
|
|
|
std::array<uint32_t, 4> scalars;
|
|
|
|
|
|
|
|
for (uint32_t c = 0; c < vector.type.ccount; c++) {
|
|
|
|
const char* components = "rgba";
|
|
|
|
|
|
|
|
uint32_t specId = m_module.specConst32(specTypeId, c);
|
|
|
|
m_module.decorateSpecId(specId, uint32_t(DxvkSpecConstantId::ColorComponentMappings) + 4 * i + c);
|
|
|
|
m_module.setDebugName(specId, str::format("omap", i, ".", components[c]).c_str());
|
|
|
|
|
|
|
|
scalars[c] = m_module.opVectorExtractDynamic(compTypeId, vector.id, specId);
|
|
|
|
}
|
|
|
|
|
|
|
|
vector.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(vector.type),
|
|
|
|
vector.type.ccount,
|
|
|
|
scalars.data());
|
|
|
|
|
|
|
|
emitValueStore(m_oRegs[i], vector,
|
|
|
|
DxbcRegMask::firstN(vector.type.ccount));
|
|
|
|
}
|
|
|
|
}
|
2017-12-21 16:00:36 +01:00
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitVsSystemValueLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::VertexId: {
|
2017-12-27 12:49:25 +01:00
|
|
|
const uint32_t typeId = getScalarTypeId(DxbcScalarType::Uint32);
|
|
|
|
|
2018-01-29 10:41:41 +01:00
|
|
|
if (m_vs.builtinVertexId == 0) {
|
|
|
|
m_vs.builtinVertexId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInVertexIndex,
|
|
|
|
"vs_vertex_index");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_vs.builtinBaseVertex == 0) {
|
|
|
|
m_vs.builtinBaseVertex = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInBaseVertex,
|
|
|
|
"vs_base_vertex");
|
|
|
|
}
|
|
|
|
|
2017-12-27 12:49:25 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opISub(typeId,
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinVertexId),
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinBaseVertex));
|
|
|
|
return result;
|
2017-12-21 16:00:36 +01:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcSystemValue::InstanceId: {
|
2017-12-27 12:49:25 +01:00
|
|
|
const uint32_t typeId = getScalarTypeId(DxbcScalarType::Uint32);
|
|
|
|
|
2018-01-29 10:41:41 +01:00
|
|
|
if (m_vs.builtinInstanceId == 0) {
|
|
|
|
m_vs.builtinInstanceId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInInstanceIndex,
|
|
|
|
"vs_instance_index");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_vs.builtinBaseInstance == 0) {
|
|
|
|
m_vs.builtinBaseInstance = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInBaseInstance,
|
|
|
|
"vs_base_instance");
|
|
|
|
}
|
|
|
|
|
2017-12-27 12:49:25 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opISub(typeId,
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinInstanceId),
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinBaseInstance));
|
|
|
|
return result;
|
2017-12-21 16:00:36 +01:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled VS SV input: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitGsSystemValueLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
uint32_t vertexId) {
|
2018-03-24 16:23:31 +01:00
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position: {
|
|
|
|
const std::array<uint32_t, 2> indices = {
|
|
|
|
m_module.consti32(vertexId),
|
|
|
|
m_module.consti32(PerVertex_Position),
|
|
|
|
};
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptrIn;
|
|
|
|
ptrIn.type.ctype = DxbcScalarType::Float32;
|
|
|
|
ptrIn.type.ccount = 4;
|
|
|
|
|
|
|
|
ptrIn.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(ptrIn.type),
|
|
|
|
spv::StorageClassInput),
|
|
|
|
m_perVertexIn,
|
|
|
|
indices.size(),
|
|
|
|
indices.data());
|
|
|
|
|
|
|
|
return emitRegisterExtract(
|
|
|
|
emitValueLoad(ptrIn), mask);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled GS SV input: ", sv));
|
|
|
|
}
|
2017-12-21 16:00:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitPsSystemValueLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position: {
|
2018-01-29 10:41:41 +01:00
|
|
|
if (m_ps.builtinFragCoord == 0) {
|
|
|
|
m_ps.builtinFragCoord = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Float32, 4, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInFragCoord,
|
|
|
|
"ps_frag_coord");
|
|
|
|
}
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
DxbcRegisterPointer ptrIn;
|
2018-04-05 20:10:00 +02:00
|
|
|
ptrIn.type = { DxbcScalarType::Float32, 4 };
|
2017-12-21 16:00:36 +01:00
|
|
|
ptrIn.id = m_ps.builtinFragCoord;
|
|
|
|
|
2018-04-05 20:10:00 +02:00
|
|
|
// The X, Y and Z components of the SV_POSITION semantic
|
|
|
|
// are identical to Vulkan's FragCoord builtin, but we
|
|
|
|
// need to compute the reciprocal of the W component.
|
|
|
|
DxbcRegisterValue fragCoord = emitValueLoad(ptrIn);
|
|
|
|
|
|
|
|
uint32_t componentIndex = 3;
|
|
|
|
uint32_t t_f32 = m_module.defFloatType(32);
|
|
|
|
uint32_t v_wComp = m_module.opCompositeExtract(t_f32, fragCoord.id, 1, &componentIndex);
|
|
|
|
v_wComp = m_module.opFDiv(t_f32, m_module.constf32(1.0f), v_wComp);
|
|
|
|
|
|
|
|
fragCoord.id = m_module.opCompositeInsert(
|
|
|
|
getVectorTypeId(fragCoord.type),
|
|
|
|
v_wComp, fragCoord.id,
|
|
|
|
1, &componentIndex);
|
|
|
|
|
|
|
|
return emitRegisterExtract(fragCoord, mask);
|
2017-12-21 16:00:36 +01:00
|
|
|
} break;
|
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
case DxbcSystemValue::IsFrontFace: {
|
2018-01-29 10:41:41 +01:00
|
|
|
if (m_ps.builtinIsFrontFace == 0) {
|
|
|
|
m_ps.builtinIsFrontFace = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Bool, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInFrontFacing,
|
|
|
|
"ps_is_front_face");
|
|
|
|
}
|
|
|
|
|
2018-01-17 02:12:29 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opSelect(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
m_module.opLoad(
|
|
|
|
m_module.defBoolType(),
|
|
|
|
m_ps.builtinIsFrontFace),
|
|
|
|
m_module.constu32(0xFFFFFFFF),
|
|
|
|
m_module.constu32(0x00000000));
|
|
|
|
return result;
|
|
|
|
} break;
|
|
|
|
|
2018-04-08 18:25:44 +02:00
|
|
|
case DxbcSystemValue::PrimitiveId: {
|
|
|
|
if (m_primitiveIdIn == 0) {
|
|
|
|
m_module.enableCapability(spv::CapabilityGeometry);
|
|
|
|
|
|
|
|
m_primitiveIdIn = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInPrimitiveId,
|
|
|
|
"ps_primitive_id");
|
|
|
|
}
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptrIn;
|
|
|
|
ptrIn.type = { DxbcScalarType::Uint32, 1 };
|
|
|
|
ptrIn.id = m_primitiveIdIn;
|
|
|
|
|
|
|
|
return emitValueLoad(ptrIn);
|
|
|
|
} break;
|
|
|
|
|
2018-01-28 15:32:35 +01:00
|
|
|
case DxbcSystemValue::SampleIndex: {
|
2018-01-29 10:41:41 +01:00
|
|
|
if (m_ps.builtinSampleId == 0) {
|
|
|
|
m_module.enableCapability(spv::CapabilitySampleRateShading);
|
|
|
|
|
|
|
|
m_ps.builtinSampleId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInSampleId,
|
|
|
|
"ps_sample_id");
|
|
|
|
}
|
|
|
|
|
2018-01-28 15:32:35 +01:00
|
|
|
DxbcRegisterPointer ptrIn;
|
|
|
|
ptrIn.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
ptrIn.type.ccount = 1;
|
|
|
|
ptrIn.id = m_ps.builtinSampleId;
|
|
|
|
|
2018-02-15 18:07:40 +01:00
|
|
|
return emitValueLoad(ptrIn);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcSystemValue::RenderTargetId: {
|
|
|
|
if (m_ps.builtinLayer == 0) {
|
2018-02-16 09:59:41 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityGeometry);
|
|
|
|
|
2018-02-15 18:07:40 +01:00
|
|
|
m_ps.builtinLayer = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInLayer,
|
2018-06-27 12:02:54 +02:00
|
|
|
"v_layer");
|
2018-02-15 18:07:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
ptr.type.ccount = 1;
|
|
|
|
ptr.id = m_ps.builtinLayer;
|
|
|
|
|
|
|
|
return emitValueLoad(ptr);
|
2018-01-28 15:32:35 +01:00
|
|
|
} break;
|
|
|
|
|
2018-06-27 12:02:54 +02:00
|
|
|
case DxbcSystemValue::ViewportId: {
|
|
|
|
if (m_ps.builtinViewportId == 0) {
|
|
|
|
m_module.enableCapability(spv::CapabilityMultiViewport);
|
|
|
|
|
|
|
|
m_ps.builtinViewportId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInViewportIndex,
|
|
|
|
"v_viewport");
|
|
|
|
}
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
ptr.type.ccount = 1;
|
|
|
|
ptr.id = m_ps.builtinViewportId;
|
|
|
|
|
|
|
|
return emitValueLoad(ptr);
|
|
|
|
} break;
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled PS SV input: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitVsSystemValueStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
const DxbcRegisterValue& value) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position: {
|
|
|
|
const uint32_t memberId = m_module.consti32(PerVertex_Position);
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type.ctype = DxbcScalarType::Float32;
|
|
|
|
ptr.type.ccount = 4;
|
|
|
|
|
|
|
|
ptr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(ptr.type),
|
|
|
|
spv::StorageClassOutput),
|
|
|
|
m_perVertexOut, 1, &memberId);
|
|
|
|
|
|
|
|
emitValueStore(ptr, value, mask);
|
|
|
|
} break;
|
|
|
|
|
2018-06-01 13:57:26 +02:00
|
|
|
case DxbcSystemValue::RenderTargetId: {
|
2018-10-08 09:34:56 +02:00
|
|
|
if (m_programInfo.type() != DxbcProgramType::GeometryShader)
|
2018-06-01 13:57:26 +02:00
|
|
|
enableShaderViewportIndexLayer();
|
|
|
|
|
|
|
|
if (m_gs.builtinLayer == 0) {
|
2018-06-27 12:02:54 +02:00
|
|
|
m_module.enableCapability(spv::CapabilityGeometry);
|
|
|
|
|
2018-06-01 13:57:26 +02:00
|
|
|
m_gs.builtinLayer = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassOutput },
|
|
|
|
spv::BuiltInLayer,
|
|
|
|
"o_layer");
|
|
|
|
}
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type = { DxbcScalarType::Uint32, 1 };
|
|
|
|
ptr.id = m_gs.builtinLayer;
|
|
|
|
|
|
|
|
emitValueStore(
|
|
|
|
ptr, emitRegisterExtract(value, mask),
|
|
|
|
DxbcRegMask(true, false, false, false));
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcSystemValue::ViewportId: {
|
2018-10-08 09:34:56 +02:00
|
|
|
if (m_programInfo.type() != DxbcProgramType::GeometryShader)
|
2018-06-01 13:57:26 +02:00
|
|
|
enableShaderViewportIndexLayer();
|
|
|
|
|
|
|
|
if (m_gs.builtinViewportId == 0) {
|
|
|
|
m_module.enableCapability(spv::CapabilityMultiViewport);
|
|
|
|
|
|
|
|
m_gs.builtinViewportId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassOutput },
|
|
|
|
spv::BuiltInViewportIndex,
|
|
|
|
"o_viewport");
|
|
|
|
}
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type = { DxbcScalarType::Uint32, 1};
|
|
|
|
ptr.id = m_gs.builtinViewportId;
|
|
|
|
|
|
|
|
emitValueStore(
|
|
|
|
ptr, emitRegisterExtract(value, mask),
|
|
|
|
DxbcRegMask(true, false, false, false));
|
|
|
|
} break;
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled VS SV output: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-06 14:49:11 +01:00
|
|
|
void DxbcCompiler::emitHsSystemValueStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
const DxbcRegisterValue& value) {
|
|
|
|
if (sv >= DxbcSystemValue::FinalQuadUeq0EdgeTessFactor
|
|
|
|
&& sv <= DxbcSystemValue::FinalLineDensityTessFactor) {
|
|
|
|
struct TessFactor {
|
|
|
|
uint32_t array = 0;
|
|
|
|
uint32_t index = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const std::array<TessFactor, 12> s_tessFactors = {{
|
|
|
|
{ m_hs.builtinTessLevelOuter, 0 }, // FinalQuadUeq0EdgeTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 1 }, // FinalQuadVeq0EdgeTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 2 }, // FinalQuadUeq1EdgeTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 3 }, // FinalQuadVeq1EdgeTessFactor
|
|
|
|
{ m_hs.builtinTessLevelInner, 0 }, // FinalQuadUInsideTessFactor
|
|
|
|
{ m_hs.builtinTessLevelInner, 1 }, // FinalQuadVInsideTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 0 }, // FinalTriUeq0EdgeTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 1 }, // FinalTriVeq0EdgeTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 2 }, // FinalTriWeq0EdgeTessFactor
|
|
|
|
{ m_hs.builtinTessLevelInner, 0 }, // FinalTriInsideTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 0 }, // FinalLineDetailTessFactor
|
|
|
|
{ m_hs.builtinTessLevelOuter, 1 }, // FinalLineDensityTessFactor
|
|
|
|
}};
|
|
|
|
|
2018-07-01 15:24:21 +02:00
|
|
|
const TessFactor tessFactor = s_tessFactors.at(uint32_t(sv)
|
|
|
|
- uint32_t(DxbcSystemValue::FinalQuadUeq0EdgeTessFactor));
|
2018-03-06 14:49:11 +01:00
|
|
|
|
|
|
|
const uint32_t tessFactorArrayIndex
|
|
|
|
= m_module.constu32(tessFactor.index);
|
|
|
|
|
2018-03-10 15:02:27 +01:00
|
|
|
// Apply global tess factor limit
|
2018-09-09 23:14:00 +02:00
|
|
|
float maxTessFactor = m_hs.maxTessFactor;
|
|
|
|
|
|
|
|
if (m_moduleInfo.tess != nullptr) {
|
|
|
|
if (m_moduleInfo.tess->maxTessFactor < maxTessFactor)
|
|
|
|
maxTessFactor = m_moduleInfo.tess->maxTessFactor;
|
|
|
|
}
|
|
|
|
|
2018-03-10 15:02:27 +01:00
|
|
|
DxbcRegisterValue tessValue = emitRegisterExtract(value, mask);
|
|
|
|
tessValue.id = m_module.opFClamp(getVectorTypeId(tessValue.type),
|
|
|
|
tessValue.id, m_module.constf32(0.0f),
|
2018-09-09 23:14:00 +02:00
|
|
|
m_module.constf32(maxTessFactor));
|
2018-03-10 15:02:27 +01:00
|
|
|
|
2018-03-06 14:49:11 +01:00
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type.ctype = DxbcScalarType::Float32;
|
|
|
|
ptr.type.ccount = 1;
|
|
|
|
ptr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(ptr.type),
|
|
|
|
spv::StorageClassOutput),
|
|
|
|
tessFactor.array, 1,
|
|
|
|
&tessFactorArrayIndex);
|
|
|
|
|
2018-03-10 15:02:27 +01:00
|
|
|
emitValueStore(ptr, tessValue,
|
2018-03-06 14:49:11 +01:00
|
|
|
DxbcRegMask(true, false, false, false));
|
|
|
|
} else {
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled HS SV output: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
void DxbcCompiler::emitGsSystemValueStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
const DxbcRegisterValue& value) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position:
|
|
|
|
case DxbcSystemValue::CullDistance:
|
|
|
|
case DxbcSystemValue::ClipDistance:
|
2018-06-01 13:57:26 +02:00
|
|
|
case DxbcSystemValue::RenderTargetId:
|
|
|
|
case DxbcSystemValue::ViewportId:
|
2017-12-21 16:00:36 +01:00
|
|
|
emitVsSystemValueStore(sv, mask, value);
|
|
|
|
break;
|
|
|
|
|
2018-04-08 18:25:44 +02:00
|
|
|
case DxbcSystemValue::PrimitiveId: {
|
|
|
|
if (m_primitiveIdOut == 0) {
|
|
|
|
m_primitiveIdOut = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassOutput },
|
|
|
|
spv::BuiltInPrimitiveId,
|
|
|
|
"gs_primitive_id");
|
|
|
|
}
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type = { DxbcScalarType::Uint32, 1};
|
|
|
|
ptr.id = m_primitiveIdOut;
|
2018-03-26 23:32:07 +02:00
|
|
|
|
|
|
|
emitValueStore(
|
|
|
|
ptr, emitRegisterExtract(value, mask),
|
|
|
|
DxbcRegMask(true, false, false, false));
|
|
|
|
} break;
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled GS SV output: ", sv));
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-18 21:14:34 +02:00
|
|
|
void DxbcCompiler::emitPsSystemValueStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
const DxbcRegisterValue& value) {
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled GS SV output: ", sv));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-06 16:47:35 +01:00
|
|
|
void DxbcCompiler::emitDsSystemValueStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
const DxbcRegisterValue& value) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position:
|
|
|
|
case DxbcSystemValue::CullDistance:
|
|
|
|
case DxbcSystemValue::ClipDistance:
|
2018-06-01 13:57:26 +02:00
|
|
|
case DxbcSystemValue::RenderTargetId:
|
|
|
|
case DxbcSystemValue::ViewportId:
|
2018-03-06 16:47:35 +01:00
|
|
|
emitVsSystemValueStore(sv, mask, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled DS SV output: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-24 11:29:07 +01:00
|
|
|
void DxbcCompiler::emitClipCullStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
uint32_t dstArray) {
|
|
|
|
uint32_t offset = 0;
|
|
|
|
|
|
|
|
if (dstArray == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (auto e = m_osgn->begin(); e != m_osgn->end(); e++) {
|
|
|
|
if (e->systemValue == sv) {
|
2018-07-01 12:44:37 +02:00
|
|
|
DxbcRegisterPointer srcPtr = m_oRegs.at(e->registerId);
|
2018-03-24 11:29:07 +01:00
|
|
|
DxbcRegisterValue srcValue = emitValueLoad(srcPtr);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
|
|
if (e->componentMask[i]) {
|
|
|
|
uint32_t offsetId = m_module.consti32(offset++);
|
|
|
|
|
|
|
|
DxbcRegisterValue component = emitRegisterExtract(
|
|
|
|
srcValue, DxbcRegMask::select(i));
|
|
|
|
|
|
|
|
DxbcRegisterPointer dstPtr;
|
|
|
|
dstPtr.type = { DxbcScalarType::Float32, 1 };
|
|
|
|
dstPtr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(dstPtr.type),
|
|
|
|
spv::StorageClassOutput),
|
|
|
|
dstArray, 1, &offsetId);
|
|
|
|
|
|
|
|
emitValueStore(dstPtr, component,
|
|
|
|
DxbcRegMask(true, false, false, false));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitClipCullLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
uint32_t srcArray) {
|
|
|
|
uint32_t offset = 0;
|
|
|
|
|
|
|
|
if (srcArray == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (auto e = m_isgn->begin(); e != m_isgn->end(); e++) {
|
|
|
|
if (e->systemValue == sv) {
|
|
|
|
// Load individual components from the source array
|
|
|
|
uint32_t componentIndex = 0;
|
|
|
|
std::array<uint32_t, 4> componentIds = {{ 0, 0, 0, 0 }};
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
|
|
if (e->componentMask[i]) {
|
|
|
|
uint32_t offsetId = m_module.consti32(offset++);
|
|
|
|
|
|
|
|
DxbcRegisterPointer srcPtr;
|
|
|
|
srcPtr.type = { DxbcScalarType::Float32, 1 };
|
|
|
|
srcPtr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(srcPtr.type),
|
|
|
|
spv::StorageClassInput),
|
|
|
|
srcArray, 1, &offsetId);
|
|
|
|
|
|
|
|
componentIds[componentIndex++]
|
|
|
|
= emitValueLoad(srcPtr).id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put everything into one vector
|
|
|
|
DxbcRegisterValue dstValue;
|
|
|
|
dstValue.type = { DxbcScalarType::Float32, componentIndex };
|
|
|
|
dstValue.id = componentIds[0];
|
|
|
|
|
|
|
|
if (componentIndex > 1) {
|
|
|
|
dstValue.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(dstValue.type),
|
|
|
|
componentIndex, componentIds.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store vector to the input array
|
|
|
|
uint32_t registerId = m_module.consti32(e->registerId);
|
|
|
|
|
|
|
|
DxbcRegisterPointer dstInput;
|
|
|
|
dstInput.type = { DxbcScalarType::Float32, 4 };
|
|
|
|
dstInput.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(dstInput.type),
|
|
|
|
spv::StorageClassPrivate),
|
|
|
|
m_vArray, 1, ®isterId);
|
|
|
|
|
|
|
|
emitValueStore(dstInput, dstValue, e->componentMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-26 19:25:20 +02:00
|
|
|
uint32_t DxbcCompiler::emitUavWriteTest(const DxbcBufferInfo& uav) {
|
|
|
|
uint32_t typeId = m_module.defBoolType();
|
|
|
|
uint32_t testId = uav.specId;
|
|
|
|
|
|
|
|
if (m_ps.killState != 0) {
|
|
|
|
uint32_t killState = m_module.opLoad(typeId, m_ps.killState);
|
|
|
|
|
|
|
|
testId = m_module.opLogicalAnd(typeId, testId,
|
|
|
|
m_module.opLogicalNot(typeId, killState));
|
|
|
|
}
|
|
|
|
|
|
|
|
return testId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-01 18:07:24 +01:00
|
|
|
void DxbcCompiler::emitInit() {
|
|
|
|
// Set up common capabilities for all shaders
|
|
|
|
m_module.enableCapability(spv::CapabilityShader);
|
|
|
|
m_module.enableCapability(spv::CapabilityImageQuery);
|
|
|
|
|
|
|
|
// Initialize the shader module with capabilities
|
|
|
|
// etc. Each shader type has its own peculiarities.
|
2018-10-08 09:34:56 +02:00
|
|
|
switch (m_programInfo.type()) {
|
2018-02-01 18:07:24 +01:00
|
|
|
case DxbcProgramType::VertexShader: emitVsInit(); break;
|
2018-03-01 07:00:54 +01:00
|
|
|
case DxbcProgramType::HullShader: emitHsInit(); break;
|
|
|
|
case DxbcProgramType::DomainShader: emitDsInit(); break;
|
2018-02-01 18:07:24 +01:00
|
|
|
case DxbcProgramType::GeometryShader: emitGsInit(); break;
|
|
|
|
case DxbcProgramType::PixelShader: emitPsInit(); break;
|
|
|
|
case DxbcProgramType::ComputeShader: emitCsInit(); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
void DxbcCompiler::emitFunctionBegin(
|
|
|
|
uint32_t entryPoint,
|
|
|
|
uint32_t returnType,
|
|
|
|
uint32_t funcType) {
|
|
|
|
this->emitFunctionEnd();
|
|
|
|
|
2018-03-06 15:52:29 +01:00
|
|
|
m_module.functionBegin(
|
2018-08-27 12:47:52 +02:00
|
|
|
returnType, entryPoint, funcType,
|
2018-03-06 15:52:29 +01:00
|
|
|
spv::FunctionControlMaskNone);
|
2018-08-27 12:47:52 +02:00
|
|
|
|
|
|
|
m_insideFunction = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitFunctionEnd() {
|
|
|
|
if (m_insideFunction) {
|
|
|
|
m_module.opReturn();
|
|
|
|
m_module.functionEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_insideFunction = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitFunctionLabel() {
|
2018-03-06 15:52:29 +01:00
|
|
|
m_module.opLabel(m_module.allocateId());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
void DxbcCompiler::emitMainFunctionBegin() {
|
|
|
|
this->emitFunctionBegin(
|
|
|
|
m_entryPointId,
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_module.defFunctionType(
|
|
|
|
m_module.defVoidType(), 0, nullptr));
|
|
|
|
this->emitFunctionLabel();
|
2018-03-06 15:52:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVsInit() {
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityClipDistance);
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityCullDistance);
|
2017-12-27 14:31:38 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityDrawParameters);
|
|
|
|
|
|
|
|
m_module.enableExtension("SPV_KHR_shader_draw_parameters");
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Declare the per-vertex output block. This is where
|
|
|
|
// the vertex shader will write the vertex position.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t perVertexStruct = this->getPerVertexBlockId();
|
|
|
|
const uint32_t perVertexPointer = m_module.defPointerType(
|
2017-12-13 15:32:54 +01:00
|
|
|
perVertexStruct, spv::StorageClassOutput);
|
|
|
|
|
|
|
|
m_perVertexOut = m_module.newVar(
|
|
|
|
perVertexPointer, spv::StorageClassOutput);
|
|
|
|
m_entryPointInterfaces.push_back(m_perVertexOut);
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.setDebugName(m_perVertexOut, "vs_vertex_out");
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Standard input array
|
|
|
|
emitDclInputArray(0);
|
|
|
|
|
2018-03-24 11:29:07 +01:00
|
|
|
// Cull/clip distances as outputs
|
|
|
|
m_clipDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullOut.numClipPlanes,
|
|
|
|
spv::BuiltInClipDistance,
|
|
|
|
spv::StorageClassOutput);
|
|
|
|
|
|
|
|
m_cullDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullOut.numCullPlanes,
|
|
|
|
spv::BuiltInCullDistance,
|
|
|
|
spv::StorageClassOutput);
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Main function of the vertex shader
|
|
|
|
m_vs.functionId = m_module.allocateId();
|
|
|
|
m_module.setDebugName(m_vs.functionId, "vs_main");
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(
|
2017-12-13 15:32:54 +01:00
|
|
|
m_vs.functionId,
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(),
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.defFunctionType(
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(), 0, nullptr));
|
|
|
|
this->emitFunctionLabel();
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-01 07:00:54 +01:00
|
|
|
void DxbcCompiler::emitHsInit() {
|
|
|
|
m_module.enableCapability(spv::CapabilityTessellation);
|
|
|
|
m_module.enableCapability(spv::CapabilityClipDistance);
|
|
|
|
m_module.enableCapability(spv::CapabilityCullDistance);
|
|
|
|
|
2018-03-05 17:23:00 +01:00
|
|
|
m_hs.builtinInvocationId = emitNewBuiltinVariable(
|
|
|
|
DxbcRegisterInfo {
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInInvocationId,
|
|
|
|
"vOutputControlPointId");
|
|
|
|
|
2018-03-05 14:07:15 +01:00
|
|
|
m_hs.builtinTessLevelOuter = emitBuiltinTessLevelOuter(spv::StorageClassOutput);
|
|
|
|
m_hs.builtinTessLevelInner = emitBuiltinTessLevelInner(spv::StorageClassOutput);
|
2018-03-01 07:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDsInit() {
|
|
|
|
m_module.enableCapability(spv::CapabilityTessellation);
|
|
|
|
m_module.enableCapability(spv::CapabilityClipDistance);
|
|
|
|
m_module.enableCapability(spv::CapabilityCullDistance);
|
|
|
|
|
2018-03-05 14:07:15 +01:00
|
|
|
m_ds.builtinTessLevelOuter = emitBuiltinTessLevelOuter(spv::StorageClassInput);
|
|
|
|
m_ds.builtinTessLevelInner = emitBuiltinTessLevelInner(spv::StorageClassInput);
|
2018-03-06 16:47:35 +01:00
|
|
|
|
|
|
|
// Declare the per-vertex output block
|
|
|
|
const uint32_t perVertexStruct = this->getPerVertexBlockId();
|
|
|
|
const uint32_t perVertexPointer = m_module.defPointerType(
|
|
|
|
perVertexStruct, spv::StorageClassOutput);
|
|
|
|
|
2018-03-24 11:29:07 +01:00
|
|
|
// Cull/clip distances as outputs
|
|
|
|
m_clipDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullOut.numClipPlanes,
|
|
|
|
spv::BuiltInClipDistance,
|
|
|
|
spv::StorageClassOutput);
|
|
|
|
|
|
|
|
m_cullDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullOut.numCullPlanes,
|
|
|
|
spv::BuiltInCullDistance,
|
|
|
|
spv::StorageClassOutput);
|
|
|
|
|
2018-03-06 16:47:35 +01:00
|
|
|
m_perVertexOut = m_module.newVar(
|
|
|
|
perVertexPointer, spv::StorageClassOutput);
|
|
|
|
m_entryPointInterfaces.push_back(m_perVertexOut);
|
|
|
|
m_module.setDebugName(m_perVertexOut, "ds_vertex_out");
|
|
|
|
|
|
|
|
// Main function of the domain shader
|
|
|
|
m_ds.functionId = m_module.allocateId();
|
|
|
|
m_module.setDebugName(m_ds.functionId, "ds_main");
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(
|
2018-03-06 16:47:35 +01:00
|
|
|
m_ds.functionId,
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(),
|
2018-03-06 16:47:35 +01:00
|
|
|
m_module.defFunctionType(
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(), 0, nullptr));
|
|
|
|
this->emitFunctionLabel();
|
2018-03-01 07:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitGsInit() {
|
|
|
|
m_module.enableCapability(spv::CapabilityGeometry);
|
|
|
|
m_module.enableCapability(spv::CapabilityClipDistance);
|
|
|
|
m_module.enableCapability(spv::CapabilityCullDistance);
|
2018-06-23 23:46:24 +02:00
|
|
|
|
|
|
|
// Enable capabilities for xfb mode if necessary
|
|
|
|
if (m_moduleInfo.xfb != nullptr) {
|
|
|
|
m_module.enableCapability(spv::CapabilityGeometryStreams);
|
|
|
|
m_module.enableCapability(spv::CapabilityTransformFeedback);
|
|
|
|
|
|
|
|
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeXfb);
|
|
|
|
}
|
2017-12-18 16:41:05 +01:00
|
|
|
|
|
|
|
// Declare the per-vertex output block. Outputs are not
|
|
|
|
// declared as arrays, instead they will be flushed when
|
|
|
|
// calling EmitVertex.
|
|
|
|
const uint32_t perVertexStruct = this->getPerVertexBlockId();
|
|
|
|
const uint32_t perVertexPointer = m_module.defPointerType(
|
|
|
|
perVertexStruct, spv::StorageClassOutput);
|
|
|
|
|
|
|
|
m_perVertexOut = m_module.newVar(
|
|
|
|
perVertexPointer, spv::StorageClassOutput);
|
|
|
|
m_entryPointInterfaces.push_back(m_perVertexOut);
|
|
|
|
m_module.setDebugName(m_perVertexOut, "gs_vertex_out");
|
|
|
|
|
2018-03-24 11:29:07 +01:00
|
|
|
// Cull/clip distances as outputs
|
|
|
|
m_clipDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullOut.numClipPlanes,
|
|
|
|
spv::BuiltInClipDistance,
|
|
|
|
spv::StorageClassOutput);
|
|
|
|
|
|
|
|
m_cullDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullOut.numCullPlanes,
|
|
|
|
spv::BuiltInCullDistance,
|
|
|
|
spv::StorageClassOutput);
|
|
|
|
|
2018-06-24 01:07:48 +02:00
|
|
|
// Emit Xfb variables if necessary
|
|
|
|
if (m_moduleInfo.xfb != nullptr)
|
|
|
|
emitXfbOutputDeclarations();
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
// Main function of the vertex shader
|
|
|
|
m_gs.functionId = m_module.allocateId();
|
|
|
|
m_module.setDebugName(m_gs.functionId, "gs_main");
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(
|
2017-12-18 16:41:05 +01:00
|
|
|
m_gs.functionId,
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(),
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.defFunctionType(
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(), 0, nullptr));
|
|
|
|
this->emitFunctionLabel();
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitPsInit() {
|
2018-02-26 16:46:34 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityDerivativeControl);
|
|
|
|
m_module.enableCapability(spv::CapabilityInterpolationFunction);
|
2017-12-27 01:37:15 +01:00
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.setExecutionMode(m_entryPointId,
|
|
|
|
spv::ExecutionModeOriginUpperLeft);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Standard input array
|
|
|
|
emitDclInputArray(0);
|
|
|
|
|
2018-03-24 11:29:07 +01:00
|
|
|
// Cull/clip distances as inputs
|
|
|
|
m_clipDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullIn.numClipPlanes,
|
|
|
|
spv::BuiltInClipDistance,
|
|
|
|
spv::StorageClassInput);
|
|
|
|
|
|
|
|
m_cullDistances = emitDclClipCullDistanceArray(
|
|
|
|
m_analysis->clipCullIn.numCullPlanes,
|
|
|
|
spv::BuiltInCullDistance,
|
|
|
|
spv::StorageClassInput);
|
|
|
|
|
2018-05-26 19:25:20 +02:00
|
|
|
// We may have to defer kill operations to the end of
|
|
|
|
// the shader in order to keep derivatives correct.
|
2018-06-23 17:14:35 +02:00
|
|
|
if (m_analysis->usesKill && m_analysis->usesDerivatives && m_moduleInfo.options.test(DxbcOption::DeferKill)) {
|
2018-05-26 19:25:20 +02:00
|
|
|
m_ps.killState = m_module.newVarInit(
|
|
|
|
m_module.defPointerType(m_module.defBoolType(), spv::StorageClassPrivate),
|
|
|
|
spv::StorageClassPrivate, m_module.constBool(false));
|
|
|
|
|
|
|
|
m_module.setDebugName(m_ps.killState, "ps_kill");
|
|
|
|
}
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Main function of the pixel shader
|
|
|
|
m_ps.functionId = m_module.allocateId();
|
|
|
|
m_module.setDebugName(m_ps.functionId, "ps_main");
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(
|
2017-12-13 15:32:54 +01:00
|
|
|
m_ps.functionId,
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(),
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.defFunctionType(
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(), 0, nullptr));
|
|
|
|
this->emitFunctionLabel();
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 17:27:40 +01:00
|
|
|
void DxbcCompiler::emitCsInit() {
|
|
|
|
// Main function of the compute shader
|
|
|
|
m_cs.functionId = m_module.allocateId();
|
2017-12-22 20:15:44 +01:00
|
|
|
m_module.setDebugName(m_cs.functionId, "cs_main");
|
2017-12-21 17:27:40 +01:00
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(
|
2017-12-21 17:27:40 +01:00
|
|
|
m_cs.functionId,
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(),
|
2017-12-21 17:27:40 +01:00
|
|
|
m_module.defFunctionType(
|
2018-08-27 12:47:52 +02:00
|
|
|
m_module.defVoidType(), 0, nullptr));
|
|
|
|
this->emitFunctionLabel();
|
2017-12-21 17:27:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVsFinalize() {
|
2018-03-06 15:52:29 +01:00
|
|
|
this->emitMainFunctionBegin();
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitInputSetup();
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_vs.functionId, 0, nullptr);
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitOutputSetup();
|
2018-03-24 11:29:07 +01:00
|
|
|
this->emitClipCullStore(DxbcSystemValue::ClipDistance, m_clipDistances);
|
|
|
|
this->emitClipCullStore(DxbcSystemValue::CullDistance, m_cullDistances);
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-01 07:00:54 +01:00
|
|
|
void DxbcCompiler::emitHsFinalize() {
|
2018-03-06 15:52:29 +01:00
|
|
|
if (m_hs.cpPhase.functionId == 0)
|
|
|
|
m_hs.cpPhase = this->emitNewHullShaderPassthroughPhase();
|
2018-03-05 17:23:00 +01:00
|
|
|
|
2018-03-06 18:29:20 +01:00
|
|
|
// Control point phase
|
2018-03-06 15:52:29 +01:00
|
|
|
this->emitMainFunctionBegin();
|
|
|
|
this->emitInputSetup(m_hs.vertexCountIn);
|
2018-03-01 12:08:06 +01:00
|
|
|
this->emitHsControlPointPhase(m_hs.cpPhase);
|
2018-03-06 18:29:20 +01:00
|
|
|
this->emitHsPhaseBarrier();
|
2018-03-01 12:08:06 +01:00
|
|
|
|
2018-04-02 16:22:19 +02:00
|
|
|
// Fork-join phases and output setup
|
|
|
|
this->emitHsInvocationBlockBegin(1);
|
|
|
|
|
2018-03-01 12:08:06 +01:00
|
|
|
for (const auto& phase : m_hs.forkPhases)
|
|
|
|
this->emitHsForkJoinPhase(phase);
|
|
|
|
|
|
|
|
for (const auto& phase : m_hs.joinPhases)
|
|
|
|
this->emitHsForkJoinPhase(phase);
|
2018-03-05 16:14:46 +01:00
|
|
|
|
2018-03-06 14:49:11 +01:00
|
|
|
this->emitOutputSetup();
|
2018-09-13 14:54:54 +02:00
|
|
|
this->emitHsOutputSetup();
|
2018-03-07 00:22:40 +01:00
|
|
|
this->emitHsInvocationBlockEnd();
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2018-03-01 07:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDsFinalize() {
|
2018-03-06 15:52:29 +01:00
|
|
|
this->emitMainFunctionBegin();
|
2018-03-06 16:47:35 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_ds.functionId, 0, nullptr);
|
|
|
|
this->emitOutputSetup();
|
2018-03-24 11:29:07 +01:00
|
|
|
this->emitClipCullStore(DxbcSystemValue::ClipDistance, m_clipDistances);
|
|
|
|
this->emitClipCullStore(DxbcSystemValue::CullDistance, m_cullDistances);
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2018-03-01 07:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitGsFinalize() {
|
2018-03-06 15:52:29 +01:00
|
|
|
this->emitMainFunctionBegin();
|
2017-12-21 12:37:20 +01:00
|
|
|
this->emitInputSetup(
|
|
|
|
primitiveVertexCount(m_gs.inputPrimitive));
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_gs.functionId, 0, nullptr);
|
|
|
|
// No output setup at this point as that was
|
|
|
|
// already done during the EmitVertex step
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitPsFinalize() {
|
2018-03-06 15:52:29 +01:00
|
|
|
this->emitMainFunctionBegin();
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitInputSetup();
|
2018-03-24 11:29:07 +01:00
|
|
|
this->emitClipCullLoad(DxbcSystemValue::ClipDistance, m_clipDistances);
|
|
|
|
this->emitClipCullLoad(DxbcSystemValue::CullDistance, m_cullDistances);
|
2018-05-26 19:25:20 +02:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_ps.functionId, 0, nullptr);
|
2018-05-26 19:25:20 +02:00
|
|
|
|
|
|
|
if (m_ps.killState != 0) {
|
|
|
|
DxbcConditional cond;
|
|
|
|
cond.labelIf = m_module.allocateId();
|
|
|
|
cond.labelEnd = m_module.allocateId();
|
|
|
|
|
|
|
|
uint32_t killTest = m_module.opLoad(m_module.defBoolType(), m_ps.killState);
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone);
|
|
|
|
m_module.opBranchConditional(killTest, cond.labelIf, cond.labelEnd);
|
|
|
|
|
|
|
|
m_module.opLabel(cond.labelIf);
|
|
|
|
m_module.opKill();
|
|
|
|
|
|
|
|
m_module.opLabel(cond.labelEnd);
|
|
|
|
}
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitOutputSetup();
|
2018-09-01 17:56:01 +02:00
|
|
|
this->emitOutputMapping();
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 17:27:40 +01:00
|
|
|
void DxbcCompiler::emitCsFinalize() {
|
2018-03-06 15:52:29 +01:00
|
|
|
this->emitMainFunctionBegin();
|
2017-12-21 17:27:40 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_cs.functionId, 0, nullptr);
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2017-12-21 17:27:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-24 01:07:48 +02:00
|
|
|
void DxbcCompiler::emitXfbOutputDeclarations() {
|
|
|
|
for (uint32_t i = 0; i < m_moduleInfo.xfb->entryCount; i++) {
|
|
|
|
const DxbcXfbEntry* xfbEntry = m_moduleInfo.xfb->entries + i;
|
|
|
|
const DxbcSgnEntry* sigEntry = m_osgn->find(
|
|
|
|
xfbEntry->semanticName,
|
|
|
|
xfbEntry->semanticIndex,
|
|
|
|
xfbEntry->streamId);
|
|
|
|
|
|
|
|
if (sigEntry == nullptr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DxbcRegisterInfo varInfo;
|
|
|
|
varInfo.type.ctype = DxbcScalarType::Float32;
|
|
|
|
varInfo.type.ccount = xfbEntry->componentCount;
|
|
|
|
varInfo.type.alength = 0;
|
|
|
|
varInfo.sclass = spv::StorageClassOutput;
|
|
|
|
|
|
|
|
uint32_t dstComponentMask = (1 << xfbEntry->componentCount) - 1;
|
|
|
|
uint32_t srcComponentMask = dstComponentMask
|
|
|
|
<< sigEntry->componentMask.firstSet()
|
|
|
|
<< xfbEntry->componentIndex;
|
|
|
|
|
|
|
|
DxbcXfbVar xfbVar;
|
|
|
|
xfbVar.varId = emitNewVariable(varInfo);
|
|
|
|
xfbVar.streamId = xfbEntry->streamId;
|
|
|
|
xfbVar.outputId = sigEntry->registerId;
|
|
|
|
xfbVar.srcMask = DxbcRegMask(srcComponentMask);
|
|
|
|
xfbVar.dstMask = DxbcRegMask(dstComponentMask);
|
|
|
|
m_xfbVars.push_back(xfbVar);
|
|
|
|
|
|
|
|
m_entryPointInterfaces.push_back(xfbVar.varId);
|
|
|
|
m_module.setDebugName(xfbVar.varId,
|
|
|
|
str::format("xfb", i).c_str());
|
|
|
|
|
|
|
|
m_module.decorateXfb(xfbVar.varId,
|
|
|
|
xfbEntry->streamId, xfbEntry->bufferId, xfbEntry->offset,
|
|
|
|
m_moduleInfo.xfb->strides[xfbEntry->bufferId]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Compact location/component assignment
|
|
|
|
for (uint32_t i = 0; i < m_xfbVars.size(); i++) {
|
|
|
|
m_xfbVars[i].location = i;
|
|
|
|
m_xfbVars[i].component = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < m_xfbVars.size(); i++) {
|
|
|
|
const DxbcXfbVar* var = &m_xfbVars[i];
|
|
|
|
|
|
|
|
m_module.decorateLocation (var->varId, var->location);
|
|
|
|
m_module.decorateComponent(var->varId, var->component);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-25 22:45:23 +02:00
|
|
|
void DxbcCompiler::emitXfbOutputSetup(
|
|
|
|
uint32_t streamId,
|
|
|
|
bool passthrough) {
|
2018-06-24 01:07:48 +02:00
|
|
|
for (size_t i = 0; i < m_xfbVars.size(); i++) {
|
|
|
|
if (m_xfbVars[i].streamId == streamId) {
|
2018-07-25 22:45:23 +02:00
|
|
|
DxbcRegisterPointer srcPtr = passthrough
|
|
|
|
? m_vRegs[m_xfbVars[i].outputId]
|
|
|
|
: m_oRegs[m_xfbVars[i].outputId];
|
|
|
|
|
|
|
|
if (passthrough) {
|
|
|
|
srcPtr = emitArrayAccess(srcPtr,
|
|
|
|
spv::StorageClassInput,
|
|
|
|
m_module.constu32(0));
|
|
|
|
}
|
|
|
|
|
2018-06-24 01:07:48 +02:00
|
|
|
DxbcRegisterPointer dstPtr;
|
|
|
|
dstPtr.type.ctype = DxbcScalarType::Float32;
|
|
|
|
dstPtr.type.ccount = m_xfbVars[i].dstMask.popCount();
|
|
|
|
dstPtr.id = m_xfbVars[i].varId;
|
|
|
|
|
|
|
|
DxbcRegisterValue value = emitRegisterExtract(
|
|
|
|
emitValueLoad(srcPtr), m_xfbVars[i].srcMask);
|
|
|
|
emitValueStore(dstPtr, value, m_xfbVars[i].dstMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-01 12:08:06 +01:00
|
|
|
void DxbcCompiler::emitHsControlPointPhase(
|
|
|
|
const DxbcCompilerHsControlPointPhase& phase) {
|
2018-03-06 15:52:29 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
phase.functionId, 0, nullptr);
|
2018-03-01 12:08:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitHsForkJoinPhase(
|
|
|
|
const DxbcCompilerHsForkJoinPhase& phase) {
|
2018-04-02 16:22:19 +02:00
|
|
|
for (uint32_t i = 0; i < phase.instanceCount; i++) {
|
|
|
|
uint32_t invocationId = m_module.constu32(i);
|
|
|
|
|
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
phase.functionId, 1,
|
|
|
|
&invocationId);
|
|
|
|
}
|
2018-03-01 12:08:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
void DxbcCompiler::emitDclInputArray(uint32_t vertexCount) {
|
|
|
|
DxbcArrayType info;
|
|
|
|
info.ctype = DxbcScalarType::Float32;
|
|
|
|
info.ccount = 4;
|
|
|
|
info.alength = DxbcMaxInterfaceRegs;
|
|
|
|
|
|
|
|
// Define the array type. This will be two-dimensional
|
|
|
|
// in some shaders, with the outer index representing
|
|
|
|
// the vertex ID within an invocation.
|
|
|
|
uint32_t arrayTypeId = getArrayTypeId(info);
|
|
|
|
|
|
|
|
if (vertexCount != 0) {
|
|
|
|
arrayTypeId = m_module.defArrayType(
|
|
|
|
arrayTypeId, m_module.constu32(vertexCount));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Define the actual variable. Note that this is private
|
|
|
|
// because we will copy input registers and some system
|
|
|
|
// variables to the array during the setup phase.
|
|
|
|
const uint32_t ptrTypeId = m_module.defPointerType(
|
|
|
|
arrayTypeId, spv::StorageClassPrivate);
|
|
|
|
|
|
|
|
const uint32_t varId = m_module.newVar(
|
|
|
|
ptrTypeId, spv::StorageClassPrivate);
|
|
|
|
|
|
|
|
m_module.setDebugName(varId, "shader_in");
|
|
|
|
m_vArray = varId;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-24 16:23:31 +01:00
|
|
|
void DxbcCompiler::emitDclInputPerVertex(
|
|
|
|
uint32_t vertexCount,
|
|
|
|
const char* varName) {
|
|
|
|
uint32_t typeId = getPerVertexBlockId();
|
|
|
|
|
|
|
|
if (vertexCount != 0) {
|
|
|
|
typeId = m_module.defArrayType(typeId,
|
|
|
|
m_module.constu32(vertexCount));
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t ptrTypeId = m_module.defPointerType(
|
|
|
|
typeId, spv::StorageClassInput);
|
|
|
|
|
|
|
|
m_perVertexIn = m_module.newVar(
|
|
|
|
ptrTypeId, spv::StorageClassInput);
|
|
|
|
m_module.setDebugName(m_perVertexIn, varName);
|
2018-04-23 18:07:31 +02:00
|
|
|
|
|
|
|
m_entryPointInterfaces.push_back(m_perVertexIn);
|
2018-03-24 16:23:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-24 11:29:07 +01:00
|
|
|
uint32_t DxbcCompiler::emitDclClipCullDistanceArray(
|
|
|
|
uint32_t length,
|
|
|
|
spv::BuiltIn builtIn,
|
|
|
|
spv::StorageClass storageClass) {
|
|
|
|
if (length == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint32_t t_f32 = m_module.defFloatType(32);
|
|
|
|
uint32_t t_arr = m_module.defArrayType(t_f32, m_module.constu32(length));
|
|
|
|
uint32_t t_ptr = m_module.defPointerType(t_arr, storageClass);
|
|
|
|
uint32_t varId = m_module.newVar(t_ptr, storageClass);
|
|
|
|
|
|
|
|
m_module.decorateBuiltIn(varId, builtIn);
|
|
|
|
m_module.setDebugName(varId,
|
|
|
|
builtIn == spv::BuiltInClipDistance
|
|
|
|
? "clip_distances"
|
|
|
|
: "cull_distances");
|
|
|
|
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
|
|
|
return varId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-05 17:23:00 +01:00
|
|
|
DxbcCompilerHsControlPointPhase DxbcCompiler::emitNewHullShaderControlPointPhase() {
|
|
|
|
uint32_t funTypeId = m_module.defFunctionType(
|
|
|
|
m_module.defVoidType(), 0, nullptr);
|
|
|
|
|
|
|
|
uint32_t funId = m_module.allocateId();
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(funId,
|
|
|
|
m_module.defVoidType(),
|
|
|
|
funTypeId);
|
|
|
|
this->emitFunctionLabel();
|
2018-03-05 17:23:00 +01:00
|
|
|
|
|
|
|
DxbcCompilerHsControlPointPhase result;
|
|
|
|
result.functionId = funId;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-06 15:52:29 +01:00
|
|
|
DxbcCompilerHsControlPointPhase DxbcCompiler::emitNewHullShaderPassthroughPhase() {
|
|
|
|
uint32_t funTypeId = m_module.defFunctionType(
|
|
|
|
m_module.defVoidType(), 0, nullptr);
|
|
|
|
|
|
|
|
// Begin passthrough function
|
|
|
|
uint32_t funId = m_module.allocateId();
|
|
|
|
m_module.setDebugName(funId, "hs_passthrough");
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(funId,
|
|
|
|
m_module.defVoidType(),
|
|
|
|
funTypeId);
|
|
|
|
this->emitFunctionLabel();
|
2018-03-06 15:52:29 +01:00
|
|
|
|
|
|
|
// We'll basically copy each input variable to the corresponding
|
|
|
|
// output, using the shader's invocation ID as the array index.
|
|
|
|
const uint32_t invocationId = m_module.opLoad(
|
|
|
|
getScalarTypeId(DxbcScalarType::Uint32),
|
|
|
|
m_hs.builtinInvocationId);
|
|
|
|
|
|
|
|
for (auto i = m_isgn->begin(); i != m_isgn->end(); i++) {
|
|
|
|
this->emitDclInput(
|
|
|
|
i->registerId, m_hs.vertexCountIn,
|
|
|
|
i->componentMask,
|
|
|
|
DxbcSystemValue::None,
|
|
|
|
DxbcInterpolationMode::Undefined);
|
|
|
|
|
|
|
|
// Vector type index
|
|
|
|
const std::array<uint32_t, 2> dstIndices
|
|
|
|
= {{ invocationId, m_module.constu32(i->registerId) }};
|
|
|
|
|
2018-07-01 15:24:21 +02:00
|
|
|
DxbcRegisterPointer srcPtr;
|
|
|
|
srcPtr.type = m_vRegs.at(i->registerId).type;
|
|
|
|
srcPtr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(getVectorTypeId(srcPtr.type), spv::StorageClassInput),
|
|
|
|
m_vRegs.at(i->registerId).id, 1, &invocationId);
|
2018-03-06 15:52:29 +01:00
|
|
|
|
2018-07-01 15:24:21 +02:00
|
|
|
DxbcRegisterValue srcValue = emitRegisterBitcast(
|
|
|
|
emitValueLoad(srcPtr), DxbcScalarType::Float32);
|
|
|
|
|
|
|
|
DxbcRegisterPointer dstPtr;
|
|
|
|
dstPtr.type = { DxbcScalarType::Float32, 4 };
|
|
|
|
dstPtr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(getVectorTypeId(dstPtr.type), spv::StorageClassOutput),
|
|
|
|
m_hs.outputPerVertex, dstIndices.size(), dstIndices.data());
|
|
|
|
|
|
|
|
emitValueStore(dstPtr, srcValue, DxbcRegMask::firstN(srcValue.type.ccount));
|
2018-03-06 15:52:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// End function
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionEnd();
|
2018-03-06 15:52:29 +01:00
|
|
|
|
|
|
|
DxbcCompilerHsControlPointPhase result;
|
|
|
|
result.functionId = funId;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-01 09:26:17 +01:00
|
|
|
DxbcCompilerHsForkJoinPhase DxbcCompiler::emitNewHullShaderForkJoinPhase() {
|
2018-03-06 18:29:20 +01:00
|
|
|
uint32_t argTypeId = m_module.defIntType(32, 0);
|
2018-03-01 09:26:17 +01:00
|
|
|
uint32_t funTypeId = m_module.defFunctionType(
|
2018-03-06 18:29:20 +01:00
|
|
|
m_module.defVoidType(), 1, &argTypeId);
|
2018-03-01 09:26:17 +01:00
|
|
|
|
|
|
|
uint32_t funId = m_module.allocateId();
|
|
|
|
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionBegin(funId,
|
|
|
|
m_module.defVoidType(),
|
|
|
|
funTypeId);
|
2018-03-06 18:29:20 +01:00
|
|
|
|
|
|
|
uint32_t argId = m_module.functionParameter(argTypeId);
|
2018-08-27 12:47:52 +02:00
|
|
|
this->emitFunctionLabel();
|
2018-03-01 09:26:17 +01:00
|
|
|
|
|
|
|
DxbcCompilerHsForkJoinPhase result;
|
2018-03-01 12:08:06 +01:00
|
|
|
result.functionId = funId;
|
2018-03-06 18:29:20 +01:00
|
|
|
result.instanceId = argId;
|
2018-03-01 09:26:17 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-05 16:14:46 +01:00
|
|
|
void DxbcCompiler::emitHsPhaseBarrier() {
|
|
|
|
uint32_t exeScopeId = m_module.constu32(spv::ScopeWorkgroup);
|
|
|
|
uint32_t memScopeId = m_module.constu32(spv::ScopeInvocation);
|
|
|
|
uint32_t semanticId = m_module.constu32(spv::MemorySemanticsMaskNone);
|
|
|
|
|
|
|
|
m_module.opControlBarrier(exeScopeId, memScopeId, semanticId);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-06 15:05:58 +01:00
|
|
|
void DxbcCompiler::emitHsInvocationBlockBegin(uint32_t count) {
|
|
|
|
uint32_t invocationId = m_module.opLoad(
|
|
|
|
getScalarTypeId(DxbcScalarType::Uint32),
|
|
|
|
m_hs.builtinInvocationId);
|
|
|
|
|
|
|
|
uint32_t condition = m_module.opULessThan(
|
|
|
|
m_module.defBoolType(), invocationId,
|
|
|
|
m_module.constu32(count));
|
|
|
|
|
|
|
|
m_hs.invocationBlockBegin = m_module.allocateId();
|
|
|
|
m_hs.invocationBlockEnd = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(
|
|
|
|
m_hs.invocationBlockEnd,
|
|
|
|
spv::SelectionControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranchConditional(
|
|
|
|
condition,
|
|
|
|
m_hs.invocationBlockBegin,
|
|
|
|
m_hs.invocationBlockEnd);
|
|
|
|
|
|
|
|
m_module.opLabel(
|
|
|
|
m_hs.invocationBlockBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitHsInvocationBlockEnd() {
|
|
|
|
m_module.opBranch (m_hs.invocationBlockEnd);
|
|
|
|
m_module.opLabel (m_hs.invocationBlockEnd);
|
|
|
|
|
|
|
|
m_hs.invocationBlockBegin = 0;
|
|
|
|
m_hs.invocationBlockEnd = 0;
|
|
|
|
}
|
2018-09-13 14:54:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitHsOutputSetup() {
|
|
|
|
uint32_t outputPerPatch = emitTessInterfacePerPatch(spv::StorageClassOutput);
|
|
|
|
|
|
|
|
uint32_t vecType = getVectorTypeId({ DxbcScalarType::Float32, 4 });
|
|
|
|
|
|
|
|
uint32_t srcPtrType = m_module.defPointerType(vecType, spv::StorageClassPrivate);
|
|
|
|
uint32_t dstPtrType = m_module.defPointerType(vecType, spv::StorageClassOutput);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 32; i++) {
|
|
|
|
if (m_hs.outputPerPatchMask & (1 << i)) {
|
|
|
|
uint32_t index = m_module.constu32(i);
|
|
|
|
|
|
|
|
uint32_t srcPtr = m_module.opAccessChain(srcPtrType, m_hs.outputPerPatch, 1, &index);
|
|
|
|
uint32_t dstPtr = m_module.opAccessChain(dstPtrType, outputPerPatch, 1, &index);
|
|
|
|
|
|
|
|
m_module.opStore(dstPtr, m_module.opLoad(vecType, srcPtr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-06 15:05:58 +01:00
|
|
|
|
|
|
|
|
2018-03-06 14:00:03 +01:00
|
|
|
uint32_t DxbcCompiler::emitTessInterfacePerPatch(spv::StorageClass storageClass) {
|
2018-09-13 14:54:54 +02:00
|
|
|
const char* name = "vPatch";
|
|
|
|
|
|
|
|
if (storageClass == spv::StorageClassPrivate)
|
|
|
|
name = "rPatch";
|
|
|
|
if (storageClass == spv::StorageClassOutput)
|
|
|
|
name = "oPatch";
|
2018-03-06 14:00:03 +01:00
|
|
|
|
|
|
|
uint32_t vecType = m_module.defVectorType (m_module.defFloatType(32), 4);
|
|
|
|
uint32_t arrType = m_module.defArrayType (vecType, m_module.constu32(32));
|
|
|
|
uint32_t ptrType = m_module.defPointerType(arrType, storageClass);
|
|
|
|
uint32_t varId = m_module.newVar (ptrType, storageClass);
|
|
|
|
|
2018-09-13 14:54:54 +02:00
|
|
|
m_module.setDebugName (varId, name);
|
2018-03-06 14:00:03 +01:00
|
|
|
|
2018-09-13 14:54:54 +02:00
|
|
|
if (storageClass != spv::StorageClassPrivate) {
|
|
|
|
m_module.decorate (varId, spv::DecorationPatch);
|
|
|
|
m_module.decorateLocation (varId, 0);
|
|
|
|
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
|
|
|
}
|
|
|
|
|
2018-03-06 14:00:03 +01:00
|
|
|
return varId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t DxbcCompiler::emitTessInterfacePerVertex(spv::StorageClass storageClass, uint32_t vertexCount) {
|
|
|
|
const bool isInput = storageClass == spv::StorageClassInput;
|
|
|
|
|
|
|
|
uint32_t vecType = m_module.defVectorType (m_module.defFloatType(32), 4);
|
|
|
|
uint32_t arrTypeInner = m_module.defArrayType (vecType, m_module.constu32(32));
|
|
|
|
uint32_t arrTypeOuter = m_module.defArrayType (arrTypeInner, m_module.constu32(vertexCount));
|
|
|
|
uint32_t ptrType = m_module.defPointerType(arrTypeOuter, storageClass);
|
|
|
|
uint32_t varId = m_module.newVar (ptrType, storageClass);
|
|
|
|
|
|
|
|
m_module.setDebugName (varId, isInput ? "vVertex" : "oVertex");
|
2018-03-28 11:55:29 +02:00
|
|
|
m_module.decorateLocation (varId, 0);
|
2018-03-06 14:00:03 +01:00
|
|
|
|
2018-09-13 14:54:54 +02:00
|
|
|
if (storageClass != spv::StorageClassPrivate)
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
2018-03-06 14:00:03 +01:00
|
|
|
return varId;
|
|
|
|
}
|
|
|
|
|
2018-03-12 12:25:10 +01:00
|
|
|
|
|
|
|
uint32_t DxbcCompiler::emitSamplePosArray() {
|
|
|
|
const std::array<uint32_t, 32> samplePosVectors = {{
|
|
|
|
// Invalid sample count / unbound resource
|
|
|
|
m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f),
|
|
|
|
// VK_SAMPLE_COUNT_1_BIT
|
|
|
|
m_module.constvec4f32(0.5f, 0.5f, 0.0f, 0.0f),
|
|
|
|
// VK_SAMPLE_COUNT_2_BIT
|
|
|
|
m_module.constvec4f32(0.75f, 0.75f, 0.0f, 0.0f),
|
2018-08-01 11:38:59 +02:00
|
|
|
m_module.constvec4f32(0.25f, 0.25f, 0.0f, 0.0f),
|
2018-03-12 12:25:10 +01:00
|
|
|
// VK_SAMPLE_COUNT_4_BIT
|
|
|
|
m_module.constvec4f32(0.375f, 0.125f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.785f, 0.375f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.125f, 0.625f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.625f, 0.875f, 0.0f, 0.0f),
|
|
|
|
// VK_SAMPLE_COUNT_8_BIT
|
|
|
|
m_module.constvec4f32(0.5625f, 0.3125f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.4375f, 0.6875f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.8125f, 0.5625f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.3125f, 0.1875f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.1875f, 0.8125f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.0625f, 0.4375f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.6875f, 0.9375f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.9375f, 0.0625f, 0.0f, 0.0f),
|
|
|
|
// VK_SAMPLE_COUNT_16_BIT
|
|
|
|
m_module.constvec4f32(0.5625f, 0.5625f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.4375f, 0.3125f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.3125f, 0.6250f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.7500f, 0.4375f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.1875f, 0.3750f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.6250f, 0.8125f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.8125f, 0.6875f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.6875f, 0.1875f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.3750f, 0.8750f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.5000f, 0.0625f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.2500f, 0.1250f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.1250f, 0.7500f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.0000f, 0.5000f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.9375f, 0.2500f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.8750f, 0.9375f, 0.0f, 0.0f),
|
|
|
|
m_module.constvec4f32(0.0625f, 0.0000f, 0.0f, 0.0f),
|
|
|
|
}};
|
|
|
|
|
|
|
|
uint32_t arrayTypeId = getArrayTypeId({
|
|
|
|
DxbcScalarType::Float32, 4,
|
2018-03-15 05:39:54 +03:00
|
|
|
static_cast<uint32_t>(samplePosVectors.size()) });
|
2018-03-12 12:25:10 +01:00
|
|
|
|
|
|
|
uint32_t samplePosArray = m_module.constComposite(
|
|
|
|
arrayTypeId,
|
|
|
|
samplePosVectors.size(),
|
|
|
|
samplePosVectors.data());
|
|
|
|
|
|
|
|
uint32_t varId = m_module.newVarInit(
|
|
|
|
m_module.defPointerType(arrayTypeId, spv::StorageClassPrivate),
|
|
|
|
spv::StorageClassPrivate, samplePosArray);
|
2018-03-06 14:00:03 +01:00
|
|
|
|
2018-03-12 12:25:10 +01:00
|
|
|
m_module.setDebugName(varId, "g_sample_pos");
|
|
|
|
return varId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::emitNewVariable(const DxbcRegisterInfo& info) {
|
|
|
|
const uint32_t ptrTypeId = this->getPointerTypeId(info);
|
|
|
|
return m_module.newVar(ptrTypeId, info.sclass);
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
uint32_t DxbcCompiler::emitNewBuiltinVariable(
|
|
|
|
const DxbcRegisterInfo& info,
|
|
|
|
spv::BuiltIn builtIn,
|
|
|
|
const char* name) {
|
|
|
|
const uint32_t varId = emitNewVariable(info);
|
|
|
|
|
|
|
|
m_module.decorateBuiltIn(varId, builtIn);
|
|
|
|
m_module.setDebugName(varId, name);
|
|
|
|
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
|
|
|
return varId;
|
|
|
|
}
|
2018-03-05 15:02:17 +01:00
|
|
|
|
2018-03-05 14:07:15 +01:00
|
|
|
|
|
|
|
uint32_t DxbcCompiler::emitBuiltinTessLevelOuter(spv::StorageClass storageClass) {
|
|
|
|
uint32_t id = emitNewBuiltinVariable(
|
|
|
|
DxbcRegisterInfo {
|
|
|
|
{ DxbcScalarType::Float32, 0, 4 },
|
|
|
|
storageClass },
|
|
|
|
spv::BuiltInTessLevelOuter,
|
|
|
|
"bTessLevelOuter");
|
|
|
|
|
|
|
|
m_module.decorate(id, spv::DecorationPatch);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t DxbcCompiler::emitBuiltinTessLevelInner(spv::StorageClass storageClass) {
|
|
|
|
uint32_t id = emitNewBuiltinVariable(
|
|
|
|
DxbcRegisterInfo {
|
|
|
|
{ DxbcScalarType::Float32, 0, 2 },
|
|
|
|
storageClass },
|
|
|
|
spv::BuiltInTessLevelInner,
|
|
|
|
"bTessLevelInner");
|
|
|
|
|
|
|
|
m_module.decorate(id, spv::DecorationPatch);
|
|
|
|
return id;
|
|
|
|
}
|
2017-12-21 12:37:20 +01:00
|
|
|
|
|
|
|
|
2018-06-01 13:57:26 +02:00
|
|
|
void DxbcCompiler::enableShaderViewportIndexLayer() {
|
|
|
|
if (!m_extensions.shaderViewportIndexLayer) {
|
|
|
|
m_extensions.shaderViewportIndexLayer = true;
|
|
|
|
|
|
|
|
m_module.enableExtension("SPV_EXT_shader_viewport_index_layer");
|
|
|
|
m_module.enableCapability(spv::CapabilityShaderViewportIndexLayerEXT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-30 17:22:36 +01:00
|
|
|
DxbcCfgBlock* DxbcCompiler::cfgFindBlock(
|
|
|
|
const std::initializer_list<DxbcCfgBlockType>& types) {
|
2017-12-18 11:53:28 +01:00
|
|
|
for (auto cur = m_controlFlowBlocks.rbegin();
|
|
|
|
cur != m_controlFlowBlocks.rend(); cur++) {
|
2017-12-30 17:22:36 +01:00
|
|
|
for (auto type : types) {
|
|
|
|
if (cur->type == type)
|
|
|
|
return &(*cur);
|
|
|
|
}
|
2017-12-18 11:53:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
DxbcBufferInfo DxbcCompiler::getBufferInfo(const DxbcRegister& reg) {
|
|
|
|
const uint32_t registerId = reg.idx[0].offset;
|
|
|
|
|
|
|
|
switch (reg.type) {
|
|
|
|
case DxbcOperandType::Resource: {
|
|
|
|
DxbcBufferInfo result;
|
2018-01-02 16:57:37 +01:00
|
|
|
result.image = m_textures.at(registerId).imageInfo;
|
2018-03-21 12:11:18 +01:00
|
|
|
result.stype = m_textures.at(registerId).sampledType;
|
2017-12-28 16:03:17 +01:00
|
|
|
result.type = m_textures.at(registerId).type;
|
2018-01-02 16:57:37 +01:00
|
|
|
result.typeId = m_textures.at(registerId).imageTypeId;
|
2017-12-28 16:03:17 +01:00
|
|
|
result.varId = m_textures.at(registerId).varId;
|
2018-01-10 13:44:04 +01:00
|
|
|
result.specId = m_textures.at(registerId).specId;
|
2017-12-28 16:03:17 +01:00
|
|
|
result.stride = m_textures.at(registerId).structStride;
|
|
|
|
return result;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOperandType::UnorderedAccessView: {
|
|
|
|
DxbcBufferInfo result;
|
2018-01-02 16:57:37 +01:00
|
|
|
result.image = m_uavs.at(registerId).imageInfo;
|
2018-03-21 12:11:18 +01:00
|
|
|
result.stype = m_uavs.at(registerId).sampledType;
|
2017-12-28 16:03:17 +01:00
|
|
|
result.type = m_uavs.at(registerId).type;
|
|
|
|
result.typeId = m_uavs.at(registerId).imageTypeId;
|
|
|
|
result.varId = m_uavs.at(registerId).varId;
|
2018-01-10 13:44:04 +01:00
|
|
|
result.specId = m_uavs.at(registerId).specId;
|
2017-12-28 16:03:17 +01:00
|
|
|
result.stride = m_uavs.at(registerId).structStride;
|
|
|
|
return result;
|
|
|
|
} break;
|
|
|
|
|
2017-12-29 00:51:31 +01:00
|
|
|
case DxbcOperandType::ThreadGroupSharedMemory: {
|
|
|
|
DxbcBufferInfo result;
|
2018-01-02 16:57:37 +01:00
|
|
|
result.image = { spv::DimBuffer, 0, 0, 0 };
|
2018-03-21 12:11:18 +01:00
|
|
|
result.stype = DxbcScalarType::Uint32;
|
2017-12-29 00:51:31 +01:00
|
|
|
result.type = m_gRegs.at(registerId).type;
|
|
|
|
result.typeId = m_module.defPointerType(
|
|
|
|
getScalarTypeId(DxbcScalarType::Uint32),
|
|
|
|
spv::StorageClassWorkgroup);
|
|
|
|
result.varId = m_gRegs.at(registerId).varId;
|
2018-01-10 13:44:04 +01:00
|
|
|
result.specId = 0;
|
2017-12-29 00:51:31 +01:00
|
|
|
result.stride = m_gRegs.at(registerId).elementStride;
|
|
|
|
return result;
|
|
|
|
} break;
|
2017-12-28 16:03:17 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format("DxbcCompiler: Invalid operand type for buffer: ", reg.type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-12 13:57:15 +02:00
|
|
|
uint32_t DxbcCompiler::getTexSizeDim(const DxbcImageInfo& imageType) const {
|
|
|
|
switch (imageType.dim) {
|
|
|
|
case spv::DimBuffer: return 1 + imageType.array;
|
|
|
|
case spv::Dim1D: return 1 + imageType.array;
|
|
|
|
case spv::Dim2D: return 2 + imageType.array;
|
|
|
|
case spv::Dim3D: return 3 + imageType.array;
|
|
|
|
case spv::DimCube: return 2 + imageType.array;
|
|
|
|
default: throw DxvkError("DxbcCompiler: getTexLayerDim: Unsupported image dimension");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-02 16:57:37 +01:00
|
|
|
uint32_t DxbcCompiler::getTexLayerDim(const DxbcImageInfo& imageType) const {
|
|
|
|
switch (imageType.dim) {
|
|
|
|
case spv::DimBuffer: return 1;
|
|
|
|
case spv::Dim1D: return 1;
|
|
|
|
case spv::Dim2D: return 2;
|
|
|
|
case spv::Dim3D: return 3;
|
|
|
|
case spv::DimCube: return 3;
|
|
|
|
default: throw DxvkError("DxbcCompiler: getTexLayerDim: Unsupported image dimension");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-07 21:04:23 +01:00
|
|
|
|
2018-01-02 16:57:37 +01:00
|
|
|
uint32_t DxbcCompiler::getTexCoordDim(const DxbcImageInfo& imageType) const {
|
|
|
|
return getTexLayerDim(imageType) + imageType.array;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-01 17:14:06 +01:00
|
|
|
DxbcRegMask DxbcCompiler::getTexCoordMask(const DxbcImageInfo& imageType) const {
|
2018-01-02 16:57:37 +01:00
|
|
|
return DxbcRegMask::firstN(getTexCoordDim(imageType));
|
2018-01-01 17:14:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-01 23:31:01 +01:00
|
|
|
DxbcVectorType DxbcCompiler::getInputRegType(uint32_t regIdx) const {
|
2018-10-08 09:34:56 +02:00
|
|
|
switch (m_programInfo.type()) {
|
2018-07-01 15:24:21 +02:00
|
|
|
case DxbcProgramType::VertexShader: {
|
|
|
|
const DxbcSgnEntry* entry = m_isgn->findByRegister(regIdx);
|
|
|
|
|
|
|
|
DxbcVectorType result;
|
|
|
|
result.ctype = DxbcScalarType::Float32;
|
|
|
|
result.ccount = 4;
|
|
|
|
|
|
|
|
if (entry != nullptr) {
|
|
|
|
result.ctype = entry->componentType;
|
|
|
|
result.ccount = entry->componentMask.popCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DxbcProgramType::DomainShader: {
|
|
|
|
DxbcVectorType result;
|
|
|
|
result.ctype = DxbcScalarType::Float32;
|
|
|
|
result.ccount = 4;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
DxbcVectorType result;
|
|
|
|
result.ctype = DxbcScalarType::Float32;
|
|
|
|
result.ccount = m_isgn->regMask(regIdx).minComponents();
|
|
|
|
return result;
|
|
|
|
}
|
2018-01-01 23:31:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-18 21:14:34 +02:00
|
|
|
DxbcVectorType DxbcCompiler::getOutputRegType(uint32_t regIdx) const {
|
2018-10-08 09:34:56 +02:00
|
|
|
switch (m_programInfo.type()) {
|
2018-07-01 15:24:21 +02:00
|
|
|
case DxbcProgramType::PixelShader: {
|
|
|
|
const DxbcSgnEntry* entry = m_osgn->findByRegister(regIdx);
|
|
|
|
|
|
|
|
DxbcVectorType result;
|
|
|
|
result.ctype = DxbcScalarType::Float32;
|
|
|
|
result.ccount = 4;
|
|
|
|
|
|
|
|
if (entry != nullptr) {
|
|
|
|
result.ctype = entry->componentType;
|
|
|
|
result.ccount = entry->componentMask.popCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DxbcProgramType::HullShader: {
|
|
|
|
DxbcVectorType result;
|
|
|
|
result.ctype = DxbcScalarType::Float32;
|
|
|
|
result.ccount = 4;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
DxbcVectorType result;
|
|
|
|
result.ctype = DxbcScalarType::Float32;
|
|
|
|
result.ccount = m_osgn->regMask(regIdx).minComponents();
|
|
|
|
return result;
|
2018-04-18 21:14:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-23 00:46:27 +02:00
|
|
|
DxbcImageInfo DxbcCompiler::getResourceType(
|
|
|
|
DxbcResourceDim resourceType,
|
|
|
|
bool isUav) const {
|
|
|
|
DxbcImageInfo typeInfo = [resourceType, isUav] () -> DxbcImageInfo {
|
|
|
|
switch (resourceType) {
|
2018-05-24 12:07:03 +02:00
|
|
|
case DxbcResourceDim::Buffer: return { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_MAX_ENUM };
|
|
|
|
case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_1D };
|
|
|
|
case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_1D_ARRAY };
|
|
|
|
case DxbcResourceDim::Texture2D: return { spv::Dim2D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D };
|
|
|
|
case DxbcResourceDim::Texture2DArr: return { spv::Dim2D, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
|
|
|
case DxbcResourceDim::Texture2DMs: return { spv::Dim2D, 0, 1, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D };
|
|
|
|
case DxbcResourceDim::Texture2DMsArr: return { spv::Dim2D, 1, 1, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY };
|
|
|
|
case DxbcResourceDim::Texture3D: return { spv::Dim3D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_3D };
|
|
|
|
case DxbcResourceDim::TextureCube: return { spv::DimCube, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_CUBE };
|
|
|
|
case DxbcResourceDim::TextureCubeArr: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY };
|
2018-04-23 00:46:27 +02:00
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Unsupported resource type: ", resourceType));
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
return typeInfo;
|
2018-01-08 13:39:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-21 12:11:18 +01:00
|
|
|
spv::ImageFormat DxbcCompiler::getScalarImageFormat(DxbcScalarType type) const {
|
|
|
|
switch (type) {
|
|
|
|
case DxbcScalarType::Float32: return spv::ImageFormatR32f;
|
|
|
|
case DxbcScalarType::Sint32: return spv::ImageFormatR32i;
|
|
|
|
case DxbcScalarType::Uint32: return spv::ImageFormatR32ui;
|
|
|
|
default: throw DxvkError("DxbcCompiler: Unhandled scalar resource type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-07 14:32:56 +02:00
|
|
|
bool DxbcCompiler::isDoubleType(DxbcScalarType type) const {
|
|
|
|
return type == DxbcScalarType::Sint64
|
|
|
|
|| type == DxbcScalarType::Uint64
|
|
|
|
|| type == DxbcScalarType::Float64;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::getScalarTypeId(DxbcScalarType type) {
|
2018-06-07 15:05:06 +02:00
|
|
|
if (type == DxbcScalarType::Float64)
|
|
|
|
m_module.enableCapability(spv::CapabilityFloat64);
|
|
|
|
|
|
|
|
if (type == DxbcScalarType::Sint64 || type == DxbcScalarType::Uint64)
|
|
|
|
m_module.enableCapability(spv::CapabilityInt64);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
switch (type) {
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcScalarType::Uint32: return m_module.defIntType(32, 0);
|
|
|
|
case DxbcScalarType::Uint64: return m_module.defIntType(64, 0);
|
|
|
|
case DxbcScalarType::Sint32: return m_module.defIntType(32, 1);
|
|
|
|
case DxbcScalarType::Sint64: return m_module.defIntType(64, 1);
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcScalarType::Float32: return m_module.defFloatType(32);
|
|
|
|
case DxbcScalarType::Float64: return m_module.defFloatType(64);
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcScalarType::Bool: return m_module.defBoolType();
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
2018-06-07 15:05:06 +02:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
throw DxvkError("DxbcCompiler: Invalid scalar type");
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::getVectorTypeId(const DxbcVectorType& type) {
|
|
|
|
uint32_t typeId = this->getScalarTypeId(type.ctype);
|
|
|
|
|
|
|
|
if (type.ccount > 1)
|
|
|
|
typeId = m_module.defVectorType(typeId, type.ccount);
|
|
|
|
|
|
|
|
return typeId;
|
2017-11-17 11:41:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
uint32_t DxbcCompiler::getArrayTypeId(const DxbcArrayType& type) {
|
|
|
|
DxbcVectorType vtype;
|
|
|
|
vtype.ctype = type.ctype;
|
|
|
|
vtype.ccount = type.ccount;
|
|
|
|
|
|
|
|
uint32_t typeId = this->getVectorTypeId(vtype);
|
|
|
|
|
|
|
|
if (type.alength != 0) {
|
|
|
|
typeId = m_module.defArrayType(typeId,
|
|
|
|
m_module.constu32(type.alength));
|
|
|
|
}
|
|
|
|
|
|
|
|
return typeId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::getPointerTypeId(const DxbcRegisterInfo& type) {
|
|
|
|
return m_module.defPointerType(
|
2017-12-18 16:41:05 +01:00
|
|
|
this->getArrayTypeId(type.type),
|
2017-12-18 00:46:44 +01:00
|
|
|
type.sclass);
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::getPerVertexBlockId() {
|
2017-12-13 15:32:54 +01:00
|
|
|
uint32_t t_f32 = m_module.defFloatType(32);
|
|
|
|
uint32_t t_f32_v4 = m_module.defVectorType(t_f32, 4);
|
2018-01-03 12:46:04 +01:00
|
|
|
// uint32_t t_f32_a4 = m_module.defArrayType(t_f32, m_module.constu32(4));
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2018-01-03 12:46:04 +01:00
|
|
|
std::array<uint32_t, 1> members;
|
2017-12-21 16:28:42 +01:00
|
|
|
members[PerVertex_Position] = t_f32_v4;
|
2018-01-03 12:46:04 +01:00
|
|
|
// members[PerVertex_CullDist] = t_f32_a4;
|
|
|
|
// members[PerVertex_ClipDist] = t_f32_a4;
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
uint32_t typeId = m_module.defStructTypeUnique(
|
|
|
|
members.size(), members.data());
|
|
|
|
|
|
|
|
m_module.memberDecorateBuiltIn(typeId, PerVertex_Position, spv::BuiltInPosition);
|
2018-01-03 12:46:04 +01:00
|
|
|
// m_module.memberDecorateBuiltIn(typeId, PerVertex_CullDist, spv::BuiltInCullDistance);
|
|
|
|
// m_module.memberDecorateBuiltIn(typeId, PerVertex_ClipDist, spv::BuiltInClipDistance);
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.decorateBlock(typeId);
|
|
|
|
|
2018-02-01 18:07:24 +01:00
|
|
|
m_module.setDebugName(typeId, "s_per_vertex");
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.setDebugMemberName(typeId, PerVertex_Position, "position");
|
2018-01-03 12:46:04 +01:00
|
|
|
// m_module.setDebugMemberName(typeId, PerVertex_CullDist, "cull_dist");
|
|
|
|
// m_module.setDebugMemberName(typeId, PerVertex_ClipDist, "clip_dist");
|
2017-12-13 15:32:54 +01:00
|
|
|
return typeId;
|
|
|
|
}
|
|
|
|
|
2018-03-01 12:08:06 +01:00
|
|
|
|
|
|
|
DxbcCompilerHsForkJoinPhase* DxbcCompiler::getCurrentHsForkJoinPhase() {
|
|
|
|
switch (m_hs.currPhaseType) {
|
|
|
|
case DxbcCompilerHsPhase::Fork: return &m_hs.forkPhases.at(m_hs.currPhaseId);
|
|
|
|
case DxbcCompilerHsPhase::Join: return &m_hs.joinPhases.at(m_hs.currPhaseId);
|
|
|
|
default: return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-08 09:34:56 +02:00
|
|
|
}
|