blob: 9660e8efc97b89f71c25905ec321dd36248af704 [file] [log] [blame]
#include "GLSnapshotTestDispatch.h"
#include "FrameBuffer.h"
#include "GLSnapshotTesting.h"
#include "GLTestUtils.h"
#include "apigen-codec-common/glUtils.h"
#include "RenderThreadInfo.h"
#include "aemu/base/files/PathUtils.h"
#include "aemu/base/files/StdioStream.h"
#include "aemu/base/system/System.h"
#include "snapshot/TextureLoader.h"
#include "snapshot/TextureSaver.h"
namespace gfxstream {
namespace gl {
using android::base::StdioStream;
using android::snapshot::TextureLoader;
using android::snapshot::TextureSaver;
static SnapshotTestDispatch* sSnapshotTestDispatch() {
static SnapshotTestDispatch* s = new SnapshotTestDispatch;
return s;
}
// static
const GLESv2Dispatch* getSnapshotTestDispatch() {
return sSnapshotTestDispatch();
}
SnapshotTestDispatch::SnapshotTestDispatch() {
mTestSystem.getTempRoot()->makeSubDir("SampleSnapshots");
mSnapshotPath = mTestSystem.getTempRoot()->makeSubPath("SampleSnapshots");
mValid = gles2_dispatch_init(this);
if (mValid) {
overrideFunctions();
} else {
fprintf(stderr, "SnapshotTestDispatch failed to initialize.\n");
ADD_FAILURE() << "SnapshotTestDispatch could not initialize.";
}
}
void SnapshotTestDispatch::overrideFunctions() {
this->glDrawArrays = (glDrawArrays_t)test_glDrawArrays;
this->glDrawElements = (glDrawElements_t)test_glDrawElements;
}
void SnapshotTestDispatch::saveSnapshot() {
FrameBuffer* fb = FrameBuffer::getFB();
if (!fb) {
FAIL() << "Could not get FrameBuffer during snapshot test.";
}
std::string timeStamp =
std::to_string(android::base::getUnixTimeUs()) + "-" +
std::to_string(mLoadCount);
mSnapshotFile = android::base::pj({mSnapshotPath, std::string("snapshot_") + timeStamp + ".snap"});
mTextureFile = android::base::pj({mSnapshotPath, std::string("textures_") + timeStamp + ".stex"});
std::unique_ptr<StdioStream> m_stream(new StdioStream(
android_fopen(mSnapshotFile.c_str(), "wb"), StdioStream::kOwner));
auto a_stream = static_cast<android::base::Stream*>(m_stream.get());
std::shared_ptr<TextureSaver> m_texture_saver(new TextureSaver(StdioStream(
android_fopen(mTextureFile.c_str(), "wb"), StdioStream::kOwner)));
fb->onSave(a_stream, m_texture_saver);
// Save thread's context and surface handles so we can restore the bind
// after load is complete.
RenderThreadInfo* threadInfo = RenderThreadInfo::get();
if (threadInfo) {
threadInfo->onSave(a_stream);
}
m_stream->close();
m_texture_saver->done();
}
void SnapshotTestDispatch::loadSnapshot() {
FrameBuffer* fb = FrameBuffer::getFB();
if (!fb) {
FAIL() << "Could not get FrameBuffer during snapshot test.";
}
// unbind so load will destroy previous objects
fb->bindContext(0, 0, 0);
std::unique_ptr<StdioStream> m_stream(new StdioStream(
android_fopen(mSnapshotFile.c_str(), "rb"), StdioStream::kOwner));
std::shared_ptr<TextureLoader> m_texture_loader(
new TextureLoader(StdioStream(android_fopen(mTextureFile.c_str(), "rb"),
StdioStream::kOwner)));
fb->onLoad(m_stream.get(), m_texture_loader);
RenderThreadInfo* threadInfo = RenderThreadInfo::get();
if (threadInfo) {
threadInfo->onLoad(m_stream.get());
// rebind to context
fb->bindContext(
threadInfo->m_glInfo->currContext ? threadInfo->m_glInfo->currContext->getHndl()
: 0,
threadInfo->m_glInfo->currDrawSurf ? threadInfo->m_glInfo->currDrawSurf->getHndl()
: 0,
threadInfo->m_glInfo->currReadSurf ? threadInfo->m_glInfo->currReadSurf->getHndl()
: 0);
}
m_stream->close();
m_texture_loader->join();
mLoadCount++;
}
// static
void SnapshotTestDispatch::testDraw(std::function<void()> doDraw) {
const GLESv2Dispatch* gl = LazyLoadedGLESv2Dispatch::get();
ASSERT_NE(nullptr, gl);
FrameBuffer* fb = FrameBuffer::getFB();
if (!fb) {
ADD_FAILURE() << "No framebuffer, test cannot snapshot.";
doDraw();
return;
}
// save then draw
((SnapshotTestDispatch*)getSnapshotTestDispatch())->saveSnapshot();
// Since current framebuffer contents are not saved, we need to draw
// onto a clean slate in order to check the result of the draw call
gl->glClear(GL_COLOR_BUFFER_BIT);
doDraw();
// save the framebuffer contents
GLuint width, height, bytesPerPixel;
width = fb->getWidth();
height = fb->getHeight();
bytesPerPixel = glUtilsPixelBitSize(GL_RGBA, GL_UNSIGNED_BYTE) / 8;
std::vector<GLubyte> prePixels = {};
prePixels.resize(width * height * bytesPerPixel);
gl->glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
prePixels.data());
// To verify that the snapshot is restoring our context, we modify the
// clear color.
std::vector<GLfloat> oldClear = {};
oldClear.resize(4);
gl->glGetFloatv(GL_COLOR_CLEAR_VALUE, oldClear.data());
EXPECT_TRUE(compareGlobalGlFloatv(gl, GL_COLOR_CLEAR_VALUE, oldClear));
gl->glClearColor(1, 1, 1, 1);
gl->glClear(GL_COLOR_BUFFER_BIT);
EXPECT_TRUE(compareGlobalGlFloatv(gl, GL_COLOR_CLEAR_VALUE, {1, 1, 1, 1}));
// load and redraw
((SnapshotTestDispatch*)getSnapshotTestDispatch())->loadSnapshot();
gl->glClear(GL_COLOR_BUFFER_BIT);
doDraw();
// check that clear is restored
EXPECT_TRUE(compareGlobalGlFloatv(gl, GL_COLOR_CLEAR_VALUE, oldClear));
// compare the framebuffer contents
std::vector<GLubyte> postPixels = {};
postPixels.resize(width * height * bytesPerPixel);
gl->glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
postPixels.data());
EXPECT_TRUE(ImageMatches(width, height, bytesPerPixel, width,
prePixels.data(), postPixels.data()));
}
} // namespace gl
} // namespace gfxstream