mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-20 22:41:36 +00:00
Add annotation filter function to event browser filters
This commit is contained in:
@@ -1501,6 +1501,7 @@ public:
|
||||
MAKE_BUILTIN_FILTER(dispatch);
|
||||
MAKE_BUILTIN_FILTER(childOf);
|
||||
MAKE_BUILTIN_FILTER(parent);
|
||||
MAKE_BUILTIN_FILTER(annot);
|
||||
|
||||
/*
|
||||
m_BuiltinFilters[lit("event")].completer = [this](ICaptureContext *ctx, QString name,
|
||||
@@ -1686,11 +1687,11 @@ private:
|
||||
QString text;
|
||||
};
|
||||
|
||||
static QList<Token> tokenise(QString parameters)
|
||||
static QList<Token> tokenise(QString parameters, QString extraChars = QString())
|
||||
{
|
||||
QList<Token> ret;
|
||||
|
||||
const QString operatorChars = lit("<>=:&");
|
||||
const QString operatorChars = lit("<>=:&") + extraChars;
|
||||
|
||||
int p = 0;
|
||||
while(p < parameters.size())
|
||||
@@ -1883,6 +1884,300 @@ searched for as a case-insensitive substring.
|
||||
};
|
||||
}
|
||||
|
||||
QString filterDescription_annot() const
|
||||
{
|
||||
return tr(R"EOD(
|
||||
<h3>$annot</h3>
|
||||
|
||||
<br />
|
||||
<table>
|
||||
<tr><td><code>$annot(annotation condition)</code><br />
|
||||
<code>$annot(annotation condition)</code></td><td> - passes if a given annotation condition is true.</td></tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
This filter queries the annotations of an event to either exist or match simple conditions. A
|
||||
condition may be omitted, but an annotation must be specified. If no condition is given then
|
||||
the filter passes if that annotation is exists</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If the annotation contains spaces, it should be surrounded with double quotes, e.g. "foo bar.sub.key"
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For numeric annotations you can compare with the normal comparison operators. For string annotations you
|
||||
can check exact equality with ==, use the keyword 'contains' to check for a case-insensitive substring
|
||||
match, or =~ to match with a regex.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Numeric properties will always fail to match the filter when used with a string comparison operator,
|
||||
and vice-versa.
|
||||
</p>
|
||||
)EOD",
|
||||
"EventFilterModel");
|
||||
}
|
||||
|
||||
IEventBrowser::EventFilterCallback filterFunction_annot(QString name, QString parameters,
|
||||
ParseTrace &trace)
|
||||
{
|
||||
parameters = parameters.trimmed();
|
||||
|
||||
rdcstr keyPath;
|
||||
|
||||
if(parameters[0] == QLatin1Char('"'))
|
||||
{
|
||||
int i = 1;
|
||||
while(i < parameters.size() && parameters[i] != QLatin1Char('"'))
|
||||
{
|
||||
if(parameters[i] == QLatin1Char('\\') && parameters[i] == QLatin1Char('"'))
|
||||
{
|
||||
keyPath += parameters[i + 1];
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if(i >= parameters.size())
|
||||
{
|
||||
trace.setError(tr("Invalid quoted annotation, no closing quote found", "EventFilterModel"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
keyPath = parameters.mid(0, i);
|
||||
|
||||
i++;
|
||||
|
||||
parameters = parameters.mid(i).trimmed();
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = parameters.indexOf(QLatin1Char(' '));
|
||||
if(i < 0)
|
||||
{
|
||||
keyPath = parameters;
|
||||
parameters.clear();
|
||||
}
|
||||
keyPath = parameters.mid(0, i);
|
||||
parameters = parameters.mid(i).trimmed();
|
||||
}
|
||||
|
||||
QList<Token> tokens = tokenise(parameters, lit("~"));
|
||||
|
||||
std::function<bool(const SDObject *)> comparer;
|
||||
|
||||
// no parameters, just return if the annotation exists
|
||||
if(tokens.isEmpty())
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if(tokens.size() == 1)
|
||||
{
|
||||
trace.setError(tr("Invalid annotation expression, expected an operator and value",
|
||||
"EventFilterModel"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QString op = tokens[0].text;
|
||||
QString param = parameters.mid(tokens[1].position);
|
||||
|
||||
static const QStringList numericOperators = {
|
||||
lit("="), lit("=="), lit("!="), lit("<"), lit(">"), lit("<="), lit(">="),
|
||||
};
|
||||
|
||||
// any numeric value that can be compared to a number
|
||||
if(numericOperators.contains(op))
|
||||
{
|
||||
int operatorIdx = numericOperators.indexOf(op);
|
||||
|
||||
// need to be a bit polymorphic here as we don't know what we'll be comparing against so we
|
||||
// don't know the type. We at least upcast to the biggest type to reduce variations
|
||||
int64_t s64 = param.toLongLong();
|
||||
uint64_t u64 = param.toULongLong();
|
||||
double d = param.toDouble();
|
||||
|
||||
comparer = [operatorIdx, d, u64, s64, param](const SDObject *obj) {
|
||||
switch(obj->type.basetype)
|
||||
{
|
||||
case SDBasic::Chunk:
|
||||
case SDBasic::Resource:
|
||||
case SDBasic::GPUAddress:
|
||||
case SDBasic::Struct:
|
||||
case SDBasic::Array:
|
||||
case SDBasic::Null:
|
||||
case SDBasic::Buffer:
|
||||
return false;
|
||||
// ambiguity with string operators here, could be an equality check on strings
|
||||
case SDBasic::String:
|
||||
if(operatorIdx <= 1)
|
||||
return QString(obj->data.str) == param;
|
||||
return false;
|
||||
case SDBasic::Boolean:
|
||||
case SDBasic::Enum:
|
||||
case SDBasic::UnsignedInteger:
|
||||
case SDBasic::Character:
|
||||
{
|
||||
switch(operatorIdx)
|
||||
{
|
||||
case 0:
|
||||
case 1: return obj->data.basic.u == u64;
|
||||
case 2: return obj->data.basic.u != u64;
|
||||
case 3: return obj->data.basic.u < u64;
|
||||
case 4: return obj->data.basic.u > u64;
|
||||
case 5: return obj->data.basic.u <= u64;
|
||||
case 6: return obj->data.basic.u >= u64;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
case SDBasic::SignedInteger:
|
||||
{
|
||||
switch(operatorIdx)
|
||||
{
|
||||
case 0:
|
||||
case 1: return obj->data.basic.i == s64;
|
||||
case 2: return obj->data.basic.i != s64;
|
||||
case 3: return obj->data.basic.i < s64;
|
||||
case 4: return obj->data.basic.i > s64;
|
||||
case 5: return obj->data.basic.i <= s64;
|
||||
case 6: return obj->data.basic.i >= s64;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
case SDBasic::Float:
|
||||
{
|
||||
switch(operatorIdx)
|
||||
{
|
||||
case 0:
|
||||
case 1: return obj->data.basic.d == d;
|
||||
case 2: return obj->data.basic.d != d;
|
||||
case 3: return obj->data.basic.d < d;
|
||||
case 4: return obj->data.basic.d > d;
|
||||
case 5: return obj->data.basic.d <= d;
|
||||
case 6: return obj->data.basic.d >= d;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
else if(op == lit("contains") || op == lit("=~"))
|
||||
{
|
||||
bool contains = op == lit("contains");
|
||||
|
||||
QRegularExpression regex;
|
||||
if(!contains)
|
||||
{
|
||||
int end = 0;
|
||||
|
||||
if(param.size() < 2 || param[0] != QLatin1Char('/'))
|
||||
{
|
||||
trace.setError(tr("Parameter to =~ should be /regex/", "EventFilterModel"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(int i = 1; i < param.size(); i++)
|
||||
{
|
||||
if(param[i] == QLatin1Char('\\'))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(param[i] == QLatin1Char('/'))
|
||||
{
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(end == 0)
|
||||
{
|
||||
trace.setError(tr("Unterminated regex", "EventFilterModel"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QString opts = param.right(param.size() - end - 1);
|
||||
|
||||
QRegularExpression::PatternOptions reOpts = QRegularExpression::NoPatternOption;
|
||||
|
||||
for(QChar c : opts)
|
||||
{
|
||||
switch(c.toLatin1())
|
||||
{
|
||||
case 'i': reOpts |= QRegularExpression::CaseInsensitiveOption; break;
|
||||
case 'x': reOpts |= QRegularExpression::ExtendedPatternSyntaxOption; break;
|
||||
case 'u': reOpts |= QRegularExpression::UseUnicodePropertiesOption; break;
|
||||
default:
|
||||
trace.setError(tr("Unexpected option '%1' after regex", "EventFilterModel").arg(c));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
regex = QRegularExpression(param.mid(1, end - 1));
|
||||
|
||||
if(!regex.isValid())
|
||||
{
|
||||
trace.setError(tr("Invalid regex", "EventFilterModel"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
param = param.toLower();
|
||||
|
||||
comparer = [contains, param, regex](const SDObject *obj) {
|
||||
if(obj->type.basetype == SDBasic::String)
|
||||
{
|
||||
if(contains)
|
||||
{
|
||||
QString str = obj->data.str;
|
||||
str = str.toLower();
|
||||
return str.contains(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
QRegularExpressionMatch match = regex.match(QString(obj->data.str));
|
||||
return match.isValid() && match.hasMatch();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
trace.setError(tr("Unrecognised annotation condition", "EventFilterModel"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return [keyPath, comparer](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId,
|
||||
const SDChunk *, const ActionDescription *action, const rdcstr &) {
|
||||
if(action)
|
||||
{
|
||||
for(const APIEvent &e : action->events)
|
||||
{
|
||||
if(e.eventId == eventId)
|
||||
{
|
||||
const SDObject *annot = e.annotations ? e.annotations->FindChildByKeyPath(keyPath) : NULL;
|
||||
|
||||
if(annot == NULL)
|
||||
return false;
|
||||
|
||||
if(comparer)
|
||||
return comparer(annot);
|
||||
else
|
||||
return annot != NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
QString filterDescription_event() const
|
||||
{
|
||||
return tr(R"EOD(
|
||||
|
||||
Reference in New Issue
Block a user