mirror of https://github.com/doitsujin/dxvk
Merge 913f7548dd
into 9b019d26ac
This commit is contained in:
commit
abbdff4775
|
@ -895,7 +895,7 @@ namespace dxvk {
|
|||
info.pushConstOffset = m_pushConstOffset;
|
||||
info.pushConstSize = m_pushConstSize;
|
||||
|
||||
return new DxvkShader(info, m_module.compile());
|
||||
return new DxvkShader(info, m_module);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -284,7 +284,7 @@ namespace dxvk {
|
|||
info.bindings = &m_bufferBinding;
|
||||
info.inputMask = m_inputMask;
|
||||
|
||||
return new DxvkShader(info, m_module.compile());
|
||||
return new DxvkShader(info, m_module);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -272,7 +272,7 @@ namespace dxvk {
|
|||
info.xfbStrides[i] = m_moduleInfo.xfb->strides[i];
|
||||
}
|
||||
|
||||
return new DxvkShader(info, m_module.compile());
|
||||
return new DxvkShader(info, m_module);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -233,7 +233,7 @@ namespace dxvk {
|
|||
if (m_programInfo.type() == DxsoProgramTypes::PixelShader)
|
||||
info.flatShadingInputs = m_ps.flatShadingMask;
|
||||
|
||||
return new DxvkShader(info, m_module.compile());
|
||||
return new DxvkShader(info, m_module);
|
||||
}
|
||||
|
||||
void DxsoCompiler::emitInit() {
|
||||
|
|
|
@ -42,6 +42,44 @@ namespace dxvk {
|
|||
return hash;
|
||||
}
|
||||
|
||||
void DxvkShader::gatherBindingOffsets(
|
||||
SpirvCodeBuffer& code,
|
||||
std::vector<BindingOffsets>& offsets) {
|
||||
// Run an analysis pass over the SPIR-V code to gather some
|
||||
// info that we may need during pipeline compilation.
|
||||
std::vector<BindingOffsets> bindingOffsets;
|
||||
std::vector<uint32_t> varIds;
|
||||
|
||||
for (const auto& ins : code) {
|
||||
if (ins.opCode() == spv::OpDecorate) {
|
||||
if (ins.arg(2) == spv::DecorationBinding) {
|
||||
uint32_t varId = ins.arg(1);
|
||||
bindingOffsets.resize(std::max(bindingOffsets.size(), size_t(varId + 1)));
|
||||
bindingOffsets[varId].bindingId = ins.arg(3);
|
||||
bindingOffsets[varId].bindingOffset = ins.offset() + 3;
|
||||
varIds.push_back(varId);
|
||||
}
|
||||
|
||||
if (ins.arg(2) == spv::DecorationDescriptorSet) {
|
||||
uint32_t varId = ins.arg(1);
|
||||
bindingOffsets.resize(std::max(bindingOffsets.size(), size_t(varId + 1)));
|
||||
bindingOffsets[varId].setOffset = ins.offset() + 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore the actual shader code, there's nothing interesting for us in there.
|
||||
if (ins.opCode() == spv::OpFunction)
|
||||
break;
|
||||
}
|
||||
|
||||
// Combine spec constant IDs with other binding info
|
||||
for (auto varId : varIds) {
|
||||
BindingOffsets info = bindingOffsets[varId];
|
||||
|
||||
if (info.bindingOffset)
|
||||
offsets.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
DxvkShader::DxvkShader(
|
||||
const DxvkShaderCreateInfo& info,
|
||||
|
@ -75,45 +113,19 @@ namespace dxvk {
|
|||
|
||||
// Run an analysis pass over the SPIR-V code to gather some
|
||||
// info that we may need during pipeline compilation.
|
||||
std::vector<BindingOffsets> bindingOffsets;
|
||||
std::vector<uint32_t> varIds;
|
||||
|
||||
SpirvCodeBuffer code = std::move(spirv);
|
||||
uint32_t o1VarId = 0;
|
||||
|
||||
for (auto ins : code) {
|
||||
if (ins.opCode() == spv::OpDecorate) {
|
||||
if (ins.arg(2) == spv::DecorationBinding) {
|
||||
uint32_t varId = ins.arg(1);
|
||||
bindingOffsets.resize(std::max(bindingOffsets.size(), size_t(varId + 1)));
|
||||
bindingOffsets[varId].bindingId = ins.arg(3);
|
||||
bindingOffsets[varId].bindingOffset = ins.offset() + 3;
|
||||
varIds.push_back(varId);
|
||||
}
|
||||
|
||||
if (ins.arg(2) == spv::DecorationBuiltIn) {
|
||||
if (ins.arg(3) == spv::BuiltInPosition)
|
||||
m_flags.set(DxvkShaderFlag::ExportsPosition);
|
||||
}
|
||||
|
||||
if (ins.arg(2) == spv::DecorationDescriptorSet) {
|
||||
uint32_t varId = ins.arg(1);
|
||||
bindingOffsets.resize(std::max(bindingOffsets.size(), size_t(varId + 1)));
|
||||
bindingOffsets[varId].setOffset = ins.offset() + 3;
|
||||
}
|
||||
|
||||
if (ins.arg(2) == spv::DecorationSpecId) {
|
||||
if (ins.arg(3) <= MaxNumSpecConstants)
|
||||
m_specConstantMask |= 1u << ins.arg(3);
|
||||
}
|
||||
|
||||
if (ins.arg(2) == spv::DecorationLocation && ins.arg(3) == 1) {
|
||||
m_o1LocOffset = ins.offset() + 3;
|
||||
o1VarId = ins.arg(1);
|
||||
}
|
||||
|
||||
if (ins.arg(2) == spv::DecorationIndex && ins.arg(1) == o1VarId)
|
||||
m_o1IdxOffset = ins.offset() + 3;
|
||||
}
|
||||
|
||||
if (ins.opCode() == spv::OpMemberDecorate) {
|
||||
|
@ -151,12 +163,43 @@ namespace dxvk {
|
|||
break;
|
||||
}
|
||||
|
||||
// Combine spec constant IDs with other binding info
|
||||
for (auto varId : varIds) {
|
||||
BindingOffsets info = bindingOffsets[varId];
|
||||
// Don't set pipeline library flag if the shader
|
||||
// doesn't actually support pipeline libraries
|
||||
m_needsLibraryCompile = canUsePipelineLibrary(true);
|
||||
}
|
||||
|
||||
if (info.bindingOffset)
|
||||
m_bindingOffsets.push_back(info);
|
||||
DxvkShader::DxvkShader(
|
||||
const DxvkShaderCreateInfo& info,
|
||||
const SpirvModule& spirv)
|
||||
: m_info (info),
|
||||
m_code (spirv.compile()),
|
||||
m_flags (spirv.getShaderFlags()),
|
||||
m_specConstantMask (spirv.getSpecConstantMask()),
|
||||
m_bindings (info.stage) {
|
||||
m_info.uniformData = nullptr;
|
||||
m_info.bindings = nullptr;
|
||||
|
||||
// Copy resource binding slot infos
|
||||
for (uint32_t i = 0; i < info.bindingCount; i++) {
|
||||
DxvkBindingInfo binding = info.bindings[i];
|
||||
binding.stage = info.stage;
|
||||
m_bindings.addBinding(binding);
|
||||
}
|
||||
|
||||
if (info.pushConstSize) {
|
||||
VkPushConstantRange pushConst;
|
||||
pushConst.stageFlags = info.stage;
|
||||
pushConst.offset = info.pushConstOffset;
|
||||
pushConst.size = info.pushConstSize;
|
||||
|
||||
m_bindings.addPushConstantRange(pushConst);
|
||||
}
|
||||
|
||||
// Copy uniform buffer data
|
||||
if (info.uniformSize) {
|
||||
m_uniformData.resize(info.uniformSize);
|
||||
std::memcpy(m_uniformData.data(), info.uniformData, info.uniformSize);
|
||||
m_info.uniformData = m_uniformData.data();
|
||||
}
|
||||
|
||||
// Don't set pipeline library flag if the shader
|
||||
|
@ -164,7 +207,6 @@ namespace dxvk {
|
|||
m_needsLibraryCompile = canUsePipelineLibrary(true);
|
||||
}
|
||||
|
||||
|
||||
DxvkShader::~DxvkShader() {
|
||||
|
||||
}
|
||||
|
@ -175,9 +217,12 @@ namespace dxvk {
|
|||
const DxvkShaderModuleCreateInfo& state) const {
|
||||
SpirvCodeBuffer spirvCode = m_code.decompress();
|
||||
uint32_t* code = spirvCode.data();
|
||||
|
||||
std::vector<BindingOffsets> bindingOffsets;
|
||||
|
||||
gatherBindingOffsets(spirvCode, bindingOffsets);
|
||||
|
||||
// Remap resource binding IDs
|
||||
for (const auto& info : m_bindingOffsets) {
|
||||
for (const auto& info : bindingOffsets) {
|
||||
auto mappedBinding = layout->lookupBinding(m_info.stage, info.bindingId);
|
||||
|
||||
if (mappedBinding) {
|
||||
|
@ -190,8 +235,30 @@ namespace dxvk {
|
|||
|
||||
// For dual-source blending we need to re-map
|
||||
// location 1, index 0 to location 0, index 1
|
||||
if (state.fsDualSrcBlend && m_o1IdxOffset && m_o1LocOffset)
|
||||
std::swap(code[m_o1IdxOffset], code[m_o1LocOffset]);
|
||||
if (state.fsDualSrcBlend) {
|
||||
uint32_t o1VarId = 0;
|
||||
uint32_t o1IdxOffset = 0;
|
||||
uint32_t o1LocOffset = 0;
|
||||
|
||||
for (auto ins : spirvCode) {
|
||||
if (ins.opCode() == spv::OpDecorate) {
|
||||
if (ins.arg(2) == spv::DecorationLocation && ins.arg(3) == 1) {
|
||||
o1LocOffset = ins.offset() + 3;
|
||||
o1VarId = ins.arg(1);
|
||||
}
|
||||
|
||||
if (ins.arg(2) == spv::DecorationIndex && ins.arg(1) == o1VarId)
|
||||
o1IdxOffset = ins.offset() + 3;
|
||||
}
|
||||
|
||||
// Ignore the actual shader code, there's nothing interesting for us in there.
|
||||
if (ins.opCode() == spv::OpFunction)
|
||||
break;
|
||||
}
|
||||
|
||||
if (o1IdxOffset && o1LocOffset)
|
||||
std::swap(code[o1IdxOffset], code[o1LocOffset]);
|
||||
}
|
||||
|
||||
// Replace undefined input variables with zero
|
||||
for (uint32_t u : bit::BitMask(state.undefinedInputs))
|
||||
|
@ -430,8 +497,9 @@ namespace dxvk {
|
|||
if (numWords) {
|
||||
code.beginInsertion(ins.offset());
|
||||
code.erase(numWords);
|
||||
code.endInsertion();
|
||||
|
||||
iter = SpirvInstructionIterator(code.data(), code.endInsertion(), code.dwords());
|
||||
iter = SpirvInstructionIterator(code.data(), ins.offset(), code.dwords());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,24 +18,6 @@ namespace dxvk {
|
|||
class DxvkPipelineManager;
|
||||
struct DxvkPipelineStats;
|
||||
|
||||
/**
|
||||
* \brief Shader flags
|
||||
*
|
||||
* Provides extra information about the features
|
||||
* used by a shader.
|
||||
*/
|
||||
enum DxvkShaderFlag : uint64_t {
|
||||
HasSampleRateShading,
|
||||
HasTransformFeedback,
|
||||
ExportsPosition,
|
||||
ExportsStencilRef,
|
||||
ExportsViewportIndexLayerFromVertexStage,
|
||||
UsesFragmentCoverage,
|
||||
UsesSparseResidency,
|
||||
};
|
||||
|
||||
using DxvkShaderFlags = Flags<DxvkShaderFlag>;
|
||||
|
||||
/**
|
||||
* \brief Shader info
|
||||
*/
|
||||
|
@ -94,11 +76,21 @@ namespace dxvk {
|
|||
class DxvkShader : public RcObject {
|
||||
|
||||
public:
|
||||
|
||||
struct BindingOffsets {
|
||||
uint32_t bindingId;
|
||||
uint32_t bindingOffset;
|
||||
uint32_t setOffset;
|
||||
};
|
||||
|
||||
DxvkShader(
|
||||
const DxvkShaderCreateInfo& info,
|
||||
SpirvCodeBuffer&& spirv);
|
||||
|
||||
DxvkShader(
|
||||
const DxvkShaderCreateInfo& info,
|
||||
const SpirvModule& spirv);
|
||||
|
||||
~DxvkShader();
|
||||
|
||||
/**
|
||||
|
@ -246,12 +238,6 @@ namespace dxvk {
|
|||
|
||||
private:
|
||||
|
||||
struct BindingOffsets {
|
||||
uint32_t bindingId;
|
||||
uint32_t bindingOffset;
|
||||
uint32_t setOffset;
|
||||
};
|
||||
|
||||
DxvkShaderCreateInfo m_info;
|
||||
SpirvCompressedBuffer m_code;
|
||||
|
||||
|
@ -259,14 +245,10 @@ namespace dxvk {
|
|||
DxvkShaderKey m_key;
|
||||
size_t m_hash = 0;
|
||||
|
||||
size_t m_o1IdxOffset = 0;
|
||||
size_t m_o1LocOffset = 0;
|
||||
|
||||
uint32_t m_specConstantMask = 0;
|
||||
std::atomic<bool> m_needsLibraryCompile = { true };
|
||||
|
||||
std::vector<char> m_uniformData;
|
||||
std::vector<BindingOffsets> m_bindingOffsets;
|
||||
|
||||
DxvkBindingLayout m_bindings;
|
||||
|
||||
|
@ -283,6 +265,10 @@ namespace dxvk {
|
|||
SpirvCodeBuffer& code,
|
||||
uint32_t inputMask);
|
||||
|
||||
static void gatherBindingOffsets(
|
||||
SpirvCodeBuffer& code,
|
||||
std::vector<BindingOffsets>& offsets);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -9,14 +9,12 @@ namespace dxvk {
|
|||
SpirvCodeBuffer::~SpirvCodeBuffer() { }
|
||||
|
||||
|
||||
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size)
|
||||
: m_ptr(size) {
|
||||
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size) {
|
||||
m_code.resize(size);
|
||||
}
|
||||
|
||||
|
||||
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size, const uint32_t* data)
|
||||
: m_ptr(size) {
|
||||
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size, const uint32_t* data) {
|
||||
m_code.resize(size);
|
||||
std::memcpy(m_code.data(), data, size * sizeof(uint32_t));
|
||||
}
|
||||
|
@ -35,18 +33,20 @@ namespace dxvk {
|
|||
m_code.resize(buffer.size() / sizeof(uint32_t));
|
||||
std::memcpy(reinterpret_cast<char*>(m_code.data()),
|
||||
buffer.data(), m_code.size() * sizeof(uint32_t));
|
||||
|
||||
m_ptr = m_code.size();
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvCodeBuffer::allocId() {
|
||||
constexpr size_t BoundIdsOffset = 3;
|
||||
|
||||
if (m_code.size() <= BoundIdsOffset)
|
||||
if (this->dwords() <= BoundIdsOffset)
|
||||
return 0;
|
||||
|
||||
return m_code[BoundIdsOffset]++;
|
||||
// If we are inserting, the buffers are swapped
|
||||
if (m_ptr == not_inserting)
|
||||
return m_code[BoundIdsOffset]++;
|
||||
else
|
||||
return m_insert[BoundIdsOffset]++;
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,92 +59,21 @@ namespace dxvk {
|
|||
const uint32_t* src = other.m_code.data();
|
||||
|
||||
std::memcpy(dst + size, src, other.size());
|
||||
m_ptr += other.m_code.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putWord(uint32_t word) {
|
||||
m_code.insert(m_code.begin() + m_ptr, word);
|
||||
m_ptr += 1;
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putIns(spv::Op opCode, uint16_t wordCount) {
|
||||
this->putWord(
|
||||
(static_cast<uint32_t>(opCode) << 0)
|
||||
| (static_cast<uint32_t>(wordCount) << 16));
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putInt32(uint32_t word) {
|
||||
this->putWord(word);
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putInt64(uint64_t value) {
|
||||
this->putWord(value >> 0);
|
||||
this->putWord(value >> 32);
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putFloat32(float value) {
|
||||
uint32_t tmp;
|
||||
static_assert(sizeof(tmp) == sizeof(value));
|
||||
std::memcpy(&tmp, &value, sizeof(value));
|
||||
this->putInt32(tmp);
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putFloat64(double value) {
|
||||
uint64_t tmp;
|
||||
static_assert(sizeof(tmp) == sizeof(value));
|
||||
std::memcpy(&tmp, &value, sizeof(value));
|
||||
this->putInt64(tmp);
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putStr(const char* str) {
|
||||
uint32_t word = 0;
|
||||
uint32_t nbit = 0;
|
||||
|
||||
for (uint32_t i = 0; str[i] != '\0'; str++) {
|
||||
word |= (static_cast<uint32_t>(str[i]) & 0xFF) << nbit;
|
||||
|
||||
if ((nbit += 8) == 32) {
|
||||
this->putWord(word);
|
||||
word = 0;
|
||||
nbit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Commit current word
|
||||
this->putWord(word);
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::putHeader(uint32_t version, uint32_t boundIds) {
|
||||
this->putWord(spv::MagicNumber);
|
||||
this->putWord(version);
|
||||
this->putWord(0); // Generator
|
||||
this->putWord(boundIds);
|
||||
this->putWord(0); // Schema
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::erase(size_t size) {
|
||||
m_code.erase(
|
||||
m_code.begin() + m_ptr,
|
||||
m_code.begin() + m_ptr + size);
|
||||
if (m_ptr == not_inserting)
|
||||
return;
|
||||
|
||||
// If we are inserting, the buffers are swapped
|
||||
m_insert.erase(
|
||||
m_insert.begin() + m_ptr,
|
||||
m_insert.begin() + m_ptr + size);
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvCodeBuffer::strLen(const char* str) {
|
||||
// Null-termination plus padding
|
||||
return (std::strlen(str) + 4) / 4;
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::store(std::ostream& stream) const {
|
||||
stream.write(
|
||||
reinterpret_cast<const char*>(m_code.data()),
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "spirv_instruction.h"
|
||||
#include "spirv_writer.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
|
@ -15,8 +16,9 @@ namespace dxvk {
|
|||
* Stores arbitrary SPIR-V instructions in a
|
||||
* format that can be read by Vulkan drivers.
|
||||
*/
|
||||
class SpirvCodeBuffer {
|
||||
|
||||
class SpirvCodeBuffer : public SpirvWriter<SpirvCodeBuffer> {
|
||||
static constexpr size_t not_inserting = ~(size_t)0;
|
||||
|
||||
public:
|
||||
|
||||
SpirvCodeBuffer();
|
||||
|
@ -47,7 +49,7 @@ namespace dxvk {
|
|||
* \returns Code size, in dwords
|
||||
*/
|
||||
uint32_t dwords() const {
|
||||
return m_code.size();
|
||||
return m_code.size() + m_insert.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +57,7 @@ namespace dxvk {
|
|||
* \returns Code size, in bytes
|
||||
*/
|
||||
size_t size() const {
|
||||
return m_code.size() * sizeof(uint32_t);
|
||||
return this->dwords() * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,57 +105,9 @@ namespace dxvk {
|
|||
* \brief Appends an 32-bit word to the buffer
|
||||
* \param [in] word The word to append
|
||||
*/
|
||||
void putWord(uint32_t word);
|
||||
|
||||
/**
|
||||
* \brief Appends an instruction word to the buffer
|
||||
*
|
||||
* Adds a single word containing both the word count
|
||||
* and the op code number for a single instruction.
|
||||
* \param [in] opCode Operand code
|
||||
* \param [in] wordCount Number of words
|
||||
*/
|
||||
void putIns(spv::Op opCode, uint16_t wordCount);
|
||||
|
||||
/**
|
||||
* \brief Appends a 32-bit integer to the buffer
|
||||
* \param [in] value The number to add
|
||||
*/
|
||||
void putInt32(uint32_t word);
|
||||
|
||||
/**
|
||||
* \brief Appends a 64-bit integer to the buffer
|
||||
*
|
||||
* A 64-bit integer will take up two 32-bit words.
|
||||
* \param [in] value 64-bit value to add
|
||||
*/
|
||||
void putInt64(uint64_t value);
|
||||
|
||||
/**
|
||||
* \brief Appends a 32-bit float to the buffer
|
||||
* \param [in] value The number to add
|
||||
*/
|
||||
void putFloat32(float value);
|
||||
|
||||
/**
|
||||
* \brief Appends a 64-bit float to the buffer
|
||||
* \param [in] value The number to add
|
||||
*/
|
||||
void putFloat64(double value);
|
||||
|
||||
/**
|
||||
* \brief Appends a literal string to the buffer
|
||||
* \param [in] str String to append to the buffer
|
||||
*/
|
||||
void putStr(const char* str);
|
||||
|
||||
/**
|
||||
* \brief Adds the header to the buffer
|
||||
*
|
||||
* \param [in] version SPIR-V version
|
||||
* \param [in] boundIds Number of bound IDs
|
||||
*/
|
||||
void putHeader(uint32_t version, uint32_t boundIds);
|
||||
void putWord(uint32_t word) {
|
||||
m_code.push_back(word);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erases given number of dwords
|
||||
|
@ -164,14 +118,6 @@ namespace dxvk {
|
|||
*/
|
||||
void erase(size_t size);
|
||||
|
||||
/**
|
||||
* \brief Computes length of a literal string
|
||||
*
|
||||
* \param [in] str The string to check
|
||||
* \returns Number of words consumed by a string
|
||||
*/
|
||||
uint32_t strLen(const char* str);
|
||||
|
||||
/**
|
||||
* \brief Stores the SPIR-V module to a stream
|
||||
*
|
||||
|
@ -192,7 +138,7 @@ namespace dxvk {
|
|||
* \returns Current instruction pointr
|
||||
*/
|
||||
size_t getInsertionPtr() const {
|
||||
return m_ptr;
|
||||
return m_code.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -203,6 +149,7 @@ namespace dxvk {
|
|||
* \returns Current instruction pointr
|
||||
*/
|
||||
void beginInsertion(size_t ptr) {
|
||||
std::swap(m_code, m_insert);
|
||||
m_ptr = ptr;
|
||||
}
|
||||
|
||||
|
@ -214,14 +161,20 @@ namespace dxvk {
|
|||
* this will restore default behaviour.
|
||||
* \returns Previous instruction pointer
|
||||
*/
|
||||
size_t endInsertion() {
|
||||
return std::exchange(m_ptr, m_code.size());
|
||||
void endInsertion() {
|
||||
std::swap(m_code, m_insert);
|
||||
m_code.insert(m_code.begin() + m_ptr,
|
||||
m_insert.begin(),
|
||||
m_insert.end());
|
||||
m_insert.clear();
|
||||
m_ptr = not_inserting;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::vector<uint32_t> m_code;
|
||||
size_t m_ptr = 0;
|
||||
std::vector<uint32_t> m_insert;
|
||||
size_t m_ptr = not_inserting;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -3,81 +3,28 @@
|
|||
namespace dxvk {
|
||||
|
||||
SpirvCompressedBuffer::SpirvCompressedBuffer()
|
||||
: m_size(0) {
|
||||
: m_dwords(0) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
SpirvCompressedBuffer::SpirvCompressedBuffer(SpirvCodeBuffer& code)
|
||||
: m_size(code.dwords()) {
|
||||
// The compression (detailed below) achieves roughly 55% of the
|
||||
// original size on average and is very consistent, so an initial
|
||||
// estimate of roughly 58% will be accurate most of the time.
|
||||
const uint32_t* data = code.data();
|
||||
m_code.reserve((m_size * 75) / 128);
|
||||
SpirvCompressedBuffer::SpirvCompressedBuffer(
|
||||
const SpirvCodeBuffer& code)
|
||||
: m_dwords(code.dwords()) {
|
||||
size_t dwords = m_dwords;
|
||||
if (dwords == 0)
|
||||
return;
|
||||
|
||||
std::array<uint32_t, 16> block;
|
||||
uint32_t blockMask = 0;
|
||||
uint32_t blockOffset = 0;
|
||||
const uint32_t *src = code.data();
|
||||
const uint32_t *end = src + dwords;
|
||||
do {
|
||||
uint32_t word = *src++;
|
||||
m_code.push_back(word & 0x7f);
|
||||
while (word >>= 7)
|
||||
m_code.push_back((word & 0x7f) | 0x80);
|
||||
} while (src != end);
|
||||
|
||||
// The algorithm used is a simple variable-to-fixed compression that
|
||||
// encodes up to two consecutive SPIR-V tokens into one DWORD using
|
||||
// a small number of different encodings. While not achieving great
|
||||
// compression ratios, the main goal is to allow decompression code
|
||||
// to be fast, with short dependency chains.
|
||||
// Compressed tokens are stored in blocks of 16 DWORDs, each preceeded
|
||||
// by a single DWORD which stores the layout for each DWORD, two bits
|
||||
// each. The supported layouts, are as follows:
|
||||
// 0x0: 1x 32-bit; 0x1: 1x 20-bit + 1x 12-bit
|
||||
// 0x2: 2x 16-bit; 0x3: 1x 12-bit + 1x 20-bit
|
||||
// These layouts are chosen to allow reasonably efficient encoding of
|
||||
// opcode tokens, which usually fit into 20 bits, followed by type IDs,
|
||||
// which tend to be low as well since most types are defined early.
|
||||
for (size_t i = 0; i < m_size; ) {
|
||||
if (likely(i + 1 < m_size)) {
|
||||
uint32_t a = data[i];
|
||||
uint32_t b = data[i + 1];
|
||||
uint32_t schema;
|
||||
uint32_t encode;
|
||||
|
||||
if (std::max(a, b) < (1u << 16)) {
|
||||
schema = 0x2;
|
||||
encode = a | (b << 16);
|
||||
} else if (a < (1u << 20) && b < (1u << 12)) {
|
||||
schema = 0x1;
|
||||
encode = a | (b << 20);
|
||||
} else if (a < (1u << 12) && b < (1u << 20)) {
|
||||
schema = 0x3;
|
||||
encode = a | (b << 12);
|
||||
} else {
|
||||
schema = 0x0;
|
||||
encode = a;
|
||||
}
|
||||
|
||||
block[blockOffset] = encode;
|
||||
blockMask |= schema << (blockOffset << 1);
|
||||
blockOffset += 1;
|
||||
|
||||
i += schema ? 2 : 1;
|
||||
} else {
|
||||
block[blockOffset] = data[i++];
|
||||
blockOffset += 1;
|
||||
}
|
||||
|
||||
if (unlikely(blockOffset == 16) || unlikely(i == m_size)) {
|
||||
m_code.insert(m_code.end(), blockMask);
|
||||
m_code.insert(m_code.end(), block.begin(), block.begin() + blockOffset);
|
||||
|
||||
blockMask = 0;
|
||||
blockOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Only shrink the array if we have lots of overhead for some reason.
|
||||
// This should only happen on shaders where our initial estimate was
|
||||
// too small. In general, we want to avoid reallocation here.
|
||||
if (m_code.capacity() > (m_code.size() * 10) / 9)
|
||||
m_code.shrink_to_fit();
|
||||
m_code.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,37 +34,26 @@ namespace dxvk {
|
|||
|
||||
|
||||
SpirvCodeBuffer SpirvCompressedBuffer::decompress() const {
|
||||
SpirvCodeBuffer code(m_size);
|
||||
uint32_t* data = code.data();
|
||||
SpirvCodeBuffer code(m_dwords);
|
||||
if (m_dwords == 0)
|
||||
return code;
|
||||
|
||||
uint32_t srcOffset = 0;
|
||||
uint32_t dstOffset = 0;
|
||||
|
||||
constexpr uint32_t shiftAmounts = 0x0c101420;
|
||||
|
||||
while (dstOffset < m_size) {
|
||||
uint32_t blockMask = m_code[srcOffset];
|
||||
|
||||
for (uint32_t i = 0; i < 16 && dstOffset < m_size; i++) {
|
||||
// Use 64-bit integers for some of the operands so we can
|
||||
// shift by 32 bits and not handle it as a special cases
|
||||
uint32_t schema = (blockMask >> (i << 1)) & 0x3;
|
||||
uint32_t shift = (shiftAmounts >> (schema << 3)) & 0xff;
|
||||
uint64_t mask = ~(~0ull << shift);
|
||||
uint64_t encode = m_code[srcOffset + i + 1];
|
||||
|
||||
data[dstOffset] = encode & mask;
|
||||
|
||||
if (likely(schema))
|
||||
data[dstOffset + 1] = encode >> shift;
|
||||
|
||||
dstOffset += schema ? 2 : 1;
|
||||
}
|
||||
|
||||
srcOffset += 17;
|
||||
}
|
||||
uint32_t *dst = code.data();
|
||||
const uint8_t *src = m_code.data();
|
||||
const uint8_t *end = src + m_code.size();
|
||||
do {
|
||||
*dst = *src++;
|
||||
if (src == end || !(*src & 0x80)) continue;
|
||||
*dst |= (*src++ & 0x7f) << 7;
|
||||
if (src == end || !(*src & 0x80)) continue;
|
||||
*dst |= (*src++ & 0x7f) << 14;
|
||||
if (src == end || !(*src & 0x80)) continue;
|
||||
*dst |= (*src++ & 0x7f) << 21;
|
||||
if (src == end || !(*src & 0x80)) continue;
|
||||
*dst |= (*src++ & 0x7f) << 28;
|
||||
} while (++dst, src != end);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "spirv_code_buffer.h"
|
||||
|
||||
#include "../util/util_raw_vector.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
|
@ -12,27 +14,115 @@ namespace dxvk {
|
|||
* Implements a fast in-memory compression
|
||||
* to keep memory footprint low.
|
||||
*/
|
||||
class SpirvCompressedBuffer {
|
||||
class SpirvCompressedBuffer : public SpirvWriter<SpirvCompressedBuffer> {
|
||||
static constexpr size_t not_inserting = ~(size_t)0;
|
||||
|
||||
public:
|
||||
|
||||
SpirvCompressedBuffer();
|
||||
|
||||
SpirvCompressedBuffer(SpirvCodeBuffer& code);
|
||||
SpirvCompressedBuffer(const SpirvCodeBuffer& code);
|
||||
|
||||
SpirvCompressedBuffer(SpirvCompressedBuffer&& other) = default;
|
||||
|
||||
~SpirvCompressedBuffer();
|
||||
|
||||
void shrink() { m_code.shrink_to_fit(); }
|
||||
|
||||
/**
|
||||
* \brief Code size, in dwords
|
||||
* \returns Code size, in dwords
|
||||
*/
|
||||
uint32_t dwords() const { return m_dwords; }
|
||||
|
||||
/**
|
||||
* \brief Code size, in bytes
|
||||
* \returns Code size, in bytes
|
||||
*/
|
||||
size_t size() const { return m_code.size() + m_insert.size(); }
|
||||
|
||||
/**
|
||||
* \brief Appends an 32-bit word to the buffer
|
||||
* \param [in] word The word to append
|
||||
*/
|
||||
void putWord(uint32_t word) {
|
||||
size_t size = m_code.size();
|
||||
m_code.resize(size + 5);
|
||||
|
||||
uint8_t *dst = m_code.data() + size;
|
||||
*dst++ = word & 0x7f;
|
||||
while (word >>= 7) *dst++ = (word & 0x7f) | 0x80;
|
||||
m_code.resize(dst - m_code.data());
|
||||
|
||||
m_dwords += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Merges two code buffers
|
||||
*
|
||||
* This is useful to generate declarations or
|
||||
* the SPIR-V header at the same time as the
|
||||
* code when doing so in advance is impossible.
|
||||
* \param [in] other Code buffer to append
|
||||
*/
|
||||
void append(const SpirvCompressedBuffer& other) {
|
||||
m_code.insert(m_code.end(),
|
||||
other.m_code.begin(),
|
||||
other.m_code.end());
|
||||
m_dwords += other.dwords();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Retrieves current insertion pointer
|
||||
*
|
||||
* Sometimes it may be necessay to insert code into the
|
||||
* middle of the stream rather than appending it. This
|
||||
* retrieves the current function pointer. Note that the
|
||||
* pointer will become invalid if any code is inserted
|
||||
* before the current pointer location.
|
||||
* \returns Current instruction pointr
|
||||
*/
|
||||
size_t getInsertionPtr() const {
|
||||
return m_code.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets insertion pointer to a specific value
|
||||
*
|
||||
* Sets the insertion pointer to a value that was
|
||||
* previously retrieved by \ref getInsertionPtr.
|
||||
* \returns Current instruction pointr
|
||||
*/
|
||||
void beginInsertion(size_t ptr) {
|
||||
std::swap(m_code, m_insert);
|
||||
m_ptr = ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets insertion pointer to the end
|
||||
*
|
||||
* After this call, new instructions will be
|
||||
* appended to the stream. In other words,
|
||||
* this will restore default behaviour.
|
||||
*/
|
||||
void endInsertion() {
|
||||
std::swap(m_code, m_insert);
|
||||
m_code.insert(m_code.begin() + m_ptr,
|
||||
m_insert.begin(),
|
||||
m_insert.end());
|
||||
m_insert.clear();
|
||||
m_ptr = not_inserting;
|
||||
}
|
||||
|
||||
SpirvCodeBuffer decompress() const;
|
||||
|
||||
private:
|
||||
|
||||
size_t m_size;
|
||||
std::vector<uint32_t> m_code;
|
||||
|
||||
void encodeDword(uint32_t dw);
|
||||
|
||||
uint32_t decodeDword(size_t& offset) const;
|
||||
uint32_t m_dwords;
|
||||
raw_vector<uint8_t> m_code;
|
||||
raw_vector<uint8_t> m_insert;
|
||||
size_t m_ptr = not_inserting;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ namespace dxvk {
|
|||
}
|
||||
|
||||
|
||||
SpirvCodeBuffer SpirvModule::compile() const {
|
||||
SpirvCodeBuffer result;
|
||||
SpirvCompressedBuffer SpirvModule::compile() const {
|
||||
SpirvCompressedBuffer result;
|
||||
result.putHeader(m_version, m_id);
|
||||
result.append(m_capabilities);
|
||||
result.append(m_extensions);
|
||||
|
@ -29,6 +29,7 @@ namespace dxvk {
|
|||
result.append(m_typeConstDefs);
|
||||
result.append(m_variables);
|
||||
result.append(m_code);
|
||||
result.shrink();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -40,12 +41,7 @@ namespace dxvk {
|
|||
|
||||
bool SpirvModule::hasCapability(
|
||||
spv::Capability capability) {
|
||||
for (auto ins : m_capabilities) {
|
||||
if (ins.opCode() == spv::OpCapability && ins.arg(1) == capability)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return m_enabledCaps.find(capability) != m_enabledCaps.end();
|
||||
}
|
||||
|
||||
void SpirvModule::enableCapability(
|
||||
|
@ -55,6 +51,20 @@ namespace dxvk {
|
|||
if (!hasCapability(capability)) {
|
||||
m_capabilities.putIns (spv::OpCapability, 2);
|
||||
m_capabilities.putWord(capability);
|
||||
m_enabledCaps.insert(capability);
|
||||
|
||||
if (capability == spv::CapabilitySampleRateShading)
|
||||
m_shaderFlags.set(DxvkShaderFlag::HasSampleRateShading);
|
||||
|
||||
if (capability == spv::CapabilityShaderViewportIndex
|
||||
|| capability == spv::CapabilityShaderLayer)
|
||||
m_shaderFlags.set(DxvkShaderFlag::ExportsViewportIndexLayerFromVertexStage);
|
||||
|
||||
if (capability == spv::CapabilitySparseResidency)
|
||||
m_shaderFlags.set(DxvkShaderFlag::UsesSparseResidency);
|
||||
|
||||
if (capability == spv::CapabilityFragmentFullyCoveredEXT)
|
||||
m_shaderFlags.set(DxvkShaderFlag::UsesFragmentCoverage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +105,13 @@ namespace dxvk {
|
|||
m_execModeInfo.putIns (spv::OpExecutionMode, 3);
|
||||
m_execModeInfo.putWord(entryPointId);
|
||||
m_execModeInfo.putWord(executionMode);
|
||||
m_enabledModes.insert(executionMode);
|
||||
|
||||
if (executionMode == spv::ExecutionModeStencilRefReplacingEXT)
|
||||
m_shaderFlags.set(DxvkShaderFlag::ExportsStencilRef);
|
||||
|
||||
if (executionMode == spv::ExecutionModeXfb)
|
||||
m_shaderFlags.set(DxvkShaderFlag::HasTransformFeedback);
|
||||
}
|
||||
|
||||
|
||||
|
@ -109,6 +126,12 @@ namespace dxvk {
|
|||
|
||||
for (uint32_t i = 0; i < argCount; i++)
|
||||
m_execModeInfo.putWord(args[i]);
|
||||
|
||||
if (executionMode == spv::ExecutionModeStencilRefReplacingEXT)
|
||||
m_shaderFlags.set(DxvkShaderFlag::ExportsStencilRef);
|
||||
|
||||
if (executionMode == spv::ExecutionModeXfb)
|
||||
m_shaderFlags.set(DxvkShaderFlag::HasTransformFeedback);
|
||||
}
|
||||
|
||||
|
||||
|
@ -197,89 +220,123 @@ namespace dxvk {
|
|||
|
||||
uint32_t SpirvModule::constBool(
|
||||
bool v) {
|
||||
return this->defConst(v
|
||||
? spv::OpConstantTrue
|
||||
: spv::OpConstantFalse,
|
||||
this->defBoolType(),
|
||||
0, nullptr);
|
||||
return this->defConstCached(m_constBool[v ? 1 : 0],
|
||||
v ? spv::OpConstantTrue : spv::OpConstantFalse,
|
||||
this->defBoolType(), 0, nullptr);
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::consti32(
|
||||
int32_t v) {
|
||||
auto it = m_constSInt32.find(v);
|
||||
if (it != m_constSInt32.end()) return it->second;
|
||||
|
||||
std::array<uint32_t, 1> data;
|
||||
std::memcpy(data.data(), &v, sizeof(v));
|
||||
|
||||
return this->defConst(
|
||||
auto id = this->defConstUnique(
|
||||
spv::OpConstant,
|
||||
this->defIntType(32, 1),
|
||||
data.size(),
|
||||
data.data());
|
||||
|
||||
m_constSInt32[v] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::consti64(
|
||||
int64_t v) {
|
||||
auto it = m_constSInt64.find(v);
|
||||
if (it != m_constSInt64.end()) return it->second;
|
||||
|
||||
std::array<uint32_t, 2> data;
|
||||
std::memcpy(data.data(), &v, sizeof(v));
|
||||
|
||||
return this->defConst(
|
||||
auto id = this->defConstUnique(
|
||||
spv::OpConstant,
|
||||
this->defIntType(64, 1),
|
||||
data.size(),
|
||||
data.data());
|
||||
|
||||
m_constSInt64[v] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::constu32(
|
||||
uint32_t v) {
|
||||
auto it = m_constUInt32.find(v);
|
||||
if (it != m_constUInt32.end()) return it->second;
|
||||
|
||||
std::array<uint32_t, 1> data;
|
||||
std::memcpy(data.data(), &v, sizeof(v));
|
||||
|
||||
return this->defConst(
|
||||
auto id = this->defConstUnique(
|
||||
spv::OpConstant,
|
||||
this->defIntType(32, 0),
|
||||
data.size(),
|
||||
data.data());
|
||||
|
||||
m_constUInt32[v] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::constu64(
|
||||
uint64_t v) {
|
||||
auto it = m_constUInt64.find(v);
|
||||
if (it != m_constUInt64.end()) return it->second;
|
||||
|
||||
std::array<uint32_t, 2> data;
|
||||
std::memcpy(data.data(), &v, sizeof(v));
|
||||
|
||||
return this->defConst(
|
||||
auto id = this->defConstUnique(
|
||||
spv::OpConstant,
|
||||
this->defIntType(64, 0),
|
||||
data.size(),
|
||||
data.data());
|
||||
|
||||
m_constUInt64[v] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::constf32(
|
||||
float v) {
|
||||
auto it = m_constFloat32.find(v);
|
||||
if (it != m_constFloat32.end()) return it->second;
|
||||
|
||||
std::array<uint32_t, 1> data;
|
||||
std::memcpy(data.data(), &v, sizeof(v));
|
||||
|
||||
return this->defConst(
|
||||
auto id = this->defConstUnique(
|
||||
spv::OpConstant,
|
||||
this->defFloatType(32),
|
||||
data.size(),
|
||||
data.data());
|
||||
|
||||
m_constFloat32[v] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::constf64(
|
||||
double v) {
|
||||
auto it = m_constFloat64.find(v);
|
||||
if (it != m_constFloat64.end()) return it->second;
|
||||
|
||||
std::array<uint32_t, 2> data;
|
||||
std::memcpy(data.data(), &v, sizeof(v));
|
||||
|
||||
return this->defConst(
|
||||
auto id = this->defConstUnique(
|
||||
spv::OpConstant,
|
||||
this->defFloatType(64),
|
||||
data.size(),
|
||||
data.data());
|
||||
|
||||
m_constFloat64[v] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
@ -465,15 +522,20 @@ namespace dxvk {
|
|||
|
||||
uint32_t SpirvModule::constUndef(
|
||||
uint32_t typeId) {
|
||||
return this->defConst(spv::OpUndef,
|
||||
auto it = m_constUndef.find(typeId);
|
||||
if (it != m_constUndef.end()) return it->second;
|
||||
|
||||
auto id = this->defConstUnique(spv::OpUndef,
|
||||
typeId, 0, nullptr);
|
||||
m_constUndef[typeId] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::lateConst32(
|
||||
uint32_t typeId) {
|
||||
uint32_t resultId = this->allocateId();
|
||||
m_lateConsts.insert(resultId);
|
||||
m_lateConsts[resultId] = m_typeConstDefs.getInsertionPtr();
|
||||
|
||||
m_typeConstDefs.putIns (spv::OpConstant, 4);
|
||||
m_typeConstDefs.putWord(typeId);
|
||||
|
@ -486,19 +548,12 @@ namespace dxvk {
|
|||
void SpirvModule::setLateConst(
|
||||
uint32_t constId,
|
||||
const uint32_t* argIds) {
|
||||
for (auto ins : m_typeConstDefs) {
|
||||
if (ins.opCode() != spv::OpConstant
|
||||
&& ins.opCode() != spv::OpConstantComposite)
|
||||
continue;
|
||||
|
||||
if (ins.arg(2) != constId)
|
||||
continue;
|
||||
auto ins = SpirvInstruction(m_typeConstDefs.data(),
|
||||
m_lateConsts[constId],
|
||||
m_typeConstDefs.dwords());
|
||||
|
||||
for (uint32_t i = 3; i < ins.length(); i++)
|
||||
ins.setArg(i, argIds[i - 3]);
|
||||
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 3; i < ins.length(); i++)
|
||||
ins.setArg(i, argIds[i - 3]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -574,6 +629,9 @@ namespace dxvk {
|
|||
m_annotations.putWord (object);
|
||||
m_annotations.putWord (spv::DecorationBuiltIn);
|
||||
m_annotations.putWord (builtIn);
|
||||
|
||||
if (builtIn == spv::BuiltInPosition)
|
||||
m_shaderFlags.set(DxvkShaderFlag::ExportsPosition);
|
||||
}
|
||||
|
||||
|
||||
|
@ -624,6 +682,8 @@ namespace dxvk {
|
|||
m_annotations.putWord (object);
|
||||
m_annotations.putWord (spv::DecorationSpecId);
|
||||
m_annotations.putInt32(specId);
|
||||
if (specId <= MaxNumSpecConstants)
|
||||
m_specConstantMask |= 1u << specId;
|
||||
}
|
||||
|
||||
|
||||
|
@ -664,6 +724,9 @@ namespace dxvk {
|
|||
m_annotations.putWord (memberId);
|
||||
m_annotations.putWord (spv::DecorationBuiltIn);
|
||||
m_annotations.putWord (builtIn);
|
||||
|
||||
if (builtIn == spv::BuiltInPosition)
|
||||
m_shaderFlags.set(DxvkShaderFlag::ExportsPosition);
|
||||
}
|
||||
|
||||
|
||||
|
@ -703,12 +766,14 @@ namespace dxvk {
|
|||
|
||||
|
||||
uint32_t SpirvModule::defVoidType() {
|
||||
return this->defType(spv::OpTypeVoid, 0, nullptr);
|
||||
return this->defTypeCached(m_typeVoid,
|
||||
spv::OpTypeVoid, 0, nullptr);
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::defBoolType() {
|
||||
return this->defType(spv::OpTypeBool, 0, nullptr);
|
||||
return this->defTypeCached(m_typeBool[0],
|
||||
spv::OpTypeBool, 0, nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
@ -716,6 +781,32 @@ namespace dxvk {
|
|||
uint32_t width,
|
||||
uint32_t isSigned) {
|
||||
std::array<uint32_t, 2> args = {{ width, isSigned }};
|
||||
switch ((int64_t)width * (isSigned ? -1 : 1)) {
|
||||
case -64:
|
||||
return this->defTypeCached(m_typeSInt64[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
case -32:
|
||||
return this->defTypeCached(m_typeSInt32[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
case -16:
|
||||
return this->defTypeCached(m_typeSInt16[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
case -8:
|
||||
return this->defTypeCached(m_typeSInt8[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
case +8:
|
||||
return this->defTypeCached(m_typeUInt8[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
case +16:
|
||||
return this->defTypeCached(m_typeUInt16[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
case +32:
|
||||
return this->defTypeCached(m_typeUInt32[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
case +64:
|
||||
return this->defTypeCached(m_typeUInt64[0],
|
||||
spv::OpTypeInt, args.size(), args.data());
|
||||
}
|
||||
return this->defType(spv::OpTypeInt,
|
||||
args.size(), args.data());
|
||||
}
|
||||
|
@ -724,6 +815,17 @@ namespace dxvk {
|
|||
uint32_t SpirvModule::defFloatType(
|
||||
uint32_t width) {
|
||||
std::array<uint32_t, 1> args = {{ width }};
|
||||
switch (width) {
|
||||
case 16:
|
||||
return this->defTypeCached(m_typeFloat16[0],
|
||||
spv::OpTypeFloat, args.size(), args.data());
|
||||
case 32:
|
||||
return this->defTypeCached(m_typeFloat32[0],
|
||||
spv::OpTypeFloat, args.size(), args.data());
|
||||
case 64:
|
||||
return this->defTypeCached(m_typeFloat64[0],
|
||||
spv::OpTypeFloat, args.size(), args.data());
|
||||
}
|
||||
return this->defType(spv::OpTypeFloat,
|
||||
args.size(), args.data());
|
||||
}
|
||||
|
@ -734,6 +836,43 @@ namespace dxvk {
|
|||
uint32_t elementCount) {
|
||||
std::array<uint32_t, 2> args =
|
||||
{{ elementType, elementCount }};
|
||||
|
||||
if (elementType == m_typeBool[0])
|
||||
return this->defTypeCached(m_typeBool[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeSInt8[0])
|
||||
return this->defTypeCached(m_typeSInt8[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeSInt16[0])
|
||||
return this->defTypeCached(m_typeSInt16[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeSInt32[0])
|
||||
return this->defTypeCached(m_typeSInt32[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeSInt64[0])
|
||||
return this->defTypeCached(m_typeSInt64[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeUInt8[0])
|
||||
return this->defTypeCached(m_typeUInt8[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeUInt16[0])
|
||||
return this->defTypeCached(m_typeUInt16[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeUInt32[0])
|
||||
return this->defTypeCached(m_typeUInt32[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeUInt64[0])
|
||||
return this->defTypeCached(m_typeUInt64[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeFloat16[0])
|
||||
return this->defTypeCached(m_typeFloat16[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeFloat32[0])
|
||||
return this->defTypeCached(m_typeFloat32[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
else if (elementType == m_typeFloat64[0])
|
||||
return this->defTypeCached(m_typeFloat64[elementCount - 1],
|
||||
spv::OpTypeVector, args.size(), args.data());
|
||||
|
||||
return this->defType(spv::OpTypeVector,
|
||||
args.size(), args.data());
|
||||
|
@ -764,13 +903,11 @@ namespace dxvk {
|
|||
uint32_t SpirvModule::defArrayTypeUnique(
|
||||
uint32_t typeId,
|
||||
uint32_t length) {
|
||||
uint32_t resultId = this->allocateId();
|
||||
std::array<uint32_t, 2> args = {{ typeId, length }};
|
||||
|
||||
m_typeConstDefs.putIns (spv::OpTypeArray, 4);
|
||||
m_typeConstDefs.putWord(resultId);
|
||||
m_typeConstDefs.putWord(typeId);
|
||||
m_typeConstDefs.putWord(length);
|
||||
return resultId;
|
||||
m_typeLocs.push_back(m_typeConstDefs.getInsertionPtr());
|
||||
return this->defTypeUnique(spv::OpTypeArray,
|
||||
args.size(), args.data());
|
||||
}
|
||||
|
||||
|
||||
|
@ -785,12 +922,11 @@ namespace dxvk {
|
|||
|
||||
uint32_t SpirvModule::defRuntimeArrayTypeUnique(
|
||||
uint32_t typeId) {
|
||||
uint32_t resultId = this->allocateId();
|
||||
std::array<uint32_t, 1> args = { typeId };
|
||||
|
||||
m_typeConstDefs.putIns (spv::OpTypeRuntimeArray, 3);
|
||||
m_typeConstDefs.putWord(resultId);
|
||||
m_typeConstDefs.putWord(typeId);
|
||||
return resultId;
|
||||
m_typeLocs.push_back(m_typeConstDefs.getInsertionPtr());
|
||||
return this->defTypeUnique(spv::OpTypeRuntimeArray,
|
||||
args.size(), args.data());
|
||||
}
|
||||
|
||||
|
||||
|
@ -820,14 +956,9 @@ namespace dxvk {
|
|||
uint32_t SpirvModule::defStructTypeUnique(
|
||||
uint32_t memberCount,
|
||||
const uint32_t* memberTypes) {
|
||||
uint32_t resultId = this->allocateId();
|
||||
|
||||
m_typeConstDefs.putIns (spv::OpTypeStruct, 2 + memberCount);
|
||||
m_typeConstDefs.putWord(resultId);
|
||||
|
||||
for (uint32_t i = 0; i < memberCount; i++)
|
||||
m_typeConstDefs.putWord(memberTypes[i]);
|
||||
return resultId;
|
||||
m_typeLocs.push_back(m_typeConstDefs.getInsertionPtr());
|
||||
return this->defTypeUnique(spv::OpTypeStruct,
|
||||
memberCount, memberTypes);
|
||||
}
|
||||
|
||||
|
||||
|
@ -845,7 +976,8 @@ namespace dxvk {
|
|||
|
||||
|
||||
uint32_t SpirvModule::defSamplerType() {
|
||||
return this->defType(spv::OpTypeSampler, 0, nullptr);
|
||||
return this->defTypeCached(m_typeSampler,
|
||||
spv::OpTypeSampler, 0, nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3691,6 +3823,32 @@ namespace dxvk {
|
|||
m_code.putWord(streamId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::defTypeCached(
|
||||
std::optional<uint32_t>&cache,
|
||||
spv::Op op,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds) {
|
||||
if (!cache)
|
||||
cache = this->defTypeUnique(op, argCount,
|
||||
argIds);
|
||||
return *cache;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::defTypeUnique(
|
||||
spv::Op op,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds) {
|
||||
uint32_t resultId = this->allocateId();
|
||||
m_typeConstDefs.putIns (op, 2 + argCount);
|
||||
m_typeConstDefs.putWord(resultId);
|
||||
|
||||
for (uint32_t i = 0; i < argCount; i++)
|
||||
m_typeConstDefs.putWord(argIds[i]);
|
||||
return resultId;
|
||||
}
|
||||
|
||||
|
||||
void SpirvModule::opBeginInvocationInterlock() {
|
||||
|
@ -3710,52 +3868,38 @@ namespace dxvk {
|
|||
// Since the type info is stored in the code buffer,
|
||||
// we can use the code buffer to look up type IDs as
|
||||
// well. Result IDs are always stored as argument 1.
|
||||
for (auto ins : m_typeConstDefs) {
|
||||
bool match = ins.opCode() == op
|
||||
&& ins.length() == 2 + argCount;
|
||||
|
||||
for (uint32_t i = 0; i < argCount && match; i++)
|
||||
match &= ins.arg(2 + i) == argIds[i];
|
||||
|
||||
if (match)
|
||||
return ins.arg(1);
|
||||
const uint32_t *data = m_typeConstDefs.data();
|
||||
for (auto i : m_typeLocs) {
|
||||
if ((data[i] & spv::OpCodeMask) == op &&
|
||||
(data[i] >> spv::WordCountShift) == (2 + argCount) &&
|
||||
(!argCount || !std::memcmp(data + i + 2, argIds, argCount * sizeof(uint32_t))))
|
||||
return data[i + 1];
|
||||
}
|
||||
|
||||
// Type not yet declared, create a new one.
|
||||
uint32_t resultId = this->allocateId();
|
||||
m_typeConstDefs.putIns (op, 2 + argCount);
|
||||
m_typeConstDefs.putWord(resultId);
|
||||
|
||||
for (uint32_t i = 0; i < argCount; i++)
|
||||
m_typeConstDefs.putWord(argIds[i]);
|
||||
return resultId;
|
||||
m_typeLocs.push_back(m_typeConstDefs.getInsertionPtr());
|
||||
return this->defTypeUnique(op, argCount, argIds);
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::defConst(
|
||||
uint32_t SpirvModule::defConstCached(
|
||||
std::optional<uint32_t>&cache,
|
||||
spv::Op op,
|
||||
uint32_t typeId,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds) {
|
||||
// Avoid declaring constants multiple times
|
||||
for (auto ins : m_typeConstDefs) {
|
||||
bool match = ins.opCode() == op
|
||||
&& ins.length() == 3 + argCount
|
||||
&& ins.arg(1) == typeId;
|
||||
|
||||
for (uint32_t i = 0; i < argCount && match; i++)
|
||||
match &= ins.arg(3 + i) == argIds[i];
|
||||
|
||||
if (!match)
|
||||
continue;
|
||||
|
||||
uint32_t id = ins.arg(2);
|
||||
if (!cache)
|
||||
cache = this->defConstUnique(op, typeId,
|
||||
argCount, argIds);
|
||||
return *cache;
|
||||
}
|
||||
|
||||
if (m_lateConsts.find(id) == m_lateConsts.end())
|
||||
return id;
|
||||
}
|
||||
|
||||
// Constant not yet declared, make a new one
|
||||
|
||||
uint32_t SpirvModule::defConstUnique(
|
||||
spv::Op op,
|
||||
uint32_t typeId,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds) {
|
||||
uint32_t resultId = this->allocateId();
|
||||
m_typeConstDefs.putIns (op, 3 + argCount);
|
||||
m_typeConstDefs.putWord(typeId);
|
||||
|
@ -3765,6 +3909,28 @@ namespace dxvk {
|
|||
m_typeConstDefs.putWord(argIds[i]);
|
||||
return resultId;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SpirvModule::defConst(
|
||||
spv::Op op,
|
||||
uint32_t typeId,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds) {
|
||||
// Avoid declaring constants multiple times
|
||||
const uint32_t *data = m_typeConstDefs.data();
|
||||
for (auto i : m_constLocs) {
|
||||
if ((data[i] & spv::OpCodeMask) == op &&
|
||||
(data[i] >> spv::WordCountShift) == (3 + argCount) &&
|
||||
data[i + 1] == typeId &&
|
||||
(!argCount || !std::memcmp(data + i + 3, argIds, argCount * sizeof(uint32_t))) &&
|
||||
m_lateConsts.find(data[i + 2]) == m_lateConsts.end())
|
||||
return data[i + 2];
|
||||
}
|
||||
|
||||
// Constant not yet declared, make a new one
|
||||
m_constLocs.push_back(m_typeConstDefs.getInsertionPtr());
|
||||
return this->defConstUnique(op, typeId, argCount, argIds);
|
||||
}
|
||||
|
||||
|
||||
void SpirvModule::instImportGlsl450() {
|
||||
|
|
|
@ -3,9 +3,30 @@
|
|||
#include <unordered_set>
|
||||
|
||||
#include "spirv_code_buffer.h"
|
||||
#include "spirv_compression.h"
|
||||
|
||||
#include "../dxvk/dxvk_limits.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief Shader flags
|
||||
*
|
||||
* Provides extra information about the features
|
||||
* used by a shader.
|
||||
*/
|
||||
enum DxvkShaderFlag : uint64_t {
|
||||
HasSampleRateShading,
|
||||
HasTransformFeedback,
|
||||
ExportsPosition,
|
||||
ExportsStencilRef,
|
||||
ExportsViewportIndexLayerFromVertexStage,
|
||||
UsesFragmentCoverage,
|
||||
UsesSparseResidency,
|
||||
};
|
||||
|
||||
using DxvkShaderFlags = Flags<DxvkShaderFlag>;
|
||||
|
||||
struct SpirvPhiLabel {
|
||||
uint32_t varId = 0;
|
||||
uint32_t labelId = 0;
|
||||
|
@ -59,7 +80,7 @@ namespace dxvk {
|
|||
|
||||
~SpirvModule();
|
||||
|
||||
SpirvCodeBuffer compile() const;
|
||||
SpirvCompressedBuffer compile() const;
|
||||
|
||||
size_t getInsertionPtr() {
|
||||
return m_code.getInsertionPtr();
|
||||
|
@ -1264,6 +1285,14 @@ namespace dxvk {
|
|||
|
||||
void opEndInvocationInterlock();
|
||||
|
||||
DxvkShaderFlags getShaderFlags() const {
|
||||
return m_shaderFlags;
|
||||
}
|
||||
|
||||
uint32_t getSpecConstantMask() const {
|
||||
return m_specConstantMask;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_version;
|
||||
|
@ -1271,27 +1300,84 @@ namespace dxvk {
|
|||
uint32_t m_instExtGlsl450 = 0;
|
||||
uint32_t m_blockId = 0;
|
||||
|
||||
SpirvCodeBuffer m_capabilities;
|
||||
SpirvCodeBuffer m_extensions;
|
||||
SpirvCodeBuffer m_instExt;
|
||||
SpirvCodeBuffer m_memoryModel;
|
||||
SpirvCodeBuffer m_entryPoints;
|
||||
SpirvCodeBuffer m_execModeInfo;
|
||||
SpirvCodeBuffer m_debugNames;
|
||||
SpirvCodeBuffer m_annotations;
|
||||
SpirvCodeBuffer m_typeConstDefs;
|
||||
SpirvCodeBuffer m_variables;
|
||||
SpirvCodeBuffer m_code;
|
||||
DxvkShaderFlags m_shaderFlags;
|
||||
uint32_t m_specConstantMask = 0;
|
||||
|
||||
std::unordered_set<uint32_t> m_lateConsts;
|
||||
SpirvCompressedBuffer m_capabilities;
|
||||
SpirvCompressedBuffer m_extensions;
|
||||
SpirvCompressedBuffer m_instExt;
|
||||
SpirvCompressedBuffer m_memoryModel;
|
||||
SpirvCompressedBuffer m_entryPoints;
|
||||
SpirvCompressedBuffer m_execModeInfo;
|
||||
SpirvCompressedBuffer m_debugNames;
|
||||
SpirvCompressedBuffer m_annotations;
|
||||
SpirvCodeBuffer m_typeConstDefs;
|
||||
SpirvCompressedBuffer m_variables;
|
||||
SpirvCompressedBuffer m_code;
|
||||
|
||||
std::unordered_set<spv::Capability> m_enabledCaps;
|
||||
std::unordered_set<spv::ExecutionMode> m_enabledModes;
|
||||
|
||||
std::vector<size_t> m_typeLocs;
|
||||
std::vector<size_t> m_constLocs;
|
||||
|
||||
std::optional<uint32_t> m_typeVoid;
|
||||
std::optional<uint32_t> m_typeSampler;
|
||||
std::optional<uint32_t> m_typeBool[4];
|
||||
std::optional<uint32_t> m_typeSInt8[4];
|
||||
std::optional<uint32_t> m_typeSInt16[4];
|
||||
std::optional<uint32_t> m_typeSInt32[4];
|
||||
std::optional<uint32_t> m_typeSInt64[4];
|
||||
std::optional<uint32_t> m_typeUInt8[4];
|
||||
std::optional<uint32_t> m_typeUInt16[4];
|
||||
std::optional<uint32_t> m_typeUInt32[4];
|
||||
std::optional<uint32_t> m_typeUInt64[4];
|
||||
std::optional<uint32_t> m_typeFloat16[4];
|
||||
std::optional<uint32_t> m_typeFloat32[4];
|
||||
std::optional<uint32_t> m_typeFloat64[4];
|
||||
|
||||
std::optional<uint32_t> m_constBool[2];
|
||||
std::unordered_map<uint32_t, uint32_t> m_constUndef;
|
||||
std::unordered_map<int32_t, uint32_t> m_constSInt32;
|
||||
std::unordered_map<int64_t, uint32_t> m_constSInt64;
|
||||
std::unordered_map<uint32_t, uint32_t> m_constUInt32;
|
||||
std::unordered_map<uint64_t, uint32_t> m_constUInt64;
|
||||
std::unordered_map<float, uint32_t> m_constFloat32;
|
||||
std::unordered_map<double, uint32_t> m_constFloat64;
|
||||
|
||||
std::unordered_map<uint32_t, size_t> m_lateConsts;
|
||||
|
||||
std::vector<uint32_t> m_interfaceVars;
|
||||
|
||||
uint32_t defTypeCached(
|
||||
std::optional<uint32_t>&cache,
|
||||
spv::Op op,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds);
|
||||
|
||||
uint32_t defTypeUnique(
|
||||
spv::Op op,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds);
|
||||
|
||||
uint32_t defType(
|
||||
spv::Op op,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds);
|
||||
|
||||
uint32_t defConstCached(
|
||||
std::optional<uint32_t>&cache,
|
||||
spv::Op op,
|
||||
uint32_t typeId,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds);
|
||||
|
||||
uint32_t defConstUnique(
|
||||
spv::Op op,
|
||||
uint32_t typeId,
|
||||
uint32_t argCount,
|
||||
const uint32_t* argIds);
|
||||
|
||||
uint32_t defConst(
|
||||
spv::Op op,
|
||||
uint32_t typeId,
|
||||
|
@ -1317,4 +1403,4 @@ namespace dxvk {
|
|||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
#pragma once
|
||||
|
||||
#include "spirv_instruction.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief SPIR-V code buffer
|
||||
*
|
||||
* Helper class for generating SPIR-V shaders.
|
||||
* Stores arbitrary SPIR-V instructions in a
|
||||
* format that can be read by Vulkan drivers.
|
||||
*/
|
||||
template<class SpirvBuffer>
|
||||
class SpirvWriter {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Appends an 32-bit word to the buffer
|
||||
* \param [in] word The word to append
|
||||
*/
|
||||
void putWord(uint32_t word) {
|
||||
static_cast<SpirvBuffer*>(this)->putWord(word);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Appends an instruction word to the buffer
|
||||
*
|
||||
* Adds a single word containing both the word count
|
||||
* and the op code number for a single instruction.
|
||||
* \param [in] opCode Operand code
|
||||
* \param [in] wordCount Number of words
|
||||
*/
|
||||
void putIns(spv::Op opCode, uint16_t wordCount) {
|
||||
this->putWord(
|
||||
(static_cast<uint32_t>(opCode) << 0)
|
||||
| (static_cast<uint32_t>(wordCount) << 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Appends a 32-bit integer to the buffer
|
||||
* \param [in] value The number to add
|
||||
*/
|
||||
void putInt32(uint32_t word) {
|
||||
this->putWord(word);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Appends a 64-bit integer to the buffer
|
||||
*
|
||||
* A 64-bit integer will take up two 32-bit words.
|
||||
* \param [in] value 64-bit value to add
|
||||
*/
|
||||
void putInt64(uint64_t value) {
|
||||
this->putWord(value >> 0);
|
||||
this->putWord(value >> 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Appends a 32-bit float to the buffer
|
||||
* \param [in] value The number to add
|
||||
*/
|
||||
void putFloat32(float value) {
|
||||
this->putInt32(bit::cast<uint32_t>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Appends a 64-bit float to the buffer
|
||||
* \param [in] value The number to add
|
||||
*/
|
||||
void putFloat64(double value) {
|
||||
this->putInt64(bit::cast<uint64_t>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Appends a literal string to the buffer
|
||||
* \param [in] str String to append to the buffer
|
||||
*/
|
||||
void putStr(const char* str) {
|
||||
uint32_t word = 0;
|
||||
uint32_t nbit = 0;
|
||||
|
||||
for (uint32_t i = 0; str[i] != '\0'; str++) {
|
||||
word |= (static_cast<uint32_t>(str[i]) & 0xFF) << nbit;
|
||||
|
||||
if ((nbit += 8) == 32) {
|
||||
this->putWord(word);
|
||||
word = 0;
|
||||
nbit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Commit current word
|
||||
this->putWord(word);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds the header to the buffer
|
||||
*
|
||||
* \param [in] version SPIR-V version
|
||||
* \param [in] boundIds Number of bound IDs
|
||||
*/
|
||||
void putHeader(uint32_t version, uint32_t boundIds) {
|
||||
this->putWord(spv::MagicNumber);
|
||||
this->putWord(version);
|
||||
this->putWord(0); // Generator
|
||||
this->putWord(boundIds);
|
||||
this->putWord(0); // Schema
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Computes length of a literal string
|
||||
*
|
||||
* \param [in] str The string to check
|
||||
* \returns Number of words consumed by a string
|
||||
*/
|
||||
uint32_t strLen(const char* str) {
|
||||
// Null-termination plus padding
|
||||
return (std::strlen(str) + 4) / 4;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief Uninitialized vector
|
||||
*
|
||||
* Implements a vector with uinitialized storage to avoid
|
||||
* std::vector default initialization.
|
||||
*/
|
||||
template<typename T>
|
||||
class raw_vector {
|
||||
struct deleter {
|
||||
void operator()(void* p) const { std::free(p); }
|
||||
};
|
||||
using pointer_type = std::unique_ptr<T, deleter>;
|
||||
|
||||
public:
|
||||
|
||||
void reserve(size_t n) {
|
||||
n = pick_capacity(n);
|
||||
if (n > m_capacity)
|
||||
reallocate(n);
|
||||
}
|
||||
|
||||
void shrink_to_fit() {
|
||||
size_t n = pick_capacity(m_size);
|
||||
reallocate(n);
|
||||
}
|
||||
|
||||
void resize(size_t n) {
|
||||
if (n >= m_size) {
|
||||
reserve(n);
|
||||
std::uninitialized_default_construct(ptr(m_size), ptr(n));
|
||||
}
|
||||
m_size = n;
|
||||
}
|
||||
|
||||
void push_back(const T& object) {
|
||||
reserve(m_size + 1);
|
||||
*ptr(m_size++) = object;
|
||||
}
|
||||
|
||||
void push_back(T&& object) {
|
||||
reserve(m_size + 1);
|
||||
*ptr(m_size++) = std::move(object);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void emplace_back(Args... args) {
|
||||
reserve(m_size + 1);
|
||||
*ptr(m_size++) = T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void erase(size_t idx) {
|
||||
if (idx < m_size)
|
||||
std::memmove(ptr(idx), ptr(idx + 1), (m_size - idx) * sizeof(T));
|
||||
m_size -= 1;
|
||||
}
|
||||
|
||||
void insert(const T* pos, const T* begin, const T* end) {
|
||||
if (begin == end)
|
||||
return;
|
||||
|
||||
size_t off = pos - ptr(0);
|
||||
size_t size = m_size;
|
||||
size_t count = (end - begin);
|
||||
resize(size + count);
|
||||
|
||||
if (off < size)
|
||||
std::memmove(ptr(off) + count, ptr(off),
|
||||
(size - off) * sizeof(T));
|
||||
std::memcpy(ptr(off), begin, count * sizeof(T));
|
||||
}
|
||||
|
||||
void pop_back() { m_size--; }
|
||||
|
||||
void clear() { m_size = 0; }
|
||||
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
const T* data() const { return ptr(0); }
|
||||
T* data() { return ptr(0); }
|
||||
|
||||
const T* begin() const { return ptr(0); }
|
||||
T* begin() { return ptr(0); }
|
||||
|
||||
const T* end() const { return ptr(m_size); }
|
||||
T* end() { return ptr(m_size); }
|
||||
|
||||
T& operator [] (size_t idx) { return *ptr(idx); }
|
||||
const T& operator [] (size_t idx) const { return *ptr(idx); }
|
||||
|
||||
T& front() { return *ptr(0); }
|
||||
const T& front() const { return *ptr(0); }
|
||||
|
||||
T& back() { return *ptr(m_size - 1); }
|
||||
const T& back() const { return *ptr(m_size - 1); }
|
||||
|
||||
private:
|
||||
|
||||
pointer_type m_ptr = nullptr;
|
||||
size_t m_size = 0;
|
||||
size_t m_capacity = 0;
|
||||
|
||||
size_t pick_capacity(size_t n) {
|
||||
size_t capacity = m_capacity;
|
||||
if (capacity < 128)
|
||||
capacity = 128;
|
||||
|
||||
while (capacity < n)
|
||||
capacity *= 2;
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
void reallocate(size_t n) {
|
||||
void* ptr = std::realloc(m_ptr.get(), n * sizeof(T));
|
||||
m_ptr.release();
|
||||
m_ptr.reset(static_cast<T*>(ptr));
|
||||
m_capacity = n;
|
||||
}
|
||||
|
||||
T* ptr(size_t idx) {
|
||||
return reinterpret_cast<T*>(m_ptr.get()) + idx;
|
||||
}
|
||||
|
||||
const T* ptr(size_t idx) const {
|
||||
return reinterpret_cast<const T*>(m_ptr.get()) + idx;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue