diff --git a/qrenderdoc/Widgets/CustomPaintWidget.cpp b/qrenderdoc/Widgets/CustomPaintWidget.cpp index 38816de87..d661b4f0b 100644 --- a/qrenderdoc/Widgets/CustomPaintWidget.cpp +++ b/qrenderdoc/Widgets/CustomPaintWidget.cpp @@ -14,6 +14,16 @@ CustomPaintWidget::~CustomPaintWidget() } +void CustomPaintWidget::mousePressEvent(QMouseEvent *e) +{ + emit clicked(e); +} + +void CustomPaintWidget::mouseMoveEvent(QMouseEvent *e) +{ + emit mouseMove(e); +} + void CustomPaintWidget::paintEvent(QPaintEvent *e) { if(m_Output) diff --git a/qrenderdoc/Widgets/CustomPaintWidget.h b/qrenderdoc/Widgets/CustomPaintWidget.h index f7b4dbd9a..b8a634d76 100644 --- a/qrenderdoc/Widgets/CustomPaintWidget.h +++ b/qrenderdoc/Widgets/CustomPaintWidget.h @@ -14,7 +14,13 @@ class CustomPaintWidget : public QWidget void SetOutput(IReplayOutput *out) { m_Output = out; } - signals: + signals: + void clicked(QMouseEvent *e); + void mouseMove(QMouseEvent *e); + + private slots: + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); public slots: diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index ca082b655..d4ed7f4de 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -26,6 +26,9 @@ m_Core(core) QWidget *renderContainer = ui->renderContainer; + QObject::connect(ui->render, &CustomPaintWidget::clicked, this, &TextureViewer::on_render_clicked); + QObject::connect(ui->render, &CustomPaintWidget::mouseMove, this, &TextureViewer::on_render_clicked); + ui->dockarea->addToolWindow(ui->renderContainer, ToolWindowManager::EmptySpace); ui->dockarea->setToolWindowProperties(renderContainer, ToolWindowManager::DisallowUserDocking | ToolWindowManager::HideCloseButton | @@ -113,6 +116,39 @@ TextureViewer::~TextureViewer() delete ui; } +void TextureViewer::on_render_clicked(QMouseEvent *e) +{ + uint32_t x = (uint32_t)e->x(); + uint32_t y = (uint32_t)e->y(); + + if(e->buttons() & Qt::RightButton) + { + m_Core->Renderer()->AsyncInvoke([this,x,y](IReplayRenderer *) { + + ResourceId id; + if(m_Core->APIProps().pipelineType == ePipelineState_D3D11) + id = m_Core->CurD3D11PipelineState.m_OM.RenderTargets[0].Resource; + else + id = m_Core->CurGLPipelineState.m_FB.m_DrawFBO.Color[0].Obj; + + PixelValue val; + ReplayOutput_PickPixel(m_Output, id, false, x, y, 0, 0, 0, &val); + + QString str = QString("Pixel %1 %2 %3 %4").arg(val.value_f[0]).arg(val.value_f[1]).arg(val.value_f[2]).arg(val.value_f[3]); + + GUIInvoke::call([this,str,val]() { + ui->statusText->setText(str); + QPalette Pal(palette()); + + // set black background + Pal.setColor(QPalette::Background, QColor(int(val.value_f[0]*255), int(val.value_f[1]*255), int(val.value_f[2]*255))); + ui->pickSwatch->setAutoFillBackground(true); + ui->pickSwatch->setPalette(Pal); + }); + }); + } +} + void TextureViewer::OnLogfileLoaded() { #if defined(WIN32) diff --git a/qrenderdoc/Windows/TextureViewer.h b/qrenderdoc/Windows/TextureViewer.h index 4902d22eb..7f952bf31 100644 --- a/qrenderdoc/Windows/TextureViewer.h +++ b/qrenderdoc/Windows/TextureViewer.h @@ -2,6 +2,7 @@ #define TEXTUREVIEWER_H #include +#include #include "Code/Core.h" @@ -22,6 +23,9 @@ class TextureViewer : public QFrame, public ILogViewerForm void OnLogfileClosed(); void OnEventSelected(uint32_t frameID, uint32_t eventID); + private slots: + void on_render_clicked(QMouseEvent *e); + private: Ui::TextureViewer *ui; Core *m_Core; diff --git a/qrenderdoc/Windows/TextureViewer.ui b/qrenderdoc/Windows/TextureViewer.ui index bb7843b35..751d6c625 100644 --- a/qrenderdoc/Windows/TextureViewer.ui +++ b/qrenderdoc/Windows/TextureViewer.ui @@ -842,7 +842,7 @@ - + 0 @@ -864,7 +864,7 @@ - + 0 diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 82ef042b8..c38a260d0 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -25,6 +25,8 @@ #include "vk_core.h" #include "serialise/string_utils.h" +// VKTODOHIGH need to call vkResetCommandBuffer() before calling vkBeginCommandBuffer() + // VKTODOLOW dirty buffers should propagate through to their memory somehow // images can be separately dirty since we can't just copy their memory // (tiling could be different) diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 074da43ac..30c84d549 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -265,7 +265,96 @@ vector VulkanReplay::GetBuffers() void VulkanReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace, uint32_t mip, uint32_t sample, float pixel[4]) { - RDCUNIMPLEMENTED("PickPixel"); + //VULKANNOTIMP("PickPixel"); + + ResourceId resid; + VkImage fakeBBIm = VK_NULL_HANDLE; + VkDeviceMemory fakeBBMem = VK_NULL_HANDLE; + m_pDriver->GetFakeBB(resid, fakeBBIm, fakeBBMem); + + VkDevice dev = m_pDriver->GetDev(); + VkCmdBuffer cmd = m_pDriver->GetCmd(); + VkQueue q = m_pDriver->GetQ(); + const VulkanFunctions &vk = m_pDriver->m_Real; + + VkDeviceMemory readbackmem = VK_NULL_HANDLE; + + { + VkMemoryAllocInfo allocInfo = { + /*.sType =*/ VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO, + /*.pNext =*/ NULL, + /*.allocationSize =*/ 128, + /*.memoryTypeIndex =*/ 0, // VKTODOHIGH find appropriate memory type index + }; + + VkResult res = vk.vkAllocMemory(dev, &allocInfo, &readbackmem); + RDCASSERT(res == VK_SUCCESS); + + VkBufferCreateInfo bufInfo = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL, + 128, VK_BUFFER_USAGE_GENERAL, 0, + VK_SHARING_MODE_EXCLUSIVE, 0, NULL, + }; + + VkBuffer destbuf; + + res = vk.vkCreateBuffer(dev, &bufInfo, &destbuf); + RDCASSERT(res == VK_SUCCESS); + + res = vk.vkBindBufferMemory(dev, destbuf, readbackmem, 0); + RDCASSERT(res == VK_SUCCESS); + + // VKTODOHIGH find out the actual current image state + VkImageMemoryBarrier fakeTrans = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, + 0, 0, VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, + 0, 0, fakeBBIm, + { VK_IMAGE_ASPECT_COLOR, 0, 1, 0, 1 } }; + + VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT }; + + res = vk.vkBeginCommandBuffer(cmd, &beginInfo); + RDCASSERT(res == VK_SUCCESS); + + void *barrier = (void *)&fakeTrans; + vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + fakeTrans.oldLayout = fakeTrans.newLayout; + + VkBufferImageCopy region = { + 0, 128, 1, + { VK_IMAGE_ASPECT_COLOR, 0, 0}, { (int)x, (int)y, 0 }, + { 1, 1, 1 }, + }; + vk.vkCmdCopyImageToBuffer(cmd, fakeBBIm, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, destbuf, 1, ®ion); + + fakeTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI; + vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + vk.vkEndCommandBuffer(cmd); + + vk.vkQueueSubmit(q, 1, &cmd, VK_NULL_HANDLE); + + vk.vkQueueWaitIdle(q); + } + + // VKTODOHIGH ultra cheeky - map memory directly without copying + // to host-visible memory + byte *pData = NULL; + vk.vkMapMemory(dev, readbackmem, 0, 0, 0, (void **)&pData); + + RDCASSERT(pData != NULL); + + // VKTODOHIGH assuming BGRA + pixel[2] = float(pData[0])/255.0f; + pixel[1] = float(pData[1])/255.0f; + pixel[0] = float(pData[2])/255.0f; + pixel[3] = float(pData[3])/255.0f; + + vk.vkUnmapMemory(dev, readbackmem); + + vk.vkDeviceWaitIdle(dev); + + vk.vkFreeMemory(dev, readbackmem); } uint32_t VulkanReplay::PickVertex(uint32_t frameID, uint32_t eventID, MeshDisplay cfg, uint32_t x, uint32_t y) @@ -428,6 +517,7 @@ void VulkanReplay::FlipOutputWindow(uint64_t id) VkDeviceMemory fakeBBMem = VK_NULL_HANDLE; m_pDriver->GetFakeBB(resid, fakeBBIm, fakeBBMem); + // VKTODOHIGH find out the actual current image state VkImageMemoryBarrier fakeTrans = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, 0, 0, VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL,