blob: 8ea3945d7832c02a2a5754e62aa961c003dc2213 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "GLSnapshotTesting.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"
#include "GLTestUtils.h"
#include "OpenGLTestContext.h"
#include <gtest/gtest.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES3/gl31.h>
namespace gfxstream {
namespace gl {
using android::base::StdioStream;
using android::snapshot::TextureLoader;
using android::snapshot::TextureSaver;
std::string describeGlEnum(GLenum enumValue) {
std::ostringstream description;
description << "0x" << std::hex << enumValue
<< " (" << getEnumString(enumValue) << ")";
return description.str();
}
template <class T>
testing::AssertionResult compareValue(T expected,
T actual,
const std::string& description) {
if (expected != actual) {
return testing::AssertionFailure()
<< description << "\n\tvalue was:\t"
<< testing::PrintToString(actual) << "\n\t expected:\t"
<< testing::PrintToString(expected) << "\t";
}
return testing::AssertionSuccess();
}
template testing::AssertionResult compareValue<GLboolean>(GLboolean,
GLboolean,
const std::string&);
template testing::AssertionResult compareValue<GLint>(GLint,
GLint,
const std::string&);
template testing::AssertionResult compareValue<GLfloat>(GLfloat,
GLfloat,
const std::string&);
testing::AssertionResult compareGlobalGlBoolean(const GLESv2Dispatch* gl,
GLenum name,
GLboolean expected) {
GLboolean current;
gl->glGetBooleanv(name, &current);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareValue<GLboolean>(expected, current,
"GL global boolean mismatch for parameter " +
describeGlEnum(name) + ":");
}
testing::AssertionResult compareGlobalGlInt(const GLESv2Dispatch* gl,
GLenum name,
GLint expected) {
GLint current;
gl->glGetIntegerv(name, &current);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareValue<GLint>(expected, current,
"GL global int mismatch for parameter " +
describeGlEnum(name) + ":");
}
testing::AssertionResult compareGlobalGlInt_i(const GLESv2Dispatch* gl,
GLenum name,
GLuint index,
GLint expected) {
GLint current;
gl->glGetIntegeri_v(name, index, &current);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareValue<GLint>(expected, current,
"GL global int_i mismatch for parameter " +
describeGlEnum(name) + ":" + std::to_string(index));
}
testing::AssertionResult compareGlobalGlFloat(const GLESv2Dispatch* gl,
GLenum name,
GLfloat expected) {
GLfloat current;
gl->glGetFloatv(name, &current);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareValue<GLfloat>(expected, current,
"GL global float mismatch for parameter " +
describeGlEnum(name) + ":");
}
template <class T>
testing::AssertionResult compareVector(const std::vector<T>& expected,
const std::vector<T>& actual,
const std::string& description) {
std::stringstream message;
if (expected.size() != actual.size()) {
message << " (!) sizes do not match (actual " << actual.size()
<< ", expected " << expected.size() << ")\n";
}
int mismatches = 0;
for (int i = 0; i < expected.size(); i++) {
if (i >= actual.size()) {
if (mismatches < 10) {
mismatches++;
message << " no match for:\t"
<< testing::PrintToString(expected[i]) << "\n";
} else {
mismatches += expected.size() - i;
message << "\n nothing can match remaining elements.\n";
break;
}
} else if (expected[i] != actual[i]) {
mismatches++;
if (mismatches < 15) {
message << " at index " << i << ":\n\tvalue was:\t"
<< testing::PrintToString(actual[i])
<< "\n\t expected:\t"
<< testing::PrintToString(expected[i]) << "\n";
} else if (mismatches == 15) {
message << " ... and indices " << i;
} else if (mismatches < 50) {
message << ", " << i;
} else if (mismatches == 50) {
message << ", etc...";
}
}
}
if (mismatches > 0) {
return testing::AssertionFailure()
<< description << " had " << mismatches << " mismatches.\n"
<< " expected: " << testing::PrintToString(expected) << "\n"
<< " actual: " << testing::PrintToString(actual) << "\n"
<< message.str() << " \n";
}
return testing::AssertionSuccess();
}
template testing::AssertionResult compareVector<GLboolean>(
const std::vector<GLboolean>&,
const std::vector<GLboolean>&,
const std::string&);
template testing::AssertionResult compareVector<GLint>(
const std::vector<GLint>&,
const std::vector<GLint>&,
const std::string&);
template testing::AssertionResult compareVector<GLfloat>(
const std::vector<GLfloat>&,
const std::vector<GLfloat>&,
const std::string&);
testing::AssertionResult compareGlobalGlBooleanv(
const GLESv2Dispatch* gl,
GLenum name,
const std::vector<GLboolean>& expected,
GLuint size) {
std::vector<GLboolean> current;
current.resize(std::max(size, static_cast<GLuint>(expected.size())));
gl->glGetBooleanv(name, &current[0]);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareVector<GLboolean>(
expected, current,
"GL global booleanv parameter " + describeGlEnum(name));
}
testing::AssertionResult compareGlobalGlBooleanv_i(
const GLESv2Dispatch* gl,
GLenum name,
GLuint index,
const std::vector<GLboolean>& expected,
GLuint size) {
std::vector<GLboolean> current;
current.resize(std::max(size, static_cast<GLuint>(expected.size())));
gl->glGetBooleani_v(name, index, &current[0]);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareVector<GLboolean>(
expected, current,
"GL global booleanv_i parameter " + describeGlEnum(name) + ":" + std::to_string(index) );
}
testing::AssertionResult compareGlobalGlIntv(const GLESv2Dispatch* gl,
GLenum name,
const std::vector<GLint>& expected,
GLuint size) {
std::vector<GLint> current;
current.resize(std::max(size, static_cast<GLuint>(expected.size())));
gl->glGetIntegerv(name, &current[0]);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareVector<GLint>(
expected, current,
"GL global intv parameter " + describeGlEnum(name));
}
testing::AssertionResult compareGlobalGlFloatv(
const GLESv2Dispatch* gl,
GLenum name,
const std::vector<GLfloat>& expected,
GLuint size) {
std::vector<GLfloat> current;
current.resize(std::max(size, static_cast<GLuint>(expected.size())));
gl->glGetFloatv(name, &current[0]);
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
return compareVector<GLfloat>(
expected, current,
"GL global floatv parameter " + describeGlEnum(name));
}
void SnapshotTest::SetUp() {
GLTest::SetUp();
mTestSystem.getTempRoot()->makeSubDir("Snapshots");
mSnapshotPath = mTestSystem.getTempRoot()->makeSubPath("Snapshots");
}
void SnapshotTest::saveSnapshot(const std::string streamFile,
const std::string textureFile) {
const EGLDispatch* egl = LazyLoadedEGLDispatch::get();
std::unique_ptr<StdioStream> m_stream(new StdioStream(
android_fopen(streamFile.c_str(), "wb"), StdioStream::kOwner));
auto egl_stream = static_cast<EGLStreamKHR>(m_stream.get());
std::unique_ptr<TextureSaver> m_texture_saver(new TextureSaver(StdioStream(
android_fopen(textureFile.c_str(), "wb"), StdioStream::kOwner)));
egl->eglPreSaveContext(m_display, m_context, egl_stream);
egl->eglSaveAllImages(m_display, egl_stream, &m_texture_saver);
egl->eglSaveContext(m_display, m_context, egl_stream);
// Skip saving a bunch of FrameBuffer's fields
// Skip saving colorbuffers
// Skip saving window surfaces
egl->eglSaveConfig(m_display, m_config, egl_stream);
// Skip saving a bunch of process-owned objects
egl->eglPostSaveContext(m_display, m_context, egl_stream);
m_stream->close();
m_texture_saver->done();
}
void SnapshotTest::loadSnapshot(const std::string streamFile,
const std::string textureFile) {
const EGLDispatch* egl = LazyLoadedEGLDispatch::get();
std::unique_ptr<StdioStream> m_stream(new StdioStream(
android_fopen(streamFile.c_str(), "rb"), StdioStream::kOwner));
auto egl_stream = static_cast<EGLStreamKHR>(m_stream.get());
std::shared_ptr<TextureLoader> m_texture_loader(
new TextureLoader(StdioStream(android_fopen(textureFile.c_str(), "rb"),
StdioStream::kOwner)));
egl->eglLoadAllImages(m_display, egl_stream, &m_texture_loader);
EGLint contextAttribs[5] = {EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_CONTEXT_MINOR_VERSION_KHR, 0, EGL_NONE};
m_context = egl->eglLoadContext(m_display, &contextAttribs[0], egl_stream);
m_config = egl->eglLoadConfig(m_display, egl_stream);
m_surface = pbufferSurface(m_display, m_config, kTestSurfaceSize[0],
kTestSurfaceSize[0]);
egl->eglPostLoadAllImages(m_display, egl_stream);
m_stream->close();
m_texture_loader->join();
egl->eglMakeCurrent(m_display, m_surface, m_surface, m_context);
}
void SnapshotTest::preloadReset() {
GLTest::TearDown();
GLTest::SetUp();
}
void SnapshotTest::doSnapshot(std::function<void()> preloadCheck = [] {}) {
std::string timeStamp =
std::to_string(android::base::getUnixTimeUs());
std::string snapshotFile =
android::base::pj({mSnapshotPath, std::string("snapshot_") + timeStamp + ".snap"});
std::string textureFile =
android::base::pj({mSnapshotPath, std::string("textures_") + timeStamp + ".stex"});
saveSnapshot(snapshotFile, textureFile);
preloadReset();
preloadCheck();
loadSnapshot(snapshotFile, textureFile);
EXPECT_NE(m_context, EGL_NO_CONTEXT);
EXPECT_NE(m_surface, EGL_NO_SURFACE);
}
void SnapshotPreserveTest::doCheckedSnapshot() {
{
SCOPED_TRACE("during pre-snapshot default state check");
defaultStateCheck();
ASSERT_EQ(GL_NO_ERROR, gl->glGetError());
}
{
SCOPED_TRACE("during pre-snapshot state change");
stateChange();
ASSERT_EQ(GL_NO_ERROR, gl->glGetError());
}
{
SCOPED_TRACE("during pre-snapshot changed state check");
changedStateCheck();
}
SnapshotTest::doSnapshot([this] {
SCOPED_TRACE("during post-reset default state check");
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
defaultStateCheck();
});
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
{
SCOPED_TRACE("during post-snapshot changed state check");
changedStateCheck();
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
}
}
} // namespace gl
} // namespace gfxstream