Skip to content

Commit

Permalink
qapitrace: Add RENDER and STATE groups
Browse files Browse the repository at this point in the history
Description
-----------
This commit extends Zack Rusin's implementation of glPushDebugGroup/
glPopDebugGroup groups logic to encompass remaining calls into two
additional blocks of calls consisting of

1) Render operations (blocks of calls terminated by a render operation,
                      e.g., glClear, glDraw, glEnd, etc.)
2) State changes

Details
-------
The rule for processing the calls is that every "PushDebugGroup" starts
a push down into a new level in the tree; every "PopDebugGroup" pops up
one level.

Using "group(s)" as an alias for "glPushDebugGroup(s)", then independent
of the groups, every gl Call is collected into a block, or "chunk", of
calls.  If there is no current "chunk" for a call being processed (such
as the beginning of the frame, or the first call of a group), then a new
"chunk" is started. If a RENDER operation occurs, then it closes the
current chunk and gives its name to it. Since this chunk is now closed,
any subsequent call must create a new chunk. When pushing or popping a
group, if there is an open chunk, then it will be implicitly closed with
a name of "State changes". 

The fully expanded tree would expand to every gl call (possibly omitting
the PushDebugGroup and PopDebugGroup; they must necessarily exist for
every group but have no purpose other than to name those groups.)

Comments
--------
Below is a use case example of thinking how this would look. Although
this implementation doesn't completely follow it due to limitations
in utilizing the existing groups logic, it was an easier way to get
something working for feedback without messing too much with the
infrastructure. A more robust implementation would be to add a new
class that would be layered as a list of intermediary objects between
the Frame and its Calls. The intermediary objects would then have their
lists of the Frame's Call objects.

In this implementation, the group name displays when the branch is
collapsed but on expansion displays the gl Call with the remaining
grouped Calls branched below it.

A couple other notes:
* This is merged on top of pull request apitrace#242:

      qapitrace: merge duplicate logic in TraceLoader

  in order to work correctly for both fully loaded traces and
  on-demand Frame loading

* Unpaired glPopDebugGroups will not be added to a group (left dangling)

* If a group consists of only one item then a group name isn't used.

Testing
-------
Testing only done on debian linux

Tested various traces given from past gui issues

    qapitrace crashes on glPopDebugGroup apitrace#218
    Parser::adjust_call_flags crash when 'call' is NULL apitrace#117

and other available traces.

Use case example
----------------
Example list of calls

glXChooseVisual
glXCreateContext
glXMakeCurrent
glClearColor
glClear                         <­ RENDER operation
glPushDebugGroup("Background)"
glClearColor
glClear                         <­ RENDER operation
glShaderSource
glCompileShader
glLinkProgram
glDrawElements                  <­ RENDER operation
glPopDebugGroup
glGetTexture
glBindTexture
glTexImage2D
glPushDebugGroup("Foreground")
glClearColor
glClear                         <­ RENDER operation
glEnable
glUseProgram
glDrawElements                  <­ RENDER operation
glPushDebugGroup("Character")
glClearColor
glClear                         <­ RENDER operation
glEnable
glDrawElements
glPushDebugGroup("Head")
glClearColor
glClear                         <­ RENDER operation
glDisable
glDrawElements                  <­ RENDER operation
glPopDebugGroup
glPushDebugGroup("Body")
glClearColor
glClear                         <­ RENDER operation
glEnable
glDrawElements                  <­ RENDER operation
glPopDebugGroup
glPopDebugGroup
glPopDebugGroup

In the above list of calls are several (including nested) groups and
render operations (either glClear or glDrawElements); remaining calls
will be grouped into State changes.

The first frame would look like the following:

­ Frame
  + Clear
  + "Background" group
  + State changes
  + "Foreground" group

Clicking to expand "Foreground" would render:

­ Frame
  + Clear
  + "Background" group
  + State changes
  ­ "Foreground" group
    + Clear
    + DrawElements
    + "Character" group

Then, expanding "Character" group

­ Frame
  + State changes
  + Clear
  + "Background" group
  + State changes
  ­ "Foreground" group
    + Clear
    + DrawElements
    ­ "Character" group
      + Clear
      + DrawElements
      + "Head" group
      + "Body" group

The fully expanded tree for above example would then look like

­ Frame
  ­ Clear
    . glXChooseVisual
    . glxCreateContext
    . glxMakeCurrent
    . glClearColor
    . glClear
  ­ "Background" group
    ­ Clear
      . glClearColor
      . glClear
    ­ DrawElements
      . glShaderSource
      . glCompileShader
      . glLinkProgram
      . glDrawElements
  ­ State changes
    . glGetTexture
    . glBindTexture
    . glTextImage2D
  ­ "Foreground" group
    ­ Clear
      . glClearColor
      . glClear
    ­ DrawElements
      . glEnable
      . glUseProgram
      . glDrawElements
    ­ "Character" group
      ­ Clear
        . glClearColor
        . glClear
      ­ DrawElements
        . glEnable
        . glDrawElements
      ­ "Head" group
        ­ Clear
          . glClearColor
          . glClear
        ­ DrawElements
          . glDisable
          . glDrawElements
      ­ "Body" group
        ­ Clear
          . glClearColor
          . glClear
        ­ DrawElements
          . glEnable
          . glDrawElements
  • Loading branch information
lawlove committed Mar 28, 2014
1 parent aab2c4b commit ad84ca0
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 85 deletions.
4 changes: 4 additions & 0 deletions gui/apitrace.cpp
Expand Up @@ -456,6 +456,10 @@ int ApiTrace::callInFrame(int callIdx) const
return -1;
}

void ApiTrace::setFilterModel(ApiTraceFilter *proxyModel) {
m_loader->setFilterModel(proxyModel);
}

void ApiTrace::setCallError(const ApiTraceError &error)
{
int frameIdx = callInFrame(error.callIndex);
Expand Down
3 changes: 3 additions & 0 deletions gui/apitrace.h
Expand Up @@ -9,6 +9,7 @@
#include <QSet>

class TraceLoader;
class ApiTraceFilter;
class SaverThread;
class QThread;

Expand Down Expand Up @@ -75,6 +76,8 @@ class ApiTrace : public QObject

bool hasErrors() const;

void setFilterModel(ApiTraceFilter *proxyModel);

trace::API api() const;

public slots:
Expand Down
132 changes: 84 additions & 48 deletions gui/apitracecall.cpp
Expand Up @@ -605,17 +605,21 @@ ApiTraceEvent::ApiTraceEvent()
: m_type(ApiTraceEvent::None),
m_hasBinaryData(false),
m_binaryDataIndex(0),
m_state(0),
m_staticText(0)
m_state(NULL),
m_expandedState(false),
m_staticText(NULL),
m_alternateStaticText(NULL)
{
}

ApiTraceEvent::ApiTraceEvent(Type t)
: m_type(t),
m_hasBinaryData(false),
m_binaryDataIndex(0),
m_state(0),
m_staticText(0)
m_state(NULL),
m_expandedState(false),
m_staticText(NULL),
m_alternateStaticText(NULL)
{
}

Expand Down Expand Up @@ -765,6 +769,14 @@ ApiTraceCall::addChild(ApiTraceCall *call)
}


void
ApiTraceCall::popLastChild()
{
if (m_children.count())
m_children.erase(m_children.end()-1);
}


int
ApiTraceCall::callIndex(ApiTraceCall *call) const
{
Expand Down Expand Up @@ -926,55 +938,79 @@ void ApiTraceCall::setBacktrace(QString backtrace)

QStaticText ApiTraceCall::staticText() const
{
if (m_staticText && !m_staticText->text().isEmpty())
return *m_staticText;

QVector<QVariant> argValues = arguments();
if (m_expandedState || m_alternateText.isEmpty()) {
if (m_staticText && !m_staticText->text().isEmpty()) {
return *m_staticText;
} else {
QVector<QVariant> argValues = arguments();

QString richText = QString::fromLatin1(
"<span style=\"font-weight:bold\">%1</span>(").arg(
QString richText = QString::fromLatin1(
"<span style=\"font-weight:bold\">%1</span>(").arg(
m_signature->name());
QStringList argNames = m_signature->argNames();
for (int i = 0; i < argNames.count(); ++i) {
richText += QLatin1String("<span style=\"color:#0000ff\">");
QString argText = apiVariantToString(argValues[i]);

//if arguments are really long (e.g. shader text), cut them
// and elide it
if (argText.length() > 40) {
QString shortened = argText.mid(0, 40);
shortened[argText.length() - 5] = '.';
shortened[argText.length() - 4] = '.';
shortened[argText.length() - 3] = '.';
shortened[argText.length() - 2] = argText.at(argText.length() - 2);
shortened[argText.length() - 1] = argText.at(argText.length() - 1);
richText += shortened;
QStringList argNames = m_signature->argNames();
for (int i = 0; i < argNames.count(); ++i) {
richText += QLatin1String("<span style=\"color:#0000ff\">");
QString argText = apiVariantToString(argValues[i]);

//if arguments are really long (e.g. shader text), cut them
// and elide it
if (argText.length() > 40) {
QString shortened = argText.mid(0, 40);
shortened[argText.length() - 5] = '.';
shortened[argText.length() - 4] = '.';
shortened[argText.length() - 3] = '.';
shortened[argText.length() - 2] = argText.at(
argText.length() - 2);
shortened[argText.length() - 1] = argText.at(
argText.length() - 1);
richText += shortened;
} else {
richText += argText;
}
richText += QLatin1String("</span>");
if (i < argNames.count() - 1)
richText += QLatin1String(", ");
}
richText += QLatin1String(")");
if (m_returnValue.isValid()) {
richText +=
QLatin1Literal(" = ") %
QLatin1Literal("<span style=\"color:#0000ff\">") %
apiVariantToString(m_returnValue) %
QLatin1Literal("</span>");
}

if (!m_staticText)
m_staticText = new QStaticText(richText);
else
m_staticText->setText(richText);
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
m_staticText->setTextOption(opt);
m_staticText->prepare();

return *m_staticText;
}
} else { // use alternate text
if (m_alternateStaticText && !m_alternateStaticText->text().isEmpty()) {
return *m_alternateStaticText;
} else {
richText += argText;
QString richText = QString::fromLatin1(
"<span style=\"font-weight:bold\">%1</span>").arg(
m_alternateText);

if (!m_alternateStaticText)
m_alternateStaticText = new QStaticText(richText);
else
m_alternateStaticText->setText(richText);
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
m_alternateStaticText->setTextOption(opt);
m_alternateStaticText->prepare();

return *m_alternateStaticText;
}
richText += QLatin1String("</span>");
if (i < argNames.count() - 1)
richText += QLatin1String(", ");
}
richText += QLatin1String(")");
if (m_returnValue.isValid()) {
richText +=
QLatin1Literal(" = ") %
QLatin1Literal("<span style=\"color:#0000ff\">") %
apiVariantToString(m_returnValue) %
QLatin1Literal("</span>");
}

if (!m_staticText)
m_staticText = new QStaticText(richText);
else
m_staticText->setText(richText);
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
m_staticText->setTextOption(opt);
m_staticText->prepare();

return *m_staticText;
}

QString ApiTraceCall::toHtml() const
Expand Down
20 changes: 20 additions & 0 deletions gui/apitracecall.h
Expand Up @@ -224,6 +224,7 @@ class ApiTraceEvent
virtual int numChildren() const = 0;
virtual int callIndex(ApiTraceCall *call) const = 0;
virtual ApiTraceEvent *eventAtRow(int row) const = 0;
virtual void setAlternateText (QString) {}

QVariantMap stateParameters() const;
ApiTraceState *state() const;
Expand All @@ -232,14 +233,22 @@ class ApiTraceEvent
{
return m_state && !m_state->isEmpty();
}
void setExpandedState(bool state)
{
m_expandedState = state;
}

protected:
int m_type : 4;
mutable bool m_hasBinaryData;
mutable int m_binaryDataIndex:8;
ApiTraceState *m_state;
bool m_expandedState;

mutable QStaticText *m_staticText;

QString m_alternateText;
mutable QStaticText *m_alternateStaticText;
};
Q_DECLARE_METATYPE(ApiTraceEvent*);

Expand All @@ -263,13 +272,19 @@ class ApiTraceCall : public ApiTraceEvent
ApiTraceFrame *parentFrame()const;
void setParentFrame(ApiTraceFrame *frame);

void setGroupName (QString text)
{
if (numChildren())
setAlternateText(text);
}
int callIndex(ApiTraceCall *call) const;

ApiTraceEvent *parentEvent() const;
ApiTraceCall *parentCall() const;
QVector<ApiTraceCall*> children() const;
ApiTraceEvent *eventAtRow(int row) const;
void addChild(ApiTraceCall *call);
void popLastChild();
void finishedAddingChildren();

bool hasError() const;
Expand Down Expand Up @@ -300,6 +315,11 @@ class ApiTraceCall : public ApiTraceEvent
private:
void loadData(TraceLoader *loader,
const trace::Call *tcall);
void setAlternateText (QString text)
{
m_alternateText = text;
}

private:
int m_index;
ApiTraceCallSignature *m_signature;
Expand Down
5 changes: 5 additions & 0 deletions gui/apitracefilter.cpp
Expand Up @@ -28,6 +28,11 @@ bool ApiTraceFilter::filterAcceptsRow(int sourceRow,
}

ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
return filterAcceptsCall(call);
}

bool ApiTraceFilter::filterAcceptsCall(ApiTraceCall *call) const
{
QString function = call->name();

if (!m_regexp.isEmpty() && m_regexp.isValid()) {
Expand Down
2 changes: 2 additions & 0 deletions gui/apitracefilter.h
Expand Up @@ -32,6 +32,8 @@ class ApiTraceFilter : public QSortFilterProxyModel
QString customFilterRegexp() const;

QModelIndex indexForCall(ApiTraceCall *call) const;

bool filterAcceptsCall(ApiTraceCall *call) const;
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;

Expand Down
22 changes: 22 additions & 0 deletions gui/apitracemodel.cpp
Expand Up @@ -418,4 +418,26 @@ void ApiTraceModel::endLoadingFrame(ApiTraceFrame *frame)
m_loadingFrames.remove(frame);
}

void ApiTraceModel::expandedSlot(const QModelIndex& index)
{
setExpandedState(index, true);
}

void ApiTraceModel::collapsedSlot(const QModelIndex& index)
{
setExpandedState(index, false);
}

void ApiTraceModel::setExpandedState(const QModelIndex& index, bool state)
{
const QAbstractItemModel* model = index.model();
QVariant variantData = model->data(index, ApiTraceModel::EventRole);
ApiTraceEvent *parentItem = variantData.value<ApiTraceEvent*>();

// Don't care about Frames
if (parentItem->type() == ApiTraceEvent::Frame)
return;

parentItem->setExpandedState(state);
}
#include "apitracemodel.moc"
3 changes: 3 additions & 0 deletions gui/apitracemodel.h
Expand Up @@ -28,6 +28,7 @@ class ApiTraceModel : public QAbstractItemModel
void stateSetOnEvent(ApiTraceEvent *event);

QModelIndex indexForCall(ApiTraceCall *call) const;
void setExpandedState(const QModelIndex& index, bool state);

public:
/* QAbstractItemModel { */
Expand Down Expand Up @@ -59,6 +60,8 @@ private slots:
void frameChanged(ApiTraceFrame *frame);
void beginLoadingFrame(ApiTraceFrame *frame, int numAdded);
void endLoadingFrame(ApiTraceFrame *frame);
void expandedSlot(const QModelIndex&);
void collapsedSlot(const QModelIndex&);

private:
ApiTraceEvent *item(const QModelIndex &index) const;
Expand Down
8 changes: 8 additions & 0 deletions gui/mainwindow.cpp
Expand Up @@ -766,6 +766,9 @@ void MainWindow::initObjects()
m_model->setApiTrace(m_trace);
m_proxyModel = new ApiTraceFilter();
m_proxyModel->setSourceModel(m_model);

m_trace->setFilterModel(m_proxyModel);

m_ui.callView->setModel(m_proxyModel);
m_ui.callView->setItemDelegate(
new ApiCallDelegate(m_ui.callView));
Expand Down Expand Up @@ -897,6 +900,11 @@ void MainWindow::initConnections()
connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(customContextMenuRequested(QPoint)));

connect(m_ui.callView, SIGNAL(expanded(const QModelIndex&)),
m_model, SLOT(expandedSlot(const QModelIndex&)));
connect(m_ui.callView, SIGNAL(collapsed(const QModelIndex&)),
m_model, SLOT(collapsedSlot(const QModelIndex&)));

connect(m_ui.surfacesTreeWidget,
SIGNAL(customContextMenuRequested(const QPoint &)),
SLOT(showSurfacesMenu(const QPoint &)));
Expand Down

0 comments on commit ad84ca0

Please sign in to comment.