Commit dce37b7d by Manh Nguyen Committed by Commit Bot

Serialize framebuffers + compare contexts for CaptureReplayTests

Adds to frame capture the ability to serialize a frame's pre-swap GL state and store it in the binary data file Adds to CaptureReplayTests the ability to compare its serialized GL state with the serialized state pulled from the binary data file Adds a serialization module that serializes framebuffers' GL states and the contents of their color attachments Adds checks to automation script so that it would skips tests that do not produce the expected trace files Adds exception handling to automation script so that it will not crash when a replay build crashes Bug: angleproject:4779 Change-Id: I40a02e018073749e79f0ddbfd3d4065745548f46 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2258295 Commit-Queue: Manh Nguyen <nguyenmh@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 5fec8ecf
...@@ -98,6 +98,12 @@ config("internal_config") { ...@@ -98,6 +98,12 @@ config("internal_config") {
defines += [ "ANGLE_IS_32_BIT_CPU" ] defines += [ "ANGLE_IS_32_BIT_CPU" ]
} }
if (is_win) {
defines += [ "ANGLE_IS_WIN" ]
} else if (is_linux) {
defines += [ "ANGLE_IS_LINUX" ]
}
if (angle_enable_trace) { if (angle_enable_trace) {
defines += [ "ANGLE_ENABLE_DEBUG_TRACE=1" ] defines += [ "ANGLE_ENABLE_DEBUG_TRACE=1" ]
} }
...@@ -907,6 +913,14 @@ angle_static_library("libGLESv2_static") { ...@@ -907,6 +913,14 @@ angle_static_library("libGLESv2_static") {
public_deps = [ ":libANGLE" ] public_deps = [ ":libANGLE" ]
} }
angle_static_library("libGLESv2_with_capture_static") {
sources = libglesv2_sources
configs += [ ":debug_annotations_config" ]
public_configs += [ ":angle_static" ]
deps = [ ":includes" ]
public_deps = [ ":libANGLE_with_capture" ]
}
angle_shared_library("libGLESv1_CM") { angle_shared_library("libGLESv1_CM") {
sources = libglesv1_cm_sources sources = libglesv1_cm_sources
output_name = "libGLESv1_CM${angle_libs_suffix}" output_name = "libGLESv1_CM${angle_libs_suffix}"
...@@ -987,6 +1001,19 @@ angle_shared_library("libEGL") { ...@@ -987,6 +1001,19 @@ angle_shared_library("libEGL") {
angle_static_library("libEGL_static") { angle_static_library("libEGL_static") {
sources = libegl_sources sources = libegl_sources
configs += [
":debug_annotations_config",
":library_name_config",
]
public_configs += [ ":angle_static" ]
deps = [
":includes",
":libGLESv2_static",
]
}
angle_static_library("libEGL_with_capture_static") {
sources = libegl_sources
configs += [ configs += [
":debug_annotations_config", ":debug_annotations_config",
...@@ -997,7 +1024,7 @@ angle_static_library("libEGL_static") { ...@@ -997,7 +1024,7 @@ angle_static_library("libEGL_static") {
deps = [ deps = [
":includes", ":includes",
":libGLESv2_static", ":libGLESv2_with_capture_static",
] ]
} }
......
...@@ -73,6 +73,8 @@ Some simple environment variables control frame capture: ...@@ -73,6 +73,8 @@ Some simple environment variables control frame capture:
foo::ReplayContext1Frame(i); foo::ReplayContext1Frame(i);
} }
``` ```
* `ANGLE_CAPTURE_SERIALIZE_STATE`:
* Set to `1` to enable GL state serialization. Default is `0`.
A good way to test out the capture is to use environment variables in conjunction with the sample A good way to test out the capture is to use environment variables in conjunction with the sample
template. For example: template. For example:
......
...@@ -236,8 +236,10 @@ class BinaryOutputStream : angle::NonCopyable ...@@ -236,8 +236,10 @@ class BinaryOutputStream : angle::NonCopyable
const void *data() const { return mData.size() ? &mData[0] : nullptr; } const void *data() const { return mData.size() ? &mData[0] : nullptr; }
const std::vector<uint8_t> &getData() const { return mData; }
private: private:
std::vector<char> mData; std::vector<uint8_t> mData;
template <typename T> template <typename T>
void write(const T *v, size_t num) void write(const T *v, size_t num)
......
...@@ -8437,7 +8437,7 @@ egl::Error Context::unsetDefaultFramebuffer() ...@@ -8437,7 +8437,7 @@ egl::Error Context::unsetDefaultFramebuffer()
return egl::NoError(); return egl::NoError();
} }
void Context::onPostSwap() const void Context::onPreSwap() const
{ {
// Dump frame capture if enabled. // Dump frame capture if enabled.
mFrameCapture->onEndFrame(this); mFrameCapture->onEndFrame(this);
......
...@@ -604,7 +604,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -604,7 +604,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
return mTransformFeedbackMap; return mTransformFeedbackMap;
} }
void onPostSwap() const; void onPreSwap() const;
Program *getActiveLinkedProgram() const; Program *getActiveLinkedProgram() const;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "libANGLE/VertexArray.h" #include "libANGLE/VertexArray.h"
#include "libANGLE/capture_gles_2_0_autogen.h" #include "libANGLE/capture_gles_2_0_autogen.h"
#include "libANGLE/capture_gles_3_0_autogen.h" #include "libANGLE/capture_gles_3_0_autogen.h"
#include "libANGLE/frame_capture_utils.h"
#include "libANGLE/gl_enum_utils.h" #include "libANGLE/gl_enum_utils.h"
#include "libANGLE/queryconversions.h" #include "libANGLE/queryconversions.h"
#include "libANGLE/queryutils.h" #include "libANGLE/queryutils.h"
...@@ -44,12 +45,13 @@ namespace angle ...@@ -44,12 +45,13 @@ namespace angle
namespace namespace
{ {
constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED"; constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED";
constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR"; constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START"; constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START";
constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END"; constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END";
constexpr char kCaptureLabel[] = "ANGLE_CAPTURE_LABEL"; constexpr char kCaptureLabel[] = "ANGLE_CAPTURE_LABEL";
constexpr char kCompression[] = "ANGLE_CAPTURE_COMPRESSION"; constexpr char kCompression[] = "ANGLE_CAPTURE_COMPRESSION";
constexpr char kSerializeStateEnabledVarName[] = "ANGLE_CAPTURE_SERIALIZE_STATE";
constexpr size_t kBinaryAlignment = 16; constexpr size_t kBinaryAlignment = 16;
...@@ -910,7 +912,7 @@ void WriteCppReplay(bool compression, ...@@ -910,7 +912,7 @@ void WriteCppReplay(bool compression,
void WriteCppReplayIndexFiles(bool compression, void WriteCppReplayIndexFiles(bool compression,
const std::string &outDir, const std::string &outDir,
gl::ContextID contextId, const gl::Context *context,
const std::string &captureLabel, const std::string &captureLabel,
uint32_t frameStart, uint32_t frameStart,
uint32_t frameEnd, uint32_t frameEnd,
...@@ -918,8 +920,28 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -918,8 +920,28 @@ void WriteCppReplayIndexFiles(bool compression,
EGLint drawSurfaceHeight, EGLint drawSurfaceHeight,
size_t readBufferSize, size_t readBufferSize,
const gl::AttribArray<size_t> &clientArraySizes, const gl::AttribArray<size_t> &clientArraySizes,
const HasResourceTypeMap &hasResourceType) const HasResourceTypeMap &hasResourceType,
bool serializeStateEnabled,
std::vector<uint8_t> &binaryData)
{ {
gl::ContextID contextId = context->id();
size_t serializedContextLength = 0;
size_t serializedContextOffset = 0;
if (serializeStateEnabled)
{
gl::BinaryOutputStream serializedContextData{};
if (SerializeContext(&serializedContextData, const_cast<gl::Context *>(context)) !=
Result::Continue)
{
return;
}
// store serialized context behind capture replay data
serializedContextLength = serializedContextData.length();
serializedContextOffset = rx::roundUp(binaryData.size(), kBinaryAlignment);
binaryData.resize(serializedContextOffset + serializedContextLength);
memcpy(binaryData.data() + serializedContextOffset, serializedContextData.data(),
serializedContextLength);
}
size_t maxClientArraySize = MaxClientArraySize(clientArraySizes); size_t maxClientArraySize = MaxClientArraySize(clientArraySizes);
std::stringstream header; std::stringstream header;
...@@ -927,8 +949,7 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -927,8 +949,7 @@ void WriteCppReplayIndexFiles(bool compression,
header << "#pragma once\n"; header << "#pragma once\n";
header << "\n"; header << "\n";
header << "#include \"util/gles_loader_autogen.h\"\n"; header << "#include \"util/util_gl.h\"\n";
header << "#include \"util/egl_loader_autogen.h\"\n";
header << "\n"; header << "\n";
header << "#include <cstdint>\n"; header << "#include <cstdint>\n";
header << "#include <cstdio>\n"; header << "#include <cstdio>\n";
...@@ -967,6 +988,11 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -967,6 +988,11 @@ void WriteCppReplayIndexFiles(bool compression,
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 << "void ResetContext" << static_cast<int>(contextId) << "Replay();\n";
if (serializeStateEnabled)
{
header << "std::vector<uint8_t> GetSerializedContextState" << static_cast<int>(contextId)
<< "Data();\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";
...@@ -1098,6 +1124,21 @@ void WriteCppReplayIndexFiles(bool compression, ...@@ -1098,6 +1124,21 @@ void WriteCppReplayIndexFiles(bool compression,
source << " }\n"; source << " }\n";
source << "}\n"; source << "}\n";
source << "\n"; source << "\n";
if (serializeStateEnabled)
{
source << "std::vector<uint8_t> GetSerializedContextState" << static_cast<int>(contextId)
<< "Data()\n";
source << "{\n";
source << " std::vector<uint8_t> serializedContextData(" << serializedContextLength
<< ");\n";
source << " memcpy(serializedContextData.data(), &gBinaryData["
<< serializedContextOffset << "], " << serializedContextLength << ");\n";
source << " return serializedContextData;\n";
source << "}\n";
source << "\n";
}
source << "void SetBinaryDataDecompressCallback(DecompressCallback callback)\n"; source << "void SetBinaryDataDecompressCallback(DecompressCallback callback)\n";
source << "{\n"; source << "{\n";
source << " gDecompressCallback = callback;\n"; source << " gDecompressCallback = callback;\n";
...@@ -3145,6 +3186,7 @@ ReplayContext::~ReplayContext() {} ...@@ -3145,6 +3186,7 @@ ReplayContext::~ReplayContext() {}
FrameCapture::FrameCapture() FrameCapture::FrameCapture()
: mEnabled(true), : mEnabled(true),
mSerializeStateEnabled(false),
mCompression(true), mCompression(true),
mClientVertexArrayMap{}, mClientVertexArrayMap{},
mFrameIndex(0), mFrameIndex(0),
...@@ -3206,6 +3248,12 @@ FrameCapture::FrameCapture() ...@@ -3206,6 +3248,12 @@ FrameCapture::FrameCapture()
{ {
mCompression = false; mCompression = false;
} }
std::string serializeStateEnabledFromEnv =
angle::GetEnvironmentVar(kSerializeStateEnabledVarName);
if (serializeStateEnabledFromEnv == "1")
{
mSerializeStateEnabled = true;
}
} }
FrameCapture::~FrameCapture() = default; FrameCapture::~FrameCapture() = default;
...@@ -3863,10 +3911,10 @@ void FrameCapture::onEndFrame(const gl::Context *context) ...@@ -3863,10 +3911,10 @@ void FrameCapture::onEndFrame(const gl::Context *context)
// Save the index files after the last frame. // Save the index files after the last frame.
if (mFrameIndex == mFrameEnd) if (mFrameIndex == mFrameEnd)
{ {
WriteCppReplayIndexFiles(mCompression, mOutDirectory, context->id(), mCaptureLabel, WriteCppReplayIndexFiles(mCompression, mOutDirectory, context, mCaptureLabel,
mFrameStart, mFrameEnd, mDrawSurfaceWidth, mDrawSurfaceHeight, mFrameStart, mFrameEnd, mDrawSurfaceWidth, mDrawSurfaceHeight,
mReadBufferSize, mClientArraySizes, mHasResourceType); mReadBufferSize, mClientArraySizes, mHasResourceType,
mSerializeStateEnabled, mBinaryData);
if (!mBinaryData.empty()) if (!mBinaryData.empty())
{ {
SaveBinaryData(mCompression, mOutDirectory, context->id(), mCaptureLabel, SaveBinaryData(mCompression, mOutDirectory, context->id(), mCaptureLabel,
......
...@@ -311,6 +311,7 @@ class FrameCapture final : angle::NonCopyable ...@@ -311,6 +311,7 @@ class FrameCapture final : angle::NonCopyable
std::vector<uint8_t> mBinaryData; std::vector<uint8_t> mBinaryData;
bool mEnabled = false; bool mEnabled = false;
bool mSerializeStateEnabled;
std::string mOutDirectory; std::string mOutDirectory;
std::string mCaptureLabel; std::string mCaptureLabel;
bool mCompression; bool mCompression;
......
...@@ -357,7 +357,7 @@ FramebufferState::FramebufferState(const Caps &caps, FramebufferID id, ContextID ...@@ -357,7 +357,7 @@ FramebufferState::FramebufferState(const Caps &caps, FramebufferID id, ContextID
FramebufferState::~FramebufferState() {} FramebufferState::~FramebufferState() {}
const std::string &FramebufferState::getLabel() const std::string &FramebufferState::getLabel() const
{ {
return mLabel; return mLabel;
} }
......
...@@ -65,7 +65,7 @@ class FramebufferState final : angle::NonCopyable ...@@ -65,7 +65,7 @@ class FramebufferState final : angle::NonCopyable
FramebufferState(const Caps &caps, FramebufferID id, ContextID owningContextID); FramebufferState(const Caps &caps, FramebufferID id, ContextID owningContextID);
~FramebufferState(); ~FramebufferState();
const std::string &getLabel(); const std::string &getLabel() const;
size_t getReadIndex() const; size_t getReadIndex() const;
const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const; const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const;
...@@ -248,6 +248,8 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -248,6 +248,8 @@ class Framebuffer final : public angle::ObserverInterface,
return mState.mColorAttachments; return mState.mColorAttachments;
} }
const FramebufferState &getState() const { return mState; }
const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const; const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const;
bool isMultiview() const; bool isMultiview() const;
bool readDisallowedByMultiview() const; bool readDisallowedByMultiview() const;
......
...@@ -158,8 +158,6 @@ void Surface::postSwap(const gl::Context *context) ...@@ -158,8 +158,6 @@ void Surface::postSwap(const gl::Context *context)
mInitState = gl::InitState::MayNeedInit; mInitState = gl::InitState::MayNeedInit;
onStateChange(angle::SubjectMessage::SubjectChanged); onStateChange(angle::SubjectMessage::SubjectChanged);
} }
context->onPostSwap();
} }
Error Surface::initialize(const Display *display) Error Surface::initialize(const Display *display)
...@@ -278,6 +276,7 @@ EGLint Surface::getType() const ...@@ -278,6 +276,7 @@ EGLint Surface::getType() const
Error Surface::swap(const gl::Context *context) Error Surface::swap(const gl::Context *context)
{ {
ANGLE_TRACE_EVENT0("gpu.angle", "egl::Surface::swap"); ANGLE_TRACE_EVENT0("gpu.angle", "egl::Surface::swap");
context->onPreSwap();
context->getState().getOverlay()->onSwap(); context->getState().getOverlay()->onSwap();
......
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// frame_capture_utils.cpp:
// ANGLE frame capture util implementation.
//
#include "libANGLE/frame_capture_utils.h"
#include "common/MemoryBuffer.h"
#include "common/angleutils.h"
#include "libANGLE/BinaryStream.h"
#include "libANGLE/Context.h"
#include "libANGLE/Framebuffer.h"
#define ANGLE_STATIC_ASSERT_SIZE(className, classSize) \
static_assert( \
sizeof(className) == classSize, \
ANGLE_STRINGIFY(className) " class has changed. Please " \
"update its serialize method in " __FILE__);
namespace angle
{
Result ReadPixelsFromAttachment(gl::Context *context,
gl::Framebuffer *framebuffer,
const gl::FramebufferAttachment &framebufferAttachment,
ScratchBuffer *scratchBuffer,
MemoryBuffer **pixels)
{
gl::Extents extents = framebufferAttachment.getSize();
gl::Format format = framebufferAttachment.getFormat();
ANGLE_CHECK_GL_ALLOC(
context,
scratchBuffer->get(format.info->pixelBytes * extents.width * extents.height, pixels));
ANGLE_TRY(framebuffer->readPixels(context, gl::Rectangle{0, 0, extents.width, extents.height},
format.info->format, format.info->type, gl::PixelPackState{},
nullptr, (*pixels)->data()));
return Result::Continue;
}
Result SerializeContext(gl::BinaryOutputStream *bos, gl::Context *context)
{
const gl::FramebufferManager &framebufferManager =
context->getState().getFramebufferManagerForCapture();
for (const auto &framebuffer : framebufferManager)
{
gl::Framebuffer *framebufferPtr = framebuffer.second;
ANGLE_TRY(SerializeFramebuffer(context, bos, framebufferPtr));
}
return Result::Continue;
}
Result SerializeFramebuffer(gl::Context *context,
gl::BinaryOutputStream *bos,
gl::Framebuffer *framebuffer)
{
#if defined(ANGLE_IS_64_BIT_CPU) && (defined(ANGLE_IS_WIN) || defined(ANGLE_IS_LINUX))
constexpr size_t kSizeOfGlFramebuffer = 744;
ANGLE_STATIC_ASSERT_SIZE(gl::Framebuffer, kSizeOfGlFramebuffer)
#endif
return SerializeFramebufferState(context, bos, framebuffer, framebuffer->getState());
}
Result SerializeFramebufferState(gl::Context *context,
gl::BinaryOutputStream *bos,
gl::Framebuffer *framebuffer,
const gl::FramebufferState &framebufferState)
{
#if defined(ANGLE_IS_64_BIT_CPU) && (defined(ANGLE_IS_WIN) || defined(ANGLE_IS_LINUX))
constexpr size_t kSizeOfGlFramebufferState = 488;
ANGLE_STATIC_ASSERT_SIZE(gl::FramebufferState, kSizeOfGlFramebufferState)
#endif
bos->writeInt(framebufferState.id().value);
bos->writeString(framebufferState.getLabel());
bos->writeIntVector(framebufferState.getDrawBufferStates());
bos->writeInt(framebufferState.getReadBufferState());
bos->writeInt(framebufferState.getDefaultWidth());
bos->writeInt(framebufferState.getDefaultHeight());
bos->writeInt(framebufferState.getDefaultSamples());
bos->writeInt(framebufferState.getDefaultFixedSampleLocations());
bos->writeInt(framebufferState.getDefaultLayers());
context->bindFramebuffer(GL_FRAMEBUFFER, framebufferState.id());
ANGLE_TRY(framebuffer->syncState(context, GL_FRAMEBUFFER));
const std::vector<gl::FramebufferAttachment> &colorAttachments =
framebufferState.getColorAttachments();
ScratchBuffer scratchBuffer(1);
for (const gl::FramebufferAttachment &colorAttachment : colorAttachments)
{
if (colorAttachment.isAttached())
{
ANGLE_TRY(SerializeFramebufferAttachment(context, bos, &scratchBuffer, framebuffer,
colorAttachment));
}
}
scratchBuffer.clear();
return Result::Continue;
}
Result SerializeFramebufferAttachment(gl::Context *context,
gl::BinaryOutputStream *bos,
ScratchBuffer *scratchBuffer,
gl::Framebuffer *framebuffer,
const gl::FramebufferAttachment &framebufferAttachment)
{
#if defined(ANGLE_IS_64_BIT_CPU) && (defined(ANGLE_IS_WIN) || defined(ANGLE_IS_LINUX))
constexpr size_t kSizeOfGlFramebufferAttachment = 48;
ANGLE_STATIC_ASSERT_SIZE(gl::FramebufferAttachment, kSizeOfGlFramebufferAttachment)
#endif
bos->writeInt(framebufferAttachment.type());
// serialize target variable
bos->writeInt(framebufferAttachment.getBinding());
if (framebufferAttachment.type() == GL_TEXTURE)
{
SerializeImageIndex(bos, framebufferAttachment.getTextureImageIndex());
}
bos->writeInt(framebufferAttachment.getNumViews());
bos->writeInt(framebufferAttachment.isMultiview());
bos->writeInt(framebufferAttachment.getBaseViewIndex());
bos->writeInt(framebufferAttachment.getRenderToTextureSamples());
MemoryBuffer *pixelsPtr = nullptr;
context->readBuffer(framebufferAttachment.getBinding());
ANGLE_TRY(framebuffer->syncState(context, GL_FRAMEBUFFER));
ANGLE_TRY(ReadPixelsFromAttachment(context, framebuffer, framebufferAttachment, scratchBuffer,
&pixelsPtr));
bos->writeBytes(pixelsPtr->data(), pixelsPtr->size());
return Result::Continue;
}
void SerializeImageIndex(gl::BinaryOutputStream *bos, const gl::ImageIndex &imageIndex)
{
// size of ImageIndex on 64-bit and 86-bit systems are the same
constexpr size_t kSizeOfGlImageIndex = 16;
ANGLE_STATIC_ASSERT_SIZE(gl::ImageIndex, kSizeOfGlImageIndex)
bos->writeEnum(imageIndex.getType());
bos->writeInt(imageIndex.getLevelIndex());
bos->writeInt(imageIndex.getLayerIndex());
bos->writeInt(imageIndex.getLayerCount());
}
} // namespace angle
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// frame_capture_utils.h:
// ANGLE frame capture utils interface.
//
#ifndef FRAME_CAPTURE_UTILS_H_
#define FRAME_CAPTURE_UTILS_H_
#include <vector>
#include "libANGLE/Error.h"
namespace gl
{
class BinaryOutputStream;
class Context;
class Framebuffer;
class FramebufferAttachment;
class FramebufferState;
class ImageIndex;
} // namespace gl
typedef unsigned int GLenum;
namespace angle
{
class MemoryBuffer;
class ScratchBuffer;
Result SerializeContext(gl::BinaryOutputStream *bos, gl::Context *context);
Result SerializeFramebuffer(gl::Context *context,
gl::BinaryOutputStream *bos,
gl::Framebuffer *framebuffer);
Result SerializeFramebufferState(gl::Context *context,
gl::BinaryOutputStream *bos,
gl::Framebuffer *framebuffer,
const gl::FramebufferState &framebufferState);
Result SerializeFramebufferAttachment(gl::Context *context,
gl::BinaryOutputStream *bos,
ScratchBuffer *scratchBuffer,
gl::Framebuffer *framebuffer,
const gl::FramebufferAttachment &framebufferAttachment);
void SerializeImageIndex(gl::BinaryOutputStream *bos, const gl::ImageIndex &imageIndex);
} // namespace angle
#endif // FRAME_CAPTURE_UTILS_H_
...@@ -913,6 +913,8 @@ libangle_capture_sources = [ ...@@ -913,6 +913,8 @@ libangle_capture_sources = [
"src/libANGLE/capture_gles_ext_autogen.cpp", "src/libANGLE/capture_gles_ext_autogen.cpp",
"src/libANGLE/capture_gles_ext_params.cpp", "src/libANGLE/capture_gles_ext_params.cpp",
"src/libANGLE/frame_capture_replay_autogen.cpp", "src/libANGLE/frame_capture_replay_autogen.cpp",
"src/libANGLE/frame_capture_utils.cpp",
"src/libANGLE/frame_capture_utils.h",
"src/libANGLE/frame_capture_utils_autogen.cpp", "src/libANGLE/frame_capture_utils_autogen.cpp",
"src/libANGLE/gl_enum_utils.cpp", "src/libANGLE/gl_enum_utils.cpp",
"src/libANGLE/gl_enum_utils_autogen.cpp", "src/libANGLE/gl_enum_utils_autogen.cpp",
......
...@@ -128,11 +128,15 @@ class Test(): ...@@ -128,11 +128,15 @@ class Test():
return (e.returncode, e.output) return (e.returncode, e.output)
def BuildReplay(self, build_dir, replay_exec): def BuildReplay(self, build_dir, replay_exec):
RunGnGen(build_dir, [("use_goma", self.use_goma), try:
("angle_with_capture_by_default", "true"), RunGnGen(build_dir, [("use_goma", self.use_goma),
("angle_build_capture_replay_tests", "true")]) ("angle_with_capture_by_default", "true"),
RunAutoninja(build_dir, replay_exec) ("angle_build_capture_replay_tests", "true")])
Logger.log("Built replay of " + self.full_test_name) RunAutoninja(build_dir, replay_exec)
Logger.log("Built replay of " + self.full_test_name)
return (0, "Built replay of " + self.full_test_name)
except subprocess.CalledProcessError as e:
return (e.returncode, e.output)
def RunReplay(self, build_dir, replay_exec): def RunReplay(self, build_dir, replay_exec):
try: try:
...@@ -149,19 +153,27 @@ class Test(): ...@@ -149,19 +153,27 @@ class Test():
def ClearFolderContent(path): def ClearFolderContent(path):
all_files = [] all_files = []
for f in os.listdir(path): for f in os.listdir(path):
if os.path.isfile(path + "/" + f) and f.startswith("angle_capture_context"): if os.path.isfile(os.path.join(path, f)) and f.startswith("angle_capture_context"):
os.remove(path + "/" + f) os.remove(os.path.join(path, f))
def CanRunReplay(path): def CanRunReplay(path):
files = [ required_trace_files = {
"angle_capture_context1.h", "angle_capture_context1.cpp", "angle_capture_context1.h", "angle_capture_context1.cpp",
"angle_capture_context1_files.txt", "angle_capture_context1_frame000.cpp" "angle_capture_context1_files.txt", "angle_capture_context1_frame000.cpp"
] }
for file in files: binary_data_file = "angle_capture_context1.angledata.gz"
if not os.path.isfile(path + "/" + file): required_trace_files_count = 0
for f in os.listdir(path):
if f in required_trace_files:
required_trace_files_count += 1
elif os.path.isfile(os.path.join(path, f)) and f != binary_data_file and \
f.startswith("angle_capture_context"):
# if trace_files of another context exists, then the test creates multiple contexts
# or capture multiple frames
return False return False
return True # angle_capture_context1.angledata.gz can be missing
return required_trace_files_count == len(required_trace_files)
def SetCWDToAngleFolder(): def SetCWDToAngleFolder():
...@@ -179,7 +191,8 @@ def main(build_dir, verbose, use_goma, gtest_filter, test_exec): ...@@ -179,7 +191,8 @@ def main(build_dir, verbose, use_goma, gtest_filter, test_exec):
if not os.path.isdir(capture_out_dir): if not os.path.isdir(capture_out_dir):
os.mkdir(capture_out_dir) os.mkdir(capture_out_dir)
environment_vars = [("ANGLE_CAPTURE_FRAME_END", "0"), environment_vars = [("ANGLE_CAPTURE_FRAME_END", "0"),
("ANGLE_CAPTURE_OUT_DIR", capture_out_dir)] ("ANGLE_CAPTURE_OUT_DIR", capture_out_dir),
("ANGLE_CAPTURE_SERIALIZE_STATE", "1")]
replay_exec = "capture_replay_tests" replay_exec = "capture_replay_tests"
if platform == "win32": if platform == "win32":
test_exec += ".exe" test_exec += ".exe"
...@@ -197,29 +210,48 @@ def main(build_dir, verbose, use_goma, gtest_filter, test_exec): ...@@ -197,29 +210,48 @@ def main(build_dir, verbose, use_goma, gtest_filter, test_exec):
for environment_var in environment_vars: for environment_var in environment_vars:
os.environ[environment_var[0]] = environment_var[1] os.environ[environment_var[0]] = environment_var[1]
passed_count = 0
failed_count = 0
skipped_count = 0
failed_tests = []
for test in all_tests: for test in all_tests:
if verbose: if verbose:
print("*" * 30) print("*" * 30)
ClearFolderContent(capture_out_dir) ClearFolderContent(capture_out_dir)
os.environ["ANGLE_CAPTURE_ENABLED"] = "1" os.environ["ANGLE_CAPTURE_ENABLED"] = "1"
run_output = test.Run(build_dir + "/" + test_exec) run_output = test.Run(build_dir + "/" + test_exec)
if run_output[0] == 0 and CanRunReplay(capture_out_dir): if run_output[0] != 0 or not CanRunReplay(capture_out_dir):
os.environ["ANGLE_CAPTURE_ENABLED"] = "0"
test.BuildReplay(build_dir, replay_exec)
replay_output = test.RunReplay(build_dir, replay_exec)
if replay_output[0] != 0:
print("Failed: " + test.full_test_name)
print(replay_output[1])
else:
print("Passed: " + test.full_test_name)
else:
print("Skipped: " + test.full_test_name + ". Skipping replay since capture" + \ print("Skipped: " + test.full_test_name + ". Skipping replay since capture" + \
"didn't produce appropriate files or has crashed") " didn't produce appropriate files or has crashed")
skipped_count += 1
continue
os.environ["ANGLE_CAPTURE_ENABLED"] = "0"
build_output = test.BuildReplay(build_dir, replay_exec)
if build_output[0] != 0:
print("Skipped: " + test.full_test_name + ". Skipping replay since failing to" + \
" build replay")
skipped_count += 1
continue
replay_output = test.RunReplay(build_dir, replay_exec)
if replay_output[0] != 0:
print("Failed: " + test.full_test_name)
print(replay_output[1])
failed_count += 1
failed_tests.append(test.full_test_name)
else:
print("Passed: " + test.full_test_name)
passed_count += 1
for environment_var in environment_vars: for environment_var in environment_vars:
del os.environ[environment_var[0]] del os.environ[environment_var[0]]
if os.path.isdir(capture_out_dir): if os.path.isdir(capture_out_dir):
shutil.rmtree(capture_out_dir) shutil.rmtree(capture_out_dir)
print("\n\n\n")
print("Passed:", passed_count, "Failed:", failed_count, "Skipped:", skipped_count)
print("Failed tests:")
for failed_test in failed_tests:
print("\t" + failed_test)
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -27,12 +27,15 @@ if (angle_build_capture_replay_tests) { ...@@ -27,12 +27,15 @@ if (angle_build_capture_replay_tests) {
sources = sources =
rebase_path(_trace_sources, ".", "traces") + [ "CaptureReplayTest.cpp" ] rebase_path(_trace_sources, ".", "traces") + [ "CaptureReplayTest.cpp" ]
deps = [ deps = [
"$angle_root:angle_common",
"$angle_root:angle_compression", "$angle_root:angle_compression",
"../../../:angle_common", "$angle_root:libEGL_with_capture_static",
"../../../util:angle_util", "$angle_root/util:angle_util_static",
"../../../util:angle_util_loader_headers", ]
configs += [
"$angle_root:library_name_config",
"$angle_root:libANGLE_config",
] ]
configs += [ "../../../:library_name_config" ]
suppressed_configs += [ "$angle_root:constructor_and_destructor_warnings" ] suppressed_configs += [ "$angle_root:constructor_and_destructor_warnings" ]
......
...@@ -8,11 +8,11 @@ ...@@ -8,11 +8,11 @@
// //
#include "common/system_utils.h" #include "common/system_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/frame_capture_utils.h"
#include "util/EGLPlatformParameters.h" #include "util/EGLPlatformParameters.h"
#include "util/EGLWindow.h" #include "util/EGLWindow.h"
#include "util/OSWindow.h" #include "util/OSWindow.h"
#include "util/egl_loader_autogen.h"
#include "util/gles_loader_autogen.h"
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
...@@ -38,6 +38,10 @@ std::function<void(int)> ReplayContextFrame = reinterpret_cast<void (*)(int)>( ...@@ -38,6 +38,10 @@ std::function<void(int)> ReplayContextFrame = reinterpret_cast<void (*)(int)>(
std::function<void()> ResetContextReplay = reinterpret_cast<void (*)()>( std::function<void()> ResetContextReplay = reinterpret_cast<void (*)()>(
ANGLE_MACRO_CONCAT(ResetContext, ANGLE_MACRO_CONCAT(ResetContext,
ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_TEST_CONTEXT_ID, Replay))); ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_TEST_CONTEXT_ID, Replay)));
std::function<std::vector<uint8_t>()> GetSerializedContextStateData =
reinterpret_cast<std::vector<uint8_t> (*)()>(
ANGLE_MACRO_CONCAT(GetSerializedContextState,
ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_TEST_CONTEXT_ID, Data)));
class CaptureReplayTest class CaptureReplayTest
{ {
...@@ -113,8 +117,6 @@ class CaptureReplayTest ...@@ -113,8 +117,6 @@ class CaptureReplayTest
return -1; return -1;
} }
angle::LoadGLES(eglGetProcAddress);
int result = 0; int result = 0;
if (!initialize()) if (!initialize())
...@@ -123,6 +125,18 @@ class CaptureReplayTest ...@@ -123,6 +125,18 @@ class CaptureReplayTest
} }
draw(); draw();
gl::Context *context = static_cast<gl::Context *>(mEGLWindow->getContext());
gl::BinaryOutputStream bos;
if (angle::SerializeContext(&bos, context) != angle::Result::Continue)
{
return -1;
}
bool isEqual = compareSerializedStates(bos);
if (!isEqual)
{
result = -1;
}
swap(); swap();
mEGLWindow->destroyGL(); mEGLWindow->destroyGL();
...@@ -132,6 +146,11 @@ class CaptureReplayTest ...@@ -132,6 +146,11 @@ class CaptureReplayTest
} }
private: private:
bool compareSerializedStates(const gl::BinaryOutputStream &replaySerializedContextData)
{
return GetSerializedContextStateData() == replaySerializedContextData.getData();
}
uint32_t mWidth; uint32_t mWidth;
uint32_t mHeight; uint32_t mHeight;
OSWindow *mOSWindow; OSWindow *mOSWindow;
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
# include "angle_gl.h" # include "angle_gl.h"
#endif // defined(ANGLE_USE_UTIL_LOADER) #endif // defined(ANGLE_USE_UTIL_LOADER)
#include <string>
namespace angle namespace angle
{ {
inline bool CheckExtensionExists(const char *allExtensions, const std::string &extName) inline bool CheckExtensionExists(const char *allExtensions, const std::string &extName)
......
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