X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=gui%2Fapitracecall.cpp;h=6570368e1e13885668b92e05068d78999f00e20d;hb=18081d51fb0a0a5b1b7cb9fa2923339fae58de7b;hp=b964739726f26b974ba23278a55b06946444bc4d;hpb=18eade5a1b8820633210e4127129a8b14450e13f;p=apitrace diff --git a/gui/apitracecall.cpp b/gui/apitracecall.cpp index b964739..6570368 100644 --- a/gui/apitracecall.cpp +++ b/gui/apitracecall.cpp @@ -1,10 +1,50 @@ #include "apitracecall.h" +#include "apitrace.h" #include "trace_model.hpp" #include - -ApiPointer::ApiPointer(int val) +#include +#include +#define QT_USE_FAST_OPERATOR_PLUS +#include +#include + +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) { } @@ -17,11 +57,76 @@ 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("
\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("<"); + } else if (plain[i] == QLatin1Char('>')) { + rich += QLatin1String(">"); + } else if (plain[i] == QLatin1Char('&')) { + rich += QLatin1String("&"); + } 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()); } + if (variant.userType() == QVariant::ByteArray) { + if (variant.toByteArray().size() < 1024) { + int bytes = variant.toByteArray().size(); + return QObject::tr("[binary data, size = %1 bytes]").arg(bytes); + } else { + float kb = variant.toByteArray().size()/1024.; + return QObject::tr("[binary data, size = %1 kb]").arg(kb); + } + } + + if (variant.userType() == QVariant::String) { + return plainTextToHTML(variant.toString(), multiLine); + } if (variant.userType() < QVariant::UserType) { return variant.toString(); @@ -39,6 +144,9 @@ QString apiVariantToString(const QVariant &variant) if (variant.canConvert()) { return variant.value().toString(); } + if (variant.canConvert()) { + return variant.value().toString(); + } return QString(); } @@ -105,9 +213,9 @@ QString ApiStruct::toString() const str += QLatin1String("{"); for (unsigned i = 0; i < m_members.count(); ++i) { - str += m_sig.memberNames[i]; - str += QLatin1String(" = "); - str += apiVariantToString(m_members[i]); + str += m_sig.memberNames[i] % + QLatin1Literal(" = ") % + apiVariantToString(m_members[i]); if (i < m_members.count() - 1) str += QLatin1String(", "); } @@ -133,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) @@ -163,7 +271,13 @@ void VariantVisitor::visit(Trace::String *node) void VariantVisitor::visit(Trace::Enum *e) { - m_variant = QVariant(QString::fromStdString(e->sig->first)); + VariantVisitor vis; + e->sig->second->visit(vis); + + QVariant val = vis.variant(); + + m_variant = QVariant::fromValue( + ApiEnum(QString::fromStdString(e->sig->first), val)); } void VariantVisitor::visit(Trace::Bitmask *bitmask) @@ -183,6 +297,14 @@ void VariantVisitor::visit(Trace::Array *array) void VariantVisitor::visit(Trace::Blob *blob) { + //XXX + //FIXME: this is a nasty hack. Trace::Blob's can't + // delete the contents in the destructor because + // the data is being used by other calls. We piggy back + // on that assumption and don't deep copy the data. If + // Blob's will start deleting the data we will need to + // start deep copying it or switch to using something like + // Boost's shared_ptr or Qt's QSharedPointer to handle it QByteArray barray = QByteArray::fromRawData(blob->buf, blob->size); m_variant = QVariant(barray); } @@ -197,6 +319,11 @@ ApiArray::ApiArray(const Trace::Array *arr) init(arr); } +ApiArray::ApiArray(const QList &vals) + : m_array(vals) +{ +} + QString ApiArray::toString() const { QString str; @@ -226,40 +353,585 @@ void ApiArray::init(const Trace::Array *arr) } QStaticText ApiTraceCall::staticText() const +{ + if (m_staticText && !m_staticText->text().isEmpty()) + return *m_staticText; + + QVariantList argValues = arguments(); + + QString richText = QString::fromLatin1( + "%1(").arg(m_name); + for (int i = 0; i < m_argNames.count(); ++i) { + richText += QLatin1String(""); + QString argText = apiVariantToString(argValues[i]); + + //if arguments are really long (e.g. shader text), cut them + // and elide it + if (argText.length() > 40) { + QString shortened = argText.mid(0, 40); + shortened[argText.length() - 5] = '.'; + shortened[argText.length() - 4] = '.'; + shortened[argText.length() - 3] = '.'; + shortened[argText.length() - 2] = argText[argText.length() - 2]; + shortened[argText.length() - 1] = argText[argText.length() - 1]; + richText += shortened; + } else { + richText += argText; + } + richText += QLatin1String(""); + if (i < m_argNames.count() - 1) + richText += QLatin1String(", "); + } + richText += QLatin1String(")"); + if (m_returnValue.isValid()) { + richText += + QLatin1Literal(" = ") % + QLatin1Literal("") % + apiVariantToString(m_returnValue) % + QLatin1Literal(""); + } + + if (!m_staticText) + m_staticText = new QStaticText(richText); + else + m_staticText->setText(richText); + QTextOption opt; + opt.setWrapMode(QTextOption::NoWrap); + m_staticText->setTextOption(opt); + m_staticText->prepare(); + + return *m_staticText; +} + +QString ApiTraceCall::toHtml() const { if (!m_richText.isEmpty()) - return m_staticText; - - m_richText = QString::fromLatin1("%1(").arg(name); - for (int i = 0; i < argNames.count(); ++i) { - m_richText += argNames[i]; - m_richText += QString::fromLatin1(" = "); - m_richText += QLatin1String(""); - m_richText += apiVariantToString(argValues[i]); - m_richText += QLatin1String(""); - if (i < argNames.count() - 1) - m_richText += QString::fromLatin1(", "); + return m_richText; + + m_richText = QLatin1String("
"); + + if (m_helpUrl.isEmpty()) { + m_richText += QString::fromLatin1( + "%1) %2(") + .arg(m_index) + .arg(m_name); + } else { + m_richText += QString::fromLatin1( + "%1) %3(") + .arg(m_index) + .arg(m_helpUrl.toString()) + .arg(m_name); + } + + QVariantList argValues = arguments(); + for (int i = 0; i < m_argNames.count(); ++i) { + m_richText += + QLatin1String("") + + m_argNames[i] + + QLatin1String("") + + QLatin1Literal(" = ") + + QLatin1Literal("") + + apiVariantToString(argValues[i], true) + + QLatin1Literal(""); + if (i < m_argNames.count() - 1) + m_richText += QLatin1String(", "); } m_richText += QLatin1String(")"); - if (returnValue.isValid()) { - m_richText += QLatin1String(" = "); - m_richText += QLatin1String(""); - m_richText += apiVariantToString(returnValue); - m_richText += QLatin1String(""); + if (m_returnValue.isValid()) { + m_richText += + QLatin1String(" = ") + + QLatin1String("") + + apiVariantToString(m_returnValue, true) + + QLatin1String(""); } + m_richText += QLatin1String("
"); + + if (hasError()) { + QString errorStr = + QString::fromLatin1( + "
%1
") + .arg(m_error); + m_richText += errorStr; + } + + m_richText = + QString::fromLatin1( + "%2") + .arg(styleSheet) + .arg(m_richText); + m_richText.squeeze(); + + //qDebug()<text().isEmpty()) + return *m_staticText; + + QString richText; + + //mark the frame if it uploads more than a meg a frame + if (m_binaryDataSize > (1024*1024)) { + richText = + QObject::tr( + "" + "Frame %1" + "" + "    (%2MB)") + .arg(number) + .arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2); + } else { + richText = + QObject::tr( + "Frame %1") + .arg(number); + } + + if (!m_staticText) + m_staticText = new QStaticText(richText); - m_staticText.setText(m_richText); QTextOption opt; opt.setWrapMode(QTextOption::NoWrap); - m_staticText.setTextOption(opt); - m_staticText.prepare(); + m_staticText->setTextOption(opt); + m_staticText->prepare(); - return m_staticText; + return *m_staticText; } -QString ApiTraceCall::richText() const +int ApiTraceCall::numChildren() const { - staticText(); - return m_richText; + return 0; +} + +int ApiTraceFrame::numChildren() const +{ + return m_calls.count(); +} + +ApiTraceFrame::ApiTraceFrame() + : ApiTraceEvent(ApiTraceEvent::Frame), + m_parentTrace(0), + m_binaryDataSize(0) +{ +} + +ApiTraceCall::ApiTraceCall() + : ApiTraceEvent(ApiTraceEvent::Call), + m_hasBinaryData(false), + m_binaryDataIndex(0) +{ +} + +ApiTraceEvent::ApiTraceEvent() + : m_type(ApiTraceEvent::None), + m_staticText(0) +{ +} + +ApiTraceEvent::ApiTraceEvent(Type t) + : m_type(t), + m_staticText(0) +{ +} + +ApiTraceCall::~ApiTraceCall() +{ +} + +QVariantMap ApiTraceEvent::stateParameters() const +{ + return m_state.parameters(); +} + +ApiTraceState ApiTraceEvent::state() const +{ + return m_state; +} + +void ApiTraceEvent::setState(const ApiTraceState &state) +{ + m_state = state; +} + +bool ApiTraceCall::hasBinaryData() const +{ + return m_hasBinaryData; +} + +int ApiTraceCall::binaryDataIndex() const +{ + return m_binaryDataIndex; +} + +ApiTraceState::ApiTraceState() +{ +} + +ApiTraceState::ApiTraceState(const QVariantMap &parsedJson) +{ + m_parameters = parsedJson[QLatin1String("parameters")].toMap(); + QVariantMap attachedShaders = + parsedJson[QLatin1String("shaders")].toMap(); + QVariantMap::const_iterator itr; + + + for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd(); + ++itr) { + QString type = itr.key(); + QString source = itr.value().toString(); + m_shaderSources[type] = source; + } + + 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 = + parsedJson[QLatin1String("framebuffer")].toMap(); + for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) { + QVariantMap buffer = itr.value().toMap(); + QSize size(buffer[QLatin1String("__width__")].toInt(), + buffer[QLatin1String("__height__")].toInt()); + QString cls = buffer[QLatin1String("__class__")].toString(); + QString type = buffer[QLatin1String("__type__")].toString(); + bool normalized = buffer[QLatin1String("__normalized__")].toBool(); + int numChannels = buffer[QLatin1String("__channels__")].toInt(); + + Q_ASSERT(type == QLatin1String("uint8")); + Q_ASSERT(normalized == true); + + QByteArray dataArray = + buffer[QLatin1String("__data__")].toByteArray(); + + ApiFramebuffer fbo; + fbo.setSize(size); + fbo.setNumChannels(numChannels); + fbo.setType(itr.key()); + fbo.contentsFromBase64(dataArray); + m_framebuffers.append(fbo); + } +} + +QVariantMap ApiTraceState::parameters() const +{ + return m_parameters; +} + +QMap ApiTraceState::shaderSources() const +{ + return m_shaderSources; +} + +bool ApiTraceState::isEmpty() const +{ + return m_parameters.isEmpty(); +} + +QList ApiTraceState::textures() const +{ + return m_textures; +} + +QList ApiTraceState::framebuffers() const +{ + return m_framebuffers; +} + +QList ApiArray::values() const +{ + return m_array; +} + +int ApiTraceCall::index() const +{ + return m_index; +} + +QString ApiTraceCall::name() const +{ + return m_name; +} + +QStringList ApiTraceCall::argNames() const +{ + return m_argNames; +} + +QVariantList ApiTraceCall::arguments() const +{ + if (m_editedValues.isEmpty()) + return m_argValues; + else + return m_editedValues; +} + +QVariant ApiTraceCall::returnValue() const +{ + return m_returnValue; +} + +QUrl ApiTraceCall::helpUrl() const +{ + return m_helpUrl; +} + +ApiTraceCall::ApiTraceCall(const Trace::Call *call) + : ApiTraceEvent(ApiTraceEvent::Call), + m_hasBinaryData(false), + m_binaryDataIndex(0) +{ + m_name = QString::fromStdString(call->sig->name); + m_index = call->no; + + QString argumentsText; + for (int i = 0; i < call->sig->arg_names.size(); ++i) { + m_argNames += + QString::fromStdString(call->sig->arg_names[i]); + } + if (call->ret) { + VariantVisitor retVisitor; + call->ret->visit(retVisitor); + m_returnValue = retVisitor.variant(); + } + for (int i = 0; i < call->args.size(); ++i) { + VariantVisitor argVisitor; + call->args[i]->visit(argVisitor); + m_argValues += argVisitor.variant(); + if (m_argValues[i].type() == QVariant::ByteArray) { + m_hasBinaryData = true; + m_binaryDataIndex = i; + } + } +} + +void ApiTraceCall::setHelpUrl(const QUrl &url) +{ + m_helpUrl = url; +} + +void ApiTraceCall::setParentFrame(ApiTraceFrame *frame) +{ + m_parentFrame = frame; +} + +ApiTraceFrame * ApiTraceCall::parentFrame()const +{ + return m_parentFrame; +} + +ApiTraceEvent::~ApiTraceEvent() +{ + delete m_staticText; +} + +void ApiTraceCall::revert() +{ + setEditedValues(QVariantList()); +} + +ApiTrace * ApiTraceFrame::parentTrace() const +{ + return m_parentTrace; +} + +void ApiTraceFrame::setParentTrace(ApiTrace *trace) +{ + m_parentTrace = trace; +} + +QVariantList ApiTraceCall::originalValues() const +{ + return m_argValues; +} + +void ApiTraceCall::setEditedValues(const QVariantList &lst) +{ + 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()) { + trace->callEdited(this); + } else { + trace->callReverted(this); + } + } +} + +QVariantList ApiTraceCall::editedValues() const +{ + return m_editedValues; +} + +bool ApiTraceCall::edited() const +{ + return !m_editedValues.isEmpty(); +} + +ApiEnum::ApiEnum(const QString &name, const QVariant &val) + : m_name(name), + m_value(val) +{ +} + +QString ApiEnum::toString() const +{ + return m_name; +} + +QVariant ApiEnum::value() const +{ + return m_value; +} + +QString ApiEnum::name() const +{ + return m_name; +} + +unsigned long long ApiBitmask::value() const +{ + return m_value; +} + +ApiBitmask::Signature ApiBitmask::signature() const +{ + return m_sig; +} + +ApiStruct::Signature ApiStruct::signature() const +{ + return m_sig; +} + +QList ApiStruct::values() const +{ + return m_members; } + +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 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; +} +