void ShaderViewer::debugShader()

in qrenderdoc/Windows/ShaderViewer.cpp [394:1177]


void ShaderViewer::debugShader(const ShaderReflection *shader, ResourceId pipeline,
                               ShaderDebugTrace *trace, const QString &debugContext)
{
  m_ShaderDetails = shader;
  m_Pipeline = pipeline;
  m_Trace = trace;
  m_Stage = ShaderStage::Vertex;
  m_DebugContext = debugContext;

  // no recompilation happening, hide that group
  ui->compilationGroup->hide();

  // no replacing allowed, stay in find mode
  m_FindReplace->allowUserModeChange(false);

  if(!m_ShaderDetails)
    m_Trace = NULL;

  if(m_ShaderDetails)
  {
    m_Stage = m_ShaderDetails->stage;

    QPointer<ShaderViewer> me(this);

    m_Ctx.Replay().AsyncInvoke([me, this](IReplayController *r) {
      if(!me)
        return;

      rdcarray<rdcstr> targets = r->GetDisassemblyTargets(m_Pipeline != ResourceId());

      if(m_Pipeline == ResourceId())
      {
        rdcarray<rdcstr> pipelineTargets = r->GetDisassemblyTargets(true);

        if(pipelineTargets.size() > targets.size())
        {
          m_PipelineTargets = pipelineTargets;
          m_PipelineTargets.removeIf([&targets](const rdcstr &t) { return targets.contains(t); });
        }
      }

      rdcstr disasm = r->DisassembleShader(m_Pipeline, m_ShaderDetails, "");

      if(!me)
        return;

      GUIInvoke::call(this, [this, targets, disasm]() {
        QStringList targetNames;
        for(int i = 0; i < targets.count(); i++)
        {
          QString target = targets[i];
          targetNames << QString(targets[i]);

          if(i == 0)
          {
            // add any custom decompiling tools we have after the first one
            for(const ShaderProcessingTool &d : m_Ctx.Config().ShaderProcessors)
            {
              if(d.input == m_ShaderDetails->encoding && IsTextRepresentation(d.output))
                targetNames << targetName(d);
            }
          }
        }

        if(!m_PipelineTargets.empty())
          targetNames << tr("More disassembly formats...");

        m_DisassemblyType->clear();
        m_DisassemblyType->addItems(targetNames);
        m_DisassemblyType->setCurrentIndex(0);
        QObject::connect(m_DisassemblyType, OverloadedSlot<int>::of(&QComboBox::currentIndexChanged),
                         this, &ShaderViewer::disassemble_typeChanged);

        // read-only applies to us too!
        m_DisassemblyView->setReadOnly(false);
        SetTextAndUpdateMargin0(m_DisassemblyView, disasm);
        m_DisassemblyView->setReadOnly(true);
      });
    });
  }

  updateWindowTitle();

  // we always want to highlight words/registers
  QObject::connect(m_DisassemblyView, &ScintillaEdit::buttonReleased, this,
                   &ShaderViewer::disassembly_buttonReleased);

  if(m_Trace)
  {
    if(m_Stage == ShaderStage::Vertex)
    {
      ANALYTIC_SET(ShaderDebug.Vertex, true);
    }
    else if(m_Stage == ShaderStage::Pixel)
    {
      ANALYTIC_SET(ShaderDebug.Pixel, true);
    }
    else if(m_Stage == ShaderStage::Compute)
    {
      ANALYTIC_SET(ShaderDebug.Compute, true);
    }

    m_DisassemblyFrame->layout()->removeWidget(m_DisassemblyToolbar);
  }

  if(m_ShaderDetails && !m_ShaderDetails->debugInfo.files.isEmpty())
  {
    updateWindowTitle();

    // add all the files, skipping any that have empty contents. We push a NULL in that case so the
    // indices still match up with what the debug info expects. Debug info *shouldn't* point us at
    // an empty file, but if it does we'll just bail out when we see NULL
    m_FileScintillas.reserve(m_ShaderDetails->debugInfo.files.count());

    QWidget *sel = NULL;
    int32_t entryFile = m_ShaderDetails->debugInfo.entryLocation.fileIndex;
    int32_t i = -1;
    for(const ShaderSourceFile &f : m_ShaderDetails->debugInfo.files)
    {
      i++;
      if(f.contents.isEmpty())
      {
        m_FileScintillas.push_back(NULL);
        continue;
      }

      QString name = QFileInfo(f.filename).fileName();
      QString text = f.contents;

      ScintillaEdit *scintilla = AddFileScintilla(name, text, m_ShaderDetails->debugInfo.encoding);

      if(sel == NULL)
        sel = scintilla;

      if(i == entryFile)
      {
        sel = scintilla;

        if(m_ShaderDetails->debugInfo.entryLocation.lineStart > 0)
        {
          GUIInvoke::defer(scintilla, [scintilla, this]() {
            ensureLineScrolled(scintilla, m_ShaderDetails->debugInfo.entryLocation.lineStart);
          });
        }
      }

      m_FileScintillas.push_back(scintilla);
    }

    if(m_Trace || sel == NULL)
      sel = m_DisassemblyFrame;

    if(m_ShaderDetails->debugInfo.files.size() > 2)
      addFileList();

    ToolWindowManager::raiseToolWindow(sel);
  }

  // hide edit buttons
  ui->refresh->hide();
  ui->unrefresh->hide();
  ui->resetEdits->hide();
  ui->editStatusLabel->hide();
  ui->snippets->hide();
  ui->editSep->hide();

  if(m_Trace)
  {
    // hide signatures
    ui->inputSig->hide();
    ui->outputSig->hide();

    // hide int/float toggles except on DXBC, other encodings are strongly typed
    if(m_ShaderDetails->encoding != ShaderEncoding::DXBC)
    {
      ui->intView->hide();
      ui->floatView->hide();
    }

    if(!m_ShaderDetails->debugInfo.sourceDebugInformation)
    {
      ui->debugToggle->setEnabled(false);
      ui->debugToggle->setText(tr("Source debugging Unavailable"));
    }

    ui->debugVars->setColumns({tr("Name"), tr("Value")});
    ui->debugVars->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    ui->debugVars->header()->setSectionResizeMode(1, QHeaderView::Interactive);

    ui->sourceVars->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")});
    ui->sourceVars->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    ui->sourceVars->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
    ui->sourceVars->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
    ui->sourceVars->header()->setSectionResizeMode(3, QHeaderView::Interactive);

    ui->constants->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")});
    ui->constants->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    ui->constants->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
    ui->constants->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
    ui->constants->header()->setSectionResizeMode(3, QHeaderView::Interactive);

    ui->constants->header()->resizeSection(0, 80);

    ui->accessedResources->setColumns({tr("Location"), tr("Type"), tr("Info")});
    ui->accessedResources->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    ui->accessedResources->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
    ui->accessedResources->header()->setSectionResizeMode(2, QHeaderView::Interactive);

    ui->accessedResources->header()->resizeSection(0, 80);

    ui->debugVars->setTooltipElidedItems(false);
    ui->constants->setTooltipElidedItems(false);
    ui->accessedResources->setTooltipElidedItems(false);

    ui->watch->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")});
    ui->watch->header()->setSectionResizeMode(0, QHeaderView::Interactive);
    ui->watch->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
    ui->watch->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
    ui->watch->header()->setSectionResizeMode(3, QHeaderView::Interactive);

    ui->watch->header()->resizeSection(0, 80);

    ui->watch->setItemDelegate(new FullEditorDelegate(ui->watch));

    ToolWindowManager::ToolWindowProperty windowProps =
        ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow;
    ui->watch->setWindowTitle(tr("Watch"));
    ui->docking->addToolWindow(
        ui->watch, ToolWindowManager::AreaReference(ToolWindowManager::BottomOf,
                                                    ui->docking->areaOf(m_DisassemblyFrame), 0.25f));
    ui->docking->setToolWindowProperties(ui->watch, windowProps);

    ui->debugVars->setWindowTitle(tr("Variable Values"));
    ui->docking->addToolWindow(
        ui->debugVars,
        ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->docking->areaOf(ui->watch)));
    ui->docking->setToolWindowProperties(ui->debugVars, windowProps);

    ui->constants->setWindowTitle(tr("Constants && Resources"));
    ui->docking->addToolWindow(
        ui->constants, ToolWindowManager::AreaReference(ToolWindowManager::LeftOf,
                                                        ui->docking->areaOf(ui->debugVars), 0.5f));
    ui->docking->setToolWindowProperties(ui->constants, windowProps);

    ui->resourcesPanel->setWindowTitle(tr("Accessed Resources"));
    ui->docking->addToolWindow(
        ui->resourcesPanel, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
                                                             ui->docking->areaOf(ui->constants)));
    ui->docking->setToolWindowProperties(ui->resourcesPanel, windowProps);
    ui->docking->raiseToolWindow(ui->constants);

    ui->callstack->setWindowTitle(tr("Callstack"));
    ui->docking->addToolWindow(
        ui->callstack, ToolWindowManager::AreaReference(ToolWindowManager::RightOf,
                                                        ui->docking->areaOf(ui->debugVars), 0.2f));
    ui->docking->setToolWindowProperties(ui->callstack, windowProps);

    ui->sourceVars->setWindowTitle(tr("High-level Variables"));
    ui->docking->addToolWindow(
        ui->sourceVars, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
                                                         ui->docking->areaOf(ui->debugVars)));
    ui->docking->setToolWindowProperties(ui->sourceVars, windowProps);

    bool hasLineInfo = false;
    LineColumnInfo prevLine;

    for(uint32_t inst = 0; inst < m_Trace->instInfo.size(); inst++)
    {
      const InstructionSourceInfo &instInfo = m_Trace->instInfo[inst];

      LineColumnInfo line = instInfo.lineInfo;

      int disasmLine = (int)line.disassemblyLine;
      if(disasmLine > 0 && disasmLine >= m_AsmLine2Inst.size())
      {
        int oldSize = m_AsmLine2Inst.size();
        m_AsmLine2Inst.resize(disasmLine + 1);
        for(int i = oldSize; i < disasmLine; i++)
          m_AsmLine2Inst[i] = -1;
      }

      if(disasmLine > 0)
        m_AsmLine2Inst[disasmLine] = (int)instInfo.instruction;

      if(line.fileIndex < 0)
        continue;

      hasLineInfo = true;

      // store the source location *without* any disassembly line
      line.disassemblyLine = 0;

      // skip any instructions with the same mapping as the last one, this is a contiguous block
      if(line.SourceEqual(prevLine))
        continue;

      prevLine = line;

      m_Location2Inst[line].push_back(instInfo.instruction);
    }

    // if we don't have line mapping info, assume we also don't have useful high-level variable
    // info. Show the debug variables first rather than a potentially empty source variables panel.
    if(!hasLineInfo)
      ui->docking->raiseToolWindow(ui->debugVars);

    // set up stepping/running actions

    // we register the shortcuts via MainWindow so that it works regardless of the active scintilla
    // but still handles multiple shader viewers being present (the one with focus will get the
    // input)

    // all shortcuts have a reverse version with shift. This means step out is Ctrl-F11 instead of
    // Shift-F11, but otherwise the shortcuts behave the same as visual studio

    {
      QMenu *backwardsMenu = new QMenu(this);
      backwardsMenu->setToolTipsVisible(true);
      QAction *act;

      act = MakeExecuteAction(tr("&Run backwards"), Icons::control_start_blue(),
                              tr("Run backwards to the start of the shader"),
                              QKeySequence(Qt::Key_F5 | Qt::ShiftModifier));

      QObject::connect(act, &QAction::triggered, [this]() { runTo(~0U, false); });
      backwardsMenu->addAction(act);

      act = MakeExecuteAction(
          tr("Run backwards to &Cursor"), Icons::control_reverse_cursor_blue(),
          tr("Run backwards until execution reaches the cursor, or the start of the shader"),
          QKeySequence(Qt::Key_F10 | Qt::ControlModifier | Qt::ShiftModifier));

      QObject::connect(act, &QAction::triggered, [this]() { runToCursor(false); });
      backwardsMenu->addAction(act);

      act = MakeExecuteAction(tr("Run backwards to &Sample"), Icons::control_reverse_sample_blue(),
                              tr("Run backwards until execution reads from a resource, or the "
                                 "start of the shader is reached"),
                              QKeySequence());

      QObject::connect(act, &QAction::triggered,
                       [this]() { runTo(~0U, false, ShaderEvents::SampleLoadGather); });
      backwardsMenu->addAction(act);

      act = MakeExecuteAction(
          tr("Run backwards to &NaN/Inf"), Icons::control_reverse_nan_blue(),
          tr("Run backwards until a floating point instruction generates a NaN "
             "or Inf, an integer instruction divides by 0, or the start of the shader is reached"),
          QKeySequence());

      QObject::connect(act, &QAction::triggered,
                       [this]() { runTo(~0U, false, ShaderEvents::GeneratedNanOrInf); });
      backwardsMenu->addAction(act);

      backwardsMenu->addSeparator();

      act = MakeExecuteAction(tr("Step backwards &Over"), Icons::control_reverse_blue(),
                              tr("Step backwards, and don't enter functions when source debugging"),
                              QKeySequence(Qt::Key_F10 | Qt::ShiftModifier));

      QObject::connect(act, &QAction::triggered, [this]() { step(false, StepOver); });
      backwardsMenu->addAction(act);

      act = MakeExecuteAction(tr("Step backwards &Into"), Icons::control_reverse_blue(),
                              tr("Step backwards, entering functions when source debugging"),
                              QKeySequence(Qt::Key_F11 | Qt::ShiftModifier));

      QObject::connect(act, &QAction::triggered, [this]() { step(false, StepInto); });
      backwardsMenu->addAction(act);

      act =
          MakeExecuteAction(tr("Step backwards Ou&t"), Icons::control_reverse_blue(),
                            tr("Step backwards, out of the current function when source debugging"),
                            QKeySequence(Qt::Key_F11 | Qt::ControlModifier | Qt::ShiftModifier));

      QObject::connect(act, &QAction::triggered, [this]() { step(false, StepOut); });
      backwardsMenu->addAction(act);

      ui->execBackwards->setMenu(backwardsMenu);
    }

    {
      QMenu *forwardsMenu = new QMenu(this);
      forwardsMenu->setToolTipsVisible(true);
      QAction *act;

      act = MakeExecuteAction(tr("&Run forwards"), Icons::control_end_blue(),
                              tr("Run forwards to the start of the shader"),
                              QKeySequence(Qt::Key_F5));

      QObject::connect(act, &QAction::triggered, [this]() { runTo(~0U, true); });
      forwardsMenu->addAction(act);

      act = MakeExecuteAction(
          tr("Run forwards to &Cursor"), Icons::control_cursor_blue(),
          tr("Run forwards until execution reaches the cursor, or the end of the shader"),
          QKeySequence(Qt::Key_F10 | Qt::ControlModifier));

      QObject::connect(act, &QAction::triggered, [this]() { runToCursor(true); });
      forwardsMenu->addAction(act);

      act = MakeExecuteAction(tr("Run forwards to &Sample"), Icons::control_sample_blue(),
                              tr("Run forwards until execution reads from a resource, or the "
                                 "end of the shader is reached"),
                              QKeySequence());

      QObject::connect(act, &QAction::triggered,
                       [this]() { runTo(~0U, true, ShaderEvents::SampleLoadGather); });
      forwardsMenu->addAction(act);

      act = MakeExecuteAction(
          tr("Run forwards to &NaN/Inf"), Icons::control_nan_blue(),
          tr("Run forwards until a floating point instruction generates a NaN "
             "or Inf, an integer instruction divides by 0, or the end of the shader is reached"),
          QKeySequence());

      QObject::connect(act, &QAction::triggered,
                       [this]() { runTo(~0U, true, ShaderEvents::GeneratedNanOrInf); });
      forwardsMenu->addAction(act);

      forwardsMenu->addSeparator();

      act = MakeExecuteAction(tr("Step forwards &Over"), Icons::control_play_blue(),
                              tr("Step forwards, and don't enter functions when source debugging"),
                              QKeySequence(Qt::Key_F10));

      QObject::connect(act, &QAction::triggered, [this]() { step(true, StepOver); });
      forwardsMenu->addAction(act);

      act = MakeExecuteAction(tr("Step forwards &Into"), Icons::control_play_blue(),
                              tr("Step forwards, entering functions when source debugging"),
                              QKeySequence(Qt::Key_F11));

      QObject::connect(act, &QAction::triggered, [this]() { step(true, StepInto); });
      forwardsMenu->addAction(act);

      act =
          MakeExecuteAction(tr("Step forwards Ou&t"), Icons::control_play_blue(),
                            tr("Step forwards, out of the current function when source debugging"),
                            QKeySequence(Qt::Key_F11 | Qt::ControlModifier));

      QObject::connect(act, &QAction::triggered, [this]() { step(true, StepOut); });
      forwardsMenu->addAction(act);

      ui->execForwards->setMenu(forwardsMenu);
    }

    for(ScintillaEdit *edit : m_Scintillas)
    {
      edit->setMarginWidthN(1, 20.0 * devicePixelRatioF());

      // display current line in margin 2, distinct from breakpoint in margin 1
      sptr_t markMask = (1 << CURRENT_MARKER) | (1 << FINISHED_MARKER);

      edit->setMarginMaskN(1, edit->marginMaskN(1) & ~markMask);
      edit->setMarginMaskN(2, edit->marginMaskN(2) | markMask);

      // suppress the built-in context menu and hook up our own
      edit->usePopUp(SC_POPUP_NEVER);

      edit->setContextMenuPolicy(Qt::CustomContextMenu);
      QObject::connect(edit, &ScintillaEdit::customContextMenuRequested, this,
                       &ShaderViewer::debug_contextMenu);

      edit->setMouseDwellTime(500);

      QObject::connect(edit, &ScintillaEdit::dwellStart, this, &ShaderViewer::disasm_tooltipShow);
      QObject::connect(edit, &ScintillaEdit::dwellEnd, this, &ShaderViewer::disasm_tooltipHide);
    }

    // toggle breakpoint - F9
    m_Ctx.GetMainWindow()->RegisterShortcut(QKeySequence(Qt::Key_F9).toString(), this,
                                            [this](QWidget *) { ToggleBreakpointOnInstruction(); });

    // event filter to pick up tooltip events
    ui->constants->installEventFilter(this);
    ui->accessedResources->installEventFilter(this);
    ui->debugVars->installEventFilter(this);
    ui->watch->installEventFilter(this);

    cacheResources();

    m_BackgroundRunning.release();

    QPointer<ShaderViewer> me(this);

    m_DeferredInit = true;

    m_Ctx.Replay().AsyncInvoke([this, me](IReplayController *r) {
      if(!me)
        return;

      rdcarray<ShaderDebugState> *states = new rdcarray<ShaderDebugState>();

      states->append(r->ContinueDebug(m_Trace->debugger));

      rdcarray<ShaderDebugState> nextStates;

      bool finished = false;
      do
      {
        if(!me)
        {
          delete states;
          return;
        }

        nextStates = r->ContinueDebug(m_Trace->debugger);

        if(!me)
        {
          delete states;
          return;
        }

        finished = nextStates.empty();
        states->append(std::move(nextStates));
      } while(!finished && m_BackgroundRunning.available() == 1);

      if(!me)
      {
        delete states;
        return;
      }

      m_BackgroundRunning.tryAcquire(1);

      r->SetFrameEvent(m_Ctx.CurEvent(), true);

      if(!me)
      {
        delete states;
        return;
      }

      GUIInvoke::call(this, [this, states]() {
        m_States.swap(*states);
        delete states;

        if(!m_States.empty())
        {
          for(const ShaderVariableChange &c : GetCurrentState().changes)
            m_Variables.push_back(c.after);
        }

        bool preferSourceDebug = false;

        for(const ShaderCompileFlag &flag : m_ShaderDetails->debugInfo.compileFlags.flags)
        {
          if(flag.name == "preferSourceDebug")
          {
            preferSourceDebug = true;
            break;
          }
        }

        updateDebugState();

        // we do updateDebugging() again because the first call finds the scintilla for the current
        // source file, the second time jumps to it.
        if(preferSourceDebug)
        {
          // if we're not on a source line, move forward to the first source line
          while(!m_CurInstructionScintilla)
          {
            do
            {
              applyForwardsChange();

              if(GetCurrentInstInfo().lineInfo.fileIndex >= 0)
                break;

              if(IsLastState())
                break;

            } while(true);

            updateDebugState();

            if(IsLastState())
              break;
          }

          // if we got to the last state something's wrong - we're preferring source debug but we
          // didn't ever reach an instruction mapped to source lines? just reverse course and don't
          // switch to source debugging
          if(IsLastState())
          {
            m_FirstSourceStateIdx = ~0U;
            runTo(~0U, false);
          }
          else
          {
            m_FirstSourceStateIdx = m_CurrentStateIdx;
            gotoSourceDebugging();
            updateDebugState();
          }
        }

        m_DeferredInit = false;
        for(std::function<void(ShaderViewer *)> &f : m_DeferredCommands)
          f(this);
        m_DeferredCommands.clear();
      });
    });

    GUIInvoke::defer(this, [this, debugContext]() {
      // wait a short while before displaying the progress dialog (which won't show if we're already
      // done by the time we reach it)
      for(int i = 0; m_BackgroundRunning.available() == 1 && i < 100; i++)
        QThread::msleep(5);

      ShowProgressDialog(
          this, tr("Debugging %1").arg(debugContext),
          [this]() { return m_BackgroundRunning.available() == 0; }, NULL,
          [this]() { m_BackgroundRunning.acquire(); });
    });

    m_CurrentStateIdx = 0;

    QObject::connect(ui->watch, &RDTreeWidget::keyPress, this, &ShaderViewer::watch_keyPress);

    ui->watch->setContextMenuPolicy(Qt::CustomContextMenu);
    QObject::connect(ui->watch, &RDTreeWidget::customContextMenuRequested, this,
                     &ShaderViewer::variables_contextMenu);
    ui->constants->setContextMenuPolicy(Qt::CustomContextMenu);
    QObject::connect(ui->constants, &RDTreeWidget::customContextMenuRequested, this,
                     &ShaderViewer::variables_contextMenu);
    ui->debugVars->setContextMenuPolicy(Qt::CustomContextMenu);
    QObject::connect(ui->debugVars, &RDTreeWidget::customContextMenuRequested, this,
                     &ShaderViewer::variables_contextMenu);
    ui->sourceVars->setContextMenuPolicy(Qt::CustomContextMenu);
    QObject::connect(ui->sourceVars, &RDTreeWidget::customContextMenuRequested, this,
                     &ShaderViewer::variables_contextMenu);
    ui->accessedResources->setContextMenuPolicy(Qt::CustomContextMenu);
    QObject::connect(ui->accessedResources, &RDTreeWidget::customContextMenuRequested, this,
                     &ShaderViewer::accessedResources_contextMenu);

    RDTreeWidgetItem *item = new RDTreeWidgetItem({
        QVariant(),
        QVariant(),
        QVariant(),
        QVariant(),
    });
    item->setEditable(0, true);
    ui->watch->addTopLevelItem(item);

    ToolWindowManager::raiseToolWindow(m_DisassemblyFrame);
  }
  else
  {
    // hide watch, constants, variables
    ui->watch->hide();
    ui->debugVars->hide();
    ui->constants->hide();
    ui->resourcesPanel->hide();
    ui->sourceVars->hide();
    ui->callstack->hide();

    // hide debugging toolbar buttons
    ui->editSep->hide();
    ui->execBackwards->hide();
    ui->execForwards->hide();
    ui->regFormatSep->hide();
    ui->intView->hide();
    ui->floatView->hide();
    ui->debugToggleSep->hide();
    ui->debugToggle->hide();

    // show input and output signatures
    ui->inputSig->setColumns(
        {tr("Name"), tr("Index"), tr("Reg"), tr("Type"), tr("SysValue"), tr("Mask"), tr("Used")});
    for(int i = 0; i < ui->inputSig->header()->count(); i++)
      ui->inputSig->header()->setSectionResizeMode(i, QHeaderView::ResizeToContents);

    ui->outputSig->setColumns(
        {tr("Name"), tr("Index"), tr("Reg"), tr("Type"), tr("SysValue"), tr("Mask"), tr("Used")});
    for(int i = 0; i < ui->outputSig->header()->count(); i++)
      ui->outputSig->header()->setSectionResizeMode(i, QHeaderView::ResizeToContents);

    if(m_ShaderDetails)
    {
      for(const SigParameter &s : m_ShaderDetails->inputSignature)
      {
        QString name = s.varName.isEmpty()
                           ? QString(s.semanticName)
                           : QFormatStr("%1 (%2)").arg(s.varName).arg(s.semanticName);
        if(s.semanticName.isEmpty())
          name = s.varName;

        QString semIdx = s.needSemanticIndex ? QString::number(s.semanticIndex) : QString();

        QString regIdx =
            s.systemValue == ShaderBuiltin::Undefined ? QString::number(s.regIndex) : lit("-");

        ui->inputSig->addTopLevelItem(new RDTreeWidgetItem(
            {name, semIdx, regIdx, TypeString(s), ToQStr(s.systemValue),
             GetComponentString(s.regChannelMask), GetComponentString(s.channelUsedMask)}));
      }

      bool multipleStreams = false;
      for(const SigParameter &s : m_ShaderDetails->outputSignature)
      {
        if(s.stream > 0)
        {
          multipleStreams = true;
          break;
        }
      }

      for(const SigParameter &s : m_ShaderDetails->outputSignature)
      {
        QString name = s.varName.isEmpty()
                           ? QString(s.semanticName)
                           : QFormatStr("%1 (%2)").arg(s.varName).arg(s.semanticName);
        if(s.semanticName.isEmpty())
          name = s.varName;

        if(multipleStreams)
          name = QFormatStr("Stream %1 : %2").arg(s.stream).arg(name);
        if(s.perPrimitiveRate)
          name += tr(" (Per-Prim)");

        QString semIdx = s.needSemanticIndex ? QString::number(s.semanticIndex) : QString();

        QString regIdx =
            s.systemValue == ShaderBuiltin::Undefined ? QString::number(s.regIndex) : lit("-");

        ui->outputSig->addTopLevelItem(new RDTreeWidgetItem(
            {name, semIdx, regIdx, TypeString(s), ToQStr(s.systemValue),
             GetComponentString(s.regChannelMask), GetComponentString(s.channelUsedMask)}));
      }
    }

    ui->inputSig->setWindowTitle(tr("Input Signature"));
    ui->docking->addToolWindow(ui->inputSig, ToolWindowManager::AreaReference(
                                                 ToolWindowManager::BottomOf,
                                                 ui->docking->areaOf(m_DisassemblyFrame), 0.2f));
    ui->docking->setToolWindowProperties(
        ui->inputSig, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);

    ui->outputSig->setWindowTitle(tr("Output Signature"));
    ui->docking->addToolWindow(
        ui->outputSig, ToolWindowManager::AreaReference(ToolWindowManager::RightOf,
                                                        ui->docking->areaOf(ui->inputSig), 0.5f));
    ui->docking->setToolWindowProperties(
        ui->outputSig, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
  }

  for(ScintillaEdit *edit : m_Scintillas)
  {
    // C# LightCoral
    edit->markerSetBack(CURRENT_MARKER, SCINTILLA_COLOUR(240, 128, 128));
    edit->markerSetBack(CURRENT_MARKER + 1, SCINTILLA_COLOUR(240, 128, 128));
    edit->markerDefine(CURRENT_MARKER, SC_MARK_SHORTARROW);
    edit->markerDefine(CURRENT_MARKER + 1, SC_MARK_BACKGROUND);
    edit->indicSetFore(CURRENT_INDICATOR, SCINTILLA_COLOUR(240, 128, 128));
    edit->indicSetAlpha(CURRENT_INDICATOR, 220);
    edit->indicSetOutlineAlpha(CURRENT_INDICATOR, 255);
    edit->indicSetUnder(CURRENT_INDICATOR, true);
    edit->indicSetStyle(CURRENT_INDICATOR, INDIC_STRAIGHTBOX);
    edit->indicSetHoverFore(CURRENT_INDICATOR, SCINTILLA_COLOUR(240, 128, 128));
    edit->indicSetHoverStyle(CURRENT_INDICATOR, INDIC_STRAIGHTBOX);

    // C# LightSlateGray
    edit->markerSetBack(FINISHED_MARKER, SCINTILLA_COLOUR(119, 136, 153));
    edit->markerSetBack(FINISHED_MARKER + 1, SCINTILLA_COLOUR(119, 136, 153));
    edit->markerDefine(FINISHED_MARKER, SC_MARK_ROUNDRECT);
    edit->markerDefine(FINISHED_MARKER + 1, SC_MARK_BACKGROUND);
    edit->indicSetFore(FINISHED_INDICATOR, SCINTILLA_COLOUR(119, 136, 153));
    edit->indicSetAlpha(FINISHED_INDICATOR, 220);
    edit->indicSetOutlineAlpha(FINISHED_INDICATOR, 255);
    edit->indicSetUnder(FINISHED_INDICATOR, true);
    edit->indicSetStyle(FINISHED_INDICATOR, INDIC_STRAIGHTBOX);
    edit->indicSetHoverFore(FINISHED_INDICATOR, SCINTILLA_COLOUR(119, 136, 153));
    edit->indicSetHoverStyle(FINISHED_INDICATOR, INDIC_STRAIGHTBOX);

    // C# Red
    edit->markerSetBack(BREAKPOINT_MARKER, SCINTILLA_COLOUR(255, 0, 0));
    edit->markerSetBack(BREAKPOINT_MARKER + 1, SCINTILLA_COLOUR(255, 0, 0));
    edit->markerDefine(BREAKPOINT_MARKER, SC_MARK_CIRCLE);
    edit->markerDefine(BREAKPOINT_MARKER + 1, SC_MARK_BACKGROUND);
  }
}