You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
12 KiB
319 lines
12 KiB
// 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 "base/PathUtils.h"
|
|
#include "base/StdioStream.h"
|
|
#include "base/System.h"
|
|
#include "base/testing/TestSystem.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 emugl {
|
|
|
|
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, ¤t);
|
|
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, ¤t);
|
|
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
|
|
return compareValue<GLint>(expected, current,
|
|
"GL global int mismatch for parameter " +
|
|
describeGlEnum(name) + ":");
|
|
}
|
|
|
|
testing::AssertionResult compareGlobalGlFloat(const GLESv2Dispatch* gl,
|
|
GLenum name,
|
|
GLfloat expected) {
|
|
GLfloat current;
|
|
gl->glGetFloatv(name, ¤t);
|
|
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, ¤t[0]);
|
|
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
|
|
return compareVector<GLboolean>(
|
|
expected, current,
|
|
"GL global booleanv parameter " + describeGlEnum(name));
|
|
}
|
|
|
|
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, ¤t[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, ¤t[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<EGLStream>(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<EGLStream>(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 emugl
|