diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp
index d0f3a0736..e9c6ab6c2 100644
--- a/qrenderdoc/Windows/TextureViewer.cpp
+++ b/qrenderdoc/Windows/TextureViewer.cpp
@@ -549,9 +549,154 @@ void TextureViewer::UI_UpdateTextureDetails()
void TextureViewer::UI_OnTextureSelectionChanged(bool newdraw)
{
+ FetchTexture *texptr = m_Core->GetTexture(m_TexDisplay.texid);
+
+ // reset high-water mark
+ m_HighWaterStatusLength = 0;
+
+ if(texptr == NULL)
+ return;
+
+ FetchTexture &tex = *texptr;
+
+ bool newtex = true;
+
+ // refresh scroll position
+ setScrollPosition(getScrollPosition());
+
+ UI_UpdateStatusText();
+
+ ui->mipLevel->clear();
+
+ m_TexDisplay.mip = 0;
+ m_TexDisplay.sliceFace = 0;
+
+ bool usemipsettings = true;
+ bool useslicesettings = true;
+
+ if(tex.msSamp > 1)
+ {
+ for(uint32_t i = 0; i < tex.msSamp; i++)
+ ui->mipLevel->addItem(QString("Sample %1").arg(i));
+
+ // add an option to display unweighted average resolved value,
+ // to get an idea of how the samples average
+ if(tex.format.compType != eCompType_UInt && tex.format.compType != eCompType_SInt &&
+ tex.format.compType != eCompType_Depth && (tex.creationFlags & eTextureCreate_DSV) == 0)
+ ui->mipLevel->addItem(tr("Average val"));
+
+ ui->mipLabel->setText(tr("Sample"));
+
+ ui->mipLevel->setCurrentIndex(0);
+ }
+ else
+ {
+ for(uint32_t i = 0; i < tex.mips; i++)
+ ui->mipLevel->addItem(QString::number(i) + QString(" - ") +
+ QString::number(qMax(1U, tex.width >> i)) + QString("x") +
+ QString::number(qMax(1U, tex.height >> i)));
+
+ ui->mipLabel->setText(tr("Mip"));
+
+ int highestMip = -1;
+
+ // only switch to the selected mip for outputs, and when changing drawcall
+ /*
+ if (!CurrentTextureIsLocked && m_Following.Type != FollowType.ReadOnly && newdraw)
+ highestMip = m_Following.GetHighestMip(m_Core);
+ */
+
+ // assuming we get a valid mip for the highest mip, only switch to it
+ // if we've selected a new texture, or if it's different than the last mip.
+ // This prevents the case where the user has clicked on another mip and
+ // we don't want to snap their view back when stepping between events with the
+ // same mip used. But it does mean that if they are stepping between
+ // events with different mips used, then we will update in that case.
+ if(highestMip >= 0 && (newtex || highestMip != m_PrevHighestMip))
+ {
+ usemipsettings = false;
+ ui->mipLevel->setCurrentIndex(qBound(0, highestMip, (int)tex.mips - 1));
+ }
+
+ if(ui->mipLevel->currentIndex() == -1)
+ ui->mipLevel->setCurrentIndex(qBound(0, m_PrevHighestMip, (int)tex.mips - 1));
+
+ m_PrevHighestMip = highestMip;
+ }
+
+ if(tex.mips == 1 && tex.msSamp <= 1)
+ ui->mipLevel->setEnabled(false);
+ else
+ ui->mipLevel->setEnabled(true);
+
+ ui->sliceFace->clear();
+
+ if(tex.numSubresources == tex.mips && tex.depth <= 1)
+ {
+ ui->sliceFace->setEnabled(false);
+ }
+ else
+ {
+ ui->mipLevel->setEnabled(true);
+
+ QString cubeFaces[] = {"X+", "X-", "Y+", "Y-", "Z+", "Z-"};
+
+ uint32_t numSlices = (qMax(1U, tex.depth) * tex.numSubresources) / tex.mips;
+
+ // for 3D textures, display the number of slices at this mip
+ if(tex.depth > 1)
+ numSlices = qMax(1u, tex.depth >> (int)ui->mipLevel->currentIndex());
+
+ for(uint32_t i = 0; i < numSlices; i++)
+ {
+ if(tex.cubemap)
+ {
+ QString name = cubeFaces[i % 6];
+ if(numSlices > 6)
+ name = QString("[%1] %2").arg(i / 6).arg(
+ cubeFaces[i % 6]); // Front 1, Back 2, 3, 4 etc for cube arrays
+ ui->sliceFace->addItem(name);
+ }
+ else
+ {
+ ui->sliceFace->addItem(tr("Slice ") + i);
+ }
+ }
+
+ int firstArraySlice = -1;
+ // only switch to the selected mip for outputs, and when changing drawcall
+ /*
+ if (!CurrentTextureIsLocked && m_Following.Type != FollowType.ReadOnly && newdraw)
+ firstArraySlice = m_Following.GetFirstArraySlice(m_Core);
+ */
+
+ // see above with highestMip and prevHighestMip for the logic behind this
+ if(firstArraySlice >= 0 && (newtex || firstArraySlice != m_PrevFirstArraySlice))
+ {
+ useslicesettings = false;
+ ui->sliceFace->setCurrentIndex(qBound(0, firstArraySlice, (int)numSlices - 1));
+ }
+
+ if(ui->sliceFace->currentIndex() == -1)
+ ui->sliceFace->setCurrentIndex(qBound(0, m_PrevFirstArraySlice, (int)numSlices - 1));
+
+ m_PrevFirstArraySlice = firstArraySlice;
+ }
+
UI_UpdateFittedScale();
UI_UpdateTextureDetails();
UI_UpdateChannels();
+
+ m_Core->Renderer()->AsyncInvoke([this](IReplayRenderer *) {
+ // RT_UpdateVisualRange(r);
+
+ RT_UpdateAndDisplay();
+
+ if(m_Output != NULL)
+ RT_PickPixelsAndUpdate();
+
+ // TODO - GetUsage and update TimelineBar
+ });
}
void TextureViewer::UI_UpdateChannels()
@@ -1206,3 +1351,82 @@ void TextureViewer::on_checkerBack_clicked()
ui->pixelcontextgrid->update();
}
}
+
+void TextureViewer::on_mipLevel_currentIndexChanged(int index)
+{
+ FetchTexture *texptr = m_Core->GetTexture(m_TexDisplay.texid);
+ if(texptr == NULL)
+ return;
+
+ FetchTexture &tex = *texptr;
+
+ uint prevSlice = m_TexDisplay.sliceFace;
+
+ if(tex.mips > 1)
+ {
+ m_TexDisplay.mip = (uint32_t)index;
+ m_TexDisplay.sampleIdx = 0;
+ }
+ else
+ {
+ m_TexDisplay.mip = 0;
+ m_TexDisplay.sampleIdx = (uint32_t)index;
+ if(m_TexDisplay.sampleIdx == tex.msSamp)
+ m_TexDisplay.sampleIdx = ~0U;
+ }
+
+ // For 3D textures, update the slice list for this mip
+ if(tex.depth > 1)
+ {
+ uint32_t newSlice = prevSlice >> (int)m_TexDisplay.mip;
+
+ uint32_t numSlices = qMax(1U, tex.depth >> (int)m_TexDisplay.mip);
+
+ ui->sliceFace->clear();
+
+ for(uint32_t i = 0; i < numSlices; i++)
+ ui->sliceFace->addItem(tr("Slice ") + i);
+
+ // changing sliceFace index will handle updating range & re-picking
+ ui->sliceFace->setCurrentIndex((int)qBound(0U, newSlice, numSlices - 1));
+
+ return;
+ }
+
+ // INVOKE_MEMFN(RT_UpdateVisualRange);
+
+ if(m_Output != NULL && m_PickedPoint.x() >= 0 && m_PickedPoint.y() >= 0)
+ {
+ m_Core->Renderer()->AsyncInvoke([this](IReplayRenderer *) {
+ if(m_Output != NULL)
+ RT_PickPixelsAndUpdate();
+ });
+ }
+
+ INVOKE_MEMFN(RT_UpdateAndDisplay);
+}
+
+void TextureViewer::on_sliceFace_currentIndexChanged(int index)
+{
+ FetchTexture *texptr = m_Core->GetTexture(m_TexDisplay.texid);
+ if(texptr == NULL)
+ return;
+
+ FetchTexture &tex = *texptr;
+ m_TexDisplay.sliceFace = (uint32_t)index;
+
+ if(tex.depth > 1)
+ m_TexDisplay.sliceFace = (uint32_t)(index << (int)m_TexDisplay.mip);
+
+ // INVOKE_MEMFN(RT_UpdateVisualRange);
+
+ if(m_Output != NULL && m_PickedPoint.x() >= 0 && m_PickedPoint.y() >= 0)
+ {
+ m_Core->Renderer()->AsyncInvoke([this](IReplayRenderer *) {
+ if(m_Output != NULL)
+ RT_PickPixelsAndUpdate();
+ });
+ }
+
+ INVOKE_MEMFN(RT_UpdateAndDisplay);
+}
diff --git a/qrenderdoc/Windows/TextureViewer.h b/qrenderdoc/Windows/TextureViewer.h
index e1d187490..dff548d48 100644
--- a/qrenderdoc/Windows/TextureViewer.h
+++ b/qrenderdoc/Windows/TextureViewer.h
@@ -37,6 +37,9 @@ private slots:
void on_zoomOption_currentIndexChanged(int index);
void on_zoomOption_returnPressed();
+ void on_mipLevel_currentIndexChanged(int index);
+ void on_sliceFace_currentIndexChanged(int index);
+
void on_overlay_currentIndexChanged(int index);
void on_zoomRange_clicked();
@@ -92,6 +95,8 @@ private:
PixelValue m_CurHoverValue;
int m_HighWaterStatusLength = 0;
+ int m_PrevFirstArraySlice = -1;
+ int m_PrevHighestMip = -1;
Ui::TextureViewer *ui;
Core *m_Core = NULL;
diff --git a/qrenderdoc/Windows/TextureViewer.ui b/qrenderdoc/Windows/TextureViewer.ui
index a54bc7ab2..85e05d261 100644
--- a/qrenderdoc/Windows/TextureViewer.ui
+++ b/qrenderdoc/Windows/TextureViewer.ui
@@ -528,7 +528,14 @@
-
-
+
+
+ 20
+
+
+ QComboBox::AdjustToContents
+
+
-
@@ -538,7 +545,14 @@
-
-
+
+
+ 20
+
+
+ QComboBox::AdjustToContents
+
+
@@ -710,6 +724,9 @@
128
+
+ QComboBox::NoInsert
+
-