<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2017-2018 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "ResourceInspector.h"
#include <QKeyEvent>
#include "Widgets/Extended/RDHeaderView.h"
#include "ui_ResourceInspector.h"

static const int ResourceIdRole = Qt::UserRole;
static const int FilterRole = Qt::UserRole + 1;

class ResourceListItemModel : public QAbstractItemModel
{
public:
  ResourceListItemModel(QWidget *parent, ICaptureContext &ctx)
      : QAbstractItemModel(parent), m_Ctx(ctx)
  {
  }

  void reset()
  {
    emit beginResetModel();
    emit endResetModel();
  }

  QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
  {
    if(row < 0 || row >= rowCount())
      return QModelIndex();

    return createIndex(row, 0);
  }

  QModelIndex parent(const QModelIndex &index) const override { return QModelIndex(); }
  int rowCount(const QModelIndex &parent = QModelIndex()) const override
  {
    return m_Ctx.GetResources().count();
  }
  int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 1; }
  Qt::ItemFlags flags(const QModelIndex &index) const override
  {
    if(!index.isValid())
      return 0;

    return QAbstractItemModel::flags(index);
  }

  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
  {
    if(index.isValid())
    {
      const rdcarray<ResourceDescription> &resources = m_Ctx.GetResources();

      if(index.row() < resources.count())
      {
        const ResourceDescription &desc = resources[index.row()];

        if(role == Qt::DisplayRole)
          return m_Ctx.GetResourceName(desc.resourceId);

        if(role == ResourceIdRole)
          return QVariant::fromValue(desc.resourceId);

        if(role == FilterRole)
          return ToQStr(desc.type) + lit(" ") + m_Ctx.GetResourceName(desc.resourceId);
      }
    }

    return QVariant();
  }

private:
  ICaptureContext &m_Ctx;
};

ResourceInspector::ResourceInspector(ICaptureContext &ctx, QWidget *parent)
    : QFrame(parent), ui(new Ui::ResourceInspector), m_Ctx(ctx)
{
  ui->setupUi(this);

  ui->resourceName->setText(tr("No Resource Selected"));

  ui->resetName->hide();
  ui->resourceNameEdit->hide();
  ui->renameResource->setEnabled(false);

  ui->viewContents->hide();

  m_ResourceModel = new ResourceListItemModel(this, m_Ctx);

  m_FilterModel = new QSortFilterProxyModel(this);
  m_FilterModel->setSourceModel(m_ResourceModel);
  m_FilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
  m_FilterModel->setFilterRole(FilterRole);

  ui->resourceList->setModel(m_FilterModel);

  ui->initChunks->setColumns({lit("Parameter"), tr("Value")});
  ui->initChunks->header()->resizeSection(0, 200);

  ui->initChunks->setFont(Formatter::PreferredFont());
  ui->relatedResources->setFont(Formatter::PreferredFont());
  ui->resourceUsage->setFont(Formatter::PreferredFont());

  {
    RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this);
    ui->relatedResources->setHeader(header);

    ui->relatedResources->setColumns({tr("Type"), tr("Resource")});
    header->setColumnStretchHints({-1, 1});

    ui->relatedResources->setClearSelectionOnFocusLoss(true);
  }

  ui->resourceUsage->setColumns({tr("EID"), tr("Usage")});

  QObject::connect(ui->resourceList, &QListView::activated, this,
                   &ResourceInspector::resource_doubleClicked);
  QObject::connect(ui->relatedResources, &QTreeView::activated, this,
                   &ResourceInspector::resource_doubleClicked);

  Inspect(ResourceId());

  m_Ctx.AddCaptureViewer(this);
}

ResourceInspector::~ResourceInspector()
{
  m_Ctx.BuiltinWindowClosed(this);
  m_Ctx.RemoveCaptureViewer(this);
  delete ui;
}

void ResourceInspector::Inspect(ResourceId id)
{
  if(m_Resource == id)
    return;

  m_Resource = id;

  ui->viewContents->setVisible(m_Ctx.GetTexture(id) || m_Ctx.GetBuffer(id));

  m_Entries.clear();

  m_ResourceModel->reset();

  if(m_Ctx.HasResourceCustomName(id))
    ui->resetName->show();
  else
    ui->resetName->hide();

  ui->initChunks->setUpdatesEnabled(false);
  ui->initChunks->clear();
  ui->resourceUsage->clear();

  const SDFile &file = m_Ctx.GetStructuredFile();
  const ResourceDescription *desc = m_Ctx.GetResource(id);

  m_Ctx.Replay().AsyncInvoke([this, id](IReplayController *r) {
    rdcarray<EventUsage> usage = r->GetUsage(id);

    rdcarray<ShaderEntryPoint> entries = r->GetShaderEntryPoints(id);

    GUIInvoke::call([this, id, entries, usage] {

      if(!entries.isEmpty())
      {
        m_Entries = entries;
        ui->viewContents->setVisible(true);
      }

      CombineUsageEvents(
          m_Ctx, usage, [this, id](uint32_t startEID, uint32_t endEID, ResourceUsage use) {
            QString text;

            if(startEID == endEID)
              text = QFormatStr("EID %1").arg(startEID);
            else
              text = QFormatStr("EID %1-%2").arg(startEID).arg(endEID);

            RDTreeWidgetItem *item =
                new RDTreeWidgetItem({text, ToQStr(use, m_Ctx.APIProps().pipelineType)});
            item->setData(0, ResourceIdRole, QVariant(endEID));

            ui->resourceUsage->addTopLevelItem(item);
          });
    });
  });

  if(desc)
  {
    ANALYTIC_SET(UIFeatures.ResourceInspect, true);

    ui->resourceName->setText(m_Ctx.GetResourceName(id));

    ui->relatedResources->beginUpdate();
    ui->relatedResources->clear();
    for(ResourceId parent : desc->parentResources)
    {
      RDTreeWidgetItem *item = new RDTreeWidgetItem({tr("Parent"), parent});
      item->setData(0, ResourceIdRole, QVariant::fromValue(parent));<commit_msg>Allow activating right column of usage details in resource inspector<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2017-2018 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "ResourceInspector.h"
#include <QKeyEvent>
#include "Widgets/Extended/RDHeaderView.h"
#include "ui_ResourceInspector.h"

static const int ResourceIdRole = Qt::UserRole;
static const int FilterRole = Qt::UserRole + 1;

class ResourceListItemModel : public QAbstractItemModel
{
public:
  ResourceListItemModel(QWidget *parent, ICaptureContext &ctx)
      : QAbstractItemModel(parent), m_Ctx(ctx)
  {
  }

  void reset()
  {
    emit beginResetModel();
    emit endResetModel();
  }

  QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
  {
    if(row < 0 || row >= rowCount())
      return QModelIndex();

    return createIndex(row, 0);
  }

  QModelIndex parent(const QModelIndex &index) const override { return QModelIndex(); }
  int rowCount(const QModelIndex &parent = QModelIndex()) const override
  {
    return m_Ctx.GetResources().count();
  }
  int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 1; }
  Qt::ItemFlags flags(const QModelIndex &index) const override
  {
    if(!index.isValid())
      return 0;

    return QAbstractItemModel::flags(index);
  }

  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
  {
    if(index.isValid())
    {
      const rdcarray<ResourceDescription> &resources = m_Ctx.GetResources();

      if(index.row() < resources.count())
      {
        const ResourceDescription &desc = resources[index.row()];

        if(role == Qt::DisplayRole)
          return m_Ctx.GetResourceName(desc.resourceId);

        if(role == ResourceIdRole)
          return QVariant::fromValue(desc.resourceId);

        if(role == FilterRole)
          return ToQStr(desc.type) + lit(" ") + m_Ctx.GetResourceName(desc.resourceId);
      }
    }

    return QVariant();
  }

private:
  ICaptureContext &m_Ctx;
};

ResourceInspector::ResourceInspector(ICaptureContext &ctx, QWidget *parent)
    : QFrame(parent), ui(new Ui::ResourceInspector), m_Ctx(ctx)
{
  ui->setupUi(this);

  ui->resourceName->setText(tr("No Resource Selected"));

  ui->resetName->hide();
  ui->resourceNameEdit->hide();
  ui->renameResource->setEnabled(false);

  ui->viewContents->hide();

  m_ResourceModel = new ResourceListItemModel(this, m_Ctx);

  m_FilterModel = new QSortFilterProxyModel(this);
  m_FilterModel->setSourceModel(m_ResourceModel);
  m_FilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
  m_FilterModel->setFilterRole(FilterRole);

  ui->resourceList->setModel(m_FilterModel);

  ui->initChunks->setColumns({lit("Parameter"), tr("Value")});
  ui->initChunks->header()->resizeSection(0, 200);

  ui->initChunks->setFont(Formatter::PreferredFont());
  ui->relatedResources->setFont(Formatter::PreferredFont());
  ui->resourceUsage->setFont(Formatter::PreferredFont());

  {
    RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this);
    ui->relatedResources->setHeader(header);

    ui->relatedResources->setColumns({tr("Type"), tr("Resource")});
    header->setColumnStretchHints({-1, 1});

    ui->relatedResources->setClearSelectionOnFocusLoss(true);
  }

  ui->resourceUsage->setColumns({tr("EID"), tr("Usage")});

  QObject::connect(ui->resourceList, &QListView::activated, this,
                   &ResourceInspector::resource_doubleClicked);
  QObject::connect(ui->relatedResources, &QTreeView::activated, this,
                   &ResourceInspector::resource_doubleClicked);

  Inspect(ResourceId());

  m_Ctx.AddCaptureViewer(this);
}

ResourceInspector::~ResourceInspector()
{
  m_Ctx.BuiltinWindowClosed(this);
  m_Ctx.RemoveCaptureViewer(this);
  delete ui;
}

void ResourceInspector::Inspect(ResourceId id)
{
  if(m_Resource == id)
    return;

  m_Resource = id;

  ui->viewContents->setVisible(m_Ctx.GetTexture(id) || m_Ctx.GetBuffer(id));

  m_Entries.clear();

  m_ResourceModel->reset();

  if(m_Ctx.HasResourceCustomName(id))
    ui->resetName->show();
  else
    ui->resetName->hide();

  ui->initChunks->setUpdatesEnabled(false);
  ui->initChunks->clear();
  ui->resourceUsage->clear();

  const SDFile &file = m_Ctx.GetStructuredFile();
  const ResourceDescription *desc = m_Ctx.GetResource(id);

  m_Ctx.Replay().AsyncInvoke([this, id](IReplayController *r) {
    rdcarray<EventUsage> usage = r->GetUsage(id);

    rdcarray<ShaderEntryPoint> entries = r->GetShaderEntryPoints(id);

    GUIInvoke::call([this, id, entries, usage] {

      if(!entries.isEmpty())
      {
        m_Entries = entries;
        ui->viewContents->setVisible(true);
      }

      CombineUsageEvents(
          m_Ctx, usage, [this, id](uint32_t startEID, uint32_t endEID, ResourceUsage use) {
            QString text;

            if(startEID == endEID)
              text = QFormatStr("EID %1").arg(startEID);
            else
              text = QFormatStr("EID %1-%2").arg(startEID).arg(endEID);

            RDTreeWidgetItem *item =
                new RDTreeWidgetItem({text, ToQStr(use, m_Ctx.APIProps().pipelineType)});
            item->setData(0, ResourceIdRole, QVariant(endEID));
            item->setData(1, ResourceIdRole, QVariant(endEID));

            ui->resourceUsage->addTopLevelItem(item);
          });
    });
  });

  if(desc)
  {
    ANALYTIC_SET(UIFeatures.ResourceInspect, true);

    ui->resourceName->setText(m_Ctx.GetResourceName(id));

    ui->relatedResources->beginUpdate();
    ui->relatedResources->clear();
    for(ResourceId parent : desc->parentResources)
    {
      RDTreeWidgetItem *item = new RDTreeWidgetItem({tr("Parent"), parent});
      item->setData(0, ResourceIdRole, QVariant::fromValue(parent));
{'fraction_non_alphanumeric': 0.14682281500759053, 'fraction_numerical': 0.00412058121882455, 'mean_word_length': 4.189047261815454, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 148, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2544648', 'n_tokens_mistral': 4208, 'n_tokens_neox': 4055, 'n_words': 925}