Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/compiler/spirv/nir_load_libclc.c
4545 views
1
/*
2
* Copyright © 2020 Intel Corporation
3
*
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
* copy of this software and associated documentation files (the "Software"),
6
* to deal in the Software without restriction, including without limitation
7
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
* and/or sell copies of the Software, and to permit persons to whom the
9
* Software is furnished to do so, subject to the following conditions:
10
*
11
* The above copyright notice and this permission notice (including the next
12
* paragraph) shall be included in all copies or substantial portions of the
13
* Software.
14
*
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
* IN THE SOFTWARE.
22
*/
23
24
#include "nir.h"
25
#include "nir_serialize.h"
26
#include "nir_spirv.h"
27
#include "util/mesa-sha1.h"
28
29
#ifdef DYNAMIC_LIBCLC_PATH
30
#include <fcntl.h>
31
#include <sys/types.h>
32
#include <sys/stat.h>
33
#include <sys/mman.h>
34
#include <unistd.h>
35
#endif
36
37
#ifdef HAVE_STATIC_LIBCLC_ZSTD
38
#include <zstd.h>
39
#endif
40
41
#ifdef HAVE_STATIC_LIBCLC_SPIRV
42
#include "spirv-mesa3d-.spv.h"
43
#endif
44
45
#ifdef HAVE_STATIC_LIBCLC_SPIRV64
46
#include "spirv64-mesa3d-.spv.h"
47
#endif
48
49
struct clc_file {
50
unsigned bit_size;
51
const char *static_data;
52
size_t static_data_size;
53
const char *sys_path;
54
};
55
56
static const struct clc_file libclc_files[] = {
57
{
58
.bit_size = 32,
59
#ifdef HAVE_STATIC_LIBCLC_SPIRV
60
.static_data = libclc_spirv_mesa3d_spv,
61
.static_data_size = sizeof(libclc_spirv_mesa3d_spv),
62
#endif
63
#ifdef DYNAMIC_LIBCLC_PATH
64
.sys_path = DYNAMIC_LIBCLC_PATH "spirv-mesa3d-.spv",
65
#endif
66
},
67
{
68
.bit_size = 64,
69
#ifdef HAVE_STATIC_LIBCLC_SPIRV64
70
.static_data = libclc_spirv64_mesa3d_spv,
71
.static_data_size = sizeof(libclc_spirv64_mesa3d_spv),
72
#endif
73
#ifdef DYNAMIC_LIBCLC_PATH
74
.sys_path = DYNAMIC_LIBCLC_PATH "spirv64-mesa3d-.spv",
75
#endif
76
},
77
};
78
79
static const struct clc_file *
80
get_libclc_file(unsigned ptr_bit_size)
81
{
82
assert(ptr_bit_size == 32 || ptr_bit_size == 64);
83
return &libclc_files[ptr_bit_size / 64];
84
}
85
86
struct clc_data {
87
const struct clc_file *file;
88
89
unsigned char cache_key[20];
90
91
int fd;
92
const void *data;
93
size_t size;
94
};
95
96
static bool
97
open_clc_data(struct clc_data *clc, unsigned ptr_bit_size)
98
{
99
memset(clc, 0, sizeof(*clc));
100
clc->file = get_libclc_file(ptr_bit_size);
101
clc->fd = -1;
102
103
if (clc->file->static_data) {
104
snprintf((char *)clc->cache_key, sizeof(clc->cache_key),
105
"libclc-spirv%d", ptr_bit_size);
106
return true;
107
}
108
109
#ifdef DYNAMIC_LIBCLC_PATH
110
if (clc->file->sys_path != NULL) {
111
int fd = open(clc->file->sys_path, O_RDONLY);
112
if (fd < 0)
113
return false;
114
115
struct stat stat;
116
int ret = fstat(fd, &stat);
117
if (ret < 0) {
118
fprintf(stderr, "fstat failed on %s: %m\n", clc->file->sys_path);
119
close(fd);
120
return false;
121
}
122
123
struct mesa_sha1 ctx;
124
_mesa_sha1_init(&ctx);
125
_mesa_sha1_update(&ctx, clc->file->sys_path, strlen(clc->file->sys_path));
126
_mesa_sha1_update(&ctx, &stat.st_mtim, sizeof(stat.st_mtim));
127
_mesa_sha1_final(&ctx, clc->cache_key);
128
129
clc->fd = fd;
130
131
return true;
132
}
133
#endif
134
135
return false;
136
}
137
138
#define SPIRV_WORD_SIZE 4
139
140
static bool
141
map_clc_data(struct clc_data *clc)
142
{
143
if (clc->file->static_data) {
144
#ifdef HAVE_STATIC_LIBCLC_ZSTD
145
unsigned long long cmp_size =
146
ZSTD_getFrameContentSize(clc->file->static_data,
147
clc->file->static_data_size);
148
if (cmp_size == ZSTD_CONTENTSIZE_UNKNOWN ||
149
cmp_size == ZSTD_CONTENTSIZE_ERROR) {
150
fprintf(stderr, "Could not determine the decompressed size of the "
151
"libclc SPIR-V\n");
152
return false;
153
}
154
155
size_t frame_size =
156
ZSTD_findFrameCompressedSize(clc->file->static_data,
157
clc->file->static_data_size);
158
if (ZSTD_isError(frame_size)) {
159
fprintf(stderr, "Could not determine the size of the first ZSTD frame "
160
"when decompressing libclc SPIR-V: %s\n",
161
ZSTD_getErrorName(frame_size));
162
return false;
163
}
164
165
void *dest = malloc(cmp_size + 1);
166
size_t size = ZSTD_decompress(dest, cmp_size, clc->file->static_data,
167
frame_size);
168
if (ZSTD_isError(size)) {
169
free(dest);
170
fprintf(stderr, "Error decompressing libclc SPIR-V: %s\n",
171
ZSTD_getErrorName(size));
172
return false;
173
}
174
175
clc->data = dest;
176
clc->size = size;
177
#else
178
clc->data = clc->file->static_data;
179
clc->size = clc->file->static_data_size;
180
#endif
181
return true;
182
}
183
184
#ifdef DYNAMIC_LIBCLC_PATH
185
if (clc->file->sys_path != NULL) {
186
off_t len = lseek(clc->fd, 0, SEEK_END);
187
if (len % SPIRV_WORD_SIZE != 0) {
188
fprintf(stderr, "File length isn't a multiple of the word size\n");
189
return false;
190
}
191
clc->size = len;
192
193
clc->data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, clc->fd, 0);
194
if (clc->data == MAP_FAILED) {
195
fprintf(stderr, "Failed to mmap libclc SPIR-V: %m\n");
196
return false;
197
}
198
199
return true;
200
}
201
#endif
202
203
return true;
204
}
205
206
static void
207
close_clc_data(struct clc_data *clc)
208
{
209
if (clc->file->static_data) {
210
#ifdef HAVE_STATIC_LIBCLC_ZSTD
211
free((void *)clc->data);
212
#endif
213
return;
214
}
215
216
#ifdef DYNAMIC_LIBCLC_PATH
217
if (clc->file->sys_path != NULL) {
218
if (clc->data)
219
munmap((void *)clc->data, clc->size);
220
close(clc->fd);
221
}
222
#endif
223
}
224
225
/** Returns true if libclc is found
226
*
227
* If libclc is compiled in statically, this always returns true. If we
228
* depend on a dynamic libclc, this opens and tries to stat the file.
229
*/
230
bool
231
nir_can_find_libclc(unsigned ptr_bit_size)
232
{
233
struct clc_data clc;
234
if (open_clc_data(&clc, ptr_bit_size)) {
235
close_clc_data(&clc);
236
return true;
237
} else {
238
return false;
239
}
240
}
241
242
nir_shader *
243
nir_load_libclc_shader(unsigned ptr_bit_size,
244
struct disk_cache *disk_cache,
245
const struct spirv_to_nir_options *spirv_options,
246
const nir_shader_compiler_options *nir_options)
247
{
248
assert(ptr_bit_size ==
249
nir_address_format_bit_size(spirv_options->global_addr_format));
250
251
struct clc_data clc;
252
if (!open_clc_data(&clc, ptr_bit_size))
253
return NULL;
254
255
#ifdef ENABLE_SHADER_CACHE
256
cache_key cache_key;
257
if (disk_cache) {
258
disk_cache_compute_key(disk_cache, clc.cache_key,
259
sizeof(clc.cache_key), cache_key);
260
261
size_t buffer_size;
262
uint8_t *buffer = disk_cache_get(disk_cache, cache_key, &buffer_size);
263
if (buffer) {
264
struct blob_reader blob;
265
blob_reader_init(&blob, buffer, buffer_size);
266
nir_shader *nir = nir_deserialize(NULL, nir_options, &blob);
267
free(buffer);
268
close_clc_data(&clc);
269
return nir;
270
}
271
}
272
#endif
273
274
if (!map_clc_data(&clc)) {
275
close_clc_data(&clc);
276
return NULL;
277
}
278
279
struct spirv_to_nir_options spirv_lib_options = *spirv_options;
280
spirv_lib_options.create_library = true;
281
282
assert(clc.size % SPIRV_WORD_SIZE == 0);
283
nir_shader *nir = spirv_to_nir(clc.data, clc.size / SPIRV_WORD_SIZE,
284
NULL, 0, MESA_SHADER_KERNEL, NULL,
285
&spirv_lib_options, nir_options);
286
nir_validate_shader(nir, "after nir_load_clc_shader");
287
288
/* nir_inline_libclc will assume that the functions in this shader are
289
* already ready to lower. This means we need to inline any function_temp
290
* initializers and lower any early returns.
291
*/
292
nir->info.internal = true;
293
NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
294
NIR_PASS_V(nir, nir_lower_returns);
295
296
/* TODO: One day, we may want to run some optimizations on the libclc
297
* shader once and cache them to save time in each shader call.
298
*/
299
300
#ifdef ENABLE_SHADER_CACHE
301
if (disk_cache) {
302
struct blob blob;
303
blob_init(&blob);
304
nir_serialize(&blob, nir, false);
305
disk_cache_put(disk_cache, cache_key, blob.data, blob.size, NULL);
306
}
307
#endif
308
309
close_clc_data(&clc);
310
return nir;
311
}
312
313