2 * Mesa 3-D graphics library
4 * Copyright (c) 2017 Intel Corporation
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
26 * \file program_binary.c
28 * Helper functions for serializing a binary program.
32 #include "compiler/blob.h"
33 #include "compiler/glsl/serialize.h"
34 #include "main/errors.h"
35 #include "main/mtypes.h"
36 #include "main/shaderapi.h"
37 #include "util/bitscan.h"
38 #include "util/crc32.h"
39 #include "program_binary.h"
40 #include "program/prog_parameter.h"
43 * Mesa supports one binary format, but it must differentiate between formats
44 * produced by different drivers and different Mesa versions.
46 * Mesa uses a uint32_t value to specify an internal format. The only format
47 * defined has one uint32_t value of 0, followed by 20 bytes specifying a sha1
48 * that uniquely identifies the Mesa driver type and version.
51 struct program_binary_header
{
52 /* If internal_format is 0, it must be followed by the 20 byte sha1 that
53 * identifies the Mesa driver and version supported. If we want to support
54 * something besides a sha1, then a new internal_format value can be added.
56 uint32_t internal_format
;
58 /* Fields following sha1 can be changed since the sha1 will guarantee that
59 * the binary only works with the same Mesa version.
66 * Returns the header size needed for a binary
69 get_program_binary_header_size(void)
71 return sizeof(struct program_binary_header
);
75 write_program_binary(const void *payload
, unsigned payload_size
,
76 const void *sha1
, void *binary
, unsigned binary_size
,
77 GLenum
*binary_format
)
79 struct program_binary_header
*hdr
= binary
;
81 if (binary_size
< sizeof(*hdr
))
84 /* binary_size is the size of the buffer provided by the application.
85 * Make sure our program (payload) will fit in the buffer.
87 if (payload_size
> binary_size
- sizeof(*hdr
))
90 hdr
->internal_format
= 0;
91 memcpy(hdr
->sha1
, sha1
, sizeof(hdr
->sha1
));
92 memcpy(hdr
+ 1, payload
, payload_size
);
93 hdr
->size
= payload_size
;
95 hdr
->crc32
= util_hash_crc32(hdr
+ 1, payload_size
);
96 *binary_format
= GL_PROGRAM_BINARY_FORMAT_MESA
;
102 simple_header_checks(const struct program_binary_header
*hdr
, unsigned length
)
104 if (hdr
== NULL
|| length
< sizeof(*hdr
))
107 if (hdr
->internal_format
!= 0)
114 check_crc32(const struct program_binary_header
*hdr
, unsigned length
)
119 crc32_len
= hdr
->size
;
120 if (crc32_len
> length
- sizeof(*hdr
))
123 crc32
= util_hash_crc32(hdr
+ 1, crc32_len
);
124 if (hdr
->crc32
!= crc32
)
131 is_program_binary_valid(GLenum binary_format
, const void *sha1
,
132 const struct program_binary_header
*hdr
,
135 if (binary_format
!= GL_PROGRAM_BINARY_FORMAT_MESA
)
138 if (!simple_header_checks(hdr
, length
))
141 if (memcmp(hdr
->sha1
, sha1
, sizeof(hdr
->sha1
)) != 0)
144 if (!check_crc32(hdr
, length
))
151 * Returns the payload within the binary.
153 * If NULL is returned, then the binary not supported. If non-NULL is
154 * returned, it will be a pointer contained within the specified `binary`
157 * This can be used to access the payload of `binary` during the
158 * glProgramBinary call.
161 get_program_binary_payload(GLenum binary_format
, const void *sha1
,
162 const void *binary
, unsigned length
)
164 const struct program_binary_header
*hdr
= binary
;
165 if (!is_program_binary_valid(binary_format
, sha1
, hdr
, length
))
167 return (const uint8_t*)binary
+ sizeof(*hdr
);
171 write_program_payload(struct gl_context
*ctx
, struct blob
*blob
,
172 struct gl_shader_program
*sh_prog
)
174 for (unsigned stage
= 0; stage
< MESA_SHADER_STAGES
; stage
++) {
175 struct gl_linked_shader
*shader
= sh_prog
->_LinkedShaders
[stage
];
177 ctx
->Driver
.ProgramBinarySerializeDriverBlob(ctx
, sh_prog
,
181 serialize_glsl_program(blob
, ctx
, sh_prog
);
183 for (unsigned stage
= 0; stage
< MESA_SHADER_STAGES
; stage
++) {
184 struct gl_linked_shader
*shader
= sh_prog
->_LinkedShaders
[stage
];
186 struct gl_program
*prog
= sh_prog
->_LinkedShaders
[stage
]->Program
;
187 ralloc_free(prog
->driver_cache_blob
);
188 prog
->driver_cache_blob
= NULL
;
189 prog
->driver_cache_blob_size
= 0;
195 read_program_payload(struct gl_context
*ctx
, struct blob_reader
*blob
,
196 GLenum binary_format
, struct gl_shader_program
*sh_prog
)
198 if (!deserialize_glsl_program(blob
, ctx
, sh_prog
))
202 for (stage
= 0; stage
< ARRAY_SIZE(sh_prog
->_LinkedShaders
); stage
++) {
203 struct gl_linked_shader
*shader
= sh_prog
->_LinkedShaders
[stage
];
207 ctx
->Driver
.ProgramBinaryDeserializeDriverBlob(ctx
, sh_prog
,
215 _mesa_get_program_binary_length(struct gl_context
*ctx
,
216 struct gl_shader_program
*sh_prog
,
220 blob_init_fixed(&blob
, NULL
, SIZE_MAX
);
221 write_program_payload(ctx
, &blob
, sh_prog
);
222 *length
= get_program_binary_header_size() + blob
.size
;
227 _mesa_get_program_binary(struct gl_context
*ctx
,
228 struct gl_shader_program
*sh_prog
,
229 GLsizei buf_size
, GLsizei
*length
,
230 GLenum
*binary_format
, GLvoid
*binary
)
233 uint8_t driver_sha1
[20];
234 unsigned header_size
= get_program_binary_header_size();
236 ctx
->Driver
.GetProgramBinaryDriverSHA1(ctx
, driver_sha1
);
240 if (buf_size
< header_size
)
243 write_program_payload(ctx
, &blob
, sh_prog
);
244 if (blob
.size
+ header_size
> buf_size
||
248 bool written
= write_program_binary(blob
.data
, blob
.size
, driver_sha1
,
249 binary
, buf_size
, binary_format
);
250 if (!written
|| blob
.out_of_memory
)
253 *length
= header_size
+ blob
.size
;
259 _mesa_error(ctx
, GL_INVALID_OPERATION
,
260 "glGetProgramBinary(buffer too small)");
266 _mesa_program_binary(struct gl_context
*ctx
, struct gl_shader_program
*sh_prog
,
267 GLenum binary_format
, const GLvoid
*binary
,
270 uint8_t driver_sha1
[20];
271 unsigned header_size
= get_program_binary_header_size();
273 ctx
->Driver
.GetProgramBinaryDriverSHA1(ctx
, driver_sha1
);
275 const void *payload
= get_program_binary_payload(binary_format
, driver_sha1
,
278 if (payload
== NULL
) {
279 sh_prog
->data
->LinkStatus
= LINKING_FAILURE
;
283 struct blob_reader blob
;
284 blob_reader_init(&blob
, payload
, length
- header_size
);
286 unsigned programs_in_use
= 0;
288 for (unsigned stage
= 0; stage
< MESA_SHADER_STAGES
; stage
++) {
289 if (ctx
->_Shader
->CurrentProgram
[stage
] &&
290 ctx
->_Shader
->CurrentProgram
[stage
]->Id
== sh_prog
->Name
) {
291 programs_in_use
|= 1 << stage
;
295 if (!read_program_payload(ctx
, &blob
, binary_format
, sh_prog
)) {
296 sh_prog
->data
->LinkStatus
= LINKING_FAILURE
;
300 /* From section 7.3 (Program Objects) of the OpenGL 4.5 spec:
302 * "If LinkProgram or ProgramBinary successfully re-links a program
303 * object that is active for any shader stage, then the newly generated
304 * executable code will be installed as part of the current rendering
305 * state for all shader stages where the program is active.
306 * Additionally, the newly generated executable code is made part of
307 * the state of any program pipeline for all stages where the program
310 while (programs_in_use
) {
311 const int stage
= u_bit_scan(&programs_in_use
);
313 struct gl_program
*prog
= NULL
;
314 if (sh_prog
->_LinkedShaders
[stage
])
315 prog
= sh_prog
->_LinkedShaders
[stage
]->Program
;
317 _mesa_use_program(ctx
, stage
, sh_prog
, prog
, ctx
->_Shader
);
320 sh_prog
->data
->LinkStatus
= LINKING_SKIPPED
;