1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "GLSnapshotTesting.h"
16
17 #include "base/PathUtils.h"
18 #include "base/StdioStream.h"
19 #include "base/System.h"
20 #include "base/testing/TestSystem.h"
21 #include "snapshot/TextureLoader.h"
22 #include "snapshot/TextureSaver.h"
23
24 #include "GLTestUtils.h"
25 #include "OpenGLTestContext.h"
26
27 #include <gtest/gtest.h>
28
29 #include <EGL/egl.h>
30 #include <GLES2/gl2.h>
31 #include <GLES3/gl31.h>
32
33 namespace emugl {
34
35 using android::base::StdioStream;
36 using android::snapshot::TextureLoader;
37 using android::snapshot::TextureSaver;
38
describeGlEnum(GLenum enumValue)39 std::string describeGlEnum(GLenum enumValue) {
40 std::ostringstream description;
41 description << "0x" << std::hex << enumValue
42 << " (" << getEnumString(enumValue) << ")";
43 return description.str();
44 }
45
46 template <class T>
compareValue(T expected,T actual,const std::string & description)47 testing::AssertionResult compareValue(T expected,
48 T actual,
49 const std::string& description) {
50 if (expected != actual) {
51 return testing::AssertionFailure()
52 << description << "\n\tvalue was:\t"
53 << testing::PrintToString(actual) << "\n\t expected:\t"
54 << testing::PrintToString(expected) << "\t";
55 }
56 return testing::AssertionSuccess();
57 }
58
59 template testing::AssertionResult compareValue<GLboolean>(GLboolean,
60 GLboolean,
61 const std::string&);
62 template testing::AssertionResult compareValue<GLint>(GLint,
63 GLint,
64 const std::string&);
65 template testing::AssertionResult compareValue<GLfloat>(GLfloat,
66 GLfloat,
67 const std::string&);
68
compareGlobalGlBoolean(const GLESv2Dispatch * gl,GLenum name,GLboolean expected)69 testing::AssertionResult compareGlobalGlBoolean(const GLESv2Dispatch* gl,
70 GLenum name,
71 GLboolean expected) {
72 GLboolean current;
73 gl->glGetBooleanv(name, ¤t);
74 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
75 return compareValue<GLboolean>(expected, current,
76 "GL global boolean mismatch for parameter " +
77 describeGlEnum(name) + ":");
78 }
79
compareGlobalGlInt(const GLESv2Dispatch * gl,GLenum name,GLint expected)80 testing::AssertionResult compareGlobalGlInt(const GLESv2Dispatch* gl,
81 GLenum name,
82 GLint expected) {
83 GLint current;
84 gl->glGetIntegerv(name, ¤t);
85 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
86 return compareValue<GLint>(expected, current,
87 "GL global int mismatch for parameter " +
88 describeGlEnum(name) + ":");
89 }
90
compareGlobalGlFloat(const GLESv2Dispatch * gl,GLenum name,GLfloat expected)91 testing::AssertionResult compareGlobalGlFloat(const GLESv2Dispatch* gl,
92 GLenum name,
93 GLfloat expected) {
94 GLfloat current;
95 gl->glGetFloatv(name, ¤t);
96 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
97 return compareValue<GLfloat>(expected, current,
98 "GL global float mismatch for parameter " +
99 describeGlEnum(name) + ":");
100 }
101
102 template <class T>
compareVector(const std::vector<T> & expected,const std::vector<T> & actual,const std::string & description)103 testing::AssertionResult compareVector(const std::vector<T>& expected,
104 const std::vector<T>& actual,
105 const std::string& description) {
106 std::stringstream message;
107 if (expected.size() != actual.size()) {
108 message << " (!) sizes do not match (actual " << actual.size()
109 << ", expected " << expected.size() << ")\n";
110 }
111
112 int mismatches = 0;
113 for (int i = 0; i < expected.size(); i++) {
114 if (i >= actual.size()) {
115 if (mismatches < 10) {
116 mismatches++;
117 message << " no match for:\t"
118 << testing::PrintToString(expected[i]) << "\n";
119 } else {
120 mismatches += expected.size() - i;
121 message << "\n nothing can match remaining elements.\n";
122 break;
123 }
124 } else if (expected[i] != actual[i]) {
125 mismatches++;
126 if (mismatches < 15) {
127 message << " at index " << i << ":\n\tvalue was:\t"
128 << testing::PrintToString(actual[i])
129 << "\n\t expected:\t"
130 << testing::PrintToString(expected[i]) << "\n";
131 } else if (mismatches == 15) {
132 message << " ... and indices " << i;
133 } else if (mismatches < 50) {
134 message << ", " << i;
135 } else if (mismatches == 50) {
136 message << ", etc...";
137 }
138 }
139 }
140 if (mismatches > 0) {
141 return testing::AssertionFailure()
142 << description << " had " << mismatches << " mismatches.\n"
143 << " expected: " << testing::PrintToString(expected) << "\n"
144 << " actual: " << testing::PrintToString(actual) << "\n"
145 << message.str() << " \n";
146 }
147 return testing::AssertionSuccess();
148 }
149
150 template testing::AssertionResult compareVector<GLboolean>(
151 const std::vector<GLboolean>&,
152 const std::vector<GLboolean>&,
153 const std::string&);
154 template testing::AssertionResult compareVector<GLint>(
155 const std::vector<GLint>&,
156 const std::vector<GLint>&,
157 const std::string&);
158 template testing::AssertionResult compareVector<GLfloat>(
159 const std::vector<GLfloat>&,
160 const std::vector<GLfloat>&,
161 const std::string&);
162
compareGlobalGlBooleanv(const GLESv2Dispatch * gl,GLenum name,const std::vector<GLboolean> & expected,GLuint size)163 testing::AssertionResult compareGlobalGlBooleanv(
164 const GLESv2Dispatch* gl,
165 GLenum name,
166 const std::vector<GLboolean>& expected,
167 GLuint size) {
168 std::vector<GLboolean> current;
169 current.resize(std::max(size, static_cast<GLuint>(expected.size())));
170 gl->glGetBooleanv(name, ¤t[0]);
171 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
172 return compareVector<GLboolean>(
173 expected, current,
174 "GL global booleanv parameter " + describeGlEnum(name));
175 }
176
compareGlobalGlIntv(const GLESv2Dispatch * gl,GLenum name,const std::vector<GLint> & expected,GLuint size)177 testing::AssertionResult compareGlobalGlIntv(const GLESv2Dispatch* gl,
178 GLenum name,
179 const std::vector<GLint>& expected,
180 GLuint size) {
181 std::vector<GLint> current;
182 current.resize(std::max(size, static_cast<GLuint>(expected.size())));
183 gl->glGetIntegerv(name, ¤t[0]);
184 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
185 return compareVector<GLint>(
186 expected, current,
187 "GL global intv parameter " + describeGlEnum(name));
188 }
189
compareGlobalGlFloatv(const GLESv2Dispatch * gl,GLenum name,const std::vector<GLfloat> & expected,GLuint size)190 testing::AssertionResult compareGlobalGlFloatv(
191 const GLESv2Dispatch* gl,
192 GLenum name,
193 const std::vector<GLfloat>& expected,
194 GLuint size) {
195 std::vector<GLfloat> current;
196 current.resize(std::max(size, static_cast<GLuint>(expected.size())));
197 gl->glGetFloatv(name, ¤t[0]);
198 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
199 return compareVector<GLfloat>(
200 expected, current,
201 "GL global floatv parameter " + describeGlEnum(name));
202 }
203
SetUp()204 void SnapshotTest::SetUp() {
205 GLTest::SetUp();
206 mTestSystem.getTempRoot()->makeSubDir("Snapshots");
207 mSnapshotPath = mTestSystem.getTempRoot()->makeSubPath("Snapshots");
208 }
209
saveSnapshot(const std::string streamFile,const std::string textureFile)210 void SnapshotTest::saveSnapshot(const std::string streamFile,
211 const std::string textureFile) {
212 const EGLDispatch* egl = LazyLoadedEGLDispatch::get();
213
214 std::unique_ptr<StdioStream> m_stream(new StdioStream(
215 android_fopen(streamFile.c_str(), "wb"), StdioStream::kOwner));
216 auto egl_stream = static_cast<EGLStream>(m_stream.get());
217 std::unique_ptr<TextureSaver> m_texture_saver(new TextureSaver(StdioStream(
218 android_fopen(textureFile.c_str(), "wb"), StdioStream::kOwner)));
219
220 egl->eglPreSaveContext(m_display, m_context, egl_stream);
221 egl->eglSaveAllImages(m_display, egl_stream, &m_texture_saver);
222
223 egl->eglSaveContext(m_display, m_context, egl_stream);
224
225 // Skip saving a bunch of FrameBuffer's fields
226 // Skip saving colorbuffers
227 // Skip saving window surfaces
228
229 egl->eglSaveConfig(m_display, m_config, egl_stream);
230
231 // Skip saving a bunch of process-owned objects
232
233 egl->eglPostSaveContext(m_display, m_context, egl_stream);
234
235 m_stream->close();
236 m_texture_saver->done();
237 }
238
loadSnapshot(const std::string streamFile,const std::string textureFile)239 void SnapshotTest::loadSnapshot(const std::string streamFile,
240 const std::string textureFile) {
241 const EGLDispatch* egl = LazyLoadedEGLDispatch::get();
242
243 std::unique_ptr<StdioStream> m_stream(new StdioStream(
244 android_fopen(streamFile.c_str(), "rb"), StdioStream::kOwner));
245 auto egl_stream = static_cast<EGLStream>(m_stream.get());
246 std::shared_ptr<TextureLoader> m_texture_loader(
247 new TextureLoader(StdioStream(android_fopen(textureFile.c_str(), "rb"),
248 StdioStream::kOwner)));
249
250 egl->eglLoadAllImages(m_display, egl_stream, &m_texture_loader);
251
252 EGLint contextAttribs[5] = {EGL_CONTEXT_CLIENT_VERSION, 3,
253 EGL_CONTEXT_MINOR_VERSION_KHR, 0, EGL_NONE};
254
255 m_context = egl->eglLoadContext(m_display, &contextAttribs[0], egl_stream);
256 m_config = egl->eglLoadConfig(m_display, egl_stream);
257 m_surface = pbufferSurface(m_display, m_config, kTestSurfaceSize[0],
258 kTestSurfaceSize[0]);
259 egl->eglPostLoadAllImages(m_display, egl_stream);
260
261 m_stream->close();
262 m_texture_loader->join();
263 egl->eglMakeCurrent(m_display, m_surface, m_surface, m_context);
264 }
265
preloadReset()266 void SnapshotTest::preloadReset() {
267 GLTest::TearDown();
268 GLTest::SetUp();
269 }
270
__anonc7d861d00102null271 void SnapshotTest::doSnapshot(std::function<void()> preloadCheck = [] {}) {
272 std::string timeStamp =
273 std::to_string(android::base::getUnixTimeUs());
274 std::string snapshotFile =
275 android::base::pj({mSnapshotPath, std::string("snapshot_") + timeStamp + ".snap"});
276 std::string textureFile =
277 android::base::pj({mSnapshotPath, std::string("textures_") + timeStamp + ".stex"});
278
279 saveSnapshot(snapshotFile, textureFile);
280
281 preloadReset();
282 preloadCheck();
283
284 loadSnapshot(snapshotFile, textureFile);
285
286 EXPECT_NE(m_context, EGL_NO_CONTEXT);
287 EXPECT_NE(m_surface, EGL_NO_SURFACE);
288 }
289
doCheckedSnapshot()290 void SnapshotPreserveTest::doCheckedSnapshot() {
291 {
292 SCOPED_TRACE("during pre-snapshot default state check");
293 defaultStateCheck();
294 ASSERT_EQ(GL_NO_ERROR, gl->glGetError());
295 }
296 {
297 SCOPED_TRACE("during pre-snapshot state change");
298 stateChange();
299 ASSERT_EQ(GL_NO_ERROR, gl->glGetError());
300 }
301 {
302 SCOPED_TRACE("during pre-snapshot changed state check");
303 changedStateCheck();
304 }
305 SnapshotTest::doSnapshot([this] {
306 SCOPED_TRACE("during post-reset default state check");
307 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
308 defaultStateCheck();
309 });
310 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
311 {
312 SCOPED_TRACE("during post-snapshot changed state check");
313 changedStateCheck();
314 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
315 }
316 }
317
318 } // namespace emugl
319