1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
26 // File: vogl_shader_state.cpp
27 #include "vogl_shader_state.h"
28 #include "vogl_growable_array.h"
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),
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),
54 vogl_shader_state &vogl_shader_state::operator=(const vogl_shader_state &rhs)
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;
76 vogl_shader_state::~vogl_shader_state()
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)
86 VOGL_NOTE_UNUSED(remapper);
87 VOGL_NOTE_UNUSED(context_info);
88 VOGL_NOTE_UNUSED(target);
94 VOGL_ASSERT(handle <= cUINT32_MAX);
96 m_snapshot_handle = static_cast<GLuint>(handle);
98 GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_SHADER_TYPE, reinterpret_cast<GLint *>(&m_shader_type));
99 if (vogl_check_gl_error())
107 GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_DELETE_STATUS, &val);
109 m_marked_for_deletion = (val != 0);
111 GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_COMPILE_STATUS, &val);
113 m_compile_status = (val != 0);
115 GLint info_log_length = 0;
116 GL_ENTRYPOINT(glGetShaderiv)(m_snapshot_handle, GL_INFO_LOG_LENGTH, &info_log_length);
119 growable_array<GLchar, 4096> temp_buf(info_log_length);
123 GLint actual_len = 0;
124 GL_ENTRYPOINT(glGetShaderInfoLog)(m_snapshot_handle, info_log_length, &actual_len, temp_buf.get_ptr());
127 m_info_log.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
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);
135 if (shader_source_length)
137 temp_buf.resize(shader_source_length);
139 GLint actual_len = 0;
140 GL_ENTRYPOINT(glGetShaderSource)(m_snapshot_handle, shader_source_length, &actual_len, temp_buf.get_ptr());
143 set_source(reinterpret_cast<char *>(temp_buf.get_ptr()));
151 bool vogl_shader_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
155 VOGL_NOTE_UNUSED(context_info);
159 m_restore_compile_status = false;
164 bool created_handle = false;
168 handle = GL_ENTRYPOINT(glCreateShader)(m_shader_type);
169 if ((vogl_check_gl_error()) || (!handle))
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);
175 created_handle = true;
178 VOGL_ASSERT(handle <= cUINT32_MAX);
180 if (m_source.get_len())
182 GLchar *const pStr = (GLchar *)(m_source.get_ptr());
184 GL_ENTRYPOINT(glShaderSource)(static_cast<GLuint>(handle), 1, &pStr, NULL);
185 if (vogl_check_gl_error())
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())
193 // Immediately get the compile status now to avoid driver bugs I've seen in the wild.
195 GL_ENTRYPOINT(glGetShaderiv)(static_cast<GLuint>(handle), GL_COMPILE_STATUS, &val);
198 m_restore_compile_status = (val != 0);
200 if (!m_restore_compile_status)
202 GLint info_log_length = 0;
203 GL_ENTRYPOINT(glGetShaderiv)(static_cast<GLuint>(handle), GL_INFO_LOG_LENGTH, &info_log_length);
206 growable_array<GLchar, 4096> temp_buf(info_log_length);
208 dynamic_string info_log;
211 GLint actual_len = 0;
212 GL_ENTRYPOINT(glGetShaderInfoLog)(static_cast<GLuint>(handle), info_log_length, &actual_len, temp_buf.get_ptr());
215 info_log.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
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());
221 else if (!m_compile_status)
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());
233 remapper.delete_handle_and_object(VOGL_NAMESPACE_SHADERS, m_snapshot_handle, handle);
235 //GL_ENTRYPOINT(glDeleteShader)(static_cast<GLuint>(handle));
236 //VOGL_CHECK_GL_ERROR;
243 bool vogl_shader_state::remap_handles(vogl_handle_remapper &remapper)
250 m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_SHADERS, m_snapshot_handle));
255 void vogl_shader_state::clear()
259 m_snapshot_handle = 0;
260 m_shader_type = GL_NONE;
264 m_source_blob_id.clear();
266 m_marked_for_deletion = false;
267 m_compile_status = false;
268 m_restore_compile_status = false;
273 bool vogl_shader_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
280 dynamic_string source_blob_id;
282 if (m_source.get_len())
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");
292 dynamic_string prefix(cVarArg, "%s_%u", pPrefix, m_compile_status);
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())
298 m_source_blob_id = source_blob_id;
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);
311 bool vogl_shader_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
317 dynamic_string source_blob_id(node.value_as_string("source_blob_id"));
318 if (!source_blob_id.is_empty())
320 m_source_blob_id = source_blob_id;
322 uint8_vec source_data;
323 if (!blob_manager.get(source_blob_id, source_data))
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);
331 if (!source_data.is_empty())
332 m_source.set_from_buf(source_data.get_ptr(), source_data.size());
335 m_snapshot_handle = node.value_as_uint32("handle");
336 VOGL_ASSERT(m_snapshot_handle);
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))
342 m_info_log = node.value_as_string("info_log");
344 m_marked_for_deletion = node.value_as_bool("marked_for_deletion");
345 m_compile_status = node.value_as_bool("compile_status");
352 bool vogl_shader_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
356 if ((!m_is_valid) || (!rhs_obj.is_valid()))
359 if (rhs_obj.get_type() != cGLSTShader)
362 const vogl_shader_state &rhs = static_cast<const vogl_shader_state &>(rhs_obj);
367 // only compare the stuff we can save/restore
368 if (m_shader_type != rhs.m_shader_type)
371 if (m_source.compare(rhs.m_source, true) != 0)
377 bool vogl_shader_state::compare_full_state(const vogl_shader_state &rhs) const
384 CMP(m_snapshot_handle);
388 CMP(m_marked_for_deletion);
389 CMP(m_compile_status);