Commit 06ce17e0 by Cody Northrop Committed by Commit Bot

Capture/Replay: Reset buffers on replay loop

This CL adds infrastructure for tracking whether resources need to be reset when looping back to the beginning of the frame sequence. A new function is generated on the last frame: ResetContext*Replay(). It will contain calls to gen, delete, and restore contents of resources. This CL only supports Buffer resets. Bug: b/152512564 Bug: angleproject:3662 Bug: angleproject:4599 Change-Id: I46672dd70dcb997967e3cc0897308144f2582e21 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2168121 Commit-Queue: Cody Northrop <cnorthrop@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent c02cdff7
...@@ -27,6 +27,9 @@ std::function<void()> SetupContextReplay = reinterpret_cast<void (*)()>( ...@@ -27,6 +27,9 @@ std::function<void()> SetupContextReplay = reinterpret_cast<void (*)()>(
std::function<void(int)> ReplayContextFrame = reinterpret_cast<void (*)(int)>( std::function<void(int)> ReplayContextFrame = reinterpret_cast<void (*)(int)>(
ANGLE_MACRO_CONCAT(ReplayContext, ANGLE_MACRO_CONCAT(ReplayContext,
ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_SAMPLE_CONTEXT_ID, Frame))); ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_SAMPLE_CONTEXT_ID, Frame)));
std::function<void()> ResetContextReplay = reinterpret_cast<void (*)()>(
ANGLE_MACRO_CONCAT(ResetContext,
ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_SAMPLE_CONTEXT_ID, Replay)));
class CaptureReplaySample : public SampleApplication class CaptureReplaySample : public SampleApplication
{ {
...@@ -59,12 +62,19 @@ class CaptureReplaySample : public SampleApplication ...@@ -59,12 +62,19 @@ class CaptureReplaySample : public SampleApplication
// Compute the current frame, looping from kReplayFrameStart to kReplayFrameEnd. // Compute the current frame, looping from kReplayFrameStart to kReplayFrameEnd.
uint32_t frame = uint32_t frame =
kReplayFrameStart + (mCurrentFrame % (kReplayFrameEnd - kReplayFrameStart)); kReplayFrameStart + (mCurrentFrame % (kReplayFrameEnd - kReplayFrameStart));
if (mPreviousFrame > frame)
{
ResetContextReplay();
}
ReplayContextFrame(frame); ReplayContextFrame(frame);
mPreviousFrame = frame;
mCurrentFrame++; mCurrentFrame++;
} }
private: private:
uint32_t mCurrentFrame = 0; uint32_t mCurrentFrame = 0;
uint32_t mPreviousFrame = 0;
}; };
int main(int argc, char **argv) int main(int argc, char **argv)
......
...@@ -734,13 +734,84 @@ void WriteLoadBinaryDataCall(bool compression, ...@@ -734,13 +734,84 @@ void WriteLoadBinaryDataCall(bool compression,
out << " LoadBinaryData(\"" << binaryDataFileName << "\");\n"; out << " LoadBinaryData(\"" << binaryDataFileName << "\");\n";
} }
void MaybeResetResources(std::stringstream &out,
ResourceIDType resourceIDType,
DataCounters *counters,
std::stringstream &header,
ResourceTracker *resourceTracker,
std::vector<uint8_t> *binaryData)
{
switch (resourceIDType)
{
case ResourceIDType::Buffer:
{
BufferSet &newBuffers = resourceTracker->getNewBuffers();
BufferCalls &bufferRegenCalls = resourceTracker->getBufferRegenCalls();
BufferCalls &bufferRestoreCalls = resourceTracker->getBufferRestoreCalls();
// If we have any new buffers generated and not deleted during the run, delete them now
if (!newBuffers.empty())
{
out << " const GLuint deleteBuffers[] = {";
BufferSet::iterator bufferIter = newBuffers.begin();
for (size_t i = 0; bufferIter != newBuffers.end(); ++i, ++bufferIter)
{
if (i > 0)
{
out << ", ";
}
if ((i % 4) == 0)
{
out << "\n ";
}
out << "gBufferMap[" << (*bufferIter).value << "]";
}
out << "};\n";
out << " glDeleteBuffers(" << newBuffers.size() << ", deleteBuffers);\n";
}
// If any of our starting buffers were deleted during the run, recreate them
BufferSet &buffersToRegen = resourceTracker->getBuffersToRegen();
for (const gl::BufferID id : buffersToRegen)
{
// Emit their regen calls
for (CallCapture &call : bufferRegenCalls[id])
{
out << " ";
WriteCppReplayForCall(call, counters, out, header, binaryData);
out << ";\n";
}
}
// If any of our starting buffers were modified during the run, restore their contents
BufferSet &buffersToRestore = resourceTracker->getBuffersToRestore();
for (const gl::BufferID id : buffersToRestore)
{
// Emit their restore calls
for (CallCapture &call : bufferRestoreCalls[id])
{
out << " ";
WriteCppReplayForCall(call, counters, out, header, binaryData);
out << ";\n";
}
}
break;
}
default:
// TODO (http://anglebug.com/4599): Reset more than just buffers
break;
}
}
void WriteCppReplay(bool compression, void WriteCppReplay(bool compression,
const std::string &outDir, const std::string &outDir,
gl::ContextID contextId, gl::ContextID contextId,
const std::string &captureLabel, const std::string &captureLabel,
uint32_t frameIndex, uint32_t frameIndex,
uint32_t frameEnd,
const std::vector<CallCapture> &frameCalls, const std::vector<CallCapture> &frameCalls,
const std::vector<CallCapture> &setupCalls, const std::vector<CallCapture> &setupCalls,
ResourceTracker *resourceTracker,
std::vector<uint8_t> *binaryData) std::vector<uint8_t> *binaryData)
{ {
DataCounters counters; DataCounters counters;
...@@ -782,6 +853,28 @@ void WriteCppReplay(bool compression, ...@@ -782,6 +853,28 @@ void WriteCppReplay(bool compression,
out << "\n"; out << "\n";
} }
if (frameIndex == frameEnd)
{
// Emit code to reset back to starting state
out << "void ResetContext" << Str(static_cast<int>(contextId)) << "Replay()\n";
out << "{\n";
std::stringstream restoreCallStream;
// For now, we only reset buffer states
// TODO (http://anglebug.com/4599): Reset more state on frame loop
for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
{
MaybeResetResources(restoreCallStream, resourceType, &counters, header, resourceTracker,
binaryData);
}
out << restoreCallStream.str();
out << "}\n";
out << "\n";
}
out << "void " << FmtReplayFunction(contextId, frameIndex) << "\n"; out << "void " << FmtReplayFunction(contextId, frameIndex) << "\n";
out << "{\n"; out << "{\n";
...@@ -869,6 +962,7 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -869,6 +962,7 @@ void WriteCppReplayIndexFiles(bool compression,
header << "void SetupContext" << static_cast<int>(contextId) << "Replay();\n"; header << "void SetupContext" << static_cast<int>(contextId) << "Replay();\n";
header << "void ReplayContext" << static_cast<int>(contextId) header << "void ReplayContext" << static_cast<int>(contextId)
<< "Frame(uint32_t frameIndex);\n"; << "Frame(uint32_t frameIndex);\n";
header << "void ResetContext" << static_cast<int>(contextId) << "Replay();\n";
header << "\n"; header << "\n";
header << "using FramebufferChangeCallback = void(*)(void *userData, GLenum target, GLuint " header << "using FramebufferChangeCallback = void(*)(void *userData, GLenum target, GLuint "
"framebuffer);\n"; "framebuffer);\n";
...@@ -1752,8 +1846,39 @@ void CaptureTextureContents(std::vector<CallCapture> *setupCalls, ...@@ -1752,8 +1846,39 @@ void CaptureTextureContents(std::vector<CallCapture> *setupCalls,
} }
} }
// TODO(http://anglebug.com/4599): Improve reset/restore call generation
// There are multiple ways to track reset calls for individual resources. For now, we are tracking
// separate lists of instructions that mirror the calls created during mid-execution setup. Other
// methods could involve passing the original CallCaptures to this function, or tracking the
// indices of original setup calls.
void CaptureBufferResetCalls(const gl::State &replayState,
ResourceTracker *resourceTracker,
gl::BufferID *id,
const gl::Buffer *buffer)
{
// Track this as a starting resource that may need to be restored.
BufferSet &startingBuffers = resourceTracker->getStartingBuffers();
startingBuffers.insert(*id);
// Track calls to regenerate a given buffer
BufferCalls &bufferRegenCalls = resourceTracker->getBufferRegenCalls();
Capture(&bufferRegenCalls[*id], CaptureDeleteBuffers(replayState, true, 1, id));
Capture(&bufferRegenCalls[*id], CaptureGenBuffers(replayState, true, 1, id));
MaybeCaptureUpdateResourceIDs(&bufferRegenCalls[*id]);
// Track calls to restore a given buffer's contents
BufferCalls &bufferRestoreCalls = resourceTracker->getBufferRestoreCalls();
Capture(&bufferRestoreCalls[*id],
CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
Capture(&bufferRestoreCalls[*id],
CaptureBufferData(replayState, true, gl::BufferBinding::Array,
static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(),
buffer->getUsage()));
}
void CaptureMidExecutionSetup(const gl::Context *context, void CaptureMidExecutionSetup(const gl::Context *context,
std::vector<CallCapture> *setupCalls, std::vector<CallCapture> *setupCalls,
ResourceTracker *resourceTracker,
const ShaderSourceMap &cachedShaderSources, const ShaderSourceMap &cachedShaderSources,
const ProgramSourceMap &cachedProgramSources, const ProgramSourceMap &cachedProgramSources,
const TextureLevelDataMap &cachedTextureLevelData) const TextureLevelDataMap &cachedTextureLevelData)
...@@ -1807,6 +1932,9 @@ void CaptureMidExecutionSetup(const gl::Context *context, ...@@ -1807,6 +1932,9 @@ void CaptureMidExecutionSetup(const gl::Context *context,
static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(), static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(),
buffer->getUsage())); buffer->getUsage()));
// Generate the calls needed to restore this buffer to original state for frame looping
CaptureBufferResetCalls(replayState, resourceTracker, &id, buffer);
GLboolean dontCare; GLboolean dontCare;
(void)buffer->unmap(context, &dontCare); (void)buffer->unmap(context, &dontCare);
} }
...@@ -3229,6 +3357,28 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur ...@@ -3229,6 +3357,28 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur
{ {
mBufferDataMap.erase(bufferDataInfo); mBufferDataMap.erase(bufferDataInfo);
} }
// If we're capturing, track what new buffers have been genned
if (mFrameIndex >= mFrameStart)
{
mResourceTracker.setDeletedBuffer(bufferIDs[i]);
}
}
break;
}
case gl::EntryPoint::GenBuffers:
{
GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
const gl::BufferID *bufferIDs =
call.params.getParam("buffersPacked", ParamType::TBufferIDPointer, 1)
.value.BufferIDPointerVal;
for (GLsizei i = 0; i < count; i++)
{
// If we're capturing, track what new buffers have been genned
if (mFrameIndex >= mFrameStart)
{
mResourceTracker.setGennedBuffer(bufferIDs[i]);
}
} }
break; break;
} }
...@@ -3391,6 +3541,9 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur ...@@ -3391,6 +3541,9 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur
// Track the bufferID that was just mapped // Track the bufferID that was just mapped
call.params.setMappedBufferID(buffer->id()); call.params.setMappedBufferID(buffer->id());
// Remember that it was mapped writable, for use during state reset
mResourceTracker.setBufferModified(buffer->id());
} }
break; break;
} }
...@@ -3403,6 +3556,20 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur ...@@ -3403,6 +3556,20 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur
break; break;
} }
case gl::EntryPoint::BufferData:
case gl::EntryPoint::BufferSubData:
{
gl::BufferBinding target =
call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
.value.BufferBindingVal;
gl::Buffer *buffer = context->getState().getTargetBuffer(target);
// Track that this buffer's contents have been modified
mResourceTracker.setBufferModified(buffer->id());
break;
}
default: default:
break; break;
} }
...@@ -3574,7 +3741,7 @@ void FrameCapture::onEndFrame(const gl::Context *context) ...@@ -3574,7 +3741,7 @@ void FrameCapture::onEndFrame(const gl::Context *context)
if (!mFrameCalls.empty() && mFrameIndex >= mFrameStart) if (!mFrameCalls.empty() && mFrameIndex >= mFrameStart)
{ {
WriteCppReplay(mCompression, mOutDirectory, context->id(), mCaptureLabel, mFrameIndex, WriteCppReplay(mCompression, mOutDirectory, context->id(), mCaptureLabel, mFrameIndex,
mFrameCalls, mSetupCalls, &mBinaryData); mFrameEnd, mFrameCalls, mSetupCalls, &mResourceTracker, &mBinaryData);
// Save the index files after the last frame. // Save the index files after the last frame.
if (mFrameIndex == mFrameEnd) if (mFrameIndex == mFrameEnd)
...@@ -3612,8 +3779,8 @@ void FrameCapture::onEndFrame(const gl::Context *context) ...@@ -3612,8 +3779,8 @@ void FrameCapture::onEndFrame(const gl::Context *context)
if (enabled() && mFrameIndex == mFrameStart) if (enabled() && mFrameIndex == mFrameStart)
{ {
mSetupCalls.clear(); mSetupCalls.clear();
CaptureMidExecutionSetup(context, &mSetupCalls, mCachedShaderSources, mCachedProgramSources, CaptureMidExecutionSetup(context, &mSetupCalls, &mResourceTracker, mCachedShaderSources,
mCachedTextureLevelData); mCachedProgramSources, mCachedTextureLevelData);
} }
} }
...@@ -3627,6 +3794,48 @@ int DataCounters::getAndIncrement(gl::EntryPoint entryPoint, const std::string & ...@@ -3627,6 +3794,48 @@ int DataCounters::getAndIncrement(gl::EntryPoint entryPoint, const std::string &
return mData[counterKey]++; return mData[counterKey]++;
} }
ResourceTracker::ResourceTracker() = default;
ResourceTracker::~ResourceTracker() = default;
void ResourceTracker::setDeletedBuffer(gl::BufferID id)
{
if (mNewBuffers.find(id) != mNewBuffers.end())
{
// This is a buffer genned after MEC was initialized, just clear it, since there will be no
// actions required for it to return to starting state.
mNewBuffers.erase(id);
return;
}
// Ensure this buffer was in our starting set
// It's possible this could fire if the app deletes buffers that were never generated
ASSERT(mStartingBuffers.find(id) != mStartingBuffers.end());
// In this case, the app is deleting a buffer we started with, we need to regen on loop
mBuffersToRegen.insert(id);
mBuffersToRestore.insert(id);
}
void ResourceTracker::setGennedBuffer(gl::BufferID id)
{
if (mStartingBuffers.find(id) == mStartingBuffers.end())
{
// This is a buffer genned after MEC was initialized, track it
mNewBuffers.insert(id);
return;
}
}
void ResourceTracker::setBufferModified(gl::BufferID id)
{
// If this was a starting buffer, we need to track it for restore
if (mStartingBuffers.find(id) != mStartingBuffers.end())
{
mBuffersToRestore.insert(id);
}
}
bool FrameCapture::isCapturing() const bool FrameCapture::isCapturing() const
{ {
// Currently we will always do a capture up until the last frame. In the future we could improve // Currently we will always do a capture up until the last frame. In the future we could improve
......
...@@ -177,6 +177,44 @@ class DataCounters final : angle::NonCopyable ...@@ -177,6 +177,44 @@ class DataCounters final : angle::NonCopyable
std::map<Counter, int> mData; std::map<Counter, int> mData;
}; };
using BufferSet = std::set<gl::BufferID>;
using BufferCalls = std::map<gl::BufferID, std::vector<CallCapture>>;
// Helper to track resource changes during the capture
class ResourceTracker final : angle::NonCopyable
{
public:
ResourceTracker();
~ResourceTracker();
BufferCalls &getBufferRegenCalls() { return mBufferRegenCalls; }
BufferCalls &getBufferRestoreCalls() { return mBufferRestoreCalls; }
BufferSet &getStartingBuffers() { return mStartingBuffers; }
BufferSet &getNewBuffers() { return mNewBuffers; }
BufferSet &getBuffersToRegen() { return mBuffersToRegen; }
BufferSet &getBuffersToRestore() { return mBuffersToRestore; }
void setGennedBuffer(gl::BufferID id);
void setDeletedBuffer(gl::BufferID id);
void setBufferModified(gl::BufferID id);
private:
// Buffer regen calls will delete and gen a buffer
BufferCalls mBufferRegenCalls;
// Buffer restore calls will restore the contents of a buffer
BufferCalls mBufferRestoreCalls;
// Starting buffers include all the buffers created during setup for MEC
BufferSet mStartingBuffers;
// New buffers are those generated while capturing
BufferSet mNewBuffers;
// Buffers to regen are a list of starting buffers that need to be deleted and genned
BufferSet mBuffersToRegen;
// Buffers to restore include any starting buffers with contents modified during the run
BufferSet mBuffersToRestore;
};
// Used by the CPP replay to filter out unnecessary code. // Used by the CPP replay to filter out unnecessary code.
using HasResourceTypeMap = angle::PackedEnumBitSet<ResourceIDType>; using HasResourceTypeMap = angle::PackedEnumBitSet<ResourceIDType>;
...@@ -225,7 +263,6 @@ class FrameCapture final : angle::NonCopyable ...@@ -225,7 +263,6 @@ class FrameCapture final : angle::NonCopyable
std::vector<CallCapture> mSetupCalls; std::vector<CallCapture> mSetupCalls;
std::vector<CallCapture> mFrameCalls; std::vector<CallCapture> mFrameCalls;
std::vector<CallCapture> mTearDownCalls;
// We save one large buffer of binary data for the whole CPP replay. // We save one large buffer of binary data for the whole CPP replay.
// This simplifies a lot of file management. // This simplifies a lot of file management.
...@@ -244,6 +281,8 @@ class FrameCapture final : angle::NonCopyable ...@@ -244,6 +281,8 @@ class FrameCapture final : angle::NonCopyable
HasResourceTypeMap mHasResourceType; HasResourceTypeMap mHasResourceType;
BufferDataMap mBufferDataMap; BufferDataMap mBufferDataMap;
ResourceTracker mResourceTracker;
// Cache most recently compiled and linked sources. // Cache most recently compiled and linked sources.
ShaderSourceMap mCachedShaderSources; ShaderSourceMap mCachedShaderSources;
ProgramSourceMap mCachedProgramSources; ProgramSourceMap mCachedProgramSources;
......
...@@ -18,6 +18,8 @@ namespace angle ...@@ -18,6 +18,8 @@ namespace angle
CallCapture::~CallCapture() {} CallCapture::~CallCapture() {}
ParamBuffer::~ParamBuffer() {} ParamBuffer::~ParamBuffer() {}
ParamCapture::~ParamCapture() {} ParamCapture::~ParamCapture() {}
ResourceTracker::ResourceTracker() {}
ResourceTracker::~ResourceTracker() {}
FrameCapture::FrameCapture() {} FrameCapture::FrameCapture() {}
FrameCapture::~FrameCapture() {} FrameCapture::~FrameCapture() {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment