/src/mozilla-central/gfx/tests/gtest/TestTextures.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* vim:set ts=2 sw=2 sts=2 et: */ |
2 | | /* Any copyright is dedicated to the Public Domain. |
3 | | * http://creativecommons.org/publicdomain/zero/1.0/ |
4 | | */ |
5 | | |
6 | | #include "gtest/gtest.h" |
7 | | #include "gmock/gmock.h" |
8 | | #include "TestLayers.h" |
9 | | |
10 | | #include "mozilla/gfx/2D.h" |
11 | | #include "mozilla/gfx/Tools.h" |
12 | | #include "mozilla/layers/BufferTexture.h" |
13 | | #include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild |
14 | | #include "mozilla/layers/TextureClient.h" |
15 | | #include "mozilla/layers/TextureHost.h" |
16 | | #include "mozilla/RefPtr.h" |
17 | | #include "gfx2DGlue.h" |
18 | | #include "gfxImageSurface.h" |
19 | | #include "gfxTypes.h" |
20 | | #include "ImageContainer.h" |
21 | | #include "mozilla/layers/ImageDataSerializer.h" |
22 | | |
23 | | using namespace mozilla; |
24 | | using namespace mozilla::gfx; |
25 | | using namespace mozilla::layers; |
26 | | |
27 | | /* |
28 | | * This test performs the following actions: |
29 | | * - creates a surface |
30 | | * - initialize a texture client with it |
31 | | * - serilaizes the texture client |
32 | | * - deserializes the data into a texture host |
33 | | * - reads the surface from the texture host. |
34 | | * |
35 | | * The surface in the end should be equal to the inital one. |
36 | | * This test is run for different combinations of texture types and |
37 | | * image formats. |
38 | | */ |
39 | | |
40 | | namespace mozilla { |
41 | | namespace layers { |
42 | | |
43 | | // fills the surface with values betwee 0 and 100. |
44 | 0 | void SetupSurface(gfxImageSurface* surface) { |
45 | 0 | int bpp = gfxASurface::BytePerPixelFromFormat(surface->Format()); |
46 | 0 | int stride = surface->Stride(); |
47 | 0 | uint8_t val = 0; |
48 | 0 | uint8_t* data = surface->Data(); |
49 | 0 | for (int y = 0; y < surface->Height(); ++y) { |
50 | 0 | for (int x = 0; x < surface->Height(); ++x) { |
51 | 0 | for (int b = 0; b < bpp; ++b) { |
52 | 0 | data[y*stride + x*bpp + b] = val; |
53 | 0 | if (val == 100) { |
54 | 0 | val = 0; |
55 | 0 | } else { |
56 | 0 | ++val; |
57 | 0 | } |
58 | 0 | } |
59 | 0 | } |
60 | 0 | } |
61 | 0 | } |
62 | | |
63 | | // return true if two surfaces contain the same data |
64 | | void AssertSurfacesEqual(gfxImageSurface* surface1, |
65 | | gfxImageSurface* surface2) |
66 | 0 | { |
67 | 0 | ASSERT_EQ(surface1->GetSize(), surface2->GetSize()); |
68 | 0 | ASSERT_EQ(surface1->Format(), surface2->Format()); |
69 | 0 |
|
70 | 0 | uint8_t* data1 = surface1->Data(); |
71 | 0 | uint8_t* data2 = surface2->Data(); |
72 | 0 | int stride1 = surface1->Stride(); |
73 | 0 | int stride2 = surface2->Stride(); |
74 | 0 | int bpp = gfxASurface::BytePerPixelFromFormat(surface1->Format()); |
75 | 0 |
|
76 | 0 | for (int y = 0; y < surface1->Height(); ++y) { |
77 | 0 | for (int x = 0; x < surface1->Width(); ++x) { |
78 | 0 | for (int b = 0; b < bpp; ++b) { |
79 | 0 | ASSERT_EQ(data1[y*stride1 + x*bpp + b], |
80 | 0 | data2[y*stride2 + x*bpp + b]); |
81 | 0 | } |
82 | 0 | } |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | | void AssertSurfacesEqual(SourceSurface* surface1, |
87 | | SourceSurface* surface2) |
88 | 0 | { |
89 | 0 | ASSERT_EQ(surface1->GetSize(), surface2->GetSize()); |
90 | 0 | ASSERT_EQ(surface1->GetFormat(), surface2->GetFormat()); |
91 | 0 |
|
92 | 0 | RefPtr<DataSourceSurface> dataSurface1 = surface1->GetDataSurface(); |
93 | 0 | RefPtr<DataSourceSurface> dataSurface2 = surface2->GetDataSurface(); |
94 | 0 | DataSourceSurface::MappedSurface map1; |
95 | 0 | DataSourceSurface::MappedSurface map2; |
96 | 0 | if (!dataSurface1->Map(DataSourceSurface::READ, &map1)) { |
97 | 0 | return; |
98 | 0 | } |
99 | 0 | if (!dataSurface2->Map(DataSourceSurface::READ, &map2)) { |
100 | 0 | dataSurface1->Unmap(); |
101 | 0 | return; |
102 | 0 | } |
103 | 0 | uint8_t* data1 = map1.mData; |
104 | 0 | uint8_t* data2 = map2.mData; |
105 | 0 | int stride1 = map1.mStride; |
106 | 0 | int stride2 = map2.mStride; |
107 | 0 | int bpp = BytesPerPixel(surface1->GetFormat()); |
108 | 0 | int width = surface1->GetSize().width; |
109 | 0 | int height = surface1->GetSize().height; |
110 | 0 |
|
111 | 0 | for (int y = 0; y < height; ++y) { |
112 | 0 | for (int x = 0; x < width; ++x) { |
113 | 0 | for (int b = 0; b < bpp; ++b) { |
114 | 0 | ASSERT_EQ(data1[y*stride1 + x*bpp + b], |
115 | 0 | data2[y*stride2 + x*bpp + b]); |
116 | 0 | } |
117 | 0 | } |
118 | 0 | } |
119 | 0 |
|
120 | 0 | dataSurface1->Unmap(); |
121 | 0 | dataSurface2->Unmap(); |
122 | 0 | } |
123 | | |
124 | | // Run the test for a texture client and a surface |
125 | 0 | void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) { |
126 | 0 |
|
127 | 0 | // client allocation |
128 | 0 | ASSERT_TRUE(texture->CanExposeDrawTarget()); |
129 | 0 |
|
130 | 0 | ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE)); |
131 | 0 | // client painting |
132 | 0 | RefPtr<DrawTarget> dt = texture->BorrowDrawTarget(); |
133 | 0 | RefPtr<SourceSurface> source = |
134 | 0 | gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface); |
135 | 0 | dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint()); |
136 | 0 |
|
137 | 0 | RefPtr<SourceSurface> snapshot = dt->Snapshot(); |
138 | 0 |
|
139 | 0 | AssertSurfacesEqual(snapshot, source); |
140 | 0 |
|
141 | 0 | dt = nullptr; // drop reference before calling Unlock() |
142 | 0 | texture->Unlock(); |
143 | 0 |
|
144 | 0 | // client serialization |
145 | 0 | SurfaceDescriptor descriptor; |
146 | 0 | ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor)); |
147 | 0 |
|
148 | 0 | ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t); |
149 | 0 |
|
150 | 0 | // host deserialization |
151 | 0 | RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator(); |
152 | 0 | RefPtr<TextureHost> host = CreateBackendIndependentTextureHost(descriptor, deallocator, |
153 | 0 | LayersBackend::LAYERS_NONE, |
154 | 0 | texture->GetFlags()); |
155 | 0 |
|
156 | 0 | ASSERT_TRUE(host.get() != nullptr); |
157 | 0 | ASSERT_EQ(host->GetFlags(), texture->GetFlags()); |
158 | 0 |
|
159 | 0 | // host read |
160 | 0 |
|
161 | 0 | // XXX - this can fail because lock tries to upload the texture but it needs a |
162 | 0 | // Compositor to do that. We could add a DummyComposior for testing but I am |
163 | 0 | // not sure it'll be worth it. Maybe always test against a BasicCompositor, |
164 | 0 | // but the latter needs a widget... |
165 | 0 | if (host->Lock()) { |
166 | 0 | RefPtr<mozilla::gfx::DataSourceSurface> hostDataSurface = host->GetAsSurface(); |
167 | 0 |
|
168 | 0 | DataSourceSurface::ScopedMap map(hostDataSurface, DataSourceSurface::READ); |
169 | 0 | RefPtr<gfxImageSurface> hostSurface = |
170 | 0 | new gfxImageSurface(map.GetData(), |
171 | 0 | hostDataSurface->GetSize(), |
172 | 0 | map.GetStride(), |
173 | 0 | SurfaceFormatToImageFormat(hostDataSurface->GetFormat())); |
174 | 0 | AssertSurfacesEqual(surface, hostSurface.get()); |
175 | 0 | host->Unlock(); |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | | // Same as above, for YCbCr surfaces |
180 | 0 | void TestTextureClientYCbCr(TextureClient* client, PlanarYCbCrData& ycbcrData) { |
181 | 0 | client->Lock(OpenMode::OPEN_READ_WRITE); |
182 | 0 | UpdateYCbCrTextureClient(client, ycbcrData); |
183 | 0 | client->Unlock(); |
184 | 0 |
|
185 | 0 | // client serialization |
186 | 0 | SurfaceDescriptor descriptor; |
187 | 0 | ASSERT_TRUE(client->ToSurfaceDescriptor(descriptor)); |
188 | 0 |
|
189 | 0 | ASSERT_EQ(descriptor.type(), SurfaceDescriptor::TSurfaceDescriptorBuffer); |
190 | 0 | auto bufferDesc = descriptor.get_SurfaceDescriptorBuffer(); |
191 | 0 | ASSERT_EQ(bufferDesc.desc().type(), BufferDescriptor::TYCbCrDescriptor); |
192 | 0 | auto ycbcrDesc = bufferDesc.desc().get_YCbCrDescriptor(); |
193 | 0 | ASSERT_EQ(ycbcrDesc.ySize(), ycbcrData.mYSize); |
194 | 0 | ASSERT_EQ(ycbcrDesc.cbCrSize(), ycbcrData.mCbCrSize); |
195 | 0 | ASSERT_EQ(ycbcrDesc.stereoMode(), ycbcrData.mStereoMode); |
196 | 0 |
|
197 | 0 | // host deserialization |
198 | 0 | RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator(); |
199 | 0 | RefPtr<TextureHost> textureHost = CreateBackendIndependentTextureHost(descriptor, deallocator, |
200 | 0 | LayersBackend::LAYERS_NONE, |
201 | 0 | client->GetFlags()); |
202 | 0 |
|
203 | 0 | RefPtr<BufferTextureHost> host = static_cast<BufferTextureHost*>(textureHost.get()); |
204 | 0 |
|
205 | 0 | ASSERT_TRUE(host.get() != nullptr); |
206 | 0 | ASSERT_EQ(host->GetFlags(), client->GetFlags()); |
207 | 0 |
|
208 | 0 | // host read |
209 | 0 |
|
210 | 0 | if (host->Lock()) { |
211 | 0 | // This will work iff the compositor is not BasicCompositor |
212 | 0 | ASSERT_EQ(host->GetFormat(), mozilla::gfx::SurfaceFormat::YUV); |
213 | 0 | host->Unlock(); |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | } // namespace layers |
218 | | } // namespace mozilla |
219 | | |
220 | 0 | TEST(Layers, TextureSerialization) { |
221 | 0 | // the test is run on all the following image formats |
222 | 0 | gfxImageFormat formats[3] = { |
223 | 0 | SurfaceFormat::A8R8G8B8_UINT32, |
224 | 0 | SurfaceFormat::X8R8G8B8_UINT32, |
225 | 0 | SurfaceFormat::A8, |
226 | 0 | }; |
227 | 0 |
|
228 | 0 | for (int f = 0; f < 3; ++f) { |
229 | 0 | RefPtr<gfxImageSurface> surface = new gfxImageSurface(IntSize(400,300), formats[f]); |
230 | 0 | SetupSurface(surface.get()); |
231 | 0 | AssertSurfacesEqual(surface, surface); |
232 | 0 |
|
233 | 0 | auto texData = BufferTextureData::Create(surface->GetSize(), |
234 | 0 | gfx::ImageFormatToSurfaceFormat(surface->Format()), |
235 | 0 | gfx::BackendType::CAIRO, LayersBackend::LAYERS_NONE, |
236 | 0 | TextureFlags::DEALLOCATE_CLIENT, ALLOC_DEFAULT, nullptr |
237 | 0 | ); |
238 | 0 | ASSERT_TRUE(!!texData); |
239 | 0 |
|
240 | 0 | RefPtr<TextureClient> client = new TextureClient( |
241 | 0 | texData, TextureFlags::DEALLOCATE_CLIENT, nullptr |
242 | 0 | ); |
243 | 0 |
|
244 | 0 | TestTextureClientSurface(client, surface); |
245 | 0 |
|
246 | 0 | // XXX - Test more texture client types. |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | 0 | TEST(Layers, TextureYCbCrSerialization) { |
251 | 0 | RefPtr<gfxImageSurface> ySurface = new gfxImageSurface(IntSize(400,300), SurfaceFormat::A8); |
252 | 0 | RefPtr<gfxImageSurface> cbSurface = new gfxImageSurface(IntSize(200,150), SurfaceFormat::A8); |
253 | 0 | RefPtr<gfxImageSurface> crSurface = new gfxImageSurface(IntSize(200,150), SurfaceFormat::A8); |
254 | 0 | SetupSurface(ySurface.get()); |
255 | 0 | SetupSurface(cbSurface.get()); |
256 | 0 | SetupSurface(crSurface.get()); |
257 | 0 |
|
258 | 0 | PlanarYCbCrData clientData; |
259 | 0 | clientData.mYChannel = ySurface->Data(); |
260 | 0 | clientData.mCbChannel = cbSurface->Data(); |
261 | 0 | clientData.mCrChannel = crSurface->Data(); |
262 | 0 | clientData.mYSize = ySurface->GetSize(); |
263 | 0 | clientData.mPicSize = ySurface->GetSize(); |
264 | 0 | clientData.mCbCrSize = cbSurface->GetSize(); |
265 | 0 | clientData.mYStride = ySurface->Stride(); |
266 | 0 | clientData.mCbCrStride = cbSurface->Stride(); |
267 | 0 | clientData.mStereoMode = StereoMode::MONO; |
268 | 0 | clientData.mYUVColorSpace = YUVColorSpace::BT601; |
269 | 0 | clientData.mBitDepth = 8; |
270 | 0 | clientData.mYSkip = 0; |
271 | 0 | clientData.mCbSkip = 0; |
272 | 0 | clientData.mCrSkip = 0; |
273 | 0 | clientData.mCrSkip = 0; |
274 | 0 | clientData.mPicX = 0; |
275 | 0 | clientData.mPicX = 0; |
276 | 0 |
|
277 | 0 | uint32_t namespaceId = 1; |
278 | 0 | ImageBridgeChild::InitSameProcess(namespaceId); |
279 | 0 |
|
280 | 0 | RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton(); |
281 | 0 | static int retry = 5; |
282 | 0 | while(!imageBridge->IPCOpen() && retry) { |
283 | 0 | // IPDL connection takes time especially in slow testing environment, like |
284 | 0 | // VM machines. Here we added retry mechanism to wait for IPDL connnection. |
285 | | #ifdef XP_WIN |
286 | | Sleep(1); |
287 | | #else |
288 | | sleep(1); |
289 | 0 | #endif |
290 | 0 | retry--; |
291 | 0 | } |
292 | 0 |
|
293 | 0 | // Skip this testing if IPDL connection is not ready |
294 | 0 | if (!retry && !imageBridge->IPCOpen()) { |
295 | 0 | return; |
296 | 0 | } |
297 | 0 | |
298 | 0 | RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(imageBridge, clientData.mYSize, clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride, |
299 | 0 | StereoMode::MONO, YUVColorSpace::BT601, |
300 | 0 | 8, TextureFlags::DEALLOCATE_CLIENT); |
301 | 0 |
|
302 | 0 | TestTextureClientYCbCr(client, clientData); |
303 | 0 |
|
304 | 0 | // XXX - Test more texture client types. |
305 | 0 | } |