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