mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-19 20:32:15 +02:00
libobs-d3d11: Add checksum to shader cache
A few reports came in of cache files with the correct size but full of null bytes, presumably from a system crash and an SSD lying about buffer flushes. This commit adds a checksum at the end of the compiled bytecode so we don't try to use invalid data. Co-Authored-By: derrod <dennis@obsproject.com>
This commit is contained in:
parent
9f66c23825
commit
1641812580
@ -206,13 +206,13 @@ void gs_shader::BuildConstantBuffer()
|
||||
gs_shader_set_default(¶ms[i]);
|
||||
}
|
||||
|
||||
static uint64_t fnv1a_hash(const char *str)
|
||||
static uint64_t fnv1a_hash(const char *str, size_t len)
|
||||
{
|
||||
const uint64_t FNV_OFFSET = 14695981039346656037ULL;
|
||||
const uint64_t FNV_PRIME = 1099511628211ULL;
|
||||
uint64_t hash = FNV_OFFSET;
|
||||
while (*str) {
|
||||
hash ^= (uint64_t)*str++;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
hash ^= (uint64_t)str[i];
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
return hash;
|
||||
@ -224,31 +224,64 @@ void gs_shader::Compile(const char *shaderString, const char *file,
|
||||
ComPtr<ID3D10Blob> errorsBlob;
|
||||
HRESULT hr;
|
||||
|
||||
bool is_cached = false;
|
||||
char hashstr[20];
|
||||
|
||||
if (!shaderString)
|
||||
throw "No shader string specified";
|
||||
|
||||
uint64_t hash = fnv1a_hash(shaderString);
|
||||
size_t shaderStrLen = strlen(shaderString);
|
||||
uint64_t hash = fnv1a_hash(shaderString, shaderStrLen);
|
||||
snprintf(hashstr, sizeof(hashstr), "%02llx", hash);
|
||||
|
||||
BPtr program_data =
|
||||
os_get_program_data_path_ptr("obs-studio/shader-cache");
|
||||
auto cachePath = filesystem::u8path(program_data.Get()) / hashstr;
|
||||
// Increment if on-disk format changes
|
||||
cachePath += ".v2";
|
||||
|
||||
std::fstream cacheFile;
|
||||
cacheFile.exceptions(fstream::badbit | fstream::eofbit);
|
||||
|
||||
if (filesystem::exists(cachePath) && !filesystem::is_empty(cachePath))
|
||||
cacheFile.open(cachePath, ios::in | ios::binary | ios::ate);
|
||||
|
||||
if (cacheFile.is_open()) {
|
||||
streampos len = cacheFile.tellg();
|
||||
cacheFile.seekg(0, ios::beg);
|
||||
uint64_t checksum;
|
||||
|
||||
D3DCreateBlob(len, shader);
|
||||
cacheFile.read((char *)(*shader)->GetBufferPointer(), len);
|
||||
} else {
|
||||
hr = D3DCompile(shaderString, strlen(shaderString), file, NULL,
|
||||
NULL, "main", target,
|
||||
try {
|
||||
streampos len = cacheFile.tellg();
|
||||
// Not enough data for checksum + shader
|
||||
if (len <= sizeof(checksum))
|
||||
throw length_error("File truncated");
|
||||
|
||||
cacheFile.seekg(0, ios::beg);
|
||||
|
||||
len -= sizeof(checksum);
|
||||
D3DCreateBlob(len, shader);
|
||||
cacheFile.read((char *)(*shader)->GetBufferPointer(),
|
||||
len);
|
||||
uint64_t calculated_checksum = fnv1a_hash(
|
||||
(char *)(*shader)->GetBufferPointer(), len);
|
||||
|
||||
cacheFile.read((char *)&checksum, sizeof(checksum));
|
||||
if (calculated_checksum != checksum)
|
||||
throw exception("Checksum mismatch");
|
||||
|
||||
is_cached = true;
|
||||
} catch (const exception &e) {
|
||||
// Something went wrong reading the cache file, delete it
|
||||
blog(LOG_WARNING,
|
||||
"Loading shader cache file failed with \"%s\": %s",
|
||||
e.what(), file);
|
||||
cacheFile.close();
|
||||
filesystem::remove(cachePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_cached) {
|
||||
hr = D3DCompile(shaderString, shaderStrLen, file, NULL, NULL,
|
||||
"main", target,
|
||||
D3D10_SHADER_OPTIMIZATION_LEVEL3, 0, shader,
|
||||
errorsBlob.Assign());
|
||||
if (FAILED(hr)) {
|
||||
@ -260,8 +293,23 @@ void gs_shader::Compile(const char *shaderString, const char *file,
|
||||
|
||||
cacheFile.open(cachePath, ios::out | ios::binary);
|
||||
if (cacheFile.is_open()) {
|
||||
cacheFile.write((char *)(*shader)->GetBufferPointer(),
|
||||
try {
|
||||
uint64_t calculated_checksum = fnv1a_hash(
|
||||
(char *)(*shader)->GetBufferPointer(),
|
||||
(*shader)->GetBufferSize());
|
||||
|
||||
cacheFile.write(
|
||||
(char *)(*shader)->GetBufferPointer(),
|
||||
(*shader)->GetBufferSize());
|
||||
cacheFile.write((char *)&calculated_checksum,
|
||||
sizeof(calculated_checksum));
|
||||
} catch (const exception &e) {
|
||||
blog(LOG_WARNING,
|
||||
"Writing shader cache file failed with \"%s\": %s",
|
||||
e.what(), file);
|
||||
cacheFile.close();
|
||||
filesystem::remove(cachePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user