Compare commits

...

564 Commits

Author SHA1 Message Date
Siana Gearz
d4cfc10609 Starting linux64 support 2012-02-25 08:19:58 +01:00
Siana Gearz
0bdf54e6d9 This fixes Copy+Wear from object 2012-02-24 03:27:24 +01:00
Siana Gearz
10af9d64f2 One day this will be a memory stat 2012-02-24 03:26:06 +01:00
Siana Gearz
a76b0cb9da Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-02-23 18:51:49 +01:00
Aleric Inglewood
0450358df3 Clean up after Linden Lab 2012-02-23 18:50:28 +01:00
Siana Gearz
a632010b95 Boost: target Windows XP strictly 2012-02-23 15:35:50 +01:00
Siana Gearz
a1038f3972 Merge git://github.com/LightDrake/SingularityViewer 2012-02-23 03:06:19 +01:00
Siana Gearz
93f6ea1a07 Pure linden boost on windows 2012-02-23 03:05:31 +01:00
Siana Gearz
d721fd65b3 Bugfix repackager 2012-02-23 03:03:15 +01:00
Siana Gearz
e207f2e025 Fix scroll buttons on dark skin 2012-02-23 02:29:59 +01:00
Siana Gearz
be37d9b5a9 Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-02-22 18:02:04 +01:00
Siana Gearz
e39327c017 Merge branch 'master' of git://github.com/TighMacFanatic/SingularityViewer 2012-02-22 18:01:45 +01:00
Drake Arconis
edbc22961e Revert "Further OS X changes."
This reverts commit 803011582c.
2012-02-22 05:49:38 -05:00
Drake Arconis
3250725aae Annoying Crash Fix.
This fixes that thing where saving scripts in deleted object makes
viewer go FFFFFF.
2012-02-21 10:56:27 -05:00
Drake Arconis
803011582c Further OS X changes. 2012-02-21 10:53:47 -05:00
Aleric Inglewood
2206f68129 Fix webkit on Ubuntu
WARNING("Plugin"): LLPluginInstance::load: apr_dso_load of
*/viewer-linux-i686-releasesse2/newview/packaged/bin/llplugin/libmedia_plugin_webkit.so
failed with error 20019 , additional info string:
*/viewer-linux-i686-releasesse2/newview/packaged/bin/llplugin/libmedia_plugin_webkit.so:
undefined symbol: XSyncIntsToValue

Apparently our webkit package is underlinked, it doesn't
include libXext which is needed for symbol XSyncIntsToValue.
Both, Fritigern and Inusaito had this problem on Ubuntu.
This patch adds the library to the webkit plugin as a work-
around.
2012-02-21 15:52:57 +01:00
Drake Arconis
6388324552 Revert "This works for now. Need to do more testing."
This reverts commit c6d6eed7d3.
2012-02-21 09:27:10 -05:00
TighMacFanatic
89f35779b4 Fixed Windlight presets not loading user created files. 2012-02-20 16:37:34 -05:00
Drake Arconis
ad1e3f1d2d Merge remote-tracking branch 'upstream/master' 2012-02-20 07:19:35 -05:00
Siana Gearz
9477a5f32f Fix code bug making notifications unreadable colour 2012-02-20 05:38:57 +01:00
Siana Gearz
8e71cf8e6e Revert "Replace all NotifyCautionBoxColor's and AlertCautionBoxColor's that were using tan with black, like Emerald skin's NotifyCautionBoxColor."
This reverts commit 0fef1407e3.
2012-02-20 05:05:57 +01:00
Siana Gearz
ea7396d2cf Crashfix audio panel in skin 2012-02-20 05:05:53 +01:00
Siana Gearz
5c7debecc8 Extra info from j2c decoder in case of cache corruption 2012-02-20 03:47:06 +01:00
Drake Arconis
c6d6eed7d3 This works for now. Need to do more testing. 2012-02-19 19:43:53 -05:00
Aleric Inglewood
39f8deb498 Merge remote-tracking branch 'siana/master' 2012-02-19 23:54:59 +01:00
Drake Arconis
665569dfc7 I forgot why I made these. 2012-02-19 17:07:42 -05:00
Drake Arconis
19f72cf967 Merge remote-tracking branch 'upstream/master' 2012-02-19 16:16:34 -05:00
Drake Arconis
a4363de59e Updated APR and Zlib prebuilts. 2012-02-19 15:59:47 -05:00
Siana Gearz
c3aa321d51 Fix classic skin 2012-02-19 21:43:31 +01:00
Siana Gearz
9b268c3654 Fixing the crash in grid list 2012-02-19 20:07:17 +01:00
Drake Arconis
925c42294f OS X Builds now.
Fixed building on OS X.
Fixed line endings.
Need to find better way to do fmodwrapper thing.
2012-02-19 11:30:27 -05:00
Drake Arconis
23811df3cf Adding to Ignore. 2012-02-19 06:43:04 -05:00
Siana Gearz
f7cc9c4d33 Hopefully more correctness in grid manager editor 2012-02-19 00:42:23 +01:00
Aleric Inglewood
66ccff964b Comment out old Google Translate support and as such remove the need for JSON.
Note that on standalone, json headers are installed in json/,
no idea why in the packages this isn't the case... But anyway,
it isn't used at all anymore now so it doesn't really matter.

I didn't remove the json package for non-standalone and the
downloading thereof... (I can't test non-standalone).
2012-02-18 16:45:11 +01:00
Aleric Inglewood
0c82b8bd3a Merge remote-tracking branch 'siana/master' 2012-02-18 02:23:26 +01:00
Aleric Inglewood
4e8b69206c LLViewerRegion: now compiles and seems to work. 2012-02-18 02:22:49 +01:00
Aleric Inglewood
0a7fc0e65e LLViewerRegion WIP: brought LLTrans up to speed with V3. 2012-02-18 02:22:49 +01:00
Aleric Inglewood
787e03e821 LLViewerRegion WIP: Sync indra/llmessage/llregionflags.h with V3
It seems a few flags disappeared; adjusted the code accordingly.
Should do the same as V3 now.
2012-02-18 02:22:49 +01:00
Aleric Inglewood
f381b0c2dd LLViewerRegion WIP: Use LLViewerRegion::getCapAPI
Viewer 3 uses this in LLAgent::setStartPosition and
copy_inventory_from_notecard; so I brought those
up to speed with V3.

The viewer now uses LLViewerRegion::getCapAPI in all
the same place as V3.
2012-02-18 02:22:49 +01:00
Aleric Inglewood
b8744b9e6a LLViewerRegion WIP: Added all headers that are dragged in.
Most in particular llevents.h, which comes along with
the demand that the old events in llevent.h are put
in a namespace LLOldEvents. Made all changes necessary
to compile the rest of the code again (without changing
the actual code: it's still using the old events).

This patch also removes LLStopWhenHandled and LLStandardSignal
from indra/llui/llnotifications.h because those are
moved to llevents.h. That seems to be the only change
to indra/llui/llnotifications.h that isn't floater related,
so I left the rest of that file alone.
2012-02-18 02:22:49 +01:00
Aleric Inglewood
59ee1a1041 LLViewerRegion WIP: Initial copy of llviewerregion.cpp
This is basically the changes in viewer 3 to this file,
except that I left alone:

1) We still have a cloud layer (mCloudLayer)
2) LLViewerRegion::regionFlagsToString still adds comma's
   and a lot more flags.
3) We still have the extra accessor LLViewerRegion::getNetDetailsForLCD()
4) The actual capabilities requested in LLViewerRegionImpl::buildCapabilityNames
   have been left alone and still request the same caps as before.
5) LLViewerRegion::meshUploadEnabled and LLViewerRegion::meshRezEnabled
   have been left alone. They seems to do more work in our case,
   checking if we have the capability SimulatorFeatures, and doing
   something smart when that is not the case.
2012-02-18 02:22:48 +01:00
Aleric Inglewood
88d0983e37 LLViewerRegion WIP: this should compile now. 2012-02-18 02:22:48 +01:00
Aleric Inglewood
3680e022fc LLViewerRegion: WIP - header changes and anticipated compile fixes
Does not compile now.
llviewerregion.cpp wasn't touched.
Other changes elsewhere in the code (like in llstartup.cpp) might still be needed.
2012-02-18 02:22:27 +01:00
Siana Gearz
a642b725e0 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-02-17 16:00:00 +01:00
Siana Gearz
dbc3a8c65f Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-02-17 15:59:16 +01:00
Siana Gearz
86cafba0df Linux fixes by Tofu 2012-02-17 15:57:21 +01:00
Siana Gearz
60ded2ad14 Initial invisiprim support in deferred 2012-02-17 05:52:41 +01:00
Lirusaito
fa560a7081 Fixed up a few settings' strings to display more correct descriptions for ascent settings in debug settings. 2012-02-16 23:10:20 -05:00
Lirusaito
5023f23cc4 Alt-H for Teleport History, like Phoenix. 2012-02-16 20:09:30 -05:00
Lirusaito
0fef1407e3 Replace all NotifyCautionBoxColor's and AlertCautionBoxColor's that were using tan with black, like Emerald skin's NotifyCautionBoxColor.
Black is a far better color for white text to sit on than the former
harsh tan. This will allow users to read notifications like the ones
from changing server versions.
2012-02-16 19:40:36 -05:00
Lirusaito
a09c505fa0 The search line_editor in the status bar needed to be about 20% taller. 2012-02-16 18:28:50 -05:00
Lirusaito
023909b8e0 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-02-16 16:00:27 -05:00
Siana Gearz
60081d7ed5 Melanie's fix of FMOD on OS X 2012-02-16 13:23:00 +01:00
Lirusaito
0b5235d054 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-02-15 23:13:52 -05:00
Siana Gearz
071554acae Prim blacklisting on derender from Phoenix 2012-02-16 05:02:52 +01:00
Aleric Inglewood
f05343dec1 New libcwd debug channel CAPS. 2012-02-16 02:30:14 +01:00
Lirusaito
a403cef605 Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-02-15 20:20:49 -05:00
Aleric Inglewood
9b4b234c12 Allow AIStateMachine::kill to be called while not running.
This can be used for example when a parent statemachine has
a child statemachine and wants to kill() it upon finish.
2012-02-16 01:10:48 +01:00
Aleric Inglewood
42518dbfaa This fixes a compile error under certain conditions for me. 2012-02-15 20:22:41 +01:00
Aleric Inglewood
4e700a5808 Merge remote-tracking branch 'siana/master' 2012-02-15 20:20:56 +01:00
Siana Gearz
647af6c15a RLVa: fix drag and drop on inventory items 2012-02-15 16:28:17 +01:00
Siana Gearz
b91ba258e1 Make double click more humane on X11 2012-02-15 14:45:09 +01:00
Siana Gearz
fe579d6e4b Activity/spam radar enhancement, from Avination 2012-02-15 14:07:03 +01:00
Lirusaito
6d3044b448 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-02-14 23:48:13 -05:00
Lirusaito
db8564bb3c Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-02-14 23:48:08 -05:00
Siana Gearz
28f73d1037 Fix large chat font being same as medium 2012-02-15 04:41:51 +01:00
Siana Gearz
dd9196efac Fix texture preview showing useless inventory UUID 2012-02-15 01:07:41 +01:00
Siana Gearz
c542c7bdbb Work around pink textures on ATI with 3.1<=GL<=3.2 2012-02-15 01:01:11 +01:00
Siana Gearz
f8aad35775 Skinning and cloth settings realigned properly 2012-02-14 21:29:22 +01:00
Siana Gearz
657c8e8729 Warn if sending imcomplete appearence 2012-02-14 21:06:59 +01:00
Siana Gearz
a451a8f566 SH-2963 Fix for highlight transparent not highlighting 100% transparent objects. 2012-02-14 16:45:31 +01:00
Aleric Inglewood
0e5b1a8fa7 Allow to LD_PRELOAD (any) library just for singularity-do-not-run-directly
This adds support for running:

AI_PRELOAD="/path/to/libfoo.so" ./singularity

Note that setting and exporting LD_PRELOAD there doesn't work,
because then those libs are loaded for every executable that
is being run after, including the plugins.

This patch also removes the use of tcmalloc from the plugins...
Before it exported LD_PRELOAD, using tcmalloc for all the command
line tools up till and including running the singularity binary,
as well as for the plugins. Now, it only preloads the tcmalloc
libraries for singularity-do-not-run-directly.
2012-02-14 16:32:12 +01:00
Lirusaito
e922207797 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-02-13 18:12:23 -05:00
Lirusaito
e447bd98a5 Fix the panel in floater_script_preview.xml to look more like its cousin in floater_live_lsleditor.xml, this removes the awkward border. 2012-02-13 17:47:46 -05:00
Siana Gearz
a0e9e8d4b2 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-02-13 20:41:04 +01:00
Siana Gearz
09817403ff Merge branch 'master' of git://github.com/TighMacFanatic/SingularityViewer 2012-02-13 20:40:37 +01:00
Siana Gearz
8f6ef05429 Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-02-13 20:40:04 +01:00
Siana Gearz
4cbf259ab3 Experimental tablet and pen support 2012-02-13 20:38:44 +01:00
Siana Gearz
042622b5db Fix a logic typo in windlight floater 2012-02-13 15:56:31 +01:00
Lirusaito
eff72e15cd Added icons for Sky and Water buttons for wlf remote. 2012-02-12 17:32:28 -05:00
Lirusaito
aacd33ada7 Added left and right buttons to make the wlf remote better. 2012-02-12 17:09:30 -05:00
Lirusaito
fe00eed401 Added new Windlight components to the wlf remote, and reorganized the bottom area for said parts to fit. (Icons for Sky and Water buttons, not included)
Addition of Previous/Next preset buttons, buttons to open advanced water and sky floaters, daylight image and slider,
Added two checboxes, one for region settings to be used, the other for them to always be used, the latter acts strangely but may work... former is fine.
removal of texts since this will be covered by the icons on for advanced floaters
2012-02-12 17:09:17 -05:00
Lirusaito
000d00579b Add the control name AscentAutoCloseOOC to the checkbox in preferences, so it actually is connected to the autoclosing setting's code. 2012-02-12 17:09:09 -05:00
Lirusaito
63dd39ea51 Merge branch 'master' of git://github.com/TighMacFanatic/SingularityViewer 2012-02-11 23:35:26 -05:00
TighMacFanatic
87cb88f2f7 Fixed CMake issue with Git version when building locally. 2012-02-11 23:04:16 -05:00
Lirusaito
cb3e8215d6 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-02-11 22:35:11 -05:00
Lirusaito
a3900a4830 Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-02-11 22:34:41 -05:00
Lirusaito
0f9139305a Widen floater_instant_message's Call/End button to 80 from 53, stop haligning it "right", increase floater's min_width so the expanded button won't stick out ever.
The button will be consistent in width with other butons atop instant messages, it looks terrible aligned right, it'll default to center now.
Expansion should also aid in translation, surely the previously allotted space was uncomfortable in other languages.
2012-02-11 22:15:12 -05:00
Siana Gearz
0a8504fb3c Uncertain attempt at preventing borked texturedata on fetch fallback. 2012-02-12 00:19:25 +01:00
Siana Gearz
d709200fc0 Those memory pools can't be of ANY use with our malloc 2012-02-11 17:01:52 +01:00
Siana Gearz
77df33ebf6 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-02-11 17:00:00 +01:00
Siana Gearz
8064d561f4 Merge branch 'master' of git://github.com/lkalif/SingularityViewer 2012-02-11 16:59:49 +01:00
Siana Gearz
aaf1e4d21b Merge branch 'master' of https://github.com/Shyotl/SingularityViewer 2012-02-11 16:58:27 +01:00
Shyotl
9f9a4cbaaf Brought over LLAvatarPropertiesProcessor, and updated relevant floaters to utilize it. (Mainly in the avatar properties floaters) 2012-02-11 04:07:34 -06:00
Inusaito Sayori
e7806f9dac Set label_width's to 60, the previous largest space allotted sliders for fonts/translations. 2012-02-11 03:02:43 -05:00
Inusaito Sayori
d7eb4fc656 Revert to label_width changes made recently for audio sliders. 2012-02-11 02:30:18 -05:00
Shyotl
d10724ae6f Remove some dead savedsettings. 2012-02-10 11:20:28 -06:00
Latif Khalifa
0a3116422a Restore windows shortcut name to include 'Viewer' 2012-02-10 14:09:32 +01:00
Aleric Inglewood
0a59b3bb54 Merge remote-tracking branch 'siana/master' 2012-02-10 02:14:44 +01:00
Aleric Inglewood
e0b21b08fa Add AITimer and AIPersistentTimer state machines.
For use inside other state machines that need timer events.
Wraps AIFrameTimer.
2012-02-10 02:10:12 +01:00
Aleric Inglewood
d4591828c8 Finished AIFrameTimer.
Added documentation.
Added AIFrameTimer::isRunning.
Fixed a bug: mCallback was deleted before it was used.
2012-02-10 01:37:43 +01:00
Aleric Inglewood
cad0597524 Allow AIStateMachine::run() to be called from callback functions.
This was already documented as working, but turned out not to work.
Now one can call any of the run(...) functions to guarantee a restart
of the statemachine. Using run() without parameters from a callback
function re-uses the old callback information.

Introduces a new enum AIStateMachine::active_type that keeps track
of on which list the statemachine resides, if any. This was necessary
because run() calls cont() which now can be called while the
statemachine is already on the active list, so it needs to know
more than just if it's on the continued_statemachines list or
not.
2012-02-10 00:23:20 +01:00
Siana Gearz
e10581e319 Resolve extern warning on GCC 2012-02-09 22:13:44 +01:00
Shyotl
f7b2139e81 Renderer update. Incl mac fixes, DoF improvements, windlight floater code cleanup. (shining-fixes merge) 2012-02-09 14:26:21 -06:00
Lirusaito
f55452d6dd Pad right removed from join call, it made the button look bad. Also moved said button adjacent the button before it to match with other buttons. 2012-02-09 14:34:37 -05:00
Latif Khalifa
b0c675498c Base viewer executable and installer names on the viewer channel
Example config:
/python develop.py -G VC100 -t ReleaseSSE2 configure -DPACKAGE:BOOL=ON -DFMODEX:BOOL=ON -DVIEWER_CHANNEL:STRING="Singularity Alpha" -DVIEWER_LOGIN_CHANNEL:STRING="Singularity Alpha"

VIEWER_LOGIN_CHANNEL is the string that is used in --chanel switch to the shortcut created
VIEWER_CHANNEL is the string used for executable name and installer name

This example will produce: SingularityAlphaViewer.exe and installer named
SingularityAlpha_x-y-z-buildnr_Setup.exe
2012-02-09 19:48:32 +01:00
Lirusaito
f16da13198 Added keyboard shortcut for whisper in chatbar's tooltip. 2012-02-09 13:37:03 -05:00
Lirusaito
0827b3fce9 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-02-09 11:49:54 -05:00
Siana Gearz
a3a7f72b7c Limit PCH size sanely 2012-02-09 15:22:07 +01:00
Siana Gearz
acf378e9f2 Auto-version system 2012-02-09 14:03:49 +01:00
Lirusaito
a39209cd37 Fixed alignment of chats, finally everything lines up.
Things look way more professional as buttons and chat and the like all have consisent positions and sizes, to the pixel.
2012-02-09 02:18:41 -05:00
Siana Gearz
2ef5673c18 Let's not segfault Lenny's GCC 2012-02-09 02:10:21 +01:00
Lirusaito
4c10ae1a4c Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer
Conflicts:
	indra/newview/skins/default/xui/en-us/panel_audio.xml
	indra/newview/skins/default/xui/en-us/panel_friends.xml
	indra/newview/ascentprefssys.cpp
	indra/newview/skins/default/xui/en-us/panel_preferences_ascent_system.xml
2012-02-08 13:13:58 -05:00
Lirusaito
66c3f282de Removed repetitive search text. 2012-02-08 12:54:47 -05:00
Lirusaito
fec75d9436 Added commandline for Fake Away Status.
Users can turn on and off the fake away status by typing a command.
Can be placed inside gesture to be performed together with going away.
2012-02-08 12:54:34 -05:00
Siana Gearz
ef68f43687 Fix RLVa bug with wearables being unwearables 2012-02-08 16:19:12 +01:00
Siana Gearz
13780fae2a Revert "Culling and positioning search properly for contacts."
This reverts commit 78409deaa7.
2012-02-08 13:05:51 +01:00
Lirusaito
c294456e2a Added keyboard shortcut for build.
Consistency with other toolbar tooltips.
2012-02-07 14:41:07 -05:00
Lirusaito
6410fb3209 Fix to make audio panel look nice on Windows as well as Linux. 2012-02-07 14:40:10 -05:00
Lirusaito
af22591cc0 Fixed mQuietSnapshotsToDisk compilation error. 2012-02-07 10:57:04 -05:00
Siana Gearz
b0267e4631 Revert "Newer TCMalloc, now with headers!"
This reverts commit 69a2d0bc5e.
2012-02-07 15:52:15 +01:00
Siana Gearz
69a2d0bc5e Newer TCMalloc, now with headers! 2012-02-07 14:01:56 +01:00
Siana Gearz
108b9211ff Revert "Added Quiet Snapshots To Disk checkbox to Security tab of System Preferences, also corrected comments, since Privacy is called Security now."
This reverts commit 787f91085d.
2012-02-07 12:49:59 +01:00
Siana Gearz
97f2474956 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-02-07 12:43:28 +01:00
Siana Gearz
b4c68d56b6 Reverting Lirusaito's botch on volume controls 2012-02-07 12:43:12 +01:00
Lirusaito
03926c2374 That shouldn't have been deleted, my bad.
image_unselected, so the button will actually show, unselected isn't anything.
2012-02-06 21:45:09 -05:00
Lirusaito
787f91085d Added Quiet Snapshots To Disk checkbox to Security tab of System Preferences, also corrected comments, since Privacy is called Security now.
Users can find and change Quiet Snapshot setting easily in a logical place in their preferences.
2012-02-06 19:38:17 -05:00
Aleric Inglewood
1b37bd5df6 Merge branch 'master', remote-tracking branch 'siana/master' 2012-02-06 22:50:06 +01:00
Aleric Inglewood
ea0a5c521f Fix for libcwd 2012-02-06 22:34:34 +01:00
Lirusaito
6ba69adccc Renewed support for / starting chat on linux. 2012-02-06 15:30:27 -05:00
Lirusaito
481361f714 Swapped tabs for spaces for proper display on Windows. 2012-02-06 15:29:25 -05:00
Siana Gearz
e1e34623cb UI purdyness had its flaws - should revisit later 2012-02-06 20:12:02 +01:00
Lirusaito
78409deaa7 Culling and positioning search properly for contacts. 2012-02-06 03:21:44 -05:00
Siana Gearz
787a18d3a8 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-02-06 04:32:44 +01:00
Siana Gearz
548f9d0a88 Restore starting chat by slash 2012-02-06 04:32:17 +01:00
Siana Gearz
8450386330 Allow to disable chat animation 2012-02-06 03:52:25 +01:00
Lirusaito
8adf2965fd Apostrophes aren't so difficult. 2012-02-05 21:36:40 -05:00
Lirusaito
e0bf075f7f word_wrap should be enabled, looks better that way. 2012-02-05 21:36:01 -05:00
Siana Gearz
8a49d4ef94 Allow pretty much any key for gestures, like Emerald 2012-02-06 03:01:15 +01:00
Lirusaito
d85e1afd7e Spelling fix to documentation. 2012-02-05 18:18:22 -05:00
Lirusaito
c96d1c3aa5 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-02-05 17:57:30 -05:00
Lirusaito
1cbc825461 Culled, some slight tweaks
for the good of the world.
TODO: Realign chat history's.
2012-02-05 17:34:05 -05:00
Siana Gearz
f0d6200e8e Build fix for Visual Studio 2012-02-05 21:53:12 +01:00
Lirusaito
ad24fcca39 Culling, shouldn't affect appearance.
(anymore)
2012-02-05 14:36:45 -05:00
Lirusaito
06837a2a77 Stop prepending ((\[[ to actions that end with ))/]], fix compile warning for npos, and remove unnecessary checks from sendMsg()
Users can have their actions not get broken should they end with )) or ]] without beginning OOC, while AutoOOC is enabled.
2012-02-05 14:22:52 -05:00
Siana Gearz
47537ba764 Forgotten files for LightShare 2012-02-05 15:43:57 +01:00
Siana Gearz
cd51a6f795 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-02-05 15:23:51 +01:00
Siana Gearz
3d40cd86ac Merge branch 'master' of https://github.com/Shyotl/SingularityViewer 2012-02-05 15:14:53 +01:00
Siana Gearz
b03b4db7c7 Buddy search adjustment 2012-02-05 15:13:52 +01:00
Siana Gearz
859e2701f2 Restore LightShare compatibility 2012-02-05 13:52:50 +01:00
Shyotl
2617df44db VC doesn't seem to like ctor being used to assign a reference pointer. 2012-02-05 05:58:36 -06:00
Shyotl
01a8e11ef3 Merge branch 'master' of https://github.com/AlericInglewood/SingularityViewer.git
Conflicts:
	indra/llcommon/llfasttimer_class.cpp
	indra/llcommon/llfasttimer_class.h
2012-02-05 05:57:44 -06:00
Aleric Inglewood
5a455eac91 Fixed LLFastTimers et al.
No longer crash when std::vector moves in memory when it grows.
Fixed display of timer values (in ms).
2012-02-05 05:35:01 +01:00
Shyotl
e18e051bfc sDelayedVBOEnable is still required to prevent framerates from getting clobbered on gl context change. 2012-02-03 14:25:51 -06:00
Shyotl
bf44d3f98e Prevent using multisample FBOs if FBOs are not enabled, or multisample FBOs are unsupported 2012-02-03 14:12:36 -06:00
Shyotl
b6fa215c4e Don't render particles on visually muted avatars. 2012-02-03 14:11:29 -06:00
Shyotl
726dc02aaa Was looking up ascent colors in incorrect control group. 2012-02-03 14:11:10 -06:00
Shyotl
9ee0c5acf3 Having BackgroundYieldTime set to a large enough value would cause a stall if client isn't in focus during login. 2012-02-02 23:44:35 -06:00
Shyotl
5e0140ba81 Fixed a minor syntax error in menu_inventory.xml. 2012-02-02 23:43:04 -06:00
Shyotl
a690c970e6 RenderAutoMuteByteLimit and RenderAutoMuteSurfaceArea: Only 'grey' out visually blocked avatars on impostor pass. 2012-02-02 18:14:52 -06:00
Shyotl
db28c382e0 Apparently LLMeshRepoThread::lodReceived leaked and LL decided to fix it. 2012-02-02 18:07:29 -06:00
Shyotl
1747d529d8 Stall object updates (including flexi) while tiling. Also fix matrix stack breakage 2012-02-02 18:05:07 -06:00
Lirusaito
27763d8390 Small fix for skins' Additional Info box.
Accidentally made it editable while culling; No longer.
2012-02-02 17:42:19 -05:00
Lirusaito
ef79781c9a Disabled editing the avatar key in profiles.
Should have already been disabled, the copy button only copies the original key either way and the key is unchanging.
2012-02-02 17:00:39 -05:00
Siana Gearz
7b6ff27c4b Awesomize buddy search 2012-02-02 12:18:35 +01:00
Siana Gearz
2526d04397 Cosmetic fix llRegionSayTo 2012-02-02 11:31:46 +01:00
Siana Gearz
4ffa06ae07 More reliable preview perm checks 2012-02-02 09:52:34 +01:00
Lirusaito
03c0509a6c Tiny change to allow /ME to work in instant messages.
Sometimes people want to type in all caps, sometimes caps lock goes on by accident, either way, in instant messages, it won't affect /ME anymore.
TODO: /ME for local chat.
2012-02-02 03:49:48 -05:00
Siana Gearz
f68211d83e Awesomely fixed OpenAL for Linux 2012-02-02 09:06:12 +01:00
Siana Gearz
7694f915f5 Fix cache thinking once full, always full. 2012-02-02 08:43:01 +01:00
Lirusaito
0340778775 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer
Conflicts:
	indra/newview/llvoavatar.cpp
2012-02-01 11:24:45 -05:00
Lirusaito
6fecfad2a2 Removed value panels from volume. 2012-02-01 11:10:23 -05:00
Shyotl
e9fa5e6f3d Fixed some static initialization brokenness with fasttimers. 2012-02-01 07:48:40 -06:00
Siana Gearz
b5ec7fe015 Actually enable Region WindLight 2012-02-01 12:20:54 +01:00
Shyotl
b0ccec2b62 Merge branch 'master' of https://github.com/AlericInglewood/SingularityViewer.git
Conflicts:
	indra/newview/llvoavatar.cpp
2012-02-01 03:10:46 -06:00
Siana Gearz
55cce46454 Restoring preset loading 2012-02-01 09:59:31 +01:00
Siana Gearz
071745007a Partial port of Phoenix Windlight parts 2012-02-01 06:36:10 +01:00
Aleric Inglewood
b755bcd495 Merge remote-tracking branch 'origin/master' 2012-02-01 03:35:10 +01:00
Aleric Inglewood
315552d1e0 Allow main window to be unfocused during startup in debug mode. 2012-02-01 03:34:19 +01:00
Aleric Inglewood
e7b2972fc8 LLVFile::readFile must return LLPrivateMemoryPool allocated data. 2012-02-01 03:33:39 +01:00
Aleric Inglewood
e1675f6bd1 Remove erroneous comment. 2012-02-01 03:32:10 +01:00
Aleric Inglewood
2044b88015 Don't use NamedTimerFactory while it's being initialized. 2012-02-01 03:31:19 +01:00
Aleric Inglewood
77a21f95e5 Singleton<> improvements.
Allow Singleton<>::instance to be inlined
and add a new warning for accesses to the
singleton while its being initialized.
2012-02-01 03:27:04 +01:00
Aleric Inglewood
a4e05eea9d Delete singleton instance last, which seems slightly more robust. 2012-02-01 03:23:55 +01:00
Lirusaito
373cb7fb39 Added floater shortcut to toolbar. 2012-01-31 12:54:04 -05:00
Lirusaito
2e240e70d6 Added in snapshot keyboard shortcut to tooltip. 2012-01-31 08:03:16 -05:00
Lirusaito
bdcb5dc877 Culled what wasn't needed, added in keyboard shortcut helpers in tooltips
Things still probably can be culled to shorten this file, but what definitely wasn't needed is removed.
People who want to learn keyboard shortcuts now need only hover their mouse over the buttons on the toolbar.
2012-01-31 07:29:39 -05:00
Lirusaito
e7a59c1031 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-31 03:59:25 -05:00
Shyotl
8ae25e4812 GCC compile fix. 2012-01-31 02:57:32 -06:00
Lirusaito
3548521bef Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-31 03:55:57 -05:00
Shyotl
d15ed9ed34 I borked up the sUseStreamDraw logic earlier. When its a stream type, and sUseStreamDraw is false, don't use a vbo at all (I mistakenly had it use a dynamic vbo instead). Also, removed an unused (due to being renamed) setting from settings.xml 2012-01-31 02:54:42 -06:00
Lirusaito
655365247c Merge branch 'master' of git://github.com/Shyotl/SingularityViewer
Conflicts:
	indra/newview/llpanelmaininventory.cpp
	indra/newview/llpanelmaininventory.h
	indra/newview/llvoavatar.cpp
2012-01-31 02:47:02 -05:00
Shyotl
eaecc9a4d0 RenderVBOEnable was completely broken by ll, so I fixed it. 2012-01-30 19:50:41 -06:00
Lirusaito
dfe33963bb More precise volume controls. 2012-01-30 13:38:39 -05:00
Lirusaito
661f5ede04 Removed max node quick setting(set volatile in settings), put LOD(Obj. Detail) in its place. 2012-01-30 12:44:08 -05:00
Lirusaito
5858f2822c Further Culling and proper constraints of VolumeLOD for protection. 2012-01-30 12:27:55 -05:00
Lirusaito
379ecccec2 Just more culling.
Would have known to do this earlier, but experience is gained through experiences and this was early. Won't move anything, just reduces clutter.
2012-01-30 10:29:59 -05:00
Lirusaito
e69c605c4f Culled attributes.
Looks the same, takes up less space.
2012-01-30 09:41:42 -05:00
Shyotl
f6711e6b4f Dont alphamask the bodies of visually muted avatars. 2012-01-30 03:52:16 -06:00
Shyotl
e327e7a15e Converted some frequent setting lookups to LLCachedControl 2012-01-30 03:49:52 -06:00
Shyotl
9973dfd1c7 Tweaked stream buffer size used in fmodex. 2012-01-30 03:44:20 -06:00
Shyotl
b045d9bd96 Precache a few more inventory icons. 2012-01-30 03:28:35 -06:00
Lirusaito
64181e19b8 Safe changes to the IM Floater, Made the send box smaller and cleaned up. 2012-01-30 04:16:52 -05:00
Lirusaito
ef927134f0 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-01-30 01:12:47 -05:00
Lirusaito
f9398a3ab1 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-30 00:06:51 -05:00
Lirusaito
450402d83d Forward declare LLInventoryViewFinder
Compilation error fix.
2012-01-30 00:02:04 -05:00
Shyotl
9f7e9299bd Pulled apart LLInventoryPanel and LLInventoryView. TODO: LLInventoryView needs to be chopped apart into a floater (LLFloaterInventory) and a panel (LLPanelMainInventory). 2012-01-29 23:17:28 -05:00
Shyotl
681901cb7c LLInventoryViewFinder renamed to LLFloaterInventoryFinder 2012-01-29 17:15:18 -06:00
Shyotl
a0c549534f Pulled apart LLInventoryPanel and LLInventoryView. TODO: LLInventoryView needs to be chopped apart into a floater (LLFloaterInventory) and a panel (LLPanelMainInventory). 2012-01-29 17:10:04 -06:00
Shyotl
9453c2c2e9 Memory tracking system updated. 2012-01-29 01:33:14 -06:00
Shyotl
32c6365bf9 Octree tweakage. 2012-01-29 01:27:40 -06:00
Shyotl
6c921ba340 Routine V3 merge. 2012-01-29 01:25:39 -06:00
Shyotl
f5818b7621 Pass FMOD_IGNORETAGS flag when creating FMOD Ex streams. Without the flag it is more prone to failing to successfully detect stream format info under certain circumstances. 2012-01-29 01:20:26 -06:00
Shyotl
ac1a6101e1 Streaming audio ticker was getting stuck on (Loading...) 2012-01-29 01:16:34 -06:00
Shyotl
7ff58abc17 Too lazy to amend earlier commit. llpanelvolume.cpp needs lltrans.h 2012-01-28 23:52:48 -06:00
Shyotl
c548aa3497 Enable Hardware Skinning by default for med/high/ultra settings. 2012-01-28 22:11:51 -06:00
Shyotl
d0c782c7ae A few more translated tidbits. Also converted french strings.xml from BOM with windows lineendings, to non-BOM with unix lineendings. 2012-01-28 22:07:13 -06:00
Shyotl
d9640ecc65 Little cleanup regarding crashlogger toggling. 2012-01-28 22:02:10 -06:00
Siana Gearz
b177324058 Merge branch 'master' of https://github.com/Shyotl/SingularityViewer 2012-01-29 04:54:36 +01:00
Shyotl
e7a20b04de El crappo. Failed to include this file in earlier commit. 2012-01-28 21:37:19 -06:00
Siana Gearz
5245547f28 Merge branch 'master' of https://github.com/Shyotl/SingularityViewer 2012-01-28 22:25:40 +01:00
Siana Gearz
ef696d895d Avoid tag detection spam from radar on every object update 2012-01-28 22:00:21 +01:00
Siana Gearz
a15e72dc03 Force translator off, remove its UI in chat, small adjustments 2012-01-28 21:52:26 +01:00
Siana Gearz
b7997a2677 Force single texture cache clear 2012-01-28 21:51:15 +01:00
Lirusaito
635c8d0f72 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-28 07:02:47 -05:00
Lirusaito
c9269c04be Fix to quick audio controls, made them less fragile. If cherrypicking 0c3bff3, you should cherrypick this as well, and vice-versa. 2012-01-28 06:50:40 -05:00
Lirusaito
0c3bff3763 Changes to make the changed panel audio look right. 2012-01-28 06:39:39 -05:00
Lirusaito
f06d9294a2 cleaned up default permissions floater, preparing for other changes.
Also gave the panel a border, changing its size to look nice.
2012-01-28 01:57:39 -05:00
Shyotl
c39ddb5991 Packager now conditionally includes fmodex/fmod.dll if project is set to use them. 2012-01-28 00:04:21 -06:00
Lirusaito
690338a122 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-27 23:48:52 -05:00
Shyotl
9dd8f1b8ab Temporarily disable RenderDelayVBUpdate, as it's completely broken. 2012-01-27 21:40:34 -06:00
Shyotl
744b5b6404 Shiny HUD objects not fullbright. https://bitbucket.org/VirLinden/viewer-development-shining-fixes/changeset/c7fca203c70f 2012-01-27 21:28:27 -06:00
Siana Gearz
381d86adb7 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-01-28 03:26:52 +01:00
Siana Gearz
26858c026a Merge branch 'master' of https://github.com/Shyotl/SingularityViewer 2012-01-28 03:25:36 +01:00
Siana Gearz
c454044913 Merge branch 'master' of git://github.com/lkalif/SingularityViewer 2012-01-28 03:02:23 +01:00
Siana Gearz
6e57791397 Use NaCL nametag everywhere, even in radar 2012-01-28 03:00:42 +01:00
Lirusaito
1c5fc42772 This bar keeps us from clicking the bottom of the screen, why ever was it commented out? 2012-01-27 04:03:46 -05:00
Lirusaito
9bab3a362f Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-27 03:39:23 -05:00
Shyotl
fc65380f2a Added auto avatar muting(appearance only) based on avatar complexity (disabled by default). Added several missing debug views. 2012-01-27 02:36:26 -06:00
Shyotl
c1b749e12c Added some missed fasttimers, removed some old ones. 2012-01-27 02:31:57 -06:00
Lirusaito
d310ae3bc6 Changed three tabs to one space and two sets four spaces to avoid square character showing up in some cases. 2012-01-27 03:31:44 -05:00
Lirusaito
9ab0cd11e7 Changed name to Main (General) for easier support for older versions, while retaining the distinct new name. 2012-01-27 02:21:38 -05:00
Lirusaito
51c3f3253b Changed the name of Vanity's General tab to Main 2012-01-27 02:10:45 -05:00
Lirusaito
00c00b617c Changed spoof tag's name, added a heads up tooltip for enhanced boobs checkbox, fixed a large typo that made boob rebound have three decimal places, chopped the final bulk of preferences(may need another sweep through earlier ones) 2012-01-27 02:01:45 -05:00
Lirusaito
c0dd8cac46 Merge branch 'master' of git://github.com/lkalif/SingularityViewer 2012-01-26 23:30:41 -05:00
Lirusaito
8c35361878 Did some more chopping, added value boxes for volume controls that can be set, also made sure that the sliders stayed the same happy size. 2012-01-26 23:25:51 -05:00
Latif Khalifa
2e59ae1340 Implemented support for external script editor based on v3 code (part 2) 2012-01-27 05:25:18 +01:00
Latif Khalifa
856507a580 Implemented support for external script editor based on v3 code 2012-01-27 05:22:33 +01:00
Siana Gearz
8899dbef3c Fix plug-ins not working when path >32 characters 2012-01-27 05:08:15 +01:00
Lirusaito
8e4ea7e3b4 Removed all the scraps, commented out translations, since google decided to "be evil", Made the border around the radio smaller to look nicer. 2012-01-26 22:01:25 -05:00
Lirusaito
5524959f33 More sculpting away of old material, this one seemed to barely need follows 2012-01-26 20:27:42 -05:00
Lirusaito
39959712c1 Once again allowed for port values to reach their full potential, Extended input boxes, move some things left and right(no rearrangements), removal of old stuff. 2012-01-26 19:04:43 -05:00
Lirusaito
c652df10c6 Chopchopchop 2012-01-26 17:27:10 -05:00
Lirusaito
131dc84256 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-26 17:10:44 -05:00
Shyotl
afed6dad84 Fix deferred on mac. VBO cleanup. https://bitbucket.org/VirLinden/viewer-development-shining-fixes/changeset/853c76596bdb 2012-01-26 16:09:47 -06:00
Lirusaito
b18033d023 Whoever modified this last is awesome, mostly two liners, not five lines per tag like normal ^*^ Chopped off boring stuffs. 2012-01-26 16:59:54 -05:00
Lirusaito
26bb68fcb7 Changed Texture offset increment to .01
Chopped some surrounding code, which shouldn't matter, didn't clean completely.
2012-01-26 16:29:25 -05:00
Lirusaito
2c4f850e5f Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-26 16:10:34 -05:00
Lirusaito
502fd90a8d Didn't really change much here, except a ton of repositioning due to the use of text_editors... these should be texts, most of them. 2012-01-26 16:07:04 -05:00
Lirusaito
f2c6ab27e3 Pulled all the oldstuff, moved some things to nicer spots and nicer sizes and changed the port range so that they can be anywhere within 16-bit unsigned number range. 2012-01-26 14:42:11 -05:00
Lirusaito
40bbeebd27 Fixed what I broke with custom checkbox. Returned the initial value of checkbox, as well. 2012-01-26 14:17:21 -05:00
Shyotl
f9d802f832 SH-2565 Move resetVertexBuffer operation to a consistent location (also avoid redundant resetting of vertex buffers on detail switches). Change assertion to a warning with count info. Fix bytes pooled debug display. Remove unused static vertex buffer. https://bitbucket.org/VirLinden/viewer-development-shining-fixes/changeset/56ac56c3cc9a 2012-01-26 12:55:24 -06:00
Shyotl
a6f95d21af LLFolderViewItem and LLFolderViewFolder migrated to llfolderviewitem.cpp/h. 2012-01-26 12:54:38 -06:00
Lirusaito
87f0edc652 Did a ton of stuff that makes General Preferences look prettier, added, changed and rephrased stuff relating to languages... Yay!
Removed old bulk.
2012-01-26 13:36:33 -05:00
Lirusaito
97c0800701 Moved strings to a more logical place, rearranged the layout of the top where grouping by row isn't important so that it is grouped by column to inherit left, changed the feathering sliders into spinners, because we're already using spinners everywhere else, and they're nicer.
And once more, removed all the bulk of old.
Should mention this is the joystick setup found in input preferences.
2012-01-26 06:57:36 -05:00
Lirusaito
da96043a24 Chopped off a ton of the old stuff, and added value fields... These don't seem to work right, however, no matter, the value fields help make more precise changes.
The existence of these were brought up to me by a phoenix user, not sure why showing them was set false.
2012-01-26 05:43:09 -05:00
Shyotl
f6fcf8ca38 Merge branch 'master' of git://github.com/siana/SingularityViewer.git 2012-01-26 00:14:38 -06:00
Lirusaito
43b826bd52 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-26 00:03:53 -05:00
Shyotl
cef03d52cb Un-inline LLFastTimer::NamedTimer::getCountAverage and LLFastTimer::NamedTimer::getCallAverage so some experimenting can be done in llfasttimer_class.cpp without massive recompiles. 2012-01-25 20:14:38 -06:00
Shyotl
29cdfdc13e Crashfix in LLAgent::teleportViaLocation if called before avatar is loaded (ex: doubleclicking the minimap) 2012-01-25 20:12:26 -06:00
Shyotl
2b8a5b4bd1 Allow tooltip to be refreshed when hovering over fasttimer bars. 2012-01-25 20:11:42 -06:00
Shyotl
910cb29a7f Truncating fasttimer times to integers in the hovertext is not very useful.. 2012-01-25 20:11:07 -06:00
Siana Gearz
3261332e0b ouch-i-feel-so-stupid build fix 2012-01-26 02:30:42 +01:00
Siana Gearz
6939d2d70b Build fixes 2012-01-25 23:45:18 +01:00
Siana Gearz
df65a3b7fc Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-01-25 22:20:31 +01:00
Siana Gearz
7d9299d870 Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-01-25 22:18:21 +01:00
Lirusaito
00ae7c3cd3 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-25 15:52:32 -05:00
Lirusaito
769c4f43bd More cleaning up, some rephrasal of old stuff, changed some words to better suitsuit Singularity.
*yawn*
2012-01-25 15:45:23 -05:00
Siana Gearz
7fec659979 Update RLVa to 1.4.0-compatible 2012-01-25 21:11:33 +01:00
Lirusaito
d104d15c53 Yay, more cleaning! 2012-01-25 12:54:43 -05:00
Lirusaito
22137671da Stuff looks nicer than before, and removed a few more large archaic tag attributes. Also restored missing names, names for translating... wish they weren't so large... 2012-01-25 03:33:02 -05:00
Lirusaito
9577607c22 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-24 21:15:32 -05:00
Lirusaito
4340cee991 Merge branch 'master' of git://github.com/AlericInglewood/SingularityViewer 2012-01-24 21:15:15 -05:00
Siana Gearz
d385de9139 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer
Conflicts:
	indra/newview/llstartup.cpp
	indra/newview/llviewerregion.cpp
2012-01-24 21:22:49 +01:00
Shyotl
c4e0122846 Call display_startup() periodically during initilization to avoid buggy drivers/windows from issuing vpu recover erroneously. 2012-01-23 20:36:25 -06:00
Shyotl
6374f012de new fasttimers, including ui. 2012-01-23 18:30:54 -06:00
Shyotl
7d4c2aa54f LLInventoryFilter migrated into llinventoryfilter.cpp/h 2012-01-22 21:46:10 -06:00
Shyotl
c5024ff04d Stab in the dark at fixing color corruption on 64bit builds. 2012-01-22 21:45:10 -06:00
Aleric Inglewood
63117686b1 Typo fix in comment. 2012-01-22 03:12:24 +01:00
Aleric Inglewood
f6bd78083d More merge fixes 2012-01-22 02:49:06 +01:00
Aleric Inglewood
f176aeed8d More 64-bit related compile errors. 2012-01-22 02:48:28 +01:00
Shyotl
51746dc751 Added wearable index handling to LLAgentWearables to scope out further changes. Index is still always zero for now, so behavior should not have changed. (marked with // TODO: MULTI-WEARABLE) 2012-01-21 19:02:19 -06:00
Aleric Inglewood
94d6ef126f Fix compiler error on 64-bit: error: cast from ‘char*’ to ‘U32 {aka unsigned int}’ loses precision 2012-01-22 01:56:35 +01:00
Aleric Inglewood
14f2df2a72 Remove compiler warning 'cast from pointer to integer of different size'. 2012-01-22 01:50:25 +01:00
Aleric Inglewood
82693aae35 linden_common.h must be included first for libcwd 2012-01-22 01:38:33 +01:00
Aleric Inglewood
c4e55464c9 Resolved collisions of merge with siana/master 2012-01-22 01:32:52 +01:00
Shyotl
4a4b786a60 New inventory observer implementation. 2012-01-21 04:54:39 -06:00
Shyotl
d7d65df9e0 HTTP inventory fetching enabled. 2012-01-21 02:37:22 -06:00
Shyotl
40400d696b Updated inv fetching impl. Haven't ticked on new caps. 2012-01-21 00:11:19 -06:00
Shyotl
4c201a9b83 Inv fetch migrated from LLInventoryModel to LLInventoryModelBackgroundFetch singleton. 2012-01-20 19:09:44 -06:00
Shyotl
b338506229 VBO updates from Vir Lindens shining fixes. VBO mapping perf improvement. Alpha rigged attachments render fix, hopefully. Crashfix in void pushWireframe. 2012-01-19 19:01:44 -06:00
Shyotl
7805508e8d SH-2827 crashfix. https://bitbucket.org/VirLinden/viewer-development-shining-fixes/changeset/806c10f621af 2012-01-19 10:36:56 -06:00
Shyotl
9837b0ea2e A little bit of code shuffling and member renaming in llinventoryview.cpp to line up with v3 a bit better. TO-DO: Many functions in LLInventoryView need to be migrated to something akin to v3s LLPanelMainInventory class. 2012-01-18 23:23:29 -06:00
Shyotl
60d5bb81f2 Tweak to CannotCopyWarning notification. Updated french notifications.xml a tiny bit. 2012-01-18 17:51:09 -06:00
Lirusaito
76cb68600c Rewrite of ancient xml preferences(The rest of the preferences still need this) so it takes up way less space and actually makes more sense...
last commit for a while do to illness.
2012-01-17 05:24:36 -05:00
Lirusaito
a6b270b02d Merge branch 'master' of git://github.com/lkalif/SingularityViewer 2012-01-16 23:14:13 -05:00
Lirusaito
f68a899185 Moved show avatar in Mouselook to Mouselook options... cause that's where it kinda should be... 2012-01-14 11:06:44 -05:00
Latif Khalifa
3299d27197 Added string needed to avoid modal error dialog with marketplace delivery 2012-01-14 16:16:52 +01:00
Siana Gearz
a39bf61977 Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-01-14 13:54:24 +01:00
Siana Gearz
6a290864af Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-14 07:38:31 +01:00
Siana Gearz
20ab55752d Remove megaregion support 2012-01-14 06:46:16 +01:00
Lirusaito
4190816023 Removed the first configure command description and aligned the first two commands with the last two so it looks prettier. 2012-01-13 23:55:49 -05:00
Lirusaito
e33a15d410 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-13 23:46:32 -05:00
Siana Gearz
419f7b41af Global lineending fixup 2012-01-14 05:42:53 +01:00
Shyotl
670b85a86f Migrated code out of drag and drop, and into LLGiveInventory. 2012-01-13 22:41:15 -06:00
Siana Gearz
e835d51bdf Revert "Crude port of megaregion mem corruption fix, by Rev"
This reverts commit 6b60fe40c6.
2012-01-14 05:33:05 +01:00
Siana Gearz
242134d2af Line ending unbotch 2012-01-14 05:32:22 +01:00
Shyotl
f0f2834cfe More stuff migrated to llinventoryfunctions.cpp 2012-01-13 21:05:53 -06:00
Shyotl
860bddb1f5 Added LLViewerInventoryType. Migrated some code to llinventoryfunctions.cpp. Pulled LLFolderViewEventListener out into itse own header. Misc tweaks to LLInventoryBridge and LLFolderBridge 2012-01-13 18:11:03 -06:00
Lirusaito
14f9cd57e1 Moved the button up one pixel, aligning it with voice and sound expand buttons, lowered Water preset text a bit, and lowered the water bar one pixel perfectly centering it with the button. 2012-01-13 17:43:15 -05:00
Lirusaito
063112bf59 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-13 15:59:52 -05:00
Siana Gearz
e764329701 Fixed up a merge botch 2012-01-13 21:59:00 +01:00
Lirusaito
27a17b332f Spelling fix 2012-01-13 15:51:58 -05:00
Lirusaito
373688cfb0 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-13 15:50:16 -05:00
Siana Gearz
09d37d7dfd Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer
Conflicts:
	indra/newview/skins/default/xui/en-us/wlfPanel_AdvSettings_expanded.xml
2012-01-13 19:18:22 +01:00
Siana Gearz
b2dd4f652e Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-13 19:14:43 +01:00
Siana Gearz
6bbff07e00 UI Purdyness 2012-01-13 19:14:14 +01:00
Lirusaito
09c7e66844 Spelling fix:
Query only has on r.
2012-01-13 03:23:20 -05:00
Shyotl
c371b82900 Added an optional val_width to slider and multi_slider_bar. When using this be sure it isn't truncating, as setting this value sets the textbox to a fixed width, with no consideration to the actual value it can contain. 2012-01-13 01:02:46 -06:00
Lirusaito
5da990e8fb Eep, did I leave that part of the test in?! 2012-01-13 00:52:13 -05:00
Lirusaito
b96beb478c Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-13 00:32:03 -05:00
Shyotl
ad3a16fd3b Merge branch 'master' of https://github.com/Lirusaito/SingularityViewer.git 2012-01-12 23:30:17 -06:00
Shyotl
21b6b89577 If this assert hits then I want your callstack, and so does Bao Linden. SH-2827 2012-01-12 23:28:38 -06:00
Shyotl
55a66737c0 Disable a certain octree error that likes to fire a bit frequently. 2012-01-12 23:24:44 -06:00
Lirusaito
198e499798 Fix for error: incomplete type ‘LLInventoryObserver’ used in nested name specifier presented by GCC 4.6.2 2012-01-13 00:18:37 -05:00
Lirusaito
9d3b276f61 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-12 23:39:16 -05:00
Lirusaito
b0967463ca Take a good look at the Settings Panel in the release, see how icky it looks? Well, Boom! 2012-01-12 23:35:58 -05:00
Shyotl
1cc33caaa3 Pulled llinventorymodel apart into llinventorymodel, llinventoryobserver and llinventoryfunctions 2012-01-12 18:57:10 -06:00
Lirusaito
0777bedde8 Made it so the quick setting changer button never leaves its exact spot pixel for pixel;
Moved where exactly the bottom of the expanded panel was to match the unexpanded bottom;
Extended the bars of WindLight because their lack of capitalization on available real estate has been bugging me;
Changed the tooltip of the WindLight Water control to no longer _reflect the Sky_.
2012-01-12 17:48:43 -05:00
Lirusaito
be978ab76b A small change issued by Siana to correct a small problem. 2012-01-12 17:35:41 -05:00
Lirusaito
21a64b306d Added in two options under Help->Bug Reporting for reporting bugs with Singularity more easily:
Report Singularity Bug goes to the new issue page.
Singularity Issue Tracker goes to the issues list.
Spelling correction.
WebLaunchSinguIssue is called in these two new menu options.
2012-01-12 13:40:14 -05:00
Lirusaito
5a66c2bfba Added WebLaunchSinguIssue Notification, which will make sense in the next commit.
Also corrected location of Report Bug from in Tools to in Help under Bug Reporting.
2012-01-12 13:32:44 -05:00
Lirusaito
e5a27e6558 Added WebLaunchSinguIssue notification, but if feel my translation skills inadequate, so I'm just throwing this out here for someone like Nomade Zhao to correct and finish. 2012-01-12 13:25:20 -05:00
Lirusaito
6f3a92d772 Fixed spelling. 2012-01-12 13:10:27 -05:00
Siana Gearz
4feb69e342 Crouch toggle rewrite - removed garbage. 2012-01-12 06:35:42 +01:00
Siana Gearz
d2b2c501bc Merge branch 'master' of git://github.com/Lirusaito/SingularityViewer 2012-01-12 03:44:48 +01:00
Siana Gearz
465660e235 Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-12 01:42:59 +01:00
Siana Gearz
4eea8624b7 Fix keyboard shortcuts like ctrl+w for floaters 2012-01-12 01:37:13 +01:00
Lirusaito
09213950c3 May or may not fix a lack of AO stand animations caused by my last commit... probably does 2012-01-11 11:30:18 -05:00
Lirusaito
d93f0bb56a Rubber-band camera in crouchtoggle avoided. 2012-01-11 11:18:23 -05:00
Lirusaito
4de23007a5 Added crouch toggle, global bool called isCrouch, function agent_toggle_down bound to toggle_down.
Shift+crouch=toggle, crouch doesn't work while toggle crouched, at the moment.
Formatted keys.ini to be completely aligned.
2012-01-11 11:04:12 -05:00
Lirusaito
8543106f5e Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-11 02:28:14 -05:00
Shyotl
ced937cc46 Implemented CreateInventoryCategory sim caps support. (https://bitbucket.org/lindenlab/viewer-development/changeset/d327dcc8ae51) 2012-01-11 00:55:03 -06:00
Shyotl
9476aedaf6 Respect passed alpha value for checkerboard. 2012-01-11 00:50:39 -06:00
Shyotl
68bc6749e5 Hooked LLTrans into LLNotification stuffs. Also, fixed [CURRENCY] showing up in several places on the buy land window. 2012-01-11 00:49:14 -06:00
Lirusaito
c5d4ccc78d Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-09 17:59:17 -05:00
Shyotl
e847b105f2 CURRENCY added as a default token replacement within LLTrans. (Will be useful when strings are migrated from ui xmls to strings.xml). Also, fixed compile (missed llwindowcallbacks.cpp in my previous commit) 2012-01-09 16:54:13 -06:00
Lirusaito
d6e9b50b99 Spelling fix 2012-01-09 16:14:33 -05:00
Lirusaito
61beedd3d9 Changed style of comments with asterisks to avoid highlighting errors on //* with weak highlighters, change is to all files that could potentially break highlights
Most were needed, though some were just for possible problems with highlighting, should not affect performance whatsoever.
2012-01-09 05:40:03 -05:00
Lirusaito
35333f0105 Spelling fix 2012-01-09 05:20:12 -05:00
Lirusaito
be700d0719 Spelling fix, while changing comment styling. 2012-01-09 05:18:01 -05:00
Lirusaito
5dd2f5e2cf Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-09 05:10:15 -05:00
Shyotl
e9bd6a3b0d Better translation support relating to client initialization/login. 2012-01-09 01:40:30 -06:00
Lirusaito
d505e515e4 Moved parentheses into liru_assert_strip for usability. 2012-01-08 21:03:13 -05:00
Lirusaito
9fd813267e Added three macros, two to help the final one which exists to show file and line on llassert messages;
Changed the presentation of said messages to look nicer.
2012-01-08 18:21:43 -05:00
Lirusaito
f14e6cdb60 Pretty sure all I did was fix some things that didn't look nice to me or poor syntax highlighters, shouldn't affect performance... 2012-01-07 18:14:36 -05:00
Lirusaito
fe87ac0f3d Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-07 18:14:15 -05:00
Siana Gearz
a6f36abab0 Semicolon care and feeding 2012-01-07 23:44:46 +01:00
Lirusaito
621e677898 Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-07 17:09:43 -05:00
Lirusaito
626a94cf8f Merge branch 'master' of git://github.com/siana/SingularityViewer 2012-01-07 15:38:58 -05:00
Siana Gearz
d66074d2ea Merge branch 'master' of git://github.com/Shyotl/SingularityViewer 2012-01-07 21:36:24 +01:00
Shyotl
bf28f6514f Don't grey out 'properties' if selecting multiple items in inventory. 2012-01-06 13:49:24 -06:00
Shyotl
cf62e22409 gpu_table update 2012-01-06 13:48:21 -06:00
Lirusaito
900d063d4f Just fixed this file up a bit to look a bit nicer... Won't affect performance. 2012-01-06 11:18:31 -05:00
Siana Gearz
b911e86b02 Merge branch 'future' of git://github.com/Shyotl/SingularityViewer 2012-01-06 10:40:05 +01:00
Shyotl
54bfdb7704 Stippling removed. Emulated with a texture and some texcoord manipluation. (looks identical, and works with and without shaders) 2012-01-06 00:53:18 -06:00
Lirusaito
566152c230 Almost completely stripped, saving the final piece for tomorrow
((Double clicking from list of chat people on right of chats))
2012-01-05 21:56:48 -05:00
Shyotl
dcbf39bcfa (Loading...) text in ticker now goes away after 10 seconds. Some streams simply do not provide artist/title metadata. Only parse metadata every half second (opposed to every frame). Also fixed paused text getting clobbered. 2012-01-05 15:52:03 -06:00
Shyotl
c73913a864 Decided to allow width adjustment to ticker floater. It's a little wonky due to blocking height adjustment. LLFloater is kinda crappy and doesen't provide a real max_height/max_width mechanism to do this, so the behavior is a little wonky, but good enough for now. 2012-01-04 18:05:58 -06:00
Shyotl
d3ba4a2b80 Less hardcoded element manipluation for media ticker(allows more freedom for skins, etc). Added gridlines to visualizer. Added oscillator color to color tables (This process sucks, btw). 2012-01-04 03:55:53 -06:00
Shyotl
af7affa9dd Removed code for a case that will never occur (for oscillator). Also tiny bugfix related to iterating down wavedata buffers. 2012-01-03 21:31:21 -06:00
Shyotl
89e738003c Default RednerDelayVBUpdate to false. 2012-01-03 20:28:15 -06:00
Shyotl
a3f7399d7d Added media ticker to Singularity menu. Will be greyed out for anything not FMOD based. Consider implementing in other sound libraries as a to-do. FMOD Ex also displays an oscillator (making fmod3 do this is too dirty, so that's not going to be backported). 2012-01-03 20:27:56 -06:00
Shyotl
dff195d120 Several transparent ui-related components were writing to the depthbuffer for some silly reason. Don't do that since it messes with occlusion. 2011-12-30 19:15:21 -06:00
Shyotl
48552e3027 Set blendtype back to BT_ALPHA after postprocess, because ui assumes such [aka: glBlendFunc(GL_SOURCE_ALPHA, GL_ONE_MINUS_SOURCE_ALPHA)]. Also, line-ending fixup.. again. 2011-12-30 17:19:23 -06:00
Siana Gearz
c6ce417fac Attempt at fixing a Linux build botch in ReleaseSSE2 2011-12-29 13:39:59 +01:00
Siana Gearz
a7510d3b3a Merge git://github.com/Lirusaito/SingularityViewer 2011-12-28 04:28:49 +01:00
CobraElDiablo
aad10c9b8d SH-2516 Fix: Full Bright Geometry Rendering Increases Rapidly, Destroying Frame Rate. By Bao Linden 2011-12-28 04:27:04 +01:00
Shyotl
ca96e00135 Enable gl sync fence. Missed a vertexbuffer flush. moved LLMultiFloater out of llfloater.(h|cpp) and into its own header and source file, matching v2. 2011-12-26 23:40:18 -06:00
Inusaito Sayori
536189340d Merge branch 'master' of git://github.com/siana/SingularityViewer 2011-12-24 23:39:41 -05:00
Siana Gearz
23b97efb69 Prebuilt fetch now honors CMake-specified plattform 2011-12-25 05:21:37 +01:00
Siana Gearz
2c8e3bb0a3 More Linux build fixes 2011-12-25 04:28:17 +01:00
Inusaito Sayori
7a48cb9979 Merge branch 'master' of git://github.com/siana/SingularityViewer 2011-12-24 22:09:45 -05:00
Siana Gearz
2f5caa27fd Merge branch 'master' of git://github.com/TighMacFanatic/SingularityViewer 2011-12-25 03:37:03 +01:00
TighMacFanatic
d69f00741f Openjpeg compile fixes. 2011-12-24 15:07:52 -05:00
Siana Gearz
8c4a52e58d Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-12-24 20:12:47 +01:00
Siana Gearz
fd22d80dfd Merge branch 'future' of https://github.com/Shyotl/SingularityViewer
Conflicts:
	indra/newview/app_settings/shaders/class2/effects/gaussBlurF.glsl
	indra/newview/llmanipscale.cpp
	indra/newview/llmeshrepository.cpp
	indra/newview/llviewerregion.cpp
	indra/newview/llvovolume.cpp
2011-12-24 20:12:12 +01:00
TighMacFanatic
e071dc52fd Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-12-24 13:52:14 -05:00
Shyotl
e1cbeb7e02 Oopsies. Undoing a change that shouldn't have made it into the previous commits. Was simply a part of an experiment. 2011-12-24 11:30:09 -06:00
Inusaito Sayori
9d367cf9f2 Spelling fix 2011-12-24 08:29:58 -05:00
Inusaito
3890e61fbc Merge branch 'master' of git://github.com/siana/SingularityViewer 2011-12-24 03:44:08 -05:00
Siana Gearz
3df853b74b Merge branch 'future' of git://github.com/TighMacFanatic/SingularityViewer 2011-12-24 09:00:31 +01:00
Siana Gearz
10bc483305 Merge branch 'master' of git://github.com/TighMacFanatic/SingularityViewer 2011-12-24 09:00:03 +01:00
Siana Gearz
f55fa45b73 No more spammy spam from inventory view, mini update to V3 2011-12-24 08:56:58 +01:00
Siana Gearz
acb24dd3e1 Some Resident scrubbing from IM headers, a-la V3 2011-12-24 08:56:20 +01:00
Inusaito Sayori
4b87c45298 Highlight for selected line should be visible without eyestrain. 2011-12-24 00:31:14 -05:00
Shyotl
a2fb56bf48 Removed soundgroups. Unneeded and redundant. Any potential usefulness is ursuped by LLAudioEngine's channel management, which I do not want to alter. 2011-12-23 18:29:13 -06:00
Shyotl
37095dc2cd Provide FMOD Ex with format hints. Use software mixing only. Fixed issue with channels being silently stolen due to maxchannels being too low (Wind and streaming audio + 30 sound sources were exceeding max channel count) 2011-12-23 18:00:54 -06:00
Siana Gearz
a57ac5a954 Make OpenJPEG respect our build settings 2011-12-23 23:23:55 +01:00
Siana Gearz
29045609a3 Attempt to fix linking on newer linux 2011-12-23 18:31:47 +01:00
Shyotl
b4dad425aa Fixed wind not mixing with other audio. Play no more than 30 world sounds at once (new sounds just muted until channels free up). Add fmod profiler support (SHEnableFMODExProfiler). Use FMOD_UNICODE on windows instead of loading files manually and sending raw data to fmod. Incl other misc cleanup. 2011-12-23 02:29:49 -06:00
Shyotl
c73414f1a1 A stab at fmodex support on windows. To use, install 'FMOD Ex Programmers API' to its defualt program files directory, and run develop.py with -DFMODEX:BOOL=ON set. That /should/ be all it takes. 2011-12-22 18:01:07 -06:00
Shyotl
6920bee5e2 Fmod metadata parsing. Innitial. 2011-12-21 01:23:04 -06:00
Shyotl
6a5e203884 Remove hardcoded var passing between LLPostProcess and its floater. Now driven through the xml file. 2011-12-17 00:20:54 -06:00
TighMacFanatic
6b1f44ed6d Fix for a rare error involving llSetPayPrice being used on a child prim. 2011-12-16 18:59:53 -05:00
TighMacFanatic
ab8cd3e5d5 #endif location fix 2011-12-16 18:44:51 -05:00
Shyotl
78f7cc0d64 Prim rotate tool was lacking colors on its 'rings' due to missing gGL.diffuseColor4fv calls. 2011-12-16 06:01:05 -06:00
Shyotl
b607650d5c V3 merge. Supposedly improves baked texture loading on other avatars. Also increase discard bias on ati if vram starts running out. 2011-12-16 05:57:48 -06:00
Shyotl
1bfa72fa7c V3 merge. Slight update. Fixes lighting oddities. 2011-12-16 01:50:57 -06:00
Shyotl
0030ca3af7 Fixed extra post-process shaders. Cleaned up and no longer using deprecated functions. 2011-12-16 01:46:20 -06:00
Shyotl
a53e08032f Prep for UI matrices. 2011-12-11 01:13:47 -06:00
Shyotl
62febda165 Disable rlva name substitution in LLHUDNameTag::setString. Was redundant and hosed up client tags. 2011-12-11 00:47:44 -06:00
Shyotl
a726de0e99 LLTexLayer now using shaders if applicable. Also added misc tidbits missed earlier. 2011-12-10 22:51:52 -06:00
Shyotl
918d527b14 Resolved nametag bubble image failing to load promptly. Also updated texture to that used in v3, as it looks cleaner. 2011-12-10 22:50:26 -06:00
Shyotl
5919c9a788 Resolved issue with nametag bubble vanishing due to incorrect blend states. 2011-12-10 22:47:43 -06:00
Shyotl
14f2248ea1 LLRenderTarget::copyContents more lenient to errors. 2011-12-10 22:46:26 -06:00
Shyotl
81499fc6ea -Font init cleanup. Now has a dedicated position in startup initilization. 2011-12-10 22:45:42 -06:00
Shyotl
ffb285c6ff Huge renderer update (WIP). Still plenty to do, especially pertaining to UI.
-Nametag bubble visbility is oddly inconsistent. May vanish with future planned UI merges...
-VBOs are PAINFULLY slow on ATI hardware. This repos self-compiled davep/shining-fixes branch, so I'll leave the ball in LL's court for now regarding that.
2011-12-09 14:02:29 -06:00
TighMacFanatic
e08e8cf131 Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-12-03 13:03:39 -05:00
Siana Gearz
40b4b47023 Silly build fix 2011-12-03 18:52:04 +01:00
TighMacFanatic
2565caf62a Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-12-03 11:52:29 -05:00
Siana Gearz
d63df79b85 Adding Neck and Root attachment points 2011-12-03 03:51:15 +01:00
Siana Gearz
697dd7e929 Preparing to add mesh upload. 2011-12-03 03:43:23 +01:00
TighMacFanatic
f613be9276 Added new water windlight 2011-12-02 10:41:53 -05:00
TighMacFanatic
f5a08b1c12 Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-12-01 12:13:56 -05:00
TighMacFanatic
f40bbb1602 Merge branch 'master' of https://github.com/Shyotl/SingularityViewer 2011-12-01 12:13:42 -05:00
Siana Gearz
aeb766ee37 Attempt at work around OS X VBO slowness 2011-12-01 17:51:30 +01:00
Siana Gearz
cd67046b33 Silly way to clear stringstream on GCC 2011-12-01 17:41:50 +01:00
Siana Gearz
66f4c170cb Merge branch 'future' of https://github.com/Shyotl/SingularityViewer into renderer32 2011-12-01 16:51:43 +01:00
Siana Gearz
a485760028 Skipping first time dialogs enabled 2011-12-01 16:51:27 +01:00
Shyotl
0b5a2cd6a3 Un-break for compilers that don't yet support stringstream copying ala C++0x's MoveConstructible 2011-12-01 02:21:23 -06:00
Shyotl
8e7733b2ce Lazy shader update. attribute -> ATTRIBUTE, varying -> VARYING. Moved some shader preprocessor stuff from llviewershadermgr.cpp to llshadermgr.cpp to match LL (although it looks more messy) 2011-11-24 01:03:03 -06:00
Shyotl
08d2c17c65 Alphamask detection tweakage. 2011-11-24 00:31:45 -06:00
Shyotl
09f7136cf4 'Preserve texture scaling when animating textures when "size x" and "size y" parameters to llSetTextureAnim are zero.' https://bitbucket.org/davep/shining-fixes/changeset/db8a3f49c250 2011-11-23 23:44:04 -06:00
Shyotl
c4b77e247e LLInstanceTracker v3 merge 2011-11-23 23:40:31 -06:00
Shyotl
9f9daba33d Missed some glRotatef -> gGL.rotatef conversions. 2011-11-23 23:39:42 -06:00
Shyotl
dc3831c86b llmath v3 merge. 2011-11-23 23:04:54 -06:00
Siana Gearz
ab14f1908c Merge branch 'future' of https://github.com/Shyotl/SingularityViewer into renderer32 2011-11-24 04:43:47 +01:00
Shyotl
bf6e1d6c75 Unbork shadows. Apparently texture/texturProj do not return a vector for shadow samplers. 2011-11-23 21:30:32 -06:00
Shyotl
5d8d402403 Fix shader error dumping. (the error file was not being written to.) Also catch exceptions if shader log dir is inaccessable or otherwise unable to be cleared. 2011-11-23 21:29:42 -06:00
Siana Gearz
b8eb3076ce Merge branch 'future' of https://github.com/Shyotl/SingularityViewer into renderer32 2011-11-23 23:21:14 +01:00
TighMacFanatic
d16e48e128 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-11-22 00:04:40 -05:00
Shyotl
e46c906a8b Invalid texure indexes now display as pink to better assist debugging. 2011-11-21 15:56:24 -06:00
Shyotl
83e8a9076b Several gl calls now wrapped via LLRender (gGL) as prep for future changes:
glMatrixMode -> gGL.matrixMode
..GL_MODELVIEW -> LLRender::MM_MODELVIEW
..GL_POJECTION -> LLRender::MM_PROJECTION
..GL_TEXTURE -> LLRender::MM_TEXTURE
glMultMatrix -> gGL.multMatrix
glLoadMatrixf -> gGL.loadMatrix
glPushMatrix -> gGL.pushMatrix
glPopMatrix -> gGL.popMatrix
glLoadIdentity -> gGL.loadIdentity
glRotatef -> gGL.rotatef
glTransformf -> gGL.transformf
glOrtho -> gGL.ortho
glColor3f -> gGL.diffuseColor3f
glColor3fv -> gGL.diffuseColor3fv
glColor4f -> gGL.diffuseColor4f
glColor4fv -> gGL.diffuseColor4fv
glColor4ubv -> gGL.diffuseColor4ubv
glLightModelfv(GL_LIGHT_MODEL_AMBIENT -> gGL.
2011-11-21 15:55:44 -06:00
TighMacFanatic
56b28756ad Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-11-19 21:36:41 -05:00
TighMacFanatic
13fad75811 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-11-19 21:36:32 -05:00
Shyotl
1cf367aae5 A little vectorization. gluProject replaced with vectorized glProjectf. Added LLMatrix4a::rotate4. Tweaked LLMatrix4a::rotate. Removed extra _mm_movehl_ps call in LLMatrix3a::setTranspose 2011-11-19 20:02:31 -06:00
Shyotl
70909f86c8 Changed some GL matricies to single precision. 2011-11-19 19:05:19 -06:00
Shyotl
1fd908b2c4 Removed maximum_alpha uniform (as it's always 1.0 anyhow) 2011-11-19 15:13:16 -06:00
Siana Gearz
f0108c17b9 Build floater fixes by Cale Flanagan +tinies 2011-11-14 06:36:46 +01:00
Siana Gearz
0566bde61b Merge branch 'master' of git://github.com/LightDrake/SingularityViewer 2011-11-14 06:13:47 +01:00
TighMacFanatic
a31fc8a612 Issue 14 - unlimited message lengths resolved. 2011-11-11 01:58:43 -05:00
TighMacFanatic
2ba9e16a6c Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-11-10 12:27:02 -05:00
Siana Gearz
e6dce6ad7b #9 - region restart delay 2011-11-10 03:45:29 +01:00
TighMacFanatic
c87caebcf7 Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-11-08 21:13:30 -05:00
Siana Gearz
121a557fe5 Fixes bug #79 2011-11-08 21:54:26 +01:00
Drake Arconis
6aa7f219cd Merge remote-tracking branch 'upstream/master' 2011-11-08 05:22:47 -05:00
TighMacFanatic
e9e05bec90 Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-11-07 19:31:53 -05:00
Siana Gearz
8421661a89 Version 1.6.0(3) 2011-11-08 01:27:12 +01:00
Siana Gearz
7fea7c589d Make floating text draw distance tunable 2011-11-08 01:26:51 +01:00
TighMacFanatic
0e3b735a60 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-11-07 18:44:33 -05:00
TighMacFanatic
236f5757a2 Fix paste bug in object size to clamp to new megaprim maximums. 2011-11-07 18:42:11 -05:00
unknown
0a44e3ee09 Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-11-07 18:29:23 -05:00
Shyotl
ee5a9c97be Removing some shader warnings under gl 3.x context. 2011-11-07 16:43:30 -06:00
Shyotl
2126654979 Nuked some old redundant camera code. 2011-11-07 16:42:41 -06:00
Shyotl
df40a8c5cf Fade 'Press ESC to leave mouselook' away after a while. 2011-11-07 16:42:22 -06:00
Siana Gearz
34fb647903 Fix unable to delete links and make more purdy 2011-11-07 05:34:12 +01:00
Siana Gearz
5de4f1e2da Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-11-07 02:36:24 +01:00
Siana Gearz
18dd31f51a Fix outfit saving 2011-11-07 02:36:05 +01:00
Shyotl
40689f7d1e Fixed silly movement bug after taking high-res snapshots. 2011-11-06 16:29:43 -06:00
Siana Gearz
4ce0345842 Fix setting adult, mostly 2011-11-06 16:35:49 +01:00
Sovereign Engineer
844e38702a Merge remote-tracking branch 'upstream/master' 2011-11-03 14:32:35 -04:00
Siana Gearz
5f074c8c82 Fix most annoying minimap glitch 2011-11-02 18:37:56 +01:00
Siana Gearz
5c2b1d396b French! Huge thanks to Nomade Zhao! 2011-11-02 05:28:02 +01:00
Siana Gearz
b0b7837c4d Root prim gizmo fix 2011-11-02 04:42:23 +01:00
Siana Gearz
7789c5a703 Restore map overlay on SL 2011-11-01 18:14:43 +01:00
Shyotl
b64cb8d339 Lineending fixup 2011-10-31 20:44:43 -05:00
Shyotl
3891928092 vary_texture_index only needed in shaders when batching. 2011-10-31 20:44:33 -05:00
Shyotl
f5983208eb Hide the mouse cursor a bit better when grabbing items in mouselook. 2011-10-31 20:44:27 -05:00
Shyotl
0ef5931212 Added a bit of debug output for viewer tool states. 2011-10-31 20:43:56 -05:00
Shyotl
6964cec6f1 Merge branch 'master' of git://github.com/siana/SingularityViewer.git into future 2011-10-31 20:41:36 -05:00
Drake Arconis
a35ef93d19 Right Click Mouse Scroll Zoom and Xcode fix 2011-10-31 15:43:05 -04:00
Drake Arconis
e5f75d1e48 XCode fixes 2011-10-31 13:24:31 -04:00
Siana Gearz
231af66b90 Fixing for OS X 2011-10-31 16:00:05 +01:00
Siana Gearz
f386fc75df Did SELinux complain about THIS? 2011-10-31 05:24:00 +01:00
Siana Gearz
cc45a24aff Darwin doesn't use extension function pointer mechanism 2011-10-28 02:05:09 +02:00
Siana Gearz
e06d0b2e70 Whoopsie! 2011-10-27 23:02:35 +02:00
Siana Gearz
0281ac4afe There is no glext prebuilt on OS X 2011-10-27 22:39:28 +02:00
Brett Murphy
7227c01d79 updated install.xml with correct freetype package for mac-osx 2011-10-27 22:35:38 +02:00
Brett Murphy
c5abde1616 added ctrl-alt-p option for phantom avatar 2011-10-27 22:32:38 +02:00
Siana Gearz
c2a4951f20 Fix grid info fetch ignoring certificates 2011-10-26 21:35:19 +02:00
Shyotl
0fde15246b Moved some settings out of the main settings.xml and into settings_sh.xml where they belong. 2011-10-22 01:57:39 -05:00
Shyotl
b3423de80a Shaders dumped to logs/shader_dump directory if 'ShyotlDumpRawShaders' is true, or a shader throws an error of some sort. 2011-10-22 01:55:08 -05:00
Siana Gearz
743449e635 Tinies, custom OpenAL on Linux 2011-10-22 05:09:49 +02:00
Shyotl
405182025d Resolved warning about 8dcd4a48-2d37-4909-9f78-f7a9eb4ef903.j2c not being found. Also renamed it to transparent.j2c because I hate seeing local textures with uuid filenames... and because v2 renamed it too. 2011-10-21 21:46:35 -05:00
Siana Gearz
bd6d14d741 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-10-22 01:17:59 +02:00
Shyotl
0e190cadcd LLWorld::destroyClass() wasn't releasing water texture refs. 2011-10-21 15:26:59 -05:00
Shyotl
408f5a4a51 SH-2564. Testing region visibility driven by spatial partitions. 2011-10-21 15:26:27 -05:00
Shyotl
53f5957b92 Animated faces with bumpmapping now behave as expected. 2011-10-21 15:03:46 -05:00
Shyotl
fb32a0be5a Failed to start tracking a new icon. (Inv_Invalid.png) 2011-10-21 14:52:13 -05:00
Shyotl
474e1ec7cc Particles (including clouds) vectorized a bit more. 2011-10-21 14:50:55 -05:00
Siana Gearz
1e9d09b82e Merge branch 'future' of https://github.com/Shyotl/SingularityViewer
Conflicts:
	indra/newview/llinventoryicon.cpp
2011-10-21 05:37:07 +02:00
Siana Gearz
c3b1bf4b76 Actually, tga loads more rapidly for some reason 2011-10-21 05:35:33 +02:00
Shyotl
e2d9c31f5e Fixed inventory icons for shirt and physic wearables. 2011-10-20 22:24:58 -05:00
Siana Gearz
7b81e9d248 Merge branch 'master' of github.com:siana/SingularityViewer 2011-10-21 04:56:21 +02:00
Siana Gearz
6e6c438e2a Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-10-21 04:55:08 +02:00
Siana Gearz
4606c7512e Icon mishaps, thx Cryo for spotting. +Standalone fix 2011-10-21 04:54:55 +02:00
Shyotl
88e373c960 The copy button in the face edit panel saves the texture params to a non-existant saved setting for some reason. This throws a crasloop prompt. Setting is never read back, so just commenting out this line. 2011-10-20 19:29:51 -05:00
Siana Gearz
f2deb39dac Merge branch 'future' of https://github.com/Shyotl/SingularityViewer
Conflicts:
	indra/newview/lldrawpoolbump.cpp
2011-10-21 01:42:12 +02:00
Shyotl
8a5793ce23 Selected face overlay no longer z-fights with the selected prim face. 2011-10-20 16:23:28 -05:00
unknown
a36c5f2ee6 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer into future 2011-10-20 14:56:37 -04:00
Shyotl
c63bf31328 I don't trust this statement to work without the parentesis. 2011-10-20 13:46:56 -05:00
Shyotl
5d811163a6 gDeferredShadowAlphaMaskProgram should be unbound after renderpass 2011-10-20 13:45:28 -05:00
Shyotl
78a98cbf08 Bump shader needs to have texture0 sample assigned to index0, and texture1 as index1. Confirmed in LLDrawPoolBump::bindBumpMap. (This probably isn't required yet, but appears safe) 2011-10-20 13:45:00 -05:00
Shyotl
91348909b5 Shuffled gObjectSimpleWaterProgram and gObjectSimpleAlphaMaskProgram around 2011-10-20 13:43:25 -05:00
Shyotl
73f2543a6f Use LLGLShader::sIndexTextureChannels instead of mNumTextureImageUnits directly.. also check that channel value isn't invalid. Also make super-sure shaders are disabled on pre opengl 2.0 hardware. 2011-10-20 13:37:09 -05:00
unknown
d80458b726 Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-10-20 14:36:59 -04:00
Shyotl
22385c44c8 Make legacy renderpath a bit more strict. 2011-10-20 13:30:29 -05:00
Shyotl
40261d3472 Invalid texunit was still bound after bumpmapping finished 2011-10-20 13:26:08 -05:00
Siana Gearz
06ff562cd2 Added Mobility Radeon 6 series 2011-10-20 20:23:29 +02:00
unknown
e7b023778a Merge branch 'master' of https://github.com/siana/SingularityViewer 2011-10-20 14:08:10 -04:00
Siana Gearz
3e12fe15bc Version 1.6.0(2) 2011-10-20 18:44:21 +02:00
Siana Gearz
95634e8276 Don't know why exactly this works, but fixes noshader and seems safe. 2011-10-20 13:10:11 +02:00
Siana Gearz
6c19346855 Bug fixing the repackager 2011-10-20 13:09:30 +02:00
unknown
2e4419a295 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-10-19 16:12:15 -04:00
unknown
bdc95a1544 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer into future 2011-10-19 16:09:24 -04:00
Siana Gearz
68e143de1b Merge branch 'future' of https://github.com/Shyotl/SingularityViewer 2011-10-19 18:11:58 +02:00
Shyotl
5d6aadb268 Prevent estimated vram value from being clobbered if glGetIntegerv memory query returns something too small. 2011-10-19 09:53:44 -05:00
Shyotl
4368c57243 Disabled reserved attrib having reserved locations in shaders. Conflicted with attrib location assumptions elsewhere. 2011-10-19 09:43:28 -05:00
Siana Gearz
ef54491760 GCC 4.6.1 fix 2011-10-19 05:33:09 +02:00
Shyotl
d88f696604 Brought over LLInventoryIcon 2011-10-18 19:44:08 -05:00
Shyotl
82b0171a86 Workaround for mac startup hardlock. (STORM-1641) 2011-10-18 09:33:20 -05:00
Siana Gearz
142427b3e1 Version 1.6.0(1) 2011-10-18 04:17:31 +02:00
Siana Gearz
9c4d50eda3 Fix adding foreign grids 2011-10-18 04:16:17 +02:00
Siana Gearz
f3c8419de8 GCC 4.3 wasn't happy 2011-10-18 04:15:08 +02:00
Siana Gearz
d6524fbd26 Merge branch 'future' of git://github.com/Shyotl/SingularityViewer 2011-10-18 02:29:49 +02:00
Shyotl
4446b0fbad Give each reserved attribute a consistent index across all shaders. 2011-10-17 19:19:36 -05:00
Shyotl
58ea5f88dc Spew llinfos for opengl procs that aren't found. 2011-10-17 19:06:22 -05:00
Shyotl
8ce30e9ff7 Fixed a crash for hardware lacking ARB_shader_objects extension support. 2011-10-17 19:05:37 -05:00
Shyotl
67230a3290 Line-ending fixup. 2011-10-17 14:11:30 -05:00
Shyotl
c413d62536 Decouple RenderUseFBO from RenderDeferred, as toggling deferred would leave RenderUseFBO enabled regardless of previous setting. 2011-10-17 14:11:10 -05:00
Shyotl
d1b19aa389 Automatically disable new texture batching when using legacy renderpath. 2011-10-17 12:19:43 -05:00
Shyotl
06048ae818 Fixed avatars being invisible when using legacy path. Assumption of no shader use when LLGLSLShader::sNoFixedFunctionis false is not valid with Singularity. 2011-10-17 12:16:57 -05:00
Shyotl
18e5b94615 Shouldn't need this glFinish as SwapBuffers does the same thing. 2011-10-17 12:12:47 -05:00
TighMacFanatic
ff786ad2c7 Merge branch 'future' of https://github.com/Shyotl/SingularityViewer into future 2011-10-13 18:16:40 -04:00
TighMacFanatic
c95696e8a0 Merge branch 'future' of https://github.com/siana/SingularityViewer into future 2011-10-13 18:13:16 -04:00
TighMacFanatic
4dbed63f0b Merge branch 'master' of https://github.com/siana/SingularityViewer into future 2011-10-13 18:13:04 -04:00
Aleric Inglewood
3de99315e2 Some AIFrameTimer code cleanup and comment fixes. 2011-08-14 15:42:46 +02:00
Aleric Inglewood
5945793031 Implementation of AIFrameTimer. 2011-08-13 17:32:50 +02:00
Aleric Inglewood
449e7b2a84 Merge remote-tracking branch 'origin/master' 2011-08-13 17:32:27 +02:00
Siana Gearz
cdebc1c5cc Version 1.5.10(2) 2011-08-09 03:57:57 +02:00
Aleric Inglewood
e95ee85804 Make LLFrameTimer thread-safe.
LLFrameTimer::sFrameTime is accessed by the texture
thread as well. Although the only consequences are
that it's possible for a timer in the texture thread
to time out too early (or to never time out when
it's started) when it reads this variable at the
same time as that it is updated, which is pretty
inlikely, it's just not-done to leave anything
thread-unsafe when it's known to be thread-unsafe.

This patch also adds a framework for AIFrameTimer, but
that isn't implemented yet.
2011-08-08 19:21:27 +02:00
Aleric Inglewood
5e69c9fa05 Merge remote-tracking branch 'origin/master' 2011-08-08 19:20:39 +02:00
Aleric Inglewood
370890b704 We use is_main_thread() for this. 2011-08-06 13:45:01 +02:00
1290 changed files with 95231 additions and 41913 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/installed.xml /installed.xml
/indra/llcommon/llversionviewer.h
/indra/build-* /indra/build-*
/indra/tools/vstool/obj/ /indra/tools/vstool/obj/
*.aps *.aps
@@ -11,11 +12,13 @@
/indra/viewer-* /indra/viewer-*
/indra/newview/vivox-runtime/ /indra/newview/vivox-runtime/
/libraries/ /libraries/
/lib/
*.pyc *.pyc
*.orig *.orig
*.rej *.rej
*.bak *.bak
*~ *~
*.DS_Store
/LICENSES/ /LICENSES/
/edited-files.txt /edited-files.txt
qtcreator-build/ qtcreator-build/

View File

@@ -1,23 +0,0 @@
http://www.jclark.com/xml/copying.txt
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,4 +0,0 @@
This is a modified version of openal-soft from GIT (56cc03860378e2758370b773b2a6f1b4e086b49a).
The modified source which this was built from is available here:
http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/openal-soft-linden-56cc03860378e2758370b773b2a6f1b4e086b49a.tar.bz2

View File

@@ -670,19 +670,19 @@
<key>EstateChangeInfo</key> <key>EstateChangeInfo</key>
<boolean>true</boolean> <boolean>true</boolean>
<key>FetchInventoryDescendents</key> <key>FetchInventoryDescendents2</key>
<boolean>false</boolean> <boolean>false</boolean>
<key>WebFetchInventoryDescendents</key> <key>WebFetchInventoryDescendents</key>
<boolean>false</boolean> <boolean>false</boolean>
<key>FetchInventory</key> <key>FetchInventory2</key>
<boolean>true</boolean> <boolean>true</boolean>
<key>FetchLibDescendents</key> <key>FetchLibDescendents2</key>
<boolean>true</boolean> <boolean>true</boolean>
<key>FetchLib</key> <key>FetchLib2</key>
<boolean>true</boolean> <boolean>true</boolean>
</map> </map>

View File

@@ -26,7 +26,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(Variables) include(Variables)
# Load versions now. Install locations need them. # Load versions now. Install locations need them.
include(Versions) include(BuildVersion)
include(UnixInstall) include(UnixInstall)

View File

@@ -28,6 +28,9 @@ set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;ReleaseSSE2;Debug" CACHE S
# Platform-specific compilation flags. # Platform-specific compilation flags.
if (WINDOWS) if (WINDOWS)
# Remove default /Zm1000 flag that cmake inserts
string (REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# Don't build DLLs. # Don't build DLLs.
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF)
@@ -67,6 +70,10 @@ if (WINDOWS)
/Oy- /Oy-
) )
# configure win32 API for windows XP+ compatibility
set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)")
add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}")
if(MSVC80 OR MSVC90 OR MSVC10) if(MSVC80 OR MSVC90 OR MSVC10)
set(CMAKE_CXX_FLAGS_RELEASE set(CMAKE_CXX_FLAGS_RELEASE
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0" "${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
@@ -113,7 +120,7 @@ if (WINDOWS)
endif (WINDOWS) endif (WINDOWS)
set (GCC_EXTRA_OPTIMIZATION "-ffast-math -frounding-math") set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math")
if (LINUX) if (LINUX)
set(CMAKE_SKIP_RPATH TRUE) set(CMAKE_SKIP_RPATH TRUE)
@@ -156,7 +163,7 @@ if (LINUX)
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat") endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION}) endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
#Lets actualy get a numerical version of gxx's version #Lets actually get a numerical version of gxx's version
STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION}) STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION})
#gcc 4.3 and above don't like the LL boost #gcc 4.3 and above don't like the LL boost
@@ -182,29 +189,8 @@ if (LINUX)
-pthread -pthread
) )
if (SERVER) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-60")
if (EXISTS /etc/debian_version)
FILE(READ /etc/debian_version DEBIAN_VERSION)
else (EXISTS /etc/debian_version)
set(DEBIAN_VERSION "")
endif (EXISTS /etc/debian_version)
if (NOT DEBIAN_VERSION STREQUAL "3.1")
add_definitions(-DCTYPE_WORKAROUND)
endif (NOT DEBIAN_VERSION STREQUAL "3.1")
if (EXISTS /usr/lib/mysql4/mysql)
link_directories(/usr/lib/mysql4/mysql)
endif (EXISTS /usr/lib/mysql4/mysql)
add_definitions(
-msse2
-mfpmath=sse
)
endif (SERVER)
if (VIEWER)
add_definitions(-DAPPID=secondlife) add_definitions(-DAPPID=secondlife)
add_definitions(-fvisibility=hidden) add_definitions(-fvisibility=hidden)
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway. # don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
@@ -223,12 +209,11 @@ if (LINUX)
if (NOT STANDALONE) if (NOT STANDALONE)
set(MARCH_FLAG " -march=pentium4") set(MARCH_FLAG " -march=pentium4")
endif (NOT STANDALONE) endif (NOT STANDALONE)
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 "${GCC_EXTRA_OPTIMIZATIONS}) set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 "${GCC_EXTRA_OPTIMIZATIONS}) set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
endif (${ARCH} STREQUAL "x86_64") endif (${ARCH} STREQUAL "x86_64")
endif (VIEWER)
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2") set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 ${CMAKE_CXX_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-O3 ${CMAKE_CXX_FLAGS_RELEASE}")
@@ -258,13 +243,13 @@ endif (DARWIN)
if (LINUX OR DARWIN) if (LINUX OR DARWIN)
set(GCC_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs -Wno-non-virtual-dtor -Woverloaded-virtual") set(GCC_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs")
if (NOT GCC_DISABLE_FATAL_WARNINGS) if (NOT GCC_DISABLE_FATAL_WARNINGS)
set(GCC_WARNINGS "${GCC_WARNINGS} -Werror") set(GCC_WARNINGS "${GCC_WARNINGS} -Werror")
endif (NOT GCC_DISABLE_FATAL_WARNINGS) endif (NOT GCC_DISABLE_FATAL_WARNINGS)
set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder") set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder -Wno-non-virtual-dtor -Woverloaded-virtual")
set(CMAKE_C_FLAGS "${GCC_WARNINGS} ${CMAKE_C_FLAGS}") set(CMAKE_C_FLAGS "${GCC_WARNINGS} ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${GCC_CXX_WARNINGS} ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${GCC_CXX_WARNINGS} ${CMAKE_CXX_FLAGS}")
@@ -298,10 +283,6 @@ if(1 EQUAL 1)
add_definitions(-DMESH_ENABLED=1) add_definitions(-DMESH_ENABLED=1)
endif(1 EQUAL 1) endif(1 EQUAL 1)
if(SERVER)
include_directories(${LIBS_PREBUILT_DIR}/include/havok)
endif(SERVER)
SET( CMAKE_EXE_LINKER_FLAGS_RELEASESSE2 SET( CMAKE_EXE_LINKER_FLAGS_RELEASESSE2
"${CMAKE_EXE_LINKER_FLAGS_RELEASE}" CACHE STRING "${CMAKE_EXE_LINKER_FLAGS_RELEASE}" CACHE STRING
"Flags used for linking binaries under SSE2 build." "Flags used for linking binaries under SSE2 build."

View File

@@ -29,12 +29,12 @@ else (STANDALONE)
) )
elseif (DARWIN) elseif (DARWIN)
set(APR_LIBRARIES set(APR_LIBRARIES
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.3.7.dylib debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.dylib
) )
set(APRUTIL_LIBRARIES set(APRUTIL_LIBRARIES
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.3.8.dylib debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.dylib
) )
set(APRICONV_LIBRARIES iconv) set(APRICONV_LIBRARIES iconv)
else (WINDOWS) else (WINDOWS)

View File

@@ -6,6 +6,11 @@ set(DB_FIND_REQUIRED ON)
if (STANDALONE) if (STANDALONE)
include(FindBerkeleyDB) include(FindBerkeleyDB)
else (STANDALONE) else (STANDALONE)
if (LINUX)
# Need to add dependency pthread explicitely to support ld.gold.
set(DB_LIBRARIES db-4.2 pthread)
else (LINUX)
set(DB_LIBRARIES db-4.2) set(DB_LIBRARIES db-4.2)
endif (LINUX)
set(DB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) set(DB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
endif (STANDALONE) endif (STANDALONE)

View File

@@ -1,23 +1,45 @@
# -*- cmake -*- # -*- cmake -*-
function (build_version _target) # Read version components from the header file.
# Read version components from the header file. file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversionviewer.h.in lines
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversion${_target}.h lines
REGEX " LL_VERSION_") REGEX " LL_VERSION_")
foreach(line ${lines}) foreach(line ${lines})
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}") string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}") string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
set(v${comp} "${value}") set(v${comp} "${value}")
endforeach(line) endforeach(line)
# Compose the version. execute_process(
set(${_target}_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}") COMMAND git rev-list HEAD
if (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") OUTPUT_VARIABLE GIT_REV_LIST_STR
message(STATUS "Version of ${_target} is ${${_target}_VERSION}") WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
else (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") OUTPUT_STRIP_TRAILING_WHITESPACE
message(FATAL_ERROR "Could not determine ${_target} version (${${_target}_VERSION})") )
endif (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
# Report version to caller. if(GIT_REV_LIST_STR)
set(${_target}_VERSION "${${_target}_VERSION}" PARENT_SCOPE) string(REPLACE "\n" ";" GIT_REV_LIST ${GIT_REV_LIST_STR})
endfunction (build_version) else()
string(REPLACE "\n" ";" GIT_REV_LIST "")
endif()
if(GIT_REV_LIST)
list(LENGTH GIT_REV_LIST vBUILD)
else()
set(vBUILD 99)
endif()
configure_file(
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h.in
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h
)
# Compose the version.
set(viewer_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
if (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(STATUS "Version is ${viewer_VERSION}")
else (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(FATAL_ERROR "Could not determine version (${viewer_VERSION})")
endif (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
# Report version to caller.
#set(viewer_VERSION "${viewer_VERSION}" PARENT_SCOPE)

View File

@@ -19,5 +19,5 @@ else (STANDALONE)
else (WINDOWS) else (WINDOWS)
set(CARES_LIBRARIES cares) set(CARES_LIBRARIES cares)
endif (WINDOWS) endif (WINDOWS)
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares) set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares ${LIBS_PREBUILT_DIR}/include/ares )
endif (STANDALONE) endif (STANDALONE)

View File

@@ -79,11 +79,14 @@ set(cmake_SOURCE_FILES
UI.cmake UI.cmake
UnixInstall.cmake UnixInstall.cmake
Variables.cmake Variables.cmake
Versions.cmake
XmlRpcEpi.cmake XmlRpcEpi.cmake
ZLIB.cmake ZLIB.cmake
) )
if(FMODEX)
list(APPEND cmake_SOURCE_FILES FMODEX.cmake)
endif(FMODEX)
source_group("Shared Rules" FILES ${cmake_SOURCE_FILES}) source_group("Shared Rules" FILES ${cmake_SOURCE_FILES})
set(master_SOURCE_FILES set(master_SOURCE_FILES

View File

@@ -243,13 +243,52 @@ set(all_targets ${all_targets} ${out_targets})
set(release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release") set(release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release")
set(release_files set(release_files
libtcmalloc_minimal.dll libtcmalloc_minimal.dll
fmod.dll
libhunspell.dll libhunspell.dll
libapr-1.dll libapr-1.dll
libaprutil-1.dll libaprutil-1.dll
libapriconv-1.dll libapriconv-1.dll
) )
if(FMODEX)
find_path(FMODEX_BINARY_DIR fmodex.dll
${release_src_dir}
${FMODEX_SDK_DIR}/api
${FMODEX_SDK_DIR}
)
if(FMODEX_BINARY_DIR)
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ReleaseSSE2" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
endif(FMODEX_BINARY_DIR)
endif(FMODEX)
if(FMOD)
find_path(FMOD_BINARY_DIR fmod.dll
${release_src_dir}
${FMOD_SDK_DIR}/api
${FMOD_SDK_DIR}
)
if(FMOD_BINARY_DIR)
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ReleaseSSE2" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
else(FMOD_BINARY_DIR)
list(APPEND release_files fmod.dll) #Required for compile. This will cause an error in copying binaries.
endif(FMOD_BINARY_DIR)
endif(FMOD)
copy_if_different( copy_if_different(
${release_src_dir} ${release_src_dir}
"${CMAKE_CURRENT_BINARY_DIR}/Release" "${CMAKE_CURRENT_BINARY_DIR}/Release"

View File

@@ -6,6 +6,7 @@ set(install_dir "@CMAKE_SOURCE_DIR@/..")
set(scp "@SCP_EXECUTABLE@") set(scp "@SCP_EXECUTABLE@")
set(scripts_dir "@SCRIPTS_DIR@") set(scripts_dir "@SCRIPTS_DIR@")
set(sentinel_dir "@CMAKE_BINARY_DIR@/prepare") set(sentinel_dir "@CMAKE_BINARY_DIR@/prepare")
set(prebuilt_type "@PREBUILT_TYPE@")
# In proprietary mode we use scp for download. # In proprietary mode we use scp for download.
set(proprietary "@INSTALL_PROPRIETARY@") set(proprietary "@INSTALL_PROPRIETARY@")
@@ -19,7 +20,7 @@ foreach(package ${packages})
# This package is missing or out of date. # This package is missing or out of date.
message(STATUS "Obtaining${proprietary_message} prebuilt '${package}'") message(STATUS "Obtaining${proprietary_message} prebuilt '${package}'")
execute_process( execute_process(
COMMAND ${python} install.py --install-dir=${install_dir} ${scp_option} ${package} COMMAND ${python} install.py -p${prebuilt_type} --install-dir=${install_dir} ${scp_option} ${package}
WORKING_DIRECTORY ${scripts_dir} WORKING_DIRECTORY ${scripts_dir}
RESULT_VARIABLE result RESULT_VARIABLE result
) )

View File

@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
use_prebuilt_binary(fmod) use_prebuilt_binary(fmod)
endif(INSTALL_PROPRIETARY) endif(INSTALL_PROPRIETARY)
find_library(FMOD_LIBRARY_RELEASE find_library(FMOD_LIBRARY
NAMES fmod fmodvc fmod-3.75 NAMES fmod fmodvc fmod-3.75
PATHS PATHS
${ARCH_PREBUILT_DIRS_RELEASE} optimized ${ARCH_PREBUILT_DIRS_RELEASE}
debug ${ARCH_PREBUILT_DIRS_DEBUG}
) )
find_library(FMOD_LIBRARY_DEBUG
NAMES fmod fmodvc fmod-3.75
PATHS
${ARCH_PREBUILT_DIRS_DEBUG}
)
if (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
set(FMOD_LIBRARY
debug ${FMOD_LIBRARY_DEBUG}
optimized ${FMOD_LIBRARY_RELEASE})
elseif (FMOD_LIBRARY_RELEASE)
set(FMOD_LIBRARY ${FMOD_LIBRARY_RELEASE})
endif (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
if (NOT FMOD_LIBRARY) if (NOT FMOD_LIBRARY)
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.") set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
if (FMOD_SDK_DIR) if (FMOD_SDK_DIR)

70
indra/cmake/FMODEX.cmake Normal file
View File

@@ -0,0 +1,70 @@
# -*- cmake -*-
include(Linking)
if(INSTALL_PROPRIETARY)
include(Prebuilt)
use_prebuilt_binary(fmodex)
endif(INSTALL_PROPRIETARY)
find_library(FMODEX_LIBRARY
NAMES fmodex fmodex_vc fmodexL_vc
PATHS
optimized ${ARCH_PREBUILT_DIRS_RELEASE}
debug ${ARCH_PREBUILT_DIRS_DEBUG}
)
if (NOT FMODEX_LIBRARY)
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
if (FMODEX_SDK_DIR)
find_library(FMODEX_LIBRARY
fmodex fmodex_vc fmodexL_vc
PATHS
${FMODEX_SDK_DIR}/api/lib
${FMODEX_SDK_DIR}/api
${FMODEX_SDK_DIR}/lib
${FMODEX_SDK_DIR}
)
endif(FMODEX_SDK_DIR)
if(WINDOWS AND NOT FMODEX_LIBRARY)
set(FMODEX_PROG_DIR "$ENV{PROGRAMFILES}/FMOD SoundSystem/FMOD Programmers API Windows")
find_library(FMODEX_LIBRARY
fmodex_vc fmodexL_vc
PATHS
${FMODEX_PROG_DIR}/api/lib
${FMODEX_PROG_DIR}/api
${FMODEX_PROG_DIR}
)
if(FMODEX_LIBRARY)
message(STATUS "Found fmodex in ${FMODEX_PROG_DIR}")
set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR})
set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE)
endif(FMODEX_LIBRARY)
endif(WINDOWS AND NOT FMODEX_LIBRARY)
endif (NOT FMODEX_LIBRARY)
find_path(FMODEX_INCLUDE_DIR fmod.h
${LIBS_PREBUILT_DIR}/include/fmodex
${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodex
${FMODEX_SDK_DIR}/api/inc
${FMODEX_SDK_DIR}/inc
${FMODEX_SDK_DIR}
)
if (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
set(FMODEX ON CACHE BOOL "Use closed source FMOD Ex sound library.")
else (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
set(FMODEX_LIBRARY "")
set(FMODEX_INCLUDE_DIR "")
if (FMODEX)
message(STATUS "No support for FMOD Ex audio (need to set FMODEX_SDK_DIR?)")
endif (FMODEX)
set(FMODEX OFF CACHE BOOL "Use closed source FMOD Ex sound library.")
set(FMODEX OFF)
endif (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
if (FMODEX)
message(STATUS "Building with FMOD Ex audio support")
endif (FMODEX)

View File

@@ -9,7 +9,7 @@
# also defined, but not for general use are # also defined, but not for general use are
# JSONCPP_LIBRARY, where to find the jsoncpp library. # JSONCPP_LIBRARY, where to find the jsoncpp library.
FIND_PATH(JSONCPP_INCLUDE_DIR jsoncpp/json.h FIND_PATH(JSONCPP_INCLUDE_DIR json/json.h
/usr/local/include /usr/local/include
/usr/include /usr/include
) )

View File

@@ -1,10 +1,14 @@
# -*- cmake -*- # -*- cmake -*-
include(Prebuilt) include(Prebuilt)
if(WORD_SIZE EQUAL 64)
set(DISABLE_TCMALLOC TRUE)
endif()
if (STANDALONE) if (STANDALONE)
include(FindGooglePerfTools) include(FindGooglePerfTools)
else (STANDALONE) else (STANDALONE)
if (LINUX OR WINDOWS) if (LINUX OR WINDOWS AND NOT WORD_SIZE EQUAL 64)
use_prebuilt_binary(google) use_prebuilt_binary(google)
endif (LINUX OR WINDOWS) endif (LINUX OR WINDOWS)
if (WINDOWS) if (WINDOWS)
@@ -30,10 +34,6 @@ else ()
set(USE_GOOGLE_PERFTOOLS OFF) set(USE_GOOGLE_PERFTOOLS OFF)
endif () endif ()
# XXX Disable temporarily, until we have compilation issues on 64-bit
# Etch sorted.
#set(USE_GOOGLE_PERFTOOLS OFF)
if (USE_GOOGLE_PERFTOOLS) if (USE_GOOGLE_PERFTOOLS)
set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1) set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR}) include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})

View File

@@ -14,11 +14,19 @@ endif (DARWIN)
set(LLCOMMON_INCLUDE_DIRS set(LLCOMMON_INCLUDE_DIRS
${LIBS_OPEN_DIR}/cwdebug ${LIBS_OPEN_DIR}/cwdebug
${LIBS_OPEN_DIR}/llcommon ${LIBS_OPEN_DIR}/llcommon
${APRUTIL_INCLUDE_DIR}
${APR_INCLUDE_DIR} ${APR_INCLUDE_DIR}
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
) )
set(LLCOMMON_LIBRARIES llcommon) if (LINUX)
# In order to support using ld.gold on linux, we need to explicitely
# specify all libraries that llcommon uses.
# llcommon uses `clock_gettime' which is provided by librt on linux.
set(LLCOMMON_LIBRARIES llcommon rt)
else (LINUX)
set(LLCOMMON_LIBRARIES llcommon)
endif (LINUX)
set(LLCOMMON_LINK_SHARED ON CACHE BOOL "Build the llcommon target as a shared library.") set(LLCOMMON_LINK_SHARED ON CACHE BOOL "Build the llcommon target as a shared library.")
if(LLCOMMON_LINK_SHARED) if(LLCOMMON_LINK_SHARED)

View File

@@ -5,4 +5,10 @@ set(LLPLUGIN_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llplugin ${LIBS_OPEN_DIR}/llplugin
) )
set(LLPLUGIN_LIBRARIES llplugin) if (LINUX)
# In order to support using ld.gold on linux, we need to explicitely
# specify all libraries that llplugin uses.
set(LLPLUGIN_LIBRARIES llplugin pthread)
else (LINUX)
set(LLPLUGIN_LIBRARIES llplugin)
endif (LINUX)

View File

@@ -1,10 +1,10 @@
# -*- cmake -*- # -*- cmake -*-
include(Prebuilt) include(Prebuilt)
if (NOT STANDALONE) if (NOT (STANDALONE OR DARWIN))
use_prebuilt_binary(glext) use_prebuilt_binary(glext)
# possible glh_linear should have its own .cmake file instead # possible glh_linear should have its own .cmake file instead
#use_prebuilt_binary(glh_linear) #use_prebuilt_binary(glh_linear)
# actually... not any longer, it's now in git -SG # actually... not any longer, it's now in git -SG
set(GLEXT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include) set(GLEXT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
endif (NOT STANDALONE) endif ()

View File

@@ -19,7 +19,6 @@ if (WINDOWS)
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
) )
elseif (EXISTS /etc/debian_version) elseif (EXISTS /etc/debian_version)
# On Debian and Ubuntu, avoid Python 2.4 if possible. # On Debian and Ubuntu, avoid Python 2.4 if possible.

View File

@@ -38,6 +38,7 @@ else (STANDALONE)
if (LINUX) if (LINUX)
set(UI_LIBRARIES set(UI_LIBRARIES
atk-1.0 atk-1.0
X11
gdk-x11-2.0 gdk-x11-2.0
gdk_pixbuf-2.0 gdk_pixbuf-2.0
Xinerama Xinerama

View File

@@ -76,24 +76,16 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(DARWIN 1) set(DARWIN 1)
# NOTE: If specifying a different SDK with CMAKE_OSX_SYSROOT at configure #SDK Compiler and Deployment targets for XCode
# time you should also specify CMAKE_OSX_DEPLOYMENT_TARGET explicitly, if (${XCODE_VERSION} VERSION_LESS 4.0.0)
# otherwise CMAKE_OSX_SYSROOT will be overridden here. We can't just check
# for it being unset, as it gets set to the system default :(
# Default to building against the 10.5 SDK if no deployment target is
# specified.
if (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
# NOTE: setting -isysroot is NOT adequate: http://lists.apple.com/archives/Xcode-users/2007/Oct/msg00696.html
# see http://public.kitware.com/Bug/view.php?id=9959 + poppy
set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk) set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5) set(CMAKE_XCODE_ATTIBUTE_GCC_VERSION "4.2")
endif (NOT CMAKE_OSX_DEPLOYMENT_TARGET) else (${XCODE_VERSION} VERSION_LESS 4.0.0)
set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.6.sdk)
set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvmgcc42")
endif (${XCODE_VERSION} VERSION_LESS 4.0.0)
# Use GCC 4.2 set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5)
if (${CMAKE_OSX_SYSROOT} MATCHES "10.5")
set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "4.2")
endif (${CMAKE_OSX_SYSROOT} MATCHES "10.5")
# NOTE: To attempt an i386/PPC Universal build, add this on the configure line: # NOTE: To attempt an i386/PPC Universal build, add this on the configure line:
# -DCMAKE_OSX_ARCHITECTURES:STRING='i386;ppc' # -DCMAKE_OSX_ARCHITECTURES:STRING='i386;ppc'
@@ -118,6 +110,17 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if (WINDOWS)
set(PREBUILT_TYPE windows)
elseif(DARWIN)
set(PREBUILT_TYPE darwin)
elseif(LINUX AND WORD_SIZE EQUAL 32)
set(PREBUILT_TYPE linux)
elseif(LINUX AND WORD_SIZE EQUAL 64)
set(PREBUILT_TYPE linux64)
endif(WINDOWS)
# Default deploy grid # Default deploy grid
set(GRID agni CACHE STRING "Target Grid") set(GRID agni CACHE STRING "Target Grid")

View File

@@ -1,9 +0,0 @@
include(BuildVersion)
if(VIEWER)
build_version(viewer)
endif(VIEWER)
if(SERVER)
build_version(server)
endif(SERVER)

View File

@@ -67,12 +67,19 @@ elseif (LINUX)
QtNetwork QtNetwork
QtGui QtGui
QtCore QtCore
jscore crypto
ssl
# qgif
# qjpeg
jpeg jpeg
fontconfig fontconfig
X11 X11
Xrender Xrender
Xext
GL GL
) )
if (WORD_SIZE EQUAL 32)
set(WEBKIT_PLUGIN_LIBRARIES ${WEBKIT_PLUGIN_LIBRARIES} jscore)
endif (WORD_SIZE EQUAL 32)
endif (STANDALONE) endif (STANDALONE)
endif (WINDOWS) endif (WINDOWS)

View File

@@ -171,7 +171,8 @@ void stop_recording_backtraces(void)
channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk. channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk.
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking. channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces. channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used class AIStateMachine. channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine.
channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities.
} // namespace dc } // namespace dc
} // namespace DEBUGCHANNELS } // namespace DEBUGCHANNELS

View File

@@ -117,6 +117,7 @@ extern CWD_API channel_ct gtk;
extern CWD_API channel_ct sdl; extern CWD_API channel_ct sdl;
extern CWD_API channel_ct backtrace; extern CWD_API channel_ct backtrace;
extern CWD_API channel_ct statemachine; extern CWD_API channel_ct statemachine;
extern CWD_API channel_ct caps;
#endif #endif

View File

@@ -777,7 +777,6 @@ Options:
Commands: Commands:
build configure and build default target build configure and build default target
clean delete all build directories, does not affect sources clean delete all build directories, does not affect sources
configure configure project by running cmake (default command if none given)
configure configure project by running cmake (default if none given) configure configure project by running cmake (default if none given)
printbuilddirs print the build directory that will be used printbuilddirs print the build directory that will be used

View File

@@ -168,7 +168,10 @@ ARGUMENTS=[
dict(name='version', dict(name='version',
description="""This specifies the version of Second Life that is description="""This specifies the version of Second Life that is
being packaged up.""", being packaged up.""",
default=get_default_version) default=get_default_version),
dict(name='extra_libraries',
description="""List of extra libraries to include in package""",
default=None)
] ]
def usage(srctree=""): def usage(srctree=""):

View File

@@ -1,73 +1,18 @@
# Main CMakeLists.txt to build the OpenJPEG project using CMake (www.cmake.org) # -*- cmake -*-
# Written by Mathieu Malaterre
CMAKE_MINIMUM_REQUIRED(VERSION 2.6) project(openjpeg)
IF(COMMAND CMAKE_POLICY) include(00-Common)
CMAKE_POLICY(SET CMP0003 NEW)
ENDIF(COMMAND CMAKE_POLICY)
PROJECT(openjpeg)
# Do full dependency headers.
INCLUDE_REGULAR_EXPRESSION("^.*$")
#-----------------------------------------------------------------------------
# OPENJPEG version number, useful for packaging and doxygen doc: # OPENJPEG version number, useful for packaging and doxygen doc:
SET(OPENJPEG_VERSION_MAJOR 1) set(OPENJPEG_VERSION_MAJOR 1)
SET(OPENJPEG_VERSION_MINOR 4) set(OPENJPEG_VERSION_MINOR 4)
SET(OPENJPEG_VERSION_BUILD 0) set(OPENJPEG_VERSION_BUILD 0)
SET(OPENJPEG_VERSION set(OPENJPEG_VERSION
"${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}") "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}")
# This setting of SOVERSION assumes that any API change set(openjpeg_SOURCE_FILES
# will increment either the minor or major version number of openjpeg
SET(OPENJPEG_LIBRARY_PROPERTIES
VERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}"
SOVERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}"
)
# On Visual Studio 8 MS deprecated C. This removes all 1.276E1265 security
# warnings
IF(WIN32)
IF(NOT BORLAND)
IF(NOT CYGWIN)
IF(NOT MINGW)
IF(NOT ITK_ENABLE_VISUAL_STUDIO_DEPRECATED_C_WARNINGS)
ADD_DEFINITIONS(
-D_CRT_FAR_MAPPINGS_NO_DEPRECATE
-D_CRT_IS_WCTYPE_NO_DEPRECATE
-D_CRT_MANAGED_FP_NO_DEPRECATE
-D_CRT_NONSTDC_NO_DEPRECATE
-D_CRT_SECURE_NO_DEPRECATE
-D_CRT_SECURE_NO_DEPRECATE_GLOBALS
-D_CRT_SETERRORMODE_BEEP_SLEEP_NO_DEPRECATE
-D_CRT_TIME_FUNCTIONS_NO_DEPRECATE
-D_CRT_VCCLRIT_NO_DEPRECATE
-D_SCL_SECURE_NO_DEPRECATE
)
ENDIF(NOT ITK_ENABLE_VISUAL_STUDIO_DEPRECATED_C_WARNINGS)
ENDIF(NOT MINGW)
ENDIF(NOT CYGWIN)
ENDIF(NOT BORLAND)
ENDIF(WIN32)
#-----------------------------------------------------------------------------
# Test for some required system information.
# INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityC.cmake)
#-----------------------------------------------------------------------------
# Compiler specific flags:
IF(CMAKE_COMPILER_IS_GNUCC)
# For all builds, make sure openjpeg is std99 compliant:
# SET(CMAKE_C_FLAGS "-Wall -std=c99 ${CMAKE_C_FLAGS}") # FIXME: this setting prevented us from setting a coverage build.
# Do not use ffast-math for all build, it would produce incorrect results, only set for release:
SET(CMAKE_C_FLAGS_RELEASE "-ffast-math ${CMAKE_C_FLAGS_RELEASE}")
ENDIF(CMAKE_COMPILER_IS_GNUCC)
# Defines the source code for the library
SET(OPENJPEG_SRCS
bio.c bio.c
cio.c cio.c
dwt.c dwt.c
@@ -88,18 +33,52 @@ SET(OPENJPEG_SRCS
tgt.c tgt.c
) )
# Pass proper definition to preprocessor to generate shared lib set(openjpeg_HEADER_FILES
IF(WIN32) bio.h
IF(BUILD_SHARED_LIBS) cio.h
ADD_DEFINITIONS(-DOPJ_EXPORTS) dwt.h
ELSE(BUILD_SHARED_LIBS) event.h
ADD_DEFINITIONS(-DOPJ_STATIC) fix.h
ENDIF(BUILD_SHARED_LIBS) image.h
ENDIF(WIN32) int.h
j2k.h
j2k_lib.h
jp2.h
jpt.h
mct.h
mqc.h
openjpeg.h
opj_includes.h
opj_malloc.h
pi.h
raw.h
t1.h
t1_luts.h
t2.h
tcd.h
tgt.h
)
# Create the library IF(WINDOWS)
ADD_LIBRARY(openjpeg ${OPENJPEG_SRCS}) add_definitions(-D_CRT_SECURE_NO_WARNINGS)
SET_TARGET_PROPERTIES(openjpeg PROPERTIES add_definitions(-DOPJ_STATIC)
ENDIF(WINDOWS)
set_source_files_properties(${openjpeg_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND openjpeg_SOURCE_FILES ${openjpeg_HEADER_FILES})
add_library (openjpeg ${openjpeg_SOURCE_FILES})
# This setting of SOVERSION assumes that any API change
# will increment either the minor or major version number of openjpeg
set(OPENJPEG_LIBRARY_PROPERTIES
VERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}"
SOVERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}"
)
set_target_properties(openjpeg PROPERTIES
${OPENJPEG_LIBRARY_PROPERTIES}) ${OPENJPEG_LIBRARY_PROPERTIES})

View File

@@ -527,7 +527,7 @@ static void dwt_decode_tile(opj_tcd_tilecomp_t* tilec, int numres, DWT1DFN dwt_1
int w = tilec->x1 - tilec->x0; int w = tilec->x1 - tilec->x0;
h.mem = opj_aligned_malloc(dwt_decode_max_resolution(tr, numres) * sizeof(int)); h.mem = (int *)opj_aligned_malloc(dwt_decode_max_resolution(tr, numres) * sizeof(int));
v.mem = h.mem; v.mem = h.mem;
while( --numres) { while( --numres) {
@@ -570,7 +570,7 @@ static void v4dwt_interleave_h(v4dwt_t* restrict w, float* restrict a, int x, in
int count = w->sn; int count = w->sn;
int i, k; int i, k;
for(k = 0; k < 2; ++k){ for(k = 0; k < 2; ++k){
if (count + 3 * x < size && ((int) a & 0x0f) == 0 && ((int) bi & 0x0f) == 0 && (x & 0x0f) == 0) { if (count + 3 * x < size && ((long) a & 0x0f) == 0 && ((long) bi & 0x0f) == 0 && (x & 0x0f) == 0) {
/* Fast code path */ /* Fast code path */
for(i = 0; i < count; ++i){ for(i = 0; i < count; ++i){
int j = i; int j = i;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2005, Herv<EFBFBD> Drolon, FreeImage Team * Copyright (c) 2005, Hervé Drolon, FreeImage Team
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -26,41 +26,6 @@
#include "opj_includes.h" #include "opj_includes.h"
/* ==========================================================
Utility functions
==========================================================*/
#if !defined(_MSC_VER) && !defined(__MINGW32__)
static char*
i2a(unsigned i, char *a, unsigned r) {
if (i/r > 0) a = i2a(i/r,a,r);
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
return a+1;
}
/**
Transforms integer i into an ascii string and stores the result in a;
string is encoded in the base indicated by r.
@param i Number to be converted
@param a String result
@param r Base of value; must be in the range 2 - 36
@return Returns a
*/
static char *
_itoa(int i, char *a, int r) {
r = ((r < 2) || (r > 36)) ? 10 : r;
if(i < 0) {
*a = '-';
*i2a(-i, a+1, r) = 0;
}
else *i2a(i, a, r) = 0;
return a;
}
#endif /* !WIN32 */
/* ----------------------------------------------------------------------- */
opj_event_mgr_t* OPJ_CALLCONV opj_set_event_mgr(opj_common_ptr cinfo, opj_event_mgr_t *event_mgr, void *context) { opj_event_mgr_t* OPJ_CALLCONV opj_set_event_mgr(opj_common_ptr cinfo, opj_event_mgr_t *event_mgr, void *context) {
if(cinfo) { if(cinfo) {
opj_event_mgr_t *previous = cinfo->event_mgr; opj_event_mgr_t *previous = cinfo->event_mgr;

View File

@@ -236,7 +236,7 @@ static void j2k_read_unk(opj_j2k_t *j2k);
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
typedef struct j2k_prog_order{ typedef struct j2k_prog_order{
OPJ_PROG_ORDER enum_prog; OPJ_PROG_ORDER enum_prog;
char str_prog[4]; char str_prog[5];
}j2k_prog_order_t; }j2k_prog_order_t;
j2k_prog_order_t j2k_prog_order_list[] = { j2k_prog_order_t j2k_prog_order_list[] = {

View File

@@ -38,7 +38,7 @@ list(APPEND linux_crash_logger_SOURCE_FILES
${linux_crash_logger_HEADER_FILES} ${linux_crash_logger_HEADER_FILES}
) )
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt -lapr-1 -Wl,--as-needed") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES}) add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES})

View File

@@ -5,16 +5,30 @@ project(llaudio)
include(00-Common) include(00-Common)
include(Audio) include(Audio)
include(LLAudio) include(LLAudio)
include(FMOD) if(FMODEX)
include(FMODEX)
if(FMODEX)
set(FMOD OFF)
endif(FMODEX)
endif(FMODEX)
if(NOT FMODEX)
include(FMOD)
endif(NOT FMODEX)
include(OPENAL) include(OPENAL)
include(LLCommon) include(LLCommon)
include(LLMath) include(LLMath)
include(LLMessage) include(LLMessage)
include(LLVFS) include(LLVFS)
if(FMODEX)
include_directories(${FMODEX_INCLUDE_DIR})
endif(FMODEX)
if(FMOD)
include_directories(${FMOD_INCLUDE_DIR})
endif(FMOD)
include_directories( include_directories(
${LLAUDIO_INCLUDE_DIRS} ${LLAUDIO_INCLUDE_DIRS}
${FMOD_INCLUDE_DIR}
${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS}
@@ -46,6 +60,19 @@ set(llaudio_HEADER_FILES
llwindgen.h llwindgen.h
) )
if (FMODEX)
list(APPEND llaudio_SOURCE_FILES
llaudioengine_fmodex.cpp
lllistener_fmodex.cpp
llstreamingaudio_fmodex.cpp
)
list(APPEND llaudio_HEADER_FILES
llaudioengine_fmodex.h
lllistener_fmodex.h
llstreamingaudio_fmodex.h
)
endif (FMODEX)
if (FMOD) if (FMOD)
list(APPEND llaudio_SOURCE_FILES list(APPEND llaudio_SOURCE_FILES
llaudioengine_fmod.cpp llaudioengine_fmod.cpp

View File

@@ -239,12 +239,15 @@ protected:
// A list of all audio sources that are known to the viewer at this time. // A list of all audio sources that are known to the viewer at this time.
// This is most likely a superset of the ones that we actually have audio // This is most likely a superset of the ones that we actually have audio
// data for, or are playing back. // data for, or are playing back.
public://Jay: IDGAF
typedef std::map<LLUUID, LLAudioSource *> source_map; typedef std::map<LLUUID, LLAudioSource *> source_map;
protected:
typedef std::map<LLUUID, LLAudioData *> data_map; typedef std::map<LLUUID, LLAudioData *> data_map;
public://Jay: IDGAF
source_map mAllSources; source_map mAllSources;
protected:
data_map mAllData; data_map mAllData;
LLAudioChannel *mChannels[MAX_CHANNELS]; LLAudioChannel *mChannels[MAX_CHANNELS];
// Buffers needs to change into a different data structure, as the number of buffers // Buffers needs to change into a different data structure, as the number of buffers
@@ -351,6 +354,9 @@ public:
protected: protected:
LLUUID mID; // The ID of the source is that of the object if it's attached to an object. LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
LLUUID mOwnerID; // owner of the object playing the sound LLUUID mOwnerID; // owner of the object playing the sound
public:
const LLUUID &getOwnerID() { return mOwnerID; }
protected:
F32 mPriority; F32 mPriority;
F32 mGain; F32 mGain;
bool mSourceMuted; bool mSourceMuted;

View File

@@ -0,0 +1,782 @@
/**
* @file audioengine_FMODEX.cpp
* @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llstreamingaudio.h"
#include "llstreamingaudio_fmodex.h"
#include "llaudioengine_fmodex.h"
#include "lllistener_fmodex.h"
#include "llerror.h"
#include "llmath.h"
#include "llrand.h"
#include "fmod.hpp"
#include "fmod_errors.h"
#include "lldir.h"
#include "llapr.h"
#include "sound_ids.h"
#if LL_WINDOWS //Some ugly code to make missing fmodex.dll not cause a fatal error.
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include <DelayImp.h>
#pragma comment(lib, "delayimp.lib")
bool attemptDelayLoad()
{
__try
{
if( FAILED( __HrLoadAllImportsForDll( "fmodex.dll" ) ) )
return false;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
return false;
}
return true;
}
#endif
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler)
{
mInited = false;
mWindGen = NULL;
mWindDSP = NULL;
mSystem = NULL;
mEnableProfiler = enable_profiler;
}
LLAudioEngine_FMODEX::~LLAudioEngine_FMODEX()
{
}
inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
{
if(result == FMOD_OK)
return false;
llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl;
return true;
}
bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata)
{
#if LL_WINDOWS
if(!attemptDelayLoad())
return false;
#endif
U32 version;
FMOD_RESULT result;
int numdrivers;
FMOD_SPEAKERMODE speakermode;
FMOD_CAPS caps;
char name[256];
LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL;
result = FMOD::System_Create(&mSystem);
if(Check_FMOD_Error(result, "FMOD::System_Create"))
return false;
//will call LLAudioEngine_FMODEX::allocateListener, which needs a valid mSystem pointer.
LLAudioEngine::init(num_channels, userdata);
result = mSystem->getVersion(&version);
Check_FMOD_Error(result, "FMOD::System::getVersion");
if (version < FMOD_VERSION)
{
LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version
<< ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL;
}
#if LL_WINDOWS
//Is this block applicable to linux?
{
result = mSystem->getNumDrivers(&numdrivers);
Check_FMOD_Error(result, "FMOD::System::getNumDrivers");
if (numdrivers == 0)
{
result = mSystem->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
Check_FMOD_Error(result, "FMOD::System::setOutput");
}
else
{
result = mSystem->getDriverCaps(0, &caps, 0, &speakermode);
Check_FMOD_Error(result,"FMOD::System::getDriverCaps");
/*
Set the user selected speaker mode.
*/
result = mSystem->setSpeakerMode(speakermode);
Check_FMOD_Error(result, "FMOD::System::setSpeakerMode");
if (caps & FMOD_CAPS_HARDWARE_EMULATED)
{
/*
The user has the 'Acceleration' slider set to off! This is really bad
for latency! You might want to warn the user about this.
*/
result = mSystem->setDSPBufferSize(1024, 10);
Check_FMOD_Error(result, "FMOD::System::setDSPBufferSize");
}
result = mSystem->getDriverInfo(0, name, 256, 0);
Check_FMOD_Error(result, "FMOD::System::getDriverInfo");
if (strstr(name, "SigmaTel"))
{
/*
Sigmatel sound devices crackle for some reason if the format is PCM 16bit.
PCM floating point output seems to solve it.
*/
result = mSystem->setSoftwareFormat(48000, FMOD_SOUND_FORMAT_PCMFLOAT, 0,0, FMOD_DSP_RESAMPLER_LINEAR);
Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat");
}
}
}
#endif //LL_WINDOWS
// In this case, all sounds, PLUS wind and stream will be software.
result = mSystem->setSoftwareChannels(num_channels+2);
Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels");
U32 fmod_flags = FMOD_INIT_NORMAL;
if(mEnableProfiler)
fmod_flags |= FMOD_INIT_ENABLE_PROFILE;
#if LL_LINUX
// If we don't set an output method, Linux FMOD always
// decides on OSS and fails otherwise. So we'll manually
// try ESD, then OSS, then ALSA.
// Why this order? See SL-13250, but in short, OSS emulated
// on top of ALSA is ironically more reliable than raw ALSA.
// Ack, and ESD has more reliable failure modes - but has worse
// latency - than all of them, so wins for now.
bool audio_ok = false;
if (!audio_ok)
{
if (NULL == getenv("LL_BAD_FMODEX_ESD")) /*Flawfinder: ignore*/
{
LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL;
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_ESD) == FMOD_OK &&
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
}
else
{
Check_FMOD_Error(result, "ESD audio output FAILED to initialize");
}
}
else
{
LL_DEBUGS("AppInit") << "ESD audio output SKIPPED" << LL_ENDL;
}
}
if (!audio_ok)
{
if (NULL == getenv("LL_BAD_FMODEX_OSS")) /*Flawfinder: ignore*/
{
LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL;
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK &&
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
}
else
{
Check_FMOD_Error(result, "OSS audio output FAILED to initialize" << LL_ENDL;
}
}
else
{
LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
}
}
if (!audio_ok)
{
if (NULL == getenv("LL_BAD_FMODEX_ALSA")) /*Flawfinder: ignore*/
{
LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_ALSA) &&
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
}
else
{
Check_FMOD_Error(result, "ALSA audio output FAILED to initialize");
}
} else
{
LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
}
}
if (!audio_ok)
{
LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
return false;
}
// On Linux, FMOD causes a SIGPIPE for some netstream error
// conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us.
// NOW FIXED in FMOD 3.x since 2006-10-01.
//signal(SIGPIPE, SIG_IGN);
// We're interested in logging which output method we
// ended up with, for QA purposes.
FMOD_OUTPUTTYPE output_type;
mSystem->getOutput(output_type);
switch (output_type)
{
case FSOUND_OUTPUT_NOSOUND:
LL_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL; break;
case FSOUND_OUTPUT_OSS:
LL_DEBUGS("AppInit") << "Audio output: OSS" << LL_ENDL; break;
case FSOUND_OUTPUT_ESD:
LL_DEBUGS("AppInit") << "Audio output: ESD" << LL_ENDL; break;
case FSOUND_OUTPUT_ALSA:
LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL; break;
default:
LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break;
};
#else // LL_LINUX
// initialize the FMOD engine
result = mSystem->init( num_channels + 2, fmod_flags, 0);
if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
{
/*
Ok, the speaker mode selected isn't supported by this soundcard. Switch it
back to stereo...
*/
result = mSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
Check_FMOD_Error(result,"Error falling back to stereo mode");
/*
... and re-init.
*/
result = mSystem->init(100, FMOD_INIT_NORMAL, 0);
}
if(Check_FMOD_Error(result, "Error initializing FMOD"))
return false;
#endif
// set up our favourite FMOD-native streaming audio implementation if none has already been added
if (!getStreamingAudioImpl()) // no existing implementation added
setStreamingAudioImpl(new LLStreamingAudio_FMODEX(mSystem));
LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() FMOD initialized correctly" << LL_ENDL;
mInited = true;
return true;
}
std::string LLAudioEngine_FMODEX::getDriverName(bool verbose)
{
llassert_always(mSystem);
if (verbose)
{
U32 version;
if(!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion"))
{
return llformat("FMOD version %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF);
}
}
return "FMOD";
}
void LLAudioEngine_FMODEX::allocateListener(void)
{
mListenerp = (LLListener *) new LLListener_FMODEX(mSystem);
if (!mListenerp)
{
llwarns << "Listener creation failed" << llendl;
}
}
void LLAudioEngine_FMODEX::shutdown()
{
stopInternetStream();
LLAudioEngine::shutdown();
llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD" << llendl;
mSystem->close();
mSystem->release();
llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD" << llendl;
delete mListenerp;
mListenerp = NULL;
}
LLAudioBuffer * LLAudioEngine_FMODEX::createBuffer()
{
return new LLAudioBufferFMODEX(mSystem);
}
LLAudioChannel * LLAudioEngine_FMODEX::createChannel()
{
return new LLAudioChannelFMODEX(mSystem);
}
bool LLAudioEngine_FMODEX::initWind()
{
mNextWindUpdate = 0.0;
if (!mWindDSP)
{
FMOD_DSP_DESCRIPTION dspdesc;
memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero
strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit"
dspdesc.read = &windCallback; //Assign callback.
if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP"))
return false;
if(mWindGen)
delete mWindGen;
float frequency = 44100;
mWindDSP->getDefaults(&frequency,0,0,0);
mWindGen = new LLWindGen<MIXBUFFERFORMAT>((U32)frequency);
mWindDSP->setUserData((void*)mWindGen);
}
if (mWindDSP)
{
mSystem->playDSP(FMOD_CHANNEL_FREE, mWindDSP, false, 0);
return true;
}
return false;
}
void LLAudioEngine_FMODEX::cleanupWind()
{
if (mWindDSP)
{
mWindDSP->remove();
mWindDSP->release();
mWindDSP = NULL;
}
delete mWindGen;
mWindGen = NULL;
}
//-----------------------------------------------------------------------
void LLAudioEngine_FMODEX::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
{
LLVector3 wind_pos;
F64 pitch;
F64 center_freq;
if (!mEnableWind)
{
return;
}
if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
{
// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
// need to convert this to the conventional orientation DS3D and OpenAL use
// where +X = right, +Y = up, +Z = backwards
wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
// cerr << "Wind update" << endl;
pitch = 1.0 + mapWindVecToPitch(wind_vec);
center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
mWindGen->mTargetFreq = (F32)center_freq;
mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
}
}
//-----------------------------------------------------------------------
void LLAudioEngine_FMODEX::setInternalGain(F32 gain)
{
if (!mInited)
{
return;
}
gain = llclamp( gain, 0.0f, 1.0f );
FMOD::ChannelGroup *master_group;
mSystem->getMasterChannelGroup(&master_group);
master_group->setVolume(gain);
LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
if ( saimpl )
{
// fmod likes its streaming audio channel gain re-asserted after
// master volume change.
saimpl->setGain(saimpl->getGain());
}
}
//
// LLAudioChannelFMODEX implementation
//
LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0)
{
}
LLAudioChannelFMODEX::~LLAudioChannelFMODEX()
{
cleanup();
}
bool LLAudioChannelFMODEX::updateBuffer()
{
if (LLAudioChannel::updateBuffer())
{
// Base class update returned true, which means that we need to actually
// set up the channel for a different buffer.
LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer();
// Grab the FMOD sample associated with the buffer
FMOD::Sound *soundp = bufferp->getSound();
if (!soundp)
{
// This is bad, there should ALWAYS be a sound associated with a legit
// buffer.
llerrs << "No FMOD sound!" << llendl;
return false;
}
// Actually play the sound. Start it off paused so we can do all the necessary
// setup.
if(!mChannelp)
{
FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp);
Check_FMOD_Error(result, "FMOD::System::playSound");
}
//llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl;
}
// If we have a source for the channel, we need to update its gain.
if (mCurrentSourcep)
{
// SJB: warnings can spam and hurt framerate, disabling
FMOD_RESULT result;
result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain());
//Check_FMOD_Error(result, "FMOD::Channel::setVolume");
result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
/*if(Check_FMOD_Error(result, "FMOD::Channel::setMode"))
{
S32 index;
mChannelp->getIndex(&index);
llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID()
<< " at " << mCurrentSourcep->getPositionGlobal() << llendl;
}*/
}
return true;
}
void LLAudioChannelFMODEX::update3DPosition()
{
if (!mChannelp)
{
// We're not actually a live channel (i.e., we're not playing back anything)
return;
}
LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentBufferp;
if (!bufferp)
{
// We don't have a buffer associated with us (should really have been picked up
// by the above if.
return;
}
if (mCurrentSourcep->isAmbient())
{
// Ambient sound, don't need to do any positional updates.
set3DMode(false);
}
else
{
// Localized sound. Update the position and velocity of the sound.
set3DMode(true);
LLVector3 float_pos;
float_pos.setVec(mCurrentSourcep->getPositionGlobal());
FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes");
}
}
void LLAudioChannelFMODEX::updateLoop()
{
if (!mChannelp)
{
// May want to clear up the loop/sample counters.
return;
}
//
// Hack: We keep track of whether we looped or not by seeing when the
// sample position looks like it's going backwards. Not reliable; may
// yield false negatives.
//
U32 cur_pos;
mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES);
if (cur_pos < (U32)mLastSamplePos)
{
mLoopedThisFrame = true;
}
mLastSamplePos = cur_pos;
}
void LLAudioChannelFMODEX::cleanup()
{
if (!mChannelp)
{
//llinfos << "Aborting cleanup with no channel handle." << llendl;
return;
}
//llinfos << "Cleaning up channel: " << mChannelID << llendl;
Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop");
mCurrentBufferp = NULL;
mChannelp = NULL;
}
void LLAudioChannelFMODEX::play()
{
if (!mChannelp)
{
llwarns << "Playing without a channel handle, aborting" << llendl;
return;
}
Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::pause");
getSource()->setPlayedOnce(true);
}
void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp)
{
LLAudioChannelFMODEX *fmod_channelp = (LLAudioChannelFMODEX*)channelp;
if (!(fmod_channelp->mChannelp && mChannelp))
{
// Don't have channels allocated to both the master and the slave
return;
}
U32 cur_pos;
if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position"))
return;
cur_pos %= mCurrentBufferp->getLength();
// Try to match the position of our sync master
Check_FMOD_Error(mChannelp->setPosition(cur_pos,FMOD_TIMEUNIT_PCMBYTES),"Unable to set current position");
// Start us playing
play();
}
bool LLAudioChannelFMODEX::isPlaying()
{
if (!mChannelp)
{
return false;
}
bool paused, playing;
mChannelp->getPaused(&paused);
mChannelp->isPlaying(&playing);
return !paused && playing;
}
//
// LLAudioChannelFMODEX implementation
//
LLAudioBufferFMODEX::LLAudioBufferFMODEX(FMOD::System *system) : mSystemp(system), mSoundp(NULL)
{
}
LLAudioBufferFMODEX::~LLAudioBufferFMODEX()
{
if(mSoundp)
{
mSoundp->release();
mSoundp = NULL;
}
}
bool LLAudioBufferFMODEX::loadWAV(const std::string& filename)
{
// Try to open a wav file from disk. This will eventually go away, as we don't
// really want to block doing this.
if (filename.empty())
{
// invalid filename, abort.
return false;
}
if (!LLAPRFile::isExist(filename, LL_APR_RPB))
{
// File not found, abort.
return false;
}
if (mSoundp)
{
// If there's already something loaded in this buffer, clean it up.
mSoundp->release();
mSoundp = NULL;
}
FMOD_MODE base_mode = FMOD_LOOP_NORMAL | FMOD_SOFTWARE;
FMOD_CREATESOUNDEXINFO exinfo;
memset(&exinfo,0,sizeof(exinfo));
exinfo.cbsize = sizeof(exinfo);
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading.
// Load up the wav file into an fmod sample
#if LL_WINDOWS
FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp);
#else
FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp);
#endif
if (result != FMOD_OK)
{
// We failed to load the file for some reason.
llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl;
//
// If we EVER want to load wav files provided by end users, we need
// to rethink this!
//
// file is probably corrupt - remove it.
LLFile::remove(filename);
return false;
}
// Everything went well, return true
return true;
}
U32 LLAudioBufferFMODEX::getLength()
{
if (!mSoundp)
{
return 0;
}
U32 length;
mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
return length;
}
void LLAudioChannelFMODEX::set3DMode(bool use3d)
{
FMOD_MODE current_mode;
if(mChannelp->getMode(&current_mode) != FMOD_OK)
return;
FMOD_MODE new_mode = current_mode;
new_mode &= ~(use3d ? FMOD_2D : FMOD_3D);
new_mode |= use3d ? FMOD_3D : FMOD_2D;
if(current_mode != new_mode)
{
mChannelp->setMode(new_mode);
}
}
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int outchannels)
{
// originalbuffer = fmod's original mixbuffer.
// newbuffer = the buffer passed from the previous DSP unit.
// length = length in samples at this mix time.
// userdata = user parameter passed through in FSOUND_DSP_Create.
LLWindGen<LLAudioEngine_FMODEX::MIXBUFFERFORMAT> *windgen;
FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance;
thisdsp->getUserData((void **)&windgen);
S32 channels, configwidth, configheight;
thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight);
windgen->windGenerate((LLAudioEngine_FMODEX::MIXBUFFERFORMAT *)newbuffer, length);
return FMOD_OK;
}

View File

@@ -0,0 +1,131 @@
/**
* @file audioengine_FMODEX.h
* @brief Definition of LLAudioEngine class abstracting the audio
* support as a FMOD 3D implementation
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_AUDIOENGINE_FMODEX_H
#define LL_AUDIOENGINE_FMODEX_H
#include "llaudioengine.h"
#include "lllistener_fmod.h"
#include "llwindgen.h"
//Stubs
class LLAudioStreamManagerFMODEX;
namespace FMOD
{
class System;
class Channel;
class Sound;
class DSP;
}
//Interfaces
class LLAudioEngine_FMODEX : public LLAudioEngine
{
public:
LLAudioEngine_FMODEX(bool enable_profiler);
virtual ~LLAudioEngine_FMODEX();
// initialization/startup/shutdown
virtual bool init(const S32 num_channels, void *user_data);
virtual std::string getDriverName(bool verbose);
virtual void allocateListener();
virtual void shutdown();
/*virtual*/ bool initWind();
/*virtual*/ void cleanupWind();
/*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water);
typedef F32 MIXBUFFERFORMAT;
FMOD::System *getSystem() const {return mSystem;}
protected:
/*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to.
/*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel.
/*virtual*/ void setInternalGain(F32 gain);
bool mInited;
LLWindGen<MIXBUFFERFORMAT> *mWindGen;
FMOD::DSP *mWindDSP;
FMOD::System *mSystem;
bool mEnableProfiler;
};
class LLAudioChannelFMODEX : public LLAudioChannel
{
public:
LLAudioChannelFMODEX(FMOD::System *audioengine);
virtual ~LLAudioChannelFMODEX();
protected:
/*virtual*/ void play();
/*virtual*/ void playSynced(LLAudioChannel *channelp);
/*virtual*/ void cleanup();
/*virtual*/ bool isPlaying();
/*virtual*/ bool updateBuffer();
/*virtual*/ void update3DPosition();
/*virtual*/ void updateLoop();
void set3DMode(bool use3d);
protected:
FMOD::System *getSystem() const {return mSystemp;}
FMOD::System *mSystemp;
FMOD::Channel *mChannelp;
S32 mLastSamplePos;
};
class LLAudioBufferFMODEX : public LLAudioBuffer
{
public:
LLAudioBufferFMODEX(FMOD::System *audioengine);
virtual ~LLAudioBufferFMODEX();
/*virtual*/ bool loadWAV(const std::string& filename);
/*virtual*/ U32 getLength();
friend class LLAudioChannelFMODEX;
protected:
FMOD::System *getSystem() const {return mSystemp;}
FMOD::System *mSystemp;
FMOD::Sound *getSound() const{ return mSoundp; }
FMOD::Sound *mSoundp;
};
#endif // LL_AUDIOENGINE_FMODEX_H

View File

@@ -0,0 +1,132 @@
/**
* @file listener_fmod.cpp
* @brief implementation of LISTENER class abstracting the audio
* support as a FMOD 3D implementation (windows only)
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llaudioengine.h"
#include "lllistener_fmodex.h"
#include "fmod.hpp"
//-----------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------
LLListener_FMODEX::LLListener_FMODEX(FMOD::System *system)
{
mSystem = system;
init();
}
//-----------------------------------------------------------------------
LLListener_FMODEX::~LLListener_FMODEX()
{
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::init(void)
{
// do inherited
LLListener::init();
mDopplerFactor = 1.0f;
mRolloffFactor = 1.0f;
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::translate(LLVector3 offset)
{
LLListener::translate(offset);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::setPosition(LLVector3 pos)
{
LLListener::setPosition(pos);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::setVelocity(LLVector3 vel)
{
LLListener::setVelocity(vel);
mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::orient(LLVector3 up, LLVector3 at)
{
LLListener::orient(up, at);
// Welcome to the transition between right and left
// (coordinate systems, that is)
// Leaving the at vector alone results in a L/R reversal
// since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed
at = -at;
mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::commitDeferredChanges()
{
mSystem->update();
}
void LLListener_FMODEX::setRolloffFactor(F32 factor)
{
mRolloffFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
}
F32 LLListener_FMODEX::getRolloffFactor()
{
return mRolloffFactor;
}
void LLListener_FMODEX::setDopplerFactor(F32 factor)
{
mDopplerFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
}
F32 LLListener_FMODEX::getDopplerFactor()
{
return mDopplerFactor;
}

View File

@@ -1,6 +1,7 @@
/** /**
* @file llversionserver.h * @file listener_fmod.h
* @brief * @brief Description of LISTENER class abstracting the audio support
* as an FMOD 3D implementation (windows and Linux)
* *
* $LicenseInfo:firstyear=2002&license=viewergpl$ * $LicenseInfo:firstyear=2002&license=viewergpl$
* *
@@ -30,15 +31,41 @@
* $/LicenseInfo$ * $/LicenseInfo$
*/ */
#ifndef LL_LLVERSIONSERVER_H #ifndef LL_LISTENER_FMODEX_H
#define LL_LLVERSIONSERVER_H #define LL_LISTENER_FMODEX_H
const S32 LL_VERSION_MAJOR = 1; #include "lllistener.h"
const S32 LL_VERSION_MINOR = 27;
const S32 LL_VERSION_PATCH = 0;
const S32 LL_VERSION_BUILD = 132845;
const char * const LL_CHANNEL = "Second Life Server"; //Stubs
namespace FMOD
{
class System;
}
//Interfaces
class LLListener_FMODEX : public LLListener
{
public:
LLListener_FMODEX(FMOD::System *system);
virtual ~LLListener_FMODEX();
virtual void init();
virtual void translate(LLVector3 offset);
virtual void setPosition(LLVector3 pos);
virtual void setVelocity(LLVector3 vel);
virtual void orient(LLVector3 up, LLVector3 at);
virtual void commitDeferredChanges();
virtual void setDopplerFactor(F32 factor);
virtual F32 getDopplerFactor();
virtual void setRolloffFactor(F32 factor);
virtual F32 getRolloffFactor();
protected:
FMOD::System *mSystem;
F32 mDopplerFactor;
F32 mRolloffFactor;
};
#endif #endif

View File

@@ -36,6 +36,8 @@
#include "stdtypes.h" // from llcommon #include "stdtypes.h" // from llcommon
class LLSD;
// Entirely abstract. Based exactly on the historic API. // Entirely abstract. Based exactly on the historic API.
class LLStreamingAudioInterface class LLStreamingAudioInterface
{ {
@@ -51,6 +53,11 @@ class LLStreamingAudioInterface
virtual void setGain(F32 vol) = 0; virtual void setGain(F32 vol) = 0;
virtual F32 getGain() = 0; virtual F32 getGain() = 0;
virtual std::string getURL() = 0; virtual std::string getURL() = 0;
virtual bool supportsMetaData() = 0;
virtual const LLSD *getMetaData() = 0;
virtual bool supportsWaveData() = 0;
virtual bool getWaveData(float* arr, S32 count, S32 stride = 1) = 0;
}; };
#endif // LL_STREAMINGAUDIO_H #endif // LL_STREAMINGAUDIO_H

View File

@@ -51,6 +51,8 @@ public:
const std::string& getURL() { return mInternetStreamURL; } const std::string& getURL() { return mInternetStreamURL; }
int getOpenState(); int getOpenState();
FSOUND_STREAM* getStream() { return mInternetStream; }
protected: protected:
FSOUND_STREAM* mInternetStream; FSOUND_STREAM* mInternetStream;
bool mReady; bool mReady;
@@ -66,7 +68,8 @@ protected:
LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() : LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() :
mCurrentInternetStreamp(NULL), mCurrentInternetStreamp(NULL),
mFMODInternetStreamChannel(-1), mFMODInternetStreamChannel(-1),
mGain(1.0f) mGain(1.0f),
mMetaData(NULL)
{ {
// Number of milliseconds of audio to buffer for the audio card. // Number of milliseconds of audio to buffer for the audio card.
// Must be larger than the usual Second Life frame stutter time. // Must be larger than the usual Second Life frame stutter time.
@@ -87,6 +90,17 @@ LLStreamingAudio_FMOD::~LLStreamingAudio_FMOD()
// nothing interesting/safe to do. // nothing interesting/safe to do.
} }
signed char F_CALLBACKAPI MetaDataCallback(char *name, char *value, void *userdata)
{
std::string szName(name);
if(szName == "TITLE" || szName=="TIT2" || szName=="Title")
(*(LLSD*)userdata)["TITLE"] = value;
if(szName == "ARTIST" || szName=="TPE1" || szName =="WM/AlbumTitle")
(*(LLSD*)userdata)["ARTIST"] = value;
else
(*(LLSD*)userdata)[std::string(name)] = value;
return true;
}
void LLStreamingAudio_FMOD::start(const std::string& url) void LLStreamingAudio_FMOD::start(const std::string& url)
{ {
@@ -104,6 +118,10 @@ void LLStreamingAudio_FMOD::start(const std::string& url)
llinfos << "Starting internet stream: " << url << llendl; llinfos << "Starting internet stream: " << url << llendl;
mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url); mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url);
mURL = url; mURL = url;
if(mCurrentInternetStreamp->getStream())
{
mMetaData = new LLSD;
}
} }
else else
{ {
@@ -154,6 +172,10 @@ void LLStreamingAudio_FMOD::update()
// Reset volume to previously set volume // Reset volume to previously set volume
setGain(getGain()); setGain(getGain());
FSOUND_SetPaused(mFMODInternetStreamChannel, false); FSOUND_SetPaused(mFMODInternetStreamChannel, false);
if(mCurrentInternetStreamp->getStream() && mMetaData)
{
FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),&MetaDataCallback, mMetaData);
}
} }
} }
} }
@@ -184,11 +206,17 @@ void LLStreamingAudio_FMOD::update()
// buffering // buffering
break; break;
} }
} }
void LLStreamingAudio_FMOD::stop() void LLStreamingAudio_FMOD::stop()
{ {
if(mMetaData)
{
if(mCurrentInternetStreamp && mCurrentInternetStreamp->getStream())
FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),NULL,NULL);
delete mMetaData;
mMetaData = NULL;
}
if (mFMODInternetStreamChannel != -1) if (mFMODInternetStreamChannel != -1)
{ {
FSOUND_SetPaused(mFMODInternetStreamChannel, true); FSOUND_SetPaused(mFMODInternetStreamChannel, true);

View File

@@ -55,6 +55,10 @@ class LLStreamingAudio_FMOD : public LLStreamingAudioInterface
/*virtual*/ F32 getGain(); /*virtual*/ F32 getGain();
/*virtual*/ std::string getURL(); /*virtual*/ std::string getURL();
/*virtual*/ bool supportsMetaData(){return true;}
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing.
/*virtual*/ bool supportsWaveData(){return false;}
/*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1){return false;}
private: private:
LLAudioStreamManagerFMOD *mCurrentInternetStreamp; LLAudioStreamManagerFMOD *mCurrentInternetStreamp;
int mFMODInternetStreamChannel; int mFMODInternetStreamChannel;
@@ -62,6 +66,8 @@ private:
std::string mURL; std::string mURL;
F32 mGain; F32 mGain;
LLSD *mMetaData;
}; };

View File

@@ -0,0 +1,450 @@
/**
* @file streamingaudio_fmod.cpp
* @brief LLStreamingAudio_FMODEX implementation
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
* Copyright (c) 2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llmath.h"
#include "fmod.hpp"
#include "fmod_errors.h"
#include "llstreamingaudio_fmodex.h"
class LLAudioStreamManagerFMODEX
{
public:
LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url);
FMOD::Channel* startStream();
bool stopStream(); // Returns true if the stream was successfully stopped.
bool ready();
const std::string& getURL() { return mInternetStreamURL; }
FMOD_OPENSTATE getOpenState();
protected:
FMOD::System* mSystem;
FMOD::Channel* mStreamChannel;
FMOD::Sound* mInternetStream;
bool mReady;
std::string mInternetStreamURL;
};
//---------------------------------------------------------------------------
// Internet Streaming
//---------------------------------------------------------------------------
LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
mSystem(system),
mCurrentInternetStreamp(NULL),
mFMODInternetStreamChannelp(NULL),
mGain(1.0f),
mMetaData(NULL)
{
// Number of milliseconds of audio to buffer for the audio card.
// Must be larger than the usual Second Life frame stutter time.
const U32 buffer_seconds = 5; //sec
const U32 estimated_bitrate = 128; //kbit/sec
mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
// Here's where we set the size of the network buffer and some buffering
// parameters. In this case we want a network buffer of 16k, we want it
// to prebuffer 40% of that when we first connect, and we want it
// to rebuffer 80% of that whenever we encounter a buffer underrun.
// Leave the net buffer properties at the default.
//FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
}
LLStreamingAudio_FMODEX::~LLStreamingAudio_FMODEX()
{
// nothing interesting/safe to do.
}
void LLStreamingAudio_FMODEX::start(const std::string& url)
{
//if (!mInited)
//{
// llwarns << "startInternetStream before audio initialized" << llendl;
// return;
//}
// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
stop();
if (!url.empty())
{
llinfos << "Starting internet stream: " << url << llendl;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODEX(mSystem,url);
mURL = url;
mMetaData = new LLSD;
}
else
{
llinfos << "Set internet stream to null" << llendl;
mURL.clear();
}
}
void LLStreamingAudio_FMODEX::update()
{
// Kill dead internet streams, if possible
std::list<LLAudioStreamManagerFMODEX *>::iterator iter;
for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
{
LLAudioStreamManagerFMODEX *streamp = *iter;
if (streamp->stopStream())
{
llinfos << "Closed dead stream" << llendl;
delete streamp;
mDeadStreams.erase(iter++);
}
else
{
iter++;
}
}
// Don't do anything if there are no streams playing
if (!mCurrentInternetStreamp)
{
return;
}
FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState();
if (open_state == FMOD_OPENSTATE_READY)
{
// Stream is live
// start the stream if it's ready
if (!mFMODInternetStreamChannelp &&
(mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream()))
{
// Reset volume to previously set volume
setGain(getGain());
mFMODInternetStreamChannelp->setPaused(false);
}
}
else if(open_state == FMOD_OPENSTATE_ERROR)
{
stop();
return;
}
if(mFMODInternetStreamChannelp)
{
if(!mMetaData)
mMetaData = new LLSD;
FMOD::Sound *sound = NULL;
if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
{
FMOD_TAG tag;
S32 tagcount, dirtytagcount;
if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount)
{
mMetaData->clear();
for(S32 i = 0; i < tagcount; ++i)
{
if(sound->getTag(NULL, i, &tag)!=FMOD_OK)
continue;
std::string name = tag.name;
switch(tag.type) //Crappy tag translate table.
{
case(FMOD_TAGTYPE_ID3V2):
if(name == "TIT2") name = "TITLE";
else if(name == "TPE1") name = "ARTIST";
break;
case(FMOD_TAGTYPE_ASF):
if(name == "Title") name = "TITLE";
else if(name == "WM/AlbumArtist") name = "ARTIST";
break;
default:
break;
}
switch(tag.datatype)
{
case(FMOD_TAGDATATYPE_INT):
(*mMetaData)[name]=*(LLSD::Integer*)(tag.data);
llinfos << tag.name << ": " << *(int*)(tag.data) << llendl;
break;
case(FMOD_TAGDATATYPE_FLOAT):
(*mMetaData)[name]=*(LLSD::Float*)(tag.data);
llinfos << tag.name << ": " << *(float*)(tag.data) << llendl;
break;
case(FMOD_TAGDATATYPE_STRING):
{
std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen));
(*mMetaData)[name]=out;
llinfos << tag.name << ": " << out << llendl;
}
break;
case(FMOD_TAGDATATYPE_STRING_UTF16):
{
std::string out((char*)tag.data,tag.datalen);
(*mMetaData)[std::string(tag.name)]=out;
llinfos << tag.name << ": " << out << llendl;
}
break;
case(FMOD_TAGDATATYPE_STRING_UTF16BE):
{
std::string out((char*)tag.data,tag.datalen);
U16* buf = (U16*)out.c_str();
for(U32 j = 0; j < out.size()/2; ++j)
(((buf[j] & 0xff)<<8) | ((buf[j] & 0xff00)>>8));
(*mMetaData)[std::string(tag.name)]=out;
llinfos << tag.name << ": " << out << llendl;
}
default:
break;
}
}
}
}
}
}
void LLStreamingAudio_FMODEX::stop()
{
if(mMetaData)
{
delete mMetaData;
mMetaData = NULL;
}
if (mFMODInternetStreamChannelp)
{
mFMODInternetStreamChannelp->setPaused(true);
mFMODInternetStreamChannelp->setPriority(0);
mFMODInternetStreamChannelp = NULL;
}
if (mCurrentInternetStreamp)
{
llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl;
if (mCurrentInternetStreamp->stopStream())
{
delete mCurrentInternetStreamp;
}
else
{
llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl;
mDeadStreams.push_back(mCurrentInternetStreamp);
}
mCurrentInternetStreamp = NULL;
//mURL.clear();
}
}
void LLStreamingAudio_FMODEX::pause(int pauseopt)
{
if (pauseopt < 0)
{
pauseopt = mCurrentInternetStreamp ? 1 : 0;
}
if (pauseopt)
{
if (mCurrentInternetStreamp)
{
stop();
}
}
else
{
start(getURL());
}
}
// A stream is "playing" if it has been requested to start. That
// doesn't necessarily mean audio is coming out of the speakers.
int LLStreamingAudio_FMODEX::isPlaying()
{
if (mCurrentInternetStreamp)
{
return 1; // Active and playing
}
else if (!mURL.empty())
{
return 2; // "Paused"
}
else
{
return 0;
}
}
F32 LLStreamingAudio_FMODEX::getGain()
{
return mGain;
}
std::string LLStreamingAudio_FMODEX::getURL()
{
return mURL;
}
void LLStreamingAudio_FMODEX::setGain(F32 vol)
{
mGain = vol;
if (mFMODInternetStreamChannelp)
{
vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here?
mFMODInternetStreamChannelp->setVolume(vol);
}
}
/*virtual*/ bool LLStreamingAudio_FMODEX::getWaveData(float* arr, S32 count, S32 stride/*=1*/)
{
if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp)
return false;
static std::vector<float> local_array(count); //Have to have an extra buffer to mix channels. Bleh.
if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink.
local_array.resize(count);
if( mFMODInternetStreamChannelp->getWaveData(&local_array[0],count,0) == FMOD_OK &&
mFMODInternetStreamChannelp->getWaveData(&arr[0],count,1) == FMOD_OK )
{
for(S32 i = count-1;i>=0;i-=stride)
{
arr[i] += local_array[i];
arr[i] *= .5f;
}
return true;
}
return false;
}
///////////////////////////////////////////////////////
// manager of possibly-multiple internet audio streams
LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url) :
mSystem(system),
mStreamChannel(NULL),
mInternetStream(NULL),
mReady(false)
{
mInternetStreamURL = url;
FMOD_CREATESOUNDEXINFO exinfo;
memset(&exinfo,0,sizeof(exinfo));
exinfo.cbsize = sizeof(exinfo);
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG; //Hint to speed up loading.
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, &exinfo, &mInternetStream);
if (result!= FMOD_OK)
{
llwarns << "Couldn't open fmod stream, error "
<< FMOD_ErrorString(result)
<< llendl;
mReady = false;
return;
}
mReady = true;
}
FMOD::Channel *LLAudioStreamManagerFMODEX::startStream()
{
// We need a live and opened stream before we try and play it.
if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY)
{
llwarns << "No internet stream to start playing!" << llendl;
return NULL;
}
if(mStreamChannel)
return mStreamChannel; //Already have a channel for this stream.
mSystem->playSound(FMOD_CHANNEL_FREE, mInternetStream, true, &mStreamChannel);
return mStreamChannel;
}
bool LLAudioStreamManagerFMODEX::stopStream()
{
if (mInternetStream)
{
bool close = true;
switch (getOpenState())
{
case FMOD_OPENSTATE_CONNECTING:
close = false;
break;
/*case FSOUND_STREAM_NET_NOTCONNECTED:
case FSOUND_STREAM_NET_BUFFERING:
case FSOUND_STREAM_NET_READY:
case FSOUND_STREAM_NET_ERROR:*/
default:
close = true;
}
if (close)
{
mInternetStream->release();
mStreamChannel = NULL;
mInternetStream = NULL;
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState()
{
FMOD_OPENSTATE state;
mInternetStream->getOpenState(&state,NULL,NULL,NULL);
return state;
}

View File

@@ -0,0 +1,83 @@
/**
* @file streamingaudio_fmod.h
* @author Tofu Linden
* @brief Definition of LLStreamingAudio_FMOD implementation
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
* Copyright (c) 2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_STREAMINGAUDIO_FMOD_H
#define LL_STREAMINGAUDIO_FMOD_H
#include "stdtypes.h" // from llcommon
#include "llstreamingaudio.h"
//Stubs
class LLAudioStreamManagerFMODEX;
namespace FMOD
{
class System;
class Channel;
}
//Interfaces
class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface
{
public:
LLStreamingAudio_FMODEX(FMOD::System *system);
/*virtual*/ ~LLStreamingAudio_FMODEX();
/*virtual*/ void start(const std::string& url);
/*virtual*/ void stop();
/*virtual*/ void pause(int pause);
/*virtual*/ void update();
/*virtual*/ int isPlaying();
/*virtual*/ void setGain(F32 vol);
/*virtual*/ F32 getGain();
/*virtual*/ std::string getURL();
/*virtual*/ bool supportsMetaData(){return true;}
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing.
/*virtual*/ bool supportsWaveData(){return true;}
/*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1);
private:
FMOD::System *mSystem;
LLAudioStreamManagerFMODEX *mCurrentInternetStreamp;
FMOD::Channel *mFMODInternetStreamChannelp;
std::list<LLAudioStreamManagerFMODEX *> mDeadStreams;
std::string mURL;
F32 mGain;
LLSD *mMetaData;
};
#endif // LL_STREAMINGAUDIO_FMOD_H

View File

@@ -133,10 +133,10 @@ BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
return(FALSE); return(FALSE);
} }
//********************************** // **********************************
LLAPRFile outfile ; LLAPRFile outfile ;
outfile.open(out_fname,LL_APR_WPB); outfile.open(out_fname,LL_APR_WPB);
//********************************** // **********************************
if (!outfile.getFileHandle()) if (!outfile.getFileHandle())
{ {
llwarning("unable to open vorbis destination file for writing",0); llwarning("unable to open vorbis destination file for writing",0);
@@ -308,9 +308,9 @@ BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
outfile.seek(SEEK_END,-fade_length*2); outfile.seek(SEEK_END,-fade_length*2);
outfile.write(pcmout,2*fade_length); //write back xfaded last 16 samples outfile.write(pcmout,2*fade_length); //write back xfaded last 16 samples
//******************* // *******************
outfile.close(); outfile.close();
//******************* // *******************
if ((36 == data_length) || (!(eof))) if ((36 == data_length) || (!(eof)))
{ {

View File

@@ -87,10 +87,10 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
error_msg.clear(); error_msg.clear();
//******************************** // ********************************
LLAPRFile infile ; LLAPRFile infile ;
infile.open(in_fname,LL_APR_RB); infile.open(in_fname,LL_APR_RB);
//******************************** // ********************************
if (!infile.getFileHandle()) if (!infile.getFileHandle())
{ {
error_msg = "CannotUploadSoundFile"; error_msg = "CannotUploadSoundFile";
@@ -159,9 +159,9 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
file_pos += (chunk_length + 8); file_pos += (chunk_length + 8);
chunk_length = 0; chunk_length = 0;
} }
//**************** // ****************
infile.close(); infile.close();
//**************** // ****************
if (!uncompressed_pcm) if (!uncompressed_pcm)
{ {

View File

@@ -33,6 +33,7 @@
#define WINDGEN_H #define WINDGEN_H
#include "llcommon.h" #include "llcommon.h"
#include "llrand.h"
template <class MIXBUFFERFORMAT_T> template <class MIXBUFFERFORMAT_T>
class LLWindGen class LLWindGen
@@ -60,6 +61,8 @@ public:
} }
const U32 getInputSamplingRate() { return mInputSamplingRate; } const U32 getInputSamplingRate() { return mInputSamplingRate; }
const F32 getNextSample();
const F32 getClampedSample(bool clamp, F32 sample);
// newbuffer = the buffer passed from the previous DSP unit. // newbuffer = the buffer passed from the previous DSP unit.
// numsamples = length in samples-per-channel at this mix time. // numsamples = length in samples-per-channel at this mix time.
@@ -95,7 +98,7 @@ public:
// Start with white noise // Start with white noise
// This expression is fragile, rearrange it and it will break! // This expression is fragile, rearrange it and it will break!
next_sample = (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8); next_sample = getNextSample();
// Apply a pinking filter // Apply a pinking filter
// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/ // Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
@@ -132,24 +135,14 @@ public:
for (U8 i=mSubSamples; i && numsamples; --i, --numsamples) for (U8 i=mSubSamples; i && numsamples; --i, --numsamples)
{ {
mLastSample = mLastSample + delta; mLastSample = mLastSample + delta;
S32 sample_right = (S32)(mLastSample * mCurrentPanGainR); MIXBUFFERFORMAT_T sample_right = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample * mCurrentPanGainR);
S32 sample_left = (S32)mLastSample - sample_right; MIXBUFFERFORMAT_T sample_left = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample - (F32)sample_right);
if (!clip) *cursamplep = sample_left;
{
*cursamplep = (MIXBUFFERFORMAT_T)sample_left;
++cursamplep; ++cursamplep;
*cursamplep = (MIXBUFFERFORMAT_T)sample_right; *cursamplep = sample_right;
++cursamplep; ++cursamplep;
} }
else
{
*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_left, (S32)S16_MIN, (S32)S16_MAX);
++cursamplep;
*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_right, (S32)S16_MIN, (S32)S16_MAX);
++cursamplep;
}
}
} }
return newbuffer; return newbuffer;
@@ -179,4 +172,9 @@ private:
F32 mLastSample; F32 mLastSample;
}; };
template<class T> inline const F32 LLWindGen<T>::getNextSample() { return (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8); }
template<> inline const F32 LLWindGen<F32>::getNextSample() { return ll_frand()-.5f; }
template<class T> inline const F32 LLWindGen<T>::getClampedSample(bool clamp, F32 sample) { return clamp ? (F32)llclamp((S32)sample,(S32)S16_MIN,(S32)S16_MAX) : sample; }
template<> inline const F32 LLWindGen<F32>::getClampedSample(bool clamp, F32 sample) { return sample; }
#endif #endif

View File

@@ -195,15 +195,19 @@ void LLCharacter::requestStopMotion( LLMotion* motion)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// updateMotions() // updateMotions()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static LLFastTimer::DeclareTimer FTM_UPDATE_ANIMATION("Update Animation");
static LLFastTimer::DeclareTimer FTM_UPDATE_HIDDEN_ANIMATION("Update Hidden Anim");
void LLCharacter::updateMotions(e_update_t update_type) void LLCharacter::updateMotions(e_update_t update_type)
{ {
LLFastTimer t(LLFastTimer::FTM_UPDATE_ANIMATION);
if (update_type == HIDDEN_UPDATE) if (update_type == HIDDEN_UPDATE)
{ {
LLFastTimer t(FTM_UPDATE_HIDDEN_ANIMATION);
mMotionController.updateMotionsMinimal(); mMotionController.updateMotionsMinimal();
} }
else else
{ {
LLFastTimer t(FTM_UPDATE_ANIMATION);
// unpause if the number of outstanding pause requests has dropped to the initial one // unpause if the number of outstanding pause requests has dropped to the initial one
if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1) if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1)
{ {

View File

@@ -56,9 +56,7 @@ LLJoint::LLJoint()
mUpdateXform = TRUE; mUpdateXform = TRUE;
mJointNum = -1; mJointNum = -1;
touch(); touch();
#if MESH_ENABLED
mResetAfterRestoreOldXform = false; mResetAfterRestoreOldXform = false;
#endif //MESH_ENABLED
} }
@@ -242,7 +240,6 @@ void LLJoint::setPosition( const LLVector3& pos )
} }
} }
#if MESH_ENABLED
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// setPosition() // setPosition()
//-------------------------------------------------------------------- //--------------------------------------------------------------------
@@ -278,7 +275,6 @@ void LLJoint::restoreToDefaultXform( void )
mXform = mDefaultXform; mXform = mDefaultXform;
setPosition( mXform.getPosition() ); setPosition( mXform.getPosition() );
} }
#endif //MESH_ENABLED
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// getWorldPosition() // getWorldPosition()

View File

@@ -86,19 +86,14 @@ protected:
// explicit transformation members // explicit transformation members
LLXformMatrix mXform; LLXformMatrix mXform;
#if MESH_ENABLED
LLXformMatrix mOldXform; LLXformMatrix mOldXform;
LLXformMatrix mDefaultXform; LLXformMatrix mDefaultXform;
LLUUID mId; LLUUID mId;
#endif //MESH_ENABLED
public: public:
U32 mDirtyFlags; U32 mDirtyFlags;
BOOL mUpdateXform; BOOL mUpdateXform;
#if MESH_ENABLED
BOOL mResetAfterRestoreOldXform; BOOL mResetAfterRestoreOldXform;
#endif //MESH_ENABLED
// describes the skin binding pose // describes the skin binding pose
LLVector3 mSkinOffset; LLVector3 mSkinOffset;
@@ -188,8 +183,6 @@ public:
S32 getJointNum() const { return mJointNum; } S32 getJointNum() const { return mJointNum; }
void setJointNum(S32 joint_num) { mJointNum = joint_num; } void setJointNum(S32 joint_num) { mJointNum = joint_num; }
#if MESH_ENABLED
void restoreOldXform( void ); void restoreOldXform( void );
void restoreToDefaultXform( void ); void restoreToDefaultXform( void );
void setDefaultFromCurrentXform( void ); void setDefaultFromCurrentXform( void );
@@ -204,7 +197,6 @@ public:
const BOOL doesJointNeedToBeReset( void ) const { return mResetAfterRestoreOldXform; } const BOOL doesJointNeedToBeReset( void ) const { return mResetAfterRestoreOldXform; }
//Setter for joint reset flag //Setter for joint reset flag
void setJointToBeReset( BOOL val ) { mResetAfterRestoreOldXform = val; } void setJointToBeReset( BOOL val ) { mResetAfterRestoreOldXform = val; }
#endif //MESH_ENABLED
// <edit> // <edit>
std::string exportString(U32 tabs = 0); std::string exportString(U32 tabs = 0);

View File

@@ -637,9 +637,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
motionp->fadeIn(); motionp->fadeIn();
} }
//********************** // **********************
// MOTION INACTIVE // MOTION INACTIVE
//********************** // **********************
if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration()) if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration())
{ {
// this motion has gone on too long, deactivate it // this motion has gone on too long, deactivate it
@@ -659,9 +659,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
} }
} }
//********************** // **********************
// MOTION EASE OUT // MOTION EASE OUT
//********************** // **********************
else if (motionp->isStopped() && mAnimTime > motionp->getStopTime()) else if (motionp->isStopped() && mAnimTime > motionp->getStopTime())
{ {
// is this the first iteration in the ease out phase? // is this the first iteration in the ease out phase?
@@ -684,9 +684,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature); update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
} }
//********************** // **********************
// MOTION ACTIVE // MOTION ACTIVE
//********************** // **********************
else if (mAnimTime > motionp->mActivationTimestamp + motionp->getEaseInDuration()) else if (mAnimTime > motionp->mActivationTimestamp + motionp->getEaseInDuration())
{ {
posep->setWeight(motionp->getFadeWeight()); posep->setWeight(motionp->getFadeWeight());
@@ -707,9 +707,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature); update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
} }
//********************** // **********************
// MOTION EASE IN // MOTION EASE IN
//********************** // **********************
else if (mAnimTime >= motionp->mActivationTimestamp) else if (mAnimTime >= motionp->mActivationTimestamp)
{ {
if (mLastTime < motionp->mActivationTimestamp) if (mLastTime < motionp->mActivationTimestamp)

View File

@@ -15,8 +15,11 @@ include_directories(
) )
set(llcommon_SOURCE_FILES set(llcommon_SOURCE_FILES
aiframetimer.cpp
imageids.cpp imageids.cpp
indra_constants.cpp indra_constants.cpp
llallocator.cpp
llallocator_heap_profile.cpp
llapp.cpp llapp.cpp
llapr.cpp llapr.cpp
llaprpool.cpp llaprpool.cpp
@@ -26,16 +29,23 @@ set(llcommon_SOURCE_FILES
llbase64.cpp llbase64.cpp
llcommon.cpp llcommon.cpp
llcommonutils.cpp llcommonutils.cpp
llcoros.cpp
llcrc.cpp llcrc.cpp
llcriticaldamp.cpp llcriticaldamp.cpp
llcursortypes.cpp llcursortypes.cpp
lldate.cpp lldate.cpp
lldependencies.cpp
lldictionary.cpp lldictionary.cpp
llerror.cpp llerror.cpp
llerrorthread.cpp llerrorthread.cpp
llevent.cpp llevent.cpp
lleventapi.cpp
lleventcoro.cpp
lleventdispatcher.cpp
lleventfilter.cpp
llevents.cpp
lleventtimer.cpp lleventtimer.cpp
llfasttimer.cpp llfasttimer_class.cpp
llfile.cpp llfile.cpp
llfindlocale.cpp llfindlocale.cpp
llfixedbuffer.cpp llfixedbuffer.cpp
@@ -51,9 +61,11 @@ set(llcommon_SOURCE_FILES
llmd5.cpp llmd5.cpp
llmemory.cpp llmemory.cpp
llmemorystream.cpp llmemorystream.cpp
llmemtype.cpp
llmetrics.cpp llmetrics.cpp
llmortician.cpp llmortician.cpp
lloptioninterface.cpp lloptioninterface.cpp
llptrto.cpp
llprocesslauncher.cpp llprocesslauncher.cpp
llprocessor.cpp llprocessor.cpp
llqueuedthread.cpp llqueuedthread.cpp
@@ -79,6 +91,7 @@ set(llcommon_SOURCE_FILES
lluri.cpp lluri.cpp
lluuid.cpp lluuid.cpp
llworkerthread.cpp llworkerthread.cpp
ll_template_cast.h
metaclass.cpp metaclass.cpp
metaproperty.cpp metaproperty.cpp
reflective.cpp reflective.cpp
@@ -89,6 +102,7 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES set(llcommon_HEADER_FILES
CMakeLists.txt CMakeLists.txt
aiframetimer.h
aithreadsafe.h aithreadsafe.h
bitpack.h bitpack.h
ctype_workaround.h ctype_workaround.h
@@ -98,6 +112,8 @@ set(llcommon_HEADER_FILES
linden_common.h linden_common.h
linked_lists.h linked_lists.h
llaccountingcost.h llaccountingcost.h
llallocator.h
llallocator_heap_profile.h
llagentconstants.h llagentconstants.h
llavatarname.h llavatarname.h
llapp.h llapp.h
@@ -113,6 +129,7 @@ set(llcommon_HEADER_FILES
llclickaction.h llclickaction.h
llcommon.h llcommon.h
llcommonutils.h llcommonutils.h
llcoros.h
llcrc.h llcrc.h
llcriticaldamp.h llcriticaldamp.h
llcursortypes.h llcursortypes.h
@@ -120,6 +137,7 @@ set(llcommon_HEADER_FILES
lldarrayptr.h lldarrayptr.h
lldate.h lldate.h
lldefs.h lldefs.h
lldependencies.h
lldeleteutils.h lldeleteutils.h
lldepthstack.h lldepthstack.h
lldictionary.h lldictionary.h
@@ -132,10 +150,16 @@ set(llcommon_HEADER_FILES
llerrorlegacy.h llerrorlegacy.h
llerrorthread.h llerrorthread.h
llevent.h llevent.h
lleventapi.h
lleventcoro.h
lleventdispatcher.h
lleventfilter.h
llevents.h
lleventemitter.h lleventemitter.h
llextendedstatus.h llextendedstatus.h
lleventtimer.h lleventtimer.h
llfasttimer.h llfasttimer.h
llfasttimer_class.h
llfile.h llfile.h
llfindlocale.h llfindlocale.h
llfixedbuffer.h llfixedbuffer.h
@@ -171,6 +195,7 @@ set(llcommon_HEADER_FILES
llprocessor.h llprocessor.h
llptrskiplist.h llptrskiplist.h
llptrskipmap.h llptrskipmap.h
llptrto.h
llqueuedthread.h llqueuedthread.h
llrand.h llrand.h
llrefcount.h llrefcount.h
@@ -203,8 +228,7 @@ set(llcommon_HEADER_FILES
lluri.h lluri.h
lluuid.h lluuid.h
lluuidhashmap.h lluuidhashmap.h
llversionserver.h llversionviewer.h.in
llversionviewer.h
llworkerthread.h llworkerthread.h
metaclass.h metaclass.h
metaclasst.h metaclasst.h

View File

@@ -0,0 +1,180 @@
/**
* @file aiframetimer.cpp
*
* Copyright (c) 2011, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 06/08/2011
* - Initial version, written by Aleric Inglewood @ SL
*/
// An AIFrameTimer object provides a callback API for timer events.
//
// Typical usage:
//
// // Any thread.
// AIFrameTimer timer;
//
// ...
// // Any thread (after successful construction is guaranteed).
// timer.create(5.5, boost::bind(&the_callback, <optional params>)); // Call the_callback(<optional params>) in 5.5 seconds.
//
// The callback function is always called by the main thread and should therefore
// be light weight.
//
// If timer.cancel() is called before the timer expires, then the callback
// function isn't called. If cancel() is called by another thread than the
// main thread, then it is possible that the callback function is called
// even while still inside cancel(), but as soon as cancel() returned it
// is guarenteed that the callback function won't be called anymore.
// Hence, if the callback function is a member of some object then
// cancel() must be called before the destruction of that object (ie from
// it's destructor). Calling cancel() multiple times is not a problem.
// Note: if cancel() is called while the callback function is being
// called then cancel() will block until the callback function returned.
//
// The timer object can be reused (by calling create() again), but
// only after either the callback function was called, or after cancel()
// returned. Most notably, you can call create() again from inside the
// callback function to "restart" the timer.
//
#include "linden_common.h"
#include "aiframetimer.h"
static F64 const NEVER = 1e16; // 317 million years.
F64 AIFrameTimer::sNextExpiration;
AIFrameTimer::timer_list_type AIFrameTimer::sTimerList;
LLMutex AIFrameTimer::sMutex;
// Notes on thread-safety of AIRunningFrameTimer (continued from aiframetimer.h)
//
// Most notably, the constructor and init() should be called as follows:
// 1) The object is constructed (AIRunningFrameTimer::AIRunningFrameTimer).
// 2) The lock is obtained.
// 3) The object is inserted in the list (operator<(AIRunningFrameTimer const&, AIRunningFrameTimer const&)).
// 4) The object is initialized (AIRunningFrameTimer::init).
// 5) The lock is released.
// This assures that the object is not yet shared at the moment that it is initialized.
void AIFrameTimer::create(F64 expiration, signal_type::slot_type const& slot)
{
AIRunningFrameTimer new_timer(expiration, this);
sMutex.lock();
llassert(mHandle.mRunningTimer == sTimerList.end()); // Create may only be called when the timer isn't already running.
mHandle.init(sTimerList.insert(new_timer), slot);
sNextExpiration = sTimerList.begin()->expiration();
sMutex.unlock();
}
void AIFrameTimer::cancel(void)
{
// In order to stop us from returning from cancel() while
// the callback function is being called (which is done
// in AIFrameTimer::handleExpiration after obtaining the
// mHandle.mMutex lock), we start with trying to obtain
// it here and as such wait till the callback function
// returned.
mHandle.mMutex.lock();
// Next we have to grab this lock in order to stop
// AIFrameTimer::handleExpiration from even entering
// in the case we manage to get it first.
sMutex.lock();
if (mHandle.mRunningTimer != sTimerList.end())
{
sTimerList.erase(mHandle.mRunningTimer);
mHandle.mRunningTimer = sTimerList.end();
sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration();
}
sMutex.unlock();
mHandle.mMutex.unlock();
}
void AIFrameTimer::handleExpiration(F64 current_frame_time)
{
sMutex.lock();
for(;;)
{
if (sTimerList.empty())
{
// No running timers left.
sNextExpiration = NEVER;
break;
}
timer_list_type::iterator running_timer = sTimerList.begin();
sNextExpiration = running_timer->expiration();
if (sNextExpiration > current_frame_time)
{
// Didn't expire yet.
break;
}
// Obtain handle of running timer through the associated AIFrameTimer object.
// Note that if the AIFrameTimer object was destructed (when running_timer->getTimer()
// would return an invalid pointer) then it called cancel(), so we can't be here.
Handle& handle(running_timer->getTimer()->mHandle);
llassert_always(running_timer == handle.mRunningTimer);
// We're going to erase this timer, so stop cancel() from doing the same.
handle.mRunningTimer = sTimerList.end();
// We keep handle.mMutex during the callback to prevent the thread that
// owns the AIFrameTimer from deleting the callback function while we
// call it: in order to do so it first has to call cancel(), which will
// block until we release this mutex again, or we won't call the callback
// function here because the trylock fails.
//
// Assuming the main thread arrived here, there are two possible states
// for the other thread that tries to delete the callback function,
// right after calling the cancel() function too:
//
// 1. It hasn't obtained the first lock yet, we obtain the handle.mMutex
// lock and the other thread will stall on the first line of cancel().
// After do_callback returns, the other thread will do nothing because
// handle.mRunningTimer equals sTimerList.end(), exit the function and
// (possibly) delete the callback object, but that is ok as we already
// returned from the callback function.
//
// 2. It already called cancel() and hangs on the second line trying to
// obtain sMutex.lock(). The trylock below fails and we never call the
// callback function. We erase the running timer here and release sMutex
// at the end, after which the other thread does nothing because
// handle.mRunningTimer equals sTimerList.end(), exits the function and
// (possibly) deletes the callback object.
//
// Note that if the other thread actually obtained the sMutex then we
// can't be here: this is still inside the critical area of sMutex.
if (handle.mMutex.tryLock()) // If this fails then another thread is in the process of cancelling this timer, so do nothing.
{
sMutex.unlock();
running_timer->do_callback(); // May not throw exceptions.
sMutex.lock();
handle.mMutex.unlock(); // Allow other thread to return from cancel() and possibly delete the callback object.
}
// Erase the timer from the running list.
sTimerList.erase(running_timer);
}
sMutex.unlock();
}

View File

@@ -0,0 +1,155 @@
/**
* @file aiframetimer.h
* @brief Implementation of AIFrameTimer.
*
* Copyright (c) 2011, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 05/08/2011
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AIFRAMETIMER_H
#define AIFRAMETIMER_H
#include "llframetimer.h"
#include "llthread.h"
#include <boost/signals2.hpp>
#include <set>
class LL_COMMON_API AIFrameTimer
{
protected:
typedef boost::signals2::signal<void (void)> signal_type;
private:
// Use separate struct for this object because it is non-copyable.
struct Signal {
signal_type mSignal;
};
// Notes on Thread-Safety
//
// This is the type of the objects stored in AIFrameTimer::sTimerList, and as such leans
// for it's thread-safety on the same lock as is used for that std::multiset as follows.
// An arbitrary thread can create, insert and initialize this object. Other threads can
// not access it until that has completed.
//
// After creation two threads can access it: the thread that created it (owns the
// AIFrameTimer object, which has an mHandle that points to this object), or the main
// thread by finding it in sTimerList.
//
// See aiframetimer.cpp for more notes.
class AIRunningFrameTimer {
private:
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
AIFrameTimer* mTimer; // The actual timer.
// Can be mutable, since only the mExpire is used for ordering this object in the multiset AIFrameTimer::sTimerList.
mutable Signal* mCallback; // Pointer to callback struct, or NULL when the object wasn't added to sTimerList yet.
public:
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(NULL), mTimer(timer) { }
~AIRunningFrameTimer() { delete mCallback; }
// This function is called after the final object was added to sTimerList (where it is initialized in-place).
void init(signal_type::slot_type const& slot) const
{
// We may only call init() once.
llassert(!mCallback);
mCallback = new Signal;
mCallback->mSignal.connect(slot);
}
// Order AIFrameTimer::sTimerList so that the timer that expires first is up front.
friend bool operator<(AIRunningFrameTimer const& ft1, AIRunningFrameTimer const& ft2) { return ft1.mExpire < ft2.mExpire; }
void do_callback(void) const { mCallback->mSignal(); }
F64 expiration(void) const { return mExpire; }
AIFrameTimer* getTimer(void) const { return mTimer; }
#if LL_DEBUG
// May not copy this object after it was initialized.
AIRunningFrameTimer(AIRunningFrameTimer const& running_frame_timer) :
mExpire(running_frame_timer.mExpire), mCallback(running_frame_timer.mCallback), mTimer(running_frame_timer.mTimer)
{ llassert(!mCallback); }
#endif
};
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
static LLMutex sMutex; // Mutex for the two global variables below.
static timer_list_type sTimerList; // List with all running timers.
static F64 sNextExpiration; // Cache of smallest value in sTimerList.
friend class LLFrameTimer; // Access to sNextExpiration.
class Handle {
public:
timer_list_type::iterator mRunningTimer; // Points to the running timer, or to sTimerList.end() when not running.
// Access to this iterator is protected by the AIFrameTimer::sMutex!
LLMutex mMutex; // A mutex used to protect us from deletion of the callback object while
// calling the callback function in the case of simultaneous expiration
// and cancellation by the thread owning the AIFrameTimer (by calling
// AIFrameTimer::cancel).
// Constructor for a not-running timer.
Handle(void) : mRunningTimer(sTimerList.end()) { }
// Actual initialization used by AIFrameTimer::create.
void init(timer_list_type::iterator const& running_timer, signal_type::slot_type const& slot)
{
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
// the object and no other thread knows of mRunningTimer at this point.
mRunningTimer = running_timer;
mRunningTimer->init(slot);
}
private:
// LLMutex has no assignment operator.
Handle& operator=(Handle const&) { return *this; }
};
Handle mHandle;
public:
// Construct an AIFrameTimer that is not running.
AIFrameTimer(void) { }
// Construction of a running AIFrameTimer with expiration time expiration in seconds, and callback slot.
AIFrameTimer(F64 expiration, signal_type::slot_type const& slot) { create(expiration, slot); }
// Destructing the AIFrameTimer object terminates the running timer (if still running).
// Note that cancel() must have returned BEFORE anything is destructed that would disallow the callback function to be called.
// So, if the AIFrameTimer is a member of an object whose callback function is called then cancel() has
// to be called manually (or from the destructor of THAT object), before that object is destructed.
// Cancel may be called multiple times.
~AIFrameTimer() { cancel(); }
void create(F64 expiration, signal_type::slot_type const& slot);
void cancel(void);
bool isRunning(void) const { bool running; sMutex.lock(); running = mHandle.mRunningTimer != sTimerList.end(); sMutex.unlock(); return running; }
protected:
static void handleExpiration(F64 current_frame_time);
};
#endif

View File

@@ -124,7 +124,7 @@ public:
// NOTE: This next two funtions are only included here // NOTE: This next two funtions are only included here
// for those too familiar with the LLLinkedList template class. // for those too familiar with the LLLinkedList template class.
// They are depreciated. resetList() is unecessary while // They are deprecated. resetList() is unecessary while
// getCurrentData() is identical to getNextData() and has // getCurrentData() is identical to getNextData() and has
// a misleading name. // a misleading name.
// //
@@ -604,7 +604,7 @@ BOOL LLDoubleLinkedList<DATA_TYPE>::checkData(const DATA_TYPE *data)
// NOTE: This next two funtions are only included here // NOTE: This next two funtions are only included here
// for those too familiar with the LLLinkedList template class. // for those too familiar with the LLLinkedList template class.
// They are depreciated. resetList() is unecessary while // They are deprecated. resetList() is unecessary while
// getCurrentData() is identical to getNextData() and has // getCurrentData() is identical to getNextData() and has
// a misleading name. // a misleading name.
// //

View File

@@ -153,11 +153,6 @@ const char LAND_LAYER_CODE = 'L';
const char WATER_LAYER_CODE = 'W'; const char WATER_LAYER_CODE = 'W';
const char WIND_LAYER_CODE = '7'; const char WIND_LAYER_CODE = '7';
const char CLOUD_LAYER_CODE = '8'; const char CLOUD_LAYER_CODE = '8';
// Extended land layer for Aurora Sim
const char AURORA_LAND_LAYER_CODE = 'M';
const char AURORA_WATER_LAYER_CODE = 'X';
const char AURORA_WIND_LAYER_CODE = '9';
const char AURORA_CLOUD_LAYER_CODE = ':';
// keys // keys
// Bit masks for various keyboard modifier keys. // Bit masks for various keyboard modifier keys.

View File

@@ -0,0 +1,177 @@
/**
* @file ll_template_cast.h
* @author Nat Goodspeed
* @date 2009-11-21
* @brief Define ll_template_cast function
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LL_TEMPLATE_CAST_H)
#define LL_LL_TEMPLATE_CAST_H
/**
* Implementation for ll_template_cast() (q.v.).
*
* Default implementation: trying to cast two completely unrelated types
* returns 0. Typically you'd specify T and U as pointer types, but in fact T
* can be any type that can be initialized with 0.
*/
template <typename T, typename U>
struct ll_template_cast_impl
{
T operator()(U)
{
return 0;
}
};
/**
* ll_template_cast<T>(some_value) is for use in a template function when
* some_value might be of arbitrary type, but you want to recognize type T
* specially.
*
* It's designed for use with pointer types. Example:
* @code
* struct SpecialClass
* {
* void someMethod(const std::string&) const;
* };
*
* template <class REALCLASS>
* void somefunc(const REALCLASS& instance)
* {
* const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance);
* if (ptr)
* {
* ptr->someMethod("Call method only available on SpecialClass");
* }
* }
* @endcode
*
* Why is this better than dynamic_cast<>? Because unless OtherClass is
* polymorphic, the following won't even compile (gcc 4.0.1):
* @code
* OtherClass other;
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other);
* @endcode
* to say nothing of this:
* @code
* void function(int);
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function);
* @endcode
* ll_template_cast handles these kinds of cases by returning 0.
*/
template <typename T, typename U>
T ll_template_cast(U value)
{
return ll_template_cast_impl<T, U>()(value);
}
/**
* Implementation for ll_template_cast() (q.v.).
*
* Implementation for identical types: return same value.
*/
template <typename T>
struct ll_template_cast_impl<T, T>
{
T operator()(T value)
{
return value;
}
};
/**
* LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of
* type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s --
* presuming that @c source can be converted to @c dest by the normal rules of
* C++.
*
* By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's
* type is literally identical to @c dest. (This is because of the
* straightforward application of template specialization rules.) That can
* lead to surprising results, e.g.:
*
* @code
* Foo myFoo;
* const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo);
* @endcode
*
* Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt>
* -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would
* force the compiler to do the right thing.)
*
* More disappointingly:
* @code
* struct Base {};
* struct Subclass: public Base {};
* Subclass object;
* Base* ptr = ll_template_cast<Base*>(&object);
* @endcode
*
* Here @c ptr will be 0 because <tt>&object</tt> is of type
* <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to
* succeed, but without our help ll_template_cast can't recognize it.
*
* The following would suffice:
* @code
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
* ...
* Base* ptr = ll_template_cast<Base*>(&object);
* @endcode
*
* However, as noted earlier, this is easily fooled:
* @code
* const Base* ptr = ll_template_cast<const Base*>(&object);
* @endcode
* would still produce 0 because we haven't yet seen:
* @code
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
* @endcode
*
* @TODO
* This macro should use Boost type_traits facilities for stripping and
* re-adding @c const and @c volatile qualifiers so that invoking
* LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all
* permitted permutations. It's really not fair to the coder to require
* separate:
* @code
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
* LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*);
* @endcode
*
* (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt>
* because that's not permitted by normal C++ assignment anyway.)
*/
#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \
template <> \
struct ll_template_cast_impl<DEST, SOURCE> \
{ \
DEST operator()(SOURCE wrapper) \
{ \
return wrapper; \
} \
}
#endif /* ! defined(LL_LL_TEMPLATE_CAST_H) */

View File

@@ -0,0 +1,134 @@
/**
* @file llallocator.cpp
* @brief Implementation of the LLAllocator class.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llallocator.h"
#if LL_USE_TCMALLOC
#include "google/heap-profiler.h"
#include "google/commandlineflags_public.h"
DECLARE_bool(heap_profile_use_stack_trace);
//DECLARE_double(tcmalloc_release_rate);
// static
void LLAllocator::pushMemType(S32 type)
{
if(isProfiling())
{
PushMemType(type);
}
}
// static
S32 LLAllocator::popMemType()
{
if (isProfiling())
{
return PopMemType();
}
else
{
return -1;
}
}
void LLAllocator::setProfilingEnabled(bool should_enable)
{
// NULL disables dumping to disk
static char const * const PREFIX = NULL;
if(should_enable)
{
HeapProfilerSetUseStackTrace(false);
HeapProfilerStart(PREFIX);
}
else
{
HeapProfilerStop();
}
}
// static
bool LLAllocator::isProfiling()
{
return IsHeapProfilerRunning();
}
std::string LLAllocator::getRawProfile()
{
// *TODO - fix google-perftools to accept an buffer to avoid this
// malloc-copy-free cycle.
char * buffer = GetHeapProfile();
std::string ret = buffer;
free(buffer);
return ret;
}
#else // LL_USE_TCMALLOC
//
// stub implementations for when tcmalloc is disabled
//
// static
void LLAllocator::pushMemType(S32 type)
{
}
// static
S32 LLAllocator::popMemType()
{
return -1;
}
void LLAllocator::setProfilingEnabled(bool should_enable)
{
}
// static
bool LLAllocator::isProfiling()
{
return false;
}
std::string LLAllocator::getRawProfile()
{
return std::string();
}
#endif // LL_USE_TCMALLOC
LLAllocatorHeapProfile const & LLAllocator::getProfile()
{
mProf.mLines.clear();
// *TODO - avoid making all these extra copies of things...
std::string prof_text = getRawProfile();
//std::cout << prof_text << std::endl;
mProf.parse(prof_text);
return mProf;
}

View File

@@ -0,0 +1,57 @@
/**
* @file llallocator.h
* @brief Declaration of the LLAllocator class.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLALLOCATOR_H
#define LL_LLALLOCATOR_H
#include <string>
#include "llmemtype.h"
#include "llallocator_heap_profile.h"
class LL_COMMON_API LLAllocator {
friend class LLMemoryView;
friend class LLMemType;
private:
static void pushMemType(S32 type);
static S32 popMemType();
public:
void setProfilingEnabled(bool should_enable);
static bool isProfiling();
LLAllocatorHeapProfile const & getProfile();
private:
std::string getRawProfile();
private:
LLAllocatorHeapProfile mProf;
};
#endif // LL_LLALLOCATOR_H

View File

@@ -0,0 +1,147 @@
/**
* @file llallocator_heap_profile.cpp
* @brief Implementation of the parser for tcmalloc heap profile data.
* @author Brad Kittenbrink
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llallocator_heap_profile.h"
#if LL_MSVC
// disable warning about boost::lexical_cast returning uninitialized data
// when it fails to parse the string
#pragma warning (disable:4701)
#pragma warning (disable:4702)
#endif
#include <boost/algorithm/string/split.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/range/iterator_range.hpp>
static const std::string HEAP_PROFILE_MAGIC_STR = "heap profile:";
static bool is_separator(char c)
{
return isspace(c) || c == '[' || c == ']' || c == ':';
}
void LLAllocatorHeapProfile::parse(std::string const & prof_text)
{
// a typedef for handling a token in the string buffer
// it's a begin/end pair of string::const_iterators
typedef boost::iterator_range<std::string::const_iterator> range_t;
mLines.clear();
if(prof_text.compare(0, HEAP_PROFILE_MAGIC_STR.length(), HEAP_PROFILE_MAGIC_STR) != 0)
{
// *TODO - determine if there should be some better error state than
// mLines being empty. -brad
llwarns << "invalid heap profile data passed into parser." << llendl;
return;
}
std::vector< range_t > prof_lines;
std::string::const_iterator prof_begin = prof_text.begin() + HEAP_PROFILE_MAGIC_STR.length();
range_t prof_range(prof_begin, prof_text.end());
boost::algorithm::split(prof_lines,
prof_range,
boost::bind(std::equal_to<llwchar>(), '\n', _1));
std::vector< range_t >::const_iterator i;
for(i = prof_lines.begin(); i != prof_lines.end() && !i->empty(); ++i)
{
range_t const & line_text = *i;
std::vector<range_t> line_elems;
boost::algorithm::split(line_elems,
line_text,
is_separator);
std::vector< range_t >::iterator j;
j = line_elems.begin();
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U32 live_count = boost::lexical_cast<U32>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U64 live_size = boost::lexical_cast<U64>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U32 tot_count = boost::lexical_cast<U32>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U64 tot_size = boost::lexical_cast<U64>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert(j != line_elems.end());
if (j != line_elems.end())
{
++j; // skip the '@'
mLines.push_back(line(live_count, live_size, tot_count, tot_size));
line & current_line = mLines.back();
for(; j != line_elems.end(); ++j)
{
if(!j->empty())
{
U32 marker = boost::lexical_cast<U32>(*j);
current_line.mTrace.push_back(marker);
}
}
}
}
// *TODO - parse MAPPED_LIBRARIES section here if we're ever interested in it
}
void LLAllocatorHeapProfile::dump(std::ostream & out) const
{
lines_t::const_iterator i;
for(i = mLines.begin(); i != mLines.end(); ++i)
{
out << i->mLiveCount << ": " << i->mLiveSize << '[' << i->mTotalCount << ": " << i->mTotalSize << "] @";
stack_trace::const_iterator j;
for(j = i->mTrace.begin(); j != i->mTrace.end(); ++j)
{
out << ' ' << *j;
}
out << '\n';
}
out.flush();
}

View File

@@ -0,0 +1,71 @@
/**
* @file llallocator_heap_profile.h
* @brief Declaration of the parser for tcmalloc heap profile data.
* @author Brad Kittenbrink
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLALLOCATOR_HEAP_PROFILE_H
#define LL_LLALLOCATOR_HEAP_PROFILE_H
#include "stdtypes.h"
#include <map>
#include <vector>
class LLAllocatorHeapProfile
{
public:
typedef int stack_marker;
typedef std::vector<stack_marker> stack_trace;
struct line {
line(U32 live_count, U64 live_size, U32 tot_count, U64 tot_size) :
mLiveSize(live_size),
mTotalSize(tot_size),
mLiveCount(live_count),
mTotalCount(tot_count)
{
}
U64 mLiveSize, mTotalSize;
U32 mLiveCount, mTotalCount;
stack_trace mTrace;
};
typedef std::vector<line> lines_t;
LLAllocatorHeapProfile()
{
}
void parse(std::string const & prof_text);
void dump(std::ostream & out) const;
public:
lines_t mLines;
};
#endif // LL_LLALLOCATOR_HEAP_PROFILE_H

View File

@@ -401,6 +401,18 @@ bool LLApp::isExiting()
return isQuitting() || isError(); return isQuitting() || isError();
} }
void LLApp::disableCrashlogger()
{
// Disable Breakpad exception handler.
sDisableCrashlogger = TRUE;
}
// static
bool LLApp::isCrashloggerDisabled()
{
return (sDisableCrashlogger == TRUE);
}
#if !LL_WINDOWS #if !LL_WINDOWS
// static // static
U32 LLApp::getSigChildCount() U32 LLApp::getSigChildCount()
@@ -734,7 +746,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl; llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
} }
if(LLApp::sDisableCrashlogger) //Don't gracefully handle any signals crash and core for a gdb post mortum if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem
{ {
clear_signals(); clear_signals();
llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl; llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl;

View File

@@ -191,6 +191,11 @@ public:
// //
virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
//
// Crash logging
//
void disableCrashlogger(); // Let the OS handle the crashes
static bool isCrashloggerDisabled(); // Get the here above set value
// //
// Application status // Application status
@@ -214,9 +219,6 @@ public:
// //
void setErrorHandler(LLAppErrorHandler handler); void setErrorHandler(LLAppErrorHandler handler);
void setSyncErrorHandler(LLAppErrorHandler handler); void setSyncErrorHandler(LLAppErrorHandler handler);
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
#if !LL_WINDOWS #if !LL_WINDOWS
// //
// Child process handling (Unix only for now) // Child process handling (Unix only for now)
@@ -245,6 +247,7 @@ protected:
static void setStatus(EAppStatus status); // Use this to change the application status. static void setStatus(EAppStatus status); // Use this to change the application status.
static EAppStatus sStatus; // Reflects current application status static EAppStatus sStatus; // Reflects current application status
static BOOL sErrorThreadRunning; // Set while the error thread is running static BOOL sErrorThreadRunning; // Set while the error thread is running
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
#if !LL_WINDOWS #if !LL_WINDOWS
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received. static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.

View File

@@ -249,7 +249,7 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
} }
// //
//******************************************************************************************************************************* // *******************************************************************************************************************************
//static components of LLAPRFile //static components of LLAPRFile
// //
@@ -504,5 +504,5 @@ bool LLAPRFile::removeDir(const std::string& dirname)
} }
// //
//end of static components of LLAPRFile //end of static components of LLAPRFile
//******************************************************************************************************************************* // *******************************************************************************************************************************
// //

View File

@@ -168,7 +168,7 @@ public:
apr_file_t* getFileHandle() {return mFile;} apr_file_t* getFileHandle() {return mFile;}
// //
//******************************************************************************************************************************* // *******************************************************************************************************************************
//static components //static components
// //
private: private:
@@ -185,7 +185,7 @@ public:
// Returns bytes read/written, 0 if read/write fails: // Returns bytes read/written, 0 if read/write fails:
static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes); static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes); static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
//******************************************************************************************************************************* // *******************************************************************************************************************************
}; };
/** /**

View File

@@ -214,17 +214,16 @@ LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_
bool LLAssetType::lookupCanLink(EType asset_type) bool LLAssetType::lookupCanLink(EType asset_type)
{ {
//Check that enabling all these other types as linkable doesn't break things. //Check that enabling all these other types as linkable doesn't break things.
/*const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
const AssetEntry *entry = dict->lookup(asset_type); const AssetEntry *entry = dict->lookup(asset_type);
if (entry) if (entry)
{ {
return entry->mCanLink; return entry->mCanLink;
} }
return false; return false;
*/
return (asset_type == AT_CLOTHING || asset_type == AT_OBJECT || asset_type == AT_CATEGORY || /*return (asset_type == AT_CLOTHING || asset_type == AT_OBJECT || asset_type == AT_CATEGORY ||
asset_type == AT_BODYPART || asset_type == AT_GESTURE); asset_type == AT_BODYPART || asset_type == AT_GESTURE);*/
} }
// static // static

154
indra/llcommon/llcoros.cpp Normal file
View File

@@ -0,0 +1,154 @@
/**
* @file llcoros.cpp
* @author Nat Goodspeed
* @date 2009-06-03
* @brief Implementation for llcoros.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llcoros.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "llevents.h"
#include "llerror.h"
#include "stringize.h"
LLCoros::LLCoros()
{
// Register our cleanup() method for "mainloop" ticks
LLEventPumps::instance().obtain("mainloop").listen(
"LLCoros", boost::bind(&LLCoros::cleanup, this, _1));
}
bool LLCoros::cleanup(const LLSD&)
{
// Walk the mCoros map, checking and removing completed coroutines.
for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; )
{
// Has this coroutine exited (normal return, exception, exit() call)
// since last tick?
if (mi->second->exited())
{
LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
// The erase() call will invalidate its passed iterator value --
// so increment mi FIRST -- but pass its original value to
// erase(). This is what postincrement is all about.
mCoros.erase(mi++);
}
else
{
// Still live, just skip this entry as if incrementing at the top
// of the loop as usual.
++mi;
}
}
return false;
}
std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
// Allowing empty name would make getName()'s not-found return ambiguous.
if (prefix.empty())
{
LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL;
}
// If the specified name isn't already in the map, just use that.
std::string name(prefix);
// Find the lowest numeric suffix that doesn't collide with an existing
// entry. Start with 2 just to make it more intuitive for any interested
// parties: e.g. "joe", "joe2", "joe3"...
for (int i = 2; ; name = STRINGIZE(prefix << i++))
{
if (mCoros.find(name) == mCoros.end())
{
LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
return name;
}
}
}
bool LLCoros::kill(const std::string& name)
{
CoroMap::iterator found = mCoros.find(name);
if (found == mCoros.end())
{
return false;
}
// Because this is a boost::ptr_map, erasing the map entry also destroys
// the referenced heap object, in this case the boost::coroutine object,
// which will terminate the coroutine.
mCoros.erase(found);
return true;
}
std::string LLCoros::getNameByID(const void* self_id) const
{
// Walk the existing coroutines, looking for one from which the 'self_id'
// passed to us comes.
for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
{
namespace coro_private = boost::coroutines::detail;
if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
== self_id)
{
return mi->first;
}
}
return "";
}
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
// does for warning suppression, and we really don't want to force
// optimization ON for other code even in Debug or RelWithDebInfo builds.
#if LL_MSVC
// work around broken optimizations
#pragma warning(disable: 4748)
#pragma optimize("", off)
#endif // LL_MSVC
std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
{
std::string name(generateDistinctName(prefix));
mCoros.insert(name, newCoro);
/* Run the coroutine until its first wait, then return here */
(*newCoro)(std::nothrow);
return name;
}
#if LL_MSVC
// reenable optimizations
#pragma optimize("", on)
#endif // LL_MSVC

166
indra/llcommon/llcoros.h Normal file
View File

@@ -0,0 +1,166 @@
/**
* @file llcoros.h
* @author Nat Goodspeed
* @date 2009-06-02
* @brief Manage running boost::coroutine instances
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLCOROS_H)
#define LL_LLCOROS_H
#include <boost/coroutine/coroutine.hpp>
#include "llsingleton.h"
#include <boost/ptr_container/ptr_map.hpp>
#include <string>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <stdexcept>
/**
* Registry of named Boost.Coroutine instances
*
* The Boost.Coroutine library supports the general case of a coroutine
* accepting arbitrary parameters and yielding multiple (sets of) results. For
* such use cases, it's natural for the invoking code to retain the coroutine
* instance: the consumer repeatedly calls into the coroutine, perhaps passing
* new parameter values, prompting it to yield its next result.
*
* Our typical coroutine usage is different, though. For us, coroutines
* provide an alternative to the @c Responder pattern. Our typical coroutine
* has @c void return, invoked in fire-and-forget mode: the handler for some
* user gesture launches the coroutine and promptly returns to the main loop.
* The coroutine initiates some action that will take multiple frames (e.g. a
* capability request), waits for its result, processes it and silently steals
* away.
*
* This usage poses two (related) problems:
*
* # Who should own the coroutine instance? If it's simply local to the
* handler code that launches it, return from the handler will destroy the
* coroutine object, terminating the coroutine.
* # Once the coroutine terminates, in whatever way, who's responsible for
* cleaning up the coroutine object?
*
* LLCoros is a Singleton collection of currently-active coroutine instances.
* Each has a name. You ask LLCoros to launch a new coroutine with a suggested
* name prefix; from your prefix it generates a distinct name, registers the
* new coroutine and returns the actual name.
*
* The name can be used to kill off the coroutine prematurely, if needed. It
* can also provide diagnostic info: we can look up the name of the
* currently-running coroutine.
*
* Finally, the next frame ("mainloop" event) after the coroutine terminates,
* LLCoros will notice its demise and destroy it.
*/
class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
{
public:
/// Canonical boost::coroutines::coroutine signature we use
typedef boost::coroutines::coroutine<void()> coro;
/// Canonical 'self' type
typedef coro::self self;
/**
* Create and start running a new coroutine with specified name. The name
* string you pass is a suggestion; it will be tweaked for uniqueness. The
* actual name is returned to you.
*
* Usage looks like this, for (e.g.) two coroutine parameters:
* @code
* class MyClass
* {
* public:
* ...
* // Do NOT NOT NOT accept reference params other than 'self'!
* // Pass by value only!
* void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
* ...
* };
* ...
* std::string name = LLCoros::instance().launch(
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
* "somestring", LLSD(17));
* @endcode
*
* Your function/method must accept LLCoros::self& as its first parameter.
* It can accept any other parameters you want -- but ONLY BY VALUE!
* Other reference parameters are a BAD IDEA! You Have Been Warned. See
* DEV-32777 comments for an explanation.
*
* Pass a callable that accepts the single LLCoros::self& parameter. It
* may work to pass a free function whose only parameter is 'self'; for
* all other cases use boost::bind(). Of course, for a non-static class
* method, the first parameter must be the class instance. Use the
* placeholder _1 for the 'self' parameter. Any other parameters should be
* passed via the bind() expression.
*
* launch() tweaks the suggested name so it won't collide with any
* existing coroutine instance, creates the coroutine instance, registers
* it with the tweaked name and runs it until its first wait. At that
* point it returns the tweaked name.
*/
template <typename CALLABLE>
std::string launch(const std::string& prefix, const CALLABLE& callable)
{
return launchImpl(prefix, new coro(callable));
}
/**
* Abort a running coroutine by name. Normally, when a coroutine either
* runs to completion or terminates with an exception, LLCoros quietly
* cleans it up. This is for use only when you must explicitly interrupt
* one prematurely. Returns @c true if the specified name was found and
* still running at the time.
*/
bool kill(const std::string& name);
/**
* From within a coroutine, pass its @c self object to look up the
* (tweaked) name string by which this coroutine is registered. Returns
* the empty string if not found (e.g. if the coroutine was launched by
* hand rather than using LLCoros::launch()).
*/
template <typename COROUTINE_SELF>
std::string getName(const COROUTINE_SELF& self) const
{
return getNameByID(self.get_id());
}
/// getName() by self.get_id()
std::string getNameByID(const void* self_id) const;
private:
friend class LLSingleton<LLCoros>;
LLCoros();
std::string launchImpl(const std::string& prefix, coro* newCoro);
std::string generateDistinctName(const std::string& prefix) const;
bool cleanup(const LLSD&);
typedef boost::ptr_map<std::string, coro> CoroMap;
CoroMap mCoros;
};
#endif /* ! defined(LL_LLCOROS_H) */

View File

@@ -43,6 +43,7 @@
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include "llfasttimer.h"
#include "lltimer.h" #include "lltimer.h"
#include "llstring.h" #include "llstring.h"
@@ -94,9 +95,11 @@ std::string LLDate::asRFC1123() const
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT")); return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
} }
LLFastTimer::DeclareTimer FT_DATE_FORMAT("Date Format");
std::string LLDate::toHTTPDateString (std::string fmt) const std::string LLDate::toHTTPDateString (std::string fmt) const
{ {
LLFastTimer ft1(FT_DATE_FORMAT);
time_t locSeconds = (time_t) mSecondsSinceEpoch; time_t locSeconds = (time_t) mSecondsSinceEpoch;
struct tm * gmt = gmtime (&locSeconds); struct tm * gmt = gmtime (&locSeconds);
@@ -105,7 +108,7 @@ std::string LLDate::toHTTPDateString (std::string fmt) const
std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt) std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
{ {
// Return Epoch UTC date LLFastTimer ft1(FT_DATE_FORMAT);
// avoid calling setlocale() unnecessarily - it's expensive. // avoid calling setlocale() unnecessarily - it's expensive.
static std::string prev_locale = ""; static std::string prev_locale = "";

View File

@@ -0,0 +1,103 @@
/**
* @file lldependencies.cpp
* @author Nat Goodspeed
* @date 2008-09-17
* @brief Implementation for lldependencies.
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lldependencies.h"
// STL headers
#include <map>
#include <sstream>
// std headers
// external library headers
#include <boost/graph/graph_traits.hpp> // for boost::graph_traits
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/exception.hpp>
// other Linden headers
LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const
{
// Construct a Boost Graph Library graph according to the constraints
// we've collected. It seems as though we ought to be able to capture
// the uniqueness of vertex keys using a setS of vertices with a
// string property -- but I don't yet understand adjacency_list well
// enough to get there. All the examples I've seen so far use integers
// for vertices.
// Define the Graph type. Use a vector for vertices so we can use the
// default topological_sort vertex lookup by int index. Use a set for
// edges because the same dependency may be stated twice: Node "a" may
// specify that it must precede "b", while "b" may also state that it
// must follow "a".
typedef boost::adjacency_list<boost::setS, boost::vecS, boost::directedS,
boost::no_property> Graph;
// Instantiate the graph. Without vertex properties, we need say no
// more about vertices than the total number.
Graph g(edges.begin(), edges.end(), vertices);
// topo sort
typedef boost::graph_traits<Graph>::vertex_descriptor VertexDesc;
typedef std::vector<VertexDesc> SortedList;
SortedList sorted;
// note that it throws not_a_dag if it finds a cycle
try
{
boost::topological_sort(g, std::back_inserter(sorted));
}
catch (const boost::not_a_dag& e)
{
// translate to the exception we define
std::ostringstream out;
out << "LLDependencies cycle: " << e.what() << '\n';
// Omit independent nodes: display only those that might contribute to
// the cycle.
describe(out, false);
throw Cycle(out.str());
}
// A peculiarity of boost::topological_sort() is that it emits results in
// REVERSE topological order: to get the result you want, you must
// traverse the SortedList using reverse iterators.
return VertexList(sorted.rbegin(), sorted.rend());
}
std::ostream& LLDependenciesBase::describe(std::ostream& out, bool full) const
{
// Should never encounter this base-class implementation; may mean that
// the KEY type doesn't have a suitable ostream operator<<().
out << "<no description available>";
return out;
}
std::string LLDependenciesBase::describe(bool full) const
{
// Just use the ostream-based describe() on a std::ostringstream. The
// implementation is here mostly so that we can avoid #include <sstream>
// in the header file.
std::ostringstream out;
describe(out, full);
return out.str();
}

View File

@@ -0,0 +1,799 @@
/**
* @file lldependencies.h
* @author Nat Goodspeed
* @date 2008-09-17
* @brief LLDependencies: a generic mechanism for expressing "b must follow a,
* but precede c"
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLDEPENDENCIES_H)
#define LL_LLDEPENDENCIES_H
#include <string>
#include <vector>
#include <set>
#include <map>
#include <stdexcept>
#include <iosfwd>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
/*****************************************************************************
* Utilities
*****************************************************************************/
/**
* generic range transformer: given a range acceptable to Boost.Range (e.g. a
* standard container, an iterator pair, ...) and a unary function to apply to
* each element of the range, make a corresponding range that lazily applies
* that function to each element on dereferencing.
*/
template<typename FUNCTION, typename RANGE>
inline
boost::iterator_range<boost::transform_iterator<FUNCTION,
typename boost::range_const_iterator<RANGE>::type> >
make_transform_range(const RANGE& range, FUNCTION function)
{
// shorthand for the iterator type embedded in our return type
typedef boost::transform_iterator<FUNCTION, typename boost::range_const_iterator<RANGE>::type>
transform_iterator;
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
transform_iterator(boost::end(range), function));
}
/// non-const version of make_transform_range()
template<typename FUNCTION, typename RANGE>
inline
boost::iterator_range<boost::transform_iterator<FUNCTION,
typename boost::range_iterator<RANGE>::type> >
make_transform_range(RANGE& range, FUNCTION function)
{
// shorthand for the iterator type embedded in our return type
typedef boost::transform_iterator<FUNCTION, typename boost::range_iterator<RANGE>::type>
transform_iterator;
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
transform_iterator(boost::end(range), function));
}
/**
* From any range compatible with Boost.Range, instantiate any class capable
* of accepting an iterator pair.
*/
template<class TYPE>
struct instance_from_range: public TYPE
{
template<typename RANGE>
instance_from_range(RANGE range):
TYPE(boost::begin(range), boost::end(range))
{}
};
/*****************************************************************************
* LLDependencies
*****************************************************************************/
/**
* LLDependencies components that should not be reinstantiated for each KEY,
* NODE specialization
*/
class LL_COMMON_API LLDependenciesBase
{
public:
virtual ~LLDependenciesBase() {}
/**
* Exception thrown by sort() if there's a cycle
*/
struct Cycle: public std::runtime_error
{
Cycle(const std::string& what): std::runtime_error(what) {}
};
/**
* Provide a short description of this LLDependencies instance on the
* specified output stream, assuming that its KEY type has an operator<<()
* that works with std::ostream.
*
* Pass @a full as @c false to omit any keys without dependency constraints.
*/
virtual std::ostream& describe(std::ostream& out, bool full=true) const;
/// describe() to a string
virtual std::string describe(bool full=true) const;
protected:
typedef std::vector< std::pair<int, int> > EdgeList;
typedef std::vector<int> VertexList;
VertexList topo_sort(int vertices, const EdgeList& edges) const;
/**
* refpair is specifically intended to capture a pair of references. This
* is better than std::pair<T1&, T2&> because some implementations of
* std::pair's ctor accept const references to the two types. If the
* types are themselves references, this results in an illegal reference-
* to-reference.
*/
template<typename T1, typename T2>
struct refpair
{
refpair(T1 value1, T2 value2):
first(value1),
second(value2)
{}
T1 first;
T2 second;
};
};
/// describe() helper: for most types, report the type as usual
template<typename T>
inline
std::ostream& LLDependencies_describe(std::ostream& out, const T& key)
{
out << key;
return out;
}
/// specialize LLDependencies_describe() for std::string
template<>
inline
std::ostream& LLDependencies_describe(std::ostream& out, const std::string& key)
{
out << '"' << key << '"';
return out;
}
/**
* It's reasonable to use LLDependencies in a keys-only way, more or less like
* std::set. For that case, the default NODE type is an empty struct.
*/
struct LLDependenciesEmpty
{
LLDependenciesEmpty() {}
/**
* Give it a constructor accepting void* so caller can pass placeholder
* values such as NULL or 0 rather than having to write
* LLDependenciesEmpty().
*/
LLDependenciesEmpty(void*) {}
};
/**
* This class manages abstract dependencies between node types of your
* choosing. As with a std::map, nodes are copied when add()ed, so the node
* type should be relatively lightweight; to manipulate dependencies between
* expensive objects, use a pointer type.
*
* For a given node, you may state the keys of nodes that must precede it
* and/or nodes that must follow it. The sort() method will produce an order
* that should work, or throw an exception if the constraints are impossible.
* We cache results to minimize the cost of repeated sort() calls.
*/
template<typename KEY = std::string,
typename NODE = LLDependenciesEmpty>
class LLDependencies: public LLDependenciesBase
{
typedef LLDependencies<KEY, NODE> self_type;
/**
* Internally, we bundle the client's NODE with its before/after keys.
*/
struct DepNode
{
typedef std::set<KEY> dep_set;
DepNode(const NODE& node_, const dep_set& after_, const dep_set& before_):
node(node_),
after(after_),
before(before_)
{}
NODE node;
dep_set after, before;
};
typedef std::map<KEY, DepNode> DepNodeMap;
typedef typename DepNodeMap::value_type DepNodeMapEntry;
/// We have various ways to get the dependencies for a given DepNode.
/// Rather than having to restate each one for 'after' and 'before'
/// separately, pass a dep_selector so we can apply each to either.
typedef boost::function<const typename DepNode::dep_set&(const DepNode&)> dep_selector;
public:
LLDependencies() {}
typedef KEY key_type;
typedef NODE node_type;
/// param type used to express lists of other node keys -- note that such
/// lists can be initialized with boost::assign::list_of()
typedef std::vector<KEY> KeyList;
/**
* Add a new node. State its dependencies on other nodes (which may not
* yet have been added) by listing the keys of nodes this new one must
* follow, and separately the keys of nodes this new one must precede.
*
* The node you pass is @em copied into an internal data structure. If you
* want to modify the node value after add()ing it, capture the returned
* NODE& reference.
*
* @note
* Actual dependency analysis is deferred to the sort() method, so
* you can add an arbitrary number of nodes without incurring analysis
* overhead for each. The flip side of this is that add()ing nodes that
* define a cycle leaves this object in a state in which sort() will
* always throw the Cycle exception.
*
* Two distinct use cases are anticipated:
* * The data used to load this object are completely known at compile
* time (e.g. LLEventPump listener names). A Cycle exception represents a
* bug which can be corrected by the coder. The program need neither catch
* Cycle nor attempt to salvage the state of this object.
* * The data are loaded at runtime, therefore the universe of
* dependencies cannot be known at compile time. The client code should
* catch Cycle.
* ** If a Cycle exception indicates fatally-flawed input data, this
* object can simply be discarded, possibly with the entire program run.
* ** If it is essential to restore this object to a working state, the
* simplest workaround is to remove() nodes in LIFO order.
* *** It may be useful to add functionality to this class to track the
* add() chronology, providing a pop() method to remove the most recently
* added node.
* *** It may further be useful to add a restore() method which would
* pop() until sort() no longer throws Cycle. This method would be
* expensive -- but it's not clear that client code could resolve the
* problem more cheaply.
*/
NODE& add(const KEY& key, const NODE& node = NODE(),
const KeyList& after = KeyList(),
const KeyList& before = KeyList())
{
// Get the passed-in lists as sets for equality comparison
typename DepNode::dep_set
after_set(after.begin(), after.end()),
before_set(before.begin(), before.end());
// Try to insert the new node; if it already exists, find the old
// node instead.
std::pair<typename DepNodeMap::iterator, bool> inserted =
mNodes.insert(typename DepNodeMap::value_type(key,
DepNode(node, after_set, before_set)));
if (! inserted.second) // bool indicating success of insert()
{
// We already have a node by this name. Have its dependencies
// changed? If the existing node's dependencies are identical, the
// result will be unchanged, so we can leave the cache intact.
// Regardless of inserted.second, inserted.first is the iterator
// to the newly-inserted (or existing) map entry. Of course, that
// entry's second is the DepNode of interest.
if (inserted.first->second.after != after_set ||
inserted.first->second.before != before_set)
{
// Dependencies have changed: clear the cached result.
mCache.clear();
// save the new dependencies
inserted.first->second.after = after_set;
inserted.first->second.before = before_set;
}
}
else // this node is new
{
// This will change results.
mCache.clear();
}
return inserted.first->second.node;
}
/// the value of an iterator, showing both KEY and its NODE
typedef refpair<const KEY&, NODE&> value_type;
/// the value of a const_iterator
typedef refpair<const KEY&, const NODE&> const_value_type;
private:
// Extract functors
static value_type value_extract(DepNodeMapEntry& entry)
{
return value_type(entry.first, entry.second.node);
}
static const_value_type const_value_extract(const DepNodeMapEntry& entry)
{
return const_value_type(entry.first, entry.second.node);
}
// All the iterator access methods return iterator ranges just to cut down
// on the friggin' boilerplate!!
/// generic mNodes range method
template<typename ITERATOR, typename FUNCTION>
boost::iterator_range<ITERATOR> generic_range(FUNCTION function)
{
return make_transform_range(mNodes, function);
}
/// generic mNodes const range method
template<typename ITERATOR, typename FUNCTION>
boost::iterator_range<ITERATOR> generic_range(FUNCTION function) const
{
return make_transform_range(mNodes, function);
}
public:
/// iterator over value_type entries
typedef boost::transform_iterator<boost::function<value_type(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> iterator;
/// range over value_type entries
typedef boost::iterator_range<iterator> range;
/// iterate over value_type <i>in @c KEY order</i> rather than dependency order
range get_range()
{
return generic_range<iterator>(value_extract);
}
/// iterator over const_value_type entries
typedef boost::transform_iterator<boost::function<const_value_type(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_iterator;
/// range over const_value_type entries
typedef boost::iterator_range<const_iterator> const_range;
/// iterate over const_value_type <i>in @c KEY order</i> rather than dependency order
const_range get_range() const
{
return generic_range<const_iterator>(const_value_extract);
}
/// iterator over stored NODEs
typedef boost::transform_iterator<boost::function<NODE&(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> node_iterator;
/// range over stored NODEs
typedef boost::iterator_range<node_iterator> node_range;
/// iterate over NODE <i>in @c KEY order</i> rather than dependency order
node_range get_node_range()
{
// First take a DepNodeMapEntry and extract a reference to its
// DepNode, then from that extract a reference to its NODE.
return generic_range<node_iterator>(
boost::bind<NODE&>(&DepNode::node,
boost::bind<DepNode&>(&DepNodeMapEntry::second, _1)));
}
/// const iterator over stored NODEs
typedef boost::transform_iterator<boost::function<const NODE&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_node_iterator;
/// const range over stored NODEs
typedef boost::iterator_range<const_node_iterator> const_node_range;
/// iterate over const NODE <i>in @c KEY order</i> rather than dependency order
const_node_range get_node_range() const
{
// First take a DepNodeMapEntry and extract a reference to its
// DepNode, then from that extract a reference to its NODE.
return generic_range<const_node_iterator>(
boost::bind<const NODE&>(&DepNode::node,
boost::bind<const DepNode&>(&DepNodeMapEntry::second, _1)));
}
/// const iterator over stored KEYs
typedef boost::transform_iterator<boost::function<const KEY&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_key_iterator;
/// const range over stored KEYs
typedef boost::iterator_range<const_key_iterator> const_key_range;
// We don't provide a non-const iterator over KEYs because they should be
// immutable, and in fact our underlying std::map won't give us non-const
// references.
/// iterate over const KEY <i>in @c KEY order</i> rather than dependency order
const_key_range get_key_range() const
{
// From a DepNodeMapEntry, extract a reference to its KEY.
return generic_range<const_key_iterator>(
boost::bind<const KEY&>(&DepNodeMapEntry::first, _1));
}
/**
* Find an existing NODE, or return NULL. We decided to avoid providing a
* method analogous to std::map::find(), for a couple of reasons:
*
* * For a find-by-key, getting back an iterator to the (key, value) pair
* is less than useful, since you already have the key in hand.
* * For a failed request, comparing to end() is problematic. First, we
* provide range accessors, so it's more code to get end(). Second, we
* provide a number of different ranges -- quick, to which one's end()
* should we compare the iterator returned by find()?
*
* The returned pointer is solely to allow expressing the not-found
* condition. LLDependencies still owns the found NODE.
*/
const NODE* get(const KEY& key) const
{
typename DepNodeMap::const_iterator found = mNodes.find(key);
if (found != mNodes.end())
{
return &found->second.node;
}
return NULL;
}
/**
* non-const get()
*/
NODE* get(const KEY& key)
{
// Use const implementation, then cast away const-ness of return
return const_cast<NODE*>(const_cast<const self_type*>(this)->get(key));
}
/**
* Remove a node with specified key. This operation is the major reason
* we rebuild the graph on the fly instead of storing it.
*/
bool remove(const KEY& key)
{
typename DepNodeMap::iterator found = mNodes.find(key);
if (found != mNodes.end())
{
mNodes.erase(found);
return true;
}
return false;
}
private:
/// cached list of iterators
typedef std::vector<iterator> iterator_list;
typedef typename iterator_list::iterator iterator_list_iterator;
public:
/**
* The return type of the sort() method needs some explanation. Provide a
* public typedef to facilitate storing the result.
*
* * We will prepare mCache by looking up DepNodeMap iterators.
* * We want to return a range containing iterators that will walk mCache.
* * If we simply stored DepNodeMap iterators and returned
* (mCache.begin(), mCache.end()), dereferencing each iterator would
* obtain a DepNodeMap iterator.
* * We want the caller to loop over @c value_type: pair<KEY, NODE>.
* * This requires two transformations:
* ** mCache must contain @c LLDependencies::iterator so that
* dereferencing each entry will obtain an @c LLDependencies::value_type
* rather than a DepNodeMapEntry.
* ** We must wrap mCache's iterators in boost::indirect_iterator so that
* dereferencing one of our returned iterators will also dereference the
* iterator contained in mCache.
*/
typedef boost::iterator_range<boost::indirect_iterator<iterator_list_iterator> > sorted_range;
/// for convenience in looping over a sorted_range
typedef typename sorted_range::iterator sorted_iterator;
/**
* Once we've loaded in the dependencies of interest, arrange them into an
* order that works -- or throw Cycle exception.
*
* Return an iterator range over (key, node) pairs that traverses them in
* the desired order.
*/
sorted_range sort() const
{
// Changes to mNodes cause us to clear our cache, so empty mCache
// means it's invalid and should be recomputed. However, if mNodes is
// also empty, then an empty mCache represents a valid order, so don't
// bother sorting.
if (mCache.empty() && ! mNodes.empty())
{
// Construct a map of node keys to distinct vertex numbers -- even for
// nodes mentioned only in before/after constraints, that haven't yet
// been explicitly added. Rely on std::map rejecting a second attempt
// to insert the same key. Use the map's size() as the vertex number
// to get a distinct value for each successful insertion.
typedef std::map<KEY, int> VertexMap;
VertexMap vmap;
// Nest each of these loops because !@#$%? MSVC warns us that its
// former broken behavior has finally been fixed -- and our builds
// treat warnings as errors.
{
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
nmi != nmend; ++nmi)
{
vmap.insert(typename VertexMap::value_type(nmi->first, vmap.size()));
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
aend = nmi->second.after.end();
ai != aend; ++ai)
{
vmap.insert(typename VertexMap::value_type(*ai, vmap.size()));
}
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
bend = nmi->second.before.end();
bi != bend; ++bi)
{
vmap.insert(typename VertexMap::value_type(*bi, vmap.size()));
}
}
}
// Define the edges. For this we must traverse mNodes again, mapping
// all the known key dependencies to integer pairs.
EdgeList edges;
{
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
nmi != nmend; ++nmi)
{
int thisnode = vmap[nmi->first];
// after dependencies: build edges from the named node to this one
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
aend = nmi->second.after.end();
ai != aend; ++ai)
{
edges.push_back(EdgeList::value_type(vmap[*ai], thisnode));
}
// before dependencies: build edges from this node to the
// named one
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
bend = nmi->second.before.end();
bi != bend; ++bi)
{
edges.push_back(EdgeList::value_type(thisnode, vmap[*bi]));
}
}
}
// Hide the gory details of our topological sort, since they shouldn't
// get reinstantiated for each distinct NODE type.
VertexList sorted(topo_sort(vmap.size(), edges));
// Build the reverse of vmap to look up the key for each vertex
// descriptor. vmap contains exactly one entry for each distinct key,
// and we're certain that the associated int values are distinct
// indexes. The fact that they're not in order is irrelevant.
KeyList vkeys(vmap.size());
for (typename VertexMap::const_iterator vmi = vmap.begin(), vmend = vmap.end();
vmi != vmend; ++vmi)
{
vkeys[vmi->second] = vmi->first;
}
// Walk the sorted output list, building the result into mCache so
// we'll have it next time someone asks.
mCache.clear();
for (VertexList::const_iterator svi = sorted.begin(), svend = sorted.end();
svi != svend; ++svi)
{
// We're certain that vkeys[*svi] exists. However, there might not
// yet be a corresponding entry in mNodes.
self_type* non_const_this(const_cast<self_type*>(this));
typename DepNodeMap::iterator found = non_const_this->mNodes.find(vkeys[*svi]);
if (found != non_const_this->mNodes.end())
{
// Make an iterator of appropriate type.
mCache.push_back(iterator(found, value_extract));
}
}
}
// Whether or not we've just recomputed mCache, it should now contain
// the results we want. Return a range of indirect_iterators over it
// so that dereferencing a returned iterator will dereference the
// iterator stored in mCache and directly reference the (key, node)
// pair.
boost::indirect_iterator<iterator_list_iterator>
begin(mCache.begin()),
end(mCache.end());
return sorted_range(begin, end);
}
using LLDependenciesBase::describe; // unhide virtual std::string describe(bool full=true) const;
/// Override base-class describe() with actual implementation
virtual std::ostream& describe(std::ostream& out, bool full=true) const
{
typename DepNodeMap::const_iterator dmi(mNodes.begin()), dmend(mNodes.end());
if (dmi != dmend)
{
std::string sep;
describe(out, sep, *dmi, full);
while (++dmi != dmend)
{
describe(out, sep, *dmi, full);
}
}
return out;
}
/// describe() helper: report a DepNodeEntry
static std::ostream& describe(std::ostream& out, std::string& sep,
const DepNodeMapEntry& entry, bool full)
{
// If we were asked for a full report, describe every node regardless
// of whether it has dependencies. If we were asked to suppress
// independent nodes, describe this one if either after or before is
// non-empty.
if (full || (! entry.second.after.empty()) || (! entry.second.before.empty()))
{
out << sep;
sep = "\n";
if (! entry.second.after.empty())
{
out << "after ";
describe(out, entry.second.after);
out << " -> ";
}
LLDependencies_describe(out, entry.first);
if (! entry.second.before.empty())
{
out << " -> before ";
describe(out, entry.second.before);
}
}
return out;
}
/// describe() helper: report a dep_set
static std::ostream& describe(std::ostream& out, const typename DepNode::dep_set& keys)
{
out << '(';
typename DepNode::dep_set::const_iterator ki(keys.begin()), kend(keys.end());
if (ki != kend)
{
LLDependencies_describe(out, *ki);
while (++ki != kend)
{
out << ", ";
LLDependencies_describe(out, *ki);
}
}
out << ')';
return out;
}
/// Iterator over the before/after KEYs on which a given NODE depends
typedef typename DepNode::dep_set::const_iterator dep_iterator;
/// range over the before/after KEYs on which a given NODE depends
typedef boost::iterator_range<dep_iterator> dep_range;
/// dependencies access from key
dep_range get_dep_range_from_key(const KEY& key, const dep_selector& selector) const
{
typename DepNodeMap::const_iterator found = mNodes.find(key);
if (found != mNodes.end())
{
return dep_range(selector(found->second));
}
// We want to return an empty range. On some platforms a default-
// constructed range (e.g. dep_range()) does NOT suffice! The client
// is likely to try to iterate from boost::begin(range) to
// boost::end(range); yet these iterators might not be valid. Instead
// return a range over a valid, empty container.
static const typename DepNode::dep_set empty_deps;
return dep_range(empty_deps.begin(), empty_deps.end());
}
/// dependencies access from any one of our key-order iterators
template<typename ITERATOR>
dep_range get_dep_range_from_xform(const ITERATOR& iterator, const dep_selector& selector) const
{
return dep_range(selector(iterator.base()->second));
}
/// dependencies access from sorted_iterator
dep_range get_dep_range_from_sorted(const sorted_iterator& sortiter,
const dep_selector& selector) const
{
// sorted_iterator is a boost::indirect_iterator wrapping an mCache
// iterator, which we can obtain by sortiter.base(). Deferencing that
// gets us an mCache entry, an 'iterator' -- one of our traversal
// iterators -- on which we can use get_dep_range_from_xform().
return get_dep_range_from_xform(*sortiter.base(), selector);
}
/**
* Get a range over the after KEYs stored for the passed KEY or iterator,
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
* range -- same as a KEY with no after KEYs. Detect existence of a KEY
* using get() instead.
*/
template<typename KEY_OR_ITER>
dep_range get_after_range(const KEY_OR_ITER& key) const;
/**
* Get a range over the before KEYs stored for the passed KEY or iterator,
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
* range -- same as a KEY with no before KEYs. Detect existence of a KEY
* using get() instead.
*/
template<typename KEY_OR_ITER>
dep_range get_before_range(const KEY_OR_ITER& key) const;
private:
DepNodeMap mNodes;
mutable iterator_list mCache;
};
/**
* Functor to get a dep_range from a KEY or iterator -- generic case. If the
* passed value isn't one of our iterator specializations, assume it's
* convertible to the KEY type.
*/
template<typename KEY_ITER>
struct LLDependencies_dep_range_from
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const KEY_ITER& key,
const SELECTOR& selector)
{
return deps.get_dep_range_from_key(key, selector);
}
};
/// Specialize LLDependencies_dep_range_from for our key-order iterators
template<typename FUNCTION, typename ITERATOR>
struct LLDependencies_dep_range_from< boost::transform_iterator<FUNCTION, ITERATOR> >
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const boost::transform_iterator<FUNCTION, ITERATOR>& iter,
const SELECTOR& selector)
{
return deps.get_dep_range_from_xform(iter, selector);
}
};
/// Specialize LLDependencies_dep_range_from for sorted_iterator
template<typename BASEITER>
struct LLDependencies_dep_range_from< boost::indirect_iterator<BASEITER> >
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const boost::indirect_iterator<BASEITER>& iter,
const SELECTOR& selector)
{
return deps.get_dep_range_from_sorted(iter, selector);
}
};
/// generic get_after_range() implementation
template<typename KEY, typename NODE>
template<typename KEY_OR_ITER>
typename LLDependencies<KEY, NODE>::dep_range
LLDependencies<KEY, NODE>::get_after_range(const KEY_OR_ITER& key_iter) const
{
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
*this,
key_iter,
boost::bind<const typename DepNode::dep_set&>(&DepNode::after, _1));
}
/// generic get_before_range() implementation
template<typename KEY, typename NODE>
template<typename KEY_OR_ITER>
typename LLDependencies<KEY, NODE>::dep_range
LLDependencies<KEY, NODE>::get_before_range(const KEY_OR_ITER& key_iter) const
{
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
*this,
key_iter,
boost::bind<const typename DepNode::dep_set&>(&DepNode::before, _1));
}
#endif /* ! defined(LL_LLDEPENDENCIES_H) */

View File

@@ -107,7 +107,14 @@ const int LL_ERR_PRICE_MISMATCH = -23018;
#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl; #define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs << "ASSERT (" << #func << ")" << llendl; #define liru_slashpos std::string(__FILE__).find_last_of("/\\")
#define liru_slashpos2 std::string(__FILE__).substr(0,liru_slashpos).find_last_of("/\\")
#define liru_assert_strip /*strip path down to lastlevel directory and filename for assert.*/\
(liru_slashpos == std::string::npos ? std::string(__FILE__)/*just filename, print as is*/\
: liru_slashpos2 == std::string::npos ? std::string(__FILE__)/*Apparently, we're in / or perhaps the top of the drive, print as is*/\
: std::string(__FILE__).substr(1+liru_slashpos2))/*print foo/bar.cpp or perhaps foo\bar.cpp*/
#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs <<"\nASSERT(" #func ")\nfile:"<<liru_assert_strip<<" line:"<<__LINE__ << llendl;
#ifdef SHOW_ASSERT #ifdef SHOW_ASSERT
#define llassert(func) llassert_always(func) #define llassert(func) llassert_always(func)

View File

@@ -34,6 +34,8 @@
#include "llevent.h" #include "llevent.h"
using namespace LLOldEvents;
/************************************************ /************************************************
Events Events
************************************************/ ************************************************/

View File

@@ -35,9 +35,12 @@
#define LL_EVENT_H #define LL_EVENT_H
#include "llsd.h" #include "llsd.h"
#include "llmemory.h" #include "llpointer.h"
#include "llthread.h" #include "llthread.h"
namespace LLOldEvents
{
class LLEventListener; class LLEventListener;
class LLEvent; class LLEvent;
class LLEventDispatcher; class LLEventDispatcher;
@@ -194,4 +197,6 @@ public:
LLSD mValue; LLSD mValue;
}; };
} // LLOldEvents
#endif // LL_EVENT_H #endif // LL_EVENT_H

View File

@@ -0,0 +1,77 @@
/**
* @file lleventapi.cpp
* @author Nat Goodspeed
* @date 2009-11-10
* @brief Implementation for lleventapi.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventapi.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llerror.h"
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
lbase(name, field),
ibase(name),
mDesc(desc)
{
}
LLEventAPI::~LLEventAPI()
{
}
LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
mResp(seed),
mReq(request),
mKey(replyKey)
{}
LLEventAPI::Response::~Response()
{
// When you instantiate a stack Response object, if the original
// request requested a reply, send it when we leave this block, no
// matter how.
sendReply(mResp, mReq, mKey);
}
void LLEventAPI::Response::warn(const std::string& warning)
{
LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL;
mResp["warnings"].append(warning);
}
void LLEventAPI::Response::error(const std::string& error)
{
// Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut
// down altogether.
LL_WARNS("LLEventAPI::Response") << error << LL_ENDL;
mResp["error"] = error;
}

166
indra/llcommon/lleventapi.h Normal file
View File

@@ -0,0 +1,166 @@
/**
* @file lleventapi.h
* @author Nat Goodspeed
* @date 2009-10-28
* @brief LLEventAPI is the base class for every class that wraps a C++ API
* in an event API
* (see https://wiki.lindenlab.com/wiki/Incremental_Viewer_Automation/Event_API).
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLEVENTAPI_H)
#define LL_LLEVENTAPI_H
#include "lleventdispatcher.h"
#include "llinstancetracker.h"
#include <string>
/**
* LLEventAPI not only provides operation dispatch functionality, inherited
* from LLDispatchListener -- it also gives us event API introspection.
* Deriving from LLInstanceTracker lets us enumerate instances.
*/
class LL_COMMON_API LLEventAPI: public LLDispatchListener,
public LLInstanceTracker<LLEventAPI, std::string>
{
typedef LLDispatchListener lbase;
typedef LLInstanceTracker<LLEventAPI, std::string> ibase;
public:
/**
* @param name LLEventPump name on which this LLEventAPI will listen. This
* also serves as the LLInstanceTracker instance key.
* @param desc Documentation string shown to a client trying to discover
* available event APIs.
* @param field LLSD::Map key used by LLDispatchListener to look up the
* subclass method to invoke [default "op"].
*/
LLEventAPI(const std::string& name, const std::string& desc, const std::string& field="op");
virtual ~LLEventAPI();
/// Get the string name of this LLEventAPI
std::string getName() const { return ibase::getKey(); }
/// Get the documentation string
std::string getDesc() const { return mDesc; }
/**
* Publish only selected add() methods from LLEventDispatcher.
* Every LLEventAPI add() @em must have a description string.
*/
template <typename CALLABLE>
void add(const std::string& name,
const std::string& desc,
CALLABLE callable,
const LLSD& required=LLSD())
{
LLEventDispatcher::add(name, desc, callable, required);
}
/**
* Instantiate a Response object in any LLEventAPI subclass method that
* wants to guarantee a reply (if requested) will be sent on exit from the
* method. The reply will be sent if request.has(@a replyKey), default
* "reply". If specified, the value of request[replyKey] is the name of
* the LLEventPump on which to send the reply. Conventionally you might
* code something like:
*
* @code
* void MyEventAPI::someMethod(const LLSD& request)
* {
* // Send a reply event as long as request.has("reply")
* Response response(LLSD(), request);
* // ...
* // will be sent in reply event
* response["somekey"] = some_data;
* }
* @endcode
*/
class LL_COMMON_API Response
{
public:
/**
* Instantiating a Response object in an LLEventAPI subclass method
* ensures that, if desired, a reply event will be sent.
*
* @a seed is the initial reply LLSD that will be further decorated before
* being sent as the reply
*
* @a request is the incoming request LLSD; we particularly care about
* [replyKey] and ["reqid"]
*
* @a replyKey [default "reply"] is the string name of the LLEventPump
* on which the caller wants a reply. If <tt>(!
* request.has(replyKey))</tt>, no reply will be sent.
*/
Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply");
~Response();
/**
* @code
* if (some condition)
* {
* response.warn("warnings are logged and collected in [\"warnings\"]");
* }
* @endcode
*/
void warn(const std::string& warning);
/**
* @code
* if (some condition isn't met)
* {
* // In a function returning void, you can validly 'return
* // expression' if the expression is itself of type void. But
* // returning is up to you; response.error() has no effect on
* // flow of control.
* return response.error("error message, logged and also sent as [\"error\"]");
* }
* @endcode
*/
void error(const std::string& error);
/**
* set other keys...
*
* @code
* // set any attributes you want to be sent in the reply
* response["info"] = some_value;
* // ...
* response["ok"] = went_well;
* @endcode
*/
LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
/**
* set the response to the given data
*/
void setResponse(LLSD const & response){ mResp = response; }
LLSD mResp, mReq;
LLSD::String mKey;
};
private:
std::string mDesc;
};
#endif /* ! defined(LL_LLEVENTAPI_H) */

View File

@@ -0,0 +1,146 @@
/**
* @file lleventcoro.cpp
* @author Nat Goodspeed
* @date 2009-04-29
* @brief Implementation for lleventcoro.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventcoro.h"
// STL headers
#include <map>
// std headers
// external library headers
// other Linden headers
#include "llsdserialize.h"
#include "llerror.h"
#include "llcoros.h"
std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
{
// First, if this coroutine was launched by LLCoros::launch(), find that name.
std::string name(LLCoros::instance().getNameByID(self_id));
if (! name.empty())
{
return name;
}
// Apparently this coroutine wasn't launched by LLCoros::launch(). Check
// whether we have a memo for this self_id.
typedef std::map<const void*, std::string> MapType;
static MapType memo;
MapType::const_iterator found = memo.find(self_id);
if (found != memo.end())
{
// this coroutine instance has called us before, reuse same name
return found->second;
}
// this is the first time we've been called for this coroutine instance
name = LLEventPump::inventName("coro");
memo[self_id] = name;
LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '"
<< name << "'" << LL_ENDL;
return name;
}
void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
{
if (rawPath.isUndefined())
{
// no-op case
return;
}
// Arrange to treat rawPath uniformly as an array. If it's not already an
// array, store it as the only entry in one.
LLSD path;
if (rawPath.isArray())
{
path = rawPath;
}
else
{
path.append(rawPath);
}
// Need to indicate a current destination -- but that current destination
// needs to change as we step through the path array. Where normally we'd
// use an LLSD& to capture a subscripted LLSD lvalue, this time we must
// instead use a pointer -- since it must be reassigned.
LLSD* pdest = &dest;
// Now loop through that array
for (LLSD::Integer i = 0; i < path.size(); ++i)
{
if (path[i].isString())
{
// *pdest is an LLSD map
pdest = &((*pdest)[path[i].asString()]);
}
else if (path[i].isInteger())
{
// *pdest is an LLSD array
pdest = &((*pdest)[path[i].asInteger()]);
}
else
{
// What do we do with Real or Array or Map or ...?
// As it's a coder error -- not a user error -- rub the coder's
// face in it so it gets fixed.
LL_ERRS("lleventcoro") << "storeToLLSDPath(" << dest << ", " << rawPath << ", " << value
<< "): path[" << i << "] bad type " << path[i].type() << LL_ENDL;
}
}
// Here *pdest is where we should store value.
*pdest = value;
}
LLSD errorException(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), instead of
// returning it, deliver it via exception.
if (result.second)
{
throw LLErrorEvent(desc, result.first);
}
// That way, our caller knows a simple return must be from the reply
// pump (pump 0).
return result.first;
}
LLSD errorLog(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), log it as a fatal
// error.
if (result.second)
{
LL_ERRS("errorLog") << desc << ":" << std::endl;
LLSDSerialize::toPrettyXML(result.first, LL_CONT);
LL_CONT << LL_ENDL;
}
// A simple return must therefore be from the reply pump (pump 0).
return result.first;
}

View File

@@ -0,0 +1,569 @@
/**
* @file lleventcoro.h
* @author Nat Goodspeed
* @date 2009-04-29
* @brief Utilities to interface between coroutines and events.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLEVENTCORO_H)
#define LL_LLEVENTCORO_H
#include <boost/coroutine/coroutine.hpp>
#include <boost/coroutine/future.hpp>
#include <boost/optional.hpp>
#include <string>
#include <stdexcept>
#include "llevents.h"
#include "llerror.h"
/**
* Like LLListenerOrPumpName, this is a class intended for parameter lists:
* accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an
* <tt>LLEventPump&</tt> or its string name. For a single parameter that could
* be either, it's not hard to overload the function -- but as soon as you
* want to accept two such parameters, this is cheaper than four overloads.
*/
class LLEventPumpOrPumpName
{
public:
/// Pass an actual LLEventPump&
LLEventPumpOrPumpName(LLEventPump& pump):
mPump(pump)
{}
/// Pass the string name of an LLEventPump
LLEventPumpOrPumpName(const std::string& pumpname):
mPump(LLEventPumps::instance().obtain(pumpname))
{}
/// Pass string constant name of an LLEventPump. This override must be
/// explicit, since otherwise passing <tt>const char*</tt> to a function
/// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two
/// different implicit conversions: <tt>const char*</tt> -> <tt>const
/// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>.
LLEventPumpOrPumpName(const char* pumpname):
mPump(LLEventPumps::instance().obtain(pumpname))
{}
/// Unspecified: "I choose not to identify an LLEventPump."
LLEventPumpOrPumpName() {}
operator LLEventPump& () const { return *mPump; }
LLEventPump& getPump() const { return *mPump; }
operator bool() const { return mPump; }
bool operator!() const { return ! mPump; }
private:
boost::optional<LLEventPump&> mPump;
};
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
/// isn't a valid LLEventPump listener: such listeners should return bool.
template <typename LISTENER>
class LLVoidListener
{
public:
LLVoidListener(const LISTENER& listener):
mListener(listener)
{}
bool operator()(const LLSD& event)
{
mListener(event);
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
};
/// LLVoidListener helper function to infer the type of the LISTENER
template <typename LISTENER>
LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
{
return LLVoidListener<LISTENER>(listener);
}
namespace LLEventDetail
{
/**
* waitForEventOn() permits a coroutine to temporarily listen on an
* LLEventPump any number of times. We don't really want to have to ask
* the caller to label each such call with a distinct string; the whole
* point of waitForEventOn() is to present a nice sequential interface to
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
* LLEventPump::inventName() to generate a distinct name for each
* temporary listener. On the other hand, because a given coroutine might
* call waitForEventOn() any number of times, we don't really want to
* consume an arbitrary number of generated inventName()s: that namespace,
* though large, is nonetheless finite. So we memoize an invented name for
* each distinct coroutine instance (each different 'self' object). We
* can't know the type of 'self', because it depends on the coroutine
* body's signature. So we cast its address to void*, looking for distinct
* pointer values. Yes, that means that an early coroutine could cache a
* value here, then be destroyed, only to be supplanted by a later
* coroutine (of the same or different type), and we'll end up
* "recognizing" the second one and reusing the listener name -- but
* that's okay, since it won't collide with any listener name used by the
* earlier coroutine since that earlier coroutine no longer exists.
*/
template <typename COROUTINE_SELF>
std::string listenerNameForCoro(COROUTINE_SELF& self)
{
return listenerNameForCoroImpl(self.get_id());
}
/// Implementation for listenerNameForCoro()
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
/**
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
* parameter:
*
* * If <tt>path.isUndefined()</tt>, do nothing.
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
* into <tt>dest[path.asString()]</tt>.
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
* value into <tt>dest[path.asInteger()]</tt>.
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
* down through the structure of @a dest. The last array entry in @a
* path specifies the entry in the lowest-level structure in @a dest
* into which to store @a value.
*
* @note
* In the degenerate case in which @a path is an empty array, @a dest will
* @em become @a value rather than @em containing it.
*/
LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
} // namespace LLEventDetail
/**
* Post specified LLSD event on the specified LLEventPump, then wait for a
* response on specified other LLEventPump. This is more than mere
* convenience: the difference between this function and the sequence
* @code
* requestPump.post(myEvent);
* LLSD reply = waitForEventOn(self, replyPump);
* @endcode
* is that the sequence above fails if the reply is posted immediately on
* @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
* sequence above, the running coroutine isn't even listening on @a replyPump
* until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
* entered. Therefore, the coroutine completely misses an immediate reply
* event, making it wait indefinitely.
*
* By contrast, postAndWait() listens on the @a replyPump @em before posting
* the specified LLSD event on the specified @a requestPump.
*
* @param self The @c self object passed into a coroutine
* @param event LLSD data to be posted on @a requestPump
* @param requestPump an LLEventPump on which to post @a event. Pass either
* the LLEventPump& or its string name. However, if you pass a
* default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
* @param replyPump an LLEventPump on which postAndWait() will listen for a
* reply. Pass either the LLEventPump& or its string name. The calling
* coroutine will wait until that reply arrives. (If you're concerned about a
* reply that might not arrive, please see also LLEventTimeout.)
* @param replyPumpNamePath specifies the location within @a event in which to
* store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
* feature; obviously you can store the name in @a event "by hand" if desired.
* @a replyPumpNamePath can be specified in any of four forms:
* * @c isUndefined() (default-constructed LLSD object): do nothing. This is
* the default behavior if you omit @a replyPumpNamePath.
* * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt>
* in <tt>event[replyPumpNamePath.asInteger()]</tt>.
* * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in
* <tt>event[replyPumpNamePath.asString()]</tt>.
* * @c isArray(): @a event has several levels of structure, e.g. map of
* maps, array of arrays, array of maps, map of arrays, ... Store
* <tt>replyPump.getName()</tt> in
* <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other
* words, examine each array entry in @a replyPumpNamePath in turn. If it's an
* <tt>LLSD::String</tt>, the current level of @a event is a map; step down to
* that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a
* event is an array; step down to that array entry. The last array entry in
* @a replyPumpNamePath specifies the entry in the lowest-level structure in
* @a event into which to store <tt>replyPump.getName()</tt>.
*/
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
{
// declare the future
boost::coroutines::future<LLSD> future(self);
// make a callback that will assign a value to the future, and listen on
// the specified LLEventPump with that callback
std::string listenerName(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection(
replyPump.getPump().listen(listenerName,
voidlistener(boost::coroutines::make_callback(future))));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If replyPumpNamePath is non-empty, store the replyPump name in the
// request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " posting to " << requestPump.getPump().getName()
<< LL_ENDL;
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
// << ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
<< LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLSD value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " resuming with " << value << LL_ENDL;
// returning should disconnect the connection
return value;
}
/// Wait for the next event on the specified LLEventPump. Pass either the
/// LLEventPump& or its string name.
template <typename SELF>
LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
{
// This is now a convenience wrapper for postAndWait().
return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
}
/// return type for two-pump variant of waitForEventOn()
typedef std::pair<LLSD, int> LLEventWithID;
namespace LLEventDetail
{
/**
* This helper is specifically for the two-pump version of waitForEventOn().
* We use a single future object, but we want to listen on two pumps with it.
* Since we must still adapt from (the callable constructed by)
* boost::coroutines::make_callback() (void return) to provide an event
* listener (bool return), we've adapted LLVoidListener for the purpose. The
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
* -- binding different instance data -- for each of the pumps. Then, when a
* pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
* that LLSD with its discriminator to feed the future object.
*/
template <typename LISTENER>
class WaitForEventOnHelper
{
public:
WaitForEventOnHelper(const LISTENER& listener, int discriminator):
mListener(listener),
mDiscrim(discriminator)
{}
// this signature is required for an LLEventPump listener
bool operator()(const LLSD& event)
{
// our future object is defined to accept LLEventWithID
mListener(LLEventWithID(event, mDiscrim));
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
const int mDiscrim;
};
/// WaitForEventOnHelper type-inference helper
template <typename LISTENER>
WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
{
return WaitForEventOnHelper<LISTENER>(listener, discriminator);
}
} // namespace LLEventDetail
/**
* This function waits for a reply on either of two specified LLEventPumps.
* Otherwise, it closely resembles postAndWait(); please see the documentation
* for that function for detailed parameter info.
*
* While we could have implemented the single-pump variant in terms of this
* one, there's enough added complexity here to make it worthwhile to give the
* single-pump variant its own straightforward implementation. Conversely,
* though we could use preprocessor logic to generate n-pump overloads up to
* BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump
* overload exists because certain event APIs are defined in terms of a reply
* LLEventPump and an error LLEventPump.
*
* The LLEventWithID return value provides not only the received event, but
* the index of the pump on which it arrived (0 or 1).
*
* @note
* I'd have preferred to overload the name postAndWait() for both signatures.
* But consider the following ambiguous call:
* @code
* postAndWait(self, LLSD(), requestPump, replyPump, "someString");
* @endcode
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
* function).
*
* It seems less burdensome to write postAndWait2() than to write either
* LLSD("someString") or LLEventOrPumpName("someString").
*/
template <typename SELF>
LLEventWithID postAndWait2(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump0,
const LLEventPumpOrPumpName& replyPump1,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
// declare the future
boost::coroutines::future<LLEventWithID> future(self);
// either callback will assign a value to this future; listen on
// each specified LLEventPump with a callback
std::string name(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection0(
replyPump0.getPump().listen(name + "a",
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0)));
LLTempBoundListener connection1(
replyPump1.getPump().listen(name + "b",
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1)));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If either replyPumpNamePath is non-empty, store the corresponding
// replyPump name in the request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
replyPump0.getPump().getName());
LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
replyPump1.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " posting to " << requestPump.getPump().getName()
<< ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLEventWithID value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
<< " resuming with (" << value.first << ", " << value.second << ")"
<< LL_ENDL;
// returning should disconnect both connections
return value;
}
/**
* Wait for the next event on either of two specified LLEventPumps.
*/
template <typename SELF>
LLEventWithID
waitForEventOn(SELF& self,
const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
{
// This is now a convenience wrapper for postAndWait2().
return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
}
/**
* Helper for the two-pump variant of waitForEventOn(), e.g.:
*
* @code
* LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
* "error response from login.cgi");
* @endcode
*
* Examines an LLEventWithID, assuming that the second pump (pump 1) is
* listening for an error indication. If the incoming data arrived on pump 1,
* throw an LLErrorEvent exception. If the incoming data arrived on pump 0,
* just return it. Since a normal return can only be from pump 0, we no longer
* need the LLEventWithID's discriminator int; we can just return the LLSD.
*
* @note I'm not worried about introducing the (fairly generic) name
* errorException() into global namespace, because how many other overloads of
* the same name are going to accept an LLEventWithID parameter?
*/
LLSD errorException(const LLEventWithID& result, const std::string& desc);
/**
* Exception thrown by errorException(). We don't call this LLEventError
* because it's not an error in event processing: rather, this exception
* announces an event that bears error information (for some other API).
*/
class LL_COMMON_API LLErrorEvent: public std::runtime_error
{
public:
LLErrorEvent(const std::string& what, const LLSD& data):
std::runtime_error(what),
mData(data)
{}
virtual ~LLErrorEvent() throw() {}
LLSD getData() const { return mData; }
private:
LLSD mData;
};
/**
* Like errorException(), save that this trips a fatal error using LL_ERRS
* rather than throwing an exception.
*/
LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
/**
* Certain event APIs require the name of an LLEventPump on which they should
* post results. While it works to invent a distinct name and let
* LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
* in a certain sense it's more robust to instantiate a local LLEventPump and
* provide its name instead. This class packages the following idiom:
*
* 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
* 2. Provide its actual name to the event API in question as the name of the
* reply LLEventPump.
* 3. Initiate the request to the event API.
* 4. Call your LLEventTempStream's wait() method to wait for the reply.
* 5. Let the LLCoroEventPump go out of scope.
*/
class LL_COMMON_API LLCoroEventPump
{
public:
LLCoroEventPump(const std::string& name="coro"):
mPump(name, true) // allow tweaking the pump instance name
{}
/// It's typical to request the LLEventPump name to direct an event API to
/// send its response to this pump.
std::string getName() const { return mPump.getName(); }
/// Less typically, we'd request the pump itself for some reason.
LLEventPump& getPump() { return mPump; }
/**
* Wait for an event on this LLEventPump.
*
* @note
* The other major usage pattern we considered was to bind @c self at
* LLCoroEventPump construction time, which would avoid passing the
* parameter to each wait() call. But if we were going to bind @c self as
* a class member, we'd need to specify a class template parameter
* indicating its type. The big advantage of passing it to the wait() call
* is that the type can be implicit.
*/
template <typename SELF>
LLSD wait(SELF& self)
{
return waitForEventOn(self, mPump);
}
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPumpNamePath=LLSD())
{
return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
}
private:
LLEventStream mPump;
};
/**
* Other event APIs require the names of two different LLEventPumps: one for
* success response, the other for error response. Extend LLCoroEventPump
* for the two-pump use case.
*/
class LL_COMMON_API LLCoroEventPumps
{
public:
LLCoroEventPumps(const std::string& name="coro",
const std::string& suff0="Reply",
const std::string& suff1="Error"):
mPump0(name + suff0, true), // allow tweaking the pump instance name
mPump1(name + suff1, true)
{}
/// request pump 0's name
std::string getName0() const { return mPump0.getName(); }
/// request pump 1's name
std::string getName1() const { return mPump1.getName(); }
/// request both names
std::pair<std::string, std::string> getNames() const
{
return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName());
}
/// request pump 0
LLEventPump& getPump0() { return mPump0; }
/// request pump 1
LLEventPump& getPump1() { return mPump1; }
/// waitForEventOn(self, either of our two LLEventPumps)
template <typename SELF>
LLEventWithID wait(SELF& self)
{
return waitForEventOn(self, mPump0, mPump1);
}
/// errorException(wait(self))
template <typename SELF>
LLSD waitWithException(SELF& self)
{
return errorException(wait(self), std::string("Error event on ") + getName1());
}
/// errorLog(wait(self))
template <typename SELF>
LLSD waitWithLog(SELF& self)
{
return errorLog(wait(self), std::string("Error event on ") + getName1());
}
template <typename SELF>
LLEventWithID postAndWait(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return postAndWait2(self, event, requestPump, mPump0, mPump1,
replyPump0NamePath, replyPump1NamePath);
}
template <typename SELF>
LLSD postAndWaitWithException(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorException(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
template <typename SELF>
LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorLog(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
private:
LLEventStream mPump0, mPump1;
};
#endif /* ! defined(LL_LLEVENTCORO_H) */

View File

@@ -0,0 +1,672 @@
/**
* @file lleventdispatcher.cpp
* @author Nat Goodspeed
* @date 2009-06-18
* @brief Implementation for lleventdispatcher.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if LL_WINDOWS
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventdispatcher.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llevents.h"
#include "llerror.h"
#include "llsdutil.h"
#include "stringize.h"
#include <memory> // std::auto_ptr
/*****************************************************************************
* LLSDArgsSource
*****************************************************************************/
/**
* Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
* if the consumer requests more elements than the array contains.
*/
class LL_COMMON_API LLSDArgsSource
{
public:
LLSDArgsSource(const std::string function, const LLSD& args);
~LLSDArgsSource();
LLSD next();
void done() const;
private:
std::string _function;
LLSD _args;
LLSD::Integer _index;
};
LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
_function(function),
_args(args),
_index(0)
{
if (! (_args.isUndefined() || _args.isArray()))
{
LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
<< _args << LL_ENDL;
}
}
LLSDArgsSource::~LLSDArgsSource()
{
done();
}
LLSD LLSDArgsSource::next()
{
if (_index >= _args.size())
{
LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
<< _args.size() << " provided: " << _args << LL_ENDL;
}
return _args[_index++];
}
void LLSDArgsSource::done() const
{
if (_index < _args.size())
{
LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
<< " of the " << _args.size() << " arguments provided: "
<< _args << LL_ENDL;
}
}
/*****************************************************************************
* LLSDArgsMapper
*****************************************************************************/
/**
* From a formal parameters description and a map of arguments, construct an
* arguments array.
*
* That is, given:
* - an LLSD array of length n containing parameter-name strings,
* corresponding to the arguments of a function of interest
* - an LLSD collection specifying default parameter values, either:
* - an LLSD array of length m <= n, matching the rightmost m params, or
* - an LLSD map explicitly stating default name=value pairs
* - an LLSD map of parameter names and actual values for a particular
* function call
* construct an LLSD array of actual argument values for this function call.
*
* The parameter-names array and the defaults collection describe the function
* being called. The map might vary with every call, providing argument values
* for the described parameters.
*
* The array of parameter names must match the number of parameters expected
* by the function of interest.
*
* If you pass a map of default parameter values, it provides default values
* as you might expect. It is an error to specify a default value for a name
* not listed in the parameters array.
*
* If you pass an array of default parameter values, it is mapped to the
* rightmost m of the n parameter names. It is an error if the default-values
* array is longer than the parameter-names array. Consider the following
* parameter names: ["a", "b", "c", "d"].
*
* - An empty array of default values (or an isUndefined() value) asserts that
* every one of the above parameter names is required.
* - An array of four default values [1, 2, 3, 4] asserts that every one of
* the above parameters is optional. If the current parameter map is empty,
* they will be passed to the function as [1, 2, 3, 4].
* - An array of two default values [11, 12] asserts that parameters "a" and
* "b" are required, while "c" and "d" are optional, having default values
* "c"=11 and "d"=12.
*
* The arguments array is constructed as follows:
*
* - Arguments-map keys not found in the parameter-names array are ignored.
* - Entries from the map provide values for an improper subset of the
* parameters named in the parameter-names array. This results in a
* tentative values array with "holes." (size of map) + (number of holes) =
* (size of names array)
* - Holes are filled with the default values.
* - Any remaining holes constitute an error.
*/
class LL_COMMON_API LLSDArgsMapper
{
public:
/// Accept description of function: function name, param names, param
/// default values
LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
/// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
LLSD map(const LLSD& argsmap) const;
private:
static std::string formatlist(const LLSD&);
// The function-name string is purely descriptive. We want error messages
// to be able to indicate which function's LLSDArgsMapper has the problem.
std::string _function;
// Store the names array pretty much as given.
LLSD _names;
// Though we're handed an array of name strings, it's more useful to us to
// store it as a map from name string to position index. Of course that's
// easy to generate from the incoming names array, but why do it more than
// once?
typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
IndexMap _indexes;
// Generated array of default values, aligned with the array of param names.
LLSD _defaults;
// Indicate whether we have a default value for each param.
typedef std::vector<char> FilledVector;
FilledVector _has_dft;
};
LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
const LLSD& names, const LLSD& defaults):
_function(function),
_names(names),
_has_dft(names.size())
{
if (! (_names.isUndefined() || _names.isArray()))
{
LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
}
LLSD::Integer nparams(_names.size());
// From _names generate _indexes.
for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
{
_indexes[_names[ni]] = ni;
}
// Presize _defaults() array so we don't have to resize it more than once.
// All entries are initialized to LLSD(); but since _has_dft is still all
// 0, they're all "holes" for now.
if (nparams)
{
_defaults[nparams - 1] = LLSD();
}
if (defaults.isUndefined() || defaults.isArray())
{
LLSD::Integer ndefaults = defaults.size();
// defaults is a (possibly empty) array. Right-align it with names.
if (ndefaults > nparams)
{
LL_ERRS("LLSDArgsMapper") << function << " names array " << names
<< " shorter than defaults array " << defaults << LL_ENDL;
}
// Offset by which we slide defaults array right to right-align with
// _names array
LLSD::Integer offset = nparams - ndefaults;
// Fill rightmost _defaults entries from defaults, and mark them as
// filled
for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
{
_defaults[i + offset] = defaults[i];
_has_dft[i + offset] = 1;
}
}
else if (defaults.isMap())
{
// defaults is a map. Use it to populate the _defaults array.
LLSD bogus;
for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
mi != mend; ++mi)
{
IndexMap::const_iterator ixit(_indexes.find(mi->first));
if (ixit == _indexes.end())
{
bogus.append(mi->first);
continue;
}
LLSD::Integer pos = ixit->second;
// Store default value at that position in the _defaults array.
_defaults[pos] = mi->second;
// Don't forget to record the fact that we've filled this
// position.
_has_dft[pos] = 1;
}
if (bogus.size())
{
LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
<< formatlist(bogus) << LL_ENDL;
}
}
else
{
LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
<< defaults << LL_ENDL;
}
}
LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
{
if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
{
LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
<< argsmap << LL_ENDL;
}
// Initialize the args array. Indexing a non-const LLSD array grows it
// to appropriate size, but we don't want to resize this one on each
// new operation. Just make it as big as we need before we start
// stuffing values into it.
LLSD args(LLSD::emptyArray());
if (_defaults.size() == 0)
{
// If this function requires no arguments, fast exit. (Don't try to
// assign to args[-1].)
return args;
}
args[_defaults.size() - 1] = LLSD();
// Get a vector of chars to indicate holes. It's tempting to just scan
// for LLSD::isUndefined() values after filling the args array from
// the map, but it's plausible for caller to explicitly pass
// isUndefined() as the value of some parameter name. That's legal
// since isUndefined() has well-defined conversions (default value)
// for LLSD data types. So use a whole separate array for detecting
// holes. (Avoid std::vector<bool> which is known to be odd -- can we
// iterate?)
FilledVector filled(args.size());
if (argsmap.isArray())
{
// Fill args from array. If there are too many args in passed array,
// ignore the rest.
LLSD::Integer size(argsmap.size());
if (size > args.size())
{
// We don't just use std::min() because we want to sneak in this
// warning if caller passes too many args.
LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
<< " params, ignoring last " << (size - args.size())
<< " of passed " << size << ": " << argsmap << LL_ENDL;
size = args.size();
}
for (LLSD::Integer i(0); i < size; ++i)
{
// Copy the actual argument from argsmap
args[i] = argsmap[i];
// Note that it's been filled
filled[i] = 1;
}
}
else
{
// argsmap is in fact a map. Walk the map.
for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
mi != mend; ++mi)
{
// mi->first is a parameter-name string, with mi->second its
// value. Look up the name's position index in _indexes.
IndexMap::const_iterator ixit(_indexes.find(mi->first));
if (ixit == _indexes.end())
{
// Allow for a map containing more params than were passed in
// our names array. Caller typically receives a map containing
// the function name, cruft such as reqid, etc. Ignore keys
// not defined in _indexes.
LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
<< mi->first << "=" << mi->second << LL_ENDL;
continue;
}
LLSD::Integer pos = ixit->second;
// Store the value at that position in the args array.
args[pos] = mi->second;
// Don't forget to record the fact that we've filled this
// position.
filled[pos] = 1;
}
}
// Fill any remaining holes from _defaults.
LLSD unfilled(LLSD::emptyArray());
for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
{
if (! filled[i])
{
// If there's no default value for this parameter, that's an
// error.
if (! _has_dft[i])
{
unfilled.append(_names[i]);
}
else
{
args[i] = _defaults[i];
}
}
}
// If any required args -- args without defaults -- were left unfilled
// by argsmap, that's a problem.
if (unfilled.size())
{
LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
<< formatlist(unfilled) << " from " << argsmap << LL_ENDL;
}
// done
return args;
}
std::string LLSDArgsMapper::formatlist(const LLSD& list)
{
std::ostringstream out;
const char* delim = "";
for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
li != lend; ++li)
{
out << delim << li->asString();
delim = ", ";
}
return out.str();
}
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
mDesc(desc),
mKey(key)
{
}
LLEventDispatcher::~LLEventDispatcher()
{
}
/**
* DispatchEntry subclass used for callables accepting(const LLSD&)
*/
struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
{
LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
DispatchEntry(desc),
mFunc(func),
mRequired(required)
{}
Callable mFunc;
LLSD mRequired;
virtual void call(const std::string& desc, const LLSD& event) const
{
// Validate the syntax of the event itself.
std::string mismatch(llsd_matches(mRequired, event));
if (! mismatch.empty())
{
LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
}
// Event syntax looks good, go for it!
mFunc(event);
}
virtual LLSD addMetadata(LLSD meta) const
{
meta["required"] = mRequired;
return meta;
}
};
/**
* DispatchEntry subclass for passing LLSD to functions accepting
* arbitrary argument types (convertible via LLSDParam)
*/
struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
{
ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
DispatchEntry(desc),
mInvoker(func)
{}
invoker_function mInvoker;
virtual void call(const std::string& desc, const LLSD& event) const
{
LLSDArgsSource src(desc, event);
mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
}
};
/**
* DispatchEntry subclass for dispatching LLSD::Array to functions accepting
* arbitrary argument types (convertible via LLSDParam)
*/
struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
{
ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
LLSD::Integer arity):
ParamsDispatchEntry(desc, func),
mArity(arity)
{}
LLSD::Integer mArity;
virtual LLSD addMetadata(LLSD meta) const
{
LLSD array(LLSD::emptyArray());
// Resize to number of arguments required
if (mArity)
array[mArity - 1] = LLSD();
llassert_always(array.size() == mArity);
meta["required"] = array;
return meta;
}
};
/**
* DispatchEntry subclass for dispatching LLSD::Map to functions accepting
* arbitrary argument types (convertible via LLSDParam)
*/
struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
{
MapParamsDispatchEntry(const std::string& name, const std::string& desc,
const invoker_function& func,
const LLSD& params, const LLSD& defaults):
ParamsDispatchEntry(desc, func),
mMapper(name, params, defaults),
mRequired(LLSD::emptyMap())
{
// Build the set of all param keys, then delete the ones that are
// optional. What's left are the ones that are required.
for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
pi != pend; ++pi)
{
mRequired[pi->asString()] = LLSD();
}
if (defaults.isArray() || defaults.isUndefined())
{
// Right-align the params and defaults arrays.
LLSD::Integer offset = params.size() - defaults.size();
// Now the name of every defaults[i] is at params[i + offset].
for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
{
// Erase this optional param from mRequired.
mRequired.erase(params[i + offset].asString());
// Instead, make an entry in mOptional with the default
// param's name and value.
mOptional[params[i + offset].asString()] = defaults[i];
}
}
else if (defaults.isMap())
{
// if defaults is already a map, then it's already in the form we
// intend to deliver in metadata
mOptional = defaults;
// Just delete from mRequired every key appearing in mOptional.
for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
mi != mend; ++mi)
{
mRequired.erase(mi->first);
}
}
}
LLSDArgsMapper mMapper;
LLSD mRequired;
LLSD mOptional;
virtual void call(const std::string& desc, const LLSD& event) const
{
// Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
// to base-class call() method.
ParamsDispatchEntry::call(desc, mMapper.map(event));
}
virtual LLSD addMetadata(LLSD meta) const
{
meta["required"] = mRequired;
meta["optional"] = mOptional;
return meta;
}
};
void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
LLSD::Integer arity)
{
mDispatch.insert(
DispatchMap::value_type(name, DispatchMap::mapped_type(
new ArrayParamsDispatchEntry(desc, invoker, arity))));
}
void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
const LLSD& params,
const LLSD& defaults)
{
mDispatch.insert(
DispatchMap::value_type(name, DispatchMap::mapped_type(
new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
}
/// Register a callable by name
void LLEventDispatcher::add(const std::string& name, const std::string& desc,
const Callable& callable, const LLSD& required)
{
mDispatch.insert(
DispatchMap::value_type(name, DispatchMap::mapped_type(
new LLSDDispatchEntry(desc, callable, required))));
}
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
<< "): " << classname << " is not a subclass "
<< "of LLEventDispatcher" << LL_ENDL;
}
/// Unregister a callable
bool LLEventDispatcher::remove(const std::string& name)
{
DispatchMap::iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return false;
}
mDispatch.erase(found);
return true;
}
/// Call a registered callable with an explicitly-specified name. If no
/// such callable exists, die with LL_ERRS.
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
{
if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
<< "' not found" << LL_ENDL;
}
}
/// Extract the @a key value from the incoming @a event, and call the
/// callable whose name is specified by that map @a key. If no such
/// callable exists, die with LL_ERRS.
void LLEventDispatcher::operator()(const LLSD& event) const
{
// This could/should be implemented in terms of the two-arg overload.
// However -- we can produce a more informative error message.
std::string name(event[mKey]);
if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
<< " value '" << name << "'" << LL_ENDL;
}
}
bool LLEventDispatcher::try_call(const LLSD& event) const
{
return try_call(event[mKey], event);
}
bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return false;
}
// Found the name, so it's plausible to even attempt the call.
found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
event);
return true; // tell caller we were able to call
}
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return LLSD();
}
LLSD meta;
meta["name"] = name;
meta["desc"] = found->second->mDesc;
return found->second->addMetadata(meta);
}
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
LLEventDispatcher(pumpname, key),
mPump(pumpname, true), // allow tweaking for uniqueness
mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
{
}
bool LLDispatchListener::process(const LLSD& event)
{
(*this)(event);
return false;
}
LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
mDesc(desc)
{}

View File

@@ -0,0 +1,541 @@
/**
* @file lleventdispatcher.h
* @author Nat Goodspeed
* @date 2009-06-18
* @brief Central mechanism for dispatching events by string name. This is
* useful when you have a single LLEventPump listener on which you can
* request different operations, vs. instantiating a different
* LLEventPump for each such operation.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*
* The invoker machinery that constructs a boost::fusion argument list for use
* with boost::fusion::invoke() is derived from
* http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
* whose license information is copied below:
*
* "(C) Copyright Tobias Schwinger
*
* Use modification and distribution are subject to the boost Software License,
* Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
*/
#if ! defined(LL_LLEVENTDISPATCHER_H)
#define LL_LLEVENTDISPATCHER_H
// nil is too generic a term to be allowed to be a global macro. In
// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
// namespace) that a global 'nil' macro breaks badly.
#if defined(nil)
// Capture the value of the macro 'nil', hoping int is an appropriate type.
static const int nil_(nil);
// Now forget the macro.
#undef nil
// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
// defined const 'nil_'. Make it static since otherwise it produces duplicate-
// symbol link errors later.
static const int& nil(nil_);
#endif
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/function_types/is_nonmember_callable_builtin.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/function_arity.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/fusion/include/push_back.hpp>
#include <boost/fusion/include/cons.hpp>
#include <boost/fusion/include/invoke.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/next.hpp>
#include <boost/mpl/deref.hpp>
#include <typeinfo>
#include "llevents.h"
#include "llsdutil.h"
class LLSD;
/**
* Given an LLSD map, examine a string-valued key and call a corresponding
* callable. This class is designed to be contained by an LLEventPump
* listener class that will register some of its own methods, though any
* callable can be used.
*/
class LL_COMMON_API LLEventDispatcher
{
public:
LLEventDispatcher(const std::string& desc, const std::string& key);
virtual ~LLEventDispatcher();
/// @name Register functions accepting(const LLSD&)
//@{
/// Accept any C++ callable with the right signature, typically a
/// boost::bind() expression
typedef boost::function<void(const LLSD&)> Callable;
/**
* Register a @a callable by @a name. The passed @a callable accepts a
* single LLSD value and uses it in any way desired, e.g. extract
* parameters and call some other function. The optional @a required
* parameter is used to validate the structure of each incoming event (see
* llsd_matches()).
*/
void add(const std::string& name,
const std::string& desc,
const Callable& callable,
const LLSD& required=LLSD());
/**
* The case of a free function (or static method) accepting(const LLSD&)
* could also be intercepted by the arbitrary-args overload below. Ensure
* that it's directed to the Callable overload above instead.
*/
void add(const std::string& name,
const std::string& desc,
void (*f)(const LLSD&),
const LLSD& required=LLSD())
{
add(name, desc, Callable(f), required);
}
/**
* Special case: a subclass of this class can pass an unbound member
* function pointer (of an LLEventDispatcher subclass) without explicitly
* specifying the <tt>boost::bind()</tt> expression. The passed @a method
* accepts a single LLSD value, presumably containing other parameters.
*/
template <class CLASS>
void add(const std::string& name,
const std::string& desc,
void (CLASS::*method)(const LLSD&),
const LLSD& required=LLSD())
{
addMethod<CLASS>(name, desc, method, required);
}
/// Overload for both const and non-const methods. The passed @a method
/// accepts a single LLSD value, presumably containing other parameters.
template <class CLASS>
void add(const std::string& name,
const std::string& desc,
void (CLASS::*method)(const LLSD&) const,
const LLSD& required=LLSD())
{
addMethod<CLASS>(name, desc, method, required);
}
//@}
/// @name Register functions with arbitrary param lists
//@{
/**
* Register a free function with arbitrary parameters. (This also works
* for static class methods.)
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* When calling this name, pass an LLSD::Array. Each entry in turn will be
* converted to the corresponding parameter type using LLSDParam.
*/
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
>::type add(const std::string& name,
const std::string& desc,
Function f);
/**
* Register a nonstatic class method with arbitrary parameters.
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* To cover cases such as a method on an LLSingleton we don't yet want to
* instantiate, instead of directly storing an instance pointer, accept a
* nullary callable returning a pointer/reference to the desired class
* instance. If you already have an instance in hand,
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
* produce suitable callables.
*
* When calling this name, pass an LLSD::Array. Each entry in turn will be
* converted to the corresponding parameter type using LLSDParam.
*/
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
>::type add(const std::string& name,
const std::string& desc,
Method f,
const InstanceGetter& getter);
/**
* Register a free function with arbitrary parameters. (This also works
* for static class methods.)
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* Pass an LLSD::Array of parameter names, and optionally another
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
*
* When calling this name, pass an LLSD::Map. We will internally generate
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
* to the corresponding parameter type using LLSDParam.
*/
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
>::type add(const std::string& name,
const std::string& desc,
Function f,
const LLSD& params,
const LLSD& defaults=LLSD());
/**
* Register a nonstatic class method with arbitrary parameters.
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* To cover cases such as a method on an LLSingleton we don't yet want to
* instantiate, instead of directly storing an instance pointer, accept a
* nullary callable returning a pointer/reference to the desired class
* instance. If you already have an instance in hand,
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
* produce suitable callables.
*
* Pass an LLSD::Array of parameter names, and optionally another
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
*
* When calling this name, pass an LLSD::Map. We will internally generate
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
* to the corresponding parameter type using LLSDParam.
*/
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
>::type add(const std::string& name,
const std::string& desc,
Method f,
const InstanceGetter& getter,
const LLSD& params,
const LLSD& defaults=LLSD());
//@}
/// Unregister a callable
bool remove(const std::string& name);
/// Call a registered callable with an explicitly-specified name. If no
/// such callable exists, die with LL_ERRS. If the @a event fails to match
/// the @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const std::string& name, const LLSD& event) const;
/// Call a registered callable with an explicitly-specified name and
/// return <tt>true</tt>. If no such callable exists, return
/// <tt>false</tt>. If the @a event fails to match the @a required
/// prototype specified at add() time, die with LL_ERRS.
bool try_call(const std::string& name, const LLSD& event) const;
/// Extract the @a key value from the incoming @a event, and call the
/// callable whose name is specified by that map @a key. If no such
/// callable exists, die with LL_ERRS. If the @a event fails to match the
/// @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const LLSD& event) const;
/// Extract the @a key value from the incoming @a event, call the callable
/// whose name is specified by that map @a key and return <tt>true</tt>.
/// If no such callable exists, return <tt>false</tt>. If the @a event
/// fails to match the @a required prototype specified at add() time, die
/// with LL_ERRS.
bool try_call(const LLSD& event) const;
/// @name Iterate over defined names
//@{
typedef std::pair<std::string, std::string> NameDesc;
private:
struct DispatchEntry
{
DispatchEntry(const std::string& desc);
virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
std::string mDesc;
virtual void call(const std::string& desc, const LLSD& event) const = 0;
virtual LLSD addMetadata(LLSD) const = 0;
};
// Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
// wants its value type to be "clonable," even just to dereference an
// iterator. I don't want to clone entries -- if I have to copy an entry
// around, I want it to continue pointing to the same DispatchEntry
// subclass object. However, I definitely want DispatchMap to destroy
// DispatchEntry if no references are outstanding at the time an entry is
// removed. This looks like a job for boost::shared_ptr.
typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
public:
/// We want the flexibility to redefine what data we store per name,
/// therefore our public interface doesn't expose DispatchMap iterators,
/// or DispatchMap itself, or DispatchEntry. Instead we explicitly
/// transform each DispatchMap item to NameDesc on dereferencing.
typedef boost::transform_iterator<NameDesc(*)(const DispatchMap::value_type&), DispatchMap::const_iterator> const_iterator;
const_iterator begin() const
{
return boost::make_transform_iterator(mDispatch.begin(), makeNameDesc);
}
const_iterator end() const
{
return boost::make_transform_iterator(mDispatch.end(), makeNameDesc);
}
//@}
/// Get information about a specific Callable
LLSD getMetadata(const std::string& name) const;
/// Retrieve the LLSD key we use for one-arg <tt>operator()</tt> method
std::string getDispatchKey() const { return mKey; }
private:
template <class CLASS, typename METHOD>
void addMethod(const std::string& name, const std::string& desc,
const METHOD& method, const LLSD& required)
{
CLASS* downcast = dynamic_cast<CLASS*>(this);
if (! downcast)
{
addFail(name, typeid(CLASS).name());
}
else
{
add(name, desc, boost::bind(method, downcast, _1), required);
}
}
void addFail(const std::string& name, const std::string& classname) const;
std::string mDesc, mKey;
DispatchMap mDispatch;
static NameDesc makeNameDesc(const DispatchMap::value_type& item)
{
return NameDesc(item.first, item.second->mDesc);
}
struct LLSDDispatchEntry;
struct ParamsDispatchEntry;
struct ArrayParamsDispatchEntry;
struct MapParamsDispatchEntry;
// Step 2 of parameter analysis. Instantiating invoker<some_function_type>
// implicitly sets its From and To parameters to the (compile time) begin
// and end iterators over that function's parameter types.
template< typename Function
, class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
, class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
>
struct invoker;
// deliver LLSD arguments one at a time
typedef boost::function<LLSD()> args_source;
// obtain args from an args_source to build param list and call target
// function
typedef boost::function<void(const args_source&)> invoker_function;
template <typename Function>
invoker_function make_invoker(Function f);
template <typename Method, typename InstanceGetter>
invoker_function make_invoker(Method f, const InstanceGetter& getter);
void addArrayParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
LLSD::Integer arity);
void addMapParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
const LLSD& params,
const LLSD& defaults);
};
/*****************************************************************************
* LLEventDispatcher template implementation details
*****************************************************************************/
// Step 3 of parameter analysis, the recursive case.
template<typename Function, class From, class To>
struct LLEventDispatcher::invoker
{
template<typename T>
struct remove_cv_ref
: boost::remove_cv< typename boost::remove_reference<T>::type >
{ };
// apply() accepts an arbitrary boost::fusion sequence as args. It
// examines the next parameter type in the parameter-types sequence
// bounded by From and To, obtains the next LLSD object from the passed
// args_source and constructs an LLSDParam of appropriate type to try
// to convert the value. It then recurs with the next parameter-types
// iterator, passing the args sequence thus far.
template<typename Args>
static inline
void apply(Function func, const args_source& argsrc, Args const & args)
{
typedef typename boost::mpl::deref<From>::type arg_type;
typedef typename boost::mpl::next<From>::type next_iter_type;
typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
invoker<Function, next_iter_type, To>::apply
( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
}
// Special treatment for instance (first) parameter of a non-static member
// function. Accept the instance-getter callable, calling that to produce
// the first args value. Since we know we're at the top of the recursion
// chain, we need not also require a partial args sequence from our caller.
template <typename InstanceGetter>
static inline
void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
{
typedef typename boost::mpl::next<From>::type next_iter_type;
// Instead of grabbing the first item from argsrc and making an
// LLSDParam of it, call getter() and pass that as the instance param.
invoker<Function, next_iter_type, To>::apply
( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
}
};
// Step 4 of parameter analysis, the leaf case. When the general
// invoker<Function, From, To> logic has advanced From until it matches To,
// the compiler will pick this template specialization.
template<typename Function, class To>
struct LLEventDispatcher::invoker<Function,To,To>
{
// the argument list is complete, now call the function
template<typename Args>
static inline
void apply(Function func, const args_source&, Args const & args)
{
boost::fusion::invoke(func, args);
}
};
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
{
// Construct an invoker_function, a callable accepting const args_source&.
// Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
// caller's LLSD::Array.
addArrayParamsDispatchEntry(name, desc, make_invoker(f),
boost::function_types::function_arity<Function>::value);
}
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
const InstanceGetter& getter)
{
// Subtract 1 from the compile-time arity because the getter takes care of
// the first parameter. We only need (arity - 1) additional arguments.
addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
boost::function_types::function_arity<Method>::value - 1);
}
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
const LLSD& params, const LLSD& defaults)
{
// See comments for previous is_nonmember_callable_builtin add().
addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
}
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
const InstanceGetter& getter,
const LLSD& params, const LLSD& defaults)
{
addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
}
template <typename Function>
LLEventDispatcher::invoker_function
LLEventDispatcher::make_invoker(Function f)
{
// Step 1 of parameter analysis, the top of the recursion. Passing a
// suitable f (see add()'s enable_if condition) to this method causes it
// to infer the function type; specifying that function type to invoker<>
// causes it to fill in the begin/end MPL iterators over the function's
// list of parameter types.
// While normally invoker::apply() could infer its template type from the
// boost::fusion::nil parameter value, here we must be explicit since
// we're boost::bind()ing it rather than calling it directly.
return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
f,
_1,
boost::fusion::nil());
}
template <typename Method, typename InstanceGetter>
LLEventDispatcher::invoker_function
LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
{
// Use invoker::method_apply() to treat the instance (first) arg specially.
return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
f,
_1,
getter);
}
/*****************************************************************************
* LLDispatchListener
*****************************************************************************/
/**
* Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
* that contains (or derives from) LLDispatchListener need only specify the
* LLEventPump name and dispatch key, and add() its methods. Incoming events
* will automatically be dispatched.
*/
class LL_COMMON_API LLDispatchListener: public LLEventDispatcher
{
public:
LLDispatchListener(const std::string& pumpname, const std::string& key);
std::string getPumpName() const { return mPump.getName(); }
private:
bool process(const LLSD& event);
LLEventStream mPump;
LLTempBoundListener mBoundListener;
};
#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */

View File

@@ -0,0 +1,166 @@
/**
* @file lleventfilter.cpp
* @author Nat Goodspeed
* @date 2009-03-05
* @brief Implementation for lleventfilter.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventfilter.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "llerror.h" // LL_ERRS
#include "llsdutil.h" // llsd_matches()
LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
LLEventStream(name, tweak)
{
source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1));
}
LLEventMatching::LLEventMatching(const LLSD& pattern):
LLEventFilter("matching"),
mPattern(pattern)
{
}
LLEventMatching::LLEventMatching(LLEventPump& source, const LLSD& pattern):
LLEventFilter(source, "matching"),
mPattern(pattern)
{
}
bool LLEventMatching::post(const LLSD& event)
{
if (! llsd_matches(mPattern, event).empty())
return false;
return LLEventStream::post(event);
}
LLEventTimeoutBase::LLEventTimeoutBase():
LLEventFilter("timeout")
{
}
LLEventTimeoutBase::LLEventTimeoutBase(LLEventPump& source):
LLEventFilter(source, "timeout")
{
}
void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action)
{
setCountdown(seconds);
mAction = action;
if (! mMainloop.connected())
{
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1));
}
}
class ErrorAfter
{
public:
ErrorAfter(const std::string& message): mMessage(message) {}
void operator()()
{
LL_ERRS("LLEventTimeout") << mMessage << LL_ENDL;
}
private:
std::string mMessage;
};
void LLEventTimeoutBase::errorAfter(F32 seconds, const std::string& message)
{
actionAfter(seconds, ErrorAfter(message));
}
class EventAfter
{
public:
EventAfter(LLEventPump& pump, const LLSD& event):
mPump(pump),
mEvent(event)
{}
void operator()()
{
mPump.post(mEvent);
}
private:
LLEventPump& mPump;
LLSD mEvent;
};
void LLEventTimeoutBase::eventAfter(F32 seconds, const LLSD& event)
{
actionAfter(seconds, EventAfter(*this, event));
}
bool LLEventTimeoutBase::post(const LLSD& event)
{
cancel();
return LLEventStream::post(event);
}
void LLEventTimeoutBase::cancel()
{
mMainloop.disconnect();
}
bool LLEventTimeoutBase::tick(const LLSD&)
{
if (countdownElapsed())
{
cancel();
mAction();
}
return false; // show event to other listeners
}
LLEventTimeout::LLEventTimeout() {}
LLEventTimeout::LLEventTimeout(LLEventPump& source):
LLEventTimeoutBase(source)
{
}
void LLEventTimeout::setCountdown(F32 seconds)
{
mTimer.setTimerExpirySec(seconds);
}
bool LLEventTimeout::countdownElapsed() const
{
return mTimer.hasExpired();
}

View File

@@ -0,0 +1,203 @@
/**
* @file lleventfilter.h
* @author Nat Goodspeed
* @date 2009-03-05
* @brief Define LLEventFilter: LLEventStream subclass with conditions
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLEVENTFILTER_H)
#define LL_LLEVENTFILTER_H
#include "llevents.h"
#include "stdtypes.h"
#include "lltimer.h"
#include <boost/function.hpp>
/**
* Generic base class
*/
class LL_COMMON_API LLEventFilter: public LLEventStream
{
public:
/// construct a standalone LLEventFilter
LLEventFilter(const std::string& name="filter", bool tweak=true):
LLEventStream(name, tweak)
{}
/// construct LLEventFilter and connect it to the specified LLEventPump
LLEventFilter(LLEventPump& source, const std::string& name="filter", bool tweak=true);
/// Post an event to all listeners
virtual bool post(const LLSD& event) = 0;
};
/**
* Pass through only events matching a specified pattern
*/
class LLEventMatching: public LLEventFilter
{
public:
/// Pass an LLSD map with keys and values the incoming event must match
LLEventMatching(const LLSD& pattern);
/// instantiate and connect
LLEventMatching(LLEventPump& source, const LLSD& pattern);
/// Only pass through events matching the pattern
virtual bool post(const LLSD& event);
private:
LLSD mPattern;
};
/**
* Wait for an event to be posted. If no such event arrives within a specified
* time, take a specified action. See LLEventTimeout for production
* implementation.
*
* @NOTE This is an abstract base class so that, for testing, we can use an
* alternate "timer" that doesn't actually consume real time.
*/
class LL_COMMON_API LLEventTimeoutBase: public LLEventFilter
{
public:
/// construct standalone
LLEventTimeoutBase();
/// construct and connect
LLEventTimeoutBase(LLEventPump& source);
/// Callable, can be constructed with boost::bind()
typedef boost::function<void()> Action;
/**
* Start countdown timer for the specified number of @a seconds. Forward
* all events. If any event arrives before timer expires, cancel timer. If
* no event arrives before timer expires, take specified @a action.
*
* This is a one-shot timer. Once it has either expired or been canceled,
* it is inert until another call to actionAfter().
*
* Calling actionAfter() while an existing timer is running cheaply
* replaces that original timer. Thus, a valid use case is to detect
* idleness of some event source by calling actionAfter() on each new
* event. A rapid sequence of events will keep the timer from expiring;
* the first gap in events longer than the specified timer will fire the
* specified Action.
*
* Any post() call cancels the timer. To be satisfied with only a
* particular event, chain on an LLEventMatching that only passes such
* events:
*
* @code
* event ultimate
* source ---> LLEventMatching ---> LLEventTimeout ---> listener
* @endcode
*
* @NOTE
* The implementation relies on frequent events on the LLEventPump named
* "mainloop".
*/
void actionAfter(F32 seconds, const Action& action);
/**
* Like actionAfter(), but where the desired Action is LL_ERRS
* termination. Pass the timeout time and the desired LL_ERRS @a message.
*
* This method is useful when, for instance, some async API guarantees an
* event, whether success or failure, within a stated time window.
* Instantiate an LLEventTimeout listening to that API and call
* errorAfter() on each async request with a timeout comfortably longer
* than the API's time guarantee (much longer than the anticipated
* "mainloop" granularity).
*
* Then if the async API breaks its promise, the program terminates with
* the specified LL_ERRS @a message. The client of the async API can
* therefore assume the guarantee is upheld.
*
* @NOTE
* errorAfter() is implemented in terms of actionAfter(), so all remarks
* about calling actionAfter() also apply to errorAfter().
*/
void errorAfter(F32 seconds, const std::string& message);
/**
* Like actionAfter(), but where the desired Action is a particular event
* for all listeners. Pass the timeout time and the desired @a event data.
*
* Suppose the timeout should only be satisfied by a particular event, but
* the ultimate listener must see all other incoming events as well, plus
* the timeout @a event if any:
*
* @code
* some LLEventMatching LLEventMatching
* event ---> for particular ---> LLEventTimeout ---> for timeout
* source event event \
* \ \ ultimate
* `-----------------------------------------------------> listener
* @endcode
*
* Since a given listener can listen on more than one LLEventPump, we can
* set things up so it sees the set union of events from LLEventTimeout
* and the original event source. However, as LLEventTimeout passes
* through all incoming events, the "particular event" that satisfies the
* left LLEventMatching would reach the ultimate listener twice. So we add
* an LLEventMatching that only passes timeout events.
*
* @NOTE
* eventAfter() is implemented in terms of actionAfter(), so all remarks
* about calling actionAfter() also apply to eventAfter().
*/
void eventAfter(F32 seconds, const LLSD& event);
/// Pass event through, canceling the countdown timer
virtual bool post(const LLSD& event);
/// Cancel timer without event
void cancel();
protected:
virtual void setCountdown(F32 seconds) = 0;
virtual bool countdownElapsed() const = 0;
private:
bool tick(const LLSD&);
LLBoundListener mMainloop;
Action mAction;
};
/// Production implementation of LLEventTimoutBase
class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase
{
public:
LLEventTimeout();
LLEventTimeout(LLEventPump& source);
protected:
virtual void setCountdown(F32 seconds);
virtual bool countdownElapsed() const;
private:
LLTimer mTimer;
};
#endif /* ! defined(LL_LLEVENTFILTER_H) */

628
indra/llcommon/llevents.cpp Normal file
View File

@@ -0,0 +1,628 @@
/**
* @file llevents.cpp
* @author Nat Goodspeed
* @date 2008-09-12
* @brief Implementation for llevents.
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
#if LL_WINDOWS
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
#endif
// associated header
#include "llevents.h"
// STL headers
#include <set>
#include <sstream>
#include <algorithm>
// std headers
#include <typeinfo>
#include <cassert>
#include <cmath>
#include <cctype>
// external library headers
#include <boost/range/iterator_range.hpp>
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
#endif
#include <boost/lexical_cast.hpp>
#if LL_WINDOWS
#pragma warning (pop)
#endif
// other Linden headers
#include "stringize.h"
#include "llerror.h"
#include "llsdutil.h"
#if LL_MSVC
#pragma warning (disable : 4702)
#endif
/*****************************************************************************
* queue_names: specify LLEventPump names that should be instantiated as
* LLEventQueue
*****************************************************************************/
/**
* At present, we recognize particular requested LLEventPump names as needing
* LLEventQueues. Later on we'll migrate this information to an external
* configuration file.
*/
const char* queue_names[] =
{
"placeholder - replace with first real name string"
};
/*****************************************************************************
* If there's a "mainloop" pump, listen on that to flush all LLEventQueues
*****************************************************************************/
struct RegisterFlush : public LLEventTrackable
{
RegisterFlush():
pumps(LLEventPumps::instance())
{
pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
}
bool flush(const LLSD&)
{
pumps.flush();
return false;
}
~RegisterFlush()
{
// LLEventTrackable handles stopListening for us.
}
LLEventPumps& pumps;
};
static RegisterFlush registerFlush;
/*****************************************************************************
* LLEventPumps
*****************************************************************************/
LLEventPumps::LLEventPumps():
// Until we migrate this information to an external config file,
// initialize mQueueNames from the static queue_names array.
mQueueNames(boost::begin(queue_names), boost::end(queue_names))
{
}
LLEventPump& LLEventPumps::obtain(const std::string& name)
{
PumpMap::iterator found = mPumpMap.find(name);
if (found != mPumpMap.end())
{
// Here we already have an LLEventPump instance with the requested
// name.
return *found->second;
}
// Here we must instantiate an LLEventPump subclass.
LLEventPump* newInstance;
// Should this name be an LLEventQueue?
PumpNames::const_iterator nfound = mQueueNames.find(name);
if (nfound != mQueueNames.end())
newInstance = new LLEventQueue(name);
else
newInstance = new LLEventStream(name);
// LLEventPump's constructor implicitly registers each new instance in
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
// delete it later.
mOurPumps.insert(newInstance);
return *newInstance;
}
void LLEventPumps::flush()
{
// Flush every known LLEventPump instance. Leave it up to each instance to
// decide what to do with the flush() call.
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
{
pmi->second->flush();
}
}
void LLEventPumps::reset()
{
// Reset every known LLEventPump instance. Leave it up to each instance to
// decide what to do with the reset() call.
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
{
pmi->second->reset();
}
}
std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
{
std::pair<PumpMap::iterator, bool> inserted =
mPumpMap.insert(PumpMap::value_type(name, const_cast<LLEventPump*>(&pump)));
// If the insert worked, then the name is unique; return that.
if (inserted.second)
return name;
// Here the new entry was NOT inserted, and therefore name isn't unique.
// Unless we're permitted to tweak it, that's Bad.
if (! tweak)
{
throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
}
// The passed name isn't unique, but we're permitted to tweak it. Find the
// first decimal-integer suffix not already taken. The insert() attempt
// above will have set inserted.first to the iterator of the existing
// entry by that name. Starting there, walk forward until we reach an
// entry that doesn't start with 'name'. For each entry consisting of name
// + integer suffix, capture the integer suffix in a set. Use a set
// because we're going to encounter string suffixes in the order: name1,
// name10, name11, name2, ... Walking those possibilities in that order
// isn't convenient to detect the first available "hole."
std::set<int> suffixes;
PumpMap::iterator pmi(inserted.first), pmend(mPumpMap.end());
// We already know inserted.first references the existing entry with
// 'name' as the key; skip that one and start with the next.
while (++pmi != pmend)
{
if (pmi->first.substr(0, name.length()) != name)
{
// Found the first entry beyond the entries starting with 'name':
// stop looping.
break;
}
// Here we're looking at an entry that starts with 'name'. Is the rest
// of it an integer?
// Dubious (?) assumption: in the local character set, decimal digits
// are in increasing order such that '9' is the last of them. This
// test deals with 'name' values such as 'a', where there might be a
// very large number of entries starting with 'a' whose suffixes
// aren't integers. A secondary assumption is that digit characters
// precede most common name characters (true in ASCII, false in
// EBCDIC). The test below is correct either way, but it's worth more
// if the assumption holds.
if (pmi->first[name.length()] > '9')
break;
// It should be cheaper to detect that we're not looking at a digit
// character -- and therefore the suffix can't possibly be an integer
// -- than to attempt the lexical_cast and catch the exception.
if (! std::isdigit(pmi->first[name.length()]))
continue;
// Okay, the first character of the suffix is a digit, it's worth at
// least attempting to convert to int.
try
{
suffixes.insert(boost::lexical_cast<int>(pmi->first.substr(name.length())));
}
catch (const boost::bad_lexical_cast&)
{
// If the rest of pmi->first isn't an int, just ignore it.
}
}
// Here we've accumulated in 'suffixes' all existing int suffixes of the
// entries starting with 'name'. Find the first unused one.
int suffix = 1;
for ( ; suffixes.find(suffix) != suffixes.end(); ++suffix)
;
// Here 'suffix' is not in 'suffixes'. Construct a new name based on that
// suffix, insert it and return it.
std::ostringstream out;
out << name << suffix;
return registerNew(pump, out.str(), tweak);
}
void LLEventPumps::unregister(const LLEventPump& pump)
{
// Remove this instance from mPumpMap
PumpMap::iterator found = mPumpMap.find(pump.getName());
if (found != mPumpMap.end())
{
mPumpMap.erase(found);
}
// If this instance is one we created, also remove it from mOurPumps so we
// won't try again to delete it later!
PumpSet::iterator psfound = mOurPumps.find(const_cast<LLEventPump*>(&pump));
if (psfound != mOurPumps.end())
{
mOurPumps.erase(psfound);
}
}
//static
bool LLEventPumps::sDeleted;
//static
void LLEventPumps::maybe_unregister(const LLEventPump& pump)
{
if (!sDeleted)
{
LLEventPumps::instance().unregister(pump);
}
}
LLEventPumps::~LLEventPumps()
{
// Deleting an LLEventPump calls its destructor, which calls maybe_unregister(),
// which would try to remove that LLEventPump instance from a NEWLY created LLEventPumps
// singleton (as we're already being destructed). Therefore, mark that we're not
// home anymore... --Aleric
sDeleted = true;
// Subsequently we can delete every LLEventPump we instantiated (via obtain()).
// We're not clearing mPumpMap or mOurPumps here... their destructors will.
for (LLEventPumps::PumpSet::iterator pump = mOurPumps.begin(); pump != mOurPumps.end(); ++pump)
{
delete *pump;
}
}
/*****************************************************************************
* LLEventPump
*****************************************************************************/
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
LLEventPump::LLEventPump(const std::string& name, bool tweak):
// Register every new instance with LLEventPumps
mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
mSignal(new LLStandardSignal()),
mEnabled(true)
{}
#if LL_WINDOWS
#pragma warning (pop)
#endif
LLEventPump::~LLEventPump()
{
// Unregister this doomed instance from LLEventPumps
LLEventPumps::maybe_unregister(*this);
}
// static data member
const LLEventPump::NameList LLEventPump::empty;
std::string LLEventPump::inventName(const std::string& pfx)
{
static long suffix = 0;
return STRINGIZE(pfx << suffix++);
}
void LLEventPump::reset()
{
mSignal.reset();
mConnections.clear();
//mDeps.clear();
}
LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
const NameList& after,
const NameList& before)
{
// Check for duplicate name before connecting listener to mSignal
ConnectionMap::const_iterator found = mConnections.find(name);
// In some cases the user might disconnect a connection explicitly -- or
// might use LLEventTrackable to disconnect implicitly. Either way, we can
// end up retaining in mConnections a zombie connection object that's
// already been disconnected. Such a connection object can't be
// reconnected -- nor, in the case of LLEventTrackable, would we want to
// try, since disconnection happens with the destruction of the listener
// object. That means it's safe to overwrite a disconnected connection
// object with the new one we're attempting. The case we want to prevent
// is only when the existing connection object is still connected.
if (found != mConnections.end() && found->second.connected())
{
throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
"' on " + typeid(*this).name() + " '" + getName() + "'");
}
// Okay, name is unique, try to reconcile its dependencies. Specify a new
// "node" value that we never use for an mSignal placement; we'll fix it
// later.
DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
// What if this listener has been added, removed and re-added? In that
// case newNode already has a non-negative value because we never remove a
// listener from mDeps. But keep processing uniformly anyway in case the
// listener was added back with different dependencies. Then mDeps.sort()
// would put it in a different position, and the old newNode placement
// value would be wrong, so we'd have to reassign it anyway. Trust that
// re-adding a listener with the same dependencies is the trivial case for
// mDeps.sort(): it can just replay its cache.
DependencyMap::sorted_range sorted_range;
try
{
// Can we pick an order that works including this new entry?
sorted_range = mDeps.sort();
}
catch (const DependencyMap::Cycle& e)
{
// No: the new node's after/before dependencies have made mDeps
// unsortable. If we leave the new node in mDeps, it will continue
// to screw up all future attempts to sort()! Pull it out.
mDeps.remove(name);
throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
" '" + getName() + "' would cause cycle: " + e.what());
}
// Walk the list to verify that we haven't changed the order.
float previous = 0.0, myprev = 0.0;
DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
dmi != sorted_range.end(); ++dmi)
{
// Since we've added the new entry with an invalid placement,
// recognize it and skip it.
if (dmi->first == name)
{
// Remember the iterator belonging to our new node, and which
// placement value was 'previous' at that point.
mydmi = dmi;
myprev = previous;
continue;
}
// If the new node has rearranged the existing nodes, we'll find
// that their placement values are no longer in increasing order.
if (dmi->second < previous)
{
// This is another scenario in which we'd better back out the
// newly-added node from mDeps -- but don't do it yet, we want to
// traverse the existing mDeps to report on it!
// Describe the change to the order of our listeners. Copy
// everything but the newest listener to a vector we can sort to
// obtain the old order.
typedef std::vector< std::pair<float, std::string> > SortNameList;
SortNameList sortnames;
for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
cdmi != cdmend; ++cdmi)
{
if (cdmi->first != name)
{
sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
}
}
std::sort(sortnames.begin(), sortnames.end());
std::ostringstream out;
out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
<< "' would move previous listener '" << dmi->first << "'\nwas: ";
SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
if (sni != snend)
{
out << sni->second;
while (++sni != snend)
{
out << ", " << sni->second;
}
}
out << "\nnow: ";
DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
if (ddmi != ddmend)
{
out << ddmi->first;
while (++ddmi != ddmend)
{
out << ", " << ddmi->first;
}
}
// NOW remove the offending listener node.
mDeps.remove(name);
// Having constructed a description of the order change, inform caller.
throw OrderChange(out.str());
}
// This node becomes the previous one.
previous = dmi->second;
}
// We just got done with a successful mDeps.add(name, ...) call. We'd
// better have found 'name' somewhere in that sorted list!
assert(mydmi != sorted_range.end());
// Four cases:
// 0. name is the only entry: placement 1.0
// 1. name is the first of several entries: placement (next placement)/2
// 2. name is between two other entries: placement (myprev + (next placement))/2
// 3. name is the last entry: placement ceil(myprev) + 1.0
// Since we've cleverly arranged for myprev to be 0.0 if name is the
// first entry, this folds down to two cases. Case 1 is subsumed by
// case 2, and case 0 is subsumed by case 3. So we need only handle
// cases 2 and 3, which means we need only detect whether name is the
// last entry. Increment mydmi to see if there's anything beyond.
if (++mydmi != sorted_range.end())
{
// The new node isn't last. Place it between the previous node and
// the successor.
newNode = (myprev + mydmi->second)/2.0;
}
else
{
// The new node is last. Bump myprev up to the next integer, add
// 1.0 and use that.
newNode = std::ceil(myprev) + 1.0;
}
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
LLBoundListener bound = mSignal->connect(newNode, listener);
mConnections[name] = bound;
return bound;
}
LLBoundListener LLEventPump::getListener(const std::string& name) const
{
ConnectionMap::const_iterator found = mConnections.find(name);
if (found != mConnections.end())
{
return found->second;
}
// not found, return dummy LLBoundListener
return LLBoundListener();
}
void LLEventPump::stopListening(const std::string& name)
{
ConnectionMap::iterator found = mConnections.find(name);
if (found != mConnections.end())
{
found->second.disconnect();
mConnections.erase(found);
}
// We intentionally do NOT remove this name from mDeps. It may happen that
// the same listener with the same name and dependencies will jump on and
// off this LLEventPump repeatedly. Keeping a cache of dependencies will
// avoid a new dependency sort in such cases.
}
/*****************************************************************************
* LLEventStream
*****************************************************************************/
bool LLEventStream::post(const LLSD& event)
{
if (! mEnabled || !mSignal)
{
return false;
}
// NOTE NOTE NOTE: Any new access to member data beyond this point should
// cause us to move our LLStandardSignal object to a pimpl class along
// with said member data. Then the local shared_ptr will preserve both.
// DEV-43463: capture a local copy of mSignal. We've turned up a
// cross-coroutine scenario (described in the Jira) in which this post()
// call could end up destroying 'this', the LLEventPump subclass instance
// containing mSignal, during the call through *mSignal. So -- capture a
// *stack* instance of the shared_ptr, ensuring that our heap
// LLStandardSignal object will live at least until post() returns, even
// if 'this' gets destroyed during the call.
boost::shared_ptr<LLStandardSignal> signal(mSignal);
// Let caller know if any one listener handled the event. This is mostly
// useful when using LLEventStream as a listener for an upstream
// LLEventPump.
return (*signal)(event);
}
/*****************************************************************************
* LLEventQueue
*****************************************************************************/
bool LLEventQueue::post(const LLSD& event)
{
if (mEnabled)
{
// Defer sending this event by queueing it until flush()
mEventQueue.push_back(event);
}
// Unconditionally return false. We won't know until flush() whether a
// listener claims to have handled the event -- meanwhile, don't block
// other listeners.
return false;
}
void LLEventQueue::flush()
{
if(!mSignal) return;
// Consider the case when a given listener on this LLEventQueue posts yet
// another event on the same queue. If we loop over mEventQueue directly,
// we'll end up processing all those events during the same flush() call
// -- rather like an EventStream. Instead, copy mEventQueue and clear it,
// so that any new events posted to this LLEventQueue during flush() will
// be processed in the *next* flush() call.
EventQueue queue(mEventQueue);
mEventQueue.clear();
// NOTE NOTE NOTE: Any new access to member data beyond this point should
// cause us to move our LLStandardSignal object to a pimpl class along
// with said member data. Then the local shared_ptr will preserve both.
// DEV-43463: capture a local copy of mSignal. See LLEventStream::post()
// for detailed comments.
boost::shared_ptr<LLStandardSignal> signal(mSignal);
for ( ; ! queue.empty(); queue.pop_front())
{
(*signal)(queue.front());
}
}
/*****************************************************************************
* LLListenerOrPumpName
*****************************************************************************/
LLListenerOrPumpName::LLListenerOrPumpName(const std::string& pumpname):
// Look up the specified pumpname, and bind its post() method as our listener
mListener(boost::bind(&LLEventPump::post,
boost::ref(LLEventPumps::instance().obtain(pumpname)),
_1))
{
}
LLListenerOrPumpName::LLListenerOrPumpName(const char* pumpname):
// Look up the specified pumpname, and bind its post() method as our listener
mListener(boost::bind(&LLEventPump::post,
boost::ref(LLEventPumps::instance().obtain(pumpname)),
_1))
{
}
bool LLListenerOrPumpName::operator()(const LLSD& event) const
{
if (! mListener)
{
throw Empty("attempting to call uninitialized");
}
return (*mListener)(event);
}
void LLReqID::stamp(LLSD& response) const
{
if (! (response.isUndefined() || response.isMap()))
{
// If 'response' was previously completely empty, it's okay to
// turn it into a map. If it was already a map, then it should be
// okay to add a key. But if it was anything else (e.g. a scalar),
// assigning a ["reqid"] key will DISCARD the previous value,
// replacing it with a map. That would be Bad.
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
<< response << LL_ENDL;
return;
}
LLSD oldReqid(response["reqid"]);
if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
{
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value "
<< oldReqid << " in response: " << response << LL_ENDL;
return;
}
response["reqid"] = mReqid;
}
bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
{
// If the original request has no value for replyKey, it's pointless to
// construct or send a reply event: on which LLEventPump should we send
// it? Allow that to be optional: if the caller wants to require replyKey,
// it can so specify when registering the operation method.
if (! request.has(replyKey))
{
return false;
}
// Here the request definitely contains replyKey; reasonable to proceed.
// Copy 'reply' to modify it.
LLSD newreply(reply);
// Get the ["reqid"] element from request
LLReqID reqID(request);
// and copy it to 'newreply'.
reqID.stamp(newreply);
// Send reply on LLEventPump named in request[replyKey]. Don't forget to
// send the modified 'newreply' instead of the original 'reply'.
return LLEventPumps::instance().obtain(request[replyKey]).post(newreply);
}

1051
indra/llcommon/llevents.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,135 +0,0 @@
/**
* @file llfasttimer.cpp
* @brief Implementation of the fast timer.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llfasttimer.h"
#include "llmemory.h"
#include "llprocessor.h"
#if LL_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "lltimer.h"
//////////////////////////////////////////////////////////////////////////////
// statics
LLFastTimer::EFastTimerType LLFastTimer::sCurType = LLFastTimer::FTM_OTHER;
int LLFastTimer::sCurDepth = 0;
U64 LLFastTimer::sStart[LLFastTimer::FTM_MAX_DEPTH];
U64 LLFastTimer::sCounter[LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCountHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCountAverage[LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCalls[LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCallHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCallAverage[LLFastTimer::FTM_NUM_TYPES];
S32 LLFastTimer::sCurFrameIndex = -1;
S32 LLFastTimer::sLastFrameIndex = -1;
int LLFastTimer::sPauseHistory = 0;
int LLFastTimer::sResetHistory = 0;
U64 LLFastTimer::sClockResolution = calc_clock_frequency(50U); // Resolution of get_clock_count()
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
{
return sClockResolution >> 8;
}
void LLFastTimer::reset()
{
countsPerSecond(); // good place to calculate clock frequency
if (sCurDepth != 0)
{
llerrs << "LLFastTimer::Reset() when sCurDepth != 0" << llendl;
}
if (sPauseHistory)
{
sResetHistory = 1;
}
else if (sResetHistory)
{
sCurFrameIndex = -1;
sResetHistory = 0;
}
else if (sCurFrameIndex >= 0)
{
int hidx = sCurFrameIndex % FTM_HISTORY_NUM;
for (S32 i=0; i<FTM_NUM_TYPES; i++)
{
sCountHistory[hidx][i] = sCounter[i];
sCountAverage[i] = (sCountAverage[i]*sCurFrameIndex + sCounter[i]) / (sCurFrameIndex+1);
sCallHistory[hidx][i] = sCalls[i];
sCallAverage[i] = (sCallAverage[i]*sCurFrameIndex + sCalls[i]) / (sCurFrameIndex+1);
}
sLastFrameIndex = sCurFrameIndex;
}
else
{
for (S32 i=0; i<FTM_NUM_TYPES; i++)
{
sCountAverage[i] = 0;
sCallAverage[i] = 0;
}
}
sCurFrameIndex++;
for (S32 i=0; i<FTM_NUM_TYPES; i++)
{
sCounter[i] = 0;
sCalls[i] = 0;
}
sCurDepth = 0;
}
//////////////////////////////////////////////////////////////////////////////
//
// Important note: These implementations must be FAST!
//
// shift off lower 8 bits for lower resolution but longer term timing
// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
// On windows these use QueryPerformanceCounter, which is arguably fine and also works on amd architectures.
U32 LLFastTimer::getCPUClockCount32()
{
return get_clock_count() >> 8;
}
U64 LLFastTimer::getCPUClockCount64()
{
return get_clock_count();
}

View File

@@ -1,294 +1,35 @@
/** /**
* @file llfasttimer.h * @file llfasttimer.h
* @brief Declaration of a fast timer. * @brief Inline implementations of fast timers.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
* *
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code * Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab * Copyright (C) 2010, Linden Research, Inc.
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* *
* There are special exceptions to the terms and conditions of the GPL as * This library is free software; you can redistribute it and/or
* it is applied to this Source Code. View the full text of the exception * modify it under the terms of the GNU Lesser General Public
* in the file doc/FLOSS-exception.txt in this software distribution, or * License as published by the Free Software Foundation;
* online at * version 2.1 of the License only.
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* *
* By copying, modifying or distributing this software, you acknowledge * This library is distributed in the hope that it will be useful,
* that you have read and understood your obligations described above, * but WITHOUT ANY WARRANTY; without even the implied warranty of
* and agree to abide by those obligations. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* *
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * You should have received a copy of the GNU Lesser General Public
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * License along with this library; if not, write to the Free Software
* COMPLETENESS OR PERFORMANCE. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$ * $/LicenseInfo$
*/ */
#ifndef LL_LLFASTTIMER_H #ifndef LL_FASTTIMER_H
#define LL_LLFASTTIMER_H #define LL_FASTTIMER_H
#define FAST_TIMER_ON 1 // Implementation of getCPUClockCount32() and getCPUClockCount64 are now in llfastertimer_class.cpp.
class LL_COMMON_API LLFastTimer
{
public:
enum EFastTimerType
{
// high level
FTM_FRAME,
FTM_UPDATE,
FTM_RENDER,
FTM_SWAP,
FTM_CLIENT_COPY,
FTM_IDLE,
FTM_SLEEP,
// general timers
FT_STRING_FORMAT,
// common messaging components
FTM_PUMP,
FTM_CURL,
FTM_PUMPIO,
// common simulation components
FTM_UPDATE_ANIMATION,
FTM_UPDATE_TERRAIN,
FTM_UPDATE_PRIMITIVES,
FTM_UPDATE_PARTICLES,
FTM_SIMULATE_PARTICLES,
FTM_UPDATE_SKY,
FTM_UPDATE_TEXTURES,
FTM_UPDATE_WLPARAM,
FTM_UPDATE_WATER,
FTM_UPDATE_CLOUDS,
FTM_UPDATE_GRASS,
FTM_UPDATE_TREE,
FTM_UPDATE_AVATAR,
#if MESH_ENABLED
FTM_UPDATE_RIGGED_VOLUME,
FTM_SKIN_RIGGED,
FTM_RIGGED_OCTREE,
#endif //MESH_ENABLED
// common render components
FTM_SHADOW_GEOMETRY,
FTM_SHADOW_RENDER,
FTM_SHADOW_TERRAIN,
FTM_SHADOW_AVATAR,
FTM_SHADOW_SIMPLE,
FTM_SHADOW_ALPHA,
FTM_SHADOW_TREE,
FTM_RENDER_GEOMETRY,
FTM_RENDER_TERRAIN,
FTM_RENDER_SIMPLE,
FTM_RENDER_FULLBRIGHT,
FTM_RENDER_GLOW,
FTM_RENDER_GRASS,
FTM_RENDER_INVISIBLE,
FTM_RENDER_SHINY,
FTM_RENDER_BUMP,
FTM_RENDER_TREES,
FTM_RENDER_CHARACTERS,
FTM_RENDER_OCCLUSION,
FTM_RENDER_ALPHA,
FTM_RENDER_CLOUDS,
FTM_RENDER_HUD,
FTM_RENDER_PARTICLES,
FTM_RENDER_WATER,
FTM_RENDER_WL_SKY,
FTM_RENDER_FAKE_VBO_UPDATE,
FTM_RENDER_TIMER,
FTM_RENDER_UI,
FTM_RENDER_BLOOM,
FTM_RENDER_BLOOM_FBO,
FTM_RENDER_FONTS,
// deferred rendering
FTM_RENDER_DEFERRED,
FTM_BIND_DEFERRED,
FTM_SUN_SHADOW,
FTM_SOFTEN_SHADOW,
FTM_EDGE_DETECTION,
FTM_GI_TRACE,
FTM_GI_GATHER,
FTM_ATMOSPHERICS,
FTM_LOCAL_LIGHTS,
FTM_FULLSCREEN_LIGHTS,
FTM_PROJECTORS,
FTM_POST,
FTM_VISIBLE_CLOUD,
// newview specific
FTM_MESSAGES,
FTM_MOUSEHANDLER,
FTM_KEYHANDLER,
FTM_REBUILD,
FTM_STATESORT,
FTM_STATESORT_DRAWABLE,
FTM_STATESORT_POSTSORT,
#if MESH_ENABLED
FTM_MESH_UPDATE,
FTM_MESH_LOCK1,
FTM_MESH_LOCK2,
FTM_LOAD_MESH_LOD,
#endif //MESH_ENABLED
FTM_REBUILD_VBO,
FTM_REBUILD_VOLUME_VB,
FTM_REBUILD_BRIDGE_VB,
FTM_REBUILD_HUD_VB,
FTM_REBUILD_TERRAIN_VB,
FTM_REBUILD_WATER_VB,
FTM_REBUILD_TREE_VB,
FTM_REBUILD_PARTICLE_VB,
FTM_REBUILD_CLOUD_VB,
FTM_REBUILD_GRASS_VB,
FTM_REBUILD_NONE_VB,
FTM_REBUILD_OCCLUSION_VB,
FTM_POOLS,
FTM_POOLRENDER,
FTM_IDLE_CB,
FTM_WORLD_UPDATE,
FTM_UPDATE_MOVE,
FTM_OCTREE_BALANCE,
FTM_UPDATE_LIGHTS,
FTM_CULL,
FTM_CULL_REBOUND,
FTM_FRUSTUM_CULL,
FTM_GEO_UPDATE,
FTM_GEO_RESERVE,
FTM_GEO_LIGHT,
FTM_GEO_SHADOW,
FTM_GEO_SKY,
FTM_GEN_VOLUME,
FTM_GEN_TRIANGLES,
FTM_GEN_FLEX,
FTM_AUDIO_UPDATE,
FTM_RESET_DRAWORDER,
FTM_OBJECTLIST_UPDATE,
FTM_AVATAR_UPDATE,
FTM_JOINT_UPDATE,
FTM_ATTACHMENT_UPDATE,
FTM_LOD_UPDATE,
FTM_REGION_UPDATE,
FTM_CLEANUP,
FTM_NETWORK,
FTM_IDLE_NETWORK,
FTM_CREATE_OBJECT,
FTM_LOAD_AVATAR,
FTM_PROCESS_MESSAGES,
FTM_PROCESS_OBJECTS,
FTM_PROCESS_IMAGES,
FTM_IMAGE_UPDATE,
FTM_IMAGE_CREATE,
FTM_IMAGE_DECODE,
FTM_IMAGE_READBACK,
FTM_IMAGE_MARK_DIRTY,
FTM_PIPELINE,
FTM_VFILE_WAIT,
FTM_FLEXIBLE_UPDATE,
FTM_OCCLUSION_READBACK,
FTM_HUD_EFFECTS,
FTM_HUD_UPDATE,
FTM_INVENTORY,
FTM_AUTO_SELECT,
FTM_ARRANGE,
FTM_FILTER,
FTM_REFRESH,
FTM_SORT,
FTM_PICK,
FTM_STATEMACHINE,
// Temp
FTM_TEMP1,
FTM_TEMP2,
FTM_TEMP3,
FTM_TEMP4,
FTM_TEMP5,
FTM_TEMP6,
FTM_TEMP7,
FTM_TEMP8,
FTM_OTHER, // Special, used by display code
FTM_NUM_TYPES
};
enum { FTM_HISTORY_NUM = 60 };
enum { FTM_MAX_DEPTH = 64 };
public:
static EFastTimerType sCurType;
LLFastTimer(EFastTimerType type)
{
#if FAST_TIMER_ON
mType = type;
sCurType = type;
// These don't get counted, because they use CPU clockticks
//gTimerBins[gCurTimerBin]++;
//LLTimer::sNumTimerCalls++;
U64 cpu_clocks = getCPUClockCount32();
sStart[sCurDepth] = cpu_clocks;
sCurDepth++;
#endif
};
~LLFastTimer()
{
#if FAST_TIMER_ON
U64 end,delta;
int i;
// These don't get counted, because they use CPU clockticks
//gTimerBins[gCurTimerBin]++;
//LLTimer::sNumTimerCalls++;
end = getCPUClockCount32();
sCurDepth--;
delta = end - sStart[sCurDepth];
sCounter[mType] += delta;
sCalls[mType]++;
// Subtract delta from parents
for (i=0; i<sCurDepth; i++)
sStart[i] += delta;
#endif
}
static void reset();
static U64 countsPerSecond();
public:
static int sCurDepth;
static U64 sStart[FTM_MAX_DEPTH];
static U64 sCounter[FTM_NUM_TYPES];
static U64 sCalls[FTM_NUM_TYPES];
static U64 sCountAverage[FTM_NUM_TYPES];
static U64 sCallAverage[FTM_NUM_TYPES];
static U64 sCountHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
static U64 sCallHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
static int sPauseHistory;
static int sResetHistory;
static U32 getCPUClockCount32();
static U64 getCPUClockCount64();
static U64 sClockResolution;
static S32 sCurFrameIndex;
static S32 sLastFrameIndex;
EFastTimerType mType;
};
// pull in the actual class definition
#include "llfasttimer_class.h"
#endif // LL_LLFASTTIMER_H #endif // LL_LLFASTTIMER_H

View File

@@ -0,0 +1,951 @@
/**
* @file llfasttimer_class.cpp
* @brief Implementation of the fast timer.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
//
// LLFastTimer documentation, written by Aleric (Feb 2012).
//
// Disclaimer: this is horrible code and I distantiate myself from its design.
// It's neither robust nor object oriented. I just document what I find, in
// order to be able to fix the bugs (that logically result from such a design).
//
// Note that the choosen names of the variables are non-intuitive and make
// understanding the code harder. However, I didn't change them in order to
// make merging less of a nightmare in the future -- Aleric.
//
//
// First of all, absolutely nothing in this code is even remotely thread-safe:
// FastTimers should only be used from the main thread and never from another
// thread.
//
// NamedTimerFactory is a singleton, accessed through NamedTimerFactory::instance().
//
// It has four pointer members which are initialized once to point to
// four objects with a life-time equal to the application/singleton:
//
// mTimerRoot --> NamedTimer("root")
// mActiveTimerRoot --> NamedTimer("Frame")
// mRootFrameState --> FrameState(mActiveTimerRoot)
// mAppTimer --> LLFastTimer(mRootFrameState)
//
// A NamedTimer has a name and a life-time of approximately that of the application.
// There is exactly one instance per unique name.
// NamedTimer's are ordered in a hierarchy with each one parent and zero or more
// children (the "root" has parent NULL).
// The parent of mActiveTimerRoot is mTimerRoot, which has one child: mActiveTimerRoot.
// NamedTimer::getDepth() returns the number of parents; mTimerRoot has a depth of 0,
// mActiveTimerRoot has a depth of 1 and so on. NamedTimer::getRootNamedTimer() just
// returns mActiveTimerRoot.
//
// Each NamedTimer is linked to exactly one FrameState object, namely
// LLFastTimer::getFrameStateList()[named_timer.getFrameStateIndex()], where
// getFrameStateList() is a static function returning a global std::vector<FrameState>.
// This vector is ordered "Depth First" (the FrameState objects (belonging to
// NamedTimer objects) with smallest depth first). The vector is re-sorted a few
// times in the beginning (and indexes in FrameState updated) since timers are added
// whenever they are first used, not in "Depth First" order, but stabilizes after a
// while. This implies that FrameState pointers can't really be used: FrameState
// objects move around in memory whenever something is inserted or removed from the
// std::vector and/or when the vector is re-sorted. However, FrameState pointers ARE
// being used and code exists that tries to update those pointers in the above
// mentioned cases (this part had bugs, which I now fixed).
//
// FrameState objects point back to their corresponding NamedTimer through mTimer.
// They have also parents: the FrameState object corresponding to the parent of mTimer.
//
// Thus, so far we have (assuming "namedtimerX" was created first):
//
// NamedTimer's: FrameState's:
//
// NULL
// ^
// |
// depth=0: "root" (mTimerRoot) <-------> getFrameStateList()[0]
// ^ ^
// | (parent) | (parent)
// | |
// depth=1: "Frame" (mActiveTimerRoot) <-------> mRootFrameState
// ^ ^ ^ ^
// | | | |
// | (parent) | (parent) | (parent) | (parent)
// | | | |
// depth=2: "namedtimerX" | <-------> getFrameStateList()[2] |
// "namedtimerY" <-------> getFrameStateList()[3]
//
// where the NamedTimer's point to the corresponding FrameState's by means of
// NamedTimer::mFrameStateIndex, and the FrameState's point back through FrameState::mTimer.
//
// Note the missing getFrameStateList()[1], which is ignored and replaced by
// a specific call to 'new FrameState' in initSingleton(). The reason for that is
// probably because otherwise mRootFrameState has to be updated every time the
// frame state list vector is moved in memory. This special case adds some complexity to,
// for instance, getFrameState() which now needs to test if the caller is mActiveTimerRoot.
//
// DeclareTimer objects are NameTimer/FrameState pointer pairs with again a lifetime
// of approximately that of the application. The are usually static, even global,
// and are passed an name as string; the name is looked up and added if not already
// existing, or else the previously created pair is returned. Obviously, "root" and
// "Frame" are the only ones that don't have a corresponding DeclareTimer object.
//
// LLFastTimer objects are short lived objects, created in a scope and destroyed
// at the end in order to measure the time that the application spent in that
// scope. They are passed DeclareTimer objects to know which timer to append to.
// LLFastTimer::mFrameState is a pointer to the corresponding timer.
// The static LLFastTimer::sCurTimerData is a CurTimerData struct that has
// a duplicate of that pointer as well as a pointer to the corresponding NamedTimer,
// of the last LLFastTimer object that was created (and not destroyed again);
// in other words: the running timer with the largest depth.
// When a new LLFastTimer object is created while one is already running,
// then this sCurTimerData is saved in the already running one (as
// LLFastTimer::mLastTimerData) and restored upon destruction of that child timer.
//
// The following FrameState pointers are being used:
//
// FrameState::mParent
// DeclareTimer::mFrameState
// CurTimerData::mFrameState
// LLFastTimer::mFrameState
//
// All of those can be invalidated whenever something is added to the std::vector<FrameState>,
// and when that vector is sorted.
//
// Adding new FrameState objects is done in NamedTimer(std::string const& name), called from
// createNamedTimer(), called whenever a DeclareTimer is constructed. At the end of the
// DeclareTimer constructor update_cached_pointers_if_changed() is called, which calls
// updateCachedPointers() if the std::vector moved in memory since last time it was called.
//
// Sorting is done in NamedTimer::resetFrame(), which theoretically can be called from
// anywhere. Also here updateCachedPointers() is called, directly after sorting the vector.
//
// I fixed updateCachedPointers() to correct all of the above pointers and removed
// another FrameState pointer that was unnecessary.
#include "linden_common.h"
#include "llfasttimer.h"
#include "llmemory.h"
#include "llprocessor.h"
#include "llsingleton.h"
#include "lltreeiterators.h"
#include "llsdserialize.h"
#include <boost/bind.hpp>
#if LL_WINDOWS
#include "lltimer.h"
#elif LL_LINUX || LL_SOLARIS
#include <sys/time.h>
#include <sched.h>
#include "lltimer.h"
#elif LL_DARWIN
#include <sys/time.h>
#include "lltimer.h" // get_clock_count()
#else
#error "architecture not supported"
#endif
//////////////////////////////////////////////////////////////////////////////
// statics
S32 LLFastTimer::sCurFrameIndex = -1;
S32 LLFastTimer::sLastFrameIndex = -1;
U64 LLFastTimer::sLastFrameTime = LLFastTimer::getCPUClockCount64();
bool LLFastTimer::sPauseHistory = 0;
bool LLFastTimer::sResetHistory = 0;
LLFastTimer::CurTimerData LLFastTimer::sCurTimerData;
BOOL LLFastTimer::sLog = FALSE;
std::string LLFastTimer::sLogName = "";
BOOL LLFastTimer::sMetricLog = FALSE;
LLMutex* LLFastTimer::sLogLock = NULL;
std::queue<LLSD> LLFastTimer::sLogQueue;
std::vector<LLFastTimer::FrameState>* LLFastTimer::sTimerInfos = NULL;
U64 LLFastTimer::sTimerCycles = 0;
U32 LLFastTimer::sTimerCalls = 0;
// FIXME: move these declarations to the relevant modules
// helper functions
typedef LLTreeDFSPostIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_bottom_up_iterator_t;
static timer_tree_bottom_up_iterator_t begin_timer_tree_bottom_up(LLFastTimer::NamedTimer& id)
{
return timer_tree_bottom_up_iterator_t(&id,
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
}
static timer_tree_bottom_up_iterator_t end_timer_tree_bottom_up()
{
return timer_tree_bottom_up_iterator_t();
}
typedef LLTreeDFSIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_dfs_iterator_t;
static timer_tree_dfs_iterator_t begin_timer_tree(LLFastTimer::NamedTimer& id)
{
return timer_tree_dfs_iterator_t(&id,
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
}
static timer_tree_dfs_iterator_t end_timer_tree()
{
return timer_tree_dfs_iterator_t();
}
// factory class that creates NamedTimers via static DeclareTimer objects
class NamedTimerFactory : public LLSingleton<NamedTimerFactory>
{
public:
NamedTimerFactory()
: mActiveTimerRoot(NULL),
mTimerRoot(NULL),
mAppTimer(NULL),
mRootFrameState(NULL)
{}
/*virtual */ void initSingleton()
{
mTimerRoot = new LLFastTimer::NamedTimer("root");
mActiveTimerRoot = new LLFastTimer::NamedTimer("Frame");
mActiveTimerRoot->setCollapsed(false);
mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
// getFrameState and setParent recursively call this function,
// so we have to work around that by using a specialized implementation
// for the special case were mTimerRoot != mActiveTimerRoot -- Aleric
mRootFrameState->mParent = &LLFastTimer::getFrameStateList()[0]; // &mTimerRoot->getFrameState()
mRootFrameState->mParent->mActiveCount = 1;
// And the following four lines are mActiveTimerRoot->setParent(mTimerRoot);
llassert(!mActiveTimerRoot->mParent);
mActiveTimerRoot->mParent = mTimerRoot; // mParent = parent;
//mRootFrameState->mParent = mRootFrameState->mParent; // getFrameState().mParent = &parent->getFrameState();
mTimerRoot->getChildren().push_back(mActiveTimerRoot); // parent->getChildren().push_back(this);
mTimerRoot->mNeedsSorting = true; // parent->mNeedsSorting = true;
mAppTimer = new LLFastTimer(mRootFrameState);
}
~NamedTimerFactory()
{
std::for_each(mTimers.begin(), mTimers.end(), DeletePairedPointer());
delete mAppTimer;
delete mActiveTimerRoot;
delete mTimerRoot;
delete mRootFrameState;
}
LLFastTimer::NamedTimer& createNamedTimer(const std::string& name)
{
timer_map_t::iterator found_it = mTimers.find(name);
if (found_it != mTimers.end())
{
return *found_it->second;
}
LLFastTimer::NamedTimer* timer = new LLFastTimer::NamedTimer(name);
timer->setParent(mTimerRoot);
mTimers.insert(std::make_pair(name, timer));
return *timer;
}
LLFastTimer::NamedTimer* getTimerByName(const std::string& name)
{
timer_map_t::iterator found_it = mTimers.find(name);
if (found_it != mTimers.end())
{
return found_it->second;
}
return NULL;
}
LLFastTimer::NamedTimer* getActiveRootTimer() { return mActiveTimerRoot; }
LLFastTimer::NamedTimer* getRootTimer() { return mTimerRoot; }
const LLFastTimer* getAppTimer() { return mAppTimer; }
LLFastTimer::FrameState& getRootFrameState() { return *mRootFrameState; }
typedef std::map<std::string, LLFastTimer::NamedTimer*> timer_map_t;
timer_map_t::iterator beginTimers() { return mTimers.begin(); }
timer_map_t::iterator endTimers() { return mTimers.end(); }
S32 timerCount() { return mTimers.size(); }
private:
timer_map_t mTimers;
LLFastTimer::NamedTimer* mActiveTimerRoot;
LLFastTimer::NamedTimer* mTimerRoot;
LLFastTimer* mAppTimer;
LLFastTimer::FrameState* mRootFrameState; // Points to memory allocated with new, so this pointer is not invalidated.
};
void update_cached_pointers_if_changed()
{
// detect when elements have moved and update cached pointers
static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
if (&*(LLFastTimer::getFrameStateList().begin()) != sFirstTimerAddress)
{
LLFastTimer::updateCachedPointers();
sFirstTimerAddress = &*(LLFastTimer::getFrameStateList().begin());
}
}
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
{
mTimer.setCollapsed(!open);
mFrameState = &mTimer.getFrameState();
update_cached_pointers_if_changed();
}
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
{
mFrameState = &mTimer.getFrameState();
update_cached_pointers_if_changed();
}
// static
void LLFastTimer::updateCachedPointers()
{
// Update DeclareTimer::mFrameState pointers.
for (DeclareTimer::instance_iter it = DeclareTimer::beginInstances(); it != DeclareTimer::endInstances(); ++it)
{
// update cached pointer
it->mFrameState = &it->mTimer.getFrameState();
}
// Update CurTimerData::mFrameState and LLFastTimer::mFrameState of timers on the stack.
FrameState& root_frame_state(NamedTimerFactory::instance().getRootFrameState()); // This one is not invalidated.
CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
// If the the following condition holds then cur_timer_data->mCurTimer == mAppTimer and
// we can stop since mAppTimer->mFrameState is allocated with new and does not invalidate.
while(cur_timer_data->mFrameState != &root_frame_state)
{
cur_timer_data->mFrameState = cur_timer_data->mCurTimer->mFrameState = &cur_timer_data->mNamedTimer->getFrameState();
cur_timer_data = &cur_timer_data->mCurTimer->mLastTimerData;
}
// Update FrameState::mParent
info_list_t& frame_state_list(getFrameStateList());
FrameState* const vector_start = &*frame_state_list.begin();
int const vector_size = frame_state_list.size();
FrameState const* const old_vector_start = root_frame_state.mParent;
if (vector_start != old_vector_start)
{
// Vector was moved; if it was sorted then FrameState::mParent will get fixed after returning from this function (see LLFastTimer::NamedTimer::resetFrame).
root_frame_state.mParent = vector_start;
ptrdiff_t offset = vector_start - old_vector_start;
llassert(frame_state_list[vector_size - 1].mParent == vector_start); // The one that was added at the end is already OK.
for (int i = 2; i < vector_size - 1; ++i)
{
FrameState*& parent = frame_state_list[i].mParent;
if (parent != &root_frame_state)
{
parent += offset;
}
}
}
}
// See lltimer.cpp.
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
std::string LLFastTimer::sClockType = "gettimeofday";
#elif LL_WINDOWS
std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
#else
#error "Platform not supported"
#endif
//static
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
{
static bool firstcall = true;
static U64 sCPUClockFrequency;
if (firstcall)
{
sCPUClockFrequency = calc_clock_frequency();
firstcall = false;
}
return sCPUClockFrequency >> 8;
}
LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
: mActiveCount(0),
mCalls(0),
mSelfTimeCounter(0),
mParent(NULL),
mLastCaller(NULL),
mMoveUpTree(false),
mTimer(timerp)
{}
LLFastTimer::NamedTimer::NamedTimer(const std::string& name)
: mName(name),
mCollapsed(true),
mParent(NULL),
mTotalTimeCounter(0),
mCountAverage(0),
mCallAverage(0),
mNeedsSorting(false)
{
info_list_t& frame_state_list = getFrameStateList();
mFrameStateIndex = frame_state_list.size();
getFrameStateList().push_back(FrameState(this));
mCountHistory = new U32[HISTORY_NUM];
memset(mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
mCallHistory = new U32[HISTORY_NUM];
memset(mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
}
LLFastTimer::NamedTimer::~NamedTimer()
{
delete[] mCountHistory;
delete[] mCallHistory;
}
std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx)
{
F64 ms_multiplier = 1000.0 / (F64)LLFastTimer::countsPerSecond();
if (history_idx < 0)
{
// by default, show average number of call
return llformat("%s (%.2f ms, %d calls)", getName().c_str(), (F32)((F32)getCountAverage() * ms_multiplier), (S32)getCallAverage());
}
else
{
return llformat("%s (%.2f ms, %d calls)", getName().c_str(), (F32)((F32)getHistoricalCount(history_idx) * ms_multiplier), (S32)getHistoricalCalls(history_idx));
}
}
void LLFastTimer::NamedTimer::setParent(NamedTimer* parent)
{
llassert_always(parent != this);
llassert_always(parent != NULL);
if (mParent)
{
// subtract our accumulated from previous parent
for (S32 i = 0; i < HISTORY_NUM; i++)
{
mParent->mCountHistory[i] -= mCountHistory[i];
}
// subtract average timing from previous parent
mParent->mCountAverage -= mCountAverage;
std::vector<NamedTimer*>& children = mParent->getChildren();
std::vector<NamedTimer*>::iterator found_it = std::find(children.begin(), children.end(), this);
if (found_it != children.end())
{
children.erase(found_it);
}
}
mParent = parent;
if (parent)
{
getFrameState().mParent = &parent->getFrameState();
parent->getChildren().push_back(this);
parent->mNeedsSorting = true;
}
}
S32 LLFastTimer::NamedTimer::getDepth()
{
S32 depth = 0;
NamedTimer* timerp = mParent;
while(timerp)
{
depth++;
timerp = timerp->mParent;
}
return depth;
}
// static
void LLFastTimer::NamedTimer::processTimes()
{
if (sCurFrameIndex < 0) return;
buildHierarchy();
accumulateTimings();
}
// sort timer info structs by depth first traversal order
struct SortTimersDFS
{
bool operator()(const LLFastTimer::FrameState& i1, const LLFastTimer::FrameState& i2)
{
return i1.mTimer->getFrameStateIndex() < i2.mTimer->getFrameStateIndex();
}
};
// sort child timers by name
struct SortTimerByName
{
bool operator()(const LLFastTimer::NamedTimer* i1, const LLFastTimer::NamedTimer* i2)
{
return i1->getName() < i2->getName();
}
};
//static
void LLFastTimer::NamedTimer::buildHierarchy()
{
if (sCurFrameIndex < 0 ) return;
// set up initial tree
{
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
{
NamedTimer& timer = *it;
if (&timer == NamedTimerFactory::instance().getRootTimer()) continue;
// bootstrap tree construction by attaching to last timer to be on stack
// when this timer was called
FrameState& frame_state(timer.getFrameState());
if (frame_state.mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
{
timer.setParent(frame_state.mLastCaller);
// no need to push up tree on first use, flag can be set spuriously
frame_state.mMoveUpTree = false;
}
}
}
// bump timers up tree if they've been flagged as being in the wrong place
// do this in a bottom up order to promote descendants first before promoting ancestors
// this preserves partial order derived from current frame's observations
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getRootTimer());
it != end_timer_tree_bottom_up();
++it)
{
NamedTimer* timerp = *it;
// skip root timer
if (timerp == NamedTimerFactory::instance().getRootTimer()) continue;
if (timerp->getFrameState().mMoveUpTree)
{
// since ancestors have already been visited, reparenting won't affect tree traversal
//step up tree, bringing our descendants with us
//llinfos << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
// " to child of " << timerp->getParent()->getParent()->getName() << llendl;
timerp->setParent(timerp->getParent()->getParent());
timerp->getFrameState().mMoveUpTree = false;
// don't bubble up any ancestors until descendants are done bubbling up
it.skipAncestors();
}
}
// sort timers by time last called, so call graph makes sense
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
it != end_timer_tree();
++it)
{
NamedTimer* timerp = (*it);
if (timerp->mNeedsSorting)
{
std::sort(timerp->getChildren().begin(), timerp->getChildren().end(), SortTimerByName());
}
timerp->mNeedsSorting = false;
}
}
//static
void LLFastTimer::NamedTimer::accumulateTimings()
{
U32 cur_time = getCPUClockCount32();
// walk up stack of active timers and accumulate current time while leaving timing structures active
LLFastTimer* cur_timer = sCurTimerData.mCurTimer;
// root defined by parent pointing to self
CurTimerData* cur_data = &sCurTimerData;
while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
{
U32 cumulative_time_delta = cur_time - cur_timer->mStartTime;
U32 self_time_delta = cumulative_time_delta - cur_data->mChildTime;
cur_data->mChildTime = 0;
cur_timer->mFrameState->mSelfTimeCounter += self_time_delta;
cur_timer->mStartTime = cur_time;
cur_data = &cur_timer->mLastTimerData;
cur_data->mChildTime += cumulative_time_delta;
cur_timer = cur_timer->mLastTimerData.mCurTimer;
}
// traverse tree in DFS post order, or bottom up
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getActiveRootTimer());
it != end_timer_tree_bottom_up();
++it)
{
NamedTimer* timerp = (*it);
timerp->mTotalTimeCounter = timerp->getFrameState().mSelfTimeCounter;
for (child_const_iter child_it = timerp->beginChildren(); child_it != timerp->endChildren(); ++child_it)
{
timerp->mTotalTimeCounter += (*child_it)->mTotalTimeCounter;
}
S32 cur_frame = sCurFrameIndex;
if (cur_frame >= 0)
{
// update timer history
int hidx = cur_frame % HISTORY_NUM;
timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter;
timerp->mCountAverage = ((U64)timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1);
timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls;
timerp->mCallAverage = ((U64)timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1);
}
}
}
U32 LLFastTimer::NamedTimer::getCountAverage() const
{
return mCountAverage;// (sCurFrameIndex <= 0 || mCountAverage <= 0) ? 0 : mCountAverage / llmin(sCurFrameIndex,(S32)HISTORY_NUM);
}
U32 LLFastTimer::NamedTimer::getCallAverage() const
{
return mCallAverage;// (sCurFrameIndex <= 0 || mCallAverage <= 0) ? 0 : mCallAverage / llmin(sCurFrameIndex,(S32)HISTORY_NUM);
}
// static
void LLFastTimer::NamedTimer::resetFrame()
{
if (sLog)
{ //output current frame counts to performance log
static S32 call_count = 0;
if (call_count % 100 == 0)
{
llinfos << "countsPerSecond (32 bit): " << countsPerSecond() << llendl;
llinfos << "get_clock_count (64 bit): " << get_clock_count() << llendl;
llinfos << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << llendl;
llinfos << "getCPUClockCount32() " << getCPUClockCount32() << llendl;
llinfos << "getCPUClockCount64() " << getCPUClockCount64() << llendl;
llinfos << "elapsed sec " << ((F64)getCPUClockCount64())/((F64)LLProcessorInfo().getCPUFrequency()*1000000.0) << llendl;
}
call_count++;
F64 iclock_freq = 1000.0 / countsPerSecond(); // good place to calculate clock frequency
F64 total_time = 0;
LLSD sd;
{
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
{
NamedTimer& timer = *it;
FrameState& info = timer.getFrameState();
sd[timer.getName()]["Time"] = (LLSD::Real) (info.mSelfTimeCounter*iclock_freq);
sd[timer.getName()]["Calls"] = (LLSD::Integer) info.mCalls;
// computing total time here because getting the root timer's getCountHistory
// doesn't work correctly on the first frame
total_time = total_time + info.mSelfTimeCounter * iclock_freq;
}
}
sd["Total"]["Time"] = (LLSD::Real) total_time;
sd["Total"]["Calls"] = (LLSD::Integer) 1;
{
LLMutexLock lock(sLogLock);
sLogQueue.push(sd);
}
}
// tag timers by position in depth first traversal of tree
S32 index = 0;
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
it != end_timer_tree();
++it)
{
NamedTimer* timerp = (*it);
timerp->mFrameStateIndex = index;
index++;
}
llassert(index == (S32)getFrameStateList().size());
// sort timers by DFS traversal order to improve cache coherency
std::sort(getFrameStateList().begin(), getFrameStateList().end(), SortTimersDFS());
// update pointers into framestatelist now that we've sorted it
updateCachedPointers();
// reset for next frame
{
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
{
NamedTimer& timer = *it;
FrameState& info = timer.getFrameState();
info.mSelfTimeCounter = 0;
info.mCalls = 0;
info.mLastCaller = NULL;
info.mMoveUpTree = false;
// update parent pointer in timer state struct
if (timer.mParent)
{
info.mParent = &timer.mParent->getFrameState();
}
}
}
//sTimerCycles = 0;
//sTimerCalls = 0;
}
//static
void LLFastTimer::NamedTimer::reset()
{
resetFrame(); // reset frame data
// walk up stack of active timers and reset start times to current time
// effectively zeroing out any accumulated time
U32 cur_time = getCPUClockCount32();
// root defined by parent pointing to self
CurTimerData* cur_data = &sCurTimerData;
LLFastTimer* cur_timer = cur_data->mCurTimer;
while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
{
cur_timer->mStartTime = cur_time;
cur_data->mChildTime = 0;
cur_data = &cur_timer->mLastTimerData;
cur_timer = cur_data->mCurTimer;
}
// reset all history
{
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
{
NamedTimer& timer = *it;
if (&timer != NamedTimerFactory::instance().getRootTimer())
{
timer.setParent(NamedTimerFactory::instance().getRootTimer());
}
timer.mCountAverage = 0;
timer.mCallAverage = 0;
memset(timer.mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
memset(timer.mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
}
}
sLastFrameIndex = 0;
sCurFrameIndex = 0;
}
//static
LLFastTimer::info_list_t& LLFastTimer::getFrameStateList()
{
if (!sTimerInfos)
{
sTimerInfos = new info_list_t;
#if 0
// Avoid the vector being moved in memory by reserving enough memory right away.
sTimerInfos->reserve(1024);
#endif
}
return *sTimerInfos;
}
U32 LLFastTimer::NamedTimer::getHistoricalCount(S32 history_index) const
{
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
return mCountHistory[history_idx];
}
U32 LLFastTimer::NamedTimer::getHistoricalCalls(S32 history_index ) const
{
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
return mCallHistory[history_idx];
}
LLFastTimer::FrameState& LLFastTimer::NamedTimer::getFrameState() const
{
llassert_always(mFrameStateIndex >= 0);
if (this == NamedTimerFactory::instance().getActiveRootTimer())
{
return NamedTimerFactory::instance().getRootFrameState();
}
return getFrameStateList()[mFrameStateIndex];
}
// static
LLFastTimer::NamedTimer& LLFastTimer::NamedTimer::getRootNamedTimer()
{
return *NamedTimerFactory::instance().getActiveRootTimer();
}
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::beginChildren()
{
return mChildren.begin();
}
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::endChildren()
{
return mChildren.end();
}
std::vector<LLFastTimer::NamedTimer*>& LLFastTimer::NamedTimer::getChildren()
{
return mChildren;
}
//static
void LLFastTimer::nextFrame()
{
countsPerSecond(); // good place to calculate clock frequency
U64 frame_time = getCPUClockCount64();
if ((frame_time - sLastFrameTime) >> 8 > 0xffffffff)
{
llinfos << "Slow frame, fast timers inaccurate" << llendl;
}
if (!sPauseHistory)
{
NamedTimer::processTimes();
sLastFrameIndex = sCurFrameIndex++;
}
// get ready for next frame
NamedTimer::resetFrame();
sLastFrameTime = frame_time;
}
//static
void LLFastTimer::dumpCurTimes()
{
// accumulate timings, etc.
NamedTimer::processTimes();
F64 clock_freq = (F64)countsPerSecond();
F64 iclock_freq = 1000.0 / clock_freq; // clock_ticks -> milliseconds
// walk over timers in depth order and output timings
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
it != end_timer_tree();
++it)
{
NamedTimer* timerp = (*it);
F64 total_time_ms = ((F64)timerp->getHistoricalCount(0) * iclock_freq);
// Don't bother with really brief times, keep output concise
if (total_time_ms < 0.1) continue;
std::ostringstream out_str;
for (S32 i = 0; i < timerp->getDepth(); i++)
{
out_str << "\t";
}
out_str << timerp->getName() << " "
<< std::setprecision(3) << total_time_ms << " ms, "
<< timerp->getHistoricalCalls(0) << " calls";
llinfos << out_str.str() << llendl;
}
}
//static
void LLFastTimer::reset()
{
NamedTimer::reset();
}
//static
void LLFastTimer::writeLog(std::ostream& os)
{
while (!sLogQueue.empty())
{
LLSD& sd = sLogQueue.front();
LLSDSerialize::toXML(sd, os);
LLMutexLock lock(sLogLock);
sLogQueue.pop();
}
}
//static
const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& name)
{
return NamedTimerFactory::instance().getTimerByName(name);
}
LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
: mFrameState(state)
{
// Only called for mAppTimer with mRootFrameState, which never invalidates.
llassert(state == &NamedTimerFactory::instance().getRootFrameState());
U32 start_time = getCPUClockCount32();
mStartTime = start_time;
mFrameState->mActiveCount++;
LLFastTimer::sCurTimerData.mCurTimer = this;
LLFastTimer::sCurTimerData.mNamedTimer = mFrameState->mTimer;
LLFastTimer::sCurTimerData.mFrameState = mFrameState;
LLFastTimer::sCurTimerData.mChildTime = 0;
// This is the root FastTimer (mAppTimer), mark it as such by having
// mLastTimerData be equal to sCurTimerData (which is a rather arbitrary
// and not very logical way to do that --Aleric).
mLastTimerData = LLFastTimer::sCurTimerData;
}
//////////////////////////////////////////////////////////////////////////////
//
// Important note: These implementations must be FAST!
//
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
// These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
U32 LLFastTimer::getCPUClockCount32()
{
return (U32)(get_clock_count()>>8);
}
U64 LLFastTimer::getCPUClockCount64()
{
return get_clock_count();
}

View File

@@ -0,0 +1,277 @@
/**
* @file llfasttimer_class.h
* @brief Declaration of a fast timer.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_FASTTIMER_CLASS_H
#define LL_FASTTIMER_CLASS_H
#include "llinstancetracker.h"
#define FAST_TIMER_ON 1
#define TIME_FAST_TIMERS 0
#define DEBUG_FAST_TIMER_THREADS 1
class LLMutex;
#include <queue>
#include "llsd.h"
LL_COMMON_API void assert_main_thread();
class LL_COMMON_API LLFastTimer
{
public:
class NamedTimer;
struct LL_COMMON_API FrameState
{
FrameState(NamedTimer* timerp);
U32 mSelfTimeCounter;
U32 mCalls;
FrameState* mParent; // info for caller timer
NamedTimer* mLastCaller; // used to bootstrap tree construction
NamedTimer* mTimer;
U16 mActiveCount; // number of timers with this ID active on stack
bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
};
// stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
class LL_COMMON_API NamedTimer
: public LLInstanceTracker<NamedTimer>
{
friend class DeclareTimer;
public:
~NamedTimer();
enum { HISTORY_NUM = 300 };
const std::string& getName() const { return mName; }
NamedTimer* getParent() const { return mParent; }
void setParent(NamedTimer* parent);
S32 getDepth();
std::string getToolTip(S32 history_index = -1);
typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
child_const_iter beginChildren();
child_const_iter endChildren();
std::vector<NamedTimer*>& getChildren();
void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
bool getCollapsed() const { return mCollapsed; }
U32 getCountAverage() const; //{ return mCountAverage }
U32 getCallAverage() const; //{ return mCallAverage }
U32 getHistoricalCount(S32 history_index = 0) const;
U32 getHistoricalCalls(S32 history_index = 0) const;
static NamedTimer& getRootNamedTimer();
S32 getFrameStateIndex() const { return mFrameStateIndex; }
FrameState& getFrameState() const;
private:
friend class LLFastTimer;
friend class NamedTimerFactory;
//
// methods
//
NamedTimer(const std::string& name);
// recursive call to gather total time from children
static void accumulateTimings();
// updates cumulative times and hierarchy,
// can be called multiple times in a frame, at any point
static void processTimes();
static void buildHierarchy();
static void resetFrame();
static void reset();
//
// members
//
S32 mFrameStateIndex;
std::string mName;
U32 mTotalTimeCounter;
U32 mCountAverage;
U32 mCallAverage;
U32* mCountHistory;
U32* mCallHistory;
// tree structure
NamedTimer* mParent; // NamedTimer of caller(parent)
std::vector<NamedTimer*> mChildren;
bool mCollapsed; // don't show children
bool mNeedsSorting; // sort children whenever child added
};
// used to statically declare a new named timer
class LL_COMMON_API DeclareTimer
: public LLInstanceTracker<DeclareTimer>
{
friend class LLFastTimer;
public:
DeclareTimer(const std::string& name, bool open);
DeclareTimer(const std::string& name);
private:
NamedTimer& mTimer;
FrameState* mFrameState;
};
public:
LLFastTimer(LLFastTimer::FrameState* state);
LL_FORCE_INLINE LLFastTimer(LLFastTimer::DeclareTimer& timer)
: mFrameState(timer.mFrameState)
{
#if TIME_FAST_TIMERS
U64 timer_start = getCPUClockCount64();
#endif
#if FAST_TIMER_ON
LLFastTimer::FrameState* frame_state = mFrameState;
mStartTime = getCPUClockCount32();
frame_state->mActiveCount++;
frame_state->mCalls++;
// keep current parent as long as it is active when we are
frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
mLastTimerData = *cur_timer_data;
cur_timer_data->mCurTimer = this;
cur_timer_data->mNamedTimer = &timer.mTimer;
cur_timer_data->mFrameState = frame_state;
cur_timer_data->mChildTime = 0;
#endif
#if TIME_FAST_TIMERS
U64 timer_end = getCPUClockCount64();
sTimerCycles += timer_end - timer_start;
#endif
#if DEBUG_FAST_TIMER_THREADS
#if !LL_RELEASE
assert_main_thread();
#endif
#endif
}
LL_FORCE_INLINE ~LLFastTimer()
{
#if TIME_FAST_TIMERS
U64 timer_start = getCPUClockCount64();
#endif
#if FAST_TIMER_ON
LLFastTimer::FrameState* frame_state = mFrameState;
U32 total_time = getCPUClockCount32() - mStartTime;
frame_state->mSelfTimeCounter += total_time - LLFastTimer::sCurTimerData.mChildTime;
frame_state->mActiveCount--;
// store last caller to bootstrap tree creation
// do this in the destructor in case of recursion to get topmost caller
frame_state->mLastCaller = mLastTimerData.mNamedTimer;
// we are only tracking self time, so subtract our total time delta from parents
mLastTimerData.mChildTime += total_time;
LLFastTimer::sCurTimerData = mLastTimerData;
#endif
#if TIME_FAST_TIMERS
U64 timer_end = getCPUClockCount64();
sTimerCycles += timer_end - timer_start;
sTimerCalls++;
#endif
}
public:
static LLMutex* sLogLock;
static std::queue<LLSD> sLogQueue;
static BOOL sLog;
static BOOL sMetricLog;
static std::string sLogName;
static bool sPauseHistory;
static bool sResetHistory;
static U64 sTimerCycles;
static U32 sTimerCalls;
typedef std::vector<FrameState> info_list_t;
static info_list_t& getFrameStateList();
// call this once a frame to reset timers
static void nextFrame();
// dumps current cumulative frame stats to log
// call nextFrame() to reset timers
static void dumpCurTimes();
// call this to reset timer hierarchy, averages, etc.
static void reset();
// called to update all FrameState pointers.
static void updateCachedPointers();
static U64 countsPerSecond();
static S32 getLastFrameIndex() { return sLastFrameIndex; }
static S32 getCurFrameIndex() { return sCurFrameIndex; }
static void writeLog(std::ostream& os);
static const NamedTimer* getTimerByName(const std::string& name);
struct CurTimerData
{
LLFastTimer* mCurTimer;
NamedTimer* mNamedTimer;
FrameState* mFrameState;
U32 mChildTime;
};
static CurTimerData sCurTimerData;
static std::string sClockType;
public:
static U32 getCPUClockCount32();
static U64 getCPUClockCount64();
private:
static S32 sCurFrameIndex;
static S32 sLastFrameIndex;
static U64 sLastFrameTime;
static info_list_t* sTimerInfos;
U32 mStartTime;
LLFastTimer::FrameState* mFrameState;
LLFastTimer::CurTimerData mLastTimerData;
};
#endif // LL_LLFASTTIMER_H

View File

@@ -35,10 +35,13 @@
#include "u64.h" #include "u64.h"
#include "llframetimer.h" #include "llframetimer.h"
#include "aiframetimer.h"
#include "llaprpool.h"
// Local constants. // Local constants.
static F64 const USEC_PER_SECOND = 1000000.0; static F64 const USEC_PER_SECOND = 1000000.0;
static F64 const USEC_TO_SEC_F64 = 0.000001; static F64 const USEC_TO_SEC_F64 = 0.000001;
static F64 const NEVER = 1e16;
// Static members // Static members
U64 const LLFrameTimer::sStartTotalTime = totalTime(); // Application start in microseconds since epoch. U64 const LLFrameTimer::sStartTotalTime = totalTime(); // Application start in microseconds since epoch.
@@ -50,41 +53,41 @@ F64 LLFrameTimer::sFrameTime = 0.0; // Current time in seconds since app
S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of frames since application start). S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of frames since application start).
U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame. U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame.
U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount. U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount.
// Mutex for the above.
apr_thread_mutex_t* LLFrameTimer::sGlobalMutex;
// static // static
void LLFrameTimer::updateFrameTime() void LLFrameTimer::global_initialization(void)
{ {
sTotalTime = totalTime(); apr_thread_mutex_create(&sGlobalMutex, APR_THREAD_MUTEX_UNNESTED, LLAPRRootPool::get()());
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64; AIFrameTimer::sNextExpiration = NEVER;
sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
} }
// static // static
void LLFrameTimer::updateFrameTimeAndCount() void LLFrameTimer::updateFrameTime(void)
{
llassert(is_main_thread());
sTotalTime = totalTime();
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
F64 new_frame_time = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
apr_thread_mutex_lock(sGlobalMutex);
sFrameTime = new_frame_time;
apr_thread_mutex_unlock(sGlobalMutex);
}
// static
void LLFrameTimer::updateFrameTimeAndCount(void)
{ {
updateFrameTime(); updateFrameTime();
sFrameDeltaTime = sTotalTime - sPrevTotalTime; sFrameDeltaTime = sTotalTime - sPrevTotalTime;
sPrevTotalTime = sTotalTime; sPrevTotalTime = sTotalTime;
++sFrameCount; ++sFrameCount;
}
void LLFrameTimer::reset(F32 expiration) // Handle AIFrameTimer expiration and callbacks.
{ if (AIFrameTimer::sNextExpiration <= sFrameTime)
llassert(!mPaused); {
mStartTime = sFrameTime; AIFrameTimer::handleExpiration(sFrameTime);
mExpiry = sFrameTime + expiration; }
}
void LLFrameTimer::start(F32 expiration)
{
reset(expiration);
mRunning = true; // Start, if not already started.
}
void LLFrameTimer::stop()
{
llassert(!mPaused);
mRunning = false;
} }
// Don't combine pause/unpause with start/stop // Don't combine pause/unpause with start/stop
@@ -95,38 +98,38 @@ void LLFrameTimer::stop()
// foo.unpause() // unpauses // foo.unpause() // unpauses
// F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause() // F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause()
// Note: elapsed would also be valid with no unpause() call (= time run until pause() called) // Note: elapsed would also be valid with no unpause() call (= time run until pause() called)
void LLFrameTimer::pause() void LLFrameTimer::pause(void)
{ {
llassert(is_main_thread());
if (!mPaused) if (!mPaused)
{ {
// Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime - mStartTime; // Abuse mStartTime to store the elapsed time so far. mStartTime = sFrameTime - mStartTime; // Abuse mStartTime to store the elapsed time so far.
} }
mPaused = true; mPaused = true;
} }
void LLFrameTimer::unpause() void LLFrameTimer::unpause(void)
{ {
llassert(is_main_thread());
if (mPaused) if (mPaused)
{ {
// Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime - mStartTime; // Set mStartTime consistent with the elapsed time so far. mStartTime = sFrameTime - mStartTime; // Set mStartTime consistent with the elapsed time so far.
} }
mPaused = false; mPaused = false;
} }
void LLFrameTimer::setTimerExpirySec(F32 expiration)
{
llassert(!mPaused);
mExpiry = mStartTime + expiration;
}
void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch) void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
{ {
llassert(is_main_thread());
llassert(!mPaused); llassert(!mPaused);
// Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime; mStartTime = sFrameTime;
mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime); mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
} }
F64 LLFrameTimer::expiresAt() const F64 LLFrameTimer::expiresAt(void) const
{ {
F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64; F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
expires_at += mExpiry; expires_at += mExpiry;
@@ -135,31 +138,47 @@ F64 LLFrameTimer::expiresAt() const
bool LLFrameTimer::checkExpirationAndReset(F32 expiration) bool LLFrameTimer::checkExpirationAndReset(F32 expiration)
{ {
if (hasExpired()) llassert(!mPaused);
F64 frame_time = getElapsedSeconds();
if (frame_time >= mExpiry)
{ {
reset(expiration); mStartTime = frame_time;
mExpiry = mStartTime + expiration;
return true; return true;
} }
return false; return false;
} }
// static F32 LLFrameTimer::getElapsedTimeAndResetF32(void)
F32 LLFrameTimer::getFrameDeltaTimeF32()
{ {
llassert(mRunning && !mPaused);
F64 frame_time = getElapsedSeconds();
F32 elapsed_time = (F32)(frame_time - mStartTime);
mExpiry = mStartTime = frame_time;
return elapsed_time;
}
// static
// Return number of seconds between the last two frames.
F32 LLFrameTimer::getFrameDeltaTimeF32(void)
{
llassert(is_main_thread());
// Only the main thread writes to sFrameDeltaTime, so there is no need for locking.
return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64); return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
} }
// static // static
// Return seconds since the current frame started // Return seconds since the current frame started
F32 LLFrameTimer::getCurrentFrameTime() F32 LLFrameTimer::getCurrentFrameTime(void)
{ {
llassert(is_main_thread());
// Only the main thread writes to sTotalTime, so there is no need for locking.
U64 frame_time = totalTime() - sTotalTime; U64 frame_time = totalTime() - sTotalTime;
return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64); return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64);
} }
// Glue code to avoid full class .h file #includes // Glue code to avoid full class .h file #includes
F32 getCurrentFrameTime() F32 getCurrentFrameTime(void)
{ {
return (F32)(LLFrameTimer::getCurrentFrameTime()); return (F32)(LLFrameTimer::getCurrentFrameTime());
} }

View File

@@ -43,88 +43,131 @@
#include "lltimer.h" #include "lltimer.h"
#include "timing.h" #include "timing.h"
#include <apr_thread_mutex.h>
class LL_COMMON_API LLFrameTimer class LL_COMMON_API LLFrameTimer
{ {
public: public:
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true). // Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
LLFrameTimer(void) : mStartTime(sFrameTime), mExpiry(0), mRunning(true), mPaused(false) { } LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); }
// Atomic reads of static variables.
// Return the number of seconds since the start of the application. // Return the number of seconds since the start of the application.
static F64 getElapsedSeconds() static F64 getElapsedSeconds(void)
{ {
// Loses msec precision after ~4.5 hours... // Loses msec precision after ~4.5 hours...
return sFrameTime; apr_thread_mutex_lock(sGlobalMutex);
F64 res = sFrameTime;
apr_thread_mutex_unlock(sGlobalMutex);
return res;
} }
// Return a low precision usec since epoch. // Return a low precision usec since epoch.
static U64 getTotalTime() static U64 getTotalTime(void)
{ {
llassert(sTotalTime); // sTotalTime is only accessed by the main thread, so no locking is necessary.
return sTotalTime; llassert(is_main_thread());
//apr_thread_mutex_lock(sGlobalMutex);
U64 res = sTotalTime;
//apr_thread_mutex_unlock(sGlobalMutex);
llassert(res);
return res;
} }
// Return a low precision seconds since epoch. // Return a low precision seconds since epoch.
static F64 getTotalSeconds() static F64 getTotalSeconds(void)
{ {
return sTotalSeconds; // sTotalSeconds is only accessed by the main thread, so no locking is necessary.
llassert(is_main_thread());
//apr_thread_mutex_lock(sGlobalMutex);
F64 res = sTotalSeconds;
//apr_thread_mutex_unlock(sGlobalMutex);
return res;
}
// Return current frame number (the number of frames since application start).
static U32 getFrameCount(void)
{
// sFrameCount is only accessed by the main thread, so no locking is necessary.
llassert(is_main_thread());
//apr_thread_mutex_lock(sGlobalMutex);
U32 res = sFrameCount;
//apr_thread_mutex_unlock(sGlobalMutex);
return res;
} }
// Call this method once per frame to update the current frame time. // Call this method once per frame to update the current frame time.
// This is actually called at some other times as well. // This is actually called at some other times as well.
static void updateFrameTime(); static void updateFrameTime(void);
// Call this method once, and only once, per frame to update the current frame count and sFrameDeltaTime. // Call this method once, and only once, per frame to update the current frame count and sFrameDeltaTime.
static void updateFrameTimeAndCount(); static void updateFrameTimeAndCount(void);
// Return current frame number (the number of frames since application start).
static U32 getFrameCount() { return sFrameCount; }
// Return duration of last frame in seconds. // Return duration of last frame in seconds.
static F32 getFrameDeltaTimeF32(); static F32 getFrameDeltaTimeF32(void);
// Return seconds since the current frame started // Return seconds since the current frame started
static F32 getCurrentFrameTime(); static F32 getCurrentFrameTime(void);
// MANIPULATORS // MANIPULATORS
void reset(F32 expiration = 0.f); // Same as start() but leaves mRunning off when called after stop(). void reset(F32 expiration = 0.f) // Same as start() but leaves mRunning off when called after stop().
void start(F32 expiration = 0.f); // Reset and (re)start with expiration. {
void stop(); // Stop running. llassert(!mPaused);
mStartTime = getElapsedSeconds();
mExpiry = mStartTime + expiration;
}
void start(F32 expiration = 0.f) // Reset and (re)start with expiration.
{
reset(expiration);
mRunning = true; // Start, if not already started.
}
void stop(void) // Stop running.
{
llassert(!mPaused);
mRunning = false;
}
void pause(); // Mark elapsed time so far. void pause(); // Mark elapsed time so far.
void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime. void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime.
void setTimerExpirySec(F32 expiration); void setTimerExpirySec(F32 expiration) { llassert(!mPaused); mExpiry = mStartTime + expiration; }
void setExpiryAt(F64 seconds_since_epoch); void setExpiryAt(F64 seconds_since_epoch);
bool checkExpirationAndReset(F32 expiration); // Returns true when expired. Only resets if expired. bool checkExpirationAndReset(F32 expiration); // Returns true when expired. Only resets if expired.
F32 getElapsedTimeAndResetF32() { F32 t = getElapsedTimeF32(); reset(); return t; } F32 getElapsedTimeAndResetF32(void);
void setAge(const F64 age) { llassert(!mPaused); mStartTime = sFrameTime - age; } void setAge(const F64 age) { llassert(!mPaused); mStartTime = getElapsedSeconds() - age; }
// ACCESSORS // ACCESSORS
bool hasExpired() const { return sFrameTime >= mExpiry; } bool hasExpired() const { return getElapsedSeconds() >= mExpiry; }
F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(sFrameTime - mStartTime); } F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(getElapsedSeconds() - mStartTime); }
bool getStarted() const { return mRunning; } bool getStarted() const { return mRunning; }
// return the seconds since epoch when this timer will expire. // return the seconds since epoch when this timer will expire.
F64 expiresAt() const; F64 expiresAt() const;
protected: public:
// A single, high resolution timer that drives all LLFrameTimers // Do one-time initialization of the static members.
// *NOTE: no longer used. static void global_initialization(void);
//static LLTimer sInternalTimer;
protected:
// //
// Aplication constants // Application constants
// //
// Application start in microseconds since epoch. // Application start in microseconds since epoch.
static U64 const sStartTotalTime; static U64 const sStartTotalTime;
// //
// Data updated per frame // Global data.
// //
// More than one thread are accessing (some of) these variables, therefore we need locking.
static apr_thread_mutex_t* sGlobalMutex;
// Current time in seconds since application start, updated together with sTotalTime. // Current time in seconds since application start, updated together with sTotalTime.
static F64 sFrameTime; static F64 sFrameTime;

View File

@@ -194,7 +194,12 @@ public:
} }
protected: protected:
LLInstanceTracker(KEY key) { add_(key); } LLInstanceTracker(KEY key)
{
// make sure static data outlives all instances
getStatic();
add_(key);
}
virtual ~LLInstanceTracker() virtual ~LLInstanceTracker()
{ {
// it's unsafe to delete instances of this type while all instances are being iterated over. // it's unsafe to delete instances of this type while all instances are being iterated over.
@@ -282,7 +287,8 @@ public:
protected: protected:
LLInstanceTracker() LLInstanceTracker()
{ {
// it's safe but unpredictable to create instances of this type while all instances are being iterated over. I hate unpredictable. This assert will probably be turned on early in the next development cycle. // make sure static data outlives all instances
getStatic();
getSet_().insert(static_cast<T*>(this)); getSet_().insert(static_cast<T*>(this));
} }
virtual ~LLInstanceTracker() virtual ~LLInstanceTracker()

View File

@@ -134,4 +134,7 @@ private:
LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b); LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b); LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
#endif // LL_LLMD5_H #endif // LL_LLMD5_H

File diff suppressed because it is too large Load Diff

View File

@@ -40,9 +40,7 @@
#endif #endif
#include "llerror.h" #include "llerror.h"
#include "llmemtype.h"
//----------------------------------------------------------------------------
#if LL_DEBUG #if LL_DEBUG
inline void* ll_aligned_malloc( size_t size, int align ) inline void* ll_aligned_malloc( size_t size, int align )
{ {
@@ -120,6 +118,10 @@ inline void ll_aligned_free_32(void *p)
#define ll_aligned_free_32 free #define ll_aligned_free_32 free
#endif // LL_DEBUG #endif // LL_DEBUG
#ifndef __DEBUG_PRIVATE_MEM__
#define __DEBUG_PRIVATE_MEM__ 0
#endif
class LL_COMMON_API LLMemory class LL_COMMON_API LLMemory
{ {
public: public:
@@ -130,10 +132,395 @@ public:
// Return value is zero if not known. // Return value is zero if not known.
static U64 getCurrentRSS(); static U64 getCurrentRSS();
static U32 getWorkingSetSize(); static U32 getWorkingSetSize();
static void* tryToAlloc(void* address, U32 size);
static void initMaxHeapSizeGB(F32 max_heap_size_gb, BOOL prevent_heap_failure);
static void updateMemoryInfo() ;
static void logMemoryInfo(BOOL update = FALSE);
static bool isMemoryPoolLow();
static U32 getAvailableMemKB() ;
static U32 getMaxMemKB() ;
static U32 getAllocatedMemKB() ;
private: private:
static char* reserveMem; static char* reserveMem;
static U32 sAvailPhysicalMemInKB ;
static U32 sMaxPhysicalMemInKB ;
static U32 sAllocatedMemInKB;
static U32 sAllocatedPageSizeInKB ;
static U32 sMaxHeapSizeInKB;
static BOOL sEnableMemoryFailurePrevention;
}; };
//----------------------------------------------------------------------------
class LLMutex ;
#if MEM_TRACK_MEM
class LL_COMMON_API LLMemTracker
{
private:
LLMemTracker() ;
~LLMemTracker() ;
public:
static void release() ;
static LLMemTracker* getInstance() ;
void track(const char* function, const int line) ;
void preDraw(BOOL pause) ;
void postDraw() ;
const char* getNextLine() ;
private:
static LLMemTracker* sInstance ;
char** mStringBuffer ;
S32 mCapacity ;
U32 mLastAllocatedMem ;
S32 mCurIndex ;
S32 mCounter;
S32 mDrawnIndex;
S32 mNumOfDrawn;
BOOL mPaused;
LLMutex* mMutexp ;
};
#define MEM_TRACK_RELEASE LLMemTracker::release() ;
#define MEM_TRACK LLMemTracker::getInstance()->track(__FUNCTION__, __LINE__) ;
#else // MEM_TRACK_MEM
#define MEM_TRACK_RELEASE
#define MEM_TRACK
#endif // MEM_TRACK_MEM
//----------------------------------------------------------------------------
//
//class LLPrivateMemoryPool defines a private memory pool for an application to use, so the application does not
//need to access the heap directly fro each memory allocation. Throught this, the allocation speed is faster,
//and reduces virtaul address space gragmentation problem.
//Note: this class is thread-safe by passing true to the constructor function. However, you do not need to do this unless
//you are sure the memory allocation and de-allocation will happen in different threads. To make the pool thread safe
//increases allocation and deallocation cost.
//
class LL_COMMON_API LLPrivateMemoryPool
{
friend class LLPrivateMemoryPoolManager ;
public:
class LL_COMMON_API LLMemoryBlock //each block is devided into slots uniformly
{
public:
LLMemoryBlock() ;
~LLMemoryBlock() ;
void init(char* buffer, U32 buffer_size, U32 slot_size) ;
void setBuffer(char* buffer, U32 buffer_size) ;
char* allocate() ;
void freeMem(void* addr) ;
bool empty() {return !mAllocatedSlots;}
bool isFull() {return mAllocatedSlots == mTotalSlots;}
bool isFree() {return !mTotalSlots;}
U32 getSlotSize()const {return mSlotSize;}
U32 getTotalSlots()const {return mTotalSlots;}
U32 getBufferSize()const {return mBufferSize;}
char* getBuffer() const {return mBuffer;}
//debug use
void resetBitMap() ;
private:
char* mBuffer;
U32 mSlotSize ; //when the block is not initialized, it is the buffer size.
U32 mBufferSize ;
U32 mUsageBits ;
U8 mTotalSlots ;
U8 mAllocatedSlots ;
U8 mDummySize ; //size of extra bytes reserved for mUsageBits.
public:
LLMemoryBlock* mPrev ;
LLMemoryBlock* mNext ;
LLMemoryBlock* mSelf ;
struct CompareAddress
{
bool operator()(const LLMemoryBlock* const& lhs, const LLMemoryBlock* const& rhs)
{
return (size_t)lhs->getBuffer() < (size_t)rhs->getBuffer();
}
};
};
class LL_COMMON_API LLMemoryChunk //is divided into memory blocks.
{
public:
LLMemoryChunk() ;
~LLMemoryChunk() ;
void init(char* buffer, U32 buffer_size, U32 min_slot_size, U32 max_slot_size, U32 min_block_size, U32 max_block_size) ;
void setBuffer(char* buffer, U32 buffer_size) ;
bool empty() ;
char* allocate(U32 size) ;
void freeMem(void* addr) ;
char* getBuffer() const {return mBuffer;}
U32 getBufferSize() const {return mBufferSize;}
U32 getAllocatedSize() const {return mAlloatedSize;}
bool containsAddress(const char* addr) const;
static U32 getMaxOverhead(U32 data_buffer_size, U32 min_slot_size,
U32 max_slot_size, U32 min_block_size, U32 max_block_size) ;
void dump() ;
private:
U32 getPageIndex(char const* addr) ;
U32 getBlockLevel(U32 size) ;
U16 getPageLevel(U32 size) ;
LLMemoryBlock* addBlock(U32 blk_idx) ;
void popAvailBlockList(U32 blk_idx) ;
void addToFreeSpace(LLMemoryBlock* blk) ;
void removeFromFreeSpace(LLMemoryBlock* blk) ;
void removeBlock(LLMemoryBlock* blk) ;
void addToAvailBlockList(LLMemoryBlock* blk) ;
U32 calcBlockSize(U32 slot_size);
LLMemoryBlock* createNewBlock(LLMemoryBlock* blk, U32 buffer_size, U32 slot_size, U32 blk_idx) ;
private:
LLMemoryBlock** mAvailBlockList ;//256 by mMinSlotSize
LLMemoryBlock** mFreeSpaceList;
LLMemoryBlock* mBlocks ; //index of blocks by address.
char* mBuffer ;
U32 mBufferSize ;
char* mDataBuffer ;
char* mMetaBuffer ;
U32 mMinBlockSize ;
U32 mMinSlotSize ;
U32 mMaxSlotSize ;
U32 mAlloatedSize ;
U16 mBlockLevels;
U16 mPartitionLevels;
public:
//form a linked list
LLMemoryChunk* mNext ;
LLMemoryChunk* mPrev ;
} ;
private:
LLPrivateMemoryPool(S32 type, U32 max_pool_size) ;
~LLPrivateMemoryPool() ;
char *allocate(U32 size) ;
void freeMem(void* addr) ;
void dump() ;
U32 getTotalAllocatedSize() ;
U32 getTotalReservedSize() {return mReservedPoolSize;}
S32 getType() const {return mType; }
bool isEmpty() const {return !mNumOfChunks; }
private:
void lock() ;
void unlock() ;
S32 getChunkIndex(U32 size) ;
LLMemoryChunk* addChunk(S32 chunk_index) ;
bool checkSize(U32 asked_size) ;
void removeChunk(LLMemoryChunk* chunk) ;
U16 findHashKey(const char* addr);
void addToHashTable(LLMemoryChunk* chunk) ;
void removeFromHashTable(LLMemoryChunk* chunk) ;
void rehash() ;
bool fillHashTable(U16 start, U16 end, LLMemoryChunk* chunk) ;
LLMemoryChunk* findChunk(const char* addr) ;
void destroyPool() ;
public:
enum
{
SMALL_ALLOCATION = 0, //from 8 bytes to 2KB(exclusive), page size 2KB, max chunk size is 4MB.
MEDIUM_ALLOCATION, //from 2KB to 512KB(exclusive), page size 32KB, max chunk size 4MB
LARGE_ALLOCATION, //from 512KB to 4MB(inclusive), page size 64KB, max chunk size 16MB
SUPER_ALLOCATION //allocation larger than 4MB.
};
enum
{
STATIC = 0 , //static pool(each alllocation stays for a long time) without threading support
VOLATILE, //Volatile pool(each allocation stays for a very short time) without threading support
STATIC_THREADED, //static pool with threading support
VOLATILE_THREADED, //volatile pool with threading support
MAX_TYPES
}; //pool types
private:
LLMutex* mMutexp ;
U32 mMaxPoolSize;
U32 mReservedPoolSize ;
LLMemoryChunk* mChunkList[SUPER_ALLOCATION] ; //all memory chunks reserved by this pool, sorted by address
U16 mNumOfChunks ;
U16 mHashFactor ;
S32 mType ;
class LLChunkHashElement
{
public:
LLChunkHashElement() {mFirst = NULL ; mSecond = NULL ;}
bool add(LLMemoryChunk* chunk) ;
void remove(LLMemoryChunk* chunk) ;
LLMemoryChunk* findChunk(const char* addr) ;
bool empty() {return !mFirst && !mSecond; }
bool full() {return mFirst && mSecond; }
bool hasElement(LLMemoryChunk* chunk) {return mFirst == chunk || mSecond == chunk;}
private:
LLMemoryChunk* mFirst ;
LLMemoryChunk* mSecond ;
};
std::vector<LLChunkHashElement> mChunkHashList ;
};
class LL_COMMON_API LLPrivateMemoryPoolManager
{
private:
LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size) ;
~LLPrivateMemoryPoolManager() ;
public:
static LLPrivateMemoryPoolManager* getInstance() ;
static void initClass(BOOL enabled, U32 pool_size) ;
static void destroyClass() ;
LLPrivateMemoryPool* newPool(S32 type) ;
void deletePool(LLPrivateMemoryPool* pool) ;
private:
std::vector<LLPrivateMemoryPool*> mPoolList ;
U32 mMaxPrivatePoolSize;
static LLPrivateMemoryPoolManager* sInstance ;
static BOOL sPrivatePoolEnabled;
static std::vector<LLPrivateMemoryPool*> sDanglingPoolList ;
public:
//debug and statistics info.
void updateStatistics() ;
U32 mTotalReservedSize ;
U32 mTotalAllocatedSize ;
public:
#if __DEBUG_PRIVATE_MEM__
static char* allocate(LLPrivateMemoryPool* poolp, U32 size, const char* function, const int line) ;
typedef std::map<char*, std::string> mem_allocation_info_t ;
static mem_allocation_info_t sMemAllocationTracker;
#else
static char* allocate(LLPrivateMemoryPool* poolp, U32 size) ;
#endif
static void freeMem(LLPrivateMemoryPool* poolp, void* addr) ;
};
//-------------------------------------------------------------------------------------
#if __DEBUG_PRIVATE_MEM__
#define ALLOCATE_MEM(poolp, size) LLPrivateMemoryPoolManager::allocate((poolp), (size), __FUNCTION__, __LINE__)
#else
#define ALLOCATE_MEM(poolp, size) LLPrivateMemoryPoolManager::allocate((poolp), (size))
//#define ALLOCATE_MEM(poolp, size) new char[size]
#endif
#define FREE_MEM(poolp, addr) LLPrivateMemoryPoolManager::freeMem((poolp), (addr))
//#define FREE_MEM(poolp, addr) delete[] addr;
//-------------------------------------------------------------------------------------
//
//the below singleton is used to test the private memory pool.
//
#if 0
class LL_COMMON_API LLPrivateMemoryPoolTester
{
private:
LLPrivateMemoryPoolTester() ;
~LLPrivateMemoryPoolTester() ;
public:
static LLPrivateMemoryPoolTester* getInstance() ;
static void destroy() ;
void run(S32 type) ;
private:
void correctnessTest() ;
void performanceTest() ;
void fragmentationtest() ;
void test(U32 min_size, U32 max_size, U32 stride, U32 times, bool random_deletion, bool output_statistics) ;
void testAndTime(U32 size, U32 times) ;
#if 0
public:
void* operator new(size_t size)
{
return (void*)sPool->allocate(size) ;
}
void operator delete(void* addr)
{
sPool->freeMem(addr) ;
}
void* operator new[](size_t size)
{
return (void*)sPool->allocate(size) ;
}
void operator delete[](void* addr)
{
sPool->freeMem(addr) ;
}
#endif
private:
static LLPrivateMemoryPoolTester* sInstance;
static LLPrivateMemoryPool* sPool ;
static LLPrivateMemoryPool* sThreadedPool ;
};
#if 0
//static
void* LLPrivateMemoryPoolTester::operator new(size_t size)
{
return (void*)sPool->allocate(size) ;
}
//static
void LLPrivateMemoryPoolTester::operator delete(void* addr)
{
sPool->free(addr) ;
}
//static
void* LLPrivateMemoryPoolTester::operator new[](size_t size)
{
return (void*)sPool->allocate(size) ;
}
//static
void LLPrivateMemoryPoolTester::operator delete[](void* addr)
{
sPool->free(addr) ;
}
#endif
#endif
//EVENTUALLY REMOVE THESE: //EVENTUALLY REMOVE THESE:
#include "llpointer.h" #include "llpointer.h"
#include "llrefcount.h" #include "llrefcount.h"

View File

@@ -0,0 +1,232 @@
/**
* @file llmemtype.cpp
* @brief Simple memory allocation/deallocation tracking stuff here
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llmemtype.h"
#include "llallocator.h"
std::vector<char const *> LLMemType::DeclareMemType::mNameList;
LLMemType::DeclareMemType LLMemType::MTYPE_INIT("Init");
LLMemType::DeclareMemType LLMemType::MTYPE_STARTUP("Startup");
LLMemType::DeclareMemType LLMemType::MTYPE_MAIN("Main");
LLMemType::DeclareMemType LLMemType::MTYPE_FRAME("Frame");
LLMemType::DeclareMemType LLMemType::MTYPE_GATHER_INPUT("GatherInput");
LLMemType::DeclareMemType LLMemType::MTYPE_JOY_KEY("JoyKey");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE("Idle");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_PUMP("IdlePump");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_NETWORK("IdleNetwork");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_REGIONS("IdleUpdateRegions");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_VIEWER_REGION("IdleUpdateViewerRegion");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_SURFACE("IdleUpdateSurface");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_PARCEL_OVERLAY("IdleUpdateParcelOverlay");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_AUDIO("IdleAudio");
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING("CacheProcessPending");
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS("CacheProcessPendingAsks");
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES("CacheProcessPendingReplies");
LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_CHECK_ALL("MessageCheckAll");
LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_PROCESS_ACKS("MessageProcessAcks");
LLMemType::DeclareMemType LLMemType::MTYPE_RENDER("Render");
LLMemType::DeclareMemType LLMemType::MTYPE_SLEEP("Sleep");
LLMemType::DeclareMemType LLMemType::MTYPE_NETWORK("Network");
LLMemType::DeclareMemType LLMemType::MTYPE_PHYSICS("Physics");
LLMemType::DeclareMemType LLMemType::MTYPE_INTERESTLIST("InterestList");
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEBASE("ImageBase");
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGERAW("ImageRaw");
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEFORMATTED("ImageFormatted");
LLMemType::DeclareMemType LLMemType::MTYPE_APPFMTIMAGE("AppFmtImage");
LLMemType::DeclareMemType LLMemType::MTYPE_APPRAWIMAGE("AppRawImage");
LLMemType::DeclareMemType LLMemType::MTYPE_APPAUXRAWIMAGE("AppAuxRawImage");
LLMemType::DeclareMemType LLMemType::MTYPE_DRAWABLE("Drawable");
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT("Object");
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE("ObjectProcessUpdate");
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE_CORE("ObjectProcessUpdateCore");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY("Display");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE("DisplayUpdate");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_CAMERA("DisplayUpdateCam");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_GEOM("DisplayUpdateGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SWAP("DisplaySwap");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_HUD("DisplayUpdateHud");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_GEN_REFLECTION("DisplayGenRefl");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE("DisplayImageUpdate");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_STATE_SORT("DisplayStateSort");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SKY("DisplaySky");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_GEOM("DisplayRenderGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_FLUSH("DisplayRenderFlush");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_UI("DisplayRenderUI");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_ATTACHMENTS("DisplayRenderAttach");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DATA("VertexData");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CONSTRUCTOR("VertexConstr");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTRUCTOR("VertexDestr");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_VERTICES("VertexCreateVerts");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_INDICES("VertexCreateIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_BUFFER("VertexDestroyBuff");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_INDICES("VertexDestroyIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_VERTS("VertexUpdateVerts");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_INDICES("VertexUpdateIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER("VertexAllocateBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_RESIZE_BUFFER("VertexResizeBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER("VertexMapBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES("VertexMapBufferVerts");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES("VertexMapBufferIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UNMAP_BUFFER("VertexUnmapBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_STRIDE("VertexSetStride");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_BUFFER("VertexSetBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER("VertexSetupVertBuff");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CLEANUP_CLASS("VertexCleanupClass");
LLMemType::DeclareMemType LLMemType::MTYPE_SPACE_PARTITION("SpacePartition");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE("Pipeline");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_INIT("PipelineInit");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_BUFFERS("PipelineCreateBuffs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RESTORE_GL("PipelineRestroGL");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UNLOAD_SHADERS("PipelineUnloadShaders");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL("PipelineLightingDetail");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GET_POOL_TYPE("PipelineGetPoolType");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_POOL("PipelineAddPool");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ALLOCATE_DRAWABLE("PipelineAllocDrawable");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_OBJECT("PipelineAddObj");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS("PipelineCreateObjs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_MOVE("PipelineUpdateMove");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_GEOM("PipelineUpdateGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_VISIBLE("PipelineMarkVisible");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_MOVED("PipelineMarkMoved");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_SHIFT("PipelineMarkShift");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_SHIFT_OBJECTS("PipelineShiftObjs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_TEXTURED("PipelineMarkTextured");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_REBUILD("PipelineMarkRebuild");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_CULL("PipelineUpdateCull");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_STATE_SORT("PipelineStateSort");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_POST_SORT("PipelinePostSort");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS("PipelineHudEls");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HL("PipelineRenderHL");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM("PipelineRenderGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED("PipelineRenderGeomDef");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF("PipelineRenderGeomPostDef");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_SHADOW("PipelineRenderGeomShadow");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_SELECT("PipelineRenderSelect");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_REBUILD_POOLS("PipelineRebuildPools");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_QUICK_LOOKUP("PipelineQuickLookup");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_OBJECTS("PipelineRenderObjs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GENERATE_IMPOSTOR("PipelineGenImpostors");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_BLOOM("PipelineRenderBloom");
LLMemType::DeclareMemType LLMemType::MTYPE_UPKEEP_POOLS("UpkeepPools");
LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR("Avatar");
LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR_MESH("AvatarMesh");
LLMemType::DeclareMemType LLMemType::MTYPE_PARTICLES("Particles");
LLMemType::DeclareMemType LLMemType::MTYPE_REGIONS("Regions");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY("Inventory");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DRAW("InventoryDraw");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS("InventoryBuildNewViews");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DO_FOLDER("InventoryDoFolder");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_POST_BUILD("InventoryPostBuild");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_FROM_XML("InventoryFromXML");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_CREATE_NEW_ITEM("InventoryCreateNewItem");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_INIT("InventoryViewInit");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_SHOW("InventoryViewShow");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE("InventoryViewToggle");
LLMemType::DeclareMemType LLMemType::MTYPE_ANIMATION("Animation");
LLMemType::DeclareMemType LLMemType::MTYPE_VOLUME("Volume");
LLMemType::DeclareMemType LLMemType::MTYPE_PRIMITIVE("Primitive");
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT("Script");
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_RUN("ScriptRun");
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_BYTECODE("ScriptByteCode");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_PUMP("IoPump");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_TCP("IoTCP");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_BUFFER("IoBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_HTTP_SERVER("IoHttpServer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_SERVER("IoSDServer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_CLIENT("IoSDClient");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_URL_REQUEST("IOUrlRequest");
LLMemType::DeclareMemType LLMemType::MTYPE_DIRECTX_INIT("DirectXInit");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP1("Temp1");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP2("Temp2");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP3("Temp3");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP4("Temp4");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP5("Temp5");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP6("Temp6");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP7("Temp7");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP8("Temp8");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP9("Temp9");
LLMemType::DeclareMemType LLMemType::MTYPE_OTHER("Other");
LLMemType::DeclareMemType::DeclareMemType(char const * st)
{
mID = (S32)mNameList.size();
mName = st;
mNameList.push_back(mName);
}
LLMemType::DeclareMemType::~DeclareMemType()
{
}
LLMemType::LLMemType(LLMemType::DeclareMemType& dt)
{
mTypeIndex = dt.mID;
LLAllocator::pushMemType(dt.mID);
}
LLMemType::~LLMemType()
{
LLAllocator::popMemType();
}
char const * LLMemType::getNameFromID(S32 id)
{
if (id < 0 || id >= (S32)DeclareMemType::mNameList.size())
{
return "INVALID";
}
return DeclareMemType::mNameList[id];
}
//--------------------------------------------------------------------------------------------------

View File

@@ -36,128 +36,210 @@
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
class LLMemType;
extern void* ll_allocate (size_t size);
extern void ll_release (void *p);
#define MEM_TRACK_MEM 0
#define MEM_TRACK_TYPE (1 && MEM_TRACK_MEM)
#if MEM_TRACK_TYPE
#define MEM_DUMP_DATA 1
#define MEM_TYPE_NEW(T) \
static void* operator new(size_t s) { LLMemType mt(T); return ll_allocate(s); } \
static void operator delete(void* p) { ll_release(p); }
#else
#define MEM_TYPE_NEW(T)
#endif // MEM_TRACK_TYPE
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
#include "linden_common.h"
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// WARNING: Never commit with MEM_TRACK_MEM == 1
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#define MEM_TRACK_MEM (0 && LL_WINDOWS)
#include <vector>
#define MEM_TYPE_NEW(T)
class LL_COMMON_API LLMemType class LL_COMMON_API LLMemType
{ {
public: public:
// Also update sTypeDesc in llmemory.cpp
enum EMemType // class we'll initialize all instances of as
// static members of MemType. Then use
// to construct any new mem type.
class LL_COMMON_API DeclareMemType
{ {
MTYPE_INIT, public:
MTYPE_STARTUP, DeclareMemType(char const * st);
MTYPE_MAIN, ~DeclareMemType();
MTYPE_IMAGEBASE, S32 mID;
MTYPE_IMAGERAW, char const * mName;
MTYPE_IMAGEFORMATTED,
MTYPE_APPFMTIMAGE, // array so we can map an index ID to Name
MTYPE_APPRAWIMAGE, static std::vector<char const *> mNameList;
MTYPE_APPAUXRAWIMAGE,
MTYPE_DRAWABLE,
MTYPE_OBJECT,
MTYPE_VERTEX_DATA,
MTYPE_SPACE_PARTITION,
MTYPE_PIPELINE,
MTYPE_AVATAR,
MTYPE_AVATAR_MESH,
MTYPE_PARTICLES,
MTYPE_REGIONS,
MTYPE_INVENTORY,
MTYPE_ANIMATION,
MTYPE_VOLUME,
MTYPE_PRIMITIVE,
MTYPE_NETWORK,
MTYPE_PHYSICS,
MTYPE_INTERESTLIST,
MTYPE_SCRIPT,
MTYPE_SCRIPT_RUN,
MTYPE_SCRIPT_BYTECODE,
MTYPE_IO_PUMP,
MTYPE_IO_TCP,
MTYPE_IO_BUFFER,
MTYPE_IO_HTTP_SERVER,
MTYPE_IO_SD_SERVER,
MTYPE_IO_SD_CLIENT,
MTYPE_IO_URL_REQUEST,
MTYPE_TEMP1,
MTYPE_TEMP2,
MTYPE_TEMP3,
MTYPE_TEMP4,
MTYPE_TEMP5,
MTYPE_TEMP6,
MTYPE_TEMP7,
MTYPE_TEMP8,
MTYPE_TEMP9,
MTYPE_OTHER, // Special, used by display code
MTYPE_NUM_TYPES
}; };
enum { MTYPE_MAX_DEPTH = 64 };
public: LLMemType(DeclareMemType& dt);
LLMemType(EMemType type) ~LLMemType();
{
#if MEM_TRACK_TYPE
if (type < 0 || type >= MTYPE_NUM_TYPES)
llerrs << "LLMemType error" << llendl;
if (sCurDepth < 0 || sCurDepth >= MTYPE_MAX_DEPTH)
llerrs << "LLMemType error" << llendl;
sType[sCurDepth] = sCurType;
sCurDepth++;
sCurType = type;
#endif
}
~LLMemType()
{
#if MEM_TRACK_TYPE
sCurDepth--;
sCurType = sType[sCurDepth];
#endif
}
static void reset(); static char const * getNameFromID(S32 id);
static void printMem();
public: static DeclareMemType MTYPE_INIT;
#if MEM_TRACK_TYPE static DeclareMemType MTYPE_STARTUP;
static S32 sCurDepth; static DeclareMemType MTYPE_MAIN;
static S32 sCurType; static DeclareMemType MTYPE_FRAME;
static S32 sType[MTYPE_MAX_DEPTH];
static S32 sMemCount[MTYPE_NUM_TYPES]; static DeclareMemType MTYPE_GATHER_INPUT;
static S32 sMaxMemCount[MTYPE_NUM_TYPES]; static DeclareMemType MTYPE_JOY_KEY;
static S32 sNewCount[MTYPE_NUM_TYPES];
static S32 sOverheadMem; static DeclareMemType MTYPE_IDLE;
static const char* sTypeDesc[MTYPE_NUM_TYPES]; static DeclareMemType MTYPE_IDLE_PUMP;
#endif static DeclareMemType MTYPE_IDLE_NETWORK;
static S32 sTotalMem; static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
static S32 sMaxTotalMem; static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION;
static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE;
static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY;
static DeclareMemType MTYPE_IDLE_AUDIO;
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING;
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS;
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES;
static DeclareMemType MTYPE_MESSAGE_CHECK_ALL;
static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS;
static DeclareMemType MTYPE_RENDER;
static DeclareMemType MTYPE_SLEEP;
static DeclareMemType MTYPE_NETWORK;
static DeclareMemType MTYPE_PHYSICS;
static DeclareMemType MTYPE_INTERESTLIST;
static DeclareMemType MTYPE_IMAGEBASE;
static DeclareMemType MTYPE_IMAGERAW;
static DeclareMemType MTYPE_IMAGEFORMATTED;
static DeclareMemType MTYPE_APPFMTIMAGE;
static DeclareMemType MTYPE_APPRAWIMAGE;
static DeclareMemType MTYPE_APPAUXRAWIMAGE;
static DeclareMemType MTYPE_DRAWABLE;
static DeclareMemType MTYPE_OBJECT;
static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE;
static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE;
static DeclareMemType MTYPE_DISPLAY;
static DeclareMemType MTYPE_DISPLAY_UPDATE;
static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA;
static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM;
static DeclareMemType MTYPE_DISPLAY_SWAP;
static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD;
static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION;
static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE;
static DeclareMemType MTYPE_DISPLAY_STATE_SORT;
static DeclareMemType MTYPE_DISPLAY_SKY;
static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM;
static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH;
static DeclareMemType MTYPE_DISPLAY_RENDER_UI;
static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS;
static DeclareMemType MTYPE_VERTEX_DATA;
static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR;
static DeclareMemType MTYPE_VERTEX_DESTRUCTOR;
static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES;
static DeclareMemType MTYPE_VERTEX_CREATE_INDICES;
static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER;
static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES;
static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS;
static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES;
static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER;
static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER;
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER;
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES;
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES;
static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER;
static DeclareMemType MTYPE_VERTEX_SET_STRIDE;
static DeclareMemType MTYPE_VERTEX_SET_BUFFER;
static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER;
static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS;
static DeclareMemType MTYPE_SPACE_PARTITION;
static DeclareMemType MTYPE_PIPELINE;
static DeclareMemType MTYPE_PIPELINE_INIT;
static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS;
static DeclareMemType MTYPE_PIPELINE_RESTORE_GL;
static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS;
static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL;
static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE;
static DeclareMemType MTYPE_PIPELINE_ADD_POOL;
static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE;
static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT;
static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS;
static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE;
static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM;
static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE;
static DeclareMemType MTYPE_PIPELINE_MARK_MOVED;
static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT;
static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS;
static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED;
static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD;
static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL;
static DeclareMemType MTYPE_PIPELINE_STATE_SORT;
static DeclareMemType MTYPE_PIPELINE_POST_SORT;
static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS;
static DeclareMemType MTYPE_PIPELINE_RENDER_HL;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW;
static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT;
static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS;
static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP;
static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS;
static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR;
static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM;
static DeclareMemType MTYPE_UPKEEP_POOLS;
static DeclareMemType MTYPE_AVATAR;
static DeclareMemType MTYPE_AVATAR_MESH;
static DeclareMemType MTYPE_PARTICLES;
static DeclareMemType MTYPE_REGIONS;
static DeclareMemType MTYPE_INVENTORY;
static DeclareMemType MTYPE_INVENTORY_DRAW;
static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS;
static DeclareMemType MTYPE_INVENTORY_DO_FOLDER;
static DeclareMemType MTYPE_INVENTORY_POST_BUILD;
static DeclareMemType MTYPE_INVENTORY_FROM_XML;
static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM;
static DeclareMemType MTYPE_INVENTORY_VIEW_INIT;
static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW;
static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE;
static DeclareMemType MTYPE_ANIMATION;
static DeclareMemType MTYPE_VOLUME;
static DeclareMemType MTYPE_PRIMITIVE;
static DeclareMemType MTYPE_SCRIPT;
static DeclareMemType MTYPE_SCRIPT_RUN;
static DeclareMemType MTYPE_SCRIPT_BYTECODE;
static DeclareMemType MTYPE_IO_PUMP;
static DeclareMemType MTYPE_IO_TCP;
static DeclareMemType MTYPE_IO_BUFFER;
static DeclareMemType MTYPE_IO_HTTP_SERVER;
static DeclareMemType MTYPE_IO_SD_SERVER;
static DeclareMemType MTYPE_IO_SD_CLIENT;
static DeclareMemType MTYPE_IO_URL_REQUEST;
static DeclareMemType MTYPE_DIRECTX_INIT;
static DeclareMemType MTYPE_TEMP1;
static DeclareMemType MTYPE_TEMP2;
static DeclareMemType MTYPE_TEMP3;
static DeclareMemType MTYPE_TEMP4;
static DeclareMemType MTYPE_TEMP5;
static DeclareMemType MTYPE_TEMP6;
static DeclareMemType MTYPE_TEMP7;
static DeclareMemType MTYPE_TEMP8;
static DeclareMemType MTYPE_TEMP9;
static DeclareMemType MTYPE_OTHER; // Special; used by display code
S32 mTypeIndex;
}; };
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

102
indra/llcommon/llptrto.cpp Normal file
View File

@@ -0,0 +1,102 @@
/**
* @file llptrto.cpp
* @author Nat Goodspeed
* @date 2008-08-20
* @brief Test for llptrto.h
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llptrto.h"
// STL headers
// std headers
// external library headers
#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>
// other Linden headers
#include "llmemory.h"
// a refcounted class
class RCFoo: public LLRefCount
{
public:
RCFoo() {}
};
// a refcounted subclass
class RCSubFoo: public RCFoo
{
public:
RCSubFoo() {}
};
// a refcounted class using the other refcount base class
class TSRCFoo: public LLThreadSafeRefCount
{
public:
TSRCFoo() {}
};
// a non-refcounted class
class Bar
{
public:
Bar() {}
};
// a non-refcounted subclass
class SubBar: public Bar
{
public:
SubBar() {}
};
int main(int argc, char *argv[])
{
// test LLPtrTo<>
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCFoo>::type, LLPointer<RCFoo> >::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCSubFoo>::type, LLPointer<RCSubFoo> >::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<TSRCFoo>::type, LLPointer<TSRCFoo> >::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<Bar>::type, Bar*>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<SubBar>::type, SubBar*>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<int>::type, int*>::value));
// Test LLRemovePointer<>. Note that we remove both pointer variants from
// each kind of type, regardless of whether the variant makes sense.
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCFoo*>::type, RCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCFoo> >::type, RCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCSubFoo*>::type, RCSubFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCSubFoo> >::type, RCSubFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<TSRCFoo*>::type, TSRCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<TSRCFoo> >::type, TSRCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<Bar*>::type, Bar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<Bar> >::type, Bar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<SubBar*>::type, SubBar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<SubBar> >::type, SubBar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<int*>::type, int>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<int> >::type, int>::value));
return 0;
}

87
indra/llcommon/llptrto.h Normal file
View File

@@ -0,0 +1,87 @@
/**
* @file llptrto.h
* @author Nat Goodspeed
* @date 2008-08-19
* @brief LLPtrTo<TARGET> is a template helper to pick either TARGET* or -- when
* TARGET is a subclass of LLRefCount or LLThreadSafeRefCount --
* LLPointer<TARGET>. LLPtrTo<> chooses whichever pointer type is best.
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLPTRTO_H)
#define LL_LLPTRTO_H
#include "llpointer.h"
#include "llrefcount.h" // LLRefCount
#include "llthread.h" // LLThreadSafeRefCount
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/enable_if.hpp>
/**
* LLPtrTo<TARGET>::type is either of two things:
*
* * When TARGET is a subclass of either LLRefCount or LLThreadSafeRefCount,
* LLPtrTo<TARGET>::type is LLPointer<TARGET>.
* * Otherwise, LLPtrTo<TARGET>::type is TARGET*.
*
* This way, a class template can use LLPtrTo<TARGET>::type to select an
* appropriate pointer type to store.
*/
template <class T, class ENABLE=void>
struct LLPtrTo
{
typedef T* type;
};
/// specialize for subclasses of LLRefCount
template <class T>
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLRefCount, T> >::type>
{
typedef LLPointer<T> type;
};
/// specialize for subclasses of LLThreadSafeRefCount
template <class T>
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLThreadSafeRefCount, T> >::type>
{
typedef LLPointer<T> type;
};
/**
* LLRemovePointer<PTRTYPE>::type gets you the underlying (pointee) type.
*/
template <typename PTRTYPE>
struct LLRemovePointer
{
typedef typename boost::remove_pointer<PTRTYPE>::type type;
};
/// specialize for LLPointer<SOMECLASS>
template <typename SOMECLASS>
struct LLRemovePointer< LLPointer<SOMECLASS> >
{
typedef SOMECLASS type;
};
#endif /* ! defined(LL_LLPTRTO_H) */

View File

@@ -38,7 +38,7 @@
//============================================================================ //============================================================================
// MAIN THREAD // MAIN THREAD
LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) : LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) :
LLThread(name), LLThread(name),
mThreaded(threaded), mThreaded(threaded),
mIdleThread(TRUE), mIdleThread(TRUE),
@@ -47,6 +47,11 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
{ {
if (mThreaded) if (mThreaded)
{ {
if(should_pause)
{
pause() ; //call this before start the thread.
}
start(); start();
} }
} }

Some files were not shown because too many files have changed in this diff Show More