|
1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "tinterleaving.h" |
|
17 #include "tdirectgdieglcontent_client.h" |
|
18 #include "tdirectgdieglcontent_clientserver.h" |
|
19 #include <graphics/sgimage_sw.h> |
|
20 #include <graphics/directgdiimagetarget.h> |
|
21 #include <graphics/directgdidriver.h> |
|
22 #include <e32math.h> |
|
23 |
|
24 /** |
|
25 A utility class used by the templated function CleanupResetAndDestroyPushL() to create |
|
26 a TCleanupItem item that will perform a ResetAndDestroy() operation on the class T type object. |
|
27 Used for cleanup RPointerArray objects. |
|
28 */ |
|
29 template <class T> class CleanupResetAndDestroy |
|
30 { |
|
31 public: |
|
32 static void PushL(T &aRef) |
|
33 { |
|
34 CleanupStack::PushL(TCleanupItem(&ResetAndDestroy, &aRef)); |
|
35 }; |
|
36 |
|
37 private: |
|
38 static void ResetAndDestroy(TAny *aPtr) |
|
39 { |
|
40 (STATIC_CAST(T *, aPtr))->ResetAndDestroy(); |
|
41 }; |
|
42 }; |
|
43 |
|
44 /** |
|
45 Helper function for pushing CleanupResetAndDestroy object onto CleanupStack. |
|
46 */ |
|
47 template <class T> void CleanupResetAndDestroyPushL(T &aRef) |
|
48 { |
|
49 CleanupResetAndDestroy<T>::PushL(aRef); |
|
50 }; |
|
51 |
|
52 CTInterleaving::CTInterleaving() |
|
53 { |
|
54 SetTestStepName(KTDirectGdiInterleavingStep); |
|
55 } |
|
56 |
|
57 CTInterleaving::~CTInterleaving() |
|
58 { |
|
59 } |
|
60 |
|
61 /** |
|
62 @SYMTestCaseID |
|
63 GRAPHICS-DIRECTGDI-INTERLEAVING-0001 |
|
64 |
|
65 @SYMPREQ |
|
66 PREQ39 |
|
67 |
|
68 @SYMREQ |
|
69 REQ9188 |
|
70 REQ9189 |
|
71 REQ9196 |
|
72 REQ9213 |
|
73 REQ9227 |
|
74 REQ9195 |
|
75 REQ9201 |
|
76 REQ9202 |
|
77 REQ9222 |
|
78 REQ9223 |
|
79 REQ9236 |
|
80 REQ9237 |
|
81 |
|
82 @SYMTestCaseDesc |
|
83 Test synchronized drawing with egl content |
|
84 |
|
85 @SYMTestPriority |
|
86 High |
|
87 |
|
88 @SYMTestStatus |
|
89 Implemented |
|
90 |
|
91 @SYMTestActions Establish connection to EglContentServer. |
|
92 Render frame synchronized with EGL content in following way: |
|
93 - Draw multicolor checkboard to gdi target |
|
94 - Get EGL content sgimage id from EglContentServer in synchronous mode. |
|
95 - Using obtained id create DirectGdi image source and draw it on target with DrawResource() method. |
|
96 - Draw semi-transparent vertical bars to gdi target. |
|
97 Repeat above steps a few times. |
|
98 |
|
99 @SYMTestExpectedResults |
|
100 Valid set of bitmaps should be created. |
|
101 These bitmaps should be the same as a reference bitmaps. |
|
102 */ |
|
103 void CTInterleaving::TestSyncL() |
|
104 { |
|
105 _LIT(KTestName, "Interleaving-Sync"); //test case name |
|
106 // start EGL content server |
|
107 REglContentSession eglContentSession; |
|
108 TESTNOERRORL(eglContentSession.Connect()); |
|
109 CleanupClosePushL(eglContentSession); |
|
110 |
|
111 // render few frames synchronized with egl content |
|
112 for(TInt frame=0; frame<10; frame++) |
|
113 { |
|
114 ResetGc(); |
|
115 |
|
116 DrawDirectGdiCheckboardToTarget(); |
|
117 TESTNOERRORL(iGc->GetError()); |
|
118 |
|
119 // get egl content sgimage id |
|
120 TSgDrawableId imageId; |
|
121 TESTNOERRORL(eglContentSession.GetSyncImage(imageId)); |
|
122 |
|
123 DrawImageToTargetL(imageId); |
|
124 TESTNOERRORL(iGc->GetError()); |
|
125 |
|
126 DrawDirectGdiBarsToTarget(); |
|
127 TESTNOERRORL(iGc->GetError()); |
|
128 |
|
129 // save target |
|
130 TBuf<30> testName; |
|
131 testName.Append(KTestName); |
|
132 testName.AppendNum(frame); |
|
133 TESTNOERRORL(CTDirectGdiStepBase::WriteTargetOutput(iTestParams, testName)); |
|
134 } |
|
135 |
|
136 CleanupStack::PopAndDestroy(&eglContentSession); |
|
137 } |
|
138 |
|
139 /** |
|
140 @SYMTestCaseID |
|
141 GRAPHICS-DIRECTGDI-INTERLEAVING-0002 |
|
142 |
|
143 @SYMPREQ |
|
144 PREQ39 |
|
145 |
|
146 @SYMREQ |
|
147 REQ9188 |
|
148 REQ9189 |
|
149 REQ9196 |
|
150 REQ9213 |
|
151 REQ9227 |
|
152 REQ9195 |
|
153 REQ9201 |
|
154 REQ9202 |
|
155 REQ9222 |
|
156 REQ9223 |
|
157 REQ9236 |
|
158 REQ9237 |
|
159 |
|
160 @SYMTestCaseDesc |
|
161 Test asynchronous drawing of egl content |
|
162 |
|
163 @SYMTestPriority |
|
164 High |
|
165 |
|
166 @SYMTestStatus |
|
167 Implemented |
|
168 |
|
169 @SYMTestActions Establish connection to EglContentServer. |
|
170 Render frame in following way: |
|
171 - Draw multicolor checkboard to gdi target |
|
172 - Get EGL content sgimage id and frame number from EglContentServer in asynchronous mode. |
|
173 - Using obtained id create DirectGdi image source and draw it on target with DrawResource() method. |
|
174 - Draw semi-transparent vertical bars to gdi target. |
|
175 - Find currently created target bitmap in stored bitmaps array. |
|
176 Use frame number obtained from EglContentServer as an array index. |
|
177 If it not exists in this array, store it. Otherwise compare it with stored one pixel by pixel. |
|
178 Test fails when this comparison fails. |
|
179 EglContentServer produce ten different frames, |
|
180 so for proper testing repeat above steps at least fifty times. |
|
181 |
|
182 @SYMTestExpectedResults Valid set of bitmaps should be created. |
|
183 These bitmaps should be the same as a reference bitmaps. |
|
184 */ |
|
185 void CTInterleaving::TestAsyncL(TBool aDebug) |
|
186 { |
|
187 _LIT(KTestName, "Interleaving-Async"); //test case name |
|
188 |
|
189 // start EGL content server |
|
190 REglContentSession eglContentSession; |
|
191 TESTNOERRORL(eglContentSession.Connect()); |
|
192 CleanupClosePushL(eglContentSession); |
|
193 |
|
194 // prepare array to store frames |
|
195 RPointerArray<CFbsBitmap> storedBitmaps(KEglContentAsyncMaxFrames); |
|
196 CleanupResetAndDestroyPushL(storedBitmaps); |
|
197 for(TInt i=0; i<KEglContentAsyncMaxFrames; i++) |
|
198 { |
|
199 storedBitmaps.AppendL(NULL); |
|
200 } |
|
201 |
|
202 // prepare line buffers for bitmap comparision |
|
203 TInt bufSize = iGdiTarget->SizeInPixels().iWidth*4; // buffer is 32-bit BGRA |
|
204 HBufC8* lineBuf1 = HBufC8::NewLC(bufSize); |
|
205 HBufC8* lineBuf2 = HBufC8::NewLC(bufSize); |
|
206 |
|
207 // With the EGL context continuously being generated, we cannot guarantee that |
|
208 // all the frames will be retrieved. |
|
209 // However, in theory, the probability of picking each frame can be |
|
210 // approximated to a uniform discrete distribution. |
|
211 // So we can work out how many times we need to pick a frame to get all |
|
212 // the unique frames 99% of the time. |
|
213 // This works out to be approximately 65 times for 10 unique frames. |
|
214 const TInt KNumTriesToGetAllFrames = 65; |
|
215 |
|
216 if(!aDebug) |
|
217 { |
|
218 WARN_PRINTF1(_L("Note: Due to the nature of the test, it may be possible that not all the frames are captured")); |
|
219 } |
|
220 TInt64 seed = (TInt64)User::FastCounter(); |
|
221 |
|
222 // render few frames asynchronous from egl content |
|
223 for(TInt frame=0; frame<KNumTriesToGetAllFrames; frame++) |
|
224 { |
|
225 ResetGc(); |
|
226 |
|
227 DrawDirectGdiCheckboardToTarget(); |
|
228 TESTNOERRORL(iGc->GetError()); |
|
229 |
|
230 // get egl content sgimage id |
|
231 TSgDrawableId imageId; |
|
232 TInt fnum; |
|
233 if(!aDebug) |
|
234 { |
|
235 TESTNOERRORL(eglContentSession.GetAsyncImage(imageId, fnum)); |
|
236 // Due to the nature of the asynchronous test, where the frames are |
|
237 // continuously being rendered and we can only dip in and pick the last |
|
238 // rendered frame, it cannot be guaranteed that all the unique frames will |
|
239 // be retrieved. This could be due to this loop and the EGL content |
|
240 // generation running in harmony. To try to overcome this, introduce |
|
241 // a random delay between 0 and 3 frames (0 seconds to 100 milliseconds - |
|
242 // EGL content generated at 30fps). |
|
243 TInt delay = Math::Rand(seed)%100000; |
|
244 User::After(delay); |
|
245 } |
|
246 else |
|
247 { |
|
248 TESTNOERRORL(eglContentSession.GetAsyncImageDebug(imageId, fnum)); |
|
249 } |
|
250 |
|
251 DrawImageToTargetL(imageId); |
|
252 TESTNOERRORL(iGc->GetError()); |
|
253 |
|
254 DrawDirectGdiBarsToTarget(); |
|
255 TESTNOERRORL(iGc->GetError()); |
|
256 |
|
257 iGdiTarget->Finish(); |
|
258 CFbsBitmap* targetBitmap = iGdiTarget->GetTargetFbsBitmapL(); |
|
259 |
|
260 if(storedBitmaps[fnum] != NULL) |
|
261 { |
|
262 // compare with previously stored bitmap |
|
263 TUint32 cmpMask; // BGRA mask |
|
264 if((iTestParams.iTargetPixelFormat == EUidPixelFormatXRGB_8888) || |
|
265 (iTestParams.iTargetPixelFormat == EUidPixelFormatXRGB_4444)) |
|
266 { |
|
267 cmpMask = 0xffffff00; // exclude unused pixel part from comparision |
|
268 } |
|
269 else |
|
270 { |
|
271 cmpMask = 0xffffffff; |
|
272 } |
|
273 |
|
274 TInt width = targetBitmap->SizeInPixels().iWidth; |
|
275 TInt height = targetBitmap->SizeInPixels().iHeight; |
|
276 |
|
277 TPtr8 linePtr1(lineBuf1->Des()); |
|
278 TPtr8 linePtr2(lineBuf2->Des()); |
|
279 |
|
280 TBool pass = ETrue; |
|
281 |
|
282 // compare pixel by pixel |
|
283 for(TInt line=0; line<height; line++) |
|
284 { |
|
285 targetBitmap->GetScanLine(linePtr1, TPoint(0, line), width, EColor16MA); |
|
286 storedBitmaps[fnum]->GetScanLine(linePtr2, TPoint(0, line), width, EColor16MA); |
|
287 |
|
288 const TUint32* pPtr1 = (const TUint32*)linePtr1.Ptr(); |
|
289 const TUint32* pPtr2 = (const TUint32*)linePtr2.Ptr(); |
|
290 for(TInt x=0; x<width; x++) |
|
291 { |
|
292 if((pPtr1[x] & cmpMask) != (pPtr2[x] & cmpMask)) |
|
293 { |
|
294 pass = EFalse; |
|
295 break; // break inner loop |
|
296 } |
|
297 } |
|
298 |
|
299 if(!pass) |
|
300 break; // break outer loop if test failed in inner loop |
|
301 } |
|
302 |
|
303 if(!pass) |
|
304 { |
|
305 INFO_PRINTF2(_L("Frame %d not equal to previous one"), fnum); |
|
306 TEST(pass); |
|
307 } |
|
308 } |
|
309 else |
|
310 { |
|
311 // copy and store target bitmap |
|
312 storedBitmaps[fnum] = new(ELeave) CFbsBitmap(); |
|
313 TESTL(storedBitmaps[fnum]->Create(targetBitmap->SizeInPixels(), targetBitmap->DisplayMode()) == KErrNone); |
|
314 Mem::Copy((TUint8*)storedBitmaps[fnum]->DataAddress(), |
|
315 (TUint8*)targetBitmap->DataAddress(), |
|
316 storedBitmaps[fnum]->DataStride()*storedBitmaps[fnum]->SizeInPixels().iHeight); |
|
317 |
|
318 // save target |
|
319 TBuf<40> testName; |
|
320 testName.Append(KTestName); |
|
321 if(aDebug) |
|
322 { |
|
323 _LIT(KDebug, "Debug"); |
|
324 testName.Append(KDebug); |
|
325 } |
|
326 testName.AppendNum(fnum); |
|
327 TESTNOERRORL(CTDirectGdiStepBase::WriteTargetOutput(iTestParams, testName)); |
|
328 } |
|
329 } |
|
330 |
|
331 CleanupStack::PopAndDestroy(4, &eglContentSession); |
|
332 } |
|
333 |
|
334 /** |
|
335 Draw multicolor checkboard to gdi target. |
|
336 */ |
|
337 void CTInterleaving::DrawDirectGdiCheckboardToTarget() |
|
338 { |
|
339 const TInt width = iGdiTarget->SizeInPixels().iWidth; |
|
340 const TInt height = iGdiTarget->SizeInPixels().iHeight; |
|
341 const TInt rwidth = 13; |
|
342 const TInt rheight = 13; |
|
343 |
|
344 iGc->SetPenStyle(DirectGdi::ENullPen); |
|
345 iGc->SetBrushStyle(DirectGdi::ESolidBrush); |
|
346 |
|
347 TInt color = 0; |
|
348 for(TInt y=0; y<height; y+=rheight) |
|
349 { |
|
350 for(TInt x=0; x<width; x+=rwidth) |
|
351 { |
|
352 iGc->SetBrushColor(KColor16Table[color%16]); |
|
353 color++; |
|
354 iGc->DrawRect(TRect(TPoint(x, y), TSize(rwidth, rheight))); |
|
355 } |
|
356 } |
|
357 } |
|
358 |
|
359 /** |
|
360 Draw semi-transparent vertical bars to gdi target. |
|
361 */ |
|
362 void CTInterleaving::DrawDirectGdiBarsToTarget() |
|
363 { |
|
364 TPoint pos(40, 40); |
|
365 TSize size(iGdiTarget->SizeInPixels()); |
|
366 size -= TSize(80, 80); |
|
367 TSize barSize(size.iWidth/10, size.iHeight); |
|
368 |
|
369 iGc->SetPenStyle(DirectGdi::ENullPen); |
|
370 iGc->SetBrushStyle(DirectGdi::ESolidBrush); |
|
371 |
|
372 for(TInt i=0; i<size.iWidth; i+=barSize.iWidth) |
|
373 { |
|
374 TRgb color(255, 0, 0, 25*i/barSize.iWidth); |
|
375 iGc->SetBrushColor(color); |
|
376 iGc->DrawRect(TRect(pos+TPoint(i, 0), barSize)); |
|
377 } |
|
378 } |
|
379 |
|
380 /** |
|
381 Draw four rotated EGL images to gdi target. |
|
382 */ |
|
383 void CTInterleaving::DrawImageToTargetL(TSgDrawableId aImageId) |
|
384 { |
|
385 // prepare sgimage from id |
|
386 RSgImage image; |
|
387 TESTNOERRORL(image.Open(aImageId)); |
|
388 CleanupClosePushL(image); |
|
389 |
|
390 TESTNOERRORL(CDirectGdiDriver::Open()); |
|
391 |
|
392 CDirectGdiDriver* dgdiDriver = CDirectGdiDriver::Static(); |
|
393 TESTL(dgdiDriver != NULL); |
|
394 CleanupClosePushL(*dgdiDriver); |
|
395 |
|
396 // prepare DirectGdi image source |
|
397 RDirectGdiDrawableSource source(*dgdiDriver); |
|
398 TESTNOERRORL(source.Create(image)); |
|
399 CleanupClosePushL(source); |
|
400 |
|
401 // draw sgimage resorce on gdi target |
|
402 TDrawableSourceAndEquivRotatedBmps imageSource; |
|
403 imageSource.iDrawableSrc = &source; |
|
404 TPoint pos(20, 20); |
|
405 TSize size(iGdiTarget->SizeInPixels()); |
|
406 size -= TSize(40, 40); |
|
407 size.iWidth /= 2; |
|
408 size.iHeight /= 2; |
|
409 TSize size2(size); |
|
410 size2 -= TSize(10, 10); |
|
411 |
|
412 iGc->DrawResource(TRect(pos, size2), imageSource); |
|
413 TESTNOERRORL(iGc->GetError()); |
|
414 iGc->DrawResource(TRect(pos+TPoint(size.iWidth, 0), size2), imageSource, DirectGdi::EGraphicsRotation90); |
|
415 TESTNOERRORL(iGc->GetError()); |
|
416 iGc->DrawResource(TRect(pos+TPoint(0, size.iHeight), size2), imageSource, DirectGdi::EGraphicsRotation180); |
|
417 TESTNOERRORL(iGc->GetError()); |
|
418 iGc->DrawResource(TRect(pos+size.AsPoint(), size2), imageSource, DirectGdi::EGraphicsRotation270); |
|
419 TESTNOERRORL(iGc->GetError()); |
|
420 |
|
421 CleanupStack::PopAndDestroy(3, &image); |
|
422 } |
|
423 |
|
424 /** |
|
425 Override of base class virtual |
|
426 @leave Gets system wide error code |
|
427 @return - TVerdict code |
|
428 */ |
|
429 TVerdict CTInterleaving::doTestStepPreambleL() |
|
430 { |
|
431 CTDirectGdiStepBase::doTestStepPreambleL(); |
|
432 return TestStepResult(); |
|
433 } |
|
434 |
|
435 /** |
|
436 Override of base class pure virtual |
|
437 Our implementation only gets called if the base class doTestStepPreambleL() did |
|
438 not leave. That being the case, the current test result value will be EPass. |
|
439 @leave Gets system wide error code |
|
440 @return TVerdict code |
|
441 */ |
|
442 TVerdict CTInterleaving::doTestStepL() |
|
443 { |
|
444 // Test for each target pixel format |
|
445 for(TInt targetPixelFormatIndex = iTargetPixelFormatArray.Count() - 1; targetPixelFormatIndex >= 0 ; targetPixelFormatIndex--) |
|
446 { |
|
447 iTestParams.iTargetPixelFormat = iTargetPixelFormatArray[targetPixelFormatIndex]; |
|
448 |
|
449 SetTargetL(iTestParams.iTargetPixelFormat, EOneContextOneTarget, TSize(400, 400)); |
|
450 RunTestsL(); |
|
451 } |
|
452 CloseTMSGraphicsStep(); |
|
453 return TestStepResult(); |
|
454 } |
|
455 |
|
456 /** |
|
457 Override of base class virtual |
|
458 @leave Gets system wide error code |
|
459 @return - TVerdict code |
|
460 */ |
|
461 TVerdict CTInterleaving::doTestStepPostambleL() |
|
462 { |
|
463 CTDirectGdiStepBase::doTestStepPostambleL(); |
|
464 return TestStepResult(); |
|
465 } |
|
466 |
|
467 /** |
|
468 Override of base class pure virtual |
|
469 Lists the tests to be run |
|
470 */ |
|
471 void CTInterleaving::RunTestsL() |
|
472 { |
|
473 if(iUseDirectGdi) |
|
474 { |
|
475 SetTestStepID(_L("GRAPHICS-DIRECTGDI-INTERLEAVING-0001")); |
|
476 TestSyncL(); |
|
477 RecordTestResultL(); |
|
478 SetTestStepID(_L("GRAPHICS-DIRECTGDI-INTERLEAVING-0002")); |
|
479 TestAsyncL(); |
|
480 RecordTestResultL(); |
|
481 SetTestStepID(_L("GRAPHICS-DIRECTGDI-INTERLEAVING-0002")); |
|
482 TestAsyncL(ETrue); |
|
483 RecordTestResultL(); |
|
484 } |
|
485 } |