Commit 54367a3e by Tim Van Patten Committed by Commit Bot

Capture/Replay: Support glCopyImageSubData with compressed formats

Special Forces Group 2 uses glCopyImageSubData() to copy compressed texture data, which requires special handling for ANGLE's frame capture since compressed texture data can't be read back. To support this, when glCopyImageSubData() is called with textures using compressed formats, a new mCachedTextureLevelData entry will be created for the destination texture which is a copy of the source texture data. Note that this implementation only supports copying the full level data. http://anglebug.com/5604 will track adding support for partial level copies if an app requires that in the future. Bug: angleproject:5592 Bug: angleproject:5604 Change-Id: Ibce13f91f838fd691269c796fbeb5b9c6133c6aa Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2669955Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Commit-Queue: Tim Van Patten <timvp@google.com>
parent 2dfb3fcd
...@@ -2637,7 +2637,9 @@ void CaptureMidExecutionSetup(const gl::Context *context, ...@@ -2637,7 +2637,9 @@ void CaptureMidExecutionSetup(const gl::Context *context,
const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(index); const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(index);
if (desc.size.empty()) if (desc.size.empty())
{
continue; continue;
}
const gl::InternalFormat &format = *desc.format.info; const gl::InternalFormat &format = *desc.format.info;
...@@ -3758,6 +3760,35 @@ FrameCapture::FrameCapture() ...@@ -3758,6 +3760,35 @@ FrameCapture::FrameCapture()
FrameCapture::~FrameCapture() = default; FrameCapture::~FrameCapture() = default;
void FrameCapture::copyCompressedTextureData(const gl::Context *context, const CallCapture &call)
{
// For compressed textures, we need to copy the source data that was already captured into a new
// cached texture entry for use during mid-execution capture, rather than reading it back with
// ANGLE_get_image.
GLuint srcName = call.params.getParam("srcName", ParamType::TGLuint, 0).value.GLuintVal;
GLint srcLevel = call.params.getParam("srcLevel", ParamType::TGLint, 2).value.GLintVal;
GLuint dstName = call.params.getParam("dstName", ParamType::TGLuint, 6).value.GLuintVal;
GLint dstLevel = call.params.getParam("dstLevel", ParamType::TGLint, 8).value.GLintVal;
// Look up the texture type
GLenum dstTarget = call.params.getParam("dstTarget", ParamType::TGLenum, 7).value.GLenumVal;
gl::TextureTarget dstTargetPacked = gl::PackParam<gl::TextureTarget>(dstTarget);
gl::TextureType dstTextureType = gl::TextureTargetToType(dstTargetPacked);
// Look up the currently bound texture
gl::Texture *dstTexture = context->getState().getTargetTexture(dstTextureType);
ASSERT(dstTexture);
const gl::InternalFormat &dstFormat = *dstTexture->getFormat(dstTargetPacked, dstLevel).info;
if (dstFormat.compressed)
{
context->getShareGroup()->getFrameCaptureShared()->copyCachedTextureLevel(
context, {srcName}, srcLevel, {dstName}, dstLevel, call);
}
}
void FrameCapture::captureCompressedTextureData(const gl::Context *context, const CallCapture &call) void FrameCapture::captureCompressedTextureData(const gl::Context *context, const CallCapture &call)
{ {
// For compressed textures, track a shadow copy of the data // For compressed textures, track a shadow copy of the data
...@@ -4182,6 +4213,15 @@ void FrameCapture::maybeCapturePreCallUpdates(const gl::Context *context, CallCa ...@@ -4182,6 +4213,15 @@ void FrameCapture::maybeCapturePreCallUpdates(const gl::Context *context, CallCa
break; break;
} }
case EntryPoint::GLCopyImageSubData:
case EntryPoint::GLCopyImageSubDataEXT:
case EntryPoint::GLCopyImageSubDataOES:
{
// glCopyImageSubData supports copying compressed and uncompressed texture formats.
copyCompressedTextureData(context, call);
break;
}
case EntryPoint::GLDeleteTextures: case EntryPoint::GLDeleteTextures:
{ {
// Free any TextureLevelDataMap entries being tracked for this texture // Free any TextureLevelDataMap entries being tracked for this texture
...@@ -4307,7 +4347,9 @@ void FrameCapture::maybeCapturePreCallUpdates(const gl::Context *context, CallCa ...@@ -4307,7 +4347,9 @@ void FrameCapture::maybeCapturePreCallUpdates(const gl::Context *context, CallCa
void FrameCapture::captureCall(const gl::Context *context, CallCapture &&call) void FrameCapture::captureCall(const gl::Context *context, CallCapture &&call)
{ {
if (SkipCall(call.entryPoint)) if (SkipCall(call.entryPoint))
{
return; return;
}
maybeOverrideEntryPoint(context, call); maybeOverrideEntryPoint(context, call);
...@@ -4794,6 +4836,62 @@ const std::vector<uint8_t> &FrameCaptureShared::retrieveCachedTextureLevel(gl::T ...@@ -4794,6 +4836,62 @@ const std::vector<uint8_t> &FrameCaptureShared::retrieveCachedTextureLevel(gl::T
return capturedTextureLevel; return capturedTextureLevel;
} }
void FrameCaptureShared::copyCachedTextureLevel(const gl::Context *context,
gl::TextureID srcID,
GLint srcLevel,
gl::TextureID dstID,
GLint dstLevel,
const CallCapture &call)
{
// TODO(http://anglebug.com/5604): Add support for partial level copies.
ASSERT(call.params.getParam("srcX", ParamType::TGLint, 3).value.GLintVal == 0);
ASSERT(call.params.getParam("srcY", ParamType::TGLint, 4).value.GLintVal == 0);
ASSERT(call.params.getParam("srcZ", ParamType::TGLint, 5).value.GLintVal == 0);
ASSERT(call.params.getParam("dstX", ParamType::TGLint, 9).value.GLintVal == 0);
ASSERT(call.params.getParam("dstY", ParamType::TGLint, 10).value.GLintVal == 0);
ASSERT(call.params.getParam("dstZ", ParamType::TGLint, 11).value.GLintVal == 0);
GLenum srcTarget = call.params.getParam("srcTarget", ParamType::TGLenum, 1).value.GLenumVal;
GLsizei srcWidth = call.params.getParam("srcWidth", ParamType::TGLsizei, 12).value.GLsizeiVal;
GLsizei srcHeight = call.params.getParam("srcHeight", ParamType::TGLsizei, 13).value.GLsizeiVal;
GLsizei srcDepth = call.params.getParam("srcDepth", ParamType::TGLsizei, 14).value.GLsizeiVal;
gl::Texture *srcTexture = context->getTexture({srcID});
gl::TextureTarget srcTargetPacked = gl::PackParam<gl::TextureTarget>(srcTarget);
const gl::Extents &srcExtents = srcTexture->getExtents(srcTargetPacked, srcLevel);
ASSERT(srcExtents.width == srcWidth && srcExtents.height == srcHeight &&
srcExtents.depth == srcDepth);
// Look up the data for the source texture
const auto &foundSrcTextureLevels = mCachedTextureLevelData.find(srcID);
ASSERT(foundSrcTextureLevels != mCachedTextureLevelData.end());
// For that texture, look up the data for the given level
const auto &foundSrcTextureLevel = foundSrcTextureLevels->second.find(srcLevel);
ASSERT(foundSrcTextureLevel != foundSrcTextureLevels->second.end());
const std::vector<uint8_t> &srcTextureLevel = foundSrcTextureLevel->second;
auto foundDstTextureLevels = mCachedTextureLevelData.find(dstID);
if (foundDstTextureLevels == mCachedTextureLevelData.end())
{
// Initialize the texture ID data.
auto emplaceResult = mCachedTextureLevelData.emplace(dstID, TextureLevels());
ASSERT(emplaceResult.second);
foundDstTextureLevels = emplaceResult.first;
}
TextureLevels &foundDstLevels = foundDstTextureLevels->second;
TextureLevels::iterator foundDstLevel = foundDstLevels.find(dstLevel);
if (foundDstLevel != foundDstLevels.end())
{
// If we have a cache for this level, remove it since we're recreating it.
foundDstLevels.erase(dstLevel);
}
// Initialize destination texture data and copy the source into it.
std::vector<uint8_t> dstTextureLevel = srcTextureLevel;
auto emplaceResult = foundDstLevels.emplace(dstLevel, std::move(dstTextureLevel));
ASSERT(emplaceResult.second);
}
std::vector<uint8_t> &FrameCaptureShared::getCachedTextureLevelData(gl::Texture *texture, std::vector<uint8_t> &FrameCaptureShared::getCachedTextureLevelData(gl::Texture *texture,
gl::TextureTarget target, gl::TextureTarget target,
GLint level, GLint level,
......
...@@ -343,6 +343,7 @@ class FrameCapture final : angle::NonCopyable ...@@ -343,6 +343,7 @@ class FrameCapture final : angle::NonCopyable
size_t instanceCount); size_t instanceCount);
void captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call); void captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call);
void copyCompressedTextureData(const gl::Context *context, const CallCapture &call);
void captureCompressedTextureData(const gl::Context *context, const CallCapture &call); void captureCompressedTextureData(const gl::Context *context, const CallCapture &call);
void reset(); void reset();
...@@ -408,6 +409,14 @@ class FrameCaptureShared final : angle::NonCopyable ...@@ -408,6 +409,14 @@ class FrameCaptureShared final : angle::NonCopyable
// Load data from a previously stored texture level // Load data from a previously stored texture level
const std::vector<uint8_t> &retrieveCachedTextureLevel(gl::TextureID id, GLint level); const std::vector<uint8_t> &retrieveCachedTextureLevel(gl::TextureID id, GLint level);
// Create new texture level data and copy the source into it
void copyCachedTextureLevel(const gl::Context *context,
gl::TextureID srcID,
GLint srcLevel,
gl::TextureID dstID,
GLint dstLevel,
const CallCapture &call);
// Create the location that should be used to cache texture level data // Create the location that should be used to cache texture level data
std::vector<uint8_t> &getCachedTextureLevelData(gl::Texture *texture, std::vector<uint8_t> &getCachedTextureLevelData(gl::Texture *texture,
gl::TextureTarget target, gl::TextureTarget target,
...@@ -435,12 +444,16 @@ void CaptureCallToFrameCapture(CaptureFuncT captureFunc, ...@@ -435,12 +444,16 @@ void CaptureCallToFrameCapture(CaptureFuncT captureFunc,
{ {
FrameCapture *frameCapture = context->getFrameCapture(); FrameCapture *frameCapture = context->getFrameCapture();
if (!frameCapture->isCapturing()) if (!frameCapture->isCapturing())
{
return; return;
}
CallCapture call = captureFunc(context->getState(), isCallValid, captureParams...); CallCapture call = captureFunc(context->getState(), isCallValid, captureParams...);
if (!isCallValid) if (!isCallValid)
{
INFO() << "FrameCapture: Capturing invalid call to " << GetEntryPointName(call.entryPoint); INFO() << "FrameCapture: Capturing invalid call to " << GetEntryPointName(call.entryPoint);
}
frameCapture->captureCall(context, std::move(call)); frameCapture->captureCall(context, std::move(call));
} }
......
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