mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 09:30:44 +00:00
c539510da8
* When transferring over the network, we keep texture data consistently in GL origin-bottom-left order. This means we can just flip images on display and otherwise have things consistently behaving, while still preserving the behaviour of flipping on saving to disk to try and mostly 'do the right thing' when saving an image. * The behaviour should be the same as before except for remote proxying which is fixed. The behaviour for GL is still that compressed images saved as compressed will appear to flip vertically from what is natively displayed in the UI, but I think this is the only sensible way to behave (and anyway, flipping compressed images is far too involved to be worthwhile).
288 lines
8.8 KiB
C++
288 lines
8.8 KiB
C++
#if defined(__linux__)
|
|
#define RENDERDOC_WINDOWING_XLIB 1
|
|
#define RENDERDOC_WINDOWING_XCB 1
|
|
#endif
|
|
|
|
#include "TextureViewer.h"
|
|
#include "Code/Core.h"
|
|
#include "FlowLayout.h"
|
|
#include "ui_TextureViewer.h"
|
|
|
|
#if defined(__linux__)
|
|
#include <GL/glx.h>
|
|
#include <X11/Xlib.h>
|
|
#include <xcb/xcb.h>
|
|
#include <QX11Info>
|
|
#endif
|
|
|
|
TextureViewer::TextureViewer(Core *core, QWidget *parent)
|
|
: QFrame(parent), ui(new Ui::TextureViewer), m_Core(core)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
m_Core->AddLogViewer(this);
|
|
|
|
ui->render->SetOutput(NULL);
|
|
ui->pixelContext->SetOutput(NULL);
|
|
m_Output = NULL;
|
|
|
|
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 |
|
|
ToolWindowManager::DisableDraggableTab);
|
|
|
|
ToolWindowManager::AreaReference ref(ToolWindowManager::AddTo,
|
|
ui->dockarea->areaOf(renderContainer));
|
|
|
|
/*
|
|
QWidget *lockedTabTest = new QWidget(this);
|
|
lockedTabTest->setWindowTitle(tr("Locked Tab #1"));
|
|
|
|
ui->dockarea->addToolWindow(lockedTabTest, ref);
|
|
ui->dockarea->setToolWindowProperties(lockedTabTest, ToolWindowManager::DisallowUserDocking |
|
|
ToolWindowManager::HideCloseButton);
|
|
|
|
lockedTabTest = new QWidget(this);
|
|
lockedTabTest->setWindowTitle(tr("Locked Tab #2"));
|
|
|
|
ui->dockarea->addToolWindow(lockedTabTest, ref);
|
|
ui->dockarea->setToolWindowProperties(lockedTabTest, ToolWindowManager::DisallowUserDocking |
|
|
ToolWindowManager::HideCloseButton);
|
|
|
|
lockedTabTest = new QWidget(this);
|
|
lockedTabTest->setWindowTitle(tr("Locked Tab #3"));
|
|
|
|
ui->dockarea->addToolWindow(lockedTabTest, ref);
|
|
ui->dockarea->setToolWindowProperties(lockedTabTest, ToolWindowManager::DisallowUserDocking |
|
|
ToolWindowManager::HideCloseButton);
|
|
|
|
lockedTabTest = new QWidget(this);
|
|
lockedTabTest->setWindowTitle(tr("Locked Tab #4"));
|
|
|
|
ui->dockarea->addToolWindow(lockedTabTest, ref);
|
|
ui->dockarea->setToolWindowProperties(lockedTabTest, ToolWindowManager::DisallowUserDocking |
|
|
ToolWindowManager::HideCloseButton);*/
|
|
|
|
ui->dockarea->addToolWindow(
|
|
ui->resourceThumbs, ToolWindowManager::AreaReference(ToolWindowManager::RightOf,
|
|
ui->dockarea->areaOf(renderContainer)));
|
|
ui->dockarea->setToolWindowProperties(ui->resourceThumbs, ToolWindowManager::HideCloseButton);
|
|
|
|
ui->dockarea->addToolWindow(
|
|
ui->targetThumbs, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
|
|
ui->dockarea->areaOf(ui->resourceThumbs)));
|
|
ui->dockarea->setToolWindowProperties(ui->targetThumbs, ToolWindowManager::HideCloseButton);
|
|
|
|
// need to add a way to make this less than 50% programmatically
|
|
ui->dockarea->addToolWindow(ui->pixelContextLayout, ToolWindowManager::AreaReference(
|
|
ToolWindowManager::BottomOf,
|
|
ui->dockarea->areaOf(ui->targetThumbs)));
|
|
ui->dockarea->setToolWindowProperties(ui->pixelContextLayout, ToolWindowManager::HideCloseButton);
|
|
|
|
ui->dockarea->setAllowFloatingWindow(false);
|
|
ui->dockarea->setRubberBandLineWidth(50);
|
|
|
|
renderContainer->setWindowTitle(tr("OM RenderTarget 0 - GBuffer Colour"));
|
|
ui->pixelContextLayout->setWindowTitle(tr("Pixel Context"));
|
|
ui->targetThumbs->setWindowTitle(tr("OM Targets"));
|
|
ui->resourceThumbs->setWindowTitle(tr("PS Resources"));
|
|
|
|
QVBoxLayout *vertical = new QVBoxLayout(this);
|
|
|
|
vertical->setSpacing(3);
|
|
vertical->setContentsMargins(0, 0, 0, 0);
|
|
|
|
QWidget *flow1widget = new QWidget(this);
|
|
QWidget *flow2widget = new QWidget(this);
|
|
|
|
FlowLayout *flow1 = new FlowLayout(flow1widget, 0, 3, 3);
|
|
FlowLayout *flow2 = new FlowLayout(flow2widget, 0, 3, 3);
|
|
|
|
flow1widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
|
flow2widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
|
|
|
flow1->addWidget(ui->channelsToolbar);
|
|
flow1->addWidget(ui->subresourceToolbar);
|
|
flow1->addWidget(ui->actionToolbar);
|
|
|
|
flow2->addWidget(ui->zoomToolbar);
|
|
flow2->addWidget(ui->overlayToolbar);
|
|
flow2->addWidget(ui->rangeToolbar);
|
|
|
|
vertical->addWidget(flow1widget);
|
|
vertical->addWidget(flow2widget);
|
|
vertical->addWidget(ui->dockarea);
|
|
|
|
Ui_TextureViewer *u = ui;
|
|
u->pixelcontextgrid->setAlignment(u->pushButton, Qt::AlignCenter);
|
|
u->pixelcontextgrid->setAlignment(u->pushButton_2, Qt::AlignCenter);
|
|
}
|
|
|
|
TextureViewer::~TextureViewer()
|
|
{
|
|
m_Core->RemoveLogViewer(this);
|
|
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 = m_TexDisplay.texid;
|
|
|
|
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)
|
|
|
|
WindowingSystem system = eWindowingSystem_Win32;
|
|
HWND wnd = (HWND)ui->render->winId();
|
|
|
|
#elif defined(__linux__)
|
|
|
|
XCBWindowData xcb = {
|
|
QX11Info::connection(), (xcb_window_t)ui->render->winId(),
|
|
};
|
|
|
|
XlibWindowData xlib = {QX11Info::display(), (Drawable)ui->render->winId()};
|
|
|
|
rdctype::array<WindowingSystem> systems;
|
|
m_Core->Renderer()->BlockInvoke(
|
|
[&systems](IReplayRenderer *r) { r->GetSupportedWindowSystems(&systems); });
|
|
|
|
WindowingSystem system = eWindowingSystem_Unknown;
|
|
void *wnd = NULL;
|
|
|
|
// prefer XCB
|
|
for(int32_t i = 0; i < systems.count; i++)
|
|
{
|
|
if(systems[i] == eWindowingSystem_XCB)
|
|
{
|
|
system = eWindowingSystem_XCB;
|
|
wnd = &xcb;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(int32_t i = 0; wnd == NULL && i < systems.count; i++)
|
|
{
|
|
if(systems[i] == eWindowingSystem_Xlib)
|
|
{
|
|
system = eWindowingSystem_Xlib;
|
|
wnd = &xlib;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
#error "Unknown platform"
|
|
|
|
#endif
|
|
|
|
m_Core->Renderer()->BlockInvoke([system, wnd, this](IReplayRenderer *r) {
|
|
m_Output = r->CreateOutput(system, wnd, eOutputType_TexDisplay);
|
|
ui->render->SetOutput(m_Output);
|
|
|
|
OutputConfig c = {eOutputType_TexDisplay};
|
|
m_Output->SetOutputConfig(c);
|
|
});
|
|
}
|
|
|
|
void TextureViewer::OnLogfileClosed()
|
|
{
|
|
m_Output = NULL;
|
|
ui->render->SetOutput(NULL);
|
|
}
|
|
|
|
void TextureViewer::OnEventSelected(uint32_t eventID)
|
|
{
|
|
m_Core->Renderer()->AsyncInvoke([this](IReplayRenderer *) {
|
|
|
|
TextureDisplay &d = m_TexDisplay;
|
|
|
|
if(m_Core->APIProps().pipelineType == eGraphicsAPI_D3D11)
|
|
{
|
|
d.texid = m_Core->CurD3D11PipelineState.m_OM.RenderTargets[0].Resource;
|
|
}
|
|
else if(m_Core->APIProps().pipelineType == eGraphicsAPI_OpenGL)
|
|
{
|
|
d.texid = m_Core->CurGLPipelineState.m_FB.m_DrawFBO.Color[0].Obj;
|
|
}
|
|
else
|
|
{
|
|
const VulkanPipelineState &pipe = m_Core->CurVulkanPipelineState;
|
|
if(pipe.Pass.renderpass.colorAttachments.count > 0)
|
|
d.texid = pipe.Pass.framebuffer.attachments[pipe.Pass.renderpass.colorAttachments[0]].img;
|
|
|
|
if(d.texid == ResourceId())
|
|
{
|
|
const FetchDrawcall *draw = m_Core->CurDrawcall();
|
|
if(draw)
|
|
d.texid = draw->copyDestination;
|
|
}
|
|
}
|
|
d.mip = 0;
|
|
d.sampleIdx = ~0U;
|
|
d.overlay = eTexOverlay_None;
|
|
d.CustomShader = ResourceId();
|
|
d.HDRMul = -1.0f;
|
|
d.linearDisplayAsGamma = true;
|
|
d.FlipY = false;
|
|
d.rangemin = 0.0f;
|
|
d.rangemax = 1.0f;
|
|
d.scale = -1.0f;
|
|
d.offx = 0.0f;
|
|
d.offy = 0.0f;
|
|
d.sliceFace = 0;
|
|
d.rawoutput = false;
|
|
d.lightBackgroundColour = d.darkBackgroundColour = FloatVector(0.0f, 0.0f, 0.0f, 0.0f);
|
|
d.Red = d.Green = d.Blue = true;
|
|
d.Alpha = false;
|
|
m_Output->SetTextureDisplay(d);
|
|
|
|
FetchTexture *tex = m_Core->GetTexture(d.texid);
|
|
|
|
GUIInvoke::call([this, tex]() {
|
|
if(tex)
|
|
ui->renderContainer->setWindowTitle(tr(tex->name.elems));
|
|
|
|
ui->render->update();
|
|
});
|
|
});
|
|
}
|