diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index 3dd272ae5..7561f5713 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -1219,6 +1219,7 @@ public: MAKE_BUILTIN_FILTER(param); MAKE_BUILTIN_FILTER(event); MAKE_BUILTIN_FILTER(draw); + MAKE_BUILTIN_FILTER(dispatch); } } void ResetCache() { m_VisibleCache.clear(); } @@ -1852,6 +1853,246 @@ private: } } + IEventBrowser::EventFilterCallback filterFunction_dispatch(QString name, QString parameters, + ParseError &errors) + { + // $dispatch(...) => returns true only for dispatches, optionally with a particular property + // filter + QList tokens = tokenise(parameters); + + // no parameters, just return if it's a draw + if(tokens.isEmpty()) + return [](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch); + }; + + // 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) + using PropGetter = int64_t (*)(const DrawcallDescription *); + + struct NamedProp + { + const char *name; + PropGetter getter; + }; + + static const NamedProp namedProps[] = { + NAMED_PROP("dispatchx", draw->dispatchDimension[0]), + NAMED_PROP("dispatchy", draw->dispatchDimension[1]), + NAMED_PROP("dispatchz", draw->dispatchDimension[2]), + NAMED_PROP("x", draw->dispatchDimension[0]), NAMED_PROP("y", draw->dispatchDimension[1]), + NAMED_PROP("z", draw->dispatchDimension[2]), + NAMED_PROP("dispatchsize", draw->dispatchDimension[0] * draw->dispatchDimension[1] * + draw->dispatchDimension[2]), + NAMED_PROP("size", draw->dispatchDimension[0] * draw->dispatchDimension[1] * + draw->dispatchDimension[2]), + }; + + QByteArray prop = tokens[0].text.toLower().toLatin1(); + + int numericPropIndex = -1; + for(size_t i = 0; i < ARRAY_COUNT(namedProps); i++) + { + if(prop == namedProps[i].name) + numericPropIndex = (int)i; + } + + // any numeric value that can be compared to a number + if(numericPropIndex >= 0) + { + PropGetter propGetter = namedProps[numericPropIndex].getter; + + static const QStringList operators = { + lit("=="), lit("!="), lit("<"), lit(">"), lit("<="), lit(">="), + }; + + int operatorIdx = tokens.size() > 1 ? operators.indexOf(tokens[1].text) : -1; + + if(tokens.size() > 1 && tokens[1].text == lit("=")) + operatorIdx = 0; + + if(tokens.size() != 3 || operatorIdx < 0 || operatorIdx >= operators.size()) + { + errors.setText(tr("Invalid expression, expected single comparison with operators: %3", + "EventFilterModel") + .arg(operators.join(lit(", ")))); + return NULL; + } + + bool ok = false; + int64_t value = tokens[2].text.toLongLong(&ok, 0); + + if(!ok) + { + errors.position = tokens[2].position; + errors.length = tokens[2].length; + errors.setText(tr("Invalid value, expected integer", "EventFilterModel")); + return NULL; + } + + switch(operatorIdx) + { + case 0: + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + propGetter(draw) == value; + }; + case 1: + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + propGetter(draw) != value; + }; + case 2: + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + propGetter(draw) < value; + }; + case 3: + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + propGetter(draw) > value; + }; + case 4: + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + propGetter(draw) <= value; + }; + case 5: + return [propGetter, value](ICaptureContext *, const rdcstr &, const rdcstr &, + uint32_t eventId, const SDChunk *, + const DrawcallDescription *draw, const rdcstr &) { + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + propGetter(draw) >= value; + }; + default: errors.setText(tr("Internal error", "EventFilterModel")); return NULL; + } + } + else if(tokens[0].text.toLower() == lit("duration")) + { + // deliberately don't allow equality/inequality + static const QStringList operators = { + lit("<"), lit(">"), lit("<="), lit(">="), + }; + + int operatorIdx = tokens.size() > 1 ? operators.indexOf(tokens[1].text) : -1; + + if(tokens.size() != 3 || operatorIdx < 0 || operatorIdx >= operators.size()) + { + errors.setText(tr("Invalid expression, expected single comparison with operators: %3", + "EventFilterModel") + .arg(operators.join(lit(", ")))); + return NULL; + } + + // multiplier to change the read value into nanoseconds + double mult = 1.0; + if(tokens[2].text.endsWith(lit("ms"))) + { + mult = 1000000.0; + tokens[2].text.resize(tokens[2].text.size() - 2); + } + else if(tokens[2].text.endsWith(lit("us"))) + { + mult = 1000.0; + tokens[2].text.resize(tokens[2].text.size() - 2); + } + else if(tokens[2].text.endsWith(lit("ns"))) + { + mult = 1.0; + tokens[2].text.resize(tokens[2].text.size() - 2); + } + else if(tokens[2].text.endsWith(lit("s"))) + { + mult = 1000000000.0; + tokens[2].text.resize(tokens[2].text.size() - 1); + } + else + { + errors.position = tokens[2].position; + errors.length = tokens[2].length; + errors.setText( + tr("Duration must be suffixed with one of s, ms, us, or ns", "EventFilterModel")); + return NULL; + } + + bool ok = false; + double value = tokens[2].text.toDouble(&ok); + + // if it doesn't read as a double, try as an integer + if(!ok) + { + int64_t valInt = tokens[2].text.toLongLong(&ok); + + if(ok) + value = double(valInt); + } + + if(!ok) + { + errors.position = tokens[2].position; + errors.length = tokens[2].length; + errors.setText(tr("Invalid value, expected duration suffixed with one of s, ms, us, or ns", + "EventFilterModel")); + return NULL; + } + + int64_t nanoValue = int64_t(value * mult); + + switch(operatorIdx) + { + case 0: + return + [this, nanoValue](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { + double dur = m_Model->GetSecondsDurationForEID(eventId); + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + dur >= 0.0 && int64_t(dur * 1000000000.0) < nanoValue; + }; + case 1: + return + [this, nanoValue](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { + double dur = m_Model->GetSecondsDurationForEID(eventId); + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + dur >= 0.0 && int64_t(dur * 1000000000.0) > nanoValue; + }; + case 2: + return + [this, nanoValue](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { + double dur = m_Model->GetSecondsDurationForEID(eventId); + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + dur >= 0.0 && int64_t(dur * 1000000000.0) <= nanoValue; + }; + case 3: + return + [this, nanoValue](ICaptureContext *, const rdcstr &, const rdcstr &, uint32_t eventId, + const SDChunk *, const DrawcallDescription *draw, const rdcstr &) { + double dur = m_Model->GetSecondsDurationForEID(eventId); + return draw->eventId == eventId && (draw->flags & DrawFlags::Dispatch) && + dur >= 0.0 && int64_t(dur * 1000000000.0) >= nanoValue; + }; + default: errors.setText(tr("Internal error", "EventFilterModel")); return NULL; + } + } + else + { + errors.setText(tr("Unrecognised property expression", "EventFilterModel")); + return NULL; + } + } + IEventBrowser::EventFilterCallback MakeFunctionMatcher(QString name, QString parameters, ParseError &errors) {