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, ...@@ -491,8 +491,11 @@ void WriteCppReplayForCall(const CallCapture &call,
if (access & GL_MAP_WRITE_BIT) if (access & GL_MAP_WRITE_BIT)
{ {
// Track the returned pointer so we update its data // Track the returned pointer so we update its data when unmapped
callOut << "gMappedBufferData = "; gl::BufferID bufferID = call.params.getMappedBufferID();
callOut << "gMappedBufferData[";
WriteParamValueReplay<ParamType::TBufferID>(callOut, call, bufferID);
callOut << "] = ";
} }
} }
...@@ -862,7 +865,6 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -862,7 +865,6 @@ void WriteCppReplayIndexFiles(bool compression,
header << "// Global state\n"; header << "// Global state\n";
header << "\n"; header << "\n";
header << "extern uint8_t *gBinaryData;\n"; header << "extern uint8_t *gBinaryData;\n";
header << "extern void *gMappedBufferData;\n";
source << "#include \"" << FmtCapturePrefix(contextId, captureLabel) << ".h\"\n"; source << "#include \"" << FmtCapturePrefix(contextId, captureLabel) << ".h\"\n";
source << "\n"; source << "\n";
...@@ -909,7 +911,6 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -909,7 +911,6 @@ void WriteCppReplayIndexFiles(bool compression,
source << "\n"; source << "\n";
source << "uint8_t *gBinaryData = nullptr;\n"; source << "uint8_t *gBinaryData = nullptr;\n";
source << "void* gMappedBufferData = nullptr;\n";
if (readBufferSize > 0) if (readBufferSize > 0)
{ {
...@@ -1029,12 +1030,16 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -1029,12 +1030,16 @@ void WriteCppReplayIndexFiles(bool compression,
source << "}\n"; 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 << "\n";
source << "void UpdateClientBufferData(const void *source, GLsizei size)"; source << "void UpdateClientBufferData(GLuint bufferID, const void *source, GLsizei size)";
source << "\n"; source << "\n";
source << "{\n"; source << "{\n";
source << " memcpy(gMappedBufferData, source, size);\n"; source << " memcpy(gMappedBufferData[gBufferMap[bufferID]], source, size);\n";
source << "}\n"; source << "}\n";
for (ResourceIDType resourceType : AllEnums<ResourceIDType>()) for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
...@@ -2482,6 +2487,7 @@ ParamBuffer &ParamBuffer::operator=(ParamBuffer &&other) ...@@ -2482,6 +2487,7 @@ ParamBuffer &ParamBuffer::operator=(ParamBuffer &&other)
std::swap(mClientArrayDataParam, other.mClientArrayDataParam); std::swap(mClientArrayDataParam, other.mClientArrayDataParam);
std::swap(mReadBufferSize, other.mReadBufferSize); std::swap(mReadBufferSize, other.mReadBufferSize);
std::swap(mReturnValueCapture, other.mReturnValueCapture); std::swap(mReturnValueCapture, other.mReturnValueCapture);
std::swap(mMappedBufferID, other.mMappedBufferID);
return *this; return *this;
} }
...@@ -2862,7 +2868,7 @@ void FrameCapture::captureCompressedTextureData(const gl::Context *context, cons ...@@ -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) switch (call.entryPoint)
{ {
...@@ -2882,6 +2888,24 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call ...@@ -2882,6 +2888,24 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call
break; 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: case gl::EntryPoint::DrawArrays:
{ {
if (context->getStateCache().hasAnyActiveClientAttrib()) if (context->getStateCache().hasAnyActiveClientAttrib())
...@@ -3028,7 +3052,11 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call ...@@ -3028,7 +3052,11 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call
GLsizeiptr length = GLsizeiptr length =
call.params.getParam("length", ParamType::TGLsizeiptr, 2).value.GLsizeiptrVal; 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; break;
} }
...@@ -3039,6 +3067,7 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call ...@@ -3039,6 +3067,7 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, const Call
captureMappedBufferSnapshot(context, call); captureMappedBufferSnapshot(context, call);
break; break;
} }
default: default:
break; break;
} }
...@@ -3151,9 +3180,10 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const ...@@ -3151,9 +3180,10 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const
// into what the client did to the buffer while mapped // into what the client did to the buffer while mapped
// This sequence will result in replay calls like this: // 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); // glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
// ... // ...
...@@ -3161,7 +3191,8 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const ...@@ -3161,7 +3191,8 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const
gl::BufferBinding target = gl::BufferBinding target =
call.params.getParam("targetPacked", ParamType::TBufferBinding, 0).value.BufferBindingVal; 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()) if (bufferDataInfo == mBufferDataMap.end())
{ {
// This buffer was not marked writable, so we did not back it up // 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 ...@@ -3172,14 +3203,20 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const
GLsizeiptr length = bufferDataInfo->second.second; GLsizeiptr length = bufferDataInfo->second.second;
// Map the buffer so we can copy its contents out // Map the buffer so we can copy its contents out
gl::Buffer *buffer = context->getState().getTargetBuffer(target);
ASSERT(!buffer->isMapped()); 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()); const uint8_t *data = reinterpret_cast<const uint8_t *>(buffer->getMapPointer());
// Create the parameters to our helper for use during replay // Create the parameters to our helper for use during replay
ParamBuffer dataParamBuffer; 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 // Capture the current buffer data with a binary param
ParamCapture captureData("source", ParamType::TvoidConstPointer); ParamCapture captureData("source", ParamType::TvoidConstPointer);
CaptureMemory(data, length, &captureData); CaptureMemory(data, length, &captureData);
......
...@@ -78,11 +78,18 @@ class ParamBuffer final : angle::NonCopyable ...@@ -78,11 +78,18 @@ class ParamBuffer final : angle::NonCopyable
const std::vector<ParamCapture> &getParamCaptures() const { return mParamCaptures; } 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: private:
std::vector<ParamCapture> mParamCaptures; std::vector<ParamCapture> mParamCaptures;
ParamCapture mReturnValueCapture; ParamCapture mReturnValueCapture;
int mClientArrayDataParam = -1; int mClientArrayDataParam = -1;
size_t mReadBufferSize = 0; size_t mReadBufferSize = 0;
gl::BufferID mMappedBufferID;
}; };
struct CallCapture struct CallCapture
...@@ -173,8 +180,8 @@ class DataCounters final : angle::NonCopyable ...@@ -173,8 +180,8 @@ class DataCounters final : angle::NonCopyable
// 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>;
// Map of buffing bindings to offset and size used when mapped // Map of buffer ID to offset and size used when mapped
using BufferDataMap = std::map<gl::BufferBinding, std::pair<GLintptr, GLsizeiptr>>; using BufferDataMap = std::map<gl::BufferID, std::pair<GLintptr, GLsizeiptr>>;
// A dictionary of sources indexed by shader type. // A dictionary of sources indexed by shader type.
using ProgramSources = gl::ShaderMap<std::string>; using ProgramSources = gl::ShaderMap<std::string>;
...@@ -209,7 +216,7 @@ class FrameCapture final : angle::NonCopyable ...@@ -209,7 +216,7 @@ class FrameCapture final : angle::NonCopyable
void captureCompressedTextureData(const gl::Context *context, const CallCapture &call); void captureCompressedTextureData(const gl::Context *context, const CallCapture &call);
void reset(); 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); void maybeCapturePostCallUpdates(const gl::Context *context);
static void ReplayCall(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