diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index a03975759..4c4caf026 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -1331,8 +1331,6 @@ public: m_BuiltinFilters[lit(STRINGIZE(filter_name))].description = \ filterDescription_##filter_name().trimmed(); - MAKE_BUILTIN_FILTER(any); - MAKE_BUILTIN_FILTER(all); MAKE_BUILTIN_FILTER(regex); MAKE_BUILTIN_FILTER(param); // MAKE_BUILTIN_FILTER(event); @@ -1559,81 +1557,6 @@ private: return ret; } - QString filterDescription_any() const - { - return tr(R"EOD( -$any(...) - passes if any of its terms passes - -This filter can be used to nest terms, by saying "if any of these terms passes, -the overall filter will pass". A term can be a literal value or another function. - -See also: $all() -)EOD", - "EventFilterModel"); - } - - IEventBrowser::EventFilterCallback filterFunction_any(QString name, QString parameters, - ParseTrace &trace) - { - // $any(...) => returns true if any of the nested subexpressions are true (with exclusions - // treated as normal). - rdcarray filters; - trace = ParseExpressionToFilters(parameters, filters); - - if(!trace.hasErrors()) - { - if(filters.isEmpty()) - { - trace.setError(tr("No filters provided to $any()", "EventFilterModel")); - return NULL; - } - - return [filters](ICaptureContext *ctx, const rdcstr &, const rdcstr &, uint32_t eid, - const SDChunk *chunk, const DrawcallDescription *draw, const rdcstr &name) { - return EvaluateFilterSet(*ctx, filters, false, eid, chunk, draw, name); - }; - } - - return NULL; - } - - QString filterDescription_all() const - { - return tr(R"EOD( -$all(...) - passes if all of its terms passes - -This filter can be used to nest terms, by saying "if all of these terms passes, -the overall filter will pass". A term can be a literal value or another function. - -See also: $any() -)EOD", - "EventFilterModel"); - } - - IEventBrowser::EventFilterCallback filterFunction_all(QString name, QString parameters, - ParseTrace &trace) - { - // $all(...) => returns true only if all the nested subexpressions are true - rdcarray filters; - trace = ParseExpressionToFilters(parameters, filters); - - if(!trace.hasErrors()) - { - if(filters.isEmpty()) - { - trace.setError(tr("No filters provided to $all()", "EventFilterModel")); - return NULL; - } - - return [filters](ICaptureContext *ctx, const rdcstr &, const rdcstr &, uint32_t eid, - const SDChunk *chunk, const DrawcallDescription *draw, const rdcstr &name) { - return EvaluateFilterSet(*ctx, filters, true, eid, chunk, draw, name); - }; - } - - return NULL; - } - QString filterDescription_regex() const { return tr(R"EOD( @@ -2621,6 +2544,8 @@ ParseTrace EventFilterModel::ParseExpressionToFilters(QString expr, rdcarray " -> quoted_expression -> wait until unescaped " -> start // 3) start -> $ -> function_expression -> parse name > optional whitespace -> // ( -> wait until matching parenthesis -> ) -> whitespace -> start - // 4) start -> anything else -> literal -> wait until whitespace -> start + // 4) start -> ( -> nested_expression -> wait until matching unquoted ) -> start + // 5) start -> anything else -> literal -> wait until whitespace -> start // // We also have two modifiers: // - // 5) start -> + -> note that next filter is a MustMatch -> start - // 6) start -> - -> note that next filter is a CantMatch -> start + // 6) start -> + -> note that next filter is a MustMatch -> start + // 7) start -> - -> note that next filter is a CantMatch -> start // // any non-existant edge is a parse error @@ -2646,6 +2572,7 @@ ParseTrace EventFilterModel::ParseExpressionToFilters(QString expr, rdcarray subFilters; + ParseTrace subTrace = ParseExpressionToFilters(params, subFilters); + + if(subTrace.hasErrors()) + { + // if the errors returned some sub-range for the errors, the position will be + // relative to the params string so rebase it. + if(subTrace.position > 0) + { + subTrace.position += paramStartPos; + } + else + { + // otherwise use the whole parent range which includes the function name + subTrace.position = trace.position; + subTrace.length = trace.length; + } + + return subTrace; + } + else if(subFilters.empty()) + { + trace.length = pos - trace.position + 1; + return trace.setError(tr("Unexpected empty nested expression", "EventFilterModel")); + } + + FilterExpression subexpr; + + subexpr.matchType = matchType; + subexpr.function = true; + subexpr.name = lit("$any$"); + subexpr.params = params; + + subexpr.position = trace.position; + subexpr.length = trace.length; + subexpr.exprs = subTrace.exprs; + + for(FilterExpression &f : subexpr.exprs) + { + f.position += paramStartPos; + } + + trace.exprs.push_back(subexpr); + + auto filter = [subFilters](ICaptureContext *ctx, const rdcstr &, const rdcstr &, + uint32_t eid, const SDChunk *chunk, + const DrawcallDescription *draw, const rdcstr &name) { + return EvaluateFilterSet(*ctx, subFilters, false, eid, chunk, draw, name); + }; + + filters.push_back(EventFilter(filter, matchType)); + + s.clear(); + params.clear(); + nestQuote = false; + + // move back to the start state + state = Start; + matchType = MatchType::Normal; + + // skip the ) + pos++; + continue; + } + } + + params.append(expr[pos]); + + pos++; + continue; + } + if(state == QuotedExpr) { // 2.2) handle escaping @@ -2803,6 +2878,18 @@ ParseTrace EventFilterModel::ParseExpressionToFilters(QString expr, rdcarray 0) + { + return trace.setError(tr("Encountered unterminated parenthesis", "EventFilterModel")); + } + // we should be back in the normal state, because all the other states have termination states if(state == Literal) { @@ -3750,12 +3843,13 @@ void EventBrowser::CreateFilterDialog() m_FilterSettings.FuncDocs->setText(tr(R"EOD( General filter help -Filters are made of a series of matching terms. E.g. a filter such as: +Filters loosely follow a general search-term syntax, by default matching an event +if any of the terms matches. E.g. a filter such as: Draw Clear Copy would match each string against event names, and include any event that matches -any of the above. +"Draw" OR "Clear" OR "Copy". You can also exclude matches with - such as: @@ -3768,18 +3862,25 @@ You can also require matches, which overrides any optional matches: +Draw +Indexed -Instanced -which will match only events which match Draw and match Indexed but don't match -Instanced. In this case adding a term with no + or - prefix will be ignored, +which will match only events which match "Draw" and match "Indexed" but don't match +"Instanced". In this case adding a term with no + or - prefix will be ignored, since an event will be excluded if it doesn't match all the +required terms anyway. +More complex expressions can be built with nesting: + ++Draw +(Indexed Instanced) -Indirect + +Would match only events matching "Draw" which also match at least one of "Indexed" +or "Instanced" but not "Indirect". + Finally you can use filter functions for more advanced matching than just strings. These are documented on the left here, but for example $draw(numIndices > 1000) Indexed -will include any drawcall that matches 'Indexed' as a plain string match, and -also renders more than 1000 indices. +will include any drawcall that matches "Indexed" as a plain string match, OR +renders more than 1000 indices. )EOD").trimmed()); } else if(f == lit("Literal String")) @@ -3962,8 +4063,7 @@ void EventBrowser::settings_filterApply() AddFilterSelections(m_FilterSettings.Filter->textCursor(), idx, m_FilterSettings.Filter->palette().color(QPalette::Base), trace.exprs, sels); m_FilterSettings.Explanation->clear(); - AddFilterExplanations(QString(), m_FilterSettings.Explanation->invisibleRootItem(), trace.exprs, - notesText); + AddFilterExplanations(m_FilterSettings.Explanation->invisibleRootItem(), trace.exprs, notesText); if(trace.exprs.isEmpty()) { @@ -4143,15 +4243,9 @@ void EventBrowser::filter_apply() ui->events->setCurrentIndex(m_FilterModel->mapFromSource(m_Model->GetIndexForEID(curSelEvent))); } -void EventBrowser::AddFilterExplanations(QString parentFunc, RDTreeWidgetItem *root, - QVector exprs, QString ¬es) +void EventBrowser::AddFilterExplanations(RDTreeWidgetItem *root, QVector exprs, + QString ¬es) { - bool any = false, all = false; - if(parentFunc == lit("any") || parentFunc == QString()) - any = true; - else if(parentFunc == lit("all")) - all = true; - // sort by match type std::sort(exprs.begin(), exprs.end(), [](const FilterExpression &a, const FilterExpression &b) { return a.matchType < b.matchType; @@ -4187,7 +4281,7 @@ void EventBrowser::AddFilterExplanations(QString parentFunc, RDTreeWidgetItem *r "precedence.\n" "There is no sorting amongst matches based on optional keywords, so consider making " "them +required to require all of them, or nesting in " - "+$any() to require at least one of them.\n") + "+( .. ) to require at least one of them.\n") .arg(ignored) .arg(must.printName()); } @@ -4214,7 +4308,7 @@ void EventBrowser::AddFilterExplanations(QString parentFunc, RDTreeWidgetItem *r continue; if(!first) - explanation = any ? tr("Or: ") : tr("And: "); + explanation = tr("Or: "); first = false; } @@ -4223,11 +4317,7 @@ void EventBrowser::AddFilterExplanations(QString parentFunc, RDTreeWidgetItem *r { explanation += tr("Name matches '%1'").arg(f.name); } - else if(f.name == lit("all")) - { - explanation += tr("All of..."); - } - else if(f.name == lit("any")) + else if(f.name == lit("$any$")) { explanation += tr("Any of..."); } @@ -4244,7 +4334,7 @@ void EventBrowser::AddFilterExplanations(QString parentFunc, RDTreeWidgetItem *r root->addChild(item); - AddFilterExplanations(f.name, item, f.exprs, notes); + AddFilterExplanations(item, f.exprs, notes); } } diff --git a/qrenderdoc/Windows/EventBrowser.h b/qrenderdoc/Windows/EventBrowser.h index 072fdd0a7..c13b672bd 100644 --- a/qrenderdoc/Windows/EventBrowser.h +++ b/qrenderdoc/Windows/EventBrowser.h @@ -77,6 +77,8 @@ struct FilterExpression QString printName() const { + if(name == lit("$any$")) + return lit("(...)"); if(function) return QFormatStr("$%1(%2)").arg(name).arg(params); return name; @@ -199,8 +201,7 @@ private: void ShowSavedFilterCompleter(RDTextEdit *filter); void CreateFilterDialog(); - void AddFilterExplanations(QString parentFunc, RDTreeWidgetItem *root, - QVector exprs, QString ¬es); + void AddFilterExplanations(RDTreeWidgetItem *root, QVector exprs, QString ¬es); QString GetExportString(int indent, bool firstchild, const QModelIndex &idx); void GetMaxNameLength(int &maxNameLength, int indent, bool firstchild, const QModelIndex &idx);