mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-26 09:21:37 +00:00
1722 lines
52 KiB
C++
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 ¶mType = 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
|