]> git.cworth.org Git - apitrace/blob - gui/apitracecall.cpp
bac3d73762ce1575902e3f4ce351045c3a6f0aa9
[apitrace] / gui / apitracecall.cpp
1 #include "apitracecall.h"
2
3 #include "apitrace.h"
4 #include "trace_model.hpp"
5
6 #include <QDebug>
7 #include <QObject>
8 #define QT_USE_FAST_OPERATOR_PLUS
9 #include <QStringBuilder>
10
11 const char * const styleSheet =
12     ".call {\n"
13     "    font-weight:bold;\n"
14     // text shadow looks great but doesn't work well in qtwebkit 4.7
15     "    /*text-shadow: 0px 2px 3px #555;*/\n"
16     "    font-size: 1.2em;\n"
17     "}\n"
18     ".arg-name {\n"
19     "    border: 1px solid rgb(238,206,0);\n"
20     "    border-radius: 4px;\n"
21     "    background: yellow;\n"
22     "    padding: 2px;\n"
23     "    box-shadow: 0px 1px 3px dimgrey;\n"
24     "    -webkit-transition: background 1s linear;\n"
25     "}\n"
26     ".arg-name:hover {\n"
27     "    background: white;\n"
28     "}\n"
29     ".arg-value {\n"
30     "    color: #0000ff;\n"
31     "}\n"
32     ".error {\n"
33     "    margin: 10px;\n"
34     "    padding: 0;\n"
35     "    color: red;\n"
36     "    background: #6fb2e5;\n"
37     "    box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
38     "    -o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
39     "    -webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
40     "    -moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
41     "}\n";
42
43 ApiPointer::ApiPointer(unsigned long long val)
44     : m_value(val)
45 {
46 }
47
48 QString ApiPointer::toString() const
49 {
50     if (m_value)
51         return QString("0x%1").arg(m_value, 0, 16);
52     else
53         return QLatin1String("NULL");
54 }
55
56 QString apiVariantToString(const QVariant &variant)
57 {
58     if (variant.userType() == QVariant::Double) {
59         return QString::number(variant.toFloat());
60     }
61     if (variant.userType() == QVariant::ByteArray) {
62         if (variant.toByteArray().size() < 1024) {
63             int bytes = variant.toByteArray().size();
64             return QObject::tr("[binary data, size = %1 bytes]").arg(bytes);
65         } else {
66             float kb = variant.toByteArray().size()/1024.;
67             return QObject::tr("[binary data, size = %1 kb]").arg(kb);
68         }
69     }
70
71     if (variant.userType() < QVariant::UserType) {
72         return variant.toString();
73     }
74
75     if (variant.canConvert<ApiPointer>()) {
76         return variant.value<ApiPointer>().toString();
77     }
78     if (variant.canConvert<ApiBitmask>()) {
79         return variant.value<ApiBitmask>().toString();
80     }
81     if (variant.canConvert<ApiStruct>()) {
82         return variant.value<ApiStruct>().toString();
83     }
84     if (variant.canConvert<ApiArray>()) {
85         return variant.value<ApiArray>().toString();
86     }
87     if (variant.canConvert<ApiEnum>()) {
88         return variant.value<ApiEnum>().toString();
89     }
90
91     return QString();
92 }
93
94 ApiBitmask::ApiBitmask(const Trace::Bitmask *bitmask)
95     : m_value(0)
96 {
97     init(bitmask);
98 }
99
100
101 void ApiBitmask::init(const Trace::Bitmask *bitmask)
102 {
103     if (!bitmask)
104         return;
105
106     m_value = bitmask->value;
107     for (Trace::Bitmask::Signature::const_iterator it = bitmask->sig->begin();
108          it != bitmask->sig->end(); ++it) {
109         assert(it->second);
110         QPair<QString, unsigned long long> pair;
111
112         pair.first = QString::fromStdString(it->first);
113         pair.second = it->second;
114
115         m_sig.append(pair);
116     }
117 }
118
119 QString ApiBitmask::toString() const
120 {
121     QString str;
122     unsigned long long value = m_value;
123     bool first = true;
124     for (Signature::const_iterator it = m_sig.begin();
125          value != 0 && it != m_sig.end(); ++it) {
126         Q_ASSERT(it->second);
127         if ((value & it->second) == it->second) {
128             if (!first) {
129                 str += QLatin1String(" | ");
130             }
131             str += it->first;
132             value &= ~it->second;
133             first = false;
134         }
135     }
136     if (value || first) {
137         if (!first) {
138             str += QLatin1String(" | ");
139         }
140         str += QString::fromLatin1("0x%1").arg(value, 0, 16);
141     }
142     return str;
143 }
144
145 ApiStruct::ApiStruct(const Trace::Struct *s)
146 {
147     init(s);
148 }
149
150 QString ApiStruct::toString() const
151 {
152     QString str;
153
154     str += QLatin1String("{");
155     for (unsigned i = 0; i < m_members.count(); ++i) {
156         str += m_sig.memberNames[i] %
157                QLatin1Literal(" = ") %
158                apiVariantToString(m_members[i]);
159         if (i < m_members.count() - 1)
160             str += QLatin1String(", ");
161     }
162     str += QLatin1String("}");
163
164     return str;
165 }
166
167 void ApiStruct::init(const Trace::Struct *s)
168 {
169     if (!s)
170         return;
171
172     m_sig.name = QString::fromStdString(s->sig->name);
173     for (unsigned i = 0; i < s->members.size(); ++i) {
174         VariantVisitor vis;
175         m_sig.memberNames.append(
176             QString::fromStdString(s->sig->member_names[i]));
177         s->members[i]->visit(vis);
178         m_members.append(vis.variant());
179     }
180 }
181
182 void VariantVisitor::visit(Trace::Null *)
183 {
184     m_variant = QVariant::fromValue(ApiPointer(0));
185 }
186
187 void VariantVisitor::visit(Trace::Bool *node)
188 {
189     m_variant = QVariant(node->value);
190 }
191
192 void VariantVisitor::visit(Trace::SInt *node)
193 {
194     m_variant = QVariant(node->value);
195 }
196
197 void VariantVisitor::visit(Trace::UInt *node)
198 {
199     m_variant = QVariant(node->value);
200 }
201
202 void VariantVisitor::visit(Trace::Float *node)
203 {
204     m_variant = QVariant(node->value);
205 }
206
207 void VariantVisitor::visit(Trace::String *node)
208 {
209     m_variant = QVariant(QString::fromStdString(node->value));
210 }
211
212 void VariantVisitor::visit(Trace::Enum *e)
213 {
214     VariantVisitor vis;
215     e->sig->second->visit(vis);
216
217     QVariant val = vis.variant();
218
219     m_variant = QVariant::fromValue(
220         ApiEnum(QString::fromStdString(e->sig->first), val));
221 }
222
223 void VariantVisitor::visit(Trace::Bitmask *bitmask)
224 {
225     m_variant = QVariant::fromValue(ApiBitmask(bitmask));
226 }
227
228 void VariantVisitor::visit(Trace::Struct *str)
229 {
230     m_variant = QVariant::fromValue(ApiStruct(str));
231 }
232
233 void VariantVisitor::visit(Trace::Array *array)
234 {
235     m_variant = QVariant::fromValue(ApiArray(array));
236 }
237
238 void VariantVisitor::visit(Trace::Blob *blob)
239 {
240     //XXX
241     //FIXME: this is a nasty hack. Trace::Blob's can't
242     //   delete the contents in the destructor because
243     //   the data is being used by other calls. We piggy back
244     //   on that assumption and don't deep copy the data. If
245     //   Blob's will start deleting the data we will need to
246     //   start deep copying it or switch to using something like
247     //   Boost's shared_ptr or Qt's QSharedPointer to handle it
248     QByteArray barray = QByteArray::fromRawData(blob->buf, blob->size);
249     m_variant = QVariant(barray);
250 }
251
252 void VariantVisitor::visit(Trace::Pointer *ptr)
253 {
254     m_variant = QVariant::fromValue(ApiPointer(ptr->value));
255 }
256
257 ApiArray::ApiArray(const Trace::Array *arr)
258 {
259     init(arr);
260 }
261
262 ApiArray::ApiArray(const QList<QVariant> &vals)
263     : m_array(vals)
264 {
265 }
266
267 QString ApiArray::toString() const
268 {
269     QString str;
270     str += QLatin1String("[");
271     for(int i = 0; i < m_array.count(); ++i) {
272         const QVariant &var = m_array[i];
273         str += apiVariantToString(var);
274         if (i < m_array.count() - 1)
275             str += QLatin1String(", ");
276     }
277     str += QLatin1String("]");
278
279     return str;
280 }
281
282 void ApiArray::init(const Trace::Array *arr)
283 {
284     if (!arr)
285         return;
286
287     for (int i = 0; i < arr->values.size(); ++i) {
288         VariantVisitor vis;
289         arr->values[i]->visit(vis);
290
291         m_array.append(vis.variant());
292     }
293 }
294
295 QStaticText ApiTraceCall::staticText() const
296 {
297     if (m_staticText && !m_staticText->text().isEmpty())
298         return *m_staticText;
299
300     QVariantList argValues = arguments();
301
302     QString richText = QString::fromLatin1(
303         "<span style=\"font-weight:bold\">%1</span>(").arg(m_name);
304     for (int i = 0; i < m_argNames.count(); ++i) {
305         richText += QLatin1String("<span style=\"color:#0000ff\">");
306         QString argText = apiVariantToString(argValues[i]);
307
308         //if arguments are really long (e.g. shader text), cut them
309         // and elide it
310         if (argText.length() > 40) {
311             QString shortened = argText.mid(0, 40);
312             shortened[argText.length() - 5] = '.';
313             shortened[argText.length() - 4] = '.';
314             shortened[argText.length() - 3] = '.';
315             shortened[argText.length() - 2] = argText[argText.length() - 2];
316             shortened[argText.length() - 1] = argText[argText.length() - 1];
317             richText += shortened;
318         } else {
319             richText += argText;
320         }
321         richText += QLatin1String("</span>");
322         if (i < m_argNames.count() - 1)
323             richText += QLatin1String(", ");
324     }
325     richText += QLatin1String(")");
326     if (m_returnValue.isValid()) {
327         richText +=
328             QLatin1Literal(" = ") %
329             QLatin1Literal("<span style=\"color:#0000ff\">") %
330             apiVariantToString(m_returnValue) %
331             QLatin1Literal("</span>");
332     }
333
334     if (!m_staticText)
335         m_staticText = new QStaticText(richText);
336     else
337         m_staticText->setText(richText);
338     QTextOption opt;
339     opt.setWrapMode(QTextOption::NoWrap);
340     m_staticText->setTextOption(opt);
341     m_staticText->prepare();
342
343     return *m_staticText;
344 }
345
346 QString ApiTraceCall::toHtml() const
347 {
348     if (!m_richText.isEmpty())
349         return m_richText;
350
351     m_richText = QLatin1String("<div class=\"call\">");
352
353     if (m_helpUrl.isEmpty()) {
354         m_richText += QString::fromLatin1(
355             "%1) <span class=\"callName\">%2</span>(")
356                       .arg(m_index)
357                       .arg(m_name);
358     } else {
359         m_richText += QString::fromLatin1(
360             "%1) <span class=\"callName\"><a href=\"%2\">%3</a></span>(")
361                       .arg(m_index)
362                       .arg(m_helpUrl.toString())
363                       .arg(m_name);
364     }
365
366     QVariantList argValues = arguments();
367     for (int i = 0; i < m_argNames.count(); ++i) {
368         m_richText +=
369             QLatin1String("<span class=\"arg-name\">") +
370             m_argNames[i] +
371             QLatin1String("</span>") +
372             QLatin1Literal(" = ") +
373             QLatin1Literal("<span class=\"arg-value\">") +
374             apiVariantToString(argValues[i]) +
375             QLatin1Literal("</span>");
376         if (i < m_argNames.count() - 1)
377             m_richText += QLatin1String(", ");
378     }
379     m_richText += QLatin1String(")");
380
381     if (m_returnValue.isValid()) {
382         m_richText +=
383             QLatin1String(" = ") +
384             QLatin1String("<span style=\"color:#0000ff\">") +
385             apiVariantToString(m_returnValue) +
386             QLatin1String("</span>");
387     }
388     m_richText += QLatin1String("</div>");
389
390     m_richText =
391         QString::fromLatin1(
392             "<html><head><style type=\"text/css\" media=\"all\">"
393             "%1</style></head><body>%2</body></html>")
394         .arg(styleSheet)
395         .arg(m_richText);
396     m_richText.squeeze();
397
398     //qDebug()<<m_richText;
399     return m_richText;
400 }
401
402 QString ApiTraceCall::filterText() const
403 {
404     if (!m_filterText.isEmpty())
405         return m_filterText;
406
407     QVariantList argValues = arguments();
408     m_filterText = m_name + QLatin1Literal("(");
409     for (int i = 0; i < m_argNames.count(); ++i) {
410         m_filterText += m_argNames[i] +
411                         QLatin1Literal(" = ") +
412                         apiVariantToString(argValues[i]);
413         if (argValues[i].type() == QVariant::ByteArray) {
414             m_hasBinaryData = true;
415             m_binaryDataIndex = i;
416         }
417         if (i < m_argNames.count() - 1)
418             m_filterText += QLatin1String(", ");
419     }
420     m_filterText += QLatin1String(")");
421
422     if (m_returnValue.isValid()) {
423         m_filterText += QLatin1Literal(" = ") +
424                         apiVariantToString(m_returnValue);
425     }
426     m_filterText.squeeze();
427     return m_filterText;
428 }
429
430 QStaticText ApiTraceFrame::staticText() const
431 {
432     if (m_staticText && !m_staticText->text().isEmpty())
433         return *m_staticText;
434
435     QString richText =
436         QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
437
438     if (!m_staticText)
439         m_staticText = new QStaticText(richText);
440
441     QTextOption opt;
442     opt.setWrapMode(QTextOption::NoWrap);
443     m_staticText->setTextOption(opt);
444     m_staticText->prepare();
445
446     return *m_staticText;
447 }
448
449 int ApiTraceCall::numChildren() const
450 {
451     return 0;
452 }
453
454 int ApiTraceFrame::numChildren() const
455 {
456     return calls.count();
457 }
458
459 ApiTraceFrame::ApiTraceFrame()
460     : ApiTraceEvent(ApiTraceEvent::Frame),
461       m_parentTrace(0)
462 {
463 }
464
465 ApiTraceCall::ApiTraceCall()
466     : ApiTraceEvent(ApiTraceEvent::Call),
467       m_hasBinaryData(false),
468       m_binaryDataIndex(0)
469 {
470 }
471
472 ApiTraceEvent::ApiTraceEvent()
473     : m_type(ApiTraceEvent::None),
474       m_staticText(0)
475 {
476 }
477
478 ApiTraceEvent::ApiTraceEvent(Type t)
479     : m_type(t),
480       m_staticText(0)
481 {
482 }
483
484 ApiTraceCall::~ApiTraceCall()
485 {
486 }
487
488 QVariantMap ApiTraceEvent::stateParameters() const
489 {
490     return m_state.parameters();
491 }
492
493 ApiTraceState ApiTraceEvent::state() const
494 {
495     return m_state;
496 }
497
498 void ApiTraceEvent::setState(const ApiTraceState &state)
499 {
500     m_state = state;
501 }
502
503 bool ApiTraceCall::hasBinaryData() const
504 {
505     return m_hasBinaryData;
506 }
507
508 int ApiTraceCall::binaryDataIndex() const
509 {
510     return m_binaryDataIndex;
511 }
512
513 ApiTraceState::ApiTraceState()
514 {
515 }
516
517 ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
518 {
519     m_parameters = parsedJson[QLatin1String("parameters")].toMap();
520     QVariantMap attachedShaders =
521         parsedJson[QLatin1String("shaders")].toMap();
522     QVariantMap::const_iterator itr;
523
524
525     for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
526          ++itr) {
527         QString type = itr.key();
528         QString source = itr.value().toString();
529         m_shaderSources[type] = source;
530     }
531
532     QVariantList textureUnits =
533         parsedJson[QLatin1String("textures")].toList();
534     for (int i = 0; i < textureUnits.count(); ++i) {
535         QVariantMap unit = textureUnits[i].toMap();
536         for (itr = unit.constBegin(); itr != unit.constEnd(); ++itr) {
537             QVariantMap target = itr.value().toMap();
538             if (target.count()) {
539                 QVariantList levels = target[QLatin1String("levels")].toList();
540                 for (int j = 0; j < levels.count(); ++j) {
541                     QVariantMap level = levels[j].toMap();
542                     QVariantMap image = level[QLatin1String("image")].toMap();
543                     QSize size(image[QLatin1String("__width__")].toInt(),
544                                image[QLatin1String("__height__")].toInt());
545                     QString cls = image[QLatin1String("__class__")].toString();
546                     QString type = image[QLatin1String("__type__")].toString();
547                     bool normalized =
548                         image[QLatin1String("__normalized__")].toBool();
549                     int numChannels =
550                         image[QLatin1String("__channels__")].toInt();
551
552                     Q_ASSERT(type == QLatin1String("uint8"));
553                     Q_ASSERT(normalized == true);
554
555                     QByteArray dataArray =
556                         image[QLatin1String("__data__")].toByteArray();
557
558                     ApiTexture tex;
559                     tex.setSize(size);
560                     tex.setNumChannels(numChannels);
561                     tex.setLevel(j);
562                     tex.setUnit(i);
563                     tex.setTarget(itr.key());
564                     tex.contentsFromBase64(dataArray);
565
566                     m_textures.append(tex);
567                 }
568             }
569         }
570     }
571
572     QVariantMap fbos =
573         parsedJson[QLatin1String("framebuffer")].toMap();
574     for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) {
575         QVariantMap buffer = itr.value().toMap();
576         QSize size(buffer[QLatin1String("__width__")].toInt(),
577                    buffer[QLatin1String("__height__")].toInt());
578         QString cls = buffer[QLatin1String("__class__")].toString();
579         QString type = buffer[QLatin1String("__type__")].toString();
580         bool normalized = buffer[QLatin1String("__normalized__")].toBool();
581         int numChannels = buffer[QLatin1String("__channels__")].toInt();
582
583         Q_ASSERT(type == QLatin1String("uint8"));
584         Q_ASSERT(normalized == true);
585
586         QByteArray dataArray =
587             buffer[QLatin1String("__data__")].toByteArray();
588
589         ApiFramebuffer fbo;
590         fbo.setSize(size);
591         fbo.setNumChannels(numChannels);
592         fbo.setType(itr.key());
593         fbo.contentsFromBase64(dataArray);
594         m_framebuffers.append(fbo);
595     }
596 }
597
598 QVariantMap ApiTraceState::parameters() const
599 {
600     return m_parameters;
601 }
602
603 QMap<QString, QString> ApiTraceState::shaderSources() const
604 {
605     return m_shaderSources;
606 }
607
608 bool ApiTraceState::isEmpty() const
609 {
610     return m_parameters.isEmpty();
611 }
612
613 QList<ApiTexture> ApiTraceState::textures() const
614 {
615     return m_textures;
616 }
617
618 QList<ApiFramebuffer> ApiTraceState::framebuffers() const
619 {
620     return m_framebuffers;
621 }
622
623 QList<QVariant> ApiArray::values() const
624 {
625     return m_array;
626 }
627
628 int ApiTraceCall::index() const
629 {
630     return m_index;
631 }
632
633 QString ApiTraceCall::name() const
634 {
635     return m_name;
636 }
637
638 QStringList ApiTraceCall::argNames() const
639 {
640     return m_argNames;
641 }
642
643 QVariantList ApiTraceCall::arguments() const
644 {
645     if (m_editedValues.isEmpty())
646         return m_argValues;
647     else
648         return m_editedValues;
649 }
650
651 QVariant ApiTraceCall::returnValue() const
652 {
653     return m_returnValue;
654 }
655
656 QUrl ApiTraceCall::helpUrl() const
657 {
658     return m_helpUrl;
659 }
660
661 ApiTraceCall::ApiTraceCall(const Trace::Call *call)
662     : ApiTraceEvent(ApiTraceEvent::Call),
663       m_hasBinaryData(false),
664       m_binaryDataIndex(0)
665 {
666     m_name = QString::fromStdString(call->sig->name);
667     m_index = call->no;
668
669     QString argumentsText;
670     for (int i = 0; i < call->sig->arg_names.size(); ++i) {
671         m_argNames +=
672             QString::fromStdString(call->sig->arg_names[i]);
673     }
674     if (call->ret) {
675         VariantVisitor retVisitor;
676         call->ret->visit(retVisitor);
677         m_returnValue = retVisitor.variant();
678     }
679     for (int i = 0; i < call->args.size(); ++i) {
680         VariantVisitor argVisitor;
681         call->args[i]->visit(argVisitor);
682         m_argValues += argVisitor.variant();
683     }
684 }
685
686 void ApiTraceCall::setHelpUrl(const QUrl &url)
687 {
688     m_helpUrl = url;
689 }
690
691 void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
692 {
693     m_parentFrame = frame;
694 }
695
696 ApiTraceFrame * ApiTraceCall::parentFrame()const
697 {
698     return m_parentFrame;
699 }
700
701 ApiTraceEvent::~ApiTraceEvent()
702 {
703     delete m_staticText;
704 }
705
706 void ApiTraceCall::revert()
707 {
708     setEditedValues(QVariantList());
709 }
710
711 ApiTrace * ApiTraceFrame::parentTrace() const
712 {
713     return m_parentTrace;
714 }
715
716 void ApiTraceFrame::setParentTrace(ApiTrace *trace)
717 {
718     m_parentTrace = trace;
719 }
720
721 QVariantList ApiTraceCall::originalValues() const
722 {
723     return m_argValues;
724 }
725
726 void ApiTraceCall::setEditedValues(const QVariantList &lst)
727 {
728     ApiTrace *trace = parentTrace();
729
730     m_editedValues = lst;
731     //lets regenerate data
732     m_richText = QString();
733     m_filterText = QString();
734     delete m_staticText;
735     m_staticText = 0;
736
737     if (trace) {
738         if (!lst.isEmpty()) {
739             trace->callEdited(this);
740         } else {
741             trace->callReverted(this);
742         }
743     }
744 }
745
746 QVariantList ApiTraceCall::editedValues() const
747 {
748     return m_editedValues;
749 }
750
751 bool ApiTraceCall::edited() const
752 {
753     return !m_editedValues.isEmpty();
754 }
755
756 ApiEnum::ApiEnum(const QString &name, const QVariant &val)
757     : m_name(name),
758       m_value(val)
759 {
760 }
761
762 QString ApiEnum::toString() const
763 {
764     return m_name;
765 }
766
767 QVariant ApiEnum::value() const
768 {
769     return m_value;
770 }
771
772 QString ApiEnum::name() const
773 {
774     return m_name;
775 }
776
777 unsigned long long ApiBitmask::value() const
778 {
779     return m_value;
780 }
781
782 ApiBitmask::Signature ApiBitmask::signature() const
783 {
784     return m_sig;
785 }
786
787 ApiStruct::Signature ApiStruct::signature() const
788 {
789     return m_sig;
790 }
791
792 QList<QVariant> ApiStruct::values() const
793 {
794     return m_members;
795 }
796
797 unsigned long long ApiPointer::value() const
798 {
799     return m_value;
800 }
801
802 bool ApiTraceCall::hasError() const
803 {
804     return !m_error.isEmpty();
805 }
806
807 QString ApiTraceCall::error() const
808 {
809     return m_error;
810 }
811
812 void ApiTraceCall::setError(const QString &msg)
813 {
814     if (m_error != msg) {
815         ApiTrace *trace = parentTrace();
816         m_error = msg;
817         if (trace)
818             trace->callError(this);
819     }
820 }
821
822 ApiTrace * ApiTraceCall::parentTrace() const
823 {
824     if (m_parentFrame)
825         return m_parentFrame->parentTrace();
826     return NULL;
827 }
828