|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <eikenv.h> |
|
19 #include <bautils.h> |
|
20 #include <coemain.h> |
|
21 #include <w32std.h> |
|
22 #include <gdi.h> |
|
23 #include <e32math.h> |
|
24 |
|
25 #include "uiacceltk/huiEnv.h" // Class definition |
|
26 #include <ecom/implementationinformation.h> |
|
27 #include "huirenderplugin.h" |
|
28 #include "uiacceltk/huiStatic.h" |
|
29 #include "alf/alfconstants.h" |
|
30 #include "huirendersurface.h" |
|
31 #include "uiacceltk/huiDisplay.h" |
|
32 #include "huirosterimpl.h" |
|
33 #include "uiacceltk/huiScheduler.h" |
|
34 #include "uiacceltk/huiTextureManager.h" |
|
35 #include "uiacceltk/huiControlGroup.h" |
|
36 #include "huivisualfactory.h" |
|
37 #include "uiacceltk/huiS60Skin.h" |
|
38 #include "uiacceltk/huiEvent.h" |
|
39 #include "uiacceltk/huiRoster.h" |
|
40 #include "uiacceltk/huiUtil.h" |
|
41 #include "uiacceltk/huipanic.h" |
|
42 #include "uiacceltk/huiTextStyleManager.h" |
|
43 #include "huistatictlsdata.h" |
|
44 #include "uiacceltk/HuiThemeManager.h" |
|
45 |
|
46 |
|
47 /* Constants */ |
|
48 const TInt KHuiEnvDefaultNormalRefreshIntervalMs = 40; |
|
49 |
|
50 /* If there is idle between frames, this is how much we can use as overdrive max cpu utilisation */ |
|
51 const TUint KHuiEnvMaxCpuTimeOverdriveMaxValue = 100; |
|
52 |
|
53 /* If max cpu usage has been set below this value, overdrive is not used because there is probaply a good reson |
|
54 for a low max cpu usage value */ |
|
55 const TUint KHuiEnvMaxCpuTimeOverdriveLowerThreshold = 50; |
|
56 |
|
57 // @todo: this should be obsolete once scheduling can adapt to CPU load |
|
58 const TInt KHuiEnvDefaultBusyRefreshIntervalMs = 2 * KHuiEnvDefaultNormalRefreshIntervalMs; |
|
59 |
|
60 /** Threshold number for refreshes that don't have any effect. When exceeded, |
|
61 refresh is paused. */ |
|
62 const TInt KIdleRefreshCountThreshold = 3; |
|
63 |
|
64 |
|
65 // to get around nasty ownership problem with texture manager interface |
|
66 void NullTextureManagerPtr(TAny* aPtrToPtr) |
|
67 { |
|
68 if (aPtrToPtr) |
|
69 { |
|
70 CHuiTextureManager** ptr = (CHuiTextureManager**)aPtrToPtr; |
|
71 *ptr = 0; |
|
72 } |
|
73 } |
|
74 |
|
75 |
|
76 void CHuiEnv::SetTextureManager(CHuiTextureManager& aManager) |
|
77 { |
|
78 iTextureManager = &aManager; |
|
79 } |
|
80 |
|
81 |
|
82 |
|
83 void CHuiEnv::StopRefresh() |
|
84 { |
|
85 HUI_DEBUG(_L("CHuiEnv::StopRefresh()")); |
|
86 if(iPeriodic) |
|
87 { |
|
88 iPeriodic->Cancel(); |
|
89 } |
|
90 } |
|
91 |
|
92 void CHuiEnv::RemoveDisplay(CHuiDisplay& aDisplay) |
|
93 { |
|
94 // Actually this is "DestroyDisplay" |
|
95 |
|
96 /** @todo Make a proper observer. */ |
|
97 TInt index = iDisplays.Find(&aDisplay); |
|
98 if(index >= 0) |
|
99 { |
|
100 iDisplays.Remove(index); |
|
101 } |
|
102 else |
|
103 { |
|
104 HUI_DEBUG1(_L("HuiEnv::DestroyContext: display %x not in array"), &aDisplay); |
|
105 } |
|
106 index = iOverlaidDisplays.Find(&aDisplay); |
|
107 if(index >= 0) |
|
108 { |
|
109 iOverlaidDisplays.Remove(index); |
|
110 } |
|
111 |
|
112 // Notify the current renderer of the changed number of displays |
|
113 TRAP_IGNORE(iRenderer->NotifyDisplayCountL(iDisplays.Count())) |
|
114 |
|
115 #ifdef _DEBUG |
|
116 HUI_DEBUG(_L(" Contents of iDisplays:")); |
|
117 for(TInt i = 0; i < iDisplays.Count(); ++i) |
|
118 { |
|
119 HUI_DEBUG2(_L(" %i: %x"), i, iDisplays[i]); |
|
120 } |
|
121 #endif |
|
122 } |
|
123 |
|
124 |
|
125 void CHuiEnv::AdvanceTime(TReal32 aElapsedTime) |
|
126 { |
|
127 HUI_DEBUGF( _L("CHuiEnv::AdvanceTime() - Started") ) |
|
128 |
|
129 TUint usedMaxCPUUtilization = iMaxCPUUtilization; |
|
130 |
|
131 // Calculate cpu values based on null thread cpu usage between frames. |
|
132 // Values will be updated at the end of the frame. |
|
133 if (iIdleCPUValueMonitored) |
|
134 { |
|
135 TTime currentTime; |
|
136 currentTime.HomeTime(); |
|
137 |
|
138 TTimeIntervalMicroSeconds cputime; |
|
139 iIdleCPUValueThread.GetCpuTime(cputime); |
|
140 |
|
141 TInt64 cpudelta = cputime.Int64() - iIdleCPUValue; |
|
142 TInt64 timedelta = currentTime.Int64() - iIdleCPUValuePreviousTime; |
|
143 |
|
144 // If null thread was runnign between frames, we could use more cpu if needed. |
|
145 if (cpudelta && timedelta) |
|
146 { |
|
147 // Calculate how much we want ! |
|
148 if (iMaxCPUUtilization > KHuiEnvMaxCpuTimeOverdriveLowerThreshold && |
|
149 iMaxCPUUtilization < KHuiEnvMaxCpuTimeOverdriveMaxValue ) |
|
150 { |
|
151 usedMaxCPUUtilization += (KHuiEnvMaxCpuTimeOverdriveMaxValue - iMaxCPUUtilization) * cpudelta/timedelta; |
|
152 |
|
153 // Sanity check just in case cpu/time measurements are not accurate |
|
154 if (usedMaxCPUUtilization > KHuiEnvMaxCpuTimeOverdriveMaxValue) |
|
155 { |
|
156 usedMaxCPUUtilization = KHuiEnvMaxCpuTimeOverdriveMaxValue; |
|
157 } |
|
158 } |
|
159 } |
|
160 } |
|
161 |
|
162 if(aElapsedTime > 0) |
|
163 { |
|
164 // Let the scheduler know that time has passed. It will possible animate |
|
165 // visuals and perform actions, causing dirty regions in the display. |
|
166 iScheduler->AdvanceTime(aElapsedTime); |
|
167 |
|
168 // Scheduled command might have released the environment. |
|
169 if(iState == EReleased) |
|
170 { |
|
171 HUI_DEBUG(_L("CHuiEnv::AdvanceTime() - Environment released when executing scheduled commands. AdvanceTime cancelled.")); |
|
172 return; |
|
173 } |
|
174 |
|
175 // Let the texture manager know that time has passed. It will update any |
|
176 // animated textures. |
|
177 iTextureManager->AdvanceTime(aElapsedTime); |
|
178 } |
|
179 |
|
180 // Check for no input for long time. |
|
181 TTime now = CHuiStatic::Time(); |
|
182 TTimeIntervalSeconds seconds = 0; |
|
183 now.SecondsFrom(iLastInputTime, seconds); |
|
184 if(seconds.Int() >= iIdleThreshold && !iInputIdleIsActive) |
|
185 { |
|
186 HUI_DEBUG1(_L("CHuiEnv::AdvanceTime() - No input received within %i seconds. Going to idle."), iIdleThreshold); |
|
187 iInputIdleIsActive = ETrue; |
|
188 |
|
189 // Idle state begins. |
|
190 TRAPD(err, SendIdleL(ETrue)); |
|
191 if(err != KErrNone) |
|
192 { |
|
193 // @todo Log error? |
|
194 } |
|
195 |
|
196 // Switching to idle state might have released the environment. |
|
197 if(iState == EReleased) |
|
198 { |
|
199 HUI_DEBUG(_L("CHuiEnv::AdvanceTime() - Environment released when switching to idle state. AdvanceTime cancelled.")); |
|
200 return; |
|
201 } |
|
202 } |
|
203 |
|
204 TBool somethingUpdated = EFalse; |
|
205 |
|
206 // Refresh all displays. |
|
207 TInt i; |
|
208 const TInt displayCount = iDisplays.Count(); |
|
209 RArray<TBool> displayRefreshed( displayCount ? displayCount : 1 ); |
|
210 for(i = 0; i < iDisplays.Count(); ++i) |
|
211 { |
|
212 displayRefreshed.Append(EFalse); |
|
213 if(iRefreshMode == EHuiRefreshModeForced || iDisplays[i]->IsDirty()) |
|
214 { |
|
215 MakeCurrent(*iDisplays[i]); |
|
216 |
|
217 HUI_DEBUGF1( _L("CHuiEnv::AdvanceTime() - Refreshing display %i"), i ) |
|
218 TBool updated = iDisplays[i]->Refresh(); |
|
219 displayRefreshed[i] = updated; |
|
220 if(updated) |
|
221 { |
|
222 somethingUpdated = ETrue; |
|
223 } |
|
224 } |
|
225 } |
|
226 |
|
227 TBool continueRefresh = ETrue; |
|
228 |
|
229 if(somethingUpdated) |
|
230 { |
|
231 HUI_DEBUGF( _L("CHuiEnv::AdvanceTime() - Swap buffers") ) |
|
232 |
|
233 SwapBuffers(displayRefreshed); |
|
234 iIdleRefreshCount = 0; |
|
235 // Clear change flags now that the frames are complete. |
|
236 for(i = 0; i < iDisplays.Count(); ++i) |
|
237 { |
|
238 // Clear changed for an off screen display only if the buffer has new content. |
|
239 // Index is ok becacause displayRefreshed array was defined using size of iDisplays array |
|
240 if (displayRefreshed[i]) |
|
241 { |
|
242 iDisplays[i]->ClearChanged(); |
|
243 } |
|
244 } |
|
245 } |
|
246 else if(iScheduler->PendingCount() == 0) |
|
247 { |
|
248 // But if there are scheduled commands, let's make sure they'll get |
|
249 // executed at the right time. They might get badly delayed if the |
|
250 // refresh wasn't occuring. |
|
251 |
|
252 /** @todo Use a separate timer for the scheduler? */ |
|
253 |
|
254 // Nothing happened during the display refreshing. |
|
255 iIdleRefreshCount++; |
|
256 |
|
257 // If this occurs too often, pause refresh automatically. |
|
258 if(iIdleRefreshCount > KIdleRefreshCountThreshold) |
|
259 { |
|
260 if (iFpsCounterThreshold && iMillisecondFromFPSUpdate && iFrames) |
|
261 { |
|
262 TBuf<16> numBuf; |
|
263 TReal fps = 1000*(TReal)iFrames/iMillisecondFromFPSUpdate; |
|
264 numBuf.AppendNum(fps, TRealFormat(5,2)); |
|
265 User::InfoPrint(numBuf); |
|
266 iFrames = 0; |
|
267 iMillisecondFromFPSUpdate = 0; |
|
268 } |
|
269 |
|
270 PauseRefresh(); |
|
271 continueRefresh = EFalse; |
|
272 } |
|
273 } |
|
274 else |
|
275 { |
|
276 // for PC lint |
|
277 } |
|
278 |
|
279 displayRefreshed.Close(); // Not needed any more |
|
280 |
|
281 // Clear change flags of all control groups now that the refresh has |
|
282 // been completed for all displays. |
|
283 // |
|
284 // DEPRECATE: |
|
285 // This should be removed when control opacities are deprecated! |
|
286 // Controls shouldn't need change flags because change flags are |
|
287 // only for the refresh. |
|
288 // |
|
289 for(i = 0; i < iLoadedGroups.Count(); ++i) |
|
290 { |
|
291 iLoadedGroups[i]->ClearChanged(); |
|
292 } |
|
293 |
|
294 iTextureManager->ClearChangedTextures(); |
|
295 |
|
296 if (continueRefresh) |
|
297 { |
|
298 // Refresh rate adjustment |
|
299 if (usedMaxCPUUtilization) |
|
300 { |
|
301 TUint millisecondsUsedInRefresh = CHuiStatic::MilliSecondsSinceUpdateTime(); |
|
302 |
|
303 TUint totalLoopTime = (millisecondsUsedInRefresh * 100) / usedMaxCPUUtilization; |
|
304 |
|
305 if (totalLoopTime >= iRefreshIntervalTarget) |
|
306 { |
|
307 iRefreshInterval = (millisecondsUsedInRefresh*(100-usedMaxCPUUtilization))/usedMaxCPUUtilization; |
|
308 StartRefresh(iRefreshInterval); |
|
309 } |
|
310 else if (iRefreshIntervalTarget != iRefreshInterval) |
|
311 { |
|
312 iRefreshInterval = iRefreshIntervalTarget; |
|
313 StartRefresh(iRefreshInterval); |
|
314 } |
|
315 else |
|
316 { |
|
317 // otherwise just let the periodic run as it already has good interval set |
|
318 } |
|
319 } |
|
320 |
|
321 if (iFpsCounterThreshold && iMillisecondFromFPSUpdate > iFpsCounterThreshold) |
|
322 { |
|
323 TBuf<16> numBuf; |
|
324 TReal fps = 1000*(TReal)iFrames/iMillisecondFromFPSUpdate; |
|
325 numBuf.AppendNum(fps, TRealFormat(5,2)); |
|
326 User::InfoPrint(numBuf); |
|
327 iFrames = 0; |
|
328 iMillisecondFromFPSUpdate = 0; |
|
329 } |
|
330 } |
|
331 |
|
332 iCurrentDisplay = NULL; // informs the egosystem that the drawing is done. |
|
333 CHuiStatic::ReportNewFrame(); |
|
334 |
|
335 // Store cpu value conters of null thread. Values will be used at the start of the next frame. |
|
336 if (iIdleCPUValueMonitored) |
|
337 { |
|
338 TTime currentTime; |
|
339 currentTime.HomeTime(); |
|
340 |
|
341 TTimeIntervalMicroSeconds cputime; |
|
342 iIdleCPUValueThread.GetCpuTime(cputime); |
|
343 |
|
344 // Store as previous values |
|
345 iIdleCPUValue = cputime.Int64(); |
|
346 iIdleCPUValuePreviousTime = currentTime.Int64(); |
|
347 } |
|
348 |
|
349 HUI_DEBUGF( _L("CHuiEnv::AdvanceTime() - Exited") ); |
|
350 } |
|
351 |
|
352 void CHuiEnv::NotifyInputReceivedL(const THuiEvent& aEvent) |
|
353 { |
|
354 ContinueRefresh(); |
|
355 |
|
356 if(aEvent.IsKeyEvent() || aEvent.IsPointerEvent()) |
|
357 { |
|
358 if(iInputIdleIsActive) |
|
359 { |
|
360 HUI_DEBUG(_L("CHuiEnv::NotifyInputReceivedL() - Got key/pointer input! Idle state ends!")); |
|
361 // Idle state ends. |
|
362 SendIdleL(EFalse); |
|
363 } |
|
364 |
|
365 iLastInputTime = CHuiStatic::Time(); |
|
366 iInputIdleIsActive = EFalse; |
|
367 } |
|
368 } |
|
369 |
|
370 |
|
371 void CHuiEnv::SendIdleL(TBool aIdleBegins) |
|
372 { |
|
373 CHuiDisplay* display = NULL; |
|
374 |
|
375 if(iDisplays.Count() != 0) |
|
376 { |
|
377 // If we have any displays, pass the first one. |
|
378 display = iDisplays[0]; |
|
379 } |
|
380 |
|
381 THuiEvent idleEvent(display, |
|
382 aIdleBegins ? THuiEvent::ETypeIdleBegin : |
|
383 THuiEvent::ETypeIdleEnd); |
|
384 BroadcastEventL(idleEvent); |
|
385 } |
|
386 |
|
387 |
|
388 |
|
389 void CHuiEnv::MakeCurrent(const CHuiDisplay& aDisplay) const |
|
390 { |
|
391 aDisplay.RenderSurface().MakeCurrent(); |
|
392 iCurrentDisplay = const_cast<CHuiDisplay*>(&aDisplay); |
|
393 } |
|
394 |
|
395 |
|
396 void CHuiEnv::SwapBuffers(const RArray<TBool>& aDisplayRefreshed) |
|
397 { |
|
398 /** @todo This may not work as expected when multiple displays are |
|
399 being used. */ |
|
400 |
|
401 if (iFpsCounterThreshold) |
|
402 { |
|
403 iFrames++; |
|
404 iMillisecondFromFPSUpdate += iRefreshIntervalReal; |
|
405 } |
|
406 |
|
407 /** @todo Only swap the visible displays. */ |
|
408 |
|
409 for(TInt i = 0; i < iDisplays.Count(); ++i) |
|
410 { |
|
411 // Index should ok becacause displayRefreshed array was defined using size of iDisplays array |
|
412 // This function should only be called from inside CHuiEnv even if it is public. |
|
413 // At least it is not exported. |
|
414 if (aDisplayRefreshed[i] |
|
415 && (iDisplays[i]->DisplayType() != CHuiDisplay::EDisplayOffScreenBuffer) |
|
416 && (iDisplays[i]->ScreenBufferObserver() == NULL)) |
|
417 { |
|
418 iDisplays[i]->RenderSurface().SwapBuffers(); |
|
419 } |
|
420 } |
|
421 } |
|
422 |
|
423 |
|
424 void CHuiEnv::CreateResourceReaderLC(TResourceReader& aReader, TInt aResourceId) const |
|
425 { |
|
426 CCoeEnv* coe = CCoeEnv::Static(); |
|
427 if (!coe) |
|
428 { |
|
429 User::Leave(KErrNotSupported); |
|
430 } |
|
431 coe->CreateResourceReaderLC(aReader, aResourceId); |
|
432 } |
|
433 |
|
434 void CHuiEnv::TextureLoadingCompleted(CHuiTexture& /*aTexture*/, |
|
435 TInt /*aTextureId*/, |
|
436 TInt /*aErrorCode*/) |
|
437 { |
|
438 // Texture changed flag has been set, visuals should redraw |
|
439 // changed textures automatically. |
|
440 } |
|
441 |
|
442 |
|
443 void CHuiEnv::TextureManagerStateChanged(const CHuiTextureManager& aManager) |
|
444 { |
|
445 if(aManager.State() == CHuiTextureManager::EIdle) |
|
446 { |
|
447 StartRefresh(iRefreshIntervalTarget); |
|
448 } |
|
449 else if (!iMaxCPUUtilization) |
|
450 { // only use busy refresh interwall if adaptive scheduling is not enabled |
|
451 StartRefresh(KHuiEnvDefaultBusyRefreshIntervalMs); |
|
452 } |
|
453 else |
|
454 { |
|
455 // for PC lint |
|
456 } |
|
457 } |
|
458 |
|
459 CHuiScheduler& CHuiEnv::Scheduler() |
|
460 { |
|
461 return *iScheduler; |
|
462 } |
|
463 |
|
464 |
|
465 TInt CHuiEnv::ReportAction(const THuiActionCommand& aCommand) |
|
466 { |
|
467 TInt resultError = KErrNone; |
|
468 |
|
469 for(TInt i = 0; i < iActionObservers.Count(); ++i) |
|
470 { |
|
471 TRAPD(err, iActionObservers[i].HandleActionL(aCommand)); |
|
472 if(err != KErrNone && resultError == KErrNone) |
|
473 { |
|
474 // The first error code is returned. |
|
475 resultError = err; |
|
476 } |
|
477 } |
|
478 return resultError; |
|
479 } |
|
480 |
|
481 |
|
482 RPointerArray<CHuiDisplay> CHuiEnv::Displays() const |
|
483 { |
|
484 return iDisplays; |
|
485 } |
|
486 |
|
487 |
|
488 |
|
489 void CHuiEnv::SetTimeFromLastUpdate(TUint aTimeFromLastUpdate) |
|
490 { |
|
491 iRefreshIntervalReal = aTimeFromLastUpdate; |
|
492 } |
|
493 |
|
494 |
|
495 CHuiDisplay* CHuiEnv::CurrentDisplay() const |
|
496 { |
|
497 return iCurrentDisplay; |
|
498 } |
|
499 |
|
500 TBool CHuiEnv::CPUTimeSupported() |
|
501 { |
|
502 TTimeIntervalMicroSeconds time; |
|
503 TInt err = RThread().GetCpuTime(time); |
|
504 |
|
505 if (err == KErrNone && time.Int64() > 0) |
|
506 { |
|
507 return ETrue; |
|
508 } |
|
509 else |
|
510 { |
|
511 return EFalse; |
|
512 } |
|
513 } |
|
514 |
|
515 TBool CHuiEnv::OpenHandleToIdleCPUValueThread() |
|
516 { |
|
517 // find the kernel process and then the null thread |
|
518 TFindProcess fp(_L("ekern.exe*")); |
|
519 |
|
520 TFullName kernelName; |
|
521 if (fp.Next(kernelName) == KErrNone) |
|
522 { |
|
523 // process found, append null thread identifier |
|
524 kernelName.Append(_L("::Null")); |
|
525 |
|
526 // find the thread |
|
527 TFindThread ft(kernelName); |
|
528 |
|
529 TFullName threadName; |
|
530 if (ft.Next(threadName) == KErrNone) |
|
531 { |
|
532 // open instance to the thread |
|
533 if (iIdleCPUValueThread.Open(threadName) != KErrNone) |
|
534 { |
|
535 return EFalse; |
|
536 } |
|
537 } |
|
538 } |
|
539 else |
|
540 { |
|
541 // process not found |
|
542 return EFalse; |
|
543 } |
|
544 |
|
545 // success! |
|
546 return ETrue; |
|
547 } |
|
548 |
|
549 void CHuiEnv::CloseHandleToIdleCPUValueThread() |
|
550 { |
|
551 iIdleCPUValueThread.Close(); |
|
552 } |
|
553 |
|
554 CHuiCanvasTextureCache& CHuiEnv::CanvasTextureCache() const |
|
555 { |
|
556 return *iCanvasTextureCache; |
|
557 } |