Allow python to register custom event filters

This commit is contained in:
baldurk
2021-02-25 15:41:55 +00:00
parent 21e52a10e5
commit dcaabf42e9
5 changed files with 223 additions and 66 deletions
+72 -1
View File
@@ -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 <renderdoc.APIEvent.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<bool(ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t,
const SDChunk *, const DrawcallDescription *, const rdcstr &)>
EventFilterCallback;
typedef std::function<rdcstr(ICaptureContext *, const rdcstr &, const rdcstr &)> 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;
@@ -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<rettype>(funcname, result, global_handle, exHandle);
}
+2 -4
View File
@@ -103,7 +103,7 @@ struct TypeConversion<Opaque *, false>
if(cached_type_info)
return cached_type_info;
rdcstr baseTypeName = TypeName<Opaque>();
rdcstr baseTypeName = TypeName<typename std::remove_const<Opaque>::type>();
baseTypeName += " *";
cached_type_info = SWIG_TypeQuery(baseTypeName.c_str());
@@ -124,7 +124,7 @@ struct TypeConversion<Opaque *, false>
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<Opaque *, false>
return SWIG_InternalNewPointerObj((void *)in, type_info, 0);
}
static PyObject *ConvertToPy(Opaque *in) { return ConvertToPy((const Opaque *&)in); }
};
// specialisations for basic types
+140 -58
View File
@@ -860,9 +860,6 @@ private:
friend struct EventFilterModel;
};
using EventFilterCallback =
std::function<bool(uint32_t, const SDChunk *, const DrawcallDescription *, const rdcstr &)>;
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<QString, DrawFlags> DrawFlagsLookup;
bool EvaluateFilterSet(const rdcarray<EventFilter> &filters, bool all, uint32_t eid,
const SDChunk *chunk, const DrawcallDescription *draw, QString name)
bool EvaluateFilterSet(ICaptureContext &ctx, const rdcarray<EventFilter> &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<EventFilter> &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<EventFilter> 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<QString, QPair<IEventBrowser::EventFilterCallback, IEventBrowser::FilterParseCallback>>
m_CustomFilters;
};
QMap<QString, QPair<IEventBrowser::EventFilterCallback, IEventBrowser::FilterParseCallback>>
EventFilterModel::m_CustomFilters;
static bool textEditControl(QWidget *sender)
{
if(qobject_cast<QLineEdit *>(sender) || qobject_cast<QTextEdit *>(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);
}
+3
View File
@@ -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;