mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
2854 lines
86 KiB
C++
2854 lines
86 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2019-2020 Baldur Karlsson
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "replay_proxy.h"
|
|
#include <list>
|
|
#include "lz4/lz4.h"
|
|
#include "serialise/lz4io.h"
|
|
|
|
template <>
|
|
rdcstr DoStringise(const ReplayProxyPacket &el)
|
|
{
|
|
BEGIN_ENUM_STRINGISE(ReplayProxyPacket);
|
|
{
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_RemoteExecutionKeepAlive, "RemoteExecutionKeepAlive");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_RemoteExecutionFinished, "RemoteExecutionFinished");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_ReplayLog, "ReplayLog");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_CacheBufferData, "CacheBufferData");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_CacheTextureData, "CacheTextureData");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetAPIProperties, "GetAPIProperties");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_FetchStructuredFile, "FetchStructuredFile");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetPassEvents, "GetPassEvents");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetResources, "GetResources");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetTextures, "GetTextures");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetTexture, "GetTexture");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetBuffers, "GetBuffers");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetBuffer, "GetBuffer");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetShaderEntryPoints, "GetShaderEntryPoints");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetShader, "GetShader");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetDebugMessages, "GetDebugMessages");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetBufferData, "GetBufferData");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetTextureData, "GetTextureData");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_SavePipelineState, "SavePipelineState");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetUsage, "GetUsage");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetLiveID, "GetLiveID");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetFrameRecord, "GetFrameRecord");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_IsRenderOutput, "IsRenderOutput");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_NeedRemapForFetch, "NeedRemapForFetch");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_FreeTargetResource, "FreeTargetResource");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_FetchCounters, "FetchCounters");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_EnumerateCounters, "EnumerateCounters");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_DescribeCounter, "DescribeCounter");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_FillCBufferVariables, "FillCBufferVariables");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_InitPostVS, "InitPostVS");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_InitPostVSVec, "InitPostVSVec");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetPostVS, "GetPostVS");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_BuildTargetShader, "BuildTargetShader");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_ReplaceResource, "ReplaceResource");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_RemoveReplacement, "RemoveReplacement");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_DebugVertex, "DebugVertex");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_DebugPixel, "DebugPixel");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_DebugThread, "DebugThread");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_RenderOverlay, "RenderOverlay");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_PixelHistory, "PixelHistory");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_DisassembleShader, "DisassembleShader");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetDisassemblyTargets, "GetDisassemblyTargets");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetTargetShaderEncodings, "GetTargetShaderEncodings");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_GetDriverInfo, "GetDriverInfo");
|
|
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_ContinueDebug, "ContinueDebug");
|
|
STRINGISE_ENUM_NAMED(eReplayProxy_FreeDebugger, "FreeDebugger");
|
|
}
|
|
END_ENUM_STRINGISE();
|
|
}
|
|
|
|
// utility macros for implementing proxied functions
|
|
|
|
// begins a chunk with the given packet type, and if reading verifies that the
|
|
// read type was what was expected - otherwise sets an error flag
|
|
#define PACKET_HEADER(packet) \
|
|
ReplayProxyPacket p = (ReplayProxyPacket)ser.BeginChunk(packet, 0); \
|
|
if(ser.IsReading() && p != packet) \
|
|
m_IsErrored = true;
|
|
|
|
// begins the set of parameters. Note that we only begin a chunk when writing (sending a request to
|
|
// the remote server), since on reading the chunk has already been begun to read the type to
|
|
// dispatch to the correct function.
|
|
#define BEGIN_PARAMS() \
|
|
ParamSerialiser &ser = paramser; \
|
|
if(ser.IsWriting()) \
|
|
ser.BeginChunk(packet, 0);
|
|
|
|
// end the set of parameters, and that chunk.
|
|
#define END_PARAMS() \
|
|
{ \
|
|
GET_SERIALISER.Serialise("packet"_lit, packet); \
|
|
ser.EndChunk(); \
|
|
CheckError(packet, expectedPacket); \
|
|
}
|
|
|
|
// begin serialising a return value. We begin a chunk here in either the writing or reading case
|
|
// since this chunk is used purely to send/receive the return value and is fully handled within the
|
|
// function.
|
|
#define SERIALISE_RETURN(retval) \
|
|
{ \
|
|
ReturnSerialiser &ser = retser; \
|
|
PACKET_HEADER(packet); \
|
|
SERIALISE_ELEMENT(retval); \
|
|
GET_SERIALISER.Serialise("packet"_lit, packet); \
|
|
ser.EndChunk(); \
|
|
CheckError(packet, expectedPacket); \
|
|
}
|
|
|
|
// similar to the above, but for void functions that don't return anything. We still want to check
|
|
// that both sides of the communication are on the same page.
|
|
#define SERIALISE_RETURN_VOID() \
|
|
{ \
|
|
ReturnSerialiser &ser = retser; \
|
|
PACKET_HEADER(packet); \
|
|
SERIALISE_ELEMENT(packet); \
|
|
ser.EndChunk(); \
|
|
CheckError(packet, expectedPacket); \
|
|
}
|
|
|
|
// defines the area where we're executing on the remote host. To avoid timeouts, the remote side
|
|
// will pass over to a thread and begin sending periodic keepalive packets. Once complete, it will
|
|
// send a finished packet and continue. The host side will accept any keepalive packets and continue
|
|
// once it reads the finished packet. This is very synchronous, but by design the whole replay proxy
|
|
// is very synchronous.
|
|
// The host side will also abort under the usual circumstances - if some network error happens, or
|
|
// if there is a true timeout.
|
|
// Only the remote side needs a thread because the main thread will be busy inside the actual
|
|
// workload, on the host side our thread can do the packet searching itself.
|
|
struct RemoteExecution
|
|
{
|
|
ReplayProxy *m_Proxy;
|
|
RemoteExecution(ReplayProxy *proxy)
|
|
{
|
|
m_Proxy = proxy;
|
|
m_Proxy->BeginRemoteExecution();
|
|
}
|
|
~RemoteExecution() { m_Proxy->EndRemoteExecution(); }
|
|
};
|
|
#define REMOTE_EXECUTION() RemoteExecution exec(this);
|
|
|
|
// uncomment the following to print verbose debugging prints for the remote proxy packets
|
|
//#define PROXY_DEBUG(...) RDCDEBUG(__VA_ARGS__)
|
|
|
|
#if !defined(PROXY_DEBUG)
|
|
#define PROXY_DEBUG(...) \
|
|
do \
|
|
{ \
|
|
} while(0)
|
|
#endif
|
|
|
|
// dispatches to the right implementation of the Proxied_ function, depending on whether we're on
|
|
// the remote server or not.
|
|
#define PROXY_FUNCTION(name, ...) \
|
|
PROXY_DEBUG("Proxying out %s", #name); \
|
|
if(m_RemoteServer) \
|
|
return CONCAT(Proxied_, name)(m_Reader, m_Writer, ##__VA_ARGS__); \
|
|
else \
|
|
return CONCAT(Proxied_, name)(m_Writer, m_Reader, ##__VA_ARGS__);
|
|
|
|
ReplayProxy::~ReplayProxy()
|
|
{
|
|
ShutdownRemoteExecutionThread();
|
|
|
|
ShutdownPreviewWindow();
|
|
|
|
if(m_Proxy)
|
|
m_Proxy->Shutdown();
|
|
m_Proxy = NULL;
|
|
|
|
for(auto it = m_ShaderReflectionCache.begin(); it != m_ShaderReflectionCache.end(); ++it)
|
|
delete it->second;
|
|
}
|
|
|
|
#pragma region Proxied Functions
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
bool ReplayProxy::Proxied_NeedRemapForFetch(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
const ResourceFormat &format)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_NeedRemapForFetch;
|
|
ReplayProxyPacket packet = eReplayProxy_NeedRemapForFetch;
|
|
bool ret = false;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(format);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->NeedRemapForFetch(format);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool ReplayProxy::NeedRemapForFetch(const ResourceFormat &fmt)
|
|
{
|
|
PROXY_FUNCTION(NeedRemapForFetch, fmt);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
bool ReplayProxy::Proxied_IsRenderOutput(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId id)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_IsRenderOutput;
|
|
ReplayProxyPacket packet = eReplayProxy_IsRenderOutput;
|
|
bool ret = false;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->IsRenderOutput(id);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool ReplayProxy::IsRenderOutput(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(IsRenderOutput, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
APIProperties ReplayProxy::Proxied_GetAPIProperties(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetAPIProperties;
|
|
ReplayProxyPacket packet = eReplayProxy_GetAPIProperties;
|
|
APIProperties ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetAPIProperties();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
if(!m_RemoteServer)
|
|
ret.localRenderer = m_Proxy->GetAPIProperties().localRenderer;
|
|
|
|
m_APIProps = ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
APIProperties ReplayProxy::GetAPIProperties()
|
|
{
|
|
PROXY_FUNCTION(GetAPIProperties);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
DriverInformation ReplayProxy::Proxied_GetDriverInfo(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetDriverInfo;
|
|
ReplayProxyPacket packet = eReplayProxy_GetDriverInfo;
|
|
DriverInformation ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetDriverInfo();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
DriverInformation ReplayProxy::GetDriverInfo()
|
|
{
|
|
PROXY_FUNCTION(GetDriverInfo);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<GPUDevice> ReplayProxy::Proxied_GetAvailableGPUs(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetAvailableGPUs;
|
|
ReplayProxyPacket packet = eReplayProxy_GetAvailableGPUs;
|
|
rdcarray<GPUDevice> ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetAvailableGPUs();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<GPUDevice> ReplayProxy::GetAvailableGPUs()
|
|
{
|
|
PROXY_FUNCTION(GetAvailableGPUs);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<DebugMessage> ReplayProxy::Proxied_GetDebugMessages(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetDebugMessages;
|
|
ReplayProxyPacket packet = eReplayProxy_GetDebugMessages;
|
|
rdcarray<DebugMessage> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetDebugMessages();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<DebugMessage> ReplayProxy::GetDebugMessages()
|
|
{
|
|
PROXY_FUNCTION(GetDebugMessages);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<TextureDescription> ReplayProxy::Proxied_GetTextures(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetTextures;
|
|
ReplayProxyPacket packet = eReplayProxy_GetTextures;
|
|
rdcarray<TextureDescription> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetTextures();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
if(retser.IsReading())
|
|
{
|
|
for(const TextureDescription &tex : ret)
|
|
m_TextureInfo[tex.resourceId] = tex;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<TextureDescription> ReplayProxy::GetTextures()
|
|
{
|
|
PROXY_FUNCTION(GetTextures);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
TextureDescription ReplayProxy::Proxied_GetTexture(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser, ResourceId id)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetTexture;
|
|
ReplayProxyPacket packet = eReplayProxy_GetTexture;
|
|
TextureDescription ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetTexture(id);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
if(retser.IsReading())
|
|
m_TextureInfo[id] = ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
TextureDescription ReplayProxy::GetTexture(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(GetTexture, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<BufferDescription> ReplayProxy::Proxied_GetBuffers(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetBuffers;
|
|
ReplayProxyPacket packet = eReplayProxy_GetBuffers;
|
|
rdcarray<BufferDescription> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetBuffers();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<BufferDescription> ReplayProxy::GetBuffers()
|
|
{
|
|
PROXY_FUNCTION(GetBuffers);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<ResourceDescription> ReplayProxy::Proxied_GetResources(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetResources;
|
|
ReplayProxyPacket packet = eReplayProxy_GetResources;
|
|
rdcarray<ResourceDescription> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetResources();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<ResourceDescription> ReplayProxy::GetResources()
|
|
{
|
|
PROXY_FUNCTION(GetResources);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
BufferDescription ReplayProxy::Proxied_GetBuffer(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser, ResourceId id)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetBuffer;
|
|
ReplayProxyPacket packet = eReplayProxy_GetBuffer;
|
|
BufferDescription ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetBuffer(id);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
BufferDescription ReplayProxy::GetBuffer(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(GetBuffer, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<uint32_t> ReplayProxy::Proxied_GetPassEvents(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser, uint32_t eventId)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetPassEvents;
|
|
ReplayProxyPacket packet = eReplayProxy_GetPassEvents;
|
|
rdcarray<uint32_t> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(eventId);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetPassEvents(eventId);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<uint32_t> ReplayProxy::GetPassEvents(uint32_t eventId)
|
|
{
|
|
PROXY_FUNCTION(GetPassEvents, eventId);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<EventUsage> ReplayProxy::Proxied_GetUsage(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser, ResourceId id)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetUsage;
|
|
ReplayProxyPacket packet = eReplayProxy_GetUsage;
|
|
rdcarray<EventUsage> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetUsage(id);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<EventUsage> ReplayProxy::GetUsage(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(GetUsage, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
FrameRecord ReplayProxy::Proxied_GetFrameRecord(ParamSerialiser ¶mser, ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetFrameRecord;
|
|
ReplayProxyPacket packet = eReplayProxy_GetFrameRecord;
|
|
FrameRecord ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetFrameRecord();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
if(paramser.IsWriting())
|
|
{
|
|
// re-configure the drawcall pointers, since they will be invalid
|
|
SetupDrawcallPointers(m_Drawcalls, ret.drawcallList);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
FrameRecord ReplayProxy::GetFrameRecord()
|
|
{
|
|
PROXY_FUNCTION(GetFrameRecord);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
ResourceId ReplayProxy::Proxied_GetLiveID(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId id)
|
|
{
|
|
if(paramser.IsWriting())
|
|
{
|
|
if(m_LiveIDs.find(id) != m_LiveIDs.end())
|
|
return m_LiveIDs[id];
|
|
|
|
if(m_LocalTextures.find(id) != m_LocalTextures.end())
|
|
return id;
|
|
}
|
|
|
|
if(paramser.IsErrored() || retser.IsErrored() || m_IsErrored)
|
|
return ResourceId();
|
|
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetLiveID;
|
|
ReplayProxyPacket packet = eReplayProxy_GetLiveID;
|
|
ResourceId ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetLiveID(id);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
if(paramser.IsWriting())
|
|
m_LiveIDs[id] = ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
ResourceId ReplayProxy::GetLiveID(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(GetLiveID, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<CounterResult> ReplayProxy::Proxied_FetchCounters(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser,
|
|
const rdcarray<GPUCounter> &counters)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_FetchCounters;
|
|
ReplayProxyPacket packet = eReplayProxy_FetchCounters;
|
|
rdcarray<CounterResult> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(counters);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->FetchCounters(counters);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<CounterResult> ReplayProxy::FetchCounters(const rdcarray<GPUCounter> &counters)
|
|
{
|
|
PROXY_FUNCTION(FetchCounters, counters);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<GPUCounter> ReplayProxy::Proxied_EnumerateCounters(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_EnumerateCounters;
|
|
ReplayProxyPacket packet = eReplayProxy_EnumerateCounters;
|
|
rdcarray<GPUCounter> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->EnumerateCounters();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<GPUCounter> ReplayProxy::EnumerateCounters()
|
|
{
|
|
PROXY_FUNCTION(EnumerateCounters);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
CounterDescription ReplayProxy::Proxied_DescribeCounter(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser,
|
|
GPUCounter counterID)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_DescribeCounter;
|
|
ReplayProxyPacket packet = eReplayProxy_DescribeCounter;
|
|
CounterDescription ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(counterID);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->DescribeCounter(counterID);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
CounterDescription ReplayProxy::DescribeCounter(GPUCounter counterID)
|
|
{
|
|
PROXY_FUNCTION(DescribeCounter, counterID);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_FillCBufferVariables(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId pipeline, ResourceId shader,
|
|
rdcstr entryPoint, uint32_t cbufSlot,
|
|
rdcarray<ShaderVariable> &outvars, const bytebuf &data)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_FillCBufferVariables;
|
|
ReplayProxyPacket packet = eReplayProxy_FillCBufferVariables;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(pipeline);
|
|
SERIALISE_ELEMENT(shader);
|
|
SERIALISE_ELEMENT(entryPoint);
|
|
SERIALISE_ELEMENT(cbufSlot);
|
|
SERIALISE_ELEMENT(data);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->FillCBufferVariables(pipeline, shader, entryPoint, cbufSlot, outvars, data);
|
|
}
|
|
|
|
SERIALISE_RETURN(outvars);
|
|
}
|
|
|
|
void ReplayProxy::FillCBufferVariables(ResourceId pipeline, ResourceId shader, rdcstr entryPoint,
|
|
uint32_t cbufSlot, rdcarray<ShaderVariable> &outvars,
|
|
const bytebuf &data)
|
|
{
|
|
PROXY_FUNCTION(FillCBufferVariables, pipeline, shader, entryPoint, cbufSlot, outvars, data);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_GetBufferData(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId buff, uint64_t offset, uint64_t len,
|
|
bytebuf &retData)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetBufferData;
|
|
ReplayProxyPacket packet = eReplayProxy_GetBufferData;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(buff);
|
|
SERIALISE_ELEMENT(offset);
|
|
SERIALISE_ELEMENT(len);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->GetBufferData(buff, offset, len, retData);
|
|
}
|
|
|
|
// over-estimate of total uncompressed data written. Since the decompression chain needs to know
|
|
// the exact uncompressed size, we over-estimate (to allow for length/padding/etc) and then pad
|
|
// to this amount.
|
|
uint64_t dataSize = retData.size() + 2 * retser.GetChunkAlignment();
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
SERIALISE_ELEMENT(packet);
|
|
SERIALISE_ELEMENT(dataSize);
|
|
}
|
|
|
|
char empty[128] = {};
|
|
|
|
// lz4 compress
|
|
if(retser.IsReading())
|
|
{
|
|
ReadSerialiser ser(new StreamReader(new LZ4Decompressor(retser.GetReader(), Ownership::Nothing),
|
|
dataSize, Ownership::Stream),
|
|
Ownership::Stream);
|
|
|
|
SERIALISE_ELEMENT(retData);
|
|
|
|
uint64_t offs = ser.GetReader()->GetOffset();
|
|
RDCASSERT(offs <= dataSize, offs, dataSize);
|
|
RDCASSERT(dataSize - offs < sizeof(empty), offs, dataSize);
|
|
|
|
if(offs < dataSize)
|
|
ser.GetReader()->Read(empty, dataSize - offs);
|
|
}
|
|
else
|
|
{
|
|
WriteSerialiser ser(new StreamWriter(new LZ4Compressor(retser.GetWriter(), Ownership::Nothing),
|
|
Ownership::Stream),
|
|
Ownership::Stream);
|
|
|
|
SERIALISE_ELEMENT(retData);
|
|
|
|
uint64_t offs = ser.GetWriter()->GetOffset();
|
|
RDCASSERT(offs <= dataSize, offs, dataSize);
|
|
RDCASSERT(dataSize - offs < sizeof(empty), offs, dataSize);
|
|
|
|
if(offs < dataSize)
|
|
ser.GetWriter()->Write(empty, dataSize - offs);
|
|
}
|
|
|
|
retser.EndChunk();
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData)
|
|
{
|
|
PROXY_FUNCTION(GetBufferData, buff, offset, len, retData);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_GetTextureData(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId tex, const Subresource &sub,
|
|
const GetTextureDataParams ¶ms, bytebuf &data)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetTextureData;
|
|
ReplayProxyPacket packet = eReplayProxy_GetTextureData;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(tex);
|
|
SERIALISE_ELEMENT(sub);
|
|
SERIALISE_ELEMENT(params);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->GetTextureData(tex, sub, params, data);
|
|
}
|
|
|
|
// over-estimate of total uncompressed data written. Since the decompression chain needs to know
|
|
// the exact uncompressed size, we over-estimate (to allow for length/padding/etc) and then pad
|
|
// to this amount.
|
|
uint64_t dataSize = data.size() + 2 * retser.GetChunkAlignment();
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
SERIALISE_ELEMENT(packet);
|
|
SERIALISE_ELEMENT(dataSize);
|
|
}
|
|
|
|
char empty[128] = {};
|
|
|
|
// lz4 compress
|
|
if(retser.IsReading())
|
|
{
|
|
ReadSerialiser ser(new StreamReader(new LZ4Decompressor(retser.GetReader(), Ownership::Nothing),
|
|
dataSize, Ownership::Stream),
|
|
Ownership::Stream);
|
|
|
|
SERIALISE_ELEMENT(data);
|
|
|
|
uint64_t offs = ser.GetReader()->GetOffset();
|
|
RDCASSERT(offs <= dataSize, offs, dataSize);
|
|
RDCASSERT(dataSize - offs < sizeof(empty), offs, dataSize);
|
|
|
|
if(offs < dataSize)
|
|
ser.GetReader()->Read(empty, dataSize - offs);
|
|
}
|
|
else
|
|
{
|
|
WriteSerialiser ser(new StreamWriter(new LZ4Compressor(retser.GetWriter(), Ownership::Nothing),
|
|
Ownership::Stream),
|
|
Ownership::Stream);
|
|
|
|
SERIALISE_ELEMENT(data);
|
|
|
|
uint64_t offs = ser.GetWriter()->GetOffset();
|
|
RDCASSERT(offs <= dataSize, offs, dataSize);
|
|
RDCASSERT(dataSize - offs < sizeof(empty), offs, dataSize);
|
|
|
|
if(offs < dataSize)
|
|
ser.GetWriter()->Write(empty, dataSize - offs);
|
|
}
|
|
|
|
retser.EndChunk();
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::GetTextureData(ResourceId tex, const Subresource &sub,
|
|
const GetTextureDataParams ¶ms, bytebuf &data)
|
|
{
|
|
PROXY_FUNCTION(GetTextureData, tex, sub, params, data);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_InitPostVSBuffers(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
uint32_t eventId)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_InitPostVS;
|
|
ReplayProxyPacket packet = eReplayProxy_InitPostVS;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(eventId);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->InitPostVSBuffers(eventId);
|
|
}
|
|
|
|
SERIALISE_RETURN_VOID();
|
|
}
|
|
|
|
void ReplayProxy::InitPostVSBuffers(uint32_t eventId)
|
|
{
|
|
PROXY_FUNCTION(InitPostVSBuffers, eventId);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_InitPostVSBuffers(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
const rdcarray<uint32_t> &events)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_InitPostVSVec;
|
|
ReplayProxyPacket packet = eReplayProxy_InitPostVSVec;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(events);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->InitPostVSBuffers(events);
|
|
}
|
|
|
|
SERIALISE_RETURN_VOID();
|
|
}
|
|
|
|
void ReplayProxy::InitPostVSBuffers(const rdcarray<uint32_t> &events)
|
|
{
|
|
PROXY_FUNCTION(InitPostVSBuffers, events);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
MeshFormat ReplayProxy::Proxied_GetPostVSBuffers(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
uint32_t eventId, uint32_t instID, uint32_t viewID,
|
|
MeshDataStage stage)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetPostVS;
|
|
ReplayProxyPacket packet = eReplayProxy_GetPostVS;
|
|
MeshFormat ret = {};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(eventId);
|
|
SERIALISE_ELEMENT(instID);
|
|
SERIALISE_ELEMENT(viewID);
|
|
SERIALISE_ELEMENT(stage);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetPostVSBuffers(eventId, instID, viewID, stage);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
MeshFormat ReplayProxy::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID,
|
|
MeshDataStage stage)
|
|
{
|
|
PROXY_FUNCTION(GetPostVSBuffers, eventId, instID, viewID, stage);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
ResourceId ReplayProxy::Proxied_RenderOverlay(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId texid, FloatVector clearCol,
|
|
DebugOverlay overlay, uint32_t eventId,
|
|
const rdcarray<uint32_t> &passEvents)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_RenderOverlay;
|
|
ReplayProxyPacket packet = eReplayProxy_RenderOverlay;
|
|
ResourceId ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(texid);
|
|
SERIALISE_ELEMENT(overlay);
|
|
SERIALISE_ELEMENT(clearCol);
|
|
SERIALISE_ELEMENT(eventId);
|
|
SERIALISE_ELEMENT(passEvents);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->RenderOverlay(texid, clearCol, overlay, eventId, passEvents);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ResourceId ReplayProxy::RenderOverlay(ResourceId texid, FloatVector clearCol, DebugOverlay overlay,
|
|
uint32_t eventId, const rdcarray<uint32_t> &passEvents)
|
|
{
|
|
PROXY_FUNCTION(RenderOverlay, texid, clearCol, overlay, eventId, passEvents);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<ShaderEntryPoint> ReplayProxy::Proxied_GetShaderEntryPoints(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser,
|
|
ResourceId id)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetShaderEntryPoints;
|
|
ReplayProxyPacket packet = eReplayProxy_GetShaderEntryPoints;
|
|
rdcarray<ShaderEntryPoint> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetShaderEntryPoints(id);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<ShaderEntryPoint> ReplayProxy::GetShaderEntryPoints(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(GetShaderEntryPoints, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
ShaderReflection *ReplayProxy::Proxied_GetShader(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser, ResourceId pipeline,
|
|
ResourceId shader, ShaderEntryPoint entry)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetShader;
|
|
ReplayProxyPacket packet = eReplayProxy_GetShader;
|
|
ShaderReflection *ret = NULL;
|
|
|
|
// only consider eventID part of the key on APIs where shaders are mutable
|
|
ShaderReflKey key(m_APIProps.shadersMutable ? m_EventID : 0, pipeline, shader, entry);
|
|
|
|
if(retser.IsReading() && m_ShaderReflectionCache.find(key) != m_ShaderReflectionCache.end())
|
|
return m_ShaderReflectionCache[key];
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(pipeline);
|
|
SERIALISE_ELEMENT(shader);
|
|
SERIALISE_ELEMENT(entry);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetShader(pipeline, shader, entry);
|
|
}
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
SERIALISE_ELEMENT_OPT(ret);
|
|
SERIALISE_ELEMENT(packet);
|
|
ser.EndChunk();
|
|
|
|
// if we're reading, we should have checked the cache above. If we didn't, we need to steal the
|
|
// serialised pointer here into our cache
|
|
if(ser.IsReading())
|
|
{
|
|
m_ShaderReflectionCache[key] = ret;
|
|
ret = NULL;
|
|
}
|
|
}
|
|
|
|
CheckError(packet, expectedPacket);
|
|
|
|
return m_ShaderReflectionCache[key];
|
|
}
|
|
|
|
ShaderReflection *ReplayProxy::GetShader(ResourceId pipeline, ResourceId shader,
|
|
ShaderEntryPoint entry)
|
|
{
|
|
PROXY_FUNCTION(GetShader, pipeline, shader, entry);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcstr ReplayProxy::Proxied_DisassembleShader(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId pipeline, const ShaderReflection *refl,
|
|
const rdcstr &target)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_DisassembleShader;
|
|
ReplayProxyPacket packet = eReplayProxy_DisassembleShader;
|
|
ResourceId Shader;
|
|
ShaderEntryPoint EntryPoint;
|
|
rdcstr ret;
|
|
|
|
if(refl)
|
|
{
|
|
Shader = refl->resourceId;
|
|
EntryPoint.name = refl->entryPoint;
|
|
EntryPoint.stage = refl->stage;
|
|
}
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(pipeline);
|
|
SERIALISE_ELEMENT(Shader);
|
|
SERIALISE_ELEMENT(EntryPoint);
|
|
SERIALISE_ELEMENT(target);
|
|
END_PARAMS();
|
|
}
|
|
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
{
|
|
refl = m_Remote->GetShader(pipeline, m_Remote->GetLiveID(Shader), EntryPoint);
|
|
ret = m_Remote->DisassembleShader(pipeline, refl, target);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcstr ReplayProxy::DisassembleShader(ResourceId pipeline, const ShaderReflection *refl,
|
|
const rdcstr &target)
|
|
{
|
|
PROXY_FUNCTION(DisassembleShader, pipeline, refl, target);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<rdcstr> ReplayProxy::Proxied_GetDisassemblyTargets(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetDisassemblyTargets;
|
|
ReplayProxyPacket packet = eReplayProxy_GetDisassemblyTargets;
|
|
rdcarray<rdcstr> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetDisassemblyTargets();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<rdcstr> ReplayProxy::GetDisassemblyTargets()
|
|
{
|
|
PROXY_FUNCTION(GetDisassemblyTargets);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_FreeTargetResource(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId id)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_FreeTargetResource;
|
|
ReplayProxyPacket packet = eReplayProxy_FreeTargetResource;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->FreeTargetResource(id);
|
|
}
|
|
|
|
SERIALISE_RETURN_VOID();
|
|
}
|
|
|
|
void ReplayProxy::FreeTargetResource(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(FreeTargetResource, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<ShaderEncoding> ReplayProxy::Proxied_GetTargetShaderEncodings(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_GetTargetShaderEncodings;
|
|
ReplayProxyPacket packet = eReplayProxy_GetTargetShaderEncodings;
|
|
rdcarray<ShaderEncoding> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->GetTargetShaderEncodings();
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<ShaderEncoding> ReplayProxy::GetTargetShaderEncodings()
|
|
{
|
|
PROXY_FUNCTION(GetTargetShaderEncodings);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_BuildTargetShader(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ShaderEncoding sourceEncoding, const bytebuf &source,
|
|
const rdcstr &entry,
|
|
const ShaderCompileFlags &compileFlags,
|
|
ShaderStage type, ResourceId &id, rdcstr &errors)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_BuildTargetShader;
|
|
ReplayProxyPacket packet = eReplayProxy_BuildTargetShader;
|
|
ResourceId ret_id;
|
|
rdcstr ret_errors;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(sourceEncoding);
|
|
SERIALISE_ELEMENT(source);
|
|
SERIALISE_ELEMENT(entry);
|
|
SERIALISE_ELEMENT(compileFlags);
|
|
SERIALISE_ELEMENT(type);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->BuildTargetShader(sourceEncoding, source, entry, compileFlags, type, ret_id,
|
|
ret_errors);
|
|
}
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
SERIALISE_ELEMENT(ret_id);
|
|
SERIALISE_ELEMENT(ret_errors);
|
|
SERIALISE_ELEMENT(packet);
|
|
ser.EndChunk();
|
|
|
|
id = ret_id;
|
|
errors = ret_errors;
|
|
}
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::BuildTargetShader(ShaderEncoding sourceEncoding, const bytebuf &source,
|
|
const rdcstr &entry, const ShaderCompileFlags &compileFlags,
|
|
ShaderStage type, ResourceId &id, rdcstr &errors)
|
|
{
|
|
PROXY_FUNCTION(BuildTargetShader, sourceEncoding, source, entry, compileFlags, type, id, errors);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_ReplaceResource(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId from, ResourceId to)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_ReplaceResource;
|
|
ReplayProxyPacket packet = eReplayProxy_ReplaceResource;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(from);
|
|
SERIALISE_ELEMENT(to);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->ReplaceResource(from, to);
|
|
}
|
|
|
|
SERIALISE_RETURN_VOID();
|
|
}
|
|
|
|
void ReplayProxy::ReplaceResource(ResourceId from, ResourceId to)
|
|
{
|
|
PROXY_FUNCTION(ReplaceResource, from, to);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_RemoveReplacement(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId id)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_RemoveReplacement;
|
|
ReplayProxyPacket packet = eReplayProxy_RemoveReplacement;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(id);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->RemoveReplacement(id);
|
|
}
|
|
|
|
SERIALISE_RETURN_VOID();
|
|
}
|
|
|
|
void ReplayProxy::RemoveReplacement(ResourceId id)
|
|
{
|
|
PROXY_FUNCTION(RemoveReplacement, id);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<PixelModification> ReplayProxy::Proxied_PixelHistory(
|
|
ParamSerialiser ¶mser, ReturnSerialiser &retser, rdcarray<EventUsage> events,
|
|
ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_PixelHistory;
|
|
ReplayProxyPacket packet = eReplayProxy_PixelHistory;
|
|
rdcarray<PixelModification> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(events);
|
|
SERIALISE_ELEMENT(target);
|
|
SERIALISE_ELEMENT(x);
|
|
SERIALISE_ELEMENT(y);
|
|
SERIALISE_ELEMENT(sub);
|
|
SERIALISE_ELEMENT(typeCast);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->PixelHistory(events, target, x, y, sub, typeCast);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<PixelModification> ReplayProxy::PixelHistory(rdcarray<EventUsage> events,
|
|
ResourceId target, uint32_t x, uint32_t y,
|
|
const Subresource &sub, CompType typeCast)
|
|
{
|
|
PROXY_FUNCTION(PixelHistory, events, target, x, y, sub, typeCast);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
ShaderDebugTrace *ReplayProxy::Proxied_DebugVertex(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser, uint32_t eventId,
|
|
uint32_t vertid, uint32_t instid, uint32_t idx)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_DebugVertex;
|
|
ReplayProxyPacket packet = eReplayProxy_DebugVertex;
|
|
ShaderDebugTrace *ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(eventId);
|
|
SERIALISE_ELEMENT(vertid);
|
|
SERIALISE_ELEMENT(instid);
|
|
SERIALISE_ELEMENT(idx);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->DebugVertex(eventId, vertid, instid, idx);
|
|
else
|
|
ret = new ShaderDebugTrace;
|
|
}
|
|
|
|
SERIALISE_RETURN(*ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ShaderDebugTrace *ReplayProxy::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid,
|
|
uint32_t idx)
|
|
{
|
|
PROXY_FUNCTION(DebugVertex, eventId, vertid, instid, idx);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
ShaderDebugTrace *ReplayProxy::Proxied_DebugPixel(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
uint32_t eventId, uint32_t x, uint32_t y,
|
|
uint32_t sample, uint32_t primitive)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_DebugPixel;
|
|
ReplayProxyPacket packet = eReplayProxy_DebugPixel;
|
|
ShaderDebugTrace *ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(eventId);
|
|
SERIALISE_ELEMENT(x);
|
|
SERIALISE_ELEMENT(y);
|
|
SERIALISE_ELEMENT(sample);
|
|
SERIALISE_ELEMENT(primitive);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->DebugPixel(eventId, x, y, sample, primitive);
|
|
else
|
|
ret = new ShaderDebugTrace;
|
|
}
|
|
|
|
SERIALISE_RETURN(*ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ShaderDebugTrace *ReplayProxy::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample,
|
|
uint32_t primitive)
|
|
{
|
|
PROXY_FUNCTION(DebugPixel, eventId, x, y, sample, primitive);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
ShaderDebugTrace *ReplayProxy::Proxied_DebugThread(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser, uint32_t eventId,
|
|
const uint32_t groupid[3],
|
|
const uint32_t threadid[3])
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_DebugThread;
|
|
ReplayProxyPacket packet = eReplayProxy_DebugThread;
|
|
ShaderDebugTrace *ret;
|
|
|
|
uint32_t GroupID[3] = {groupid[0], groupid[1], groupid[2]};
|
|
uint32_t ThreadID[3] = {threadid[0], threadid[1], threadid[2]};
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(eventId);
|
|
SERIALISE_ELEMENT(GroupID);
|
|
SERIALISE_ELEMENT(ThreadID);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->DebugThread(eventId, GroupID, ThreadID);
|
|
else
|
|
ret = new ShaderDebugTrace;
|
|
}
|
|
|
|
SERIALISE_RETURN(*ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ShaderDebugTrace *ReplayProxy::DebugThread(uint32_t eventId, const uint32_t groupid[3],
|
|
const uint32_t threadid[3])
|
|
{
|
|
PROXY_FUNCTION(DebugThread, eventId, groupid, threadid);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
rdcarray<ShaderDebugState> ReplayProxy::Proxied_ContinueDebug(ParamSerialiser ¶mser,
|
|
ReturnSerialiser &retser,
|
|
ShaderDebugger *debugger)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_ContinueDebug;
|
|
ReplayProxyPacket packet = eReplayProxy_ContinueDebug;
|
|
rdcarray<ShaderDebugState> ret;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
uint64_t debugger_ptr = (uint64_t)(uintptr_t)debugger;
|
|
SERIALISE_ELEMENT(debugger_ptr);
|
|
debugger = (ShaderDebugger *)(uintptr_t)debugger_ptr;
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
ret = m_Remote->ContinueDebug(debugger);
|
|
}
|
|
|
|
SERIALISE_RETURN(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcarray<ShaderDebugState> ReplayProxy::ContinueDebug(ShaderDebugger *debugger)
|
|
{
|
|
PROXY_FUNCTION(ContinueDebug, debugger);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_FreeDebugger(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ShaderDebugger *debugger)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_FreeDebugger;
|
|
ReplayProxyPacket packet = eReplayProxy_FreeDebugger;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
uint64_t debugger_ptr = (uint64_t)(uintptr_t)debugger;
|
|
SERIALISE_ELEMENT(debugger_ptr);
|
|
debugger = (ShaderDebugger *)(uintptr_t)debugger_ptr;
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->FreeDebugger(debugger);
|
|
}
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::FreeDebugger(ShaderDebugger *debugger)
|
|
{
|
|
PROXY_FUNCTION(FreeDebugger, debugger);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
uint32_t eventId)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_SavePipelineState;
|
|
ReplayProxyPacket packet = eReplayProxy_SavePipelineState;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(eventId);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
{
|
|
m_Remote->SavePipelineState(eventId);
|
|
|
|
if(m_APIProps.pipelineType == GraphicsAPI::D3D11)
|
|
m_D3D11PipelineState = *m_Remote->GetD3D11PipelineState();
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::D3D12)
|
|
m_D3D12PipelineState = *m_Remote->GetD3D12PipelineState();
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::OpenGL)
|
|
m_GLPipelineState = *m_Remote->GetGLPipelineState();
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::Vulkan)
|
|
m_VulkanPipelineState = *m_Remote->GetVulkanPipelineState();
|
|
}
|
|
}
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
if(m_APIProps.pipelineType == GraphicsAPI::D3D11)
|
|
{
|
|
SERIALISE_ELEMENT(m_D3D11PipelineState);
|
|
}
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::D3D12)
|
|
{
|
|
SERIALISE_ELEMENT(m_D3D12PipelineState);
|
|
}
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::OpenGL)
|
|
{
|
|
SERIALISE_ELEMENT(m_GLPipelineState);
|
|
}
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::Vulkan)
|
|
{
|
|
SERIALISE_ELEMENT(m_VulkanPipelineState);
|
|
}
|
|
SERIALISE_ELEMENT(packet);
|
|
ser.EndChunk();
|
|
|
|
if(retser.IsReading())
|
|
{
|
|
if(m_APIProps.pipelineType == GraphicsAPI::D3D11)
|
|
{
|
|
D3D11Pipe::Shader *stages[] = {
|
|
&m_D3D11PipelineState.vertexShader, &m_D3D11PipelineState.hullShader,
|
|
&m_D3D11PipelineState.domainShader, &m_D3D11PipelineState.geometryShader,
|
|
&m_D3D11PipelineState.pixelShader, &m_D3D11PipelineState.computeShader,
|
|
};
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
if(stages[i]->resourceId != ResourceId())
|
|
stages[i]->reflection =
|
|
GetShader(ResourceId(), GetLiveID(stages[i]->resourceId), ShaderEntryPoint());
|
|
|
|
if(m_D3D11PipelineState.inputAssembly.resourceId != ResourceId())
|
|
m_D3D11PipelineState.inputAssembly.bytecode =
|
|
GetShader(ResourceId(), GetLiveID(m_D3D11PipelineState.inputAssembly.resourceId),
|
|
ShaderEntryPoint());
|
|
}
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::D3D12)
|
|
{
|
|
D3D12Pipe::Shader *stages[] = {
|
|
&m_D3D12PipelineState.vertexShader, &m_D3D12PipelineState.hullShader,
|
|
&m_D3D12PipelineState.domainShader, &m_D3D12PipelineState.geometryShader,
|
|
&m_D3D12PipelineState.pixelShader, &m_D3D12PipelineState.computeShader,
|
|
};
|
|
|
|
ResourceId pipe = GetLiveID(m_D3D12PipelineState.pipelineResourceId);
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
if(stages[i]->resourceId != ResourceId())
|
|
stages[i]->reflection =
|
|
GetShader(pipe, GetLiveID(stages[i]->resourceId), ShaderEntryPoint());
|
|
}
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::OpenGL)
|
|
{
|
|
GLPipe::Shader *stages[] = {
|
|
&m_GLPipelineState.vertexShader, &m_GLPipelineState.tessControlShader,
|
|
&m_GLPipelineState.tessEvalShader, &m_GLPipelineState.geometryShader,
|
|
&m_GLPipelineState.fragmentShader, &m_GLPipelineState.computeShader,
|
|
};
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
if(stages[i]->shaderResourceId != ResourceId())
|
|
stages[i]->reflection =
|
|
GetShader(ResourceId(), GetLiveID(stages[i]->shaderResourceId), ShaderEntryPoint());
|
|
}
|
|
else if(m_APIProps.pipelineType == GraphicsAPI::Vulkan)
|
|
{
|
|
VKPipe::Shader *stages[] = {
|
|
&m_VulkanPipelineState.vertexShader, &m_VulkanPipelineState.tessControlShader,
|
|
&m_VulkanPipelineState.tessEvalShader, &m_VulkanPipelineState.geometryShader,
|
|
&m_VulkanPipelineState.fragmentShader, &m_VulkanPipelineState.computeShader,
|
|
};
|
|
|
|
ResourceId pipe = GetLiveID(m_VulkanPipelineState.graphics.pipelineResourceId);
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
if(i == 5)
|
|
pipe = GetLiveID(m_VulkanPipelineState.compute.pipelineResourceId);
|
|
|
|
if(stages[i]->resourceId != ResourceId())
|
|
stages[i]->reflection =
|
|
GetShader(pipe, GetLiveID(stages[i]->resourceId),
|
|
ShaderEntryPoint(stages[i]->entryPoint, stages[i]->stage));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::SavePipelineState(uint32_t eventId)
|
|
{
|
|
PROXY_FUNCTION(SavePipelineState, eventId);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_ReplayLog(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
uint32_t endEventID, ReplayLogType replayType)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_ReplayLog;
|
|
ReplayProxyPacket packet = eReplayProxy_ReplayLog;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(endEventID);
|
|
SERIALISE_ELEMENT(replayType);
|
|
END_PARAMS();
|
|
}
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->ReplayLog(endEventID, replayType);
|
|
}
|
|
|
|
if(retser.IsReading())
|
|
{
|
|
m_TextureProxyCache.clear();
|
|
m_BufferProxyCache.clear();
|
|
}
|
|
|
|
m_EventID = endEventID;
|
|
|
|
SERIALISE_RETURN_VOID();
|
|
}
|
|
|
|
void ReplayProxy::ReplayLog(uint32_t endEventID, ReplayLogType replayType)
|
|
{
|
|
PROXY_FUNCTION(ReplayLog, endEventID, replayType);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_FetchStructuredFile(ParamSerialiser ¶mser, ReturnSerialiser &retser)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_FetchStructuredFile;
|
|
ReplayProxyPacket packet = eReplayProxy_FetchStructuredFile;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
END_PARAMS();
|
|
}
|
|
|
|
SDFile *file = &m_StructuredFile;
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
file = (SDFile *)&m_Remote->GetStructuredFile();
|
|
}
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
|
|
uint64_t chunkCount = file->chunks.size();
|
|
SERIALISE_ELEMENT(chunkCount);
|
|
|
|
if(retser.IsReading())
|
|
file->chunks.resize((size_t)chunkCount);
|
|
|
|
for(size_t c = 0; c < (size_t)chunkCount; c++)
|
|
{
|
|
if(retser.IsReading())
|
|
file->chunks[c] = new SDChunk("");
|
|
|
|
ser.Serialise("chunk"_lit, *file->chunks[c]);
|
|
}
|
|
|
|
uint64_t bufferCount = file->buffers.size();
|
|
SERIALISE_ELEMENT(bufferCount);
|
|
|
|
if(retser.IsReading())
|
|
file->buffers.resize((size_t)bufferCount);
|
|
|
|
for(size_t b = 0; b < (size_t)bufferCount; b++)
|
|
{
|
|
if(retser.IsReading())
|
|
file->buffers[b] = new bytebuf;
|
|
|
|
bytebuf *buf = file->buffers[b];
|
|
|
|
ser.Serialise("buffer"_lit, *buf);
|
|
}
|
|
|
|
SERIALISE_ELEMENT(packet);
|
|
|
|
ser.EndChunk();
|
|
}
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::FetchStructuredFile()
|
|
{
|
|
PROXY_FUNCTION(FetchStructuredFile);
|
|
}
|
|
|
|
struct DeltaSection
|
|
{
|
|
uint64_t offs = 0;
|
|
bytebuf contents;
|
|
|
|
void swap(DeltaSection &o)
|
|
{
|
|
std::swap(offs, o.offs);
|
|
contents.swap(o.contents);
|
|
}
|
|
};
|
|
|
|
DECLARE_REFLECTION_STRUCT(DeltaSection);
|
|
|
|
template <typename SerialiserType>
|
|
void DoSerialise(SerialiserType &ser, DeltaSection &el)
|
|
{
|
|
SERIALISE_MEMBER(offs);
|
|
SERIALISE_MEMBER(contents);
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
void ReplayProxy::DeltaTransferBytes(SerialiserType &xferser, bytebuf &referenceData, bytebuf &newData)
|
|
{
|
|
// lz4 compress
|
|
if(xferser.IsReading())
|
|
{
|
|
uint64_t uncompSize = 0;
|
|
xferser.Serialise("uncompSize"_lit, uncompSize);
|
|
|
|
if(uncompSize == 0)
|
|
{
|
|
// fast path - no changes.
|
|
RDCDEBUG("Unchanged");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
rdcarray<DeltaSection> deltas;
|
|
|
|
{
|
|
ReadSerialiser ser(
|
|
new StreamReader(new LZ4Decompressor(xferser.GetReader(), Ownership::Nothing),
|
|
uncompSize, Ownership::Stream),
|
|
Ownership::Stream);
|
|
|
|
SERIALISE_ELEMENT(deltas);
|
|
|
|
// add any necessary padding.
|
|
uint64_t offs = ser.GetReader()->GetOffset();
|
|
RDCASSERT(offs <= uncompSize, offs, uncompSize);
|
|
|
|
if(offs < uncompSize)
|
|
{
|
|
if(uncompSize - offs > 128)
|
|
RDCERR("Unexpected amount of padding: %llu", uncompSize - offs);
|
|
ser.GetReader()->Read(NULL, uncompSize - offs);
|
|
}
|
|
}
|
|
|
|
if(deltas.empty())
|
|
{
|
|
RDCERR("Unexpected empty delta list");
|
|
}
|
|
else if(referenceData.empty())
|
|
{
|
|
// if we don't have reference data we blat the whole contents.
|
|
// in this case we only expect one delta with the whole range
|
|
if(deltas.size() != 1)
|
|
RDCERR("Got more than one delta with no reference data - taking first delta.");
|
|
|
|
referenceData = deltas.front().contents;
|
|
RDCDEBUG("Creating new reference data, %llu bytes", (uint64_t)referenceData.size());
|
|
}
|
|
else
|
|
{
|
|
uint64_t deltaBytes = 0;
|
|
|
|
// apply deltas to refData
|
|
for(const DeltaSection &delta : deltas)
|
|
{
|
|
if(delta.offs + delta.contents.size() > referenceData.size())
|
|
{
|
|
RDCERR("{%llu, %llu} larger than reference data (%llu bytes) - expanding to fit.",
|
|
delta.offs, (uint64_t)delta.contents.size(), (uint64_t)referenceData.size());
|
|
|
|
referenceData.resize(size_t(delta.offs + delta.contents.size()));
|
|
}
|
|
|
|
byte *dst = referenceData.data() + (ptrdiff_t)delta.offs;
|
|
const byte *src = delta.contents.data();
|
|
|
|
memcpy(dst, src, delta.contents.size());
|
|
|
|
deltaBytes += (uint64_t)delta.contents.size();
|
|
}
|
|
|
|
RDCDEBUG("Applied %u deltas data, %llu total delta bytes to %llu resource size",
|
|
(uint32_t)deltas.size(), deltaBytes, (uint64_t)referenceData.size());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint64_t uncompSize = 0;
|
|
|
|
// we use a list so that we don't have to reserve and pushing new sections will never cause
|
|
// previous ones to be reallocated and move around lots of data.
|
|
std::list<DeltaSection> deltasList;
|
|
|
|
if(referenceData.empty())
|
|
{
|
|
// no previous reference data, need to transfer the whole object.
|
|
deltasList.resize(1);
|
|
deltasList.back().contents = newData;
|
|
}
|
|
else
|
|
{
|
|
if(referenceData.size() != newData.size())
|
|
{
|
|
RDCERR("Reference data existed at %llu bytes, but new data is now %llu bytes",
|
|
referenceData.size(), newData.size());
|
|
|
|
// re-transfer the whole block, something went seriously wrong if the resource changed size.
|
|
deltasList.resize(1);
|
|
deltasList.back().contents = newData;
|
|
}
|
|
else
|
|
{
|
|
// do actual diff.
|
|
const byte *srcBegin = newData.data();
|
|
const byte *src = srcBegin;
|
|
const byte *dst = referenceData.data();
|
|
size_t bytesRemain = newData.size();
|
|
|
|
// we only care about large-ish chunks at a time. This prevents us generating lots of tiny
|
|
// deltas where we could batch changes together. This is tuned to not be too large (and
|
|
// thus causing us to miss too many sections we could skip) and not too small (causing us
|
|
// to devolve into lots of byte-wise deltas). The current value as of this comment of 128
|
|
// is definitely on the small end of the range, but consider e.g. an android image of
|
|
// 1440x2560 and a pixel-wide line that goes vertically from top to bottom. Reading
|
|
// horizontally that will mean 2560 different diffs, and only actually one pixel changed.
|
|
// The larger this value gets, the more redundant data we'll send along with.
|
|
const size_t chunkSize = 128;
|
|
|
|
// we use a simple state machine. Start in state 1
|
|
//
|
|
// State 1: No active delta. Look at the current chunk, if there's no difference move to the
|
|
// next chunk and stay in this state. If there is a difference, push a delta onto
|
|
// the list at the current offset. Copy the current chunk into the contents of the
|
|
// delta. Move to state 2.
|
|
// State 2. Active delta. Look at the current chunk, if there is a difference then append
|
|
// the current chunk to the last delta's contents, move to the next chunk, and stay
|
|
// in this state. If there isn't a difference, move back to state 1 (the delta is
|
|
// already 'finished' so we have no need to do anything more on it).
|
|
//
|
|
// At any point we can end the loop, both states are 'complete' at all points.
|
|
|
|
enum DeltaState
|
|
{
|
|
None,
|
|
Active
|
|
};
|
|
DeltaState state = DeltaState::None;
|
|
|
|
// loop over whole chunks
|
|
while(bytesRemain > chunkSize)
|
|
{
|
|
// check if there's a difference in this chunk.
|
|
bool chunkDiff = memcmp(src, dst, chunkSize) != 0;
|
|
|
|
// if we're in state 1
|
|
if(state == DeltaState::None)
|
|
{
|
|
// if there's a difference, append a new delta with the current offset and chunk
|
|
// contents and move to state 2
|
|
if(chunkDiff)
|
|
{
|
|
deltasList.push_back(DeltaSection());
|
|
deltasList.back().offs = src - srcBegin;
|
|
deltasList.back().contents.append(src, chunkSize);
|
|
|
|
state = DeltaState::Active;
|
|
}
|
|
}
|
|
// if we're in state 2
|
|
else if(state == DeltaState::Active)
|
|
{
|
|
// continue to append to the delta if there's another difference in this chunk.
|
|
if(chunkDiff)
|
|
{
|
|
deltasList.back().contents.append(src, chunkSize);
|
|
}
|
|
else
|
|
{
|
|
state = DeltaState::None;
|
|
}
|
|
}
|
|
|
|
// move to the next chunk
|
|
bytesRemain -= chunkSize;
|
|
src += chunkSize;
|
|
dst += chunkSize;
|
|
}
|
|
|
|
// if there are still some bytes remaining at the end of the image, smaller than the chunk
|
|
// size, just diff directly and send if needed. We could combine this with the last delta if
|
|
// we ended in the active state.
|
|
if(bytesRemain > 0 && memcmp(src, dst, bytesRemain) != 0)
|
|
{
|
|
deltasList.push_back(DeltaSection());
|
|
deltasList.back().offs = src - srcBegin;
|
|
deltasList.back().contents.append(src, bytesRemain);
|
|
}
|
|
}
|
|
}
|
|
|
|
// serialise as an array, move the storage from the list into here
|
|
rdcarray<DeltaSection> deltas(deltasList.size());
|
|
|
|
{
|
|
// swap between the list and array, so all the buffers just move storage
|
|
size_t i = 0;
|
|
for(auto it = deltasList.begin(); it != deltasList.end(); it++)
|
|
{
|
|
deltas[i].swap(*it);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// fast path - no changes.
|
|
if(deltas.empty())
|
|
{
|
|
uncompSize = 0;
|
|
}
|
|
else
|
|
{
|
|
// serialise to an invalid writer, to get the size of the data that will be written.
|
|
WriteSerialiser ser(new StreamWriter(StreamWriter::InvalidStream), Ownership::Stream);
|
|
|
|
SERIALISE_ELEMENT(deltas);
|
|
|
|
uncompSize = ser.GetWriter()->GetOffset() + ser.GetChunkAlignment();
|
|
}
|
|
|
|
xferser.Serialise("uncompSize"_lit, uncompSize);
|
|
|
|
if(uncompSize > 0)
|
|
{
|
|
WriteSerialiser ser(new StreamWriter(new LZ4Compressor(xferser.GetWriter(), Ownership::Nothing),
|
|
Ownership::Stream),
|
|
Ownership::Stream);
|
|
|
|
SERIALISE_ELEMENT(deltas);
|
|
|
|
char empty[128] = {};
|
|
|
|
// add any necessary padding.
|
|
uint64_t offs = ser.GetWriter()->GetOffset();
|
|
RDCASSERT(offs <= uncompSize, offs, uncompSize);
|
|
RDCASSERT(uncompSize - offs < sizeof(empty), offs, uncompSize);
|
|
|
|
if(offs < uncompSize)
|
|
ser.GetWriter()->Write(empty, uncompSize - offs);
|
|
}
|
|
|
|
// This is the proxy side, so we have the complete newest contents in data. Swap the new data
|
|
// into refData for next time.
|
|
referenceData.swap(newData);
|
|
}
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_CacheBufferData(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId buff)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_CacheBufferData;
|
|
ReplayProxyPacket packet = eReplayProxy_CacheBufferData;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(buff);
|
|
END_PARAMS();
|
|
}
|
|
|
|
bytebuf data;
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->GetBufferData(buff, 0, 0, data);
|
|
}
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
SERIALISE_ELEMENT(packet);
|
|
}
|
|
|
|
DeltaTransferBytes(retser, m_ProxyBufferData[buff], data);
|
|
|
|
retser.EndChunk();
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::CacheBufferData(ResourceId buff)
|
|
{
|
|
PROXY_FUNCTION(CacheBufferData, buff);
|
|
}
|
|
|
|
template <typename ParamSerialiser, typename ReturnSerialiser>
|
|
void ReplayProxy::Proxied_CacheTextureData(ParamSerialiser ¶mser, ReturnSerialiser &retser,
|
|
ResourceId tex, const Subresource &sub,
|
|
const GetTextureDataParams ¶ms)
|
|
{
|
|
const ReplayProxyPacket expectedPacket = eReplayProxy_CacheTextureData;
|
|
ReplayProxyPacket packet = eReplayProxy_CacheTextureData;
|
|
|
|
{
|
|
BEGIN_PARAMS();
|
|
SERIALISE_ELEMENT(tex);
|
|
SERIALISE_ELEMENT(sub);
|
|
SERIALISE_ELEMENT(params);
|
|
END_PARAMS();
|
|
}
|
|
|
|
bytebuf data;
|
|
|
|
{
|
|
REMOTE_EXECUTION();
|
|
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
|
|
m_Remote->GetTextureData(tex, sub, params, data);
|
|
}
|
|
|
|
{
|
|
ReturnSerialiser &ser = retser;
|
|
PACKET_HEADER(packet);
|
|
SERIALISE_ELEMENT(packet);
|
|
}
|
|
|
|
TextureCacheEntry entry = {tex, sub};
|
|
DeltaTransferBytes(retser, m_ProxyTextureData[entry], data);
|
|
|
|
retser.EndChunk();
|
|
|
|
CheckError(packet, expectedPacket);
|
|
}
|
|
|
|
void ReplayProxy::CacheTextureData(ResourceId tex, const Subresource &sub,
|
|
const GetTextureDataParams ¶ms)
|
|
{
|
|
PROXY_FUNCTION(CacheTextureData, tex, sub, params);
|
|
}
|
|
|
|
#pragma endregion Proxied Functions
|
|
|
|
// If a remap is required, modify the params that are used when getting the proxy texture data
|
|
// for replay on the current driver.
|
|
void ReplayProxy::RemapProxyTextureIfNeeded(TextureDescription &tex, GetTextureDataParams ¶ms)
|
|
{
|
|
if(NeedRemapForFetch(tex.format))
|
|
{
|
|
// currently only OpenGL ES need to remap all the depth formats for fetch
|
|
// when depth read is not supported
|
|
params.remap = RemapTexture::RGBA32;
|
|
tex.format.compCount = 4;
|
|
tex.format.compByteWidth = 4;
|
|
tex.format.compType = CompType::Float;
|
|
tex.format.type = ResourceFormatType::Regular;
|
|
tex.creationFlags &= ~TextureCategory::DepthTarget;
|
|
return;
|
|
}
|
|
|
|
if(m_Proxy->IsTextureSupported(tex))
|
|
return;
|
|
|
|
if(tex.format.Special())
|
|
{
|
|
switch(tex.format.type)
|
|
{
|
|
case ResourceFormatType::S8:
|
|
case ResourceFormatType::D16S8:
|
|
case ResourceFormatType::D24S8:
|
|
case ResourceFormatType::D32S8:
|
|
tex.format.compType = CompType::Float;
|
|
params.remap = RemapTexture::RGBA32;
|
|
tex.creationFlags &= ~TextureCategory::DepthTarget;
|
|
break;
|
|
case ResourceFormatType::A8:
|
|
tex.format.compType = CompType::UNorm;
|
|
params.remap = RemapTexture::RGBA8;
|
|
break;
|
|
case ResourceFormatType::BC1:
|
|
case ResourceFormatType::BC2:
|
|
case ResourceFormatType::BC3:
|
|
case ResourceFormatType::BC7:
|
|
tex.format.compType = CompType::UNorm;
|
|
params.remap = RemapTexture::RGBA8;
|
|
break;
|
|
case ResourceFormatType::BC4:
|
|
case ResourceFormatType::BC5:
|
|
case ResourceFormatType::BC6:
|
|
tex.format.compType = CompType::Float;
|
|
params.remap = RemapTexture::RGBA16;
|
|
break;
|
|
case ResourceFormatType::ASTC:
|
|
tex.format.compType = CompType::Float;
|
|
params.remap = RemapTexture::RGBA16;
|
|
break;
|
|
case ResourceFormatType::EAC:
|
|
case ResourceFormatType::R5G6B5:
|
|
case ResourceFormatType::R5G5B5A1:
|
|
case ResourceFormatType::R4G4:
|
|
case ResourceFormatType::R4G4B4A4:
|
|
case ResourceFormatType::ETC2: params.remap = RemapTexture::RGBA8; break;
|
|
case ResourceFormatType::R10G10B10A2: params.remap = RemapTexture::RGBA16; break;
|
|
default:
|
|
RDCERR("Don't know how to remap resource format type %u, falling back to RGBA32",
|
|
tex.format.type);
|
|
tex.format.compType = CompType::Float;
|
|
params.remap = RemapTexture::RGBA32;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(tex.format.compByteWidth == 1)
|
|
{
|
|
params.remap = RemapTexture::RGBA8;
|
|
|
|
if(tex.format.compType == CompType::SNorm || tex.format.compType == CompType::UNormSRGB)
|
|
{
|
|
params.remap = RemapTexture::RGBA16;
|
|
tex.format.compType = CompType::Float;
|
|
}
|
|
}
|
|
else if(tex.format.compByteWidth == 2)
|
|
{
|
|
params.remap = RemapTexture::RGBA16;
|
|
}
|
|
else
|
|
{
|
|
params.remap = RemapTexture::RGBA32;
|
|
}
|
|
|
|
// always remap depth to RGBA32F, because D16_UNORM will lose precision if remapped to R16_FLOAT
|
|
if(tex.format.compType == CompType::Depth)
|
|
{
|
|
params.remap = RemapTexture::RGBA32;
|
|
tex.format.compType = CompType::Float;
|
|
}
|
|
}
|
|
|
|
tex.format.SetBGRAOrder(false);
|
|
tex.format.type = ResourceFormatType::Regular;
|
|
tex.format.compCount = 4;
|
|
|
|
switch(params.remap)
|
|
{
|
|
case RemapTexture::NoRemap: RDCERR("IsTextureSupported == false, but we have no remap"); break;
|
|
case RemapTexture::RGBA8: tex.format.compByteWidth = 1; break;
|
|
case RemapTexture::RGBA16: tex.format.compByteWidth = 2; break;
|
|
case RemapTexture::RGBA32: tex.format.compByteWidth = 4; break;
|
|
}
|
|
}
|
|
|
|
void ReplayProxy::EnsureTexCached(ResourceId &texid, CompType &typeCast, const Subresource &sub)
|
|
{
|
|
if(m_Reader.IsErrored() || m_Writer.IsErrored())
|
|
return;
|
|
|
|
if(m_LocalTextures.find(texid) != m_LocalTextures.end())
|
|
return;
|
|
|
|
if(texid == ResourceId())
|
|
return;
|
|
|
|
TextureCacheEntry entry = {texid, sub};
|
|
|
|
// ignore parameters in the key which don't matter for this texture
|
|
{
|
|
auto it = m_TextureInfo.find(texid);
|
|
if(it != m_TextureInfo.end())
|
|
{
|
|
if(it->second.mips <= 1)
|
|
entry.sub.mip = 0;
|
|
|
|
if(it->second.dimension == 3 || it->second.arraysize <= 1)
|
|
entry.sub.slice = 0;
|
|
|
|
if(it->second.msSamp <= 1)
|
|
entry.sub.sample = 0;
|
|
}
|
|
}
|
|
|
|
auto proxyit = m_ProxyTextures.find(texid);
|
|
|
|
if(m_TextureProxyCache.find(entry) == m_TextureProxyCache.end())
|
|
{
|
|
if(proxyit == m_ProxyTextures.end())
|
|
{
|
|
TextureDescription tex = GetTexture(texid);
|
|
|
|
ProxyTextureProperties proxy;
|
|
RemapProxyTextureIfNeeded(tex, proxy.params);
|
|
|
|
proxy.id = m_Proxy->CreateProxyTexture(tex);
|
|
proxy.msSamp = RDCMAX(1U, tex.msSamp);
|
|
proxyit = m_ProxyTextures.insert(std::make_pair(texid, proxy)).first;
|
|
}
|
|
|
|
const ProxyTextureProperties &proxy = proxyit->second;
|
|
|
|
for(uint32_t sample = 0; sample < proxy.msSamp; sample++)
|
|
{
|
|
Subresource s = sub;
|
|
s.sample = sample;
|
|
|
|
TextureCacheEntry sampleArrayEntry = {texid, s};
|
|
|
|
GetTextureDataParams params = proxy.params;
|
|
|
|
params.typeCast = typeCast;
|
|
params.standardLayout = true;
|
|
|
|
#if ENABLED(TRANSFER_RESOURCE_CONTENTS_DELTAS)
|
|
CacheTextureData(texid, s, params);
|
|
#else
|
|
GetTextureData(texid, s, params, m_ProxyTextureData[entry]);
|
|
#endif
|
|
|
|
auto it = m_ProxyTextureData.find(sampleArrayEntry);
|
|
if(it != m_ProxyTextureData.end())
|
|
m_Proxy->SetProxyTextureData(proxy.id, s, it->second.data(), it->second.size());
|
|
}
|
|
|
|
m_TextureProxyCache.insert(entry);
|
|
}
|
|
|
|
if(proxyit->second.params.remap != RemapTexture::NoRemap)
|
|
typeCast = BaseRemapType(typeCast);
|
|
|
|
// change texid to the proxy texture's ID for passing to our proxy renderer
|
|
texid = proxyit->second.id;
|
|
}
|
|
|
|
void ReplayProxy::EnsureBufCached(ResourceId bufid)
|
|
{
|
|
if(m_Reader.IsErrored() || m_Writer.IsErrored())
|
|
return;
|
|
|
|
if(m_BufferProxyCache.find(bufid) == m_BufferProxyCache.end())
|
|
{
|
|
if(m_ProxyBufferIds.find(bufid) == m_ProxyBufferIds.end())
|
|
{
|
|
BufferDescription buf = GetBuffer(bufid);
|
|
m_ProxyBufferIds[bufid] = m_Proxy->CreateProxyBuffer(buf);
|
|
}
|
|
|
|
ResourceId proxyid = m_ProxyBufferIds[bufid];
|
|
|
|
#if ENABLED(TRANSFER_RESOURCE_CONTENTS_DELTAS)
|
|
CacheBufferData(bufid);
|
|
#else
|
|
GetBufferData(bufid, 0, 0, m_ProxyBufferData[bufid]);
|
|
#endif
|
|
|
|
auto it = m_ProxyBufferData.find(bufid);
|
|
if(it != m_ProxyBufferData.end())
|
|
m_Proxy->SetProxyBufferData(proxyid, it->second.data(), it->second.size());
|
|
|
|
m_BufferProxyCache.insert(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)
|
|
{
|
|
// if the data has changed, destroy the old window so we'll recreate
|
|
if(m_PreviewWindow == 0 || memcmp(&m_PreviewWindowingData, &data, sizeof(data)) != 0)
|
|
{
|
|
if(m_PreviewWindow)
|
|
{
|
|
RDCDEBUG("Re-creating preview window due to change in data");
|
|
m_Replay->DestroyOutputWindow(m_PreviewOutput);
|
|
}
|
|
|
|
m_PreviewOutput = m_Replay->MakeOutputWindow(data, false);
|
|
|
|
m_PreviewWindowingData = data;
|
|
}
|
|
}
|
|
|
|
if(m_FrameRecord.drawcallList.empty())
|
|
m_FrameRecord = m_Replay->GetFrameRecord();
|
|
}
|
|
}
|
|
|
|
void ReplayProxy::ShutdownPreviewWindow()
|
|
{
|
|
if(m_Replay && m_PreviewOutput)
|
|
{
|
|
m_Replay->DestroyOutputWindow(m_PreviewOutput);
|
|
m_PreviewOutput = 0;
|
|
}
|
|
|
|
if(m_PreviewWindow)
|
|
m_PreviewWindow(false, {});
|
|
}
|
|
|
|
void ReplayProxy::RefreshPreviewWindow()
|
|
{
|
|
InitPreviewWindow();
|
|
|
|
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_EventID);
|
|
|
|
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.typeCast = CompType::Typeless;
|
|
cfg.rangeMin = 0.0f;
|
|
cfg.rangeMax = 1.0f;
|
|
cfg.flipY = false;
|
|
cfg.hdrMultiplier = -1.0f;
|
|
cfg.linearDisplayAsGamma = true;
|
|
cfg.customShaderId = ResourceId();
|
|
cfg.subresource = {0, 0, 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);
|
|
|
|
// use the smaller scale, and shrink a little so we don't display it fullscreen - makes it a
|
|
// little clearer that this is the replay, not the original application
|
|
cfg.scale = RDCMIN(xScale, yScale) * 0.9f;
|
|
|
|
// centre the texture
|
|
cfg.xOffset = (float(winWidth) - float(texInfo.width) * cfg.scale) / 2.0f;
|
|
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());
|
|
}
|
|
}
|
|
|
|
void ReplayProxy::InitRemoteExecutionThread()
|
|
{
|
|
m_RemoteExecutionThread = Threading::CreateThread([this]() { RemoteExecutionThreadEntry(); });
|
|
}
|
|
|
|
void ReplayProxy::ShutdownRemoteExecutionThread()
|
|
{
|
|
if(m_RemoteExecutionThread)
|
|
{
|
|
Atomic::Inc32(&m_RemoteExecutionKill);
|
|
|
|
Threading::JoinThread(m_RemoteExecutionThread);
|
|
Threading::CloseThread(m_RemoteExecutionThread);
|
|
m_RemoteExecutionThread = 0;
|
|
}
|
|
}
|
|
|
|
void ReplayProxy::BeginRemoteExecution()
|
|
{
|
|
if(m_RemoteServer)
|
|
{
|
|
// m_RemoteExecutionActive must be inactive because it starts inactive, and we synchronise it in
|
|
// EndRemoteExecution
|
|
Atomic::CmpExch32(&m_RemoteExecutionState, RemoteExecution_Inactive, RemoteExecution_ThreadIdle);
|
|
}
|
|
else
|
|
{
|
|
// don't do anything, we go immediately to EndRemoteExecution and start reading packets
|
|
}
|
|
}
|
|
|
|
void ReplayProxy::EndRemoteExecution()
|
|
{
|
|
if(m_RemoteServer)
|
|
{
|
|
// wait until the thread is idle, and move it to inactive. This is unlikely to contend because
|
|
// the thread only becomes active when it's sending a keepalive packet.
|
|
while(Atomic::CmpExch32(&m_RemoteExecutionState, RemoteExecution_ThreadIdle,
|
|
RemoteExecution_Inactive) == RemoteExecution_ThreadIdle)
|
|
Threading::Sleep(0);
|
|
|
|
// send the finished packet
|
|
m_Writer.BeginChunk(eReplayProxy_RemoteExecutionFinished, 0);
|
|
m_Writer.EndChunk();
|
|
}
|
|
else
|
|
{
|
|
while(!m_Writer.IsErrored() && !m_Reader.IsErrored() && !m_IsErrored)
|
|
{
|
|
ReplayProxyPacket packet = m_Reader.ReadChunk<ReplayProxyPacket>();
|
|
m_Reader.EndChunk();
|
|
|
|
if(packet == eReplayProxy_RemoteExecutionKeepAlive)
|
|
{
|
|
RDCDEBUG("Got keepalive packet");
|
|
continue;
|
|
}
|
|
|
|
if(packet != eReplayProxy_RemoteExecutionFinished)
|
|
{
|
|
CheckError(packet, eReplayProxy_RemoteExecutionFinished);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
CheckError(eReplayProxy_RemoteExecutionFinished, eReplayProxy_RemoteExecutionFinished);
|
|
}
|
|
}
|
|
|
|
void ReplayProxy::RemoteExecutionThreadEntry()
|
|
{
|
|
Threading::SetCurrentThreadName("RemoteExecutionThreadEntry");
|
|
|
|
// while we're alive
|
|
while(Atomic::CmpExch32(&m_RemoteExecutionKill, 0, 0) == 0)
|
|
{
|
|
if(IsThreadIdle())
|
|
{
|
|
// while we're still idle (rather than inactive), do a period of:
|
|
// 1. Wait for 1 second, aborting if we've been moved to inactive
|
|
// 2. Send a keepalive packet
|
|
//
|
|
// The wait we tradeoff between busy-waits (to catch very short-lived executions) with waits
|
|
// to avoid spinning too much.
|
|
|
|
while(IsThreadIdle())
|
|
{
|
|
PerformanceTimer waitTimer;
|
|
while(waitTimer.GetMilliseconds() < 5 && IsThreadIdle())
|
|
(void)waitTimer; // wait
|
|
|
|
if(!IsThreadIdle())
|
|
break;
|
|
|
|
// 5ms has elapsed, wait up to 100ms in 5ms chunks
|
|
while(waitTimer.GetMilliseconds() < 100 && IsThreadIdle())
|
|
Threading::Sleep(5);
|
|
|
|
if(!IsThreadIdle())
|
|
break;
|
|
|
|
// 100ms has elapsed, wait up to 1000ms in 50ms chunks
|
|
while(waitTimer.GetMilliseconds() < 1000 && IsThreadIdle())
|
|
Threading::Sleep(50);
|
|
|
|
if(!IsThreadIdle())
|
|
break;
|
|
|
|
// if we got here, it's time to send a keepalive packet. First become active so that
|
|
// EndRemoteExecution() blocks until this finishes
|
|
if(Atomic::CmpExch32(&m_RemoteExecutionState, RemoteExecution_ThreadIdle,
|
|
RemoteExecution_ThreadActive) == RemoteExecution_ThreadIdle)
|
|
{
|
|
m_Writer.BeginChunk(eReplayProxy_RemoteExecutionKeepAlive, 0);
|
|
m_Writer.EndChunk();
|
|
|
|
Atomic::CmpExch32(&m_RemoteExecutionState, RemoteExecution_ThreadActive,
|
|
RemoteExecution_ThreadIdle);
|
|
}
|
|
}
|
|
}
|
|
|
|
// don't busy-wait
|
|
Threading::Sleep(0);
|
|
}
|
|
}
|
|
|
|
bool ReplayProxy::CheckError(ReplayProxyPacket receivedPacket, ReplayProxyPacket expectedPacket)
|
|
{
|
|
if(m_Writer.IsErrored() || m_Reader.IsErrored() || m_IsErrored)
|
|
{
|
|
RDCERR("Error during processing of %s", ToStr(expectedPacket).c_str());
|
|
m_IsErrored = true;
|
|
return true;
|
|
}
|
|
|
|
if(receivedPacket != expectedPacket)
|
|
{
|
|
RDCERR("Expected %s, received %s", ToStr(expectedPacket).c_str(), ToStr(receivedPacket).c_str());
|
|
m_IsErrored = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ReplayProxy::Tick(int type)
|
|
{
|
|
if(!m_RemoteServer)
|
|
return true;
|
|
|
|
if(m_Writer.IsErrored() || m_Reader.IsErrored() || m_IsErrored)
|
|
return false;
|
|
|
|
const ReplayProxyPacket expectedPacket = (ReplayProxyPacket)type;
|
|
ReplayProxyPacket packet = (ReplayProxyPacket)type;
|
|
|
|
PROXY_DEBUG("Received %s", ToStr(packet).c_str());
|
|
|
|
switch(packet)
|
|
{
|
|
case eReplayProxy_CacheBufferData: CacheBufferData(ResourceId()); break;
|
|
case eReplayProxy_CacheTextureData:
|
|
CacheTextureData(ResourceId(), Subresource(), GetTextureDataParams());
|
|
break;
|
|
case eReplayProxy_ReplayLog: ReplayLog(0, (ReplayLogType)0); break;
|
|
case eReplayProxy_FetchStructuredFile: FetchStructuredFile(); break;
|
|
case eReplayProxy_GetAPIProperties: GetAPIProperties(); break;
|
|
case eReplayProxy_GetPassEvents: GetPassEvents(0); break;
|
|
case eReplayProxy_GetResources: GetResources(); break;
|
|
case eReplayProxy_GetTextures: GetTextures(); break;
|
|
case eReplayProxy_GetTexture: GetTexture(ResourceId()); break;
|
|
case eReplayProxy_GetBuffers: GetBuffers(); break;
|
|
case eReplayProxy_GetBuffer: GetBuffer(ResourceId()); break;
|
|
case eReplayProxy_GetShaderEntryPoints: GetShaderEntryPoints(ResourceId()); break;
|
|
case eReplayProxy_GetShader: GetShader(ResourceId(), ResourceId(), ShaderEntryPoint()); break;
|
|
case eReplayProxy_GetDebugMessages: GetDebugMessages(); break;
|
|
case eReplayProxy_GetBufferData:
|
|
{
|
|
bytebuf dummy;
|
|
GetBufferData(ResourceId(), 0, 0, dummy);
|
|
break;
|
|
}
|
|
case eReplayProxy_GetTextureData:
|
|
{
|
|
bytebuf dummy;
|
|
GetTextureData(ResourceId(), Subresource(), GetTextureDataParams(), dummy);
|
|
break;
|
|
}
|
|
case eReplayProxy_SavePipelineState: SavePipelineState(0); break;
|
|
case eReplayProxy_GetUsage: GetUsage(ResourceId()); break;
|
|
case eReplayProxy_GetLiveID: GetLiveID(ResourceId()); break;
|
|
case eReplayProxy_GetFrameRecord: GetFrameRecord(); break;
|
|
case eReplayProxy_IsRenderOutput: IsRenderOutput(ResourceId()); break;
|
|
case eReplayProxy_NeedRemapForFetch: NeedRemapForFetch(ResourceFormat()); break;
|
|
case eReplayProxy_FreeTargetResource: FreeTargetResource(ResourceId()); break;
|
|
case eReplayProxy_FetchCounters:
|
|
{
|
|
rdcarray<GPUCounter> counters;
|
|
FetchCounters(counters);
|
|
break;
|
|
}
|
|
case eReplayProxy_EnumerateCounters: EnumerateCounters(); break;
|
|
case eReplayProxy_DescribeCounter: DescribeCounter(GPUCounter::EventGPUDuration); break;
|
|
case eReplayProxy_FillCBufferVariables:
|
|
{
|
|
rdcarray<ShaderVariable> vars;
|
|
bytebuf data;
|
|
FillCBufferVariables(ResourceId(), ResourceId(), "", 0, vars, data);
|
|
break;
|
|
}
|
|
case eReplayProxy_InitPostVS: InitPostVSBuffers(0); break;
|
|
case eReplayProxy_InitPostVSVec:
|
|
{
|
|
rdcarray<uint32_t> dummy;
|
|
InitPostVSBuffers(dummy);
|
|
break;
|
|
}
|
|
case eReplayProxy_GetPostVS: GetPostVSBuffers(0, 0, 0, MeshDataStage::Unknown); break;
|
|
case eReplayProxy_BuildTargetShader:
|
|
{
|
|
rdcstr entry;
|
|
ResourceId id;
|
|
rdcstr errors;
|
|
BuildTargetShader(ShaderEncoding::Unknown, bytebuf(), entry, ShaderCompileFlags(),
|
|
ShaderStage::Vertex, id, errors);
|
|
break;
|
|
}
|
|
case eReplayProxy_ReplaceResource: ReplaceResource(ResourceId(), ResourceId()); break;
|
|
case eReplayProxy_RemoveReplacement: RemoveReplacement(ResourceId()); break;
|
|
case eReplayProxy_DebugVertex: DebugVertex(0, 0, 0, 0); break;
|
|
case eReplayProxy_DebugPixel: DebugPixel(0, 0, 0, 0, 0); break;
|
|
case eReplayProxy_DebugThread:
|
|
{
|
|
uint32_t dummy1[3] = {0};
|
|
uint32_t dummy2[3] = {0};
|
|
DebugThread(0, dummy1, dummy2);
|
|
break;
|
|
}
|
|
case eReplayProxy_ContinueDebug: ContinueDebug(NULL); break;
|
|
case eReplayProxy_FreeDebugger: FreeDebugger(NULL); break;
|
|
case eReplayProxy_RenderOverlay:
|
|
RenderOverlay(ResourceId(), FloatVector(), DebugOverlay::NoOverlay, 0, rdcarray<uint32_t>());
|
|
break;
|
|
case eReplayProxy_PixelHistory:
|
|
PixelHistory(rdcarray<EventUsage>(), ResourceId(), 0, 0, Subresource(), CompType::Typeless);
|
|
break;
|
|
case eReplayProxy_DisassembleShader: DisassembleShader(ResourceId(), NULL, ""); break;
|
|
case eReplayProxy_GetDisassemblyTargets: GetDisassemblyTargets(); break;
|
|
case eReplayProxy_GetTargetShaderEncodings: GetTargetShaderEncodings(); break;
|
|
case eReplayProxy_GetDriverInfo: GetDriverInfo(); break;
|
|
case eReplayProxy_GetAvailableGPUs: GetAvailableGPUs(); break;
|
|
default: RDCERR("Unexpected command %u", type); return false;
|
|
}
|
|
|
|
PROXY_DEBUG("Processed %s", ToStr(packet).c_str());
|
|
|
|
if(packet == eReplayProxy_ReplayLog)
|
|
RefreshPreviewWindow();
|
|
|
|
if(CheckError(packet, packet))
|
|
return false;
|
|
|
|
return true;
|
|
}
|