Commit 84323449 by Cody Northrop Committed by Commit Bot

Capture/Replay: Track buffer contents by ID

This CL updates how we restore buffer data after the client has unmapped it. We do this because we have no visibility into whether the buffer has been changed while mapped. Tracking a buffer map/unmap pairing by target as we have been is insufficient as apps can bind multiple buffers in succession before rebinding and unmapping selectively. To avoid this, we change our buffer data resource tracking to use the buffer ID instead of target. Also, since the app can map multiple buffers, we need to track the active buffer during MapBufferRange so we can use it during replay to restore from the appropriate handle. This is a deferred operation, so we store it as a new member of the ParamBuffer to preserve the information. Test: Temple Run capture and replay Bug: b:152512564 Bug: angleproject:3662 Change-Id: I1d3f594b496e5675e814b82acb4a238f845e26d6 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2128328 Commit-Queue: Cody Northrop <cnorthrop@google.com> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com>
parent d2de511e
......@@ -491,8 +491,11 @@ void WriteCppReplayForCall(const CallCapture &call,
if (access & GL_MAP_WRITE_BIT)
{
// Track the returned pointer so we update its data
callOut << "gMappedBufferData = ";
// Track the returned pointer so we update its data when unmapped
gl::BufferID bufferID = call.params.getMappedBufferID();
callOut << "gMappedBufferData[";
WriteParamValueReplay<ParamType::TBufferID>(callOut, call, bufferID);
callOut << "] = ";
}
}
......@@ -862,7 +865,6 @@ void WriteCppReplayIndexFiles(bool compression,
header << "// Global state\n";
header << "\n";
header << "extern uint8_t *gBinaryData;\n";
header << "extern void *gMappedBufferData;\n";
source << "#include \"" << FmtCapturePrefix(contextId, captureLabel) << ".h\"\n";
source << "\n";
......@@ -909,7 +911,6 @@ void WriteCppReplayIndexFiles(bool compression,
source << "\n";
source << "uint8_t *gBinaryData = nullptr;\n";
source << "void* gMappedBufferData = nullptr;\n";
if (readBufferSize > 0)
{
......@@ -1029,12 +1030,16 @@ void WriteCppReplayIndexFiles(bool compression,
source << "}\n";
}
header << "void UpdateClientBufferData(const void *source, GLsizei size);\n";
// Data types and functions for tracking contents of mapped buffers
header << "using BufferHandleMap = std::unordered_map<GLuint, void*>;\n";
header << "extern BufferHandleMap gMappedBufferData;\n";
header << "void UpdateClientBufferData(GLuint bufferID, const void *source, GLsizei size);\n";
source << "BufferHandleMap gMappedBufferData;\n";
source << "\n";
source << "void UpdateClientBufferData(const void *source, GLsizei size)";
source << "void UpdateClientBufferData(GLuint bufferID, const void *source, GLsizei size)";
source << "\n";
source << "{\n";
source << " memcpy(gMappedBufferData, source, size);\n";
source << " memcpy(gMappedBufferData[gBufferMap[bufferID]], source, size);\n";
source << "}\n";
for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
......@@ -2482,6 +2487,7 @@ ParamBuffer &ParamBuffer::operator=(ParamBuffer &&other)
std::swap(mClientArrayDataParam, other.mClientArrayDataParam);
std::swap(mReadBufferSize, other.mReadBufferSize);
std::swap(mReturnValueCapture, other.mReturnValueCapture);
std::swap(mMappedBufferID, other.mMappedBufferID);
return *this;
}
......@@ -2862,7 +2868,7 @@ void FrameCapture::captureCompressedTextureData(const gl::Context *context, cons
}
}
void FrameCapture::maybeCaptureClientData(const gl::Context *context, const CallCapture &call)
void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCapture &call)
{
switch (call.entryPoint)
{
......@@ -2882,6 +2888,24 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call
break;
}
case gl::EntryPoint::DeleteBuffers:
{
GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
const gl::BufferID *bufferIDs =
call.params.getParam("buffersPacked", ParamType::TBufferIDConstPointer, 1)
.value.BufferIDConstPointerVal;
for (GLsizei i = 0; i < count; i++)
{
// For each buffer being deleted, check our backup of data and remove it
const auto &bufferDataInfo = mBufferDataMap.find(bufferIDs[i]);
if (bufferDataInfo != mBufferDataMap.end())
{
mBufferDataMap.erase(bufferDataInfo);
}
}
break;
}
case gl::EntryPoint::DrawArrays:
{
if (context->getStateCache().hasAnyActiveClientAttrib())
......@@ -3028,7 +3052,11 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call
GLsizeiptr length =
call.params.getParam("length", ParamType::TGLsizeiptr, 2).value.GLsizeiptrVal;
mBufferDataMap[target] = std::make_pair(offset, length);
gl::Buffer *buffer = context->getState().getTargetBuffer(target);
mBufferDataMap[buffer->id()] = std::make_pair(offset, length);
// Track the bufferID that was just mapped
call.params.setMappedBufferID(buffer->id());
}
break;
}
......@@ -3039,6 +3067,7 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call
captureMappedBufferSnapshot(context, call);
break;
}
default:
break;
}
......@@ -3151,9 +3180,10 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const
// into what the client did to the buffer while mapped
// This sequence will result in replay calls like this:
// ...
// gMappedBufferData = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 65536, GL_MAP_WRITE_BIT);
// gMappedBufferData[gBufferMap[42]] = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 65536,
// GL_MAP_WRITE_BIT);
// ...
// UpdateClientBufferData(&gBinaryData[164631024], 65536);
// UpdateClientBufferData(42, &gBinaryData[164631024], 65536);
// glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
// ...
......@@ -3161,7 +3191,8 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const
gl::BufferBinding target =
call.params.getParam("targetPacked", ParamType::TBufferBinding, 0).value.BufferBindingVal;
const auto &bufferDataInfo = mBufferDataMap.find(target);
gl::Buffer *buffer = context->getState().getTargetBuffer(target);
const auto &bufferDataInfo = mBufferDataMap.find(buffer->id());
if (bufferDataInfo == mBufferDataMap.end())
{
// This buffer was not marked writable, so we did not back it up
......@@ -3172,14 +3203,20 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const
GLsizeiptr length = bufferDataInfo->second.second;
// Map the buffer so we can copy its contents out
gl::Buffer *buffer = context->getState().getTargetBuffer(target);
ASSERT(!buffer->isMapped());
(void)buffer->mapRange(context, offset, length, GL_MAP_READ_BIT);
angle::Result result = buffer->mapRange(context, offset, length, GL_MAP_READ_BIT);
if (result != angle::Result::Continue)
{
ERR() << "Failed to mapRange of buffer" << std::endl;
}
const uint8_t *data = reinterpret_cast<const uint8_t *>(buffer->getMapPointer());
// Create the parameters to our helper for use during replay
ParamBuffer dataParamBuffer;
// Pass in the target buffer ID
dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
// Capture the current buffer data with a binary param
ParamCapture captureData("source", ParamType::TvoidConstPointer);
CaptureMemory(data, length, &captureData);
......
......@@ -78,11 +78,18 @@ class ParamBuffer final : angle::NonCopyable
const std::vector<ParamCapture> &getParamCaptures() const { return mParamCaptures; }
// These helpers allow us to track the ID of the buffer that was active when
// MapBufferRange was called. We'll use it during replay to track the
// buffer's contents, as they can be modified by the host.
void setMappedBufferID(gl::BufferID bufferID) { mMappedBufferID = bufferID; }
gl::BufferID getMappedBufferID() const { return mMappedBufferID; }
private:
std::vector<ParamCapture> mParamCaptures;
ParamCapture mReturnValueCapture;
int mClientArrayDataParam = -1;
size_t mReadBufferSize = 0;
gl::BufferID mMappedBufferID;
};
struct CallCapture
......@@ -173,8 +180,8 @@ class DataCounters final : angle::NonCopyable
// Used by the CPP replay to filter out unnecessary code.
using HasResourceTypeMap = angle::PackedEnumBitSet<ResourceIDType>;
// Map of buffing bindings to offset and size used when mapped
using BufferDataMap = std::map<gl::BufferBinding, std::pair<GLintptr, GLsizeiptr>>;
// Map of buffer ID to offset and size used when mapped
using BufferDataMap = std::map<gl::BufferID, std::pair<GLintptr, GLsizeiptr>>;
// A dictionary of sources indexed by shader type.
using ProgramSources = gl::ShaderMap<std::string>;
......@@ -209,7 +216,7 @@ class FrameCapture final : angle::NonCopyable
void captureCompressedTextureData(const gl::Context *context, const CallCapture &call);
void reset();
void maybeCaptureClientData(const gl::Context *context, const CallCapture &call);
void maybeCaptureClientData(const gl::Context *context, CallCapture &call);
void maybeCapturePostCallUpdates(const gl::Context *context);
static void ReplayCall(gl::Context *context,
......
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