Commit 22c95964 by Cody Northrop Committed by Commit Bot

Capture/Replay: Support buffer map/unmap state during MEC

Angry Birds 2 is the first app we've targeted that maps buffers across frame boundaries. This exposed some gaps and assumptions we had in the code, and required additional support for MEC. To support this, we track each buffer's starting map/unmap state and how it changes throughout the trace. Then during Reset, we emit calls to return them to the correct state: void ResetContext3Replay() { ... glBindBuffer(GL_ARRAY_BUFFER, gBufferMap[546]); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, gBufferMap[550]); gMappedBufferData[gBufferMap[550]] = glMapBufferRange(GL_ARRAY_BUFFER, 0, 8192, GL_MAP_WRITE_BIT); ... } Test: MEC of Angry Birds 2 Bug: angleproject:4599 Bug: b/157672184 Change-Id: I5c73ca4d4eba7f1ecea01467ae887bae7f2d27fd Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2231803 Commit-Queue: Cody Northrop <cnorthrop@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 5abc0c56
...@@ -710,6 +710,8 @@ void MaybeResetResources(std::stringstream &out, ...@@ -710,6 +710,8 @@ void MaybeResetResources(std::stringstream &out,
BufferSet &newBuffers = resourceTracker->getNewBuffers(); BufferSet &newBuffers = resourceTracker->getNewBuffers();
BufferCalls &bufferRegenCalls = resourceTracker->getBufferRegenCalls(); BufferCalls &bufferRegenCalls = resourceTracker->getBufferRegenCalls();
BufferCalls &bufferRestoreCalls = resourceTracker->getBufferRestoreCalls(); BufferCalls &bufferRestoreCalls = resourceTracker->getBufferRestoreCalls();
BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
// If we have any new buffers generated and not deleted during the run, delete them now // If we have any new buffers generated and not deleted during the run, delete them now
if (!newBuffers.empty()) if (!newBuffers.empty())
...@@ -755,8 +757,42 @@ void MaybeResetResources(std::stringstream &out, ...@@ -755,8 +757,42 @@ void MaybeResetResources(std::stringstream &out,
out << " "; out << " ";
WriteCppReplayForCall(call, counters, out, header, binaryData); WriteCppReplayForCall(call, counters, out, header, binaryData);
out << ";\n"; out << ";\n";
// Also note that this buffer has been implicitly unmapped by this call
resourceTracker->setBufferUnmapped(id);
} }
} }
// Update the map/unmap of buffers to match the starting state
BufferSet startingBuffers = resourceTracker->getStartingBuffers();
for (const gl::BufferID id : startingBuffers)
{
// If the buffer was mapped at the start, but is not mapped now, we need to map
if (resourceTracker->getStartingBuffersMappedInitial(id) &&
!resourceTracker->getStartingBuffersMappedCurrent(id))
{
// Emit their map calls
for (CallCapture &call : bufferMapCalls[id])
{
out << " ";
WriteCppReplayForCall(call, counters, out, header, binaryData);
out << ";\n";
}
}
// If the buffer was unmapped at the start, but is mapped now, we need to unmap
if (!resourceTracker->getStartingBuffersMappedInitial(id) &&
resourceTracker->getStartingBuffersMappedCurrent(id))
{
// Emit their unmap calls
for (CallCapture &call : bufferUnmapCalls[id])
{
out << " ";
WriteCppReplayForCall(call, counters, out, header, binaryData);
out << ";\n";
}
}
}
break; break;
} }
default: default:
...@@ -1840,6 +1876,32 @@ void CaptureBufferResetCalls(const gl::State &replayState, ...@@ -1840,6 +1876,32 @@ void CaptureBufferResetCalls(const gl::State &replayState,
CaptureBufferData(replayState, true, gl::BufferBinding::Array, CaptureBufferData(replayState, true, gl::BufferBinding::Array,
static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(), static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(),
buffer->getUsage())); buffer->getUsage()));
if (buffer->isMapped())
{
// Track calls to remap a buffer that started as mapped
BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
Capture(&bufferMapCalls[*id],
CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
void *dontCare = nullptr;
Capture(&bufferMapCalls[*id],
CaptureMapBufferRange(replayState, true, gl::BufferBinding::Array,
static_cast<GLsizeiptr>(buffer->getMapOffset()),
static_cast<GLsizeiptr>(buffer->getMapLength()),
buffer->getAccessFlags(), dontCare));
// Track the bufferID that was just mapped
bufferMapCalls[*id].back().params.setMappedBufferID(buffer->id());
}
// Track calls unmap a buffer that started as unmapped
BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
Capture(&bufferUnmapCalls[*id],
CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
Capture(&bufferUnmapCalls[*id],
CaptureUnmapBuffer(replayState, true, gl::BufferBinding::Array, GL_TRUE));
} }
void CaptureMidExecutionSetup(const gl::Context *context, void CaptureMidExecutionSetup(const gl::Context *context,
...@@ -1847,7 +1909,8 @@ void CaptureMidExecutionSetup(const gl::Context *context, ...@@ -1847,7 +1909,8 @@ void CaptureMidExecutionSetup(const gl::Context *context,
ResourceTracker *resourceTracker, ResourceTracker *resourceTracker,
const ShaderSourceMap &cachedShaderSources, const ShaderSourceMap &cachedShaderSources,
const ProgramSourceMap &cachedProgramSources, const ProgramSourceMap &cachedProgramSources,
const TextureLevelDataMap &cachedTextureLevelData) const TextureLevelDataMap &cachedTextureLevelData,
FrameCapture *frameCapture)
{ {
const gl::State &apiState = context->getState(); const gl::State &apiState = context->getState();
gl::State replayState(nullptr, nullptr, nullptr, nullptr, EGL_OPENGL_ES_API, gl::State replayState(nullptr, nullptr, nullptr, nullptr, EGL_OPENGL_ES_API,
...@@ -1880,9 +1943,16 @@ void CaptureMidExecutionSetup(const gl::Context *context, ...@@ -1880,9 +1943,16 @@ void CaptureMidExecutionSetup(const gl::Context *context,
{ {
continue; continue;
} }
ASSERT(!buffer->isMapped());
(void)buffer->mapRange(context, 0, static_cast<GLsizeiptr>(buffer->getSize()), // Remember if the buffer was already mapped
GL_MAP_READ_BIT); GLboolean bufferMapped = buffer->isMapped();
// If needed, map the buffer so we can capture its contents
if (!bufferMapped)
{
(void)buffer->mapRange(context, 0, static_cast<GLsizeiptr>(buffer->getSize()),
GL_MAP_READ_BIT);
}
// Generate binding. // Generate binding.
cap(CaptureGenBuffers(replayState, true, 1, &id)); cap(CaptureGenBuffers(replayState, true, 1, &id));
...@@ -1899,11 +1969,35 @@ void CaptureMidExecutionSetup(const gl::Context *context, ...@@ -1899,11 +1969,35 @@ void CaptureMidExecutionSetup(const gl::Context *context,
static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(), static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(),
buffer->getUsage())); buffer->getUsage()));
if (bufferMapped)
{
void *dontCare = nullptr;
Capture(setupCalls,
CaptureMapBufferRange(replayState, true, gl::BufferBinding::Array,
static_cast<GLsizeiptr>(buffer->getMapOffset()),
static_cast<GLsizeiptr>(buffer->getMapLength()),
buffer->getAccessFlags(), dontCare));
frameCapture->getResouceTracker().setStartingBufferMapped(buffer->id(), true);
frameCapture->trackBufferMapping(
&setupCalls->back(), buffer->id(), static_cast<GLsizeiptr>(buffer->getMapOffset()),
static_cast<GLsizeiptr>(buffer->getMapLength()), buffer->getAccessFlags());
}
else
{
frameCapture->getResouceTracker().setStartingBufferMapped(buffer->id(), false);
}
// Generate the calls needed to restore this buffer to original state for frame looping // Generate the calls needed to restore this buffer to original state for frame looping
CaptureBufferResetCalls(replayState, resourceTracker, &id, buffer); CaptureBufferResetCalls(replayState, resourceTracker, &id, buffer);
GLboolean dontCare; // Unmap the buffer if it wasn't already mapped
(void)buffer->unmap(context, &dontCare); if (!bufferMapped)
{
GLboolean dontCare;
(void)buffer->unmap(context, &dontCare);
}
} }
// Vertex input states. Only handles GLES 2.0 states right now. // Vertex input states. Only handles GLES 2.0 states right now.
...@@ -3296,6 +3390,30 @@ void FrameCapture::captureCompressedTextureData(const gl::Context *context, cons ...@@ -3296,6 +3390,30 @@ void FrameCapture::captureCompressedTextureData(const gl::Context *context, cons
} }
} }
void FrameCapture::trackBufferMapping(CallCapture *call,
gl::BufferID id,
GLintptr offset,
GLsizeiptr length,
GLbitfield accessFlags)
{
// Track that the buffer was mapped
mResourceTracker.setBufferMapped(id);
if (accessFlags & GL_MAP_WRITE_BIT)
{
// If this buffer was mapped writable, we don't have any visibility into what
// happens to it. Therefore, remember the details about it, and we'll read it back
// on Unmap to repopulate it during replay.
mBufferDataMap[id] = std::make_pair(offset, length);
// Track that this buffer was potentially modified
mResourceTracker.setBufferModified(id);
// Track the bufferID that was just mapped for use when writing return value
call->params.setMappedBufferID(id);
}
}
void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCapture &call) void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCapture &call)
{ {
switch (call.entryPoint) switch (call.entryPoint)
...@@ -3491,33 +3609,19 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur ...@@ -3491,33 +3609,19 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur
case gl::EntryPoint::MapBufferRange: case gl::EntryPoint::MapBufferRange:
case gl::EntryPoint::MapBufferRangeEXT: case gl::EntryPoint::MapBufferRangeEXT:
{ {
// Use the access bits to see if contents may be modified GLintptr offset =
call.params.getParam("offset", ParamType::TGLintptr, 1).value.GLintptrVal;
GLsizeiptr length =
call.params.getParam("length", ParamType::TGLsizeiptr, 2).value.GLsizeiptrVal;
GLbitfield access = GLbitfield access =
call.params.getParam("access", ParamType::TGLbitfield, 3).value.GLbitfieldVal; call.params.getParam("access", ParamType::TGLbitfield, 3).value.GLbitfieldVal;
if (access & GL_MAP_WRITE_BIT) gl::BufferBinding target =
{ call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
// If this buffer was mapped writable, we don't have any visibility into what .value.BufferBindingVal;
// happens to it. Therefore, remember the details about it, and we'll read it back gl::Buffer *buffer = context->getState().getTargetBuffer(target);
// on Unmap to repopulate it during replay.
trackBufferMapping(&call, buffer->id(), offset, length, access);
gl::BufferBinding target =
call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
.value.BufferBindingVal;
GLintptr offset =
call.params.getParam("offset", ParamType::TGLintptr, 1).value.GLintptrVal;
GLsizeiptr length =
call.params.getParam("length", ParamType::TGLsizeiptr, 2).value.GLsizeiptrVal;
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());
// Remember that it was mapped writable, for use during state reset
mResourceTracker.setBufferModified(buffer->id());
}
break; break;
} }
...@@ -3526,6 +3630,13 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur ...@@ -3526,6 +3630,13 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur
{ {
// See if we need to capture the buffer contents // See if we need to capture the buffer contents
captureMappedBufferSnapshot(context, call); captureMappedBufferSnapshot(context, call);
// Track that the buffer was unmapped, for use during state reset
gl::BufferBinding target =
call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
.value.BufferBindingVal;
gl::Buffer *buffer = context->getState().getTargetBuffer(target);
mResourceTracker.setBufferUnmapped(buffer->id());
break; break;
} }
...@@ -3541,6 +3652,15 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur ...@@ -3541,6 +3652,15 @@ void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCaptur
// Track that this buffer's contents have been modified // Track that this buffer's contents have been modified
mResourceTracker.setBufferModified(buffer->id()); mResourceTracker.setBufferModified(buffer->id());
// BufferData is equivalent to UnmapBuffer, for what we're tracking.
// From the ES 3.1 spec in BufferData section:
// If any portion of the buffer object is mapped in the current context or any
// context current to another thread, it is as though UnmapBuffer (see section
// 6.3.1) is executed in each such context prior to deleting the existing data
// store.
// Track that the buffer was unmapped, for use during state reset
mResourceTracker.setBufferUnmapped(buffer->id());
break; break;
} }
default: default:
...@@ -3753,7 +3873,7 @@ void FrameCapture::onEndFrame(const gl::Context *context) ...@@ -3753,7 +3873,7 @@ void FrameCapture::onEndFrame(const gl::Context *context)
{ {
mSetupCalls.clear(); mSetupCalls.clear();
CaptureMidExecutionSetup(context, &mSetupCalls, &mResourceTracker, mCachedShaderSources, CaptureMidExecutionSetup(context, &mSetupCalls, &mResourceTracker, mCachedShaderSources,
mCachedProgramSources, mCachedTextureLevelData); mCachedProgramSources, mCachedTextureLevelData, this);
} }
} }
...@@ -3809,6 +3929,26 @@ void ResourceTracker::setBufferModified(gl::BufferID id) ...@@ -3809,6 +3929,26 @@ void ResourceTracker::setBufferModified(gl::BufferID id)
} }
} }
void ResourceTracker::setBufferMapped(gl::BufferID id)
{
// If this was a starting buffer, we may need to restore it to original state during Reset
if (mStartingBuffers.find(id) != mStartingBuffers.end())
{
// Track that its current state is mapped (true)
mStartingBuffersMappedCurrent[id] = true;
}
}
void ResourceTracker::setBufferUnmapped(gl::BufferID id)
{
// If this was a starting buffer, we may need to restore it to original state during Reset
if (mStartingBuffers.find(id) != mStartingBuffers.end())
{
// Track that its current state is unmapped (false)
mStartingBuffersMappedCurrent[id] = false;
}
}
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
......
...@@ -180,6 +180,9 @@ class DataCounters final : angle::NonCopyable ...@@ -180,6 +180,9 @@ class DataCounters final : angle::NonCopyable
using BufferSet = std::set<gl::BufferID>; using BufferSet = std::set<gl::BufferID>;
using BufferCalls = std::map<gl::BufferID, std::vector<CallCapture>>; using BufferCalls = std::map<gl::BufferID, std::vector<CallCapture>>;
// true means mapped, false means unmapped
using BufferMapStatusMap = std::map<gl::BufferID, bool>;
// Helper to track resource changes during the capture // Helper to track resource changes during the capture
class ResourceTracker final : angle::NonCopyable class ResourceTracker final : angle::NonCopyable
{ {
...@@ -189,6 +192,8 @@ class ResourceTracker final : angle::NonCopyable ...@@ -189,6 +192,8 @@ class ResourceTracker final : angle::NonCopyable
BufferCalls &getBufferRegenCalls() { return mBufferRegenCalls; } BufferCalls &getBufferRegenCalls() { return mBufferRegenCalls; }
BufferCalls &getBufferRestoreCalls() { return mBufferRestoreCalls; } BufferCalls &getBufferRestoreCalls() { return mBufferRestoreCalls; }
BufferCalls &getBufferMapCalls() { return mBufferMapCalls; }
BufferCalls &getBufferUnmapCalls() { return mBufferUnmapCalls; }
BufferSet &getStartingBuffers() { return mStartingBuffers; } BufferSet &getStartingBuffers() { return mStartingBuffers; }
BufferSet &getNewBuffers() { return mNewBuffers; } BufferSet &getNewBuffers() { return mNewBuffers; }
...@@ -198,12 +203,35 @@ class ResourceTracker final : angle::NonCopyable ...@@ -198,12 +203,35 @@ class ResourceTracker final : angle::NonCopyable
void setGennedBuffer(gl::BufferID id); void setGennedBuffer(gl::BufferID id);
void setDeletedBuffer(gl::BufferID id); void setDeletedBuffer(gl::BufferID id);
void setBufferModified(gl::BufferID id); void setBufferModified(gl::BufferID id);
void setBufferMapped(gl::BufferID id);
void setBufferUnmapped(gl::BufferID id);
const bool &getStartingBuffersMappedCurrent(gl::BufferID id)
{
return mStartingBuffersMappedCurrent[id];
}
const bool &getStartingBuffersMappedInitial(gl::BufferID id)
{
return mStartingBuffersMappedInitial[id];
}
void setStartingBufferMapped(gl::BufferID id, bool mapped)
{
// Track the current state (which will change throughout the trace)
mStartingBuffersMappedCurrent[id] = mapped;
// And the initial state, to compare during frame loop reset
mStartingBuffersMappedInitial[id] = mapped;
}
private: private:
// Buffer regen calls will delete and gen a buffer // Buffer regen calls will delete and gen a buffer
BufferCalls mBufferRegenCalls; BufferCalls mBufferRegenCalls;
// Buffer restore calls will restore the contents of a buffer // Buffer restore calls will restore the contents of a buffer
BufferCalls mBufferRestoreCalls; BufferCalls mBufferRestoreCalls;
// Buffer map calls will map a buffer with correct offset, length, and access flags
BufferCalls mBufferMapCalls;
// Buffer unmap calls will bind and unmap a given buffer
BufferCalls mBufferUnmapCalls;
// Starting buffers include all the buffers created during setup for MEC // Starting buffers include all the buffers created during setup for MEC
BufferSet mStartingBuffers; BufferSet mStartingBuffers;
...@@ -213,6 +241,11 @@ class ResourceTracker final : angle::NonCopyable ...@@ -213,6 +241,11 @@ class ResourceTracker final : angle::NonCopyable
BufferSet mBuffersToRegen; BufferSet mBuffersToRegen;
// Buffers to restore include any starting buffers with contents modified during the run // Buffers to restore include any starting buffers with contents modified during the run
BufferSet mBuffersToRestore; BufferSet mBuffersToRestore;
// Whether a given buffer was mapped at the start of the trace
BufferMapStatusMap mStartingBuffersMappedInitial;
// The status of buffer mapping throughout the trace, modified with each Map/Unmap call
BufferMapStatusMap mStartingBuffersMappedCurrent;
}; };
// Used by the CPP replay to filter out unnecessary code. // Used by the CPP replay to filter out unnecessary code.
...@@ -245,6 +278,14 @@ class FrameCapture final : angle::NonCopyable ...@@ -245,6 +278,14 @@ class FrameCapture final : angle::NonCopyable
bool isCapturing() const; bool isCapturing() const;
void replay(gl::Context *context); void replay(gl::Context *context);
void trackBufferMapping(CallCapture *call,
gl::BufferID id,
GLintptr offset,
GLsizeiptr length,
GLbitfield accessFlags);
ResourceTracker &getResouceTracker() { return mResourceTracker; }
private: private:
void captureClientArraySnapshot(const gl::Context *context, void captureClientArraySnapshot(const gl::Context *context,
size_t vertexCount, size_t vertexCount,
......
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