mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-23 22:25:55 +00:00
Add a preview window ability to remote servers. Always on for android
* On android in particular this shows some sense of what's happening on the device and gives user feedback that it's not completely dead.
This commit is contained in:
@@ -488,6 +488,7 @@ DECLARE_REFLECTION_STRUCT(ResourceId);
|
||||
// here and document them immediately below. They can be linked to from anywhere by name.
|
||||
typedef std::function<bool()> RENDERDOC_KillCallback;
|
||||
typedef std::function<void(float)> RENDERDOC_ProgressCallback;
|
||||
typedef std::function<WindowingData(bool, const rdcarray<WindowingSystem> &)> RENDERDOC_PreviewWindowCallback;
|
||||
|
||||
DOCUMENT(R"(A stateful output handle that contains the current configuration for one particular view
|
||||
of the capture. This allows multiple outputs to run independently without interfering with each
|
||||
@@ -515,6 +516,19 @@ The different types are enumerated in :class:`ReplayOutputType`.
|
||||
|
||||
:param float progress: The latest progress amount.
|
||||
|
||||
.. function:: PreviewWindowCallback()
|
||||
|
||||
Not an actual member function - the signature for any ``PreviewWindowCallback`` callbacks.
|
||||
|
||||
Called when a preview window could optionally be opened to display some information. It will be
|
||||
called repeatedly with :paramref:`active` set to ``True`` to allow any platform-specific message
|
||||
pumping.
|
||||
|
||||
:param bool active: ``True`` if a preview window is active/opened, ``False`` if it has closed.
|
||||
:return: The windowing data for a preview window, or empty/default values if no window should be
|
||||
created.
|
||||
:rtype: WindowingData
|
||||
|
||||
.. data:: NoResult
|
||||
|
||||
No result was found in e.g. :meth:`PickVertex`.
|
||||
@@ -1858,9 +1872,12 @@ This function will block until a remote connection tells the server to shut down
|
||||
:param int port: The port to listen on, or the default port if 0.
|
||||
:param KillCallback killReplay: A callback that returns a ``bool`` indicating if the server should
|
||||
be shut down or not.
|
||||
:param PreviewWindowCallback previewWindow: A callback that returns information for a preview window
|
||||
when the server wants to display some preview of the ongoing replay.
|
||||
)");
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_BecomeRemoteServer(
|
||||
const char *listenhost, uint32_t port, RENDERDOC_KillCallback killReplay);
|
||||
const char *listenhost, uint32_t port, RENDERDOC_KillCallback killReplay,
|
||||
RENDERDOC_PreviewWindowCallback previewWindow);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Injection/execution capture functions.
|
||||
|
||||
@@ -387,7 +387,8 @@ public:
|
||||
bool IsReplayApp() const { return m_Replay; }
|
||||
const string &GetConfigSetting(string name) { return m_ConfigSettings[name]; }
|
||||
void SetConfigSetting(string name, string value) { m_ConfigSettings[name] = value; }
|
||||
void BecomeRemoteServer(const char *listenhost, uint16_t port, RENDERDOC_KillCallback killReplay);
|
||||
void BecomeRemoteServer(const char *listenhost, uint16_t port, RENDERDOC_KillCallback killReplay,
|
||||
RENDERDOC_PreviewWindowCallback previewWindow);
|
||||
|
||||
void SetCaptureOptions(const CaptureOptions &opts);
|
||||
const CaptureOptions &GetCaptureOptions() const { return m_Options; }
|
||||
|
||||
@@ -146,7 +146,8 @@ static void InactiveRemoteClientThread(ClientThread *threadData)
|
||||
}
|
||||
}
|
||||
|
||||
static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
static void ActiveRemoteClientThread(ClientThread *threadData,
|
||||
RENDERDOC_PreviewWindowCallback previewWindow)
|
||||
{
|
||||
Network::Socket *&client = threadData->socket;
|
||||
|
||||
@@ -197,7 +198,8 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
}
|
||||
|
||||
std::vector<std::string> tempFiles;
|
||||
IRemoteDriver *driver = NULL;
|
||||
IRemoteDriver *remoteDriver = NULL;
|
||||
IReplayDriver *replayDriver = NULL;
|
||||
ReplayProxy *proxy = NULL;
|
||||
RDCFile *rdc = NULL;
|
||||
Callstack::StackResolver *resolver = NULL;
|
||||
@@ -229,6 +231,9 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
{
|
||||
reader.EndChunk();
|
||||
|
||||
if(proxy)
|
||||
proxy->RefreshPreviewWindow();
|
||||
|
||||
WRITE_DATA_SCOPE();
|
||||
SCOPED_SERIALISE_CHUNK(eRemoteServer_Ping);
|
||||
}
|
||||
@@ -378,7 +383,7 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
|
||||
reader.EndChunk();
|
||||
|
||||
RDCASSERT(driver == NULL && proxy == NULL && rdc == NULL);
|
||||
RDCASSERT(remoteDriver == NULL && proxy == NULL && rdc == NULL);
|
||||
ReplayStatus status = ReplayStatus::InternalError;
|
||||
|
||||
rdc = new RDCFile();
|
||||
@@ -420,22 +425,32 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
}
|
||||
});
|
||||
|
||||
status = RenderDoc::Inst().CreateRemoteDriver(rdc, &driver);
|
||||
// if we have a replay driver, try to create it so we can display a local preview e.g.
|
||||
if(RenderDoc::Inst().HasReplayDriver(rdc->GetDriver()))
|
||||
{
|
||||
status = RenderDoc::Inst().CreateReplayDriver(rdc, &replayDriver);
|
||||
if(replayDriver)
|
||||
remoteDriver = replayDriver;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = RenderDoc::Inst().CreateRemoteDriver(rdc, &remoteDriver);
|
||||
}
|
||||
|
||||
if(status != ReplayStatus::Succeeded || driver == NULL)
|
||||
if(status != ReplayStatus::Succeeded || remoteDriver == NULL)
|
||||
{
|
||||
RDCERR("Failed to create remote driver for driver '%s'", rdc->GetDriverName().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
status = driver->ReadLogInitialisation(rdc, false);
|
||||
status = remoteDriver->ReadLogInitialisation(rdc, false);
|
||||
|
||||
if(status != ReplayStatus::Succeeded)
|
||||
{
|
||||
RDCERR("Failed to initialise remote driver.");
|
||||
|
||||
driver->Shutdown();
|
||||
driver = NULL;
|
||||
remoteDriver->Shutdown();
|
||||
remoteDriver = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -445,7 +460,7 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
Threading::JoinThread(ticker);
|
||||
Threading::CloseThread(ticker);
|
||||
|
||||
proxy = new ReplayProxy(reader, writer, driver);
|
||||
proxy = new ReplayProxy(reader, writer, remoteDriver, replayDriver, previewWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -692,11 +707,13 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
{
|
||||
reader.EndChunk();
|
||||
|
||||
if(driver)
|
||||
driver->Shutdown();
|
||||
driver = NULL;
|
||||
|
||||
SAFE_DELETE(proxy);
|
||||
|
||||
if(remoteDriver)
|
||||
remoteDriver->Shutdown();
|
||||
remoteDriver = NULL;
|
||||
replayDriver = NULL;
|
||||
|
||||
SAFE_DELETE(rdc);
|
||||
SAFE_DELETE(resolver);
|
||||
}
|
||||
@@ -746,9 +763,12 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
}
|
||||
}
|
||||
|
||||
if(driver)
|
||||
driver->Shutdown();
|
||||
SAFE_DELETE(proxy);
|
||||
|
||||
if(remoteDriver)
|
||||
remoteDriver->Shutdown();
|
||||
remoteDriver = NULL;
|
||||
replayDriver = NULL;
|
||||
SAFE_DELETE(rdc);
|
||||
SAFE_DELETE(resolver);
|
||||
|
||||
@@ -766,7 +786,8 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
|
||||
}
|
||||
|
||||
void RenderDoc::BecomeRemoteServer(const char *listenhost, uint16_t port,
|
||||
RENDERDOC_KillCallback killReplay)
|
||||
RENDERDOC_KillCallback killReplay,
|
||||
RENDERDOC_PreviewWindowCallback previewWindow)
|
||||
{
|
||||
Network::Socket *sock = Network::CreateServerSocket(listenhost, port, 1);
|
||||
|
||||
@@ -934,8 +955,9 @@ void RenderDoc::BecomeRemoteServer(const char *listenhost, uint16_t port,
|
||||
activeClientData->socket = client;
|
||||
activeClientData->allowExecution = allowExecution;
|
||||
|
||||
activeClientData->thread = Threading::CreateThread(
|
||||
[activeClientData]() { ActiveRemoteClientThread(activeClientData); });
|
||||
activeClientData->thread = Threading::CreateThread([activeClientData, previewWindow]() {
|
||||
ActiveRemoteClientThread(activeClientData, previewWindow);
|
||||
});
|
||||
|
||||
RDCLOG("Making active connection");
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
|
||||
ReplayProxy::~ReplayProxy()
|
||||
{
|
||||
ShutdownPreviewWindow();
|
||||
|
||||
if(m_Proxy)
|
||||
m_Proxy->Shutdown();
|
||||
m_Proxy = NULL;
|
||||
@@ -1295,6 +1297,9 @@ void ReplayProxy::Proxied_ReplayLog(ParamSerialiser ¶mser, ReturnSerialiser
|
||||
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
||||
m_Remote->ReplayLog(endEventID, replayType);
|
||||
|
||||
if(m_RemoteServer)
|
||||
m_PreviewEvent = endEventID;
|
||||
|
||||
if(retser.IsReading())
|
||||
{
|
||||
m_TextureProxyCache.clear();
|
||||
@@ -1834,6 +1839,141 @@ void ReplayProxy::EnsureBufCached(ResourceId bufid)
|
||||
}
|
||||
}
|
||||
|
||||
const DrawcallDescription *ReplayProxy::FindDraw(const rdcarray<DrawcallDescription> &drawcallList,
|
||||
uint32_t eventId)
|
||||
{
|
||||
for(const DrawcallDescription &d : drawcallList)
|
||||
{
|
||||
if(!d.children.empty())
|
||||
{
|
||||
const DrawcallDescription *draw = FindDraw(d.children, eventId);
|
||||
if(draw != NULL)
|
||||
return draw;
|
||||
}
|
||||
|
||||
if(d.eventId == eventId)
|
||||
return &d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ReplayProxy::InitPreviewWindow()
|
||||
{
|
||||
if(m_Replay && m_PreviewWindow)
|
||||
{
|
||||
WindowingData data = m_PreviewWindow(true, m_Replay->GetSupportedWindowSystems());
|
||||
if(data.system != WindowingSystem::Unknown)
|
||||
m_PreviewOutput = m_Replay->MakeOutputWindow(data, false);
|
||||
|
||||
m_FrameRecord = m_Replay->GetFrameRecord();
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayProxy::ShutdownPreviewWindow()
|
||||
{
|
||||
if(m_Replay && m_PreviewOutput)
|
||||
{
|
||||
m_Replay->DestroyOutputWindow(m_PreviewOutput);
|
||||
m_PreviewWindow(false, {});
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayProxy::RefreshPreviewWindow()
|
||||
{
|
||||
if(m_Replay && m_PreviewOutput)
|
||||
{
|
||||
m_Replay->BindOutputWindow(m_PreviewOutput, false);
|
||||
m_Replay->ClearOutputWindowColor(m_PreviewOutput, FloatVector(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
int32_t winWidth = 1;
|
||||
int32_t winHeight = 1;
|
||||
m_Replay->GetOutputWindowDimensions(m_PreviewOutput, winWidth, winHeight);
|
||||
|
||||
m_Replay->RenderCheckerboard();
|
||||
|
||||
const DrawcallDescription *curDraw = FindDraw(m_FrameRecord.drawcallList, m_PreviewEvent);
|
||||
|
||||
if(curDraw)
|
||||
{
|
||||
TextureDisplay cfg = {};
|
||||
|
||||
cfg.red = cfg.green = cfg.blue = true;
|
||||
cfg.alpha = false;
|
||||
|
||||
for(ResourceId id : curDraw->outputs)
|
||||
{
|
||||
if(id != ResourceId())
|
||||
{
|
||||
cfg.resourceId = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't get a colour target, try the depth target
|
||||
if(cfg.resourceId == ResourceId() && curDraw->depthOut != ResourceId())
|
||||
{
|
||||
cfg.resourceId = curDraw->depthOut;
|
||||
// red only for depth textures
|
||||
cfg.green = cfg.blue = false;
|
||||
}
|
||||
|
||||
// if we didn't get any target, use the copy destination
|
||||
if(cfg.resourceId == ResourceId())
|
||||
cfg.resourceId = curDraw->copyDestination;
|
||||
|
||||
// if we did get a texture, get the live ID for it
|
||||
if(cfg.resourceId != ResourceId())
|
||||
cfg.resourceId = m_Replay->GetLiveID(cfg.resourceId);
|
||||
|
||||
if(cfg.resourceId != ResourceId())
|
||||
{
|
||||
TextureDescription texInfo = m_Replay->GetTexture(cfg.resourceId);
|
||||
|
||||
cfg.typeHint = CompType::Typeless;
|
||||
cfg.rangeMin = 0.0f;
|
||||
cfg.rangeMax = 1.0f;
|
||||
cfg.flipY = false;
|
||||
cfg.hdrMultiplier = -1.0f;
|
||||
cfg.linearDisplayAsGamma = true;
|
||||
cfg.customShaderId = ResourceId();
|
||||
cfg.mip = 0;
|
||||
cfg.sliceFace = 0;
|
||||
cfg.sampleIdx = 0;
|
||||
cfg.rawOutput = false;
|
||||
cfg.backgroundColor = FloatVector(0, 0, 0, 0);
|
||||
cfg.overlay = DebugOverlay::NoOverlay;
|
||||
cfg.xOffset = 0.0f;
|
||||
cfg.yOffset = 0.0f;
|
||||
|
||||
float xScale = float(winWidth) / float(texInfo.width);
|
||||
float yScale = float(winHeight) / float(texInfo.height);
|
||||
|
||||
if(xScale > yScale)
|
||||
{
|
||||
// use the y scale, calculate the x offset to centre horizontally
|
||||
cfg.scale = yScale;
|
||||
|
||||
cfg.xOffset = (float(winWidth) - float(texInfo.width) * cfg.scale) / 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the x scale, calculate the y offset to centre vertically
|
||||
cfg.scale = xScale;
|
||||
|
||||
cfg.yOffset = (float(winHeight) - float(texInfo.height) * cfg.scale) / 2.0f;
|
||||
}
|
||||
|
||||
m_Replay->RenderTexture(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
m_Replay->FlipOutputWindow(m_PreviewOutput);
|
||||
|
||||
m_PreviewWindow(true, m_Replay->GetSupportedWindowSystems());
|
||||
}
|
||||
}
|
||||
|
||||
bool ReplayProxy::Tick(int type)
|
||||
{
|
||||
if(!m_RemoteServer)
|
||||
@@ -1927,6 +2067,8 @@ bool ReplayProxy::Tick(int type)
|
||||
default: RDCERR("Unexpected command %u", type); return false;
|
||||
}
|
||||
|
||||
RefreshPreviewWindow();
|
||||
|
||||
if(m_Writer.IsErrored() || m_Reader.IsErrored() || m_IsErrored)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -111,20 +111,38 @@ class ReplayProxy : public IReplayDriver
|
||||
{
|
||||
public:
|
||||
ReplayProxy(ReadSerialiser &reader, WriteSerialiser &writer, IReplayDriver *proxy)
|
||||
: m_Reader(reader), m_Writer(writer), m_Proxy(proxy), m_Remote(NULL), m_RemoteServer(false)
|
||||
: m_Reader(reader),
|
||||
m_Writer(writer),
|
||||
m_Proxy(proxy),
|
||||
m_Remote(NULL),
|
||||
m_Replay(NULL),
|
||||
m_RemoteServer(false)
|
||||
{
|
||||
GetAPIProperties();
|
||||
FetchStructuredFile();
|
||||
}
|
||||
|
||||
ReplayProxy(ReadSerialiser &reader, WriteSerialiser &writer, IRemoteDriver *remote)
|
||||
: m_Reader(reader), m_Writer(writer), m_Proxy(NULL), m_Remote(remote), m_RemoteServer(true)
|
||||
ReplayProxy(ReadSerialiser &reader, WriteSerialiser &writer, IRemoteDriver *remoteDriver,
|
||||
IReplayDriver *replayDriver, RENDERDOC_PreviewWindowCallback previewWindow)
|
||||
: m_Reader(reader),
|
||||
m_Writer(writer),
|
||||
m_Proxy(NULL),
|
||||
m_Remote(remoteDriver),
|
||||
m_Replay(replayDriver),
|
||||
m_PreviewWindow(previewWindow),
|
||||
m_RemoteServer(true)
|
||||
{
|
||||
RDCEraseEl(m_APIProps);
|
||||
|
||||
if(m_Replay)
|
||||
InitPreviewWindow();
|
||||
}
|
||||
|
||||
virtual ~ReplayProxy();
|
||||
|
||||
void InitPreviewWindow();
|
||||
void ShutdownPreviewWindow();
|
||||
|
||||
bool IsRemoteProxy() { return !m_RemoteServer; }
|
||||
void Shutdown() { delete this; }
|
||||
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
|
||||
@@ -394,6 +412,8 @@ public:
|
||||
return ResourceId();
|
||||
}
|
||||
|
||||
void RefreshPreviewWindow();
|
||||
|
||||
bool Tick(int type);
|
||||
|
||||
const D3D11Pipe::State &GetD3D11PipelineState() { return m_D3D11PipelineState; }
|
||||
@@ -521,6 +541,9 @@ private:
|
||||
void EnsureBufCached(ResourceId bufid);
|
||||
IMPLEMENT_FUNCTION_PROXIED(bool, NeedRemapForFetch, const ResourceFormat &format);
|
||||
|
||||
const DrawcallDescription *FindDraw(const rdcarray<DrawcallDescription> &drawcallList,
|
||||
uint32_t eventId);
|
||||
|
||||
struct TextureCacheEntry
|
||||
{
|
||||
ResourceId replayid;
|
||||
@@ -587,14 +610,35 @@ private:
|
||||
|
||||
std::map<ShaderReflKey, ShaderReflection *> m_ShaderReflectionCache;
|
||||
|
||||
// reader from the other side of the host <-> remote connection
|
||||
ReadSerialiser &m_Reader;
|
||||
// writer to the other side of the host <-> remote connection
|
||||
WriteSerialiser &m_Writer;
|
||||
|
||||
// the local proxy replay driver when on the host side, NULL on the remote server
|
||||
IReplayDriver *m_Proxy;
|
||||
// the remote driver on the remote server, NULL on the host side
|
||||
IRemoteDriver *m_Remote;
|
||||
// an *optional* replay driver on the remote server, could be NULL if not supported by the system
|
||||
// on the remote server. Always NULL on the host side.
|
||||
// This allows us to do some extra things on the remote server such as displaying a preview window
|
||||
IReplayDriver *m_Replay;
|
||||
|
||||
// true if we're the remote server, false if we're the host
|
||||
bool m_RemoteServer;
|
||||
|
||||
// The callback (if provided) that handles creating and ticking a preview window on the remote
|
||||
// host.
|
||||
RENDERDOC_PreviewWindowCallback m_PreviewWindow;
|
||||
// the ID of the output window to use for previewing on the remote host. Only valid/useful if
|
||||
// m_Replay is set
|
||||
uint64_t m_PreviewOutput = 0;
|
||||
|
||||
uint32_t m_PreviewEvent = 0;
|
||||
|
||||
bool m_IsErrored = false;
|
||||
|
||||
FrameRecord m_FrameRecord;
|
||||
APIProperties m_APIProps;
|
||||
|
||||
SDFile m_StructuredFile;
|
||||
|
||||
@@ -401,7 +401,8 @@ extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_GetDefaultRemoteServerP
|
||||
}
|
||||
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_BecomeRemoteServer(
|
||||
const char *listenhost, uint32_t port, RENDERDOC_KillCallback killReplay)
|
||||
const char *listenhost, uint32_t port, RENDERDOC_KillCallback killReplay,
|
||||
RENDERDOC_PreviewWindowCallback previewWindow)
|
||||
{
|
||||
if(listenhost == NULL || listenhost[0] == 0)
|
||||
listenhost = "0.0.0.0";
|
||||
@@ -410,10 +411,17 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_BecomeRemoteServer(
|
||||
if(!killReplay)
|
||||
killReplay = []() { return false; };
|
||||
|
||||
// ditto for preview windows
|
||||
if(!previewWindow)
|
||||
previewWindow = [](bool, const rdcarray<WindowingSystem> &) {
|
||||
WindowingData ret = {WindowingSystem::Unknown};
|
||||
return ret;
|
||||
};
|
||||
|
||||
if(port == 0)
|
||||
port = RENDERDOC_GetDefaultRemoteServerPort();
|
||||
|
||||
RenderDoc::Inst().BecomeRemoteServer(listenhost, (uint16_t)port, killReplay);
|
||||
RenderDoc::Inst().BecomeRemoteServer(listenhost, (uint16_t)port, killReplay, previewWindow);
|
||||
}
|
||||
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartSelfHostCapture(const char *dllname)
|
||||
|
||||
@@ -474,6 +474,7 @@ struct RemoteServerCommand : public Command
|
||||
"host", 'h', "The interface to listen on. By default listens on all interfaces", false, "");
|
||||
parser.add<uint32_t>("port", 'p', "The port to listen on.", false,
|
||||
RENDERDOC_GetDefaultRemoteServerPort());
|
||||
parser.add("preview", 'v', "Display a preview window when a replay is active.");
|
||||
}
|
||||
virtual const char *Description()
|
||||
{
|
||||
@@ -499,8 +500,19 @@ struct RemoteServerCommand : public Command
|
||||
|
||||
usingKillSignal = true;
|
||||
|
||||
// by default have a do-nothing callback that creates no windows
|
||||
RENDERDOC_PreviewWindowCallback previewWindow;
|
||||
|
||||
// if the user asked for a preview, then call to the platform-specific preview function
|
||||
if(parser.exist("preview"))
|
||||
previewWindow = &DisplayRemoteServerPreview;
|
||||
|
||||
// OR if the platform-specific preview function always has a window, then return it anyway.
|
||||
if(DisplayRemoteServerPreview(false, {}).system != WindowingSystem::Unknown)
|
||||
previewWindow = &DisplayRemoteServerPreview;
|
||||
|
||||
RENDERDOC_BecomeRemoteServer(host.empty() ? NULL : host.c_str(), port,
|
||||
[]() { return killSignal; });
|
||||
[]() { return killSignal; }, previewWindow);
|
||||
|
||||
std::cerr << std::endl << "Cleaning up from replay hosting." << std::endl;
|
||||
|
||||
|
||||
@@ -57,4 +57,5 @@ void readCapOpts(const std::string &str, CaptureOptions *opts);
|
||||
// these must be defined in platform .cpps
|
||||
void DisplayRendererPreview(IReplayController *renderer, TextureDisplay &displayCfg, uint32_t width,
|
||||
uint32_t height);
|
||||
WindowingData DisplayRemoteServerPreview(bool active, const rdcarray<WindowingSystem> &systems);
|
||||
void Daemonise();
|
||||
|
||||
@@ -57,31 +57,6 @@ void Daemonise()
|
||||
{
|
||||
}
|
||||
|
||||
void DisplayRendererPreview(IReplayController *renderer, TextureDisplay &displayCfg, uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
ANativeWindow *connectionScreenWindow = android_state->window;
|
||||
|
||||
pthread_mutex_lock(&m_DrawLock.lock);
|
||||
|
||||
IReplayOutput *out = renderer->CreateOutput(CreateAndroidWindowingData(connectionScreenWindow),
|
||||
ReplayOutputType::Texture);
|
||||
|
||||
out->SetTextureDisplay(displayCfg);
|
||||
|
||||
for(int i = 0; i < 100; i++)
|
||||
{
|
||||
renderer->SetFrameEvent(10000000, true);
|
||||
|
||||
ANDROID_LOG("Frame %i", i);
|
||||
out->Display();
|
||||
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&m_DrawLock.lock);
|
||||
}
|
||||
|
||||
void DisplayGenericSplash()
|
||||
{
|
||||
// if something else is drawing and holding the lock, then bail
|
||||
@@ -269,6 +244,57 @@ void main()
|
||||
pthread_mutex_unlock(&m_DrawLock.lock);
|
||||
}
|
||||
|
||||
WindowingData DisplayRemoteServerPreview(bool active, const rdcarray<WindowingSystem> &systems)
|
||||
{
|
||||
static bool wasActive = false;
|
||||
|
||||
// detect when the preview starts or stops
|
||||
if(wasActive != active)
|
||||
{
|
||||
wasActive = active;
|
||||
|
||||
// if we're opening it, aquire the draw lock, otherwise release it.
|
||||
if(active)
|
||||
{
|
||||
pthread_mutex_lock(&m_DrawLock.lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_unlock(&m_DrawLock.lock);
|
||||
|
||||
// when we release it, re-draw the splash
|
||||
DisplayGenericSplash();
|
||||
}
|
||||
}
|
||||
|
||||
return CreateAndroidWindowingData(android_state->window);
|
||||
}
|
||||
|
||||
void DisplayRendererPreview(IReplayController *renderer, TextureDisplay &displayCfg, uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
ANativeWindow *connectionScreenWindow = android_state->window;
|
||||
|
||||
pthread_mutex_lock(&m_DrawLock.lock);
|
||||
|
||||
IReplayOutput *out = renderer->CreateOutput(CreateAndroidWindowingData(connectionScreenWindow),
|
||||
ReplayOutputType::Texture);
|
||||
|
||||
out->SetTextureDisplay(displayCfg);
|
||||
|
||||
for(int i = 0; i < 100; i++)
|
||||
{
|
||||
renderer->SetFrameEvent(10000000, true);
|
||||
|
||||
ANDROID_LOG("Frame %i", i);
|
||||
out->Display();
|
||||
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&m_DrawLock.lock);
|
||||
}
|
||||
|
||||
// Returns the renderdoccmd arguments passed via am start
|
||||
// Examples: am start ... -e renderdoccmd "remoteserver"
|
||||
// -e renderdoccmd "replay /sdcard/capture.rdc"
|
||||
|
||||
@@ -34,6 +34,12 @@ void Daemonise()
|
||||
{
|
||||
}
|
||||
|
||||
WindowingData DisplayRemoteServerPreview(bool active, const rdcarray<WindowingSystem> &systems)
|
||||
{
|
||||
WindowingData ret = {WindowingSystem::Unknown};
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DisplayRendererPreview(IReplayController *renderer, TextureDisplay &displayCfg, uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
|
||||
@@ -202,6 +202,120 @@ void VerifyVulkanLayer(const GlobalEnvironment &env, int argc, char *argv[])
|
||||
|
||||
static Display *display = NULL;
|
||||
|
||||
WindowingData DisplayRemoteServerPreview(bool active, const rdcarray<WindowingSystem> &systems)
|
||||
{
|
||||
static WindowingData remoteServerPreview = {WindowingSystem::Unknown};
|
||||
|
||||
// we only have the preview implemented for platforms that have xlib & xcb. It's unlikely
|
||||
// a meaningful platform exists with only one, and at the time of writing no other windowing
|
||||
// systems are supported on linux for the replay
|
||||
#if defined(RENDERDOC_WINDOWING_XLIB) && defined(RENDERDOC_WINDOWING_XCB)
|
||||
if(active)
|
||||
{
|
||||
if(remoteServerPreview.system == WindowingSystem::Unknown)
|
||||
{
|
||||
// if we're first initialising, create the window
|
||||
if(display == NULL)
|
||||
return remoteServerPreview;
|
||||
|
||||
int scr = DefaultScreen(display);
|
||||
|
||||
xcb_connection_t *connection = XGetXCBConnection(display);
|
||||
|
||||
if(connection == NULL)
|
||||
{
|
||||
std::cerr << "Couldn't get XCB connection from Xlib Display" << std::endl;
|
||||
return remoteServerPreview;
|
||||
}
|
||||
|
||||
XSetEventQueueOwner(display, XCBOwnsEventQueue);
|
||||
|
||||
const xcb_setup_t *setup = xcb_get_setup(connection);
|
||||
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
|
||||
while(scr-- > 0)
|
||||
xcb_screen_next(&iter);
|
||||
|
||||
xcb_screen_t *screen = iter.data;
|
||||
|
||||
uint32_t value_mask, value_list[32];
|
||||
|
||||
xcb_window_t window = xcb_generate_id(connection);
|
||||
|
||||
value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
|
||||
value_list[0] = screen->black_pixel;
|
||||
value_list[1] =
|
||||
XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
|
||||
xcb_create_window(connection, XCB_COPY_FROM_PARENT, window, screen->root, 0, 0, 1280, 720, 0,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, value_mask, value_list);
|
||||
|
||||
/* Magic code that will send notification when window is destroyed */
|
||||
xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, 1, 12, "WM_PROTOCOLS");
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, cookie, 0);
|
||||
|
||||
xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom(connection, 0, 16, "WM_DELETE_WINDOW");
|
||||
xcb_intern_atom_reply_t *atom_wm_delete_window = xcb_intern_atom_reply(connection, cookie2, 0);
|
||||
|
||||
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME,
|
||||
XCB_ATOM_STRING, 8, sizeof("Remote Server Preview") - 1,
|
||||
"Remote Server Preview");
|
||||
|
||||
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, (*reply).atom, 4, 32, 1,
|
||||
&(*atom_wm_delete_window).atom);
|
||||
free(reply);
|
||||
|
||||
xcb_map_window(connection, window);
|
||||
|
||||
bool xcb = false, xlib = false;
|
||||
|
||||
for(size_t i = 0; i < systems.size(); i++)
|
||||
{
|
||||
if(systems[i] == WindowingSystem::Xlib)
|
||||
xlib = true;
|
||||
if(systems[i] == WindowingSystem::XCB)
|
||||
xcb = true;
|
||||
}
|
||||
|
||||
// prefer xcb
|
||||
if(xcb)
|
||||
remoteServerPreview = CreateXCBWindowingData(connection, window);
|
||||
else if(xlib)
|
||||
remoteServerPreview = CreateXlibWindowingData(display, (Drawable)window);
|
||||
|
||||
xcb_flush(connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, we can pump messages here, but we don't actually care to process any. Just clear
|
||||
// the queue
|
||||
xcb_generic_event_t *event = NULL;
|
||||
|
||||
xcb_connection_t *connection = remoteServerPreview.xcb.connection;
|
||||
|
||||
if(remoteServerPreview.system == WindowingSystem::Xlib)
|
||||
connection = XGetXCBConnection(remoteServerPreview.xlib.display);
|
||||
|
||||
if(connection)
|
||||
{
|
||||
do
|
||||
{
|
||||
event = xcb_poll_for_event(connection);
|
||||
if(event)
|
||||
free(event);
|
||||
} while(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// reset the windowing data to 'no window'
|
||||
remoteServerPreview = {WindowingSystem::Unknown};
|
||||
}
|
||||
#endif
|
||||
|
||||
return remoteServerPreview;
|
||||
}
|
||||
|
||||
void DisplayRendererPreview(IReplayController *renderer, TextureDisplay &displayCfg, uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
|
||||
@@ -128,6 +128,61 @@ void Daemonise()
|
||||
// nothing really to do, windows version of renderdoccmd is already 'detached'
|
||||
}
|
||||
|
||||
WindowingData DisplayRemoteServerPreview(bool active, const rdcarray<WindowingSystem> &systems)
|
||||
{
|
||||
static WindowingData remoteServerPreview = {WindowingSystem::Unknown};
|
||||
|
||||
if(active)
|
||||
{
|
||||
if(remoteServerPreview.system == WindowingSystem::Unknown)
|
||||
{
|
||||
// if we're first initialising, create the window
|
||||
|
||||
RECT wr = {0, 0, (LONG)1280, (LONG)720};
|
||||
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
|
||||
HWND wnd =
|
||||
CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdoccmd", L"Remote Server Preview",
|
||||
WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
wr.right - wr.left, wr.bottom - wr.top, NULL, NULL, hInstance, NULL);
|
||||
|
||||
if(wnd == NULL)
|
||||
return remoteServerPreview;
|
||||
|
||||
ShowWindow(wnd, SW_SHOW);
|
||||
UpdateWindow(wnd);
|
||||
|
||||
remoteServerPreview.system = WindowingSystem::Win32;
|
||||
remoteServerPreview.win32.window = wnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, pump messages
|
||||
MSG msg;
|
||||
ZeroMemory(&msg, sizeof(msg));
|
||||
|
||||
// Check to see if any messages are waiting in the queue
|
||||
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
// Translate the message and dispatch it to WindowProc()
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we had a previous window, destroy it.
|
||||
if(remoteServerPreview.win32.window != NULL)
|
||||
DestroyWindow(remoteServerPreview.win32.window);
|
||||
|
||||
// reset the windowing data to 'no window'
|
||||
remoteServerPreview = {WindowingSystem::Unknown};
|
||||
}
|
||||
|
||||
return remoteServerPreview;
|
||||
}
|
||||
|
||||
void DisplayRendererPreview(IReplayController *renderer, TextureDisplay &displayCfg, uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user