]> git.cworth.org Git - apitrace/blobdiff - gui/apitracecall.cpp
Linearize texture data in JSON.
[apitrace] / gui / apitracecall.cpp
index c9e61e2cb8c8e499dbd35de9a49849005b783518..6570368e1e13885668b92e05068d78999f00e20d 100644 (file)
@@ -4,9 +4,45 @@
 #include "trace_model.hpp"
 
 #include <QDebug>
+#include <QLocale>
 #include <QObject>
 #define QT_USE_FAST_OPERATOR_PLUS
 #include <QStringBuilder>
+#include <QTextDocument>
+
+const char * const styleSheet =
+    ".call {\n"
+    "    font-weight:bold;\n"
+    // text shadow looks great but doesn't work well in qtwebkit 4.7
+    "    /*text-shadow: 0px 2px 3px #555;*/\n"
+    "    font-size: 1.2em;\n"
+    "}\n"
+    ".arg-name {\n"
+    "    border: 1px solid rgb(238,206,0);\n"
+    "    border-radius: 4px;\n"
+    "    background: yellow;\n"
+    "    padding: 2px;\n"
+    "    box-shadow: 0px 1px 3px dimgrey;\n"
+    "    -webkit-transition: background 1s linear;\n"
+    "}\n"
+    ".arg-name:hover {\n"
+    "    background: white;\n"
+    "}\n"
+    ".arg-value {\n"
+    "    color: #0000ff;\n"
+    "}\n"
+    ".error {\n"
+    "    border: 1px solid rgb(255,0,0);\n"
+    "    margin: 10px;\n"
+    "    padding: 1;\n"
+    "    border-radius: 4px;\n"
+    // also looks great but qtwebkit doesn't support it
+    //"    background: #6fb2e5;\n"
+    //"    box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+    //"    -o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+    //"    -webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+    //"    -moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+    "}\n";
 
 ApiPointer::ApiPointer(unsigned long long val)
     : m_value(val)
@@ -21,7 +57,59 @@ QString ApiPointer::toString() const
         return QLatin1String("NULL");
 }
 
-QString apiVariantToString(const QVariant &variant)
+// Qt::convertFromPlainText doesn't do precisely what we want
+static QString
+plainTextToHTML(const QString & plain, bool multiLine)
+{
+    int col = 0;
+    bool quote = false;
+    QString rich;
+    for (int i = 0; i < plain.length(); ++i) {
+        if (plain[i] == QLatin1Char('\n')){
+            if (multiLine) {
+                rich += QLatin1String("<br>\n");
+            } else {
+                rich += QLatin1String("\\n");
+            }
+            col = 0;
+            quote = true;
+        } else {
+            if (plain[i] == QLatin1Char('\t')){
+                if (multiLine) {
+                    rich += QChar(0x00a0U);
+                    ++col;
+                    while (col % 8) {
+                        rich += QChar(0x00a0U);
+                        ++col;
+                    }
+                } else {
+                    rich += QLatin1String("\\t");
+                }
+                quote = true;
+            } else if (plain[i].isSpace()) {
+                rich += QChar(0x00a0U);
+                quote = true;
+            } else if (plain[i] == QLatin1Char('<')) {
+                rich += QLatin1String("&lt;");
+            } else if (plain[i] == QLatin1Char('>')) {
+                rich += QLatin1String("&gt;");
+            } else if (plain[i] == QLatin1Char('&')) {
+                rich += QLatin1String("&amp;");
+            } else {
+                rich += plain[i];
+            }
+            ++col;
+        }
+    }
+
+    if (quote) {
+        return QLatin1Literal("\"") + rich + QLatin1Literal("\"");
+    }
+
+    return rich;
+}
+
+QString apiVariantToString(const QVariant &variant, bool multiLine)
 {
     if (variant.userType() == QVariant::Double) {
         return QString::number(variant.toFloat());
@@ -36,6 +124,10 @@ QString apiVariantToString(const QVariant &variant)
         }
     }
 
+    if (variant.userType() == QVariant::String) {
+        return plainTextToHTML(variant.toString(), multiLine);
+    }
+
     if (variant.userType() < QVariant::UserType) {
         return variant.toString();
     }
@@ -149,7 +241,7 @@ void ApiStruct::init(const Trace::Struct *s)
 
 void VariantVisitor::visit(Trace::Null *)
 {
-    m_variant = QVariant(QLatin1String("NULL"));
+    m_variant = QVariant::fromValue(ApiPointer(0));
 }
 
 void VariantVisitor::visit(Trace::Bool *node)
@@ -265,11 +357,13 @@ QStaticText ApiTraceCall::staticText() const
     if (m_staticText && !m_staticText->text().isEmpty())
         return *m_staticText;
 
+    QVariantList argValues = arguments();
+
     QString richText = QString::fromLatin1(
         "<span style=\"font-weight:bold\">%1</span>(").arg(m_name);
     for (int i = 0; i < m_argNames.count(); ++i) {
         richText += QLatin1String("<span style=\"color:#0000ff\">");
-        QString argText = apiVariantToString(m_argValues[i]);
+        QString argText = apiVariantToString(argValues[i]);
 
         //if arguments are really long (e.g. shader text), cut them
         // and elide it
@@ -299,6 +393,8 @@ QStaticText ApiTraceCall::staticText() const
 
     if (!m_staticText)
         m_staticText = new QStaticText(richText);
+    else
+        m_staticText->setText(richText);
     QTextOption opt;
     opt.setWrapMode(QTextOption::NoWrap);
     m_staticText->setTextOption(opt);
@@ -312,25 +408,31 @@ QString ApiTraceCall::toHtml() const
     if (!m_richText.isEmpty())
         return m_richText;
 
+    m_richText = QLatin1String("<div class=\"call\">");
+
     if (m_helpUrl.isEmpty()) {
-        m_richText = QString::fromLatin1(
-            "%1) <span style=\"font-weight:bold\">%2</span>(")
-                     .arg(m_index)
-                     .arg(m_name);
+        m_richText += QString::fromLatin1(
+            "%1) <span class=\"callName\">%2</span>(")
+                      .arg(m_index)
+                      .arg(m_name);
     } else {
-        m_richText = QString::fromLatin1(
-            "%1) <span style=\"font-weight:bold\"><a href=\"%2\">%3</a></span>(")
+        m_richText += QString::fromLatin1(
+            "%1) <span class=\"callName\"><a href=\"%2\">%3</a></span>(")
                       .arg(m_index)
                       .arg(m_helpUrl.toString())
                       .arg(m_name);
     }
 
+    QVariantList argValues = arguments();
     for (int i = 0; i < m_argNames.count(); ++i) {
-        m_richText += m_argNames[i] +
-                      QLatin1Literal(" = ") +
-                      QLatin1Literal("<span style=\"color:#0000ff\">") +
-                      apiVariantToString(m_argValues[i]) +
-                      QLatin1Literal("</span>");
+        m_richText +=
+            QLatin1String("<span class=\"arg-name\">") +
+            m_argNames[i] +
+            QLatin1String("</span>") +
+            QLatin1Literal(" = ") +
+            QLatin1Literal("<span class=\"arg-value\">") +
+            apiVariantToString(argValues[i], true) +
+            QLatin1Literal("</span>");
         if (i < m_argNames.count() - 1)
             m_richText += QLatin1String(", ");
     }
@@ -340,10 +442,28 @@ QString ApiTraceCall::toHtml() const
         m_richText +=
             QLatin1String(" = ") +
             QLatin1String("<span style=\"color:#0000ff\">") +
-            apiVariantToString(m_returnValue) +
+            apiVariantToString(m_returnValue, true) +
             QLatin1String("</span>");
     }
+    m_richText += QLatin1String("</div>");
+
+    if (hasError()) {
+        QString errorStr =
+            QString::fromLatin1(
+                "<div class=\"error\">%1</div>")
+            .arg(m_error);
+        m_richText += errorStr;
+    }
+
+    m_richText =
+        QString::fromLatin1(
+            "<html><head><style type=\"text/css\" media=\"all\">"
+            "%1</style></head><body>%2</body></html>")
+        .arg(styleSheet)
+        .arg(m_richText);
     m_richText.squeeze();
+
+    //qDebug()<<m_richText;
     return m_richText;
 }
 
@@ -352,15 +472,12 @@ QString ApiTraceCall::filterText() const
     if (!m_filterText.isEmpty())
         return m_filterText;
 
+    QVariantList argValues = arguments();
     m_filterText = m_name + QLatin1Literal("(");
     for (int i = 0; i < m_argNames.count(); ++i) {
         m_filterText += m_argNames[i] +
                         QLatin1Literal(" = ") +
-                        apiVariantToString(m_argValues[i]);
-        if (m_argValues[i].type() == QVariant::ByteArray) {
-            m_hasBinaryData = true;
-            m_binaryDataIndex = i;
-        }
+                        apiVariantToString(argValues[i]);
         if (i < m_argNames.count() - 1)
             m_filterText += QLatin1String(", ");
     }
@@ -379,8 +496,24 @@ QStaticText ApiTraceFrame::staticText() const
     if (m_staticText && !m_staticText->text().isEmpty())
         return *m_staticText;
 
-    QString richText =
-        QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
+    QString richText;
+
+    //mark the frame if it uploads more than a meg a frame
+    if (m_binaryDataSize > (1024*1024)) {
+        richText =
+            QObject::tr(
+                "<span style=\"font-weight:bold;\">"
+                "Frame&nbsp;%1</span>"
+                "<span style=\"font-style:italic;\">"
+                "&nbsp;&nbsp;&nbsp;&nbsp;(%2MB)</span>")
+            .arg(number)
+            .arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2);
+    } else {
+        richText =
+            QObject::tr(
+                "<span style=\"font-weight:bold\">Frame %1</span>")
+            .arg(number);
+    }
 
     if (!m_staticText)
         m_staticText = new QStaticText(richText);
@@ -400,12 +533,13 @@ int ApiTraceCall::numChildren() const
 
 int ApiTraceFrame::numChildren() const
 {
-    return calls.count();
+    return m_calls.count();
 }
 
 ApiTraceFrame::ApiTraceFrame()
     : ApiTraceEvent(ApiTraceEvent::Frame),
-      m_parentTrace(0)
+      m_parentTrace(0),
+      m_binaryDataSize(0)
 {
 }
 
@@ -476,44 +610,32 @@ ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
         m_shaderSources[type] = source;
     }
 
-    QVariantList textureUnits =
-        parsedJson[QLatin1String("textures")].toList();
-    for (int i = 0; i < textureUnits.count(); ++i) {
-        QVariantMap unit = textureUnits[i].toMap();
-        for (itr = unit.constBegin(); itr != unit.constEnd(); ++itr) {
-            QVariantMap target = itr.value().toMap();
-            if (target.count()) {
-                QVariantList levels = target[QLatin1String("levels")].toList();
-                for (int j = 0; j < levels.count(); ++j) {
-                    QVariantMap level = levels[j].toMap();
-                    QVariantMap image = level[QLatin1String("image")].toMap();
-                    QSize size(image[QLatin1String("__width__")].toInt(),
-                               image[QLatin1String("__height__")].toInt());
-                    QString cls = image[QLatin1String("__class__")].toString();
-                    QString type = image[QLatin1String("__type__")].toString();
-                    bool normalized =
-                        image[QLatin1String("__normalized__")].toBool();
-                    int numChannels =
-                        image[QLatin1String("__channels__")].toInt();
-
-                    Q_ASSERT(type == QLatin1String("uint8"));
-                    Q_ASSERT(normalized == true);
-
-                    QByteArray dataArray =
-                        image[QLatin1String("__data__")].toByteArray();
-
-                    ApiTexture tex;
-                    tex.setSize(size);
-                    tex.setNumChannels(numChannels);
-                    tex.setLevel(j);
-                    tex.setUnit(i);
-                    tex.setTarget(itr.key());
-                    tex.contentsFromBase64(dataArray);
-
-                    m_textures.append(tex);
-                }
-            }
-        }
+    QVariantMap textures =
+        parsedJson[QLatin1String("textures")].toMap();
+    for (itr = textures.constBegin(); itr != textures.constEnd(); ++itr) {
+        QVariantMap image = itr.value().toMap();
+        QSize size(image[QLatin1String("__width__")].toInt(),
+                   image[QLatin1String("__height__")].toInt());
+        QString cls = image[QLatin1String("__class__")].toString();
+        QString type = image[QLatin1String("__type__")].toString();
+        bool normalized =
+            image[QLatin1String("__normalized__")].toBool();
+        int numChannels =
+            image[QLatin1String("__channels__")].toInt();
+
+        Q_ASSERT(type == QLatin1String("uint8"));
+        Q_ASSERT(normalized == true);
+
+        QByteArray dataArray =
+            image[QLatin1String("__data__")].toByteArray();
+
+        ApiTexture tex;
+        tex.setSize(size);
+        tex.setNumChannels(numChannels);
+        tex.setLabel(itr.key());
+        tex.contentsFromBase64(dataArray);
+
+        m_textures.append(tex);
     }
 
     QVariantMap fbos =
@@ -627,6 +749,10 @@ ApiTraceCall::ApiTraceCall(const Trace::Call *call)
         VariantVisitor argVisitor;
         call->args[i]->visit(argVisitor);
         m_argValues += argVisitor.variant();
+        if (m_argValues[i].type() == QVariant::ByteArray) {
+            m_hasBinaryData = true;
+            m_binaryDataIndex = i;
+        }
     }
 }
 
@@ -672,10 +798,14 @@ QVariantList ApiTraceCall::originalValues() const
 
 void ApiTraceCall::setEditedValues(const QVariantList &lst)
 {
-    ApiTrace *trace = 0;
-    if (m_parentFrame)
-        trace = m_parentFrame->parentTrace();
+    ApiTrace *trace = parentTrace();
+
     m_editedValues = lst;
+    //lets regenerate data
+    m_richText = QString();
+    m_filterText = QString();
+    delete m_staticText;
+    m_staticText = 0;
 
     if (trace) {
         if (!lst.isEmpty()) {
@@ -742,3 +872,66 @@ unsigned long long ApiPointer::value() const
     return m_value;
 }
 
+bool ApiTraceCall::hasError() const
+{
+    return !m_error.isEmpty();
+}
+
+QString ApiTraceCall::error() const
+{
+    return m_error;
+}
+
+void ApiTraceCall::setError(const QString &msg)
+{
+    if (m_error != msg) {
+        ApiTrace *trace = parentTrace();
+        m_error = msg;
+        m_richText = QString();
+        if (trace)
+            trace->callError(this);
+    }
+}
+
+ApiTrace * ApiTraceCall::parentTrace() const
+{
+    if (m_parentFrame)
+        return m_parentFrame->parentTrace();
+    return NULL;
+}
+
+void ApiTraceFrame::addCall(ApiTraceCall *call)
+{
+    m_calls.append(call);
+    if (call->hasBinaryData()) {
+        QByteArray data =
+            call->arguments()[call->binaryDataIndex()].toByteArray();
+        m_binaryDataSize += data.size();
+    }
+}
+
+QList<ApiTraceCall*> ApiTraceFrame::calls() const
+{
+    return m_calls;
+}
+
+ApiTraceCall * ApiTraceFrame::call(int idx) const
+{
+    return m_calls.value(idx);
+}
+
+int ApiTraceFrame::callIndex(ApiTraceCall *call) const
+{
+    return m_calls.indexOf(call);
+}
+
+bool ApiTraceFrame::isEmpty() const
+{
+    return m_calls.isEmpty();
+}
+
+int ApiTraceFrame::binaryDataSize() const
+{
+    return m_binaryDataSize;
+}
+