Files
renderdoc/renderdoc/driver/shaders/spirv/spirv_processor.cpp
T

1722 lines
52 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2025 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "spirv_processor.h"
#include "common/formatting.h"
#include "maths/half_convert.h"
#include "replay/common/var_dispatch_helpers.h"
#include "spirv_op_helpers.h"
namespace rdcspv
{
static void ConstructCompositeConstant(ShaderVariable &v, const rdcarray<ShaderVariable> &members)
{
if(v.rows == 0 && v.columns == 0)
{
v.members.resize(members.size());
for(size_t i = 0; i < v.members.size(); i++)
v.members[i] = members[i];
}
else
{
size_t sz = VarTypeByteSize(v.type);
for(uint32_t c = 0; c < v.columns; c++)
{
// if this is a vector, v.rows is 1 so r is always 0, meaning that it is irrelevant
for(uint32_t r = 0; r < v.rows; r++)
{
if(sz == 8)
v.value.u64v[r * v.columns + c] = members[c].value.u64v[r];
else if(sz == 4)
v.value.u32v[r * v.columns + c] = members[c].value.u32v[r];
else if(sz == 2)
v.value.u16v[r * v.columns + c] = members[c].value.u16v[r];
else
v.value.u8v[r * v.columns + c] = members[c].value.u8v[r];
}
}
}
}
Scalar scalar(VarType type)
{
switch(type)
{
case VarType::Float: return scalar<float>();
case VarType::Double: return scalar<double>();
case VarType::Half: return scalar<half_float::half>();
case VarType::SInt: return scalar<int32_t>();
case VarType::UInt: return scalar<uint32_t>();
case VarType::SShort: return scalar<int16_t>();
case VarType::UShort: return scalar<uint16_t>();
case VarType::SLong: return scalar<int64_t>();
case VarType::ULong: return scalar<uint64_t>();
case VarType::SByte: return scalar<int8_t>();
case VarType::UByte: return scalar<uint8_t>();
case VarType::Bool: return scalar<bool>();
default: RDCERR("No scalar type for %s", ToStr(type).c_str()); return scalar<void>();
}
}
void ExecutionModes::Register(const OpExecutionMode &mode)
{
if(mode.mode == ExecutionMode::LocalSize)
{
localSize.x = mode.mode.localSize.xsize;
localSize.y = mode.mode.localSize.ysize;
localSize.z = mode.mode.localSize.zsize;
}
else if(mode.mode == ExecutionMode::Triangles)
{
outTopo = Topology::TriangleList;
}
else if(mode.mode == ExecutionMode::Isolines)
{
outTopo = Topology::LineList;
}
else if(mode.mode == ExecutionMode::OutputPoints)
{
outTopo = Topology::PointList;
}
else if(mode.mode == ExecutionMode::OutputLineStrip)
{
outTopo = Topology::LineStrip;
}
else if(mode.mode == ExecutionMode::OutputTriangleStrip)
{
outTopo = Topology::TriangleStrip;
}
else if(mode.mode == ExecutionMode::Quads)
{
outTopo = Topology::TriangleList;
}
else if(mode.mode == ExecutionMode::DepthGreater)
{
depthMode = DepthGreater;
}
else if(mode.mode == ExecutionMode::DepthLess)
{
depthMode = DepthLess;
}
else
{
others.push_back(mode.mode);
}
}
void ExecutionModes::Register(const OpExecutionModeId &mode)
{
if(mode.mode == ExecutionMode::LocalSizeId)
{
localSizeId.x = mode.mode.localSizeId.xsize;
localSizeId.y = mode.mode.localSizeId.ysize;
localSizeId.z = mode.mode.localSizeId.zsize;
}
else
{
others.push_back(mode.mode);
}
}
void ExecutionModes::Unregister(const OpExecutionMode &mode)
{
if(mode.mode == ExecutionMode::LocalSize)
{
localSize = {};
}
else if(mode.mode == ExecutionMode::Triangles)
{
outTopo = Topology::Unknown;
}
else if(mode.mode == ExecutionMode::Isolines)
{
outTopo = Topology::Unknown;
}
else if(mode.mode == ExecutionMode::OutputPoints)
{
outTopo = Topology::Unknown;
}
else if(mode.mode == ExecutionMode::OutputLineStrip)
{
outTopo = Topology::Unknown;
}
else if(mode.mode == ExecutionMode::OutputTriangleStrip)
{
outTopo = Topology::Unknown;
}
else if(mode.mode == ExecutionMode::Quads)
{
outTopo = Topology::Unknown;
}
else if(mode.mode == ExecutionMode::DepthGreater)
{
depthMode = DepthNormal;
}
else if(mode.mode == ExecutionMode::DepthLess)
{
depthMode = DepthNormal;
}
else
{
ExecutionMode val = mode.mode.value;
others.removeOneIf([val](const ExecutionModeAndParamData &m) { return m.value == val; });
}
}
void ExecutionModes::Unregister(const OpExecutionModeId &mode)
{
if(mode.mode == ExecutionMode::LocalSizeId)
{
localSizeId = {};
}
else
{
ExecutionMode val = mode.mode.value;
others.removeOneIf([val](const ExecutionModeAndParamData &m) { return m.value == val; });
}
}
void Decorations::Register(const DecorationAndParamData &decoration)
{
if(decoration == Decoration::Block)
{
flags = Flags(flags | Block);
}
else if(decoration == Decoration::BufferBlock)
{
flags = Flags(flags | BufferBlock);
}
else if(decoration == Decoration::RowMajor)
{
flags = Flags(flags | RowMajor);
}
else if(decoration == Decoration::ColMajor)
{
flags = Flags(flags | ColMajor);
}
else if(decoration == Decoration::Restrict)
{
flags = Flags(flags | Restrict);
}
else if(decoration == Decoration::Aliased)
{
flags = Flags(flags | Aliased);
}
else if(decoration == Decoration::Location)
{
RDCASSERT(!(flags & HasArrayStride));
flags = Flags(flags | HasLocation);
location = decoration.location;
}
else if(decoration == Decoration::ArrayStride)
{
RDCASSERT(!(flags & HasLocation));
flags = Flags(flags | HasArrayStride);
arrayStride = decoration.arrayStride;
}
else if(decoration == Decoration::DescriptorSet)
{
RDCASSERT(!(flags & HasOffset));
flags = Flags(flags | HasDescriptorSet);
set = decoration.descriptorSet;
}
else if(decoration == Decoration::Offset)
{
RDCASSERT(!(flags & HasDescriptorSet));
flags = Flags(flags | HasOffset);
offset = decoration.offset;
}
else if(decoration == Decoration::BuiltIn)
{
RDCASSERT(!(flags & HasBinding));
flags = Flags(flags | HasBuiltIn);
builtIn = decoration.builtIn;
}
else if(decoration == Decoration::Binding)
{
RDCASSERT(!(flags & HasBuiltIn));
flags = Flags(flags | HasBinding);
binding = decoration.binding;
}
else if(decoration == Decoration::SpecId)
{
RDCASSERT(!(flags & HasMatrixStride));
flags = Flags(flags | HasSpecId);
specID = decoration.specId;
}
else if(decoration == Decoration::MatrixStride)
{
RDCASSERT(!(flags & HasSpecId));
flags = Flags(flags | HasMatrixStride);
matrixStride = decoration.matrixStride;
}
else
{
others.push_back(decoration);
}
}
void Decorations::Unregister(const DecorationAndParamData &decoration)
{
if(decoration == Decoration::Block)
{
flags = Flags(flags & ~Block);
}
else if(decoration == Decoration::BufferBlock)
{
flags = Flags(flags & ~BufferBlock);
}
else if(decoration == Decoration::RowMajor)
{
flags = Flags(flags & ~RowMajor);
}
else if(decoration == Decoration::ColMajor)
{
flags = Flags(flags & ~ColMajor);
}
else if(decoration == Decoration::Location)
{
flags = Flags(flags & ~HasLocation);
location = ~0U;
}
else if(decoration == Decoration::ArrayStride)
{
flags = Flags(flags & ~HasArrayStride);
arrayStride = ~0U;
}
else if(decoration == Decoration::DescriptorSet)
{
flags = Flags(flags & ~HasDescriptorSet);
set = ~0U;
}
else if(decoration == Decoration::Offset)
{
flags = Flags(flags & ~HasOffset);
offset = ~0U;
}
else if(decoration == Decoration::BuiltIn)
{
flags = Flags(flags & ~HasBuiltIn);
builtIn = (BuiltIn)~0U;
}
else if(decoration == Decoration::SpecId)
{
flags = Flags(flags & ~HasSpecId);
specID = ~0U;
}
else if(decoration == Decoration::MatrixStride)
{
flags = Flags(flags & ~HasMatrixStride);
matrixStride = ~0U;
}
else if(decoration == Decoration::Binding)
{
flags = Flags(flags & ~HasBinding);
binding = ~0U;
}
else
{
Decoration val = decoration.value;
others.removeIf([val](const DecorationAndParamData &m) { return m.value == val; });
}
}
Processor::Processor()
{
}
Processor::~Processor()
{
}
void Processor::Parse(const rdcarray<uint32_t> &spirvWords)
{
m_SPIRV = spirvWords;
if(m_SPIRV.size() < FirstRealWord || m_SPIRV[0] != MagicNumber)
{
RDCERR("Empty or invalid SPIR-V module");
m_SPIRV.clear();
return;
}
uint32_t packedVersion = m_SPIRV[1];
// Bytes: 0 | major | minor | 0
m_MajorVersion = uint8_t((packedVersion & 0x00ff0000) >> 16);
m_MinorVersion = uint8_t((packedVersion & 0x0000ff00) >> 8);
if(packedVersion > VersionPacked)
{
RDCERR("Unsupported SPIR-V version: %08x", packedVersion);
m_SPIRV.clear();
return;
}
m_Generator = Generator(m_SPIRV[2] >> 16);
m_GeneratorVersion = m_SPIRV[2] & 0xffff;
// [4] is reserved
RDCASSERT(m_SPIRV[4] == 0);
PreParse(m_SPIRV[3]);
// simple state machine to track which section we're in.
// Note that a couple of sections are optional and could be skipped over, at which point we insert
// a dummy OpNop so they're not empty (which will be stripped later) and record them as in
// between.
//
// We only handle single-shader modules at the moment, so some things are required by virtue of
// being required in a shader - e.g. at least the Shader capability, at least one entry point, etc
//
// Capabilities: REQUIRED (we assume - must declare Shader capability)
// Extensions: OPTIONAL
// ExtInst: OPTIONAL
// MemoryModel: REQUIRED (required by spec)
// EntryPoints: REQUIRED (we assume)
// ExecutionMode: OPTIONAL
// DebugStringSource: OPTIONAL
// DebugNames: OPTIONAL
// DebugModuleProcessed: OPTIONAL
// Annotations: OPTIONAL (in theory - would require empty shader)
// TypesVariables: REQUIRED (must at least have the entry point function type)
// Functions: REQUIRED (must have the entry point)
// set the book-ends: start of the first section and end of the last
m_Sections[Section::Count - 1].endOffset = m_SPIRV.size();
#define START_SECTION(section) \
if(m_Sections[section].startOffset == 0) \
m_Sections[section].startOffset = it.offs();
for(Iter it(m_SPIRV, FirstRealWord); it; it++)
{
Op opcode = it.opcode();
if(opcode == Op::Capability)
{
START_SECTION(Section::Capabilities);
}
else if(opcode == Op::Extension)
{
START_SECTION(Section::Extensions);
}
else if(opcode == Op::ExtInstImport)
{
START_SECTION(Section::ExtInst);
}
else if(opcode == Op::MemoryModel)
{
START_SECTION(Section::MemoryModel);
}
else if(opcode == Op::EntryPoint)
{
START_SECTION(Section::EntryPoints);
}
else if(opcode == Op::ExecutionMode || opcode == Op::ExecutionModeId)
{
START_SECTION(Section::ExecutionMode);
}
else if(opcode == Op::String || opcode == Op::Source || opcode == Op::SourceContinued ||
opcode == Op::SourceExtension)
{
START_SECTION(Section::DebugStringSource);
}
else if(opcode == Op::Name || opcode == Op::MemberName)
{
START_SECTION(Section::DebugNames);
}
else if(opcode == Op::ModuleProcessed)
{
START_SECTION(Section::DebugModuleProcessed);
}
else if(opcode == Op::Decorate || opcode == Op::MemberDecorate || opcode == Op::GroupDecorate ||
opcode == Op::GroupMemberDecorate || opcode == Op::DecorationGroup ||
opcode == Op::DecorateString || opcode == Op::MemberDecorateString)
{
START_SECTION(Section::Annotations);
}
else if(opcode == Op::Function)
{
START_SECTION(Section::Functions);
}
else
{
// if we've reached another instruction, check if we've reached the function section yet. If
// we have then assume it's an instruction inside a function and ignore. If we haven't, assume
// it's a type/variable/constant type instruction
if(m_Sections[Section::Functions].startOffset == 0)
{
START_SECTION(Section::TypesVariablesConstants);
}
}
RegisterOp(it);
}
#undef START_SECTION
PostParse();
// ensure we got everything right. First section should start at the beginning
RDCASSERTEQUAL(m_Sections[Section::First].startOffset, FirstRealWord);
// if the last section is empty, set its start ofset. This will cascade below
if(m_Sections[Section::Count - 1].startOffset == 0)
m_Sections[Section::Count - 1].startOffset = m_Sections[Section::Count - 1].endOffset;
// we now set the endOffset of each section to the start of the next. Any empty sections
// temporarily have startOffset set to endOffset, we'll pad them with a nop below.
for(int s = Section::Count - 1; s > 0; s--)
{
RDCASSERTEQUAL(m_Sections[s - 1].endOffset, 0);
m_Sections[s - 1].endOffset = m_Sections[s].startOffset;
if(m_Sections[s - 1].startOffset == 0)
m_Sections[s - 1].startOffset = m_Sections[s - 1].endOffset;
}
}
void Processor::PreParse(uint32_t maxId)
{
UpdateMaxID(maxId);
m_DeferMemberDecorations = true;
}
void Processor::UpdateMaxID(uint32_t maxId)
{
decorations.resize(maxId);
idOffsets.resize(maxId);
idTypes.resize(maxId);
}
void Processor::RegisterOp(Iter it)
{
OpDecoder opdata(it);
if(opdata.result != Id() && opdata.resultType != Id())
idTypes[opdata.result] = opdata.resultType;
if(opdata.result != Id())
idOffsets[opdata.result] = it.offs();
if(opdata.op == Op::Capability)
{
OpCapability decoded(it);
capabilities.insert(decoded.capability);
if(decoded.capability == Capability::GroupNonUniform ||
decoded.capability == Capability::GroupNonUniformVote ||
decoded.capability == Capability::GroupNonUniformArithmetic ||
decoded.capability == Capability::GroupNonUniformBallot ||
decoded.capability == Capability::GroupNonUniformShuffle ||
decoded.capability == Capability::GroupNonUniformShuffleRelative ||
decoded.capability == Capability::GroupNonUniformClustered ||
decoded.capability == Capability::GroupNonUniformRotateKHR ||
decoded.capability == Capability::GroupUniformArithmeticKHR ||
decoded.capability == Capability::SubgroupBallotKHR ||
decoded.capability == Capability::SubgroupVoteKHR)
{
m_ThreadScope |= ThreadScope::Subgroup;
}
else if(decoded.capability == Capability::GroupNonUniformQuad)
{
m_ThreadScope |= ThreadScope::Quad;
}
}
else if(opdata.op == Op::Extension)
{
OpExtension decoded(it);
extensions.insert(decoded.name);
}
else if(opdata.op == Op::ExtInstImport)
{
OpExtInstImport decoded(it);
extSets[decoded.result] = decoded.name;
if(decoded.name == "GLSL.std.450")
knownExtSet[ExtSet_GLSL450] = decoded.result;
else if(decoded.name == "NonSemantic.DebugPrintf")
knownExtSet[ExtSet_Printf] = decoded.result;
else if(decoded.name == "NonSemantic.Shader.DebugInfo.100")
knownExtSet[ExtSet_ShaderDbg] = decoded.result;
else if(decoded.name == "NonSemantic.DebugBreak")
knownExtSet[ExtSet_DebugBreak] = decoded.result;
}
else if(opdata.op == Op::EntryPoint)
{
OpEntryPoint decoded(it);
entries.push_back(
EntryPoint(decoded.executionModel, decoded.entryPoint, decoded.name, decoded.iface));
}
else if(opdata.op == Op::ExecutionMode)
{
OpExecutionMode decoded(it);
for(auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt)
{
if(entryIt->id == decoded.entryPoint)
{
entryIt->executionModes.Register(decoded);
break;
}
}
}
else if(opdata.op == Op::ExecutionModeId)
{
OpExecutionModeId decoded(it);
for(auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt)
{
if(entryIt->id == decoded.entryPoint)
{
entryIt->executionModes.Register(decoded);
break;
}
}
}
else if(opdata.op == Op::Variable)
{
OpVariable decoded(it);
// only register global variables here
if(decoded.storageClass != rdcspv::StorageClass::Function)
globals.push_back(
Variable(decoded.resultType, decoded.result, decoded.storageClass, decoded.initializer));
if(decoded.storageClass == StorageClass::Workgroup)
m_ThreadScope |= ThreadScope::Workgroup;
}
else if(opdata.op == Op::ConstantNull)
{
OpConstantNull decoded(it);
DataType &type = dataTypes[decoded.resultType];
ShaderVariable v = MakeNULL(type, 0ULL);
v.name = "NULL";
constants[decoded.result] = {decoded.resultType, decoded.result, v, {}, opdata.op};
}
else if(opdata.op == Op::Undef)
{
OpUndef decoded(it);
DataType &type = dataTypes[decoded.resultType];
ShaderVariable v = MakeNULL(type, 0xccccccccccccccccULL);
v.name = "Undef";
constants[decoded.result] = {decoded.resultType, decoded.result, v, {}, opdata.op};
}
else if(opdata.op == Op::ConstantTrue || opdata.op == Op::SpecConstantTrue)
{
OpConstantTrue decoded(it);
ShaderVariable v("true", 1, 0, 0, 0);
v.columns = 1;
v.type = VarType::Bool;
constants[decoded.result] = {decoded.resultType, decoded.result, v, {}, opdata.op};
if(opdata.op == Op::SpecConstantTrue)
specConstants.insert(decoded.result);
}
else if(opdata.op == Op::ConstantFalse || opdata.op == Op::SpecConstantFalse)
{
OpConstantFalse decoded(it);
ShaderVariable v("true", 0, 0, 0, 0);
v.columns = 1;
v.type = VarType::Bool;
constants[decoded.result] = {decoded.resultType, decoded.result, v, {}, opdata.op};
if(opdata.op == Op::SpecConstantFalse)
specConstants.insert(decoded.result);
}
else if(opdata.op == Op::ConstantComposite || opdata.op == Op::SpecConstantComposite)
{
OpConstantComposite decoded(it);
DataType &type = dataTypes[decoded.resultType];
RDCASSERT(type.type != DataType::UnknownType);
ShaderVariable v("composite", 0, 0, 0, 0);
v.rows = v.columns = 0;
if(type.type == DataType::VectorType)
{
v.type = type.scalar().Type();
v.rows = 1;
v.columns = type.vector().count & 0xf;
}
else if(type.type == DataType::MatrixType)
{
v.type = type.scalar().Type();
v.rows = type.vector().count & 0xf;
v.columns = type.matrix().count & 0xf;
// always store constants row major
v.flags |= ShaderVariableFlags::RowMajorMatrix;
}
else if(type.type == DataType::StructType)
{
v.type = VarType::Struct;
}
rdcarray<ShaderVariable> members;
for(size_t i = 0; i < decoded.constituents.size(); i++)
members.push_back(constants[decoded.constituents[i]].value);
ConstructCompositeConstant(v, members);
constants[decoded.result] = {decoded.resultType, decoded.result, v, decoded.constituents,
opdata.op};
if(opdata.op == Op::SpecConstantComposite)
specConstants.insert(decoded.result);
}
else if(opdata.op == Op::SpecConstantOp)
{
// this one has complex decoding rules, so we do it by hand.
SpecOp specop = {opdata.resultType, opdata.result, (Op)it.word(3)};
for(size_t w = 4; w < it.size(); w++)
specop.params.push_back(Id::fromWord(it.word(w)));
specOps[opdata.result] = specop;
constants[opdata.result] = {opdata.resultType, opdata.result, ShaderVariable(), {}, opdata.op};
specConstants.insert(opdata.result);
}
else if(opdata.op == Op::Constant || opdata.op == Op::SpecConstant)
{
// this one has complex decoding rules, so we do it by hand.
DataType &type = dataTypes[opdata.resultType];
RDCASSERT(type.type == DataType::ScalarType);
ShaderVariable v("value", 1, 0, 0, 0);
v.columns = 1;
v.type = type.scalar().Type();
v.value.u32v[0] = it.word(3);
if(type.scalar().width > 32)
{
v.value.u32v[1] = it.word(4);
}
else
{
// if it's signed, sign extend
if(type.scalar().signedness && type.scalar().width <= 16)
{
// is the top bit set?
if(v.value.u32v[0] & (1 << (type.scalar().width - 1)))
{
uint32_t mask = 0xFFFFFFFFU >> (32 - type.scalar().width);
v.value.u32v[0] |= ~mask & ~0U;
}
}
}
constants[opdata.result] = {opdata.resultType, opdata.result, v, {}, opdata.op};
if(opdata.op == Op::SpecConstant)
specConstants.insert(opdata.result);
}
else if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt ||
opdata.op == Op::TypeFloat)
{
dataTypes[opdata.result] = DataType(opdata.result, Scalar(it));
}
else if(opdata.op == Op::TypeVector)
{
OpTypeVector decoded(it);
dataTypes[opdata.result] =
DataType(opdata.result, decoded.componentType,
Vector(dataTypes[decoded.componentType].scalar(), decoded.componentCount));
}
else if(opdata.op == Op::TypeMatrix)
{
OpTypeMatrix decoded(it);
dataTypes[opdata.result] =
DataType(opdata.result, decoded.columnType,
Matrix(dataTypes[decoded.columnType].vector(), decoded.columnCount));
}
else if(opdata.op == Op::TypeStruct)
{
OpTypeStruct decoded(it);
dataTypes[opdata.result] = DataType(opdata.result, decoded.members);
}
else if(opdata.op == Op::TypePointer)
{
OpTypePointer decoded(it);
dataTypes[opdata.result] = DataType(opdata.result, Pointer(decoded.type, decoded.storageClass));
}
else if(opdata.op == Op::TypeArray)
{
OpTypeArray decoded(it);
dataTypes[opdata.result] = DataType(opdata.result, decoded.elementType, decoded.length);
}
else if(opdata.op == Op::TypeRuntimeArray)
{
OpTypeRuntimeArray decoded(it);
dataTypes[opdata.result] = DataType(opdata.result, decoded.elementType, Id());
}
else if(opdata.op == Op::TypeImage)
{
OpTypeImage decoded(it);
RDCASSERT(dataTypes[decoded.sampledType].type != DataType::UnknownType);
imageTypes[opdata.result] =
Image(dataTypes[decoded.sampledType].scalar(), decoded.dim, decoded.depth, decoded.arrayed,
decoded.mS, decoded.sampled, decoded.imageFormat);
dataTypes[opdata.result] = DataType(opdata.result, DataType::ImageType);
}
else if(opdata.op == Op::TypeSampler)
{
samplerTypes[opdata.result] = Sampler();
dataTypes[opdata.result] = DataType(opdata.result, DataType::SamplerType);
}
else if(opdata.op == Op::TypeSampledImage)
{
OpTypeSampledImage decoded(it);
sampledImageTypes[decoded.result] = SampledImage(decoded.imageType);
dataTypes[opdata.result] = DataType(opdata.result, DataType::SampledImageType);
}
else if(opdata.op == Op::TypeFunction)
{
OpTypeFunction decoded(it);
functionTypes[decoded.result] = FunctionType(decoded.returnType, decoded.parameters);
}
else if(opdata.op == Op::TypeRayQueryKHR)
{
OpTypeRayQueryKHR decoded(it);
dataTypes[opdata.result] = DataType(decoded.result, DataType::RayQueryType);
}
else if(opdata.op == Op::TypeAccelerationStructureKHR)
{
OpTypeAccelerationStructureKHR decoded(it);
dataTypes[opdata.result] = DataType(decoded.result, DataType::AccelerationStructureType);
}
else if(opdata.op == Op::Decorate)
{
OpDecorate decoded(it);
decorations[decoded.target].Register(decoded.decoration);
}
else if(opdata.op == Op::DecorateId)
{
OpDecorateId decoded(it);
decorations[decoded.target].Register(decoded.decoration);
}
else if(opdata.op == Op::DecorateString)
{
OpDecorateString decoded(it);
decorations[decoded.target].Register(decoded.decoration);
}
else if(opdata.op == Op::MemberDecorate)
{
OpMemberDecorate decoded(it);
if(m_DeferMemberDecorations)
{
m_MemberDecorations.push_back({decoded.structureType, decoded.member, decoded.decoration});
}
else
{
if(decoded.member < dataTypes[decoded.structureType].children.size())
dataTypes[decoded.structureType].children[decoded.member].decorations.Register(
decoded.decoration);
else
RDCERR("Non-deferred member decoration referenced invalid type member");
}
}
else if(opdata.op == Op::MemberDecorateString)
{
OpMemberDecorateString decoded(it);
if(m_DeferMemberDecorations)
{
m_MemberDecorations.push_back({decoded.structType, decoded.member, decoded.decoration});
}
else
{
if(decoded.member < dataTypes[decoded.structType].children.size())
dataTypes[decoded.structType].children[decoded.member].decorations.Register(
decoded.decoration);
else
RDCERR("Non-deferred member decoration referenced invalid type member");
}
}
else if(opdata.op == Op::DecorationGroup || opdata.op == Op::GroupDecorate ||
opdata.op == Op::GroupMemberDecorate)
{
RDCERR("Unhandled decoration group usage");
}
}
void Processor::UnregisterOp(Iter it)
{
OpDecoder opdata(it);
if(opdata.result != Id() && opdata.resultType != Id())
idTypes[opdata.result] = Id();
if(opdata.result != Id())
idOffsets[opdata.result] = 0;
if(opdata.op == Op::Capability)
{
OpCapability decoded(it);
capabilities.erase(decoded.capability);
}
else if(opdata.op == Op::Extension)
{
OpExtension decoded(it);
extensions.erase(decoded.name);
}
else if(opdata.op == Op::ExtInstImport)
{
OpExtInstImport decoded(it);
extSets.erase(decoded.result);
}
else if(opdata.op == Op::EntryPoint)
{
OpEntryPoint decoded(it);
Id entry = decoded.entryPoint;
entries.removeOneIf([entry](const EntryPoint &e) { return e.id == entry; });
}
else if(opdata.op == Op::ExecutionMode)
{
OpExecutionMode decoded(it);
for(auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt)
{
if(entryIt->id == decoded.entryPoint)
{
entryIt->executionModes.Unregister(decoded);
break;
}
}
}
else if(opdata.op == Op::Variable)
{
Id result = opdata.result;
globals.removeOneIf([result](const Variable &e) { return e.id == result; });
}
else if(opdata.op == Op::ConstantNull || opdata.op == Op::Undef ||
opdata.op == Op::ConstantTrue || opdata.op == Op::ConstantFalse ||
opdata.op == Op::ConstantComposite || opdata.op == Op::Constant ||
opdata.op == Op::SpecConstantTrue || opdata.op == Op::SpecConstantFalse ||
opdata.op == Op::SpecConstantComposite || opdata.op == Op::SpecConstant)
{
constants.erase(opdata.result);
specConstants.erase(opdata.result);
}
else if(opdata.op == Op::SpecConstantOp)
{
specOps.erase(opdata.result);
specConstants.erase(opdata.result);
}
else if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt ||
opdata.op == Op::TypeFloat || opdata.op == Op::TypeVector ||
opdata.op == Op::TypeMatrix || opdata.op == Op::TypeStruct || opdata.op == Op::TypeArray ||
opdata.op == Op::TypePointer || opdata.op == Op::TypeRuntimeArray ||
opdata.op == Op::TypeRayQueryKHR || opdata.op == Op::TypeAccelerationStructureKHR)
{
dataTypes[opdata.result] = DataType();
}
else if(opdata.op == Op::TypeImage)
{
imageTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeSampler)
{
samplerTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeSampledImage)
{
sampledImageTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeFunction)
{
functionTypes.erase(opdata.result);
}
else if(opdata.op == Op::Decorate)
{
OpDecorate decoded(it);
decorations[decoded.target].Unregister(decoded.decoration);
}
else if(opdata.op == Op::DecorateId)
{
OpDecorateId decoded(it);
decorations[decoded.target].Unregister(decoded.decoration);
}
else if(opdata.op == Op::DecorateString)
{
OpDecorateString decoded(it);
decorations[decoded.target].Unregister(decoded.decoration);
}
else if(opdata.op == Op::MemberDecorate)
{
OpMemberDecorate decoded(it);
RDCASSERT(dataTypes[decoded.structureType].type == DataType::StructType);
if(decoded.member < dataTypes[decoded.structureType].children.size())
dataTypes[decoded.structureType].children[decoded.member].decorations.Unregister(
decoded.decoration);
}
else if(opdata.op == Op::MemberDecorateString)
{
OpMemberDecorateString decoded(it);
RDCASSERT(dataTypes[decoded.structType].type == DataType::StructType);
if(decoded.member < dataTypes[decoded.structType].children.size())
dataTypes[decoded.structType].children[decoded.member].decorations.Unregister(
decoded.decoration);
}
else if(opdata.op == Op::DecorationGroup || opdata.op == Op::GroupDecorate ||
opdata.op == Op::GroupMemberDecorate)
{
RDCERR("Unhandled decoration group usage");
}
}
void Processor::PostParse()
{
for(const DeferredMemberDecoration &dec : m_MemberDecorations)
if(dec.member < dataTypes[dec.id].children.size())
dataTypes[dec.id].children[dec.member].decorations.Register(dec.dec);
m_MemberDecorations.clear();
m_DeferMemberDecorations = false;
}
Iter Processor::GetID(Id id)
{
size_t offs = idOffsets[id];
if(offs)
return Iter(m_SPIRV, offs);
return Iter();
}
ConstIter Processor::GetID(Id id) const
{
size_t offs = idOffsets[id];
if(offs)
return ConstIter(m_SPIRV, offs);
return ConstIter();
}
ShaderVariable Processor::MakeNULL(const DataType &type, uint64_t value)
{
ShaderVariable v("", 0, 0, 0, 0);
v.rows = v.columns = 0;
for(uint8_t c = 0; c < 16; c++)
v.value.u64v[c] = value;
if(type.type == DataType::VectorType)
{
v.type = type.scalar().Type();
v.rows = 1;
v.columns = type.vector().count & 0xf;
}
else if(type.type == DataType::MatrixType)
{
v.type = type.scalar().Type();
v.rows = type.vector().count & 0xf;
v.columns = type.matrix().count & 0xf;
v.flags |= ShaderVariableFlags::RowMajorMatrix;
}
else if(type.type == DataType::ScalarType)
{
v.type = type.scalar().Type();
v.rows = 1;
v.columns = 1;
}
else if(type.type == DataType::PointerType)
{
v.type = VarType::GPUPointer;
v.rows = 1;
v.columns = 1;
}
else if(type.type == DataType::ArrayType)
{
// TODO handle spec constant array length here... somehow
v.members.resize(EvaluateConstant(type.length, {}).value.u32v[0]);
for(size_t i = 0; i < v.members.size(); i++)
{
v.members[i] = MakeNULL(dataTypes[type.InnerType()], value);
v.members[i].name = StringFormat::Fmt("[%zu]", i);
}
}
else
{
if(type.type == DataType::StructType)
v.type = VarType::Struct;
else
RDCWARN("Unexpected type %d making NULL", type.type);
v.members.resize(type.children.size());
for(size_t i = 0; i < v.members.size(); i++)
{
v.members[i] = MakeNULL(dataTypes[type.children[i].type], value);
v.members[i].name = StringFormat::Fmt("_child%zu", i);
}
}
return v;
}
ShaderVariable Processor::EvaluateConstant(Id constID, const rdcarray<SpecConstant> &specInfo) const
{
auto it = constants.find(constID);
if(it == constants.end())
{
RDCERR("Lookup of unknown constant %u", constID.value());
return ShaderVariable("unknown", 0, 0, 0, 0);
}
auto specopit = specOps.find(constID);
if(specopit != specOps.end())
{
const SpecOp &specop = specopit->second;
ShaderVariable ret = {};
const DataType &retType = dataTypes[specop.type];
ret.type = retType.scalar().Type();
if(specop.params.empty())
{
RDCERR("Expected paramaters for SpecConstantOp %s", ToStr(specop.op).c_str());
return ret;
}
// these instructions have special rules, so handle them manually
if(specop.op == Op::Select)
{
// evaluate the parameters
rdcarray<ShaderVariable> params;
for(size_t i = 0; i < specop.params.size(); i++)
params.push_back(EvaluateConstant(specop.params[i], specInfo));
if(params.size() != 3)
{
RDCERR("Expected 3 paramaters for SpecConstantOp Select, got %zu", params.size());
return ret;
}
// "If Condition is a scalar and true, the result is Object 1. If Condition is a scalar and
// false, the result is Object 2."
if(params[0].columns == 1)
return params[0].value.u32v[0] ? params[1] : params[2];
// "If Condition is a vector, Result Type must be a vector with the same number of components
// as Condition and the result is a [component-wise] mix of Object 1 and Object 2."
ret = params[1];
ret.name = "derived";
for(size_t i = 0; i < params[0].columns; i++)
{
if(retType.scalar().width == 64)
ret.value.u64v[i] =
params[0].value.u32v[i] ? params[1].value.u64v[i] : params[2].value.u64v[i];
else if(retType.scalar().width == 32)
ret.value.u32v[i] =
params[0].value.u32v[i] ? params[1].value.u32v[i] : params[2].value.u32v[i];
else if(retType.scalar().width == 16)
ret.value.u16v[i] =
params[0].value.u32v[i] ? params[1].value.u16v[i] : params[2].value.u16v[i];
else
ret.value.u8v[i] = params[0].value.u8v[i] ? params[1].value.u8v[i] : params[2].value.u8v[i];
}
}
else if(specop.op == Op::CompositeExtract)
{
ShaderVariable composite = EvaluateConstant(specop.params[0], specInfo);
// the remaining parameters are actually indices
rdcarray<uint32_t> indices;
for(size_t i = 1; i < specop.params.size(); i++)
indices.push_back(specop.params[i].value());
ret = composite;
ret.name = "derived";
RDCEraseEl(ret.value);
if(composite.rows > 1)
{
ret.rows = 1;
if(indices.size() == 1)
{
// matrix returning a vector
uint32_t row = indices[0];
for(uint32_t c = 0; c < ret.columns; c++)
{
if(retType.scalar().width == 64)
ret.value.u64v[c] = composite.value.u64v[row * composite.columns + c];
else if(retType.scalar().width == 32)
ret.value.u32v[c] = composite.value.u32v[row * composite.columns + c];
else if(retType.scalar().width == 16)
ret.value.u16v[c] = composite.value.u16v[row * composite.columns + c];
else
ret.value.u8v[c] = composite.value.u8v[row * composite.columns + c];
}
}
else if(indices.size() == 2)
{
// matrix returning a scalar
uint32_t row = indices[0];
uint32_t col = indices[1];
if(retType.scalar().width == 64)
ret.value.u64v[0] = composite.value.u64v[row * composite.columns + col];
else if(retType.scalar().width == 32)
ret.value.u32v[0] = composite.value.u32v[row * composite.columns + col];
else if(retType.scalar().width == 16)
ret.value.u16v[0] = composite.value.u16v[row * composite.columns + col];
else
ret.value.u8v[0] = composite.value.u8v[row * composite.columns + col];
}
else
{
RDCERR("Unexpected number of indices %zu to SpecConstantOp CompositeInsert",
indices.size());
}
}
else
{
ret.columns = 1;
if(indices.size() == 1)
{
uint32_t col = indices[0];
// vector returning a scalar
if(retType.scalar().width == 64)
ret.value.u64v[0] = composite.value.u64v[col];
else if(retType.scalar().width == 32)
ret.value.u32v[0] = composite.value.u32v[col];
else if(retType.scalar().width == 16)
ret.value.u16v[0] = composite.value.u16v[col];
else
ret.value.u8v[0] = composite.value.u8v[col];
}
else
{
RDCERR("Unexpected number of indices %zu to SpecConstantOp CompositeInsert",
indices.size());
}
}
return ret;
}
else if(specop.op == Op::CompositeInsert)
{
if(specop.params.size() < 3)
{
RDCERR("Expected at least 3 paramaters for SpecConstantOp CompositeInsert, got %zu",
specop.params.size());
return ret;
}
ShaderVariable object = EvaluateConstant(specop.params[0], specInfo);
ShaderVariable composite = EvaluateConstant(specop.params[1], specInfo);
// the remaining parameters are actually indices
rdcarray<uint32_t> indices;
for(size_t i = 2; i < specop.params.size(); i++)
indices.push_back(specop.params[i].value());
composite.name = "derived";
if(composite.rows > 1)
{
if(indices.size() == 1)
{
// matrix inserting a vector
uint32_t row = indices[0];
for(uint32_t c = 0; c < ret.columns; c++)
{
if(retType.scalar().width == 64)
composite.value.u64v[row * composite.columns + c] = object.value.u64v[c];
else if(retType.scalar().width == 32)
composite.value.u32v[row * composite.columns + c] = object.value.u32v[c];
else if(retType.scalar().width == 16)
composite.value.u16v[row * composite.columns + c] = object.value.u16v[c];
else if(retType.scalar().width == 8)
composite.value.u8v[row * composite.columns + c] = object.value.u8v[c];
}
}
else if(indices.size() == 2)
{
// matrix inserting a scalar
uint32_t row = indices[0];
uint32_t col = indices[1];
if(retType.scalar().width == 64)
composite.value.u64v[row * composite.columns + col] = object.value.u64v[0];
else if(retType.scalar().width == 32)
composite.value.u32v[row * composite.columns + col] = object.value.u32v[0];
else if(retType.scalar().width == 16)
composite.value.u16v[row * composite.columns + col] = object.value.u16v[0];
else
composite.value.u8v[row * composite.columns + col] = object.value.u8v[0];
}
else
{
RDCERR("Unexpected number of indices %zu to SpecConstantOp CompositeInsert",
indices.size());
}
}
else
{
if(indices.size() == 1)
{
// vector inserting a scalar
if(retType.scalar().width == 64)
composite.value.u64v[indices[0]] = object.value.u64v[0];
else if(retType.scalar().width == 32)
composite.value.u32v[indices[0]] = object.value.u32v[0];
else if(retType.scalar().width == 16)
composite.value.u16v[indices[0]] = object.value.u16v[0];
else
composite.value.u8v[indices[0]] = object.value.u8v[0];
}
else
{
RDCERR("Unexpected number of indices %zu to SpecConstantOp CompositeInsert",
indices.size());
}
}
return composite;
}
else if(specop.op == Op::VectorShuffle)
{
if(specop.params.size() < 3)
{
RDCERR("Expected at least 3 paramaters for SpecConstantOp VectorShuffle, got %zu",
specop.params.size());
return ret;
}
ShaderVariable vec1 = EvaluateConstant(specop.params[0], specInfo);
ShaderVariable vec2 = EvaluateConstant(specop.params[1], specInfo);
// the remaining parameters are actually indices
rdcarray<uint32_t> indices;
for(size_t i = 2; i < specop.params.size(); i++)
indices.push_back(specop.params[i].value());
ret = ShaderVariable("derived", 0, 0, 0, 0);
ret.type = retType.scalar().Type();
ret.columns = (uint8_t)indices.size();
for(size_t i = 0; i < indices.size(); i++)
{
uint32_t idx = indices[i];
if(idx < vec1.columns)
{
if(retType.scalar().width == 64)
ret.value.u64v[i] = vec1.value.u64v[idx];
else if(retType.scalar().width == 32)
ret.value.u32v[i] = vec1.value.u32v[idx];
else if(retType.scalar().width == 16)
ret.value.u16v[i] = vec1.value.u16v[idx];
else
ret.value.u8v[i] = vec1.value.u8v[idx];
}
else
{
idx -= vec1.columns;
if(retType.scalar().width == 64)
ret.value.u64v[i] = vec2.value.u64v[idx];
else if(retType.scalar().width == 32)
ret.value.u32v[i] = vec2.value.u32v[idx];
else if(retType.scalar().width == 16)
ret.value.u16v[i] = vec2.value.u16v[idx];
else
ret.value.u8v[i] = vec2.value.u8v[idx];
}
}
return ret;
}
else if(specop.op == Op::UConvert || specop.op == Op::SConvert || specop.op == Op::FConvert)
{
ShaderVariable param = EvaluateConstant(specop.params[0], specInfo);
ret = param;
ret.name = "converted";
RDCEraseEl(ret.value);
ret.type = retType.scalar().Type();
for(uint8_t i = 0; i < param.columns; i++)
{
if(specop.op == Op::UConvert)
{
uint64_t x = 0;
#undef _IMPL
#define _IMPL(I, S, U) x = comp<U>(param, i);
IMPL_FOR_INT_TYPES_FOR_TYPE(_IMPL, param.type);
#undef _IMPL
#define _IMPL(I, S, U) comp<U>(ret, i) = (U)x;
IMPL_FOR_INT_TYPES_FOR_TYPE(_IMPL, ret.type);
}
else if(specop.op == Op::SConvert)
{
int64_t x = 0;
#undef _IMPL
#define _IMPL(I, S, U) x = comp<S>(param, i);
IMPL_FOR_INT_TYPES_FOR_TYPE(_IMPL, param.type);
#undef _IMPL
#define _IMPL(I, S, U) comp<S>(ret, i) = (S)x;
IMPL_FOR_INT_TYPES_FOR_TYPE(_IMPL, ret.type);
}
else if(specop.op == Op::FConvert)
{
double x = 0.0;
#undef _IMPL
#define _IMPL(T) x = comp<T>(param, i);
IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, param.type);
#undef _IMPL
#define _IMPL(T) comp<T>(ret, i) = (T)x;
// IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, ret.type);
if(ret.type == VarType::Float)
comp<float>(ret, i) = (float)x;
else if(ret.type == VarType::Half)
comp<half_float::half>(ret, i) = (float)x;
else if(ret.type == VarType::Double)
comp<double>(ret, i) = (double)x;
}
}
return ret;
}
// evaluate the parameters
rdcarray<ShaderVariable> params;
for(size_t i = 0; i < specop.params.size(); i++)
params.push_back(EvaluateConstant(specop.params[i], specInfo));
// all other operations are component-wise on vectors or scalars. Check that rows are all 1 and
// all cols are identical
uint8_t cols = params[0].columns;
for(size_t i = 0; i < params.size(); i++)
{
RDCASSERT(cols == params[i].columns, i, params[i].columns);
RDCASSERT(params[i].rows == 1, i, params[i].rows);
cols = RDCMIN(cols, params[i].columns);
}
// check number of parameters
switch(specop.op)
{
case Op::SNegate:
case Op::Not:
case Op::LogicalNot:
if(params.size() != 1)
{
RDCERR("Expected 1 parameter for SpecConstantOp %s, got %zu", ToStr(specop.op).c_str(),
params.size());
return ret;
}
break;
case Op::IAdd:
case Op::ISub:
case Op::IMul:
case Op::UDiv:
case Op::SDiv:
case Op::UMod:
case Op::SRem:
case Op::SMod:
case Op::ShiftRightLogical:
case Op::ShiftRightArithmetic:
case Op::ShiftLeftLogical:
case Op::BitwiseOr:
case Op::BitwiseXor:
case Op::BitwiseAnd:
case Op::LogicalOr:
case Op::LogicalAnd:
case Op::LogicalEqual:
case Op::LogicalNotEqual:
case Op::IEqual:
case Op::INotEqual:
case Op::ULessThan:
case Op::SLessThan:
case Op::UGreaterThan:
case Op::SGreaterThan:
case Op::ULessThanEqual:
case Op::SLessThanEqual:
case Op::UGreaterThanEqual:
case Op::SGreaterThanEqual:
if(params.size() != 2)
{
RDCERR("Expected 2 paramaters for SpecConstantOp %s, got %zu", ToStr(specop.op).c_str(),
params.size());
return ret;
}
break;
default:
RDCERR("Unhandled SpecConstantOp:: operation %s", ToStr(specop.op).c_str());
return ret;
}
ret = params[0];
ret.name = "derived";
for(uint32_t col = 0; col < cols; col++)
{
ShaderValue a, b;
bool signedness = retType.scalar().signedness;
// upcast parameters to 64-bit width to simplify applying operations
for(size_t p = 0; p < params.size() && p < 2; p++)
{
const DataType &paramType = dataTypes[idTypes[specop.params[p]]];
ShaderValue &val = (p == 0) ? a : b;
if(paramType.scalar().type == Op::TypeFloat)
{
if(paramType.scalar().width == 64)
val.f64v[0] = params[p].value.f64v[col];
else if(paramType.scalar().width == 32)
val.f64v[0] = params[p].value.f32v[col];
else
val.f64v[0] = (float)params[p].value.f16v[col];
}
else
{
if(paramType.scalar().signedness)
{
if(paramType.scalar().width == 64)
val.s64v[0] = params[p].value.s64v[col];
else if(paramType.scalar().width == 32)
val.s64v[0] = params[p].value.s32v[col];
else if(paramType.scalar().width == 16)
val.s64v[0] = params[p].value.s16v[col];
else
val.s64v[0] = params[p].value.s8v[col];
}
else
{
if(paramType.scalar().width == 64)
val.u64v[0] = params[p].value.u64v[col];
else if(paramType.scalar().width == 32)
val.u64v[0] = params[p].value.u32v[col];
else if(paramType.scalar().width == 16)
val.u64v[0] = params[p].value.u16v[col];
else
val.u64v[0] = params[p].value.u8v[col];
}
}
}
switch(specop.op)
{
case Op::SNegate: a.s64v[0] = -a.s64v[0]; break;
case Op::Not: a.u64v[0] = ~a.u64v[0]; break;
case Op::LogicalNot: a.u64v[0] = a.u64v[0] ? 0 : 1; break;
case Op::IAdd:
if(signedness)
a.s64v[0] += b.s64v[0];
else
a.u64v[0] += b.u64v[0];
break;
case Op::ISub:
if(signedness)
a.s64v[0] -= b.s64v[0];
else
a.u64v[0] -= b.u64v[0];
break;
case Op::IMul:
if(signedness)
a.s64v[0] *= b.s64v[0];
else
a.u64v[0] *= b.u64v[0];
break;
case Op::UDiv: a.u64v[0] /= b.u64v[0]; break;
case Op::SDiv: a.s64v[0] /= b.s64v[0]; break;
case Op::UMod: a.u64v[0] %= b.u64v[0]; break;
case Op::SRem:
case Op::SMod:
{
int64_t result = a.s64v[0] % b.s64v[0];
// flip sign to match given input operand
// "the sign of r is the same as the sign of Operand 1."
if(specop.op == Op::SRem && ((result < 0) != (a.s64v[0] < 0)))
result = -result;
// "the sign of r is the same as the sign of Operand 2."
if(specop.op == Op::SMod && ((result < 0) != (b.s64v[0] < 0)))
result = -result;
break;
}
case Op::ShiftRightLogical: a.u64v[0] >>= b.u64v[0]; break;
case Op::ShiftRightArithmetic: a.s64v[0] >>= b.s64v[0]; break;
case Op::ShiftLeftLogical: a.u64v[0] <<= b.u64v[0]; break;
case Op::BitwiseOr: a.u64v[0] |= b.u64v[0]; break;
case Op::BitwiseXor: a.u64v[0] ^= b.u64v[0]; break;
case Op::BitwiseAnd: a.u64v[0] &= b.u64v[0]; break;
case Op::LogicalOr: a.u64v[0] = (a.u64v[0] || b.u64v[0]) ? 1 : 0; break;
case Op::LogicalAnd: a.u64v[0] = (a.u64v[0] && b.u64v[0]) ? 1 : 0; break;
case Op::LogicalEqual: a.u64v[0] = (a.u64v[0] == b.u64v[0]) ? 1 : 0; break;
case Op::LogicalNotEqual: a.u64v[0] = (a.u64v[0] != b.u64v[0]) ? 1 : 0; break;
case Op::IEqual: a.u64v[0] = (a.u64v[0] == b.u64v[0]) ? 1 : 0; break;
case Op::INotEqual: a.u64v[0] = (a.u64v[0] != b.u64v[0]) ? 1 : 0; break;
case Op::ULessThan: a.u64v[0] = (a.u64v[0] < b.u64v[0]) ? 1 : 0; break;
case Op::SLessThan: a.s64v[0] = (a.s64v[0] < b.s64v[0]) ? 1 : 0; break;
case Op::UGreaterThan: a.u64v[0] = (a.u64v[0] > b.u64v[0]) ? 1 : 0; break;
case Op::SGreaterThan: a.s64v[0] = (a.s64v[0] > b.s64v[0]) ? 1 : 0; break;
case Op::ULessThanEqual: a.u64v[0] = (a.u64v[0] <= b.u64v[0]) ? 1 : 0; break;
case Op::SLessThanEqual: a.s64v[0] = (a.s64v[0] <= b.s64v[0]) ? 1 : 0; break;
case Op::UGreaterThanEqual: a.u64v[0] = (a.u64v[0] >= b.u64v[0]) ? 1 : 0; break;
case Op::SGreaterThanEqual: a.s64v[0] = (a.s64v[0] >= b.s64v[0]) ? 1 : 0; break;
default: break;
}
// downcast back to the type required
if(retType.scalar().type == Op::TypeFloat)
{
if(retType.scalar().width == 64)
ret.value.f64v[col] = a.f64v[0];
else if(retType.scalar().width == 32)
ret.value.f32v[col] = (float)a.f64v[0];
else
ret.value.f16v[col].set((float)a.f64v[0]);
}
else if(signedness)
{
if(retType.scalar().width == 64)
ret.value.s64v[col] = a.s64v[0];
else if(retType.scalar().width == 32)
ret.value.s32v[col] =
(int32_t)RDCCLAMP(a.s64v[col], (int64_t)INT32_MIN, (int64_t)INT32_MAX);
else if(retType.scalar().width == 16)
ret.value.s16v[col] =
(int16_t)RDCCLAMP(a.s64v[col], (int64_t)INT16_MIN, (int64_t)INT16_MAX);
else
ret.value.s8v[col] = (int8_t)RDCCLAMP(a.s64v[col], (int64_t)INT8_MIN, (int64_t)INT8_MAX);
}
else
{
if(retType.scalar().width == 64)
ret.value.u64v[col] = a.u64v[0];
else if(retType.scalar().width == 32)
ret.value.u32v[col] = a.u64v[0] & 0xFFFFFFFF;
else if(retType.scalar().width == 16)
ret.value.u16v[col] = a.u64v[0] & 0xFFFF;
else
ret.value.u8v[col] = a.u64v[0] & 0xFF;
}
}
return ret;
}
const Constant &c = it->second;
if(decorations[c.id].flags & Decorations::HasSpecId)
{
for(const SpecConstant &spec : specInfo)
{
// if this constant is specialised, read its data instead
if(spec.specID == decorations[c.id].specID)
{
ShaderVariable ret = c.value;
// we can always just read into u64v - if the type is smaller the LSB maps nicely.
ret.value.u64v[0] = spec.value;
return ret;
}
}
}
if(c.op == Op::SpecConstantComposite)
{
ShaderVariable ret = c.value;
rdcarray<ShaderVariable> children;
// this is wasteful because we've probably already evaluated these constants, but we don't
// expect a huge tree of spec constants so it's cleaner to do it here than expect the caller to
// tidy up from its evaluated cache.
for(size_t i = 0; i < c.children.size(); i++)
children.push_back(EvaluateConstant(c.children[i], specInfo));
ConstructCompositeConstant(ret, children);
return ret;
}
return c.value;
}
}; // namespace rdcspv