mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-19 05:52:11 +01:00
[spirv] Implement in-memory compression for shader modules
This commit is contained in:
parent
d2395180af
commit
f32200b668
@ -1,5 +1,6 @@
|
||||
spirv_src = files([
|
||||
'spirv_code_buffer.cpp',
|
||||
'spirv_compression.cpp',
|
||||
'spirv_module.cpp',
|
||||
])
|
||||
|
||||
|
@ -9,6 +9,12 @@ namespace dxvk {
|
||||
SpirvCodeBuffer::~SpirvCodeBuffer() { }
|
||||
|
||||
|
||||
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size)
|
||||
: m_ptr(size) {
|
||||
m_code.resize(size);
|
||||
}
|
||||
|
||||
|
||||
SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size, const uint32_t* data)
|
||||
: m_ptr(size) {
|
||||
m_code.resize(size);
|
||||
|
@ -21,6 +21,7 @@ namespace dxvk {
|
||||
public:
|
||||
|
||||
SpirvCodeBuffer();
|
||||
explicit SpirvCodeBuffer(uint32_t size);
|
||||
SpirvCodeBuffer(uint32_t size, const uint32_t* data);
|
||||
SpirvCodeBuffer(std::istream& stream);
|
||||
|
||||
@ -37,6 +38,14 @@ namespace dxvk {
|
||||
const uint32_t* data() const { return m_code.data(); }
|
||||
uint32_t* data() { return m_code.data(); }
|
||||
|
||||
/**
|
||||
* \brief Code size, in dwords
|
||||
* \returns Code size, in dwords
|
||||
*/
|
||||
uint32_t dwords() const {
|
||||
return m_code.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Code size, in bytes
|
||||
* \returns Code size, in bytes
|
||||
|
110
src/spirv/spirv_compression.cpp
Normal file
110
src/spirv/spirv_compression.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include "spirv_compression.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
SpirvCompressedBuffer::SpirvCompressedBuffer()
|
||||
: m_size(0) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
SpirvCompressedBuffer::SpirvCompressedBuffer(
|
||||
const SpirvCodeBuffer& code)
|
||||
: m_size(code.dwords()) {
|
||||
const uint32_t* data = code.data();
|
||||
|
||||
// The compression works by eliminating leading null bytes
|
||||
// from DWORDs, exploiting that SPIR-V IDs are consecutive
|
||||
// integers that usually fall into the 16-bit range. For
|
||||
// each DWORD, a two-bit integer is stored which indicates
|
||||
// the number of bytes it takes in the compressed buffer.
|
||||
// This way, it can achieve a compression ratio of ~50%.
|
||||
m_mask.reserve((m_size + NumMaskWords - 1) / NumMaskWords);
|
||||
m_code.reserve(m_size * 4);
|
||||
|
||||
uint64_t dstWord = 0;
|
||||
uint32_t dstShift = 0;
|
||||
|
||||
for (uint32_t i = 0; i < m_size; i += NumMaskWords) {
|
||||
uint64_t byteCounts = 0;
|
||||
|
||||
for (uint32_t w = 0; w < NumMaskWords && i + w < m_size; w++) {
|
||||
uint64_t word = data[i + w];
|
||||
uint64_t bytes = 0;
|
||||
|
||||
if (word < (1 << 8)) bytes = 0;
|
||||
else if (word < (1 << 16)) bytes = 1;
|
||||
else if (word < (1 << 24)) bytes = 2;
|
||||
else bytes = 3;
|
||||
|
||||
byteCounts |= bytes << (2 * w);
|
||||
|
||||
uint32_t bits = 8 * bytes + 8;
|
||||
uint32_t rem = bit::pack(dstWord, dstShift, word, bits);
|
||||
|
||||
if (unlikely(rem != 0)) {
|
||||
m_code.push_back(dstWord);
|
||||
|
||||
dstWord = 0;
|
||||
dstShift = 0;
|
||||
|
||||
bit::pack(dstWord, dstShift, word >> (bits - rem), rem);
|
||||
}
|
||||
}
|
||||
|
||||
m_mask.push_back(byteCounts);
|
||||
}
|
||||
|
||||
if (dstShift)
|
||||
m_code.push_back(dstWord);
|
||||
|
||||
m_mask.shrink_to_fit();
|
||||
m_code.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
SpirvCompressedBuffer::~SpirvCompressedBuffer() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
SpirvCodeBuffer SpirvCompressedBuffer::decompress() const {
|
||||
SpirvCodeBuffer code(m_size);
|
||||
uint32_t* data = code.data();
|
||||
|
||||
if (m_size == 0)
|
||||
return code;
|
||||
|
||||
uint32_t maskIdx = 0;
|
||||
uint32_t codeIdx = 0;
|
||||
|
||||
uint64_t srcWord = m_code[codeIdx++];
|
||||
uint32_t srcShift = 0;
|
||||
|
||||
for (uint32_t i = 0; i < m_size; i += NumMaskWords) {
|
||||
uint64_t srcMask = m_mask[maskIdx++];
|
||||
|
||||
for (uint32_t w = 0; w < NumMaskWords && i + w < m_size; w++) {
|
||||
uint32_t bits = 8 * ((srcMask & 3) + 1);
|
||||
|
||||
uint64_t word = 0;
|
||||
uint32_t rem = bit::unpack(word, srcWord, srcShift, bits);
|
||||
|
||||
if (unlikely(rem != 0)) {
|
||||
srcWord = m_code[codeIdx++];
|
||||
srcShift = 0;
|
||||
|
||||
uint64_t tmp = 0;
|
||||
bit::unpack(tmp, srcWord, srcShift, rem);
|
||||
word |= tmp << (bits - rem);
|
||||
}
|
||||
|
||||
data[i + w] = word;
|
||||
srcMask >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
36
src/spirv/spirv_compression.h
Normal file
36
src/spirv/spirv_compression.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "spirv_code_buffer.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief Compressed SPIR-V code buffer
|
||||
*
|
||||
* Implements a fast in-memory compression
|
||||
* to keep memory footprint low.
|
||||
*/
|
||||
class SpirvCompressedBuffer {
|
||||
constexpr static uint32_t NumMaskWords = 32;
|
||||
public:
|
||||
|
||||
SpirvCompressedBuffer();
|
||||
|
||||
SpirvCompressedBuffer(
|
||||
const SpirvCodeBuffer& code);
|
||||
|
||||
~SpirvCompressedBuffer();
|
||||
|
||||
SpirvCodeBuffer decompress() const;
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_size;
|
||||
std::vector<uint64_t> m_mask;
|
||||
std::vector<uint64_t> m_code;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "../util/util_error.h"
|
||||
#include "../util/util_flags.h"
|
||||
#include "../util/util_likely.h"
|
||||
#include "../util/util_string.h"
|
||||
|
||||
#include "../util/rc/util_rc.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user