]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_shader_state.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_shader_state.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 // File: vogl_shader_state.cpp
27 #include "vogl_shader_state.h"
28 #include "vogl_growable_array.h"
29
30 vogl_shader_state::vogl_shader_state()
31     : m_snapshot_handle(0),
32       m_shader_type(GL_NONE),
33       m_restore_compile_status(false),
34       m_marked_for_deletion(false),
35       m_compile_status(false),
36       m_is_valid(false)
37 {
38     VOGL_FUNC_TRACER
39 }
40
41 vogl_shader_state::vogl_shader_state(const vogl_shader_state &other)
42     : m_snapshot_handle(0),
43       m_shader_type(GL_NONE),
44       m_restore_compile_status(false),
45       m_marked_for_deletion(false),
46       m_compile_status(false),
47       m_is_valid(false)
48 {
49     VOGL_FUNC_TRACER
50
51     *this = other;
52 }
53
54 vogl_shader_state &vogl_shader_state::operator=(const vogl_shader_state &rhs)
55 {
56     VOGL_FUNC_TRACER
57
58     if (this == &rhs)
59         return *this;
60
61     clear();
62
63     m_snapshot_handle = rhs.m_snapshot_handle;
64     m_shader_type = rhs.m_shader_type;
65     m_info_log = rhs.m_info_log;
66     m_source = rhs.m_source;
67     m_source_blob_id = rhs.m_source_blob_id;
68     m_restore_compile_status = rhs.m_restore_compile_status;
69     m_marked_for_deletion = rhs.m_marked_for_deletion;
70     m_compile_status = rhs.m_compile_status;
71     m_is_valid = rhs.m_is_valid;
72
73     return *this;
74 }
75
76 vogl_shader_state::~vogl_shader_state()
77 {
78     VOGL_FUNC_TRACER
79 }
80
81 // TODO: This will only work in GL 2.0 or higher
82 bool vogl_shader_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
83 {
84     VOGL_FUNC_TRACER
85
86     VOGL_NOTE_UNUSED(remapper);
87     VOGL_NOTE_UNUSED(context_info);
88     VOGL_NOTE_UNUSED(target);
89
90     VOGL_CHECK_GL_ERROR;
91
92     clear();
93
94     VOGL_ASSERT(handle <= cUINT32_MAX);
95
96     m_snapshot_handle = static_cast<GLuint>(handle);
97
98     GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_SHADER_TYPE, reinterpret_cast<GLint *>(&m_shader_type));
99     if (vogl_check_gl_error())
100     {
101         clear();
102         return false;
103     }
104
105     GLint val;
106
107     GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_DELETE_STATUS, &val);
108     VOGL_CHECK_GL_ERROR;
109     m_marked_for_deletion = (val != 0);
110
111     GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_COMPILE_STATUS, &val);
112     VOGL_CHECK_GL_ERROR;
113     m_compile_status = (val != 0);
114
115     GLint info_log_length = 0;
116     GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_INFO_LOG_LENGTH, &info_log_length);
117     VOGL_CHECK_GL_ERROR;
118
119     growable_array<GLchar, 4096> temp_buf(info_log_length);
120
121     if (info_log_length)
122     {
123         GLint actual_len = 0;
124         GL_ENTRYPOINT(glGetShaderInfoLog)(m_snapshot_handle, info_log_length, &actual_len, temp_buf.get_ptr());
125         VOGL_CHECK_GL_ERROR;
126
127         m_info_log.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
128     }
129
130     // length excluding the null terminator
131     GLint shader_source_length = 0;
132     GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_SHADER_SOURCE_LENGTH, &shader_source_length);
133     VOGL_CHECK_GL_ERROR;
134
135     if (shader_source_length)
136     {
137         temp_buf.resize(shader_source_length);
138
139         GLint actual_len = 0;
140         GL_ENTRYPOINT(glGetShaderSource)(m_snapshot_handle, shader_source_length, &actual_len, temp_buf.get_ptr());
141         VOGL_CHECK_GL_ERROR;
142
143         set_source(reinterpret_cast<char *>(temp_buf.get_ptr()));
144     }
145
146     m_is_valid = true;
147
148     return true;
149 }
150
151 bool vogl_shader_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
152 {
153     VOGL_FUNC_TRACER
154
155     VOGL_NOTE_UNUSED(context_info);
156
157     VOGL_CHECK_GL_ERROR;
158
159     m_restore_compile_status = false;
160
161     if (!m_is_valid)
162         return false;
163
164     bool created_handle = false;
165
166     if (!handle)
167     {
168         handle = GL_ENTRYPOINT(glCreateShader)(m_shader_type);
169         if ((vogl_check_gl_error()) || (!handle))
170             return false;
171
172         remapper.declare_handle(VOGL_NAMESPACE_SHADERS, m_snapshot_handle, handle, GL_NONE);
173         VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_SHADERS, m_snapshot_handle) == handle);
174
175         created_handle = true;
176     }
177
178     VOGL_ASSERT(handle <= cUINT32_MAX);
179
180     if (m_source.get_len())
181     {
182         GLchar *const pStr = (GLchar *)(m_source.get_ptr());
183
184         GL_ENTRYPOINT(glShaderSource)(static_cast<GLuint>(handle), 1, &pStr, NULL);
185         if (vogl_check_gl_error())
186             goto handle_error;
187
188         // TODO: We're always trying to compile here when there's shader source, but the original object may not have been compiled. Is this important enough to shadow this?
189         GL_ENTRYPOINT(glCompileShader)(static_cast<GLuint>(handle));
190         if (vogl_check_gl_error())
191             goto handle_error;
192
193         // Immediately get the compile status now to avoid driver bugs I've seen in the wild.
194         GLint val;
195         GL_ENTRYPOINT(glGetShaderiv)(static_cast<GLuint>(handle), GL_COMPILE_STATUS, &val);
196         VOGL_CHECK_GL_ERROR;
197
198         m_restore_compile_status = (val != 0);
199
200         if (!m_restore_compile_status)
201         {
202             GLint info_log_length = 0;
203             GL_ENTRYPOINT(glGetShaderiv)(static_cast<GLuint>(handle), GL_INFO_LOG_LENGTH, &info_log_length);
204             VOGL_CHECK_GL_ERROR;
205
206             growable_array<GLchar, 4096> temp_buf(info_log_length);
207
208             dynamic_string info_log;
209             if (info_log_length)
210             {
211                 GLint actual_len = 0;
212                 GL_ENTRYPOINT(glGetShaderInfoLog)(static_cast<GLuint>(handle), info_log_length, &actual_len, temp_buf.get_ptr());
213                 VOGL_CHECK_GL_ERROR;
214
215                 info_log.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
216             }
217
218             vogl_warning_printf("%s: Restored shader failed to compile: trace compile status %u, snapshot handle %u, restore handle %u, type: %s, blob id: %s, info log:\n\"%s\"\n",
219                                VOGL_METHOD_NAME, m_compile_status, m_snapshot_handle, (uint)handle, g_gl_enums.find_gl_name(m_shader_type), m_source_blob_id.get_ptr(), info_log.get_ptr());
220         }
221         else if (!m_compile_status)
222         {
223             vogl_warning_printf("%s: Shader successfully compiled during restore, but was not compiled successfully during tracing: trace compile status %u, snapshot handle %u, restore handle %u, type: %s, blob id: %s\n",
224                                VOGL_METHOD_NAME, m_compile_status, m_snapshot_handle, (uint)handle, g_gl_enums.find_gl_name(m_shader_type), m_source_blob_id.get_ptr());
225         }
226     }
227
228     return true;
229
230 handle_error:
231     if (created_handle)
232     {
233         remapper.delete_handle_and_object(VOGL_NAMESPACE_SHADERS, m_snapshot_handle, handle);
234
235         //GL_ENTRYPOINT(glDeleteShader)(static_cast<GLuint>(handle));
236         //VOGL_CHECK_GL_ERROR;
237
238         handle = 0;
239     }
240     return false;
241 }
242
243 bool vogl_shader_state::remap_handles(vogl_handle_remapper &remapper)
244 {
245     VOGL_FUNC_TRACER
246
247     if (!m_is_valid)
248         return false;
249
250     m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_SHADERS, m_snapshot_handle));
251
252     return true;
253 }
254
255 void vogl_shader_state::clear()
256 {
257     VOGL_FUNC_TRACER
258
259     m_snapshot_handle = 0;
260     m_shader_type = GL_NONE;
261
262     m_info_log.clear();
263     m_source.clear();
264     m_source_blob_id.clear();
265
266     m_marked_for_deletion = false;
267     m_compile_status = false;
268     m_restore_compile_status = false;
269
270     m_is_valid = false;
271 }
272
273 bool vogl_shader_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
274 {
275     VOGL_FUNC_TRACER
276
277     if (!m_is_valid)
278         return false;
279
280     dynamic_string source_blob_id;
281
282     if (m_source.get_len())
283     {
284         const char *pPrefix = utils::map_value(static_cast<int>(m_shader_type), "shader",
285                                                GL_VERTEX_SHADER, "vertex_shader",
286                                                GL_COMPUTE_SHADER, "compute_shader",
287                                                GL_TESS_CONTROL_SHADER, "tess_control_shader",
288                                                GL_TESS_EVALUATION_SHADER, "tess_eval_shader",
289                                                GL_GEOMETRY_SHADER, "geom_shader",
290                                                GL_FRAGMENT_SHADER, "fragment_shader");
291
292         dynamic_string prefix(cVarArg, "%s_%u", pPrefix, m_compile_status);
293
294         source_blob_id = blob_manager.add_buf_compute_unique_id(m_source.get_ptr(), m_source.get_len(), prefix.get_ptr(), "txt");
295         if (source_blob_id.is_empty())
296             return false;
297
298         m_source_blob_id = source_blob_id;
299     }
300
301     node.add_key_value("handle", m_snapshot_handle);
302     node.add_key_value("type", g_gl_enums.find_gl_name(m_shader_type));
303     node.add_key_value("info_log", m_info_log);
304     node.add_key_value("source_blob_id", source_blob_id);
305     node.add_key_value("marked_for_deletion", m_marked_for_deletion);
306     node.add_key_value("compile_status", m_compile_status);
307
308     return true;
309 }
310
311 bool vogl_shader_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
312 {
313     VOGL_FUNC_TRACER
314
315     clear();
316
317     dynamic_string source_blob_id(node.value_as_string("source_blob_id"));
318     if (!source_blob_id.is_empty())
319     {
320         m_source_blob_id = source_blob_id;
321
322         uint8_vec source_data;
323         if (!blob_manager.get(source_blob_id, source_data))
324             return false;
325
326         // There can't be any null terminators in this buffer or dynamic_string will shit itself
327         int first_zero_ofs = source_data.find(0);
328         if (first_zero_ofs >= 0)
329             source_data.resize(first_zero_ofs);
330
331         if (!source_data.is_empty())
332             m_source.set_from_buf(source_data.get_ptr(), source_data.size());
333     }
334
335     m_snapshot_handle = node.value_as_uint32("handle");
336     VOGL_ASSERT(m_snapshot_handle);
337
338     m_shader_type = vogl_get_json_value_as_enum(node, "type");
339     if (!utils::is_in_set(static_cast<int>(m_shader_type), GL_VERTEX_SHADER, GL_COMPUTE_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADER))
340         return false;
341
342     m_info_log = node.value_as_string("info_log");
343
344     m_marked_for_deletion = node.value_as_bool("marked_for_deletion");
345     m_compile_status = node.value_as_bool("compile_status");
346
347     m_is_valid = true;
348
349     return true;
350 }
351
352 bool vogl_shader_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
353 {
354     VOGL_FUNC_TRACER
355
356     if ((!m_is_valid) || (!rhs_obj.is_valid()))
357         return false;
358
359     if (rhs_obj.get_type() != cGLSTShader)
360         return false;
361
362     const vogl_shader_state &rhs = static_cast<const vogl_shader_state &>(rhs_obj);
363
364     if (this == &rhs)
365         return true;
366
367     // only compare the stuff we can save/restore
368     if (m_shader_type != rhs.m_shader_type)
369         return false;
370
371     if (m_source.compare(rhs.m_source, true) != 0)
372         return false;
373
374     return true;
375 }
376
377 bool vogl_shader_state::compare_full_state(const vogl_shader_state &rhs) const
378 {
379     VOGL_FUNC_TRACER
380
381 #define CMP(x)      \
382     if (x != rhs.x) \
383         return false;
384     CMP(m_snapshot_handle);
385     CMP(m_shader_type);
386     CMP(m_info_log);
387     CMP(m_source);
388     CMP(m_marked_for_deletion);
389     CMP(m_compile_status);
390     CMP(m_is_valid);
391 #undef CMP
392
393     return true;
394 }