diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index c88a50bbe..3de049983 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -194,9 +194,49 @@ protected: DECLARE_REFLECTION_STRUCT(IMainWindow); -DOCUMENT("The event browser window."); +DOCUMENT(R"(The event browser window. + +.. function:: EventFilterCallback(context, filter, params, eventId, chunk, draw, name) + + Not a member function - the signature for any ``EventFilterCallback`` callbacks. + + Called for each event in a capture when performing filtering in the Event Browser. The associated + ``FilterParseCallback`` will be called first to parse the parameters, and is available for caching + or syntax checking. The same filter name and params string will be passed to this function. + + :param CaptureContext context: The current capture context. + :param str filter: The name of the filter function. + :param str params: The parameters to the filter function. + :param int eventId: The event's :data:`eventId `. + :param renderdoc.SDChunk chunk: The structured data chunk for this event. + :param renderdoc.DrawcallDescription draw: The drawcall that contains this event. If the event is + the draw itself then the event ID will be equal. + :param str name: The name of the event as shown in the event browser, for string-based filtering. + :return: Whether or not this event matches the filter + :rtype: bool + +.. function:: FilterParseCallback(context, filter, params) + + Not a member function - the signature for any ``FilterParseCallback`` callbacks. + + Called once when the filter changes, to allow parsing any any data caching, as well as reporting + of errors in the filter usage. + + :param CaptureContext context: The current capture context. + :param str filter: The name of the filter function. + :param str params: The parameters to the filter function. + :return: An empty string if the parse succeeded, otherwise any error messages to be displayed to + the user, such as syntax or other errors. + :rtype: str +)"); struct IEventBrowser { + typedef std::function + EventFilterCallback; + + typedef std::function FilterParseCallback; + DOCUMENT(R"(Retrieves the PySide2 QWidget for this :class:`EventBrowser` if PySide2 is available, or otherwise returns a unique opaque pointer that can be passed back to any RenderDoc functions expecting a QWidget. @@ -234,6 +274,37 @@ If no capture is loaded or the EID doesn't correspond to a known event, ``None`` )"); virtual const DrawcallDescription *GetDrawcallForEID(uint32_t eventId) = 0; + DOCUMENT(R"(Registers a new event browser filter function. + +Filter functions are available as $name() so long as they don't shadow an existing function. The +filter callback will be called for each event to filter. + +The parser callback will be called once when a filter is first specified or the parameters change. +Note that a filter can be used multiple times in a filter expression! For this reason the parser +may be called multiple times and the filter callback takes the parameters string. If any expensive +work is done then the parameters can be used as a cache key to cache any data once per filter +expression. + +:param str name: The name of the filter function. +:param EventFilterCallback filter: The callback to call for each candidate event to perform + filtering. +:param FilterParseCallback parser: The callback to call when the parsing the parameters and checking + for any errors. +:return: Whether or not the registration was successful. +:rtype: bool +)"); + virtual bool RegisterEventFilterFunction(const rdcstr &name, EventFilterCallback filter, + FilterParseCallback parser) = 0; + + DOCUMENT(R"(Unregisters an event browser filter function that was previously registered. + +:param str name: The name of the filter function. + +:return: Whether or not the unregistration was successful. +:rtype: bool +)"); + virtual bool UnregisterEventFilterFunction(const rdcstr &name) = 0; + protected: IEventBrowser() = default; ~IEventBrowser() = default; diff --git a/qrenderdoc/Code/pyrenderdoc/function_conversion.h b/qrenderdoc/Code/pyrenderdoc/function_conversion.h index b06d80272..c64d5c547 100644 --- a/qrenderdoc/Code/pyrenderdoc/function_conversion.h +++ b/qrenderdoc/Code/pyrenderdoc/function_conversion.h @@ -251,11 +251,14 @@ struct varfunc PyObject *result = PyObject_Call(func, args, 0); - if(result == NULL) - HandleCallbackFailure(global_handle, exHandle); - Py_DECREF(args); + if(result == NULL) + { + HandleCallbackFailure(global_handle, exHandle); + return rettype(); + } + return get_return(funcname, result, global_handle, exHandle); } diff --git a/qrenderdoc/Code/pyrenderdoc/pyconversion.h b/qrenderdoc/Code/pyrenderdoc/pyconversion.h index 2372ea146..0bd5e1540 100644 --- a/qrenderdoc/Code/pyrenderdoc/pyconversion.h +++ b/qrenderdoc/Code/pyrenderdoc/pyconversion.h @@ -103,7 +103,7 @@ struct TypeConversion if(cached_type_info) return cached_type_info; - rdcstr baseTypeName = TypeName(); + rdcstr baseTypeName = TypeName::type>(); baseTypeName += " *"; cached_type_info = SWIG_TypeQuery(baseTypeName.c_str()); @@ -124,7 +124,7 @@ struct TypeConversion return res; } - static PyObject *ConvertToPy(const Opaque *&in) + static PyObject *ConvertToPy(const Opaque *in) { swig_type_info *type_info = GetTypeInfo(); if(type_info == NULL) @@ -132,8 +132,6 @@ struct TypeConversion return SWIG_InternalNewPointerObj((void *)in, type_info, 0); } - - static PyObject *ConvertToPy(Opaque *in) { return ConvertToPy((const Opaque *&)in); } }; // specialisations for basic types diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index a7bee0455..eef620695 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -860,9 +860,6 @@ private: friend struct EventFilterModel; }; -using EventFilterCallback = - std::function; - struct EventFilter { enum MatchType @@ -872,16 +869,17 @@ struct EventFilter CantMatch }; - EventFilter(EventFilterCallback c) : callback(c), type(Normal) {} - EventFilter(EventFilterCallback c, MatchType t) : callback(c), type(t) {} - EventFilterCallback callback; + EventFilter(IEventBrowser::EventFilterCallback c) : callback(c), type(Normal) {} + EventFilter(IEventBrowser::EventFilterCallback c, MatchType t) : callback(c), type(t) {} + IEventBrowser::EventFilterCallback callback; MatchType type; }; static QMap DrawFlagsLookup; -bool EvaluateFilterSet(const rdcarray &filters, bool all, uint32_t eid, - const SDChunk *chunk, const DrawcallDescription *draw, QString name) +bool EvaluateFilterSet(ICaptureContext &ctx, const rdcarray &filters, bool all, + uint32_t eid, const SDChunk *chunk, const DrawcallDescription *draw, + QString name) { if(filters.empty()) return true; @@ -906,7 +904,7 @@ bool EvaluateFilterSet(const rdcarray &filters, bool all, uint32_t if(accept && !all && filter.type == EventFilter::Normal) continue; - bool match = filter.callback(eid, chunk, draw, name); + bool match = filter.callback(&ctx, rdcstr(), rdcstr(), eid, chunk, draw, name); // in normal mode, if it matches it should be included (unless a subsequent filter excludes // it) @@ -957,15 +955,7 @@ static const SDObject *FindChildRecursively(const SDObject *parent, rdcstr name) struct EventFilterModel : public QSortFilterProxyModel { public: - EventFilterModel(ICaptureContext &ctx) : m_Ctx(ctx) - { - // default filter, include draws that aren't pop markers - m_Filters.push_back( - EventFilter([](uint32_t, const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { - return (draw && !(draw->flags & DrawFlags::PopMarker)); - })); - } - + EventFilterModel(ICaptureContext &ctx) : m_Ctx(ctx) {} void ResetCache() { m_VisibleCache.clear(); } QString ParseExpression(QString expr) { @@ -978,6 +968,27 @@ public: return ret; } + static bool RegisterEventFilterFunction(const rdcstr &name, + IEventBrowser::EventFilterCallback filter, + IEventBrowser::FilterParseCallback parser) + { + if(m_CustomFilters[name].first != NULL) + { + qCritical() << "Registering filter function" << QString(name) + << "which is already registered."; + return false; + } + + m_CustomFilters[name] = qMakePair(filter, parser); + return true; + } + + static bool UnregisterEventFilterFunction(const rdcstr &name) + { + m_CustomFilters.remove(name); + return true; + } + protected: virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { @@ -1011,10 +1022,11 @@ protected: const SDChunk *chunk = (const SDChunk *)(sourceModel()->data(source_idx, ROLE_CHUNK).toULongLong()); const DrawcallDescription *draw = - (const DrawcallDescription *)(sourceModel()->data(source_idx, ROLE_EXACT_DRAWCALL).toULongLong()); + (const DrawcallDescription + *)(sourceModel()->data(source_idx, ROLE_GROUPED_DRAWCALL).toULongLong()); QString name = source_idx.data(Qt::DisplayRole).toString(); - m_VisibleCache[eid] = EvaluateFilterSet(m_Filters, false, eid, chunk, draw, name) ? 1 : -1; + m_VisibleCache[eid] = EvaluateFilterSet(m_Ctx, m_Filters, false, eid, chunk, draw, name); return m_VisibleCache[eid] > 0; } @@ -1028,15 +1040,17 @@ private: rdcarray m_Filters; - EventFilterCallback MakeLiteralMatcher(QString string) + IEventBrowser::EventFilterCallback MakeLiteralMatcher(QString string) { QString matchString = string.toLower(); - return [matchString](uint32_t, const SDChunk *, const DrawcallDescription *, const rdcstr &name) { + return [matchString](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t, + const SDChunk *, const DrawcallDescription *, const rdcstr &name) { return QString(name).toLower().contains(matchString); }; } - EventFilterCallback MakeFunctionMatcher(QString name, QString parameters, QString &errors) + IEventBrowser::EventFilterCallback MakeFunctionMatcher(QString name, QString parameters, + QString &errors) { // builtins if(name == lit("any")) @@ -1054,9 +1068,9 @@ private: return NULL; } - return [filters](uint32_t eid, const SDChunk *chunk, const DrawcallDescription *draw, - const rdcstr &name) { - return EvaluateFilterSet(filters, false, eid, chunk, draw, name); + 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); }; } @@ -1076,9 +1090,9 @@ private: return NULL; } - return [filters](uint32_t eid, const SDChunk *chunk, const DrawcallDescription *draw, - const rdcstr &name) { - return EvaluateFilterSet(filters, true, eid, chunk, draw, name); + 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); }; } @@ -1098,10 +1112,9 @@ private: QString paramName = parameters.mid(0, idx).trimmed(); QString paramValue = parameters.mid(idx + 1).trimmed(); - ICaptureContext *ctx = &m_Ctx; - - return [paramName, paramValue, ctx](uint32_t, const SDChunk *chunk, - const DrawcallDescription *, const rdcstr &) { + return [paramName, paramValue](ICaptureContext *ctx, const rdcstr &, const rdcstr &, uint32_t, + const SDChunk *chunk, const DrawcallDescription *, + const rdcstr &) { const SDObject *o = FindChildRecursively(chunk, paramName); if(!o) @@ -1169,22 +1182,28 @@ private: switch(operators.indexOf(params[1])) { case 0: - return [eid](uint32_t eventId, const SDChunk *, const DrawcallDescription *draw, + return [eid](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { return eventId == eid; }; case 1: - return [eid](uint32_t eventId, const SDChunk *, const DrawcallDescription *draw, + return [eid](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { return eventId != eid; }; case 2: - return [eid](uint32_t eventId, const SDChunk *, const DrawcallDescription *draw, + return [eid](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { return eventId < eid; }; case 3: - return [eid](uint32_t eventId, const SDChunk *, const DrawcallDescription *draw, + return [eid](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { return eventId > eid; }; case 4: - return [eid](uint32_t eventId, const SDChunk *, const DrawcallDescription *draw, + return [eid](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { return eventId <= eid; }; case 5: - return [eid](uint32_t eventId, const SDChunk *, const DrawcallDescription *draw, + return [eid](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { return eventId >= eid; }; default: errors = tr("Internal error, unexpected operator %1", "EventFilterModel").arg(params[1]); @@ -1198,9 +1217,9 @@ private: // no parameters, just return if it's a draw if(params.isEmpty()) - return [](uint32_t, const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { - return draw != NULL; - }; + return [](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, + const rdcstr &) { return draw->eventId == eventId; }; // we upcast to int64_t so we can compare both unsigned and signed values without losing any // precision (we don't have any uint64_ts to compare) @@ -1278,23 +1297,41 @@ private: switch(operators.indexOf(params[1])) { case 0: - return [propGetter, value](uint32_t, const SDChunk *, const DrawcallDescription *draw, - const rdcstr &) { return draw && propGetter(draw) == value; }; + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && propGetter(draw) == value; + }; case 1: - return [propGetter, value](uint32_t, const SDChunk *, const DrawcallDescription *draw, - const rdcstr &) { return draw && propGetter(draw) != value; }; + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && propGetter(draw) != value; + }; case 2: - return [propGetter, value](uint32_t, const SDChunk *, const DrawcallDescription *draw, - const rdcstr &) { return draw && propGetter(draw) < value; }; + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && propGetter(draw) < value; + }; case 3: - return [propGetter, value](uint32_t, const SDChunk *, const DrawcallDescription *draw, - const rdcstr &) { return draw && propGetter(draw) > value; }; + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && propGetter(draw) > value; + }; case 4: - return [propGetter, value](uint32_t, const SDChunk *, const DrawcallDescription *draw, - const rdcstr &) { return draw && propGetter(draw) <= value; }; + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && propGetter(draw) <= value; + }; case 5: - return [propGetter, value](uint32_t, const SDChunk *, const DrawcallDescription *draw, - const rdcstr &) { return draw && propGetter(draw) >= value; }; + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && propGetter(draw) >= value; + }; default: errors = tr("Internal error, unexpected operator %1", "EventFilterModel").arg(params[1]); return NULL; @@ -1355,9 +1392,9 @@ private: flags |= it.value(); } - return [flags](uint32_t, const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { - return draw && (draw->flags & flags); - }; + return [flags](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, + const rdcstr &) { return draw->eventId == eventId && (draw->flags & flags); }; } else { @@ -1367,6 +1404,25 @@ private: } } + if(m_CustomFilters.contains(name)) + { + IEventBrowser::EventFilterCallback innerFilter = m_CustomFilters[name].first; + IEventBrowser::FilterParseCallback innerParser = m_CustomFilters[name].second; + + rdcstr n = name; + rdcstr p = parameters; + + errors = innerParser(&m_Ctx, n, p); + if(!errors.isEmpty()) + return NULL; + + return [n, p, innerFilter](ICaptureContext *ctx, const rdcstr &, const rdcstr &, uint32_t eid, + const SDChunk *chunk, const DrawcallDescription *draw, + const rdcstr &name) { + return innerFilter(ctx, n, p, eid, chunk, draw, name); + }; + } + errors = tr("Unknown filter function $%1", "EventFilterModel").arg(name); return NULL; } @@ -1611,7 +1667,7 @@ private: .arg(expr[pos + 1]); QString errors; - EventFilterCallback filter = MakeFunctionMatcher(s, params, errors); + IEventBrowser::EventFilterCallback filter = MakeFunctionMatcher(s, params, errors); if(!filter) { @@ -1673,8 +1729,16 @@ private: return errorString; } + + // static so we don't lose this when the event browser is closed and the model is deleted. We + // could store this in the capture context but it makes more sense logically to keep it here. + static QMap> + m_CustomFilters; }; +QMap> + EventFilterModel::m_CustomFilters; + static bool textEditControl(QWidget *sender) { if(qobject_cast(sender) || qobject_cast(sender) || @@ -1828,6 +1892,13 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent) } OnCaptureClosed(); + + // set default filter, include only draws that aren't pop markers + ui->filterExpression->setText(lit("$draw() -$draw(flags & PopMarker)")); + + if(m_FilterTimeout->isActive()) + m_FilterTimeout->stop(); + filter_apply(); m_redPalette = palette(); @@ -2610,3 +2681,14 @@ const DrawcallDescription *EventBrowser::GetDrawcallForEID(uint32_t eid) { return m_Model->GetDrawcallForEID(eid); } + +bool EventBrowser::RegisterEventFilterFunction(const rdcstr &name, EventFilterCallback filter, + FilterParseCallback parser) +{ + return EventFilterModel::RegisterEventFilterFunction(name, filter, parser); +} + +bool EventBrowser::UnregisterEventFilterFunction(const rdcstr &name) +{ + return EventFilterModel::UnregisterEventFilterFunction(name); +} diff --git a/qrenderdoc/Windows/EventBrowser.h b/qrenderdoc/Windows/EventBrowser.h index 12801049a..8aa8784f2 100644 --- a/qrenderdoc/Windows/EventBrowser.h +++ b/qrenderdoc/Windows/EventBrowser.h @@ -60,6 +60,9 @@ public: void UpdateDurationColumn() override; APIEvent GetAPIEventForEID(uint32_t eid) override; const DrawcallDescription *GetDrawcallForEID(uint32_t eid) override; + bool RegisterEventFilterFunction(const rdcstr &name, EventFilterCallback filter, + FilterParseCallback parser) override; + bool UnregisterEventFilterFunction(const rdcstr &name) override; // ICaptureViewer void OnCaptureLoaded() override;