]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_trace_file_writer.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_trace_file_writer.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 //----------------------------------------------------------------------------------------------------------------------
27 // File: vogl_trace_file_writer.cpp
28 //----------------------------------------------------------------------------------------------------------------------
29 #include "vogl_trace_file_writer.h"
30 #include "vogl_console.h"
31 #include "vogl_file_utils.h"
32 #include "vogl_uuid.h"
33
34 //----------------------------------------------------------------------------------------------------------------------
35 // vogl_trace_file_writer
36 //----------------------------------------------------------------------------------------------------------------------
37 vogl_trace_file_writer::vogl_trace_file_writer(const vogl_ctypes *pCTypes)
38     : m_gl_call_counter(0),
39       m_pCTypes(pCTypes),
40       m_pTrace_archive(NULL),
41       m_delete_archive(false)
42 {
43     VOGL_FUNC_TRACER
44 }
45
46 vogl_trace_file_writer::~vogl_trace_file_writer()
47 {
48     VOGL_FUNC_TRACER
49
50     close();
51 }
52
53 // pTrace_archive may be NULL. Takes ownership of pTrace_archive.
54 // TODO: Get rid of the demarcation packet, etc. Make the initial sequence of packets more explicit.
55 bool vogl_trace_file_writer::open(const char *pFilename, vogl_archive_blob_manager *pTrace_archive, bool delete_archive, bool write_demarcation_packet, uint pointer_sizes)
56 {
57     VOGL_FUNC_TRACER
58
59     close();
60
61     if (!pFilename)
62         return false;
63
64     m_filename = pFilename;
65     if (!m_stream.open(pFilename, cDataStreamWritable | cDataStreamSeekable, false))
66     {
67         vogl_error_printf("%s: Failed opening trace file \"%s\"\n", VOGL_METHOD_NAME, pFilename);
68         return false;
69     }
70
71     vogl_message_printf("%s: Prepping trace file \"%s\"\n", VOGL_METHOD_NAME, pFilename);
72
73     m_sof_packet.init();
74     m_sof_packet.m_pointer_sizes = pointer_sizes;
75     m_sof_packet.m_first_packet_offset = sizeof(m_sof_packet);
76
77     md5_hash h(gen_uuid());
78     VOGL_ASSUME(sizeof(h) == sizeof(m_sof_packet.m_uuid));
79     memcpy(&m_sof_packet.m_uuid, &h, sizeof(h));
80
81     m_sof_packet.finalize();
82     VOGL_VERIFY(m_sof_packet.full_validation(sizeof(m_sof_packet)));
83
84     if (m_stream.write(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet))
85     {
86         vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_METHOD_NAME, pFilename);
87         return false;
88     }
89
90     if (pTrace_archive)
91     {
92         m_pTrace_archive.reset(pTrace_archive);
93         m_delete_archive = delete_archive;
94     }
95     else
96     {
97         m_pTrace_archive.reset(vogl_new(vogl_archive_blob_manager));
98         m_delete_archive = true;
99
100         if (!m_pTrace_archive->init_file_temp(cBMFReadWrite, NULL))
101         {
102             vogl_error_printf("%s: Failed opening temp archive!\n", VOGL_METHOD_NAME);
103
104             m_pTrace_archive.reset();
105
106             return false;
107         }
108     }
109
110     // TODO: The trace reader records the first offset right after SOF, I would like to do this after the demarcation packet.
111     m_frame_file_offsets.reserve(10000);
112     m_frame_file_offsets.resize(0);
113     m_frame_file_offsets.push_back(m_stream.get_ofs());
114
115     write_ctypes_packet();
116
117     write_entrypoints_packet();
118
119     if (write_demarcation_packet)
120     {
121         vogl_write_glInternalTraceCommandRAD(m_stream, m_pCTypes, cITCRDemarcation, 0, NULL);
122     }
123
124     vogl_message_printf("%s: Finished opening trace file \"%s\"\n", VOGL_METHOD_NAME, pFilename);
125
126     return true;
127 }
128
129 bool vogl_trace_file_writer::close()
130 {
131     VOGL_FUNC_TRACER
132
133     vogl_debug_printf("%s\n", VOGL_METHOD_NAME);
134
135     if (!m_stream.is_opened())
136         return false;
137
138     vogl_message_printf("%s: Flushing trace file %s (this could take some time), %u total frame file offsets\n", VOGL_METHOD_NAME, m_filename.get_ptr(), m_frame_file_offsets.size());
139
140     bool success = true;
141
142     dynamic_string trace_archive_filename;
143
144     if (!write_eof_packet())
145     {
146         vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_METHOD_NAME, m_filename.get_ptr());
147         success = false;
148     }
149     else if (m_pTrace_archive.get())
150     {
151         trace_archive_filename = m_pTrace_archive->get_archive_filename();
152
153         if ((!write_frame_file_offsets_to_archive()) || !m_pTrace_archive->deinit())
154         {
155             vogl_error_printf("%s: Failed closing trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, trace_archive_filename.get_ptr());
156             success = false;
157         }
158         else
159         {
160             if (!file_utils::get_file_size(trace_archive_filename.get_ptr(), m_sof_packet.m_archive_size))
161             {
162                 vogl_error_printf("%s: Failed determining file size of archive file \"%s\"\n", VOGL_FUNCTION_NAME, trace_archive_filename.get_ptr());
163                 success = false;
164             }
165             else if (m_sof_packet.m_archive_size)
166             {
167                 m_sof_packet.m_archive_offset = m_stream.get_size();
168
169                 vogl_message_printf("Copying %" PRIu64 " archive bytes into output trace file\n", m_sof_packet.m_archive_size);
170
171                 if (!m_stream.write_file_data(trace_archive_filename.get_ptr()))
172                 {
173                     vogl_error_printf("%s: Failed copying source archive \"%s\" into trace file!\n", VOGL_METHOD_NAME, trace_archive_filename.get_ptr());
174                     success = false;
175                 }
176
177                 m_sof_packet.m_archive_size = m_stream.get_size() - m_sof_packet.m_archive_offset;
178             }
179         }
180
181         if (success)
182         {
183             m_sof_packet.finalize();
184             VOGL_VERIFY(m_sof_packet.full_validation(sizeof(m_sof_packet)));
185
186             if (!m_stream.seek(0, false) || (m_stream.write(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet)))
187             {
188                 vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_METHOD_NAME, m_filename.get_ptr());
189                 success = false;
190             }
191         }
192     }
193
194     close_archive(trace_archive_filename.get_ptr());
195
196     uint64_t total_trace_file_size = m_stream.get_size();
197
198     if (!m_stream.close())
199     {
200         vogl_error_printf("Failed writing to or closing trace file!\n");
201         success = false;
202     }
203
204     dynamic_string full_trace_filename(m_filename);
205     file_utils::full_path(full_trace_filename);
206
207     if (success)
208         vogl_message_printf("%s: Successfully closed trace file %s, total file size: %s\n", VOGL_METHOD_NAME, full_trace_filename.get_ptr(), uint64_to_string_with_commas(total_trace_file_size).get_ptr());
209     else
210         vogl_error_printf("%s: Failed closing trace file %s! (Trace will probably not be valid.)\n", VOGL_METHOD_NAME, full_trace_filename.get_ptr());
211
212     return success;
213 }
214
215 void vogl_trace_file_writer::write_ctypes_packet()
216 {
217     VOGL_FUNC_TRACER
218
219     key_value_map typemap_key_values;
220     typemap_key_values.insert("command_type", "ctypes");
221     typemap_key_values.insert("num_ctypes", VOGL_NUM_CTYPES);
222     for (uint ctype_iter = 0; ctype_iter < VOGL_NUM_CTYPES; ctype_iter++)
223     {
224         const vogl_ctype_desc_t &desc = (*m_pCTypes)[static_cast<vogl_ctype_t>(ctype_iter)];
225
226         uint base_index = ctype_iter << 8;
227         typemap_key_values.insert(base_index++, desc.m_pName);
228         typemap_key_values.insert(base_index++, desc.m_pCType);
229         typemap_key_values.insert(base_index++, desc.m_size);
230         typemap_key_values.insert(base_index++, desc.m_loki_type_flags);
231         typemap_key_values.insert(base_index++, desc.m_is_pointer);
232         typemap_key_values.insert(base_index++, desc.m_is_opaque_pointer);
233         typemap_key_values.insert(base_index++, desc.m_is_pointer_diff);
234         typemap_key_values.insert(base_index++, desc.m_is_opaque_type);
235     }
236     vogl_write_glInternalTraceCommandRAD(m_stream, m_pCTypes, cITCRKeyValueMap, sizeof(typemap_key_values), reinterpret_cast<const GLubyte *>(&typemap_key_values));
237 }
238
239 void vogl_trace_file_writer::write_entrypoints_packet()
240 {
241     VOGL_FUNC_TRACER
242
243     key_value_map entrypoint_key_values;
244     entrypoint_key_values.insert("command_type", "entrypoints");
245     entrypoint_key_values.insert("num_entrypoints", VOGL_NUM_ENTRYPOINTS);
246     for (uint func_iter = 0; func_iter < VOGL_NUM_ENTRYPOINTS; func_iter++)
247     {
248         const gl_entrypoint_desc_t &desc = g_vogl_entrypoint_descs[func_iter];
249         entrypoint_key_values.insert(func_iter, desc.m_pName);
250     }
251
252     vogl_write_glInternalTraceCommandRAD(m_stream, m_pCTypes, cITCRKeyValueMap, sizeof(entrypoint_key_values), reinterpret_cast<const GLubyte *>(&entrypoint_key_values));
253 }
254
255 bool vogl_trace_file_writer::write_eof_packet()
256 {
257     VOGL_FUNC_TRACER
258
259     vogl_trace_stream_packet_base eof_packet;
260     eof_packet.init(cTSPTEOF, sizeof(vogl_trace_stream_packet_base));
261     eof_packet.finalize();
262     return m_stream.write(&eof_packet, sizeof(eof_packet));
263 }
264
265 bool vogl_trace_file_writer::write_frame_file_offsets_to_archive()
266 {
267     VOGL_FUNC_TRACER
268
269     if (!m_pTrace_archive.get())
270         return false;
271
272     if (m_frame_file_offsets.is_empty())
273         return true;
274
275     return m_pTrace_archive->add_buf_using_id(m_frame_file_offsets.get_ptr(), m_frame_file_offsets.size_in_bytes(), VOGL_TRACE_ARCHIVE_FRAME_FILE_OFFSETS_FILENAME).has_content();
276 }
277
278 void vogl_trace_file_writer::close_archive(const char *pArchive_filename)
279 {
280     VOGL_FUNC_TRACER
281
282     if (m_pTrace_archive.get())
283     {
284         m_pTrace_archive.reset();
285
286         if (m_delete_archive && pArchive_filename)
287         {
288             file_utils::delete_file(pArchive_filename);
289         }
290     }
291     m_delete_archive = false;
292 }