Shader: Add SpirvDecoder
This commit is contained in:
parent
5a63eb4d97
commit
09df5f389e
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SPIRVDECODER_HPP
|
||||||
|
#define NAZARA_SPIRVDECODER_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvData.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class NAZARA_SHADER_API SpirvDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SpirvDecoder() = default;
|
||||||
|
SpirvDecoder(const SpirvDecoder&) = default;
|
||||||
|
SpirvDecoder(SpirvDecoder&&) = default;
|
||||||
|
~SpirvDecoder() = default;
|
||||||
|
|
||||||
|
void Decode(const UInt32* codepoints, std::size_t count);
|
||||||
|
|
||||||
|
SpirvDecoder& operator=(const SpirvDecoder&) = default;
|
||||||
|
SpirvDecoder& operator=(SpirvDecoder&&) = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct SpirvHeader;
|
||||||
|
|
||||||
|
inline const UInt32* GetCurrentPtr() const;
|
||||||
|
|
||||||
|
virtual bool HandleHeader(const SpirvHeader& header);
|
||||||
|
virtual bool HandleOpcode(const SpirvInstruction& instruction, UInt32 wordCount) = 0;
|
||||||
|
|
||||||
|
std::string ReadString();
|
||||||
|
UInt32 ReadWord();
|
||||||
|
|
||||||
|
struct SpirvHeader
|
||||||
|
{
|
||||||
|
UInt32 generatorId;
|
||||||
|
UInt32 bound;
|
||||||
|
UInt32 schema;
|
||||||
|
UInt32 versionNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const UInt32* m_currentCodepoint;
|
||||||
|
const UInt32* m_codepointEnd;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvDecoder.inl>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvDecoder.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
inline const UInt32* SpirvDecoder::GetCurrentPtr() const
|
||||||
|
{
|
||||||
|
return m_currentCodepoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -9,12 +9,13 @@
|
||||||
|
|
||||||
#include <Nazara/Prerequisites.hpp>
|
#include <Nazara/Prerequisites.hpp>
|
||||||
#include <Nazara/Shader/Config.hpp>
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvDecoder.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Nz
|
namespace Nz
|
||||||
{
|
{
|
||||||
class NAZARA_SHADER_API SpirvPrinter
|
class NAZARA_SHADER_API SpirvPrinter : SpirvDecoder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Settings;
|
struct Settings;
|
||||||
|
|
@ -39,9 +40,8 @@ namespace Nz
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AppendInstruction();
|
bool HandleHeader(const SpirvHeader& header) override;
|
||||||
std::string ReadString();
|
bool HandleOpcode(const SpirvInstruction& instruction, UInt32 wordCount) override;
|
||||||
UInt32 ReadWord();
|
|
||||||
|
|
||||||
struct State;
|
struct State;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
#include <Nazara/Prerequisites.hpp>
|
#include <Nazara/Prerequisites.hpp>
|
||||||
#include <Nazara/Shader/Config.hpp>
|
#include <Nazara/Shader/Config.hpp>
|
||||||
#include <Nazara/Shader/ShaderAstCache.hpp>
|
|
||||||
#include <Nazara/Shader/ShaderConstantValue.hpp>
|
#include <Nazara/Shader/ShaderConstantValue.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderNodes.hpp>
|
||||||
#include <Nazara/Shader/ShaderWriter.hpp>
|
#include <Nazara/Shader/ShaderWriter.hpp>
|
||||||
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -90,9 +90,7 @@ namespace Nz
|
||||||
|
|
||||||
struct Context
|
struct Context
|
||||||
{
|
{
|
||||||
ShaderAst::AstCache cache;
|
|
||||||
const States* states = nullptr;
|
const States* states = nullptr;
|
||||||
std::vector<SpirvBlock> functionBlocks;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExtVar
|
struct ExtVar
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvDecoder.hpp>
|
||||||
|
#include <Nazara/Core/CallOnExit.hpp>
|
||||||
|
#include <Nazara/Core/StackArray.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvData.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
void SpirvDecoder::Decode(const UInt32* codepoints, std::size_t count)
|
||||||
|
{
|
||||||
|
m_currentCodepoint = codepoints;
|
||||||
|
m_codepointEnd = codepoints + count;
|
||||||
|
|
||||||
|
UInt32 magicNumber = ReadWord();
|
||||||
|
if (magicNumber != SpirvMagicNumber)
|
||||||
|
throw std::runtime_error("invalid Spir-V: magic number didn't match");
|
||||||
|
|
||||||
|
UInt32 versionNumber = ReadWord();
|
||||||
|
if (versionNumber > SpirvVersion)
|
||||||
|
throw std::runtime_error("Spir-V is more recent than decoder, dismissing");
|
||||||
|
|
||||||
|
SpirvHeader header;
|
||||||
|
header.generatorId = ReadWord();
|
||||||
|
header.bound = ReadWord();
|
||||||
|
header.schema = ReadWord();
|
||||||
|
header.versionNumber = versionNumber;
|
||||||
|
|
||||||
|
if (!HandleHeader(header))
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (m_currentCodepoint < m_codepointEnd)
|
||||||
|
{
|
||||||
|
UInt32 firstWord = ReadWord();
|
||||||
|
|
||||||
|
UInt16 wordCount = static_cast<UInt16>((firstWord >> 16) & 0xFFFF);
|
||||||
|
UInt16 opcode = static_cast<UInt16>(firstWord & 0xFFFF);
|
||||||
|
|
||||||
|
const SpirvInstruction* inst = GetInstructionData(opcode);
|
||||||
|
if (!inst)
|
||||||
|
throw std::runtime_error("invalid instruction");
|
||||||
|
|
||||||
|
if (!HandleOpcode(*inst, wordCount))
|
||||||
|
break;
|
||||||
|
|
||||||
|
m_currentCodepoint += wordCount - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpirvDecoder::HandleHeader(const SpirvHeader& /*header*/)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SpirvDecoder::ReadString()
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
UInt32 value = ReadWord();
|
||||||
|
for (std::size_t j = 0; j < 4; ++j)
|
||||||
|
{
|
||||||
|
char c = static_cast<char>((value >> (j * 8)) & 0xFF);
|
||||||
|
if (c == '\0')
|
||||||
|
return str;
|
||||||
|
|
||||||
|
str.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 SpirvDecoder::ReadWord()
|
||||||
|
{
|
||||||
|
if (m_currentCodepoint >= m_codepointEnd)
|
||||||
|
throw std::runtime_error("unexpected end of stream");
|
||||||
|
|
||||||
|
return *m_currentCodepoint++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,10 +21,7 @@ namespace Nz
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const UInt32* codepoints;
|
|
||||||
std::size_t resultOffset;
|
std::size_t resultOffset;
|
||||||
std::size_t index = 0;
|
|
||||||
std::size_t count;
|
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
const Settings& settings;
|
const Settings& settings;
|
||||||
};
|
};
|
||||||
|
|
@ -32,71 +29,49 @@ namespace Nz
|
||||||
std::string SpirvPrinter::Print(const UInt32* codepoints, std::size_t count, const Settings& settings)
|
std::string SpirvPrinter::Print(const UInt32* codepoints, std::size_t count, const Settings& settings)
|
||||||
{
|
{
|
||||||
State state(settings);
|
State state(settings);
|
||||||
state.codepoints = codepoints;
|
|
||||||
state.count = count;
|
|
||||||
|
|
||||||
m_currentState = &state;
|
m_currentState = &state;
|
||||||
CallOnExit resetOnExit([&] { m_currentState = nullptr; });
|
CallOnExit resetOnExit([&] { m_currentState = nullptr; });
|
||||||
|
|
||||||
UInt32 magicNumber = ReadWord();
|
Decode(codepoints, count);
|
||||||
if (magicNumber != SpirvMagicNumber)
|
|
||||||
throw std::runtime_error("invalid Spir-V: magic number didn't match");
|
|
||||||
|
|
||||||
if (m_currentState->settings.printHeader)
|
|
||||||
m_currentState->stream << "Spir-V module\n";
|
|
||||||
|
|
||||||
UInt32 versionNumber = ReadWord();
|
|
||||||
if (versionNumber > SpirvVersion)
|
|
||||||
throw std::runtime_error("Spir-V is more recent than printer, dismissing");
|
|
||||||
|
|
||||||
UInt8 majorVersion = ((versionNumber) >> 16) & 0xFF;
|
|
||||||
UInt8 minorVersion = ((versionNumber) >> 8) & 0xFF;
|
|
||||||
|
|
||||||
UInt32 generatorId = ReadWord();
|
|
||||||
UInt32 bound = ReadWord();
|
|
||||||
UInt32 schema = ReadWord();
|
|
||||||
|
|
||||||
state.resultOffset = std::snprintf(nullptr, 0, "%%%u = ", bound);
|
|
||||||
|
|
||||||
if (m_currentState->settings.printHeader)
|
|
||||||
{
|
|
||||||
m_currentState->stream << "Version " + std::to_string(+majorVersion) << "." << std::to_string(+minorVersion) << "\n";
|
|
||||||
m_currentState->stream << "Generator: " << std::to_string(generatorId) << "\n";
|
|
||||||
m_currentState->stream << "Bound: " << std::to_string(bound) << "\n";
|
|
||||||
m_currentState->stream << "Schema: " << std::to_string(schema) << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
while (m_currentState->index < m_currentState->count)
|
|
||||||
AppendInstruction();
|
|
||||||
|
|
||||||
return m_currentState->stream.str();
|
return m_currentState->stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvPrinter::AppendInstruction()
|
bool SpirvPrinter::HandleHeader(const SpirvHeader& header)
|
||||||
{
|
{
|
||||||
std::size_t startIndex = m_currentState->index;
|
UInt8 majorVersion = ((header.versionNumber) >> 16) & 0xFF;
|
||||||
|
UInt8 minorVersion = ((header.versionNumber) >> 8) & 0xFF;
|
||||||
|
|
||||||
UInt32 firstWord = ReadWord();
|
m_currentState->resultOffset = std::snprintf(nullptr, 0, "%%%u = ", header.bound);
|
||||||
|
|
||||||
UInt16 wordCount = static_cast<UInt16>((firstWord >> 16) & 0xFFFF);
|
if (m_currentState->settings.printHeader)
|
||||||
UInt16 opcode = static_cast<UInt16>(firstWord & 0xFFFF);
|
{
|
||||||
|
m_currentState->stream << "Version " + std::to_string(+majorVersion) << "." << std::to_string(+minorVersion) << "\n";
|
||||||
|
m_currentState->stream << "Generator: " << std::to_string(header.generatorId) << "\n";
|
||||||
|
m_currentState->stream << "Bound: " << std::to_string(header.bound) << "\n";
|
||||||
|
m_currentState->stream << "Schema: " << std::to_string(header.schema) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
const SpirvInstruction* inst = GetInstructionData(opcode);
|
return true;
|
||||||
if (!inst)
|
}
|
||||||
throw std::runtime_error("invalid instruction");
|
|
||||||
|
bool SpirvPrinter::HandleOpcode(const SpirvInstruction& instruction, UInt32 wordCount)
|
||||||
|
{
|
||||||
|
const UInt32* startPtr = GetCurrentPtr();
|
||||||
|
|
||||||
if (m_currentState->settings.printParameters)
|
if (m_currentState->settings.printParameters)
|
||||||
{
|
{
|
||||||
std::ostringstream instructionStream;
|
std::ostringstream instructionStream;
|
||||||
instructionStream << inst->name;
|
instructionStream << instruction.name;
|
||||||
|
|
||||||
UInt32 resultId = 0;
|
UInt32 resultId = 0;
|
||||||
|
|
||||||
std::size_t currentOperand = 0;
|
std::size_t currentOperand = 0;
|
||||||
std::size_t instructionEnd = startIndex + wordCount;
|
const UInt32* endPtr = startPtr + wordCount;
|
||||||
while (m_currentState->index < instructionEnd)
|
while (GetCurrentPtr() < endPtr)
|
||||||
{
|
{
|
||||||
const SpirvInstruction::Operand* operand = &inst->operands[currentOperand];
|
const SpirvInstruction::Operand* operand = &instruction.operands[currentOperand];
|
||||||
|
|
||||||
if (operand->kind != SpirvOperandKind::IdResult)
|
if (operand->kind != SpirvOperandKind::IdResult)
|
||||||
{
|
{
|
||||||
|
|
@ -181,7 +156,7 @@ namespace Nz
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
case SpirvOperandKind::PairLiteralIntegerIdRef:
|
case SpirvOperandKind::PairLiteralIntegerIdRef:
|
||||||
{
|
{
|
||||||
ReadWord();
|
ReadWord();
|
||||||
|
|
@ -210,13 +185,12 @@ namespace Nz
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
resultId = ReadWord();
|
resultId = ReadWord();
|
||||||
|
|
||||||
if (currentOperand < inst->minOperandCount - 1)
|
if (currentOperand < instruction.minOperandCount - 1)
|
||||||
currentOperand++;
|
currentOperand++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,42 +205,12 @@ namespace Nz
|
||||||
m_currentState->stream << instructionStream.str();
|
m_currentState->stream << instructionStream.str();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
m_currentState->stream << instruction.name;
|
||||||
m_currentState->stream << inst->name;
|
|
||||||
|
|
||||||
m_currentState->index += wordCount - 1;
|
|
||||||
if (m_currentState->index > m_currentState->count)
|
|
||||||
throw std::runtime_error("unexpected end of stream");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_currentState->stream << "\n";
|
m_currentState->stream << "\n";
|
||||||
|
|
||||||
assert(m_currentState->index == startIndex + wordCount);
|
assert(GetCurrentPtr() == startPtr + wordCount);
|
||||||
}
|
|
||||||
|
|
||||||
std::string SpirvPrinter::ReadString()
|
return true;
|
||||||
{
|
|
||||||
std::string str;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
UInt32 value = ReadWord();
|
|
||||||
for (std::size_t j = 0; j < 4; ++j)
|
|
||||||
{
|
|
||||||
char c = static_cast<char>((value >> (j * 8)) & 0xFF);
|
|
||||||
if (c == '\0')
|
|
||||||
return str;
|
|
||||||
|
|
||||||
str.push_back(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt32 SpirvPrinter::ReadWord()
|
|
||||||
{
|
|
||||||
if (m_currentState->index >= m_currentState->count)
|
|
||||||
throw std::runtime_error("unexpected end of stream");
|
|
||||||
|
|
||||||
return m_currentState->codepoints[m_currentState->index++];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue