ACMX 2.139.0
Dual-Backend Real-Time GPU Video Synthesis
Loading...
Searching...
No Matches
program.cpp
Go to the documentation of this file.
1#include <glad/glad.h>
2#include <glm/glm.hpp>
3#include <glm/gtc/type_ptr.hpp>
4
5#include "program.hpp"
6#include <cstdint>
7#include <cstdio>
8#include <cstring>
9#include <filesystem>
10#include <fstream>
11#include <memory>
12#include <string>
13#include <vector>
14
15namespace {
16 static uint64_t fnv1a64_bytes(const void *data, size_t n) {
17 const uint8_t *p = static_cast<const uint8_t *>(data);
18 uint64_t h = 1469598103934665603ull;
19 for (size_t i = 0; i < n; ++i) {
20 h ^= p[i];
21 h *= 1099511628211ull;
22 }
23 return h;
24 }
25
26 static uint64_t fnv1a64_str(const std::string &s) { return fnv1a64_bytes(s.data(), s.size()); }
27
28 static uint64_t fnv1a64_file(const std::string &filepath) {
29 std::ifstream f(filepath, std::ios::binary);
30 if (!f)
31 return 0;
32
33 uint64_t h = 1469598103934665603ull;
34 char buf[16384];
35
36 while (f.read(buf, sizeof(buf)) || f.gcount()) {
37 std::streamsize n = f.gcount();
38 for (std::streamsize i = 0; i < n; ++i) {
39 h ^= static_cast<uint8_t>(buf[i]);
40 h *= 1099511628211ull;
41 }
42 }
43 return h;
44 }
45
46 static uint64_t mix64(uint64_t a, uint64_t b) {
47 uint64_t x = a ^ (b + 0x9e3779b97f4a7c15ull + (a << 6) + (a >> 2));
48 x ^= (x >> 33);
49 x *= 0xff51afd7ed558ccdull;
50 x ^= (x >> 33);
51 x *= 0xc4ceb9fe1a85ec53ull;
52 x ^= (x >> 33);
53 return x;
54 }
55
56 static std::string glStr(GLenum e) {
57 const GLubyte *p = glGetString(e);
58 if (!p)
59 return {};
60 return std::string(reinterpret_cast<const char *>(p));
61 }
62
63 struct CacheHeader {
64 uint32_t magic;
65 uint16_t version;
66 uint16_t reserved;
67 uint64_t key;
68 uint32_t binaryFormat;
69 uint32_t binaryLength;
70 };
71
72 static constexpr uint32_t kMagic = 0x53434143u;
73 static constexpr uint16_t kVersion = 1;
74
75 static bool readWholeFile(const std::string &path, std::vector<uint8_t> &out) {
76 std::ifstream f(path, std::ios::binary);
77 if (!f)
78 return false;
79 f.seekg(0, std::ios::end);
80 std::streamoff sz = f.tellg();
81 if (sz <= 0)
82 return false;
83 f.seekg(0, std::ios::beg);
84 out.resize(static_cast<size_t>(sz));
85 f.read(reinterpret_cast<char *>(out.data()), static_cast<std::streamsize>(out.size()));
86 return f.good();
87 }
88
89 static bool writeFileAtomic(const std::string &path, const void *data, size_t size) {
90 std::filesystem::path p(path);
91 std::filesystem::create_directories(p.parent_path());
92
93 std::string tmp = path + ".tmp";
94 {
95 std::ofstream out(tmp, std::ios::binary | std::ios::trunc);
96 if (!out)
97 return false;
98 out.write(static_cast<const char *>(data), static_cast<std::streamsize>(size));
99 if (!out.good())
100 return false;
101 }
102
103 std::error_code ec;
104 std::filesystem::rename(tmp, path, ec);
105 if (ec) {
106 std::filesystem::remove(path, ec);
107 ec.clear();
108 std::filesystem::rename(tmp, path, ec);
109 if (ec) {
110 std::filesystem::remove(tmp, ec);
111 return false;
112 }
113 }
114 return true;
115 }
116
117 static std::string cacheDirDefault() { return "shader_cache"; }
118
119 static std::string cacheFilePath(uint64_t key) {
120 char name[64];
121 std::snprintf(name, sizeof(name), "%016llx.bin", static_cast<unsigned long long>(key));
122 std::filesystem::path p(cacheDirDefault());
123 p /= name;
124 return p.string();
125 }
126
127 static uint64_t computeProgramKeyFromFiles(const std::string &vsPath, const std::string &fsPath) {
128 uint64_t hv = fnv1a64_file(vsPath);
129 uint64_t hf = fnv1a64_file(fsPath);
130
131 uint64_t h = mix64(hv, hf);
132
133 std::string vendor = glStr(GL_VENDOR);
134 std::string renderer = glStr(GL_RENDERER);
135 std::string version = glStr(GL_VERSION);
136
137 h = mix64(h, fnv1a64_str(vendor));
138 h = mix64(h, fnv1a64_str(renderer));
139 h = mix64(h, fnv1a64_str(version));
140 h = mix64(h, fnv1a64_str("ShaderProgramCache_v1"));
141
142 return h;
143 }
144
145 static uint64_t computeProgramKeyFromText(const std::string &vs, const std::string &fs) {
146 uint64_t hv = fnv1a64_str(vs);
147 uint64_t hf = fnv1a64_str(fs);
148
149 uint64_t h = mix64(hv, hf);
150
151 std::string vendor = glStr(GL_VENDOR);
152 std::string renderer = glStr(GL_RENDERER);
153 std::string version = glStr(GL_VERSION);
154
155 h = mix64(h, fnv1a64_str(vendor));
156 h = mix64(h, fnv1a64_str(renderer));
157 h = mix64(h, fnv1a64_str(version));
158 h = mix64(h, fnv1a64_str("ShaderProgramCache_v1"));
159
160 return h;
161 }
162
163 static uint64_t computeComputeProgramKeyFromText(const std::string &source) {
164 uint64_t hash = fnv1a64_str(source);
165 hash = mix64(hash, fnv1a64_str(glStr(GL_VENDOR)));
166 hash = mix64(hash, fnv1a64_str(glStr(GL_RENDERER)));
167 hash = mix64(hash, fnv1a64_str(glStr(GL_VERSION)));
168 hash = mix64(hash, fnv1a64_str("ComputeShaderProgramCache_v1"));
169 return hash;
170 }
171
172 static bool tryLoadProgramBinary(uint64_t key, GLuint &outProg) {
173 std::vector<uint8_t> data;
174 std::string path = cacheFilePath(key);
175 if (!readWholeFile(path, data))
176 return false;
177 if (data.size() < sizeof(CacheHeader))
178 return false;
179
180 CacheHeader hdr{};
181 std::memcpy(&hdr, data.data(), sizeof(CacheHeader));
182 if (hdr.magic != kMagic)
183 return false;
184 if (hdr.version != kVersion)
185 return false;
186 if (hdr.key != key)
187 return false;
188 if (hdr.binaryLength == 0)
189 return false;
190
191 size_t need = sizeof(CacheHeader) + static_cast<size_t>(hdr.binaryLength);
192 if (data.size() != need)
193 return false;
194
195 GLuint prog = glCreateProgram();
196 glProgramBinary(prog, static_cast<GLenum>(hdr.binaryFormat), data.data() + sizeof(CacheHeader), static_cast<GLsizei>(hdr.binaryLength));
197
198 GLint ok = 0;
199 glGetProgramiv(prog, GL_LINK_STATUS, &ok);
200 if (!ok) {
201 glDeleteProgram(prog);
202 return false;
203 }
204
205 outProg = prog;
206 return true;
207 }
208
209 static bool saveProgramBinary(uint64_t key, GLuint prog) {
210 GLint binLen = 0;
211 glGetProgramiv(prog, GL_PROGRAM_BINARY_LENGTH, &binLen);
212 if (binLen <= 0)
213 return false;
214
215 std::vector<uint8_t> blob;
216 blob.resize(sizeof(CacheHeader) + static_cast<size_t>(binLen));
217
218 CacheHeader hdr{};
219 hdr.magic = kMagic;
220 hdr.version = kVersion;
221 hdr.key = key;
222
223 GLenum fmt = 0;
224 GLsizei got = 0;
225 glGetProgramBinary(prog, static_cast<GLsizei>(binLen), &got, &fmt, blob.data() + sizeof(CacheHeader));
226 if (got <= 0)
227 return false;
228
229 hdr.binaryFormat = static_cast<uint32_t>(fmt);
230 hdr.binaryLength = static_cast<uint32_t>(got);
231
232 std::memcpy(blob.data(), &hdr, sizeof(CacheHeader));
233
234 return writeFileAtomic(cacheFilePath(key), blob.data(), blob.size());
235 }
236
237} // namespace
238
239namespace ac {
240
241 bool loadComputeProgramBinaryFromCache(const std::string &source, GLuint &program) { return tryLoadProgramBinary(computeComputeProgramKeyFromText(source), program); }
242
243 bool saveComputeProgramBinaryToCache(const std::string &source, GLuint program) { return saveProgramBinary(computeComputeProgramKeyFromText(source), program); }
244
245 bool ShaderProgram::loadProgram(const std::string &v, const std::string &f) {
246 uint64_t key = computeProgramKeyFromFiles(v, f);
247 GLuint cached = 0;
248 if (tryLoadProgramBinary(key, cached)) {
249 static_cast<gl::ShaderProgram &>(*this) = gl::ShaderProgram(cached);
250 return true;
251 }
252 if (!gl::ShaderProgram::loadProgram(v, f))
253 return false;
254 saveProgramBinary(key, id());
255 return true;
256 }
257
258 bool ShaderProgram::loadProgramFromText(const std::string &v, const std::string &f) {
259 uint64_t key = computeProgramKeyFromText(v, f);
260 GLuint cached = 0;
261 if (tryLoadProgramBinary(key, cached)) {
262 static_cast<gl::ShaderProgram &>(*this) = gl::ShaderProgram(cached);
263 return true;
264 }
265 if (!gl::ShaderProgram::loadProgramFromText(v, f))
266 return false;
267 saveProgramBinary(key, id());
268 return true;
269 }
270} // namespace ac
bool loadProgram(const std::string &v, const std::string &f)
Definition program.cpp:245
bool loadProgramFromText(const std::string &v, const std::string &f)
Definition program.cpp:258
bool loadComputeProgramBinaryFromCache(const std::string &source, GLuint &program)
Restore a compute-only program binary keyed by source and GL driver.
Definition program.cpp:241
bool saveComputeProgramBinaryToCache(const std::string &source, GLuint program)
Save a linked compute-only program to its stage-specific cache key.
Definition program.cpp:243
static uint64_t computeProgramKeyFromFiles(const std::string &vsPath, const std::string &fsPath)
Definition program.cpp:127
static std::string cacheFilePath(uint64_t key)
Definition program.cpp:119
static uint64_t fnv1a64_file(const std::string &filepath)
Definition program.cpp:28
static constexpr uint16_t kVersion
Definition program.cpp:73
static std::string cacheDirDefault()
Definition program.cpp:117
static uint64_t fnv1a64_str(const std::string &s)
Definition program.cpp:26
static constexpr uint32_t kMagic
Definition program.cpp:72
static std::string glStr(GLenum e)
Definition program.cpp:56
static bool writeFileAtomic(const std::string &path, const void *data, size_t size)
Definition program.cpp:89
static bool saveProgramBinary(uint64_t key, GLuint prog)
Definition program.cpp:209
static uint64_t fnv1a64_bytes(const void *data, size_t n)
Definition program.cpp:16
static bool tryLoadProgramBinary(uint64_t key, GLuint &outProg)
Definition program.cpp:172
static uint64_t computeProgramKeyFromText(const std::string &vs, const std::string &fs)
Definition program.cpp:145
static bool readWholeFile(const std::string &path, std::vector< uint8_t > &out)
Definition program.cpp:75
static uint64_t computeComputeProgramKeyFromText(const std::string &source)
Definition program.cpp:163
static uint64_t mix64(uint64_t a, uint64_t b)
Definition program.cpp:46