diff --git a/.hgtags b/.hgtags old mode 100644 new mode 100755 index e7be33a842..c3a04c1d28 --- a/.hgtags +++ b/.hgtags @@ -331,9 +331,65 @@ f7cbd60a3f57ff1101157eeb79ea21e8898bedae DRTVWR-235 baf97f06ae17223614c5e31aa42e71d87cff07fe DRTVWR-236 18498afcdb835d6fc4d36ed935347d3b65307bad 3.4.1-beta11 b2f21e3442542283a80e7eaebae9f833e5a927b6 DRTVWR-237 -853a202426398cb0b7676aa498603a25d8ad20fb 3.4.1-beta12 -853a202426398cb0b7676aa498603a25d8ad20fb 3.4.1-beta12 -b6b68f3c2c6dd04ad88bd0575aad67bf87a9c108 3.4.1-beta12 -b6b68f3c2c6dd04ad88bd0575aad67bf87a9c108 3.4.1-beta12 3f9be82de642d468c5fc272cb9d96b46b5498402 3.4.1-beta12 e59ffd3fe0838ae6b09b242a6e9df71761b88f41 3.4.1-release +81f6b745ef27f5915fd07f988fdec9944f2bb73e DRTVWR-186 +cc953f00956be52cc64c30637bbeec310eea603f DRTVWR-181 +c04e68e1b0034fd0a20815ae24c77e5f8428e822 DRTVWR-188 +4b2c52aecb7a75de31dbb12d9f5b9a251d8707be DRTVWR-191 +78ca0bbf43a92e8914d4cfa87d69a6717ef7d4cf DRTVWR-194 +248f4acd92a706c79e842bc83d80baa7369c0c2e DRTVWR-203 +de3be913f68813a9bac7d1c671fef96d1159bcd6 DRTVWR-202 +34dbbe2b00afe90352d3acf8290eb10ab90d1c8b oz-build-test-tag +6ee71714935ffcd159db3d4f5800c1929aac54e1 DRTVWR-205 +7b22c612fc756e0ea63b10b163e81d107f85dbf8 DRTVWR-206 +b61afe175b829c149d369524a4e974dfda99facf DRTVWR-219 +32896d5e920ca9a29256ff3b747c2e99752aa5ae DRTVWR-217 +704bbae7b182a1f2811a47a054e680522966f54a 3.4.2-beta1 +288539fc0408ed4b69a99665de33bbbc2c3c08fe DRTVWR-216 +e664473c16df1d82ffaff382e7b3e023da202d52 3.4.2-beta2 +0891d7a773a31397dcad48be3fa66531d567a821 DRTVWR-242 +710785535362b3cb801b6a3dc4703be3373bd0cd 3.4.2-beta3 +e9a5886052433d5db9e504ffaca10890f9932979 DRTVWR-243 +73b84b9864dc650fe7c8fc9f52361450f0849004 3.4.2-beta4 +16310aabccf315870f7cc9bf966926c0ad6954fa 3.4.2-release +d799593b53ed733862e9a13871e318e886469377 DRTVWR-208 +e497dcde7a3653e384eb223a8a460030e89c294c DRTVWR-223 +93ab02d83f51e30a3cabad98aff89601befd9413 DRTVWR-240 +2aa72e3372a83dece4df9cf72fb1e7c34f90b5e3 DRTVWR-209 +f7bedce18ad52283e6072814db23318907261487 DRTVWR-238 +7b64c96fbcadf360bd2feaae19d330166b70877c DRTVWR-210 +5e4e4128b256525bafc07a62e35ae8527aaa9c9d DRTVWR-241 +f1d3b3fcab28ed9ea532bf50db0ba96f5c8cc8e9 DRTVWR-232 +4918b150e75df6b516fb6c2616d32043fa6b4cac DRTVWR-245 +94ab2b49458ab372a95d2d6949fdf574f413068d 3.4.3-beta1 +965b9a35e260c0f53be1a25f0db7abc8a67eaf47 DRTVWR-252 +bb10adc4f76cf0067fca7075146f00cdc0740e9d DRTVWR-251 +ab0aa2f6ba22b52fed30a2337197f589156edc75 DRTVWR-253 +48382ec79741671d19ce4cc3e8cd59e9a521e4a7 DRTVWR-254 +937ec902bb9a1cbceff17bd89e3923352b0a5fbc DRTVWR-256 +44e764a6ac9e672a4f3bce821a4b6a218590c374 DRTVWR-258 +c23d734065ed593b2413385aecd8366d8e0ee96b DRTVWR-257 +452ce96d4046dc05a3ecaecc203e2cc8ddd72e76 DRTVWR-259 +daca610d840625b5bebb966a57cb49581852c417 DRTVWR-265 +9afbdc4e24cc04feacfb2b7a10b78a64f780901a DRTVWR-266 +73280db02501f5ad041fc18b1eba68e73a81996c DRTVWR-267 +870e2d79e0063fda87187f17bbc2747766733194 3.4.3-beta3 +0a2ca6546b499239afeb66d17b2fadbcdbe36ab1 3.4.3-release +4c3460cb1fb7c6da9965e09c734d282a8e9c81f0 DRTVWR-229 +f4481df42f9a4a92bf475a80f0c51d1a4bbdfd59 DRTVWR-246 +39c5204b6e800983a41ccac8ad6dc993120197c6 DRTVWR-247 +7c7d57d393e8ae7b61623279de06eb4a62ccae6a DRTVWR-249 +f72b50ef168c159d6e79e97aa2bcafaf8577ab99 DRTVWR-230 +b418be80903520c492e1173f3afbc4021cad5d07 DRTVWR-255 +9aa1aa9f1fe13c194695a0b8f0af298296241dc2 DRTVWR-260 +84fbaf2d4141bd161731430e760949dc787ca206 DRTVWR-244 +083d2d36b5bb1c54fc3dd7caac0e7ac381a9cef0 3.4.4-beta1 +b634dec987c16e8c9c938e11e52591d9ead8fa9b DRTVWR-270 +cd39255bd23330fd30c04105f2811e941d8524fe 3.4.4-beta2 +2c4011bbc2b15b82198fd8b51f3a9fe765a08c4d DRTVWR-271 +2f8a3ef687bc55828abcb17ac1ad7cde70536d7e 3.4.4-beta3 +35cfd4cf5b895fa776592f2e630e330be7f0604e DRTVWR-273 +c374035d459af3c03dea2dd90880dfc25de64706 DRTVWR-275 +05d9f1dd7a954069af2a33abedb7713fa36a04cb 3.4.4-beta4 +e1bb1ae7d8b12faeb37933a737c199cc9b9f89cc 3.4.4-release diff --git a/BuildParams b/BuildParams index ecf15f3f2a..1ecf0bf836 100644 --- a/BuildParams +++ b/BuildParams @@ -21,13 +21,8 @@ email_status_this_is_os = true # Limit extent of codeticket updates to revisions after... codeticket_since = 3.3.0-release -clean_on_success = false -run_tests = false -build_Darwin_Debug = false -build_Darwin_RelWithDebInfo = false - # ======================================== -# Viewer Development -- +# Viewer Development # ======================================== # Report changes since... @@ -64,6 +59,7 @@ viewer-release.build_debug_release_separately = true viewer-release.build_viewer_update_version_manager = true viewer-release.codeticket_add_context = false + # ======================================== # mesh-development # ======================================== @@ -120,17 +116,6 @@ viewer-mesh.login_channel = "Project Viewer - Mesh" viewer-mesh.viewer_grid = aditi viewer-mesh.email = shining@lists.lindenlab.com -# ======================================== -# viewer-adult-check -# ======================================== - -viewer-adult-check.viewer_channel = "Project Viewer - AdultCheck" -viewer-adult-check.login_channel = "Project Viewer - AdultCheck" -viewer-adult-check.viewer_grid = agni -viewer-adult-check.build_debug_release_separately = true -viewer-adult-check.build_CYGWIN_Debug = false -viewer-adult-check.build_viewer_update_version_manager = false - # ======================================== # viewer-pathfinding # ======================================== @@ -205,4 +190,5 @@ runway.build_debug_release_separately = true runway.build_CYGWIN_Debug = false runway.build_viewer_update_version_manager = false + # eof diff --git a/autobuild.xml b/autobuild.xml index 286e4e28de..5180a60595 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -186,9 +186,9 @@ archive hash - d98078791ce345bf6168ce9ba53ca2d7 + ac37d0038c91b0672fa31a02731f0eac url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/222752/arch/Darwin/installer/boost-1.45.0-darwin-20110304.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/268347/arch/Darwin/installer/boost-1.52.0-darwin-20121218.tar.bz2 name darwin @@ -198,9 +198,9 @@ archive hash - a34e7fffdb94a6a4d8a2966b1f216da3 + 146ed8a8c2ef8ab3f0a6c4f214fc5c22 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-linux-20110310.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/268347/arch/Linux/installer/boost-1.52.0-linux-20121218.tar.bz2 name linux @@ -210,9 +210,9 @@ archive hash - 98be22c8833aa2bca184b9fa09fbb82b + 3ea60f17d986b7e8a3351298734bdca4 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-windows-20110124.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/268347/arch/CYGWIN/installer/boost-1.52.0-windows-20121218.tar.bz2 name windows @@ -918,9 +918,9 @@ archive hash - efaf5cb3e861d44518eb03f4c406f03c + e6feee3b452c2f70ce8558e30d6bc10a url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/falcon_3p-havok-source/rev/261536/arch/Darwin/installer/havok_source-2012.1-darwin-20120710.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/lindenlab_3p-havok-source/rev/268409/arch/Darwin/installer/havok_source-2012.1-darwin-20121219.tar.bz2 name darwin @@ -930,9 +930,9 @@ archive hash - 50037fff3fd3356a073cdae88348c9ab + 0c0d2058ba48446e274d6595d1d8063e url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/falcon_3p-havok-source/rev/261536/arch/Linux/installer/havok_source-2012.1-linux-20120711.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/lindenlab_3p-havok-source/rev/268409/arch/Linux/installer/havok_source-2012.1-linux-20121219.tar.bz2 name linux @@ -942,9 +942,9 @@ archive hash - cd6638f5a03469654615730c16889a60 + 88391b6e08d473506d406ca6f3e88cfb url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/falcon_3p-havok-source/rev/261536/arch/CYGWIN/installer/havok_source-2012.1-windows-20120710.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/lindenlab_3p-havok-source/rev/268409/arch/CYGWIN/installer/havok_source-2012.1-windows-20121219.tar.bz2 name windows @@ -1062,9 +1062,9 @@ archive hash - d91e1f483209cd3eba04135c6a59e829 + a5b2dff0d97b643227a58473e5c57906 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/221672/arch/Darwin/installer/kdu-6.4.1-darwin-20110218.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/256978/arch/Darwin/installer/kdu-7.0.0-darwin-20120515.tar.bz2 name darwin @@ -1086,9 +1086,9 @@ archive hash - 6cd9f36465ef73a3df34bf2b3bba2ced + 6d80d35524e1c0c32d3385014d02d48c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/221672/arch/CYGWIN/installer/kdu-6.4.1-windows-20110218.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/256978/arch/CYGWIN/installer/kdu-7.0.0-windows-20120515.tar.bz2 name windows @@ -2224,6 +2224,8 @@ -configuration Release -project SecondLife.xcodeproj + -DENABLE_SIGNING:BOOL=YES + -DSIGNING_IDENTITY:STRING="Developer ID Application: Linden Research, Inc." configure @@ -2251,6 +2253,8 @@ -configuration Release -project SecondLife.xcodeproj + -DENABLE_SIGNING:BOOL=YES + -DSIGNING_IDENTITY:STRING="Developer ID Application: Linden Research, Inc." configure @@ -2520,6 +2524,18 @@ configure + arguments + + ..\indra + && + ..\indra\tools\vstool\VSTool.exe + --solution + SecondLife.sln + --config + Debug + --startup + secondlife-bin + options -G @@ -2611,6 +2627,18 @@ configure + arguments + + ..\indra + && + ..\indra\tools\vstool\VSTool.exe + --solution + SecondLife.sln + --config + RelWithDebInfo + --startup + secondlife-bin + options -G @@ -2687,6 +2715,18 @@ configure + arguments + + ..\indra + && + ..\indra\tools\vstool\VSTool.exe + --solution + SecondLife.sln + --config + Release + --startup + secondlife-bin + options -G diff --git a/doc/contributions.txt b/doc/contributions.txt index df504e4a8a..f53d9f5cfd 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -174,6 +174,7 @@ Ansariel Hiller VWR-26150 STORM-1685 STORM-1713 + STORM-1899 Aralara Rajal Ardy Lay STORM-859 @@ -320,6 +321,7 @@ Dale Glass VWR-1358 VWR-2041 Darien Caldwell + SH-3055 Dartagan Shepherd Debs Regent Decro Schmooz @@ -400,6 +402,7 @@ Gaberoonie Zanzibar Ganymedes Costagravas Geenz Spad STORM-1823 + STORM-1900 Gene Frostbite GeneJ Composer Geneko Nemeth @@ -636,6 +639,7 @@ Jonathan Yap STORM-1809 STORM-1793 STORM-1810 + STORM-1877 STORM-1860 STORM-1852 STORM-1870 @@ -644,6 +648,7 @@ Jonathan Yap STORM-1862 Kadah Coba STORM-1060 + STORM-1843 Jondan Lundquist Josef Munster Josette Windlow @@ -654,6 +659,8 @@ Kage Pixel VWR-11 Kagehi Kohn Kaimen Takahe +Katharine Berry + STORM-1900 Keklily Longfall Ken Lavender Ken March @@ -739,6 +746,7 @@ Marianne McCann Marine Kelley STORM-281 MartinRJ Fayray + STORM-1844 STORM-1845 Matthew Anthony Matthew Dowd @@ -883,6 +891,10 @@ Nicholaz Beresford VWR-2682 VWR-2684 Nick Rhodes +NickyD + MAINT-873 +Nicky Dasmijn + VWR-29228 Nicky Perian OPEN-1 STORM-1087 @@ -1050,9 +1062,12 @@ Shawn Kaufmat SNOW-240 Sheet Spotter Shnurui Troughton +Shyotl Kuhr + MAINT-1138 Siana Gearz STORM-960 STORM-1088 + MAINT-1138 sicarius Thorne Sicarius Toxx SignpostMarv Martin @@ -1108,10 +1123,14 @@ Sudane Erato Synystyr Texan Takeda Terrawyng TankMaster Finesmith + OPEN-140 + OPEN-142 STORM-1100 - STORM-1602 STORM-1258 + STORM-1602 + STORM-1868 VWR-26622 + VWR-29224 Talamasca Tali Rosca Tayra Dagostino @@ -1228,6 +1247,9 @@ Watty Berkson Westley Schridde Westley Streeter Whimsy Winx +Whirly Fizzle + STORM-1895 + MAINT-873 Whoops Babii VWR-631 VWR-1640 @@ -1256,6 +1278,8 @@ Whoops Babii Winter Ventura Wilton Lundquist VWR-7682 +Wolf Loonie + STORM-1868 WolfPup Lowenhar OPEN-1 OPEN-37 diff --git a/etc/message.xml b/etc/message.xml index 7b133ab205..6d8160abb5 100644 --- a/etc/message.xml +++ b/etc/message.xml @@ -385,14 +385,6 @@ trusted-sender true - - ParcelMediaURLFilter - - flavor - llsd - trusted-sender - false - ParcelNavigateMedia diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 9b775769de..93dc1aea54 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -43,6 +43,7 @@ add_subdirectory(cmake) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) +add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) add_subdirectory(${LIBS_OPEN_PREFIX}llkdu) add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 666c94af86..452fd5f356 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -51,6 +51,7 @@ if (WINDOWS) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP /Ob2 -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0" CACHE STRING "C++ compiler release options" FORCE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") set(CMAKE_CXX_STANDARD_LIBRARIES "") set(CMAKE_C_STANDARD_LIBRARIES "") @@ -206,6 +207,10 @@ if (DARWIN) # NOTE: it's critical to have both CXX_FLAGS and C_FLAGS covered. set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O0 ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O0 ${CMAKE_C_FLAGS_RELWITHDEBINFO}") + if (XCODE_VERSION GREATER 4.2) + set(ENABLE_SIGNING TRUE) + set(SIGNING_IDENTITY "Developer ID Application: Linden Research, Inc.") + endif (XCODE_VERSION GREATER 4.2) endif (DARWIN) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 2135f0584c..1acb2bbbfd 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -12,12 +12,13 @@ if (STANDALONE) set(BOOST_SIGNALS_LIBRARY boost_signals-mt) set(BOOST_SYSTEM_LIBRARY boost_system-mt) set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) + set(BOOST_THREAD_LIBRARY boost_thread-mt) else (STANDALONE) use_prebuilt_binary(boost) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) + set(BOOST_VERSION "1.52") if (WINDOWS) - set(BOOST_VERSION 1_45) if(MSVC80) set(BOOST_PROGRAM_OPTIONS_LIBRARY optimized libboost_program_options-vc80-mt-${BOOST_VERSION} @@ -37,22 +38,55 @@ else (STANDALONE) else(MSVC80) # MSVC 10.0 config set(BOOST_PROGRAM_OPTIONS_LIBRARY - optimized libboost_program_options-vc100-mt-${BOOST_VERSION} - debug libboost_program_options-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_program_options-mt + debug libboost_program_options-mt-gd) set(BOOST_REGEX_LIBRARY - optimized libboost_regex-vc100-mt-${BOOST_VERSION} - debug libboost_regex-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_regex-mt + debug libboost_regex-mt-gd) set(BOOST_SYSTEM_LIBRARY - optimized libboost_system-vc100-mt-${BOOST_VERSION} - debug libboost_system-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_system-mt + debug libboost_system-mt-gd) set(BOOST_FILESYSTEM_LIBRARY - optimized libboost_filesystem-vc100-mt-${BOOST_VERSION} - debug libboost_filesystem-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_filesystem-mt + debug libboost_filesystem-mt-gd) + set(BOOST_THREAD_LIBRARY + optimized libboost_thread-mt + debug libboost_thread-mt-gd) endif (MSVC80) - elseif (DARWIN OR LINUX) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options) - set(BOOST_REGEX_LIBRARY boost_regex) - set(BOOST_SYSTEM_LIBRARY boost_system) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem) + elseif (LINUX) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) + elseif (DARWIN) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) endif (WINDOWS) endif (STANDALONE) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 9f05c4cff2..c32e357da3 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -254,6 +254,12 @@ elseif(LINUX) libapr-1.so.0 libaprutil-1.so.0 libatk-1.0.so + libboost_program_options-mt.so.${BOOST_VERSION}.0 + libboost_regex-mt.so.${BOOST_VERSION}.0 + libboost_thread-mt.so.${BOOST_VERSION}.0 + libboost_filesystem-mt.so.${BOOST_VERSION}.0 + libboost_signals-mt.so.${BOOST_VERSION}.0 + libboost_system-mt.so.${BOOST_VERSION}.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 diff --git a/indra/cmake/GooglePerfTools.cmake b/indra/cmake/GooglePerfTools.cmake index 09501e0406..73b3642ae6 100644 --- a/indra/cmake/GooglePerfTools.cmake +++ b/indra/cmake/GooglePerfTools.cmake @@ -3,7 +3,7 @@ include(Prebuilt) # If you want to enable or disable TCMALLOC in viewer builds, this is the place. # set ON or OFF as desired. -set (USE_TCMALLOC ON) +set (USE_TCMALLOC OFF) if (STANDALONE) include(FindGooglePerfTools) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake old mode 100755 new mode 100644 index 543075db5b..21a0c8a9ca --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -201,19 +201,13 @@ FUNCTION(LL_ADD_INTEGRATION_TEST endif(TEST_DEBUG) ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files}) SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") - if (WINDOWS) - set_target_properties(INTEGRATION_TEST_${testname} - PROPERTIES - LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc" - LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO" - LINK_FLAGS_RELEASE "" - ) - endif(WINDOWS) if(STANDALONE) SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES COMPILE_FLAGS -I"${TUT_INCLUDE_DIR}") endif(STANDALONE) + # The following was copied to llcorehttp/CMakeLists.txt's texture_load target. + # Any changes made here should be replicated there. if (WINDOWS) SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index d4694ad37a..8f7bb296ce 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -24,7 +24,7 @@ endif (LINUX) add_definitions(${TCMALLOC_FLAG}) -set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a shared library.") +set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a static library.") if(LLCOMMON_LINK_SHARED) add_definitions(-DLL_COMMON_LINK_SHARED=1) endif(LLCOMMON_LINK_SHARED) diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake new file mode 100644 index 0000000000..61e4b23d98 --- /dev/null +++ b/indra/cmake/LLCoreHttp.cmake @@ -0,0 +1,16 @@ +# -*- cmake -*- + +include(CARes) +include(CURL) +include(OpenSSL) +include(Boost) + +set(LLCOREHTTP_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llcorehttp + ${CARES_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIRS} + ${BOOST_INCLUDE_DIRS} + ) + +set(LLCOREHTTP_LIBRARIES llcorehttp) diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake index f15a2c2649..ab39cbb6be 100644 --- a/indra/cmake/LLPrimitive.cmake +++ b/indra/cmake/LLPrimitive.cmake @@ -15,10 +15,10 @@ if (WINDOWS) optimized llprimitive debug libcollada14dom22-d optimized libcollada14dom22 - debug libboost_filesystem-vc100-mt-gd-1_45 - optimized libboost_filesystem-vc100-mt-1_45 - debug libboost_system-vc100-mt-gd-1_45 - optimized libboost_system-vc100-mt-1_45 + debug libboost_filesystem-mt-gd + optimized libboost_filesystem-mt + debug libboost_system-mt-gd + optimized libboost_system-mt ) else (WINDOWS) set(LLPRIMITIVE_LIBRARIES diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 56ced20abf..4b459f1a48 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -99,10 +99,20 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(DARWIN 1) + execute_process( + COMMAND sh -c "xcodebuild -version | grep Xcode | cut -d ' ' -f2 | cut -d'.' -f1-2" + OUTPUT_VARIABLE XCODE_VERSION ) + # To support a different SDK update these Xcode settings: - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5) + if (XCODE_VERSION GREATER 4.2) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.6) + else (XCODE_VERSION GREATER 4.2) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5) + endif (XCODE_VERSION GREATER 4.2) + set(CMAKE_OSX_SYSROOT macosx10.6) set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvmgcc42") + set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT dwarf-with-dsym) # NOTE: To attempt an i386/PPC Universal build, add this on the configure line: @@ -134,6 +144,11 @@ set(VIEWER ON CACHE BOOL "Build Second Life viewer.") set(VIEWER_CHANNEL "LindenDeveloper" CACHE STRING "Viewer Channel Name") set(VIEWER_LOGIN_CHANNEL ${VIEWER_CHANNEL} CACHE STRING "Fake login channel for A/B Testing") +if (XCODE_VERSION GREATER 4.2) + set(ENABLE_SIGNING OFF CACHE BOOL "Enable signing the viewer") + set(SIGNING_IDENTITY "" CACHE STRING "Specifies the signing identity to use, if necessary.") +endif (XCODE_VERSION GREATER 4.2) + set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside") set(STANDALONE OFF CACHE BOOL "Do not use Linden-supplied prebuilt libraries.") set(UNATTENDED OFF CACHE BOOL "Should be set to ON for building with VC Express editions.") diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt index e69de29bb2..0f6a8b8a1d 100644 --- a/indra/edit-me-to-trigger-new-build.txt +++ b/indra/edit-me-to-trigger-new-build.txt @@ -0,0 +1 @@ +Wed Nov 7 00:25:19 UTC 2012 diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp index 36c5b67826..034c816742 100644 --- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp @@ -240,7 +240,7 @@ void store_input_file(std::list &input_filenames, const std::string LLDirIterator iter(dir, name); while (iter.next(next_name)) { - std::string file_name = dir + gDirUtilp->getDirDelimiter() + next_name; + std::string file_name = gDirUtilp->add(dir, next_name); input_filenames.push_back(file_name); } } diff --git a/indra/integration_tests/llui_libtest/llui_libtest.cpp b/indra/integration_tests/llui_libtest/llui_libtest.cpp index 217e26c3ca..38aa1bbeb2 100644 --- a/indra/integration_tests/llui_libtest/llui_libtest.cpp +++ b/indra/integration_tests/llui_libtest/llui_libtest.cpp @@ -107,12 +107,6 @@ public: }; TestImageProvider gTestImageProvider; -static std::string get_xui_dir() -{ - std::string delim = gDirUtilp->getDirDelimiter(); - return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim; -} - void init_llui() { // Font lookup needs directory support @@ -122,13 +116,12 @@ void init_llui() const char* newview_path = "../../../newview"; #endif gDirUtilp->initAppDirs("SecondLife", newview_path); - gDirUtilp->setSkinFolder("default"); + gDirUtilp->setSkinFolder("default", "en"); // colors are no longer stored in a LLControlGroup file LLUIColorTable::instance().loadFromSettings(); - std::string config_filename = gDirUtilp->getExpandedFilename( - LL_PATH_APP_SETTINGS, "settings.xml"); + std::string config_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings.xml"); gSavedSettings.loadFromFile(config_filename); // See LLAppViewer::init() @@ -143,9 +136,7 @@ void init_llui() const bool no_register_widgets = false; LLWidgetReg::initClass( no_register_widgets ); - - // Unclear if this is needed - LLUI::setupPaths(); + // Otherwise we get translation warnings when setting up floaters // (tooltips for buttons) std::set default_args; @@ -157,7 +148,6 @@ void init_llui() // otherwise it crashes. LLFontGL::initClass(96.f, 1.f, 1.f, gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths(), false ); // don't create gl textures LLFloaterView::Params fvparams; @@ -169,6 +159,14 @@ void init_llui() gFloaterView = LLUICtrlFactory::create (fvparams); } +/*==========================================================================*| +static std::string get_xui_dir() +{ + std::string delim = gDirUtilp->getDirDelimiter(); + return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim; +} + +// buildFromFile() no longer supports generate-output-LLXMLNode void export_test_floaters() { // Convert all test floaters to new XML format @@ -191,7 +189,7 @@ void export_test_floaters() floater->buildFromFile( filename, // FALSE, // don't open floater output_node); - std::string out_filename = xui_dir + filename; + std::string out_filename = gDirUtilp->add(xui_dir, filename); std::string::size_type extension_pos = out_filename.rfind(".xml"); out_filename.resize(extension_pos); out_filename += "_new.xml"; @@ -203,6 +201,7 @@ void export_test_floaters() fclose(floater_file); } } +|*==========================================================================*/ int main(int argc, char** argv) { @@ -211,7 +210,7 @@ int main(int argc, char** argv) init_llui(); - export_test_floaters(); +// export_test_floaters(); return 0; } diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index a4fb77357c..97cc31bba0 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -167,7 +167,12 @@ ARGUMENTS=[ dict(name='version', description="""This specifies the version of Second Life that is being packaged up.""", - default=get_default_version) + default=get_default_version), + dict(name='signature', + description="""This specifies an identity to sign the viewer with, if any. + If no value is supplied, the default signature will be used, if any. Currently + only used on Mac OS X.""", + default=None) ] def usage(srctree=""): @@ -621,6 +626,23 @@ class LLManifest(object): d = src_re.sub(d_template, s.replace('\\', '/')) yield os.path.normpath(s), os.path.normpath(d) + def path2basename(self, path, file): + """ + It is a common idiom to write: + self.path(os.path.join(somedir, somefile), somefile) + + So instead you can write: + self.path2basename(somedir, somefile) + + Note that this is NOT the same as: + self.path(os.path.join(somedir, somefile)) + + which is the same as: + temppath = os.path.join(somedir, somefile) + self.path(temppath, temppath) + """ + return self.path(os.path.join(path, file), file) + def path(self, src, dst=None): sys.stdout.write("Processing %s => %s ... " % (src, dst)) sys.stdout.flush() @@ -666,6 +688,10 @@ class LLManifest(object): print "%d files" % count + # Let caller check whether we processed as many files as expected. In + # particular, let caller notice 0. + return count + def do(self, *actions): self.actions = actions self.construct() diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp index 277f0a5367..991dfd9dce 100644 --- a/indra/linux_updater/linux_updater.cpp +++ b/indra/linux_updater/linux_updater.cpp @@ -251,7 +251,7 @@ std::string next_image_filename(std::string& image_path, LLDirIterator& iter) { std::string image_filename; iter.next(image_filename); - return image_path + "/" + image_filename; + return gDirUtilp->add(image_path, image_filename); } void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data) diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 72c0091d17..ef560cd7fc 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -1792,5 +1792,3 @@ bool LLAudioData::load() mBufferp->mAudioDatap = this; return true; } - - diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp index 66b3c2bd25..0d0b85ba60 100644 --- a/indra/llcharacter/lleditingmotion.cpp +++ b/indra/llcharacter/lleditingmotion.cpp @@ -214,8 +214,10 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask) target = target * target_dist; if (!target.isFinite()) { - llerrs << "Non finite target in editing motion with target distance of " << target_dist << + // Don't error out here, set a fail-safe target vector + llwarns << "Non finite target in editing motion with target distance of " << target_dist << " and focus point " << focus_pt << llendl; + target.setVec(1.f, 1.f, 1.f); } mTarget.setPosition( target + mParentJoint.getPosition()); diff --git a/indra/llcharacter/llhandmotion.cpp b/indra/llcharacter/llhandmotion.cpp index 63937d8255..696dba0d95 100644 --- a/indra/llcharacter/llhandmotion.cpp +++ b/indra/llcharacter/llhandmotion.cpp @@ -132,18 +132,68 @@ BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask) { if (mNewPose != HAND_POSE_RELAXED && mNewPose != mCurrentPose) { - mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f); + // Only set param weight for poses other than + // default (HAND_POSE_SPREAD); HAND_POSE_SPREAD + // is not an animatable morph! + if (mNewPose != HAND_POSE_SPREAD) + { + mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f); + } + + // Reset morph weight for current pose back to its + // full extend or it might be stuck somewhere in the middle if a + // pose is requested and the old pose is requested again shortly + // after while still blending to the other pose! + if (mCurrentPose != HAND_POSE_SPREAD) + { + mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f); + } + + // Update visual params now if we won't blend + if (mCurrentPose == HAND_POSE_RELAXED) + { + mCharacter->updateVisualParams(); + } } mNewPose = HAND_POSE_RELAXED; } else { - // this is a new morph we didn't know about before - if (*requestedHandPose != mNewPose && mNewPose != mCurrentPose && mNewPose != HAND_POSE_SPREAD) + // Sometimes we seem to get garbage here, with poses that are out of bounds. + // So check for a valid pose first. + if (*requestedHandPose >= 0 && *requestedHandPose < NUM_HAND_POSES) { - mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f); + // This is a new morph we didn't know about before: + // Reset morph weight for both current and new pose + // back their starting values while still blending. + if (*requestedHandPose != mNewPose && mNewPose != mCurrentPose) + { + if (mNewPose != HAND_POSE_SPREAD) + { + mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f); + } + + // Reset morph weight for current pose back to its full extend + // or it might be stuck somewhere in the middle if a pose is + // requested and the old pose is requested again shortly after + // while still blending to the other pose! + if (mCurrentPose != HAND_POSE_SPREAD) + { + mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f); + } + + // Update visual params now if we won't blend + if (mCurrentPose == *requestedHandPose) + { + mCharacter->updateVisualParams(); + } + } + mNewPose = *requestedHandPose; + } + else + { + llwarns << "Requested hand pose out of range. Ignoring requested pose." << llendl; } - mNewPose = *requestedHandPose; } mCharacter->removeAnimationData("Hand Pose"); diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index dd7b8c6eb8..66e2bc9095 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -116,6 +116,7 @@ set(llcommon_HEADER_FILES bitpack.h ctype_workaround.h doublelinkedlist.h + fix_macros.h imageids.h indra_constants.h linden_common.h @@ -174,6 +175,7 @@ set(llcommon_HEADER_FILES llfoldertype.h llformat.h llframetimer.h + llhandle.h llhash.h llheartbeat.h llhttpstatuscodes.h diff --git a/indra/llcommon/fix_macros.h b/indra/llcommon/fix_macros.h new file mode 100644 index 0000000000..ef959decff --- /dev/null +++ b/indra/llcommon/fix_macros.h @@ -0,0 +1,25 @@ +/** + * @file fix_macros.h + * @author Nat Goodspeed + * @date 2012-11-16 + * @brief The Mac system headers seem to #define macros with obnoxiously + * generic names, preventing any library from using those names. We've + * had to fix these in so many places that it's worth making a header + * file to handle it. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Copyright (c) 2012, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// DON'T use an #include guard: every time we encounter this header, #undef +// these macros all over again. + +// who injects MACROS with such generic names?! Grr. +#ifdef equivalent +#undef equivalent +#endif + +#ifdef check +#undef check +#endif diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index 0745696ef3..0da83720bd 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -62,6 +62,7 @@ enum LAND_STAT_FLAGS STAT_FILTER_BY_PARCEL = 0x00000001, STAT_FILTER_BY_OWNER = 0x00000002, STAT_FILTER_BY_OBJECT = 0x00000004, + STAT_FILTER_BY_PARCEL_NAME = 0x00000008, STAT_REQUEST_LAST_ENTRY = 0x80000000, }; diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index af33ce666f..034546c3f3 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -168,7 +168,7 @@ public: void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); } void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); } Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++ - Type operator --(int) { return apr_atomic_dec32(&mData); } // Type-- + Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise) private: apr_uint32_t mData; diff --git a/indra/llui/llhandle.h b/indra/llcommon/llhandle.h similarity index 67% rename from indra/llui/llhandle.h rename to indra/llcommon/llhandle.h index 37c657dd92..6af5e198d6 100644 --- a/indra/llui/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -31,6 +31,10 @@ #include #include +/** + * Helper object for LLHandle. Don't instantiate these directly, used + * exclusively by LLHandle. + */ class LLTombStone : public LLRefCount { public: @@ -42,15 +46,37 @@ private: mutable void* mTarget; }; -// LLHandles are used to refer to objects whose lifetime you do not control or influence. -// Calling get() on a handle will return a pointer to the referenced object or NULL, -// if the object no longer exists. Note that during the lifetime of the returned pointer, -// you are assuming that the object will not be deleted by any action you perform, -// or any other thread, as normal when using pointers, so avoid using that pointer outside of -// the local code block. -// -// https://wiki.lindenlab.com/mediawiki/index.php?title=LLHandle&oldid=79669 - +/** + * LLHandles are used to refer to objects whose lifetime you do not control or influence. + * Calling get() on a handle will return a pointer to the referenced object or NULL, + * if the object no longer exists. Note that during the lifetime of the returned pointer, + * you are assuming that the object will not be deleted by any action you perform, + * or any other thread, as normal when using pointers, so avoid using that pointer outside of + * the local code block. + * + * https://wiki.lindenlab.com/mediawiki/index.php?title=LLHandle&oldid=79669 + * + * The implementation is like some "weak pointer" implementations. When we + * can't control the lifespan of the referenced object of interest, we can + * still instantiate a proxy object whose lifespan we DO control, and store in + * the proxy object a dumb pointer to the actual target. Then we just have to + * ensure that on destruction of the target object, the proxy's dumb pointer + * is set NULL. + * + * LLTombStone is our proxy object. LLHandle contains an LLPointer to the + * LLTombStone, so every copy of an LLHandle increments the LLTombStone's ref + * count as usual. + * + * One copy of the LLHandle, specifically the LLRootHandle, must be stored in + * the referenced object. Destroying the LLRootHandle is what NULLs the + * proxy's target pointer. + * + * Minor optimization: we want LLHandle's mTombStone to always be a valid + * LLPointer, saving some conditionals in dereferencing. That's the + * getDefaultTombStone() mechanism. The default LLTombStone object's target + * pointer is always NULL, so it's semantically identical to allowing + * mTombStone to be invalid. + */ template class LLHandle { @@ -108,6 +134,14 @@ private: } }; +/** + * LLRootHandle isa LLHandle which must be stored in the referenced object. + * You can either store it directly and explicitly bind(this), or derive from + * LLHandleProvider (q.v.) which automates that for you. The essential point + * is that destroying the LLRootHandle (as a consequence of destroying the + * referenced object) calls unbind(), setting the LLTombStone's target pointer + * NULL. + */ template class LLRootHandle : public LLHandle { @@ -144,8 +178,10 @@ private: LLRootHandle(const LLRootHandle& other) {}; }; -// Use this as a mixin for simple classes that need handles and when you don't -// want handles at multiple points of the inheritance hierarchy +/** + * Use this as a mixin for simple classes that need handles and when you don't + * want handles at multiple points of the inheritance hierarchy + */ template class LLHandleProvider { diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index 99983a19cb..9a6d1eff5c 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -35,7 +35,7 @@ #include #include "llerror.h" -#include "lltypeinfolookup.h" +#include "llstl.h" namespace LLInitParam { @@ -212,14 +212,6 @@ namespace LLInitParam public: - struct CompareTypeID - { - bool operator()(const std::type_info* lhs, const std::type_info* rhs) const - { - return lhs->before(*rhs); - } - }; - typedef std::vector > name_stack_t; typedef std::pair name_stack_range_t; typedef std::vector possible_values_t; @@ -228,9 +220,9 @@ namespace LLInitParam typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&); typedef boost::function parser_inspect_func_t; - typedef LLTypeInfoLookup parser_read_func_map_t; - typedef LLTypeInfoLookup parser_write_func_map_t; - typedef LLTypeInfoLookup parser_inspect_func_map_t; + typedef std::map parser_read_func_map_t; + typedef std::map parser_write_func_map_t; + typedef std::map parser_inspect_func_map_t; Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map) : mParseSilently(false), diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 36d7f7a44c..853c427a13 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -31,30 +31,16 @@ #include #include "llsingleton.h" -#include "lltypeinfolookup.h" +#include "llstl.h" template -class LLRegistryDefaultComparator +struct LLRegistryDefaultComparator { - bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; } -}; - -template -struct LLRegistryMapSelector -{ - typedef std::map type; -}; - -template -struct LLRegistryMapSelector -{ - typedef LLTypeInfoLookup type; -}; - -template -struct LLRegistryMapSelector -{ - typedef LLTypeInfoLookup type; + bool operator()(const T& lhs, const T& rhs) const + { + using std::less; + return less()(lhs, rhs); + } }; template > @@ -72,7 +58,7 @@ public: { friend class LLRegistry; public: - typedef typename LLRegistryMapSelector::type registry_map_t; + typedef std::map registry_map_t; bool add(ref_const_key_t key, ref_const_value_t value) { diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 7f4f670ed0..6b549e4b6f 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -1451,9 +1451,12 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option } case LLSD::TypeUUID: + { ostr.put('u'); - ostr.write((const char*)(&(data.asUUID().mData)), UUID_BYTES); + LLSD::UUID value = data.asUUID(); + ostr.write((const char*)(&value.mData), UUID_BYTES); break; + } case LLSD::TypeString: ostr.put('s'); diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 8ad12c9a03..d3941e1bc9 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -33,6 +33,7 @@ #include #include #include +#include // Use to compare the first element only of a pair // e.g. typedef std::set, compare_pair > some_pair_set_t; @@ -470,4 +471,54 @@ llbind2nd(const _Operation& __oper, const _Tp& __x) return llbinder2nd<_Operation>(__oper, _Arg2_type(__x)); } +/** + * Compare std::type_info* pointers a la std::less. We break this out as a + * separate function for use in two different std::less specializations. + */ +inline +bool before(const std::type_info* lhs, const std::type_info* rhs) +{ +#if LL_LINUX && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 4)) + // If we're building on Linux with gcc, and it's either gcc 3.x or + // 4.{0,1,2,3}, then we have to use a workaround. Note that we use gcc on + // Mac too, and some people build with gcc on Windows (cygwin or mingw). + // On Linux, different load modules may produce different type_info* + // pointers for the same type. Have to compare name strings to get good + // results. + return strcmp(lhs->name(), rhs->name()) < 0; +#else // not Linux, or gcc 4.4+ + // Just use before(), as we normally would + return lhs->before(*rhs); +#endif +} + +/** + * Specialize std::less to use std::type_info::before(). + * See MAINT-1175. It is NEVER a good idea to directly compare std::type_info* + * because, on Linux, you might get different std::type_info* pointers for the + * same type (from different load modules)! + */ +namespace std +{ + template <> + struct less: + public std::binary_function + { + bool operator()(const std::type_info* lhs, const std::type_info* rhs) const + { + return before(lhs, rhs); + } + }; + + template <> + struct less: + public std::binary_function + { + bool operator()(std::type_info* lhs, std::type_info* rhs) const + { + return before(lhs, rhs); + } + }; +} // std + #endif // LL_LLSTL_H diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index fa0eb9f72c..0c32679744 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -47,7 +47,8 @@ std::string ll_safe_string(const char* in) std::string ll_safe_string(const char* in, S32 maxlen) { - if(in) return std::string(in, maxlen); + if(in && maxlen > 0 ) return std::string(in, maxlen); + return std::string(); } diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 6073bcd0a6..c96f2191f3 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -944,13 +944,15 @@ LLSD LLMemoryInfo::loadStatsMap() state.dwLength = sizeof(state); GlobalMemoryStatusEx(&state); - stats.add("Percent Memory use", state.dwMemoryLoad); - stats.add("Total Physical KB", state.ullTotalPhys/1024); - stats.add("Avail Physical KB", state.ullAvailPhys/1024); - stats.add("Total page KB", state.ullTotalPageFile/1024); - stats.add("Avail page KB", state.ullAvailPageFile/1024); - stats.add("Total Virtual KB", state.ullTotalVirtual/1024); - stats.add("Avail Virtual KB", state.ullAvailVirtual/1024); + DWORDLONG div = 1024; + + stats.add("Percent Memory use", state.dwMemoryLoad/div); + stats.add("Total Physical KB", state.ullTotalPhys/div); + stats.add("Avail Physical KB", state.ullAvailPhys/div); + stats.add("Total page KB", state.ullTotalPageFile/div); + stats.add("Avail page KB", state.ullAvailPageFile/div); + stats.add("Total Virtual KB", state.ullTotalVirtual/div); + stats.add("Avail Virtual KB", state.ullAvailVirtual/div); PERFORMANCE_INFORMATION perf; perf.cb = sizeof(perf); @@ -982,15 +984,15 @@ LLSD LLMemoryInfo::loadStatsMap() GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem)); stats.add("Page Fault Count", pmem.PageFaultCount); - stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/1024); - stats.add("WorkingSetSize KB", pmem.WorkingSetSize/1024); - stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/1024); - stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/1024); - stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/1024); - stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/1024); - stats.add("PagefileUsage KB", pmem.PagefileUsage/1024); - stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/1024); - stats.add("PrivateUsage KB", pmem.PrivateUsage/1024); + stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/div); + stats.add("WorkingSetSize KB", pmem.WorkingSetSize/div); + stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/div); + stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/div); + stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/div); + stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/div); + stats.add("PagefileUsage KB", pmem.PagefileUsage/div); + stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/div); + stats.add("PrivateUsage KB", pmem.PrivateUsage/div); #elif LL_DARWIN diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index c2fbb544a8..1d56a52c32 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -71,6 +71,13 @@ LL_COMMON_API void assert_main_thread() } } +void LLThread::registerThreadID() +{ +#if !LL_DARWIN + sThreadID = ++sIDIter; +#endif +} + // // Handed to the APR thread creation function // diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 115bf47553..5c8bbca2ca 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -88,6 +88,11 @@ public: U32 getID() const { return mID; } + // Called by threads *not* created via LLThread to register some + // internal state used by LLMutex. You must call this once early + // in the running thread to prevent collisions with the main thread. + static void registerThreadID(); + private: BOOL mPaused; diff --git a/indra/llcommon/lltypeinfolookup.h b/indra/llcommon/lltypeinfolookup.h index 7510cc12ed..0b6862444e 100644 --- a/indra/llcommon/lltypeinfolookup.h +++ b/indra/llcommon/lltypeinfolookup.h @@ -12,9 +12,49 @@ #if ! defined(LL_LLTYPEINFOLOOKUP_H) #define LL_LLTYPEINFOLOOKUP_H -#include "llsortedvector.h" +#include +#include +#include +#include // std::binary_function #include +/** + * The following helper classes are based on the Boost.Unordered documentation: + * http://www.boost.org/doc/libs/1_45_0/doc/html/unordered/hash_equality.html + */ + +/** + * Compute hash for a string passed as const char* + */ +struct const_char_star_hash: public std::unary_function +{ + std::size_t operator()(const char* str) const + { + std::size_t seed = 0; + for ( ; *str; ++str) + { + boost::hash_combine(seed, *str); + } + return seed; + } +}; + +/** + * Compute equality for strings passed as const char* + * + * I (nat) suspect that this is where the default behavior breaks for the + * const char* values returned from std::type_info::name(). If you compare the + * two const char* pointer values, as a naive, unspecialized implementation + * will surely do, they'll compare unequal. + */ +struct const_char_star_equal: public std::binary_function +{ + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) == 0; + } +}; + /** * LLTypeInfoLookup is specifically designed for use cases for which you might * consider std::map. We have several such data @@ -23,88 +63,55 @@ * different load modules will produce different std::type_info*. * LLTypeInfoLookup contains a workaround to address this issue. * - * Specifically, when we don't find the passed std::type_info*, - * LLTypeInfoLookup performs a linear search over registered entries to - * compare name() strings. Presuming that this succeeds, we cache the new - * (previously unrecognized) std::type_info* to speed future lookups. - * - * This worst-case fallback search (linear search with string comparison) - * should only happen the first time we look up a given type from a particular - * load module other than the one from which we initially registered types. - * (However, a lookup which wouldn't succeed anyway will always have - * worst-case performance.) This class is probably best used with less than a - * few dozen different types. + * The API deliberately diverges from std::map in several respects: + * * It avoids iterators, not only begin()/end() but also as return values + * from insert() and find(). This bypasses transform_iterator overhead. + * * Since we literally use compile-time types as keys, the essential insert() + * and find() methods accept the key type as a @em template parameter, + * accepting and returning value_type as a normal runtime value. This is to + * permit future optimization (e.g. compile-time type hashing) without + * changing the API. */ template class LLTypeInfoLookup { + // Use this for our underlying implementation: lookup by + // std::type_info::name() string. This is one of the rare cases in which I + // dare use const char* directly, rather than std::string, because I'm + // sure that every value returned by std::type_info::name() is static. + // HOWEVER, specify our own hash + equality functors: naively comparing + // distinct const char* values won't work. + typedef boost::unordered_map impl_map_type; + public: - typedef LLTypeInfoLookup self; - typedef LLSortedVector vector_type; - typedef typename vector_type::key_type key_type; - typedef typename vector_type::mapped_type mapped_type; - typedef typename vector_type::value_type value_type; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; + typedef VALUE value_type; LLTypeInfoLookup() {} - iterator begin() { return mVector.begin(); } - iterator end() { return mVector.end(); } - const_iterator begin() const { return mVector.begin(); } - const_iterator end() const { return mVector.end(); } - bool empty() const { return mVector.empty(); } - std::size_t size() const { return mVector.size(); } + bool empty() const { return mMap.empty(); } + std::size_t size() const { return mMap.size(); } - std::pair insert(const std::type_info* key, const VALUE& value) + template + bool insert(const value_type& value) { - return insert(value_type(key, value)); + // Obtain and store the std::type_info::name() string as the key. + // Return just the bool from std::map::insert()'s return pair. + return mMap.insert(typename impl_map_type::value_type(typeid(KEY).name(), value)).second; } - std::pair insert(const value_type& pair) + template + boost::optional find() const { - return mVector.insert(pair); - } - - // const find() forwards to non-const find(): this can alter mVector! - const_iterator find(const std::type_info* key) const - { - return const_cast(this)->find(key); - } - - // non-const find() caches previously-unknown type_info* to speed future - // lookups. - iterator find(const std::type_info* key) - { - iterator found = mVector.find(key); - if (found != mVector.end()) - { - // If LLSortedVector::find() found, great, we're done. - return found; - } - // Here we didn't find the passed type_info*. On Linux, though, even - // for the same type, typeid(sametype) produces a different type_info* - // when used in different load modules. So the fact that we didn't - // find the type_info* we seek doesn't mean this type isn't - // registered. Scan for matching name() string. - for (typename vector_type::iterator ti(mVector.begin()), tend(mVector.end()); - ti != tend; ++ti) - { - if (std::string(ti->first->name()) == key->name()) - { - // This unrecognized 'key' is for the same type as ti->first. - // To speed future lookups, insert a new entry that lets us - // look up ti->second using this same 'key'. - return insert(key, ti->second).first; - } - } - // We simply have never seen a type with this type_info* from any load - // module. - return mVector.end(); + // Use the std::type_info::name() string as the key. + typename impl_map_type::const_iterator found = mMap.find(typeid(KEY).name()); + if (found == mMap.end()) + return boost::optional(); + return found->second; } private: - vector_type mVector; + impl_map_type mMap; }; #endif /* ! defined(LL_LLTYPEINFOLOOKUP_H) */ diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index b39ea0c6f2..21456a599b 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -37,6 +37,8 @@ // system includes #include +#include +#include void encode_character(std::ostream& ostr, std::string::value_type val) { @@ -317,7 +319,7 @@ LLURI LLURI::buildHTTP(const std::string& prefix, const LLSD& path) { LLURI result; - + // TODO: deal with '/' '?' '#' in host_port if (prefix.find("://") != prefix.npos) { @@ -342,15 +344,41 @@ LLURI LLURI::buildHTTP(const std::string& prefix, result.mEscapedPath += "/" + escapePathComponent(it->asString()); } } - else if(path.isString()) + else if (path.isString()) { - result.mEscapedPath += "/" + escapePathComponent(path.asString()); + std::string pathstr(path); + // Trailing slash is significant in HTTP land. If caller specified, + // make a point of preserving. + std::string last_slash; + std::string::size_type len(pathstr.length()); + if (len && pathstr[len-1] == '/') + { + last_slash = "/"; + } + + // Escape every individual path component, recombining with slashes. + for (boost::split_iterator + ti(pathstr, boost::first_finder("/")), tend; + ti != tend; ++ti) + { + // Eliminate a leading slash or duplicate slashes anywhere. (Extra + // slashes show up here as empty components.) This test also + // eliminates a trailing slash, hence last_slash above. + if (! ti->empty()) + { + result.mEscapedPath + += "/" + escapePathComponent(std::string(ti->begin(), ti->end())); + } + } + + // Reinstate trailing slash, if any. + result.mEscapedPath += last_slash; } else if(path.isUndefined()) { // do nothing } - else + else { llwarns << "Valid path arguments to buildHTTP are array, string, or undef, you passed type" << path.type() << llendl; diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index bcc661a920..8585af0a29 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -29,7 +29,7 @@ const S32 LL_VERSION_MAJOR = 3; const S32 LL_VERSION_MINOR = 4; -const S32 LL_VERSION_PATCH = 1; +const S32 LL_VERSION_PATCH = 4; const S32 LL_VERSION_BUILD = 0; const char * const LL_CHANNEL = "Second Life Developer"; diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp index 05289881d0..4c3bc674af 100644 --- a/indra/llcommon/tests/bitpack_test.cpp +++ b/indra/llcommon/tests/bitpack_test.cpp @@ -95,6 +95,7 @@ namespace tut ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]); unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, 8*4); // Life ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4); + ensure("keep compiler quiet", unpack_bufsize == unpack_bufsize); } // U32 packing diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp index f6d4221256..4c64f15ca7 100644 --- a/indra/llcommon/tests/lluri_test.cpp +++ b/indra/llcommon/tests/lluri_test.cpp @@ -58,12 +58,12 @@ namespace tut ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1); } }; - + typedef test_group URITestGroup; typedef URITestGroup::object URITestObject; URITestGroup uriTestGroup("LLURI"); - + template<> template<> void URITestObject::test<1>() { @@ -89,14 +89,14 @@ namespace tut template<> template<> void URITestObject::test<2>() { - // empty string + set_test_name("empty string"); checkParts(LLURI(""), "", "", "", ""); } - + template<> template<> void URITestObject::test<3>() { - // no scheme + set_test_name("no scheme"); checkParts(LLURI("foo"), "", "foo", "", ""); checkParts(LLURI("foo%3A"), "", "foo:", "", ""); } @@ -104,7 +104,7 @@ namespace tut template<> template<> void URITestObject::test<4>() { - // scheme w/o paths + set_test_name("scheme w/o paths"); checkParts(LLURI("mailto:zero@ll.com"), "mailto", "zero@ll.com", "", ""); checkParts(LLURI("silly://abc/def?foo"), @@ -114,16 +114,16 @@ namespace tut template<> template<> void URITestObject::test<5>() { - // authority section + set_test_name("authority section"); checkParts(LLURI("http:///"), "http", "///", "", "/"); - + checkParts(LLURI("http://abc"), "http", "//abc", "abc", ""); - + checkParts(LLURI("http://a%2Fb/cd"), "http", "//a/b/cd", "a/b", "/cd"); - + checkParts(LLURI("http://host?"), "http", "//host?", "host", ""); } @@ -131,13 +131,13 @@ namespace tut template<> template<> void URITestObject::test<6>() { - // path section + set_test_name("path section"); checkParts(LLURI("http://host/a/b/"), "http", "//host/a/b/", "host", "/a/b/"); - + checkParts(LLURI("http://host/a%3Fb/"), "http", "//host/a?b/", "host", "/a?b/"); - + checkParts(LLURI("http://host/a:b/"), "http", "//host/a:b/", "host", "/a:b/"); } @@ -145,16 +145,16 @@ namespace tut template<> template<> void URITestObject::test<7>() { - // query string + set_test_name("query string"); checkParts(LLURI("http://host/?"), "http", "//host/?", "host", "/", ""); - + checkParts(LLURI("http://host/?x"), "http", "//host/?x", "host", "/", "x"); - + checkParts(LLURI("http://host/??"), "http", "//host/??", "host", "/", "?"); - + checkParts(LLURI("http://host/?%3F"), "http", "//host/??", "host", "/", "?"); } @@ -167,19 +167,44 @@ namespace tut path.append("123"); checkParts(LLURI::buildHTTP("host", path), "http", "//host/x/123", "host", "/x/123"); - + LLSD query; query["123"] = "12"; query["abcd"] = "abc"; checkParts(LLURI::buildHTTP("host", path, query), "http", "//host/x/123?123=12&abcd=abc", "host", "/x/123", "123=12&abcd=abc"); + + ensure_equals(LLURI::buildHTTP("host", "").asString(), + "http://host"); + ensure_equals(LLURI::buildHTTP("host", "/").asString(), + "http://host/"); + ensure_equals(LLURI::buildHTTP("host", "//").asString(), + "http://host/"); + ensure_equals(LLURI::buildHTTP("host", "dir name").asString(), + "http://host/dir%20name"); + ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(), + "http://host/dir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(), + "http://host/dir%20name"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(), + "http://host/dir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(), + "http://host/dir%20name/subdir%20name"); + ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(), + "http://host/dir%20name/subdir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(), + "http://host/dir%20name/subdir%20name"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(), + "http://host/dir%20name/subdir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(), + "http://host/dir%20name/subdir%20name/"); } template<> template<> void URITestObject::test<9>() { - // test unescaped path components + set_test_name("test unescaped path components"); LLSD path; path.append("x@*//*$&^"); path.append("123"); @@ -190,7 +215,7 @@ namespace tut template<> template<> void URITestObject::test<10>() { - // test unescaped query components + set_test_name("test unescaped query components"); LLSD path; path.append("x"); path.append("123"); @@ -205,7 +230,7 @@ namespace tut template<> template<> void URITestObject::test<11>() { - // test unescaped host components + set_test_name("test unescaped host components"); LLSD path; path.append("x"); path.append("123"); @@ -216,16 +241,16 @@ namespace tut "http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc", "hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc"); } - + template<> template<> void URITestObject::test<12>() { - // test funky host_port values that are actually prefixes - + set_test_name("test funky host_port values that are actually prefixes"); + checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()), "http", "//example.com:8080", "example.com:8080", ""); - + checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()), "http", "//example.com:8080/", "example.com:8080", "/"); @@ -242,7 +267,7 @@ namespace tut "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789" "-._~"; - // test escape + set_test_name("test escape"); ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67"); ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40"); ensure_equals("escaping as query variable", @@ -259,13 +284,12 @@ namespace tut cedilla.push_back( (char)0xA7 ); ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7"); } - + template<> template<> void URITestObject::test<14>() { - // make sure escape and unescape of empty strings return empty - // strings. + set_test_name("make sure escape and unescape of empty strings return empty strings."); std::string uri_esc(LLURI::escape("")); ensure("escape string empty", uri_esc.empty()); std::string uri_raw(LLURI::unescape("")); @@ -275,7 +299,7 @@ namespace tut template<> template<> void URITestObject::test<15>() { - // do some round-trip tests + set_test_name("do some round-trip tests"); escapeRoundTrip("http://secondlife.com"); escapeRoundTrip("http://secondlife.com/url with spaces"); escapeRoundTrip("http://bad[domain]name.com/"); @@ -286,7 +310,7 @@ namespace tut template<> template<> void URITestObject::test<16>() { - // Test the default escaping + set_test_name("Test the default escaping"); // yes -- this mangles the url. This is expected behavior std::string simple("http://secondlife.com"); ensure_equals( @@ -302,7 +326,7 @@ namespace tut template<> template<> void URITestObject::test<17>() { - // do some round-trip tests with very long strings. + set_test_name("do some round-trip tests with very long strings."); escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six"); escapeRoundTrip( "'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale" @@ -322,7 +346,7 @@ namespace tut "D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n"); } - + template<> template<> void URITestObject::test<18>() { @@ -335,7 +359,7 @@ namespace tut ensure_equals("pathmap", u.pathArray()[1].asString(), "login"); ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test"); ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester"); - + u = LLURI("secondlife://Da Boom/128/128/128"); // if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority ensure_equals("scheme", u.scheme(), "secondlife"); @@ -350,7 +374,7 @@ namespace tut template<> template<> void URITestObject::test<19>() { - // Parse about: schemes + set_test_name("Parse about: schemes"); LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78"); ensure_equals("scheme", u.scheme(), "about"); ensure_equals("authority", u.authority(), ""); diff --git a/indra/llcommon/tests/reflection_test.cpp b/indra/llcommon/tests/reflection_test.cpp index 59491cd1fe..8980ebb1f1 100644 --- a/indra/llcommon/tests/reflection_test.cpp +++ b/indra/llcommon/tests/reflection_test.cpp @@ -207,7 +207,7 @@ namespace tut const LLReflective* reflective = property->get(aggregated_data); // Wrong reflective type, should throw exception. // useless op to get rid of compiler warning. - reflective = NULL; + reflective = reflective; } catch(...) { diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt new file mode 100644 index 0000000000..9adfd0ed62 --- /dev/null +++ b/indra/llcorehttp/CMakeLists.txt @@ -0,0 +1,186 @@ +# -*- cmake -*- + +project(llcorehttp) + +include(00-Common) +include(GoogleMock) +include(CURL) +include(CARes) +include(OpenSSL) +include(ZLIB) +include(LLCoreHttp) +include(LLAddBuildTest) +include(LLMessage) +include(LLCommon) +include(Tut) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories( + ${LLMESSAGE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} + ) + +set(llcorehttp_SOURCE_FILES + bufferarray.cpp + bufferstream.cpp + httpcommon.cpp + httpheaders.cpp + httpoptions.cpp + httprequest.cpp + httpresponse.cpp + _httplibcurl.cpp + _httpopcancel.cpp + _httpoperation.cpp + _httpoprequest.cpp + _httpopsetget.cpp + _httpopsetpriority.cpp + _httppolicy.cpp + _httppolicyclass.cpp + _httppolicyglobal.cpp + _httpreplyqueue.cpp + _httprequestqueue.cpp + _httpservice.cpp + _refcounted.cpp + ) + +set(llcorehttp_HEADER_FILES + CMakeLists.txt + + bufferarray.h + bufferstream.h + httpcommon.h + httphandler.h + httpheaders.h + httpoptions.h + httprequest.h + httpresponse.h + _httpinternal.h + _httplibcurl.h + _httpopcancel.h + _httpoperation.h + _httpoprequest.h + _httpopsetget.h + _httpopsetpriority.h + _httppolicy.h + _httppolicyclass.h + _httppolicyglobal.h + _httpreadyqueue.h + _httpreplyqueue.h + _httprequestqueue.h + _httpservice.h + _mutex.h + _refcounted.h + _thread.h + ) + +set_source_files_properties(${llcorehttp_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) +if (DARWIN OR LINUX) + # Boost headers define unused members in condition_variable so... + set_source_files_properties(${llcorehttp_SOURCE_FILES} + PROPERTIES COMPILE_FLAGS -Wno-unused-variable) +endif (DARWIN OR LINUX) + +list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) + +add_library (llcorehttp ${llcorehttp_SOURCE_FILES}) +target_link_libraries( + llcorehttp + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} + ) + +# tests +if (LL_TESTS) + SET(llcorehttp_TEST_SOURCE_FILES + tests/test_allocator.cpp + ) + + set(llcorehttp_TEST_HEADER_FILS + tests/test_httpstatus.hpp + tests/test_refcounted.hpp + tests/test_httpoperation.hpp + tests/test_httprequest.hpp + tests/test_httprequestqueue.hpp + tests/test_httpheaders.hpp + tests/test_bufferarray.hpp + tests/test_bufferstream.hpp + ) + + set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + + list(APPEND llcorehttp_TEST_SOURCE_FILES ${llcorehttp_TEST_HEADER_FILES}) + + # LL_ADD_PROJECT_UNIT_TESTS(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}") + + # set(TEST_DEBUG on) + set(test_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_SYSTEM_LIBRARY} + ${BOOST_THREAD_LIBRARY} + ) + + LL_ADD_INTEGRATION_TEST(llcorehttp + "${llcorehttp_TEST_SOURCE_FILES}" + "${test_libs}" + ${PYTHON_EXECUTABLE} + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" + ) + + # + # Example Programs + # + SET(llcorehttp_EXAMPLE_SOURCE_FILES + examples/http_texture_load.cpp + ) + + set(example_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_SYSTEM_LIBRARY} + ${BOOST_THREAD_LIBRARY} + ) + + add_executable(http_texture_load + ${llcorehttp_EXAMPLE_SOURCE_FILES} + ) + set_target_properties(http_texture_load + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}" + ) + + if (WINDOWS) + # The following come from LLAddBuildTest.cmake's INTEGRATION_TEST_xxxx target. + set_target_properties(http_texture_load + PROPERTIES + LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS ${TCMALLOC_LINK_FLAGS}" + LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO" + LINK_FLAGS_RELEASE "" + ) + endif (WINDOWS) + + target_link_libraries(http_texture_load ${example_libs}) + +endif (LL_TESTS) + diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h new file mode 100644 index 0000000000..14f744a9f1 --- /dev/null +++ b/indra/llcorehttp/_httpinternal.h @@ -0,0 +1,154 @@ +/** + * @file _httpinternal.h + * @brief Implementation constants and magic numbers + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_INTERNAL_H_ +#define _LLCORE_HTTP_INTERNAL_H_ + + +// If you find this included in a public interface header, +// something wrong is probably happening. + + +// -------------------------------------------------------------------- +// General library to-do list +// +// - Implement policy classes. Structure is mostly there just didn't +// need it for the first consumer. +// - Consider Removing 'priority' from the request interface. Its use +// in an always active class can lead to starvation of low-priority +// requests. Requires coodination of priority values across all +// components that share a class. Changing priority across threads +// is slightly expensive (relative to gain) and hasn't been completely +// implemented. And the major user of priority, texture fetches, +// may not really need it. +// - Set/get for global policy and policy classes is clumsy. Rework +// it heading in a direction that allows for more dynamic behavior. +// - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the +// pedantic. +// - Update downloader and other long-duration services are going to +// need a progress notification. Initial idea is to introduce a +// 'repeating request' which can piggyback on another request and +// persist until canceled or carrier completes. Current queue +// structures allow an HttpOperation object to be enqueued +// repeatedly, so... +// - Investigate making c-ares' re-implementation of a resolver library +// more resilient or more intelligent on Mac. Part of the DNS failure +// lies in here. The mechanism also looks a little less dynamic +// than needed in an environments where networking is changing. +// - Global optimizations: 'borrowing' connections from other classes, +// HTTP pipelining. +// - Dynamic/control system stuff: detect problems and self-adjust. +// This won't help in the face of the router problems we've looked +// at, however. Detect starvation due to UDP activity and provide +// feedback to it. +// +// Integration to-do list +// - LLTextureFetch still needs a major refactor. The use of +// LLQueuedThread makes it hard to inspect workers and do the +// resource waiting we're now doing. Rebuild along simpler lines +// some of which are suggested in new commentary at the top of +// the main source file. +// - Expand areas of usage eventually leading to the removal of LLCurl. +// Rough order of expansion: +// . Mesh fetch +// . Avatar names +// . Group membership lists +// . Caps access in general +// . 'The rest' +// - Adapt texture cache, image decode and other image consumers to +// the BufferArray model to reduce data copying. Alternatively, +// adapt this library to something else. +// +// -------------------------------------------------------------------- + + +// If '1', internal ready queues will not order ready +// requests by priority, instead it's first-come-first-served. +// Reprioritization requests have the side-effect of then +// putting the modified request at the back of the ready queue. + +#define LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY 1 + + +namespace LLCore +{ + +// Maxium number of policy classes that can be defined. +// *TODO: Currently limited to the default class, extend. +const int HTTP_POLICY_CLASS_LIMIT = 1; + +// Debug/informational tracing. Used both +// as a global option and in per-request traces. +const int HTTP_TRACE_OFF = 0; +const int HTTP_TRACE_LOW = 1; +const int HTTP_TRACE_CURL_HEADERS = 2; +const int HTTP_TRACE_CURL_BODIES = 3; + +const int HTTP_TRACE_MIN = HTTP_TRACE_OFF; +const int HTTP_TRACE_MAX = HTTP_TRACE_CURL_BODIES; + +// Request retry limits +// +// At a minimum, retries need to extend past any throttling +// window we're expecting from central services. In the case +// of Linden services running through the caps routers, there's +// a five-second or so window for throttling with some spillover. +// We want to span a few windows to allow transport to slow +// after onset of the throttles and then recover without a final +// failure. Other systems may need other constants. +const int HTTP_RETRY_COUNT_DEFAULT = 8; +const int HTTP_RETRY_COUNT_MIN = 0; +const int HTTP_RETRY_COUNT_MAX = 100; + +const int HTTP_REDIRECTS_DEFAULT = 10; + +// Timeout value used for both connect and protocol exchange. +// Retries and time-on-queue are not included and aren't +// accounted for. +const long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L; +const long HTTP_REQUEST_TIMEOUT_MIN = 0L; +const long HTTP_REQUEST_TIMEOUT_MAX = 3600L; + +// Limits on connection counts +const int HTTP_CONNECTION_LIMIT_DEFAULT = 8; +const int HTTP_CONNECTION_LIMIT_MIN = 1; +const int HTTP_CONNECTION_LIMIT_MAX = 256; + +// Tuning parameters + +// Time worker thread sleeps after a pass through the +// request, ready and active queues. +const int HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS = 2; + +// Block allocation size (a tuning parameter) is found +// in bufferarray.h. + +// Compatibility controls +const bool HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true; + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp new file mode 100644 index 0000000000..6fe0bfc7d1 --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -0,0 +1,373 @@ +/** + * @file _httplibcurl.cpp + * @brief Internal definitions of the Http libcurl thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httplibcurl.h" + +#include "httpheaders.h" +#include "bufferarray.h" +#include "_httpoprequest.h" +#include "_httppolicy.h" + +#include "llhttpstatuscodes.h" + + +namespace LLCore +{ + + +HttpLibcurl::HttpLibcurl(HttpService * service) + : mService(service), + mPolicyCount(0), + mMultiHandles(NULL) +{} + + +HttpLibcurl::~HttpLibcurl() +{ + shutdown(); + + mService = NULL; +} + + +void HttpLibcurl::shutdown() +{ + while (! mActiveOps.empty()) + { + HttpOpRequest * op(* mActiveOps.begin()); + mActiveOps.erase(mActiveOps.begin()); + + cancelRequest(op); + op->release(); + } + + if (mMultiHandles) + { + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + if (mMultiHandles[policy_class]) + { + curl_multi_cleanup(mMultiHandles[policy_class]); + mMultiHandles[policy_class] = 0; + } + } + + delete [] mMultiHandles; + mMultiHandles = NULL; + } + + mPolicyCount = 0; +} + + +void HttpLibcurl::start(int policy_count) +{ + llassert_always(policy_count <= HTTP_POLICY_CLASS_LIMIT); + llassert_always(! mMultiHandles); // One-time call only + + mPolicyCount = policy_count; + mMultiHandles = new CURLM * [mPolicyCount]; + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + mMultiHandles[policy_class] = curl_multi_init(); + } +} + + +// Give libcurl some cycles, invoke it's callbacks, process +// completed requests finalizing or issuing retries as needed. +// +// If active list goes empty *and* we didn't queue any +// requests for retry, we return a request for a hard +// sleep otherwise ask for a normal polling interval. +HttpService::ELoopSpeed HttpLibcurl::processTransport() +{ + HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); + + // Give libcurl some cycles to do I/O & callbacks + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + if (! mMultiHandles[policy_class]) + continue; + + int running(0); + CURLMcode status(CURLM_CALL_MULTI_PERFORM); + do + { + running = 0; + status = curl_multi_perform(mMultiHandles[policy_class], &running); + } + while (0 != running && CURLM_CALL_MULTI_PERFORM == status); + + // Run completion on anything done + CURLMsg * msg(NULL); + int msgs_in_queue(0); + while ((msg = curl_multi_info_read(mMultiHandles[policy_class], &msgs_in_queue))) + { + if (CURLMSG_DONE == msg->msg) + { + CURL * handle(msg->easy_handle); + CURLcode result(msg->data.result); + + if (completeRequest(mMultiHandles[policy_class], handle, result)) + { + // Request is still active, don't get too sleepy + ret = HttpService::NORMAL; + } + handle = NULL; // No longer valid on return + } + else if (CURLMSG_NONE == msg->msg) + { + // Ignore this... it shouldn't mean anything. + ; + } + else + { + LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl. Msg code: " + << msg->msg + << LL_ENDL; + } + msgs_in_queue = 0; + } + } + + if (! mActiveOps.empty()) + { + ret = HttpService::NORMAL; + } + return ret; +} + + +// Caller has provided us with a ref count on op. +void HttpLibcurl::addOp(HttpOpRequest * op) +{ + llassert_always(op->mReqPolicy < mPolicyCount); + llassert_always(mMultiHandles[op->mReqPolicy] != NULL); + + // Create standard handle + if (! op->prepareRequest(mService)) + { + // Couldn't issue request, fail with notification + // *TODO: Need failure path + return; + } + + // Make the request live + curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); + op->mCurlActive = true; + + if (op->mTracing > HTTP_TRACE_OFF) + { + HttpPolicy & policy(mService->getPolicy()); + + LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle: " + << static_cast(op) + << ", Actives: " << mActiveOps.size() + << ", Readies: " << policy.getReadyCount(op->mReqPolicy) + << LL_ENDL; + } + + // On success, make operation active + mActiveOps.insert(op); +} + + +// Implements the transport part of any cancel operation. +// See if the handle is an active operation and if so, +// use the more complicated transport-based cancelation +// method to kill the request. +bool HttpLibcurl::cancel(HttpHandle handle) +{ + HttpOpRequest * op(static_cast(handle)); + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + return false; + } + + // Cancel request + cancelRequest(op); + + // Drop references + mActiveOps.erase(it); + op->release(); + + return true; +} + + +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. Caller is expected to +// remove the op from the active list and release the op *after* +// calling this method. It must be called first to deliver the +// op to the reply queue with refcount intact. +void HttpLibcurl::cancelRequest(HttpOpRequest * op) +{ + // Deactivate request + op->mCurlActive = false; + + // Detach from multi and recycle handle + curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); + curl_easy_cleanup(op->mCurlHandle); + op->mCurlHandle = NULL; + + // Tracing + if (op->mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Cancel op and deliver for notification + op->cancel(); +} + + +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. +bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +{ + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + + if (handle != op->mCurlHandle || ! op->mCurlActive) + { + LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." + << " Handle: " << static_cast(handle) + << LL_ENDL; + return false; + } + + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + LL_WARNS("CoreHttp") << "libcurl completion for request not on active list. Continuing." + << " Handle: " << static_cast(handle) + << LL_ENDL; + return false; + } + + // Deactivate request + mActiveOps.erase(it); + op->mCurlActive = false; + + // Set final status of request if it hasn't failed by other mechanisms yet + if (op->mStatus) + { + op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); + } + if (op->mStatus) + { + int http_status(HTTP_OK); + + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); + if (http_status >= 100 && http_status <= 999) + { + char * cont_type(NULL); + curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type); + if (cont_type) + { + op->mReplyConType = cont_type; + } + op->mStatus = HttpStatus(http_status); + } + else + { + LL_WARNS("CoreHttp") << "Invalid HTTP response code (" + << http_status << ") received from server." + << LL_ENDL; + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); + } + } + + // Detach from multi and recycle handle + curl_multi_remove_handle(multi_handle, handle); + curl_easy_cleanup(handle); + op->mCurlHandle = NULL; + + // Tracing + if (op->mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Dispatch to next stage + HttpPolicy & policy(mService->getPolicy()); + bool still_active(policy.stageAfterCompletion(op)); + + return still_active; +} + + +int HttpLibcurl::getActiveCount() const +{ + return mActiveOps.size(); +} + + +int HttpLibcurl::getActiveCountInClass(int policy_class) const +{ + int count(0); + + for (active_set_t::const_iterator iter(mActiveOps.begin()); + mActiveOps.end() != iter; + ++iter) + { + if ((*iter)->mReqPolicy == policy_class) + { + ++count; + } + } + + return count; +} + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) +{ + for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin()); + + headers->mHeaders.end() != it; + ++it) + { + slist = curl_slist_append(slist, (*it).c_str()); + } + return slist; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h new file mode 100644 index 0000000000..611f029ef5 --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.h @@ -0,0 +1,129 @@ +/** + * @file _httplibcurl.h + * @brief Declarations for internal class providing libcurl transport. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_LIBCURL_H_ +#define _LLCORE_HTTP_LIBCURL_H_ + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include +#include + +#include + +#include "httprequest.h" +#include "_httpservice.h" +#include "_httpinternal.h" + + +namespace LLCore +{ + + +class HttpPolicy; +class HttpOpRequest; +class HttpHeaders; + + +/// Implements libcurl-based transport for an HttpService instance. +/// +/// Threading: Single-threaded. Other than for construction/destruction, +/// all methods are expected to be invoked in a single thread, typically +/// a worker thread of some sort. + +class HttpLibcurl +{ +public: + HttpLibcurl(HttpService * service); + virtual ~HttpLibcurl(); + +private: + HttpLibcurl(const HttpLibcurl &); // Not defined + void operator=(const HttpLibcurl &); // Not defined + +public: + /// Give cycles to libcurl to run active requests. Completed + /// operations (successful or failed) will be retried or handed + /// over to the reply queue as final responses. + /// + /// @return Indication of how long this method is + /// willing to wait for next service call. + HttpService::ELoopSpeed processTransport(); + + /// Add request to the active list. Caller is expected to have + /// provided us with a reference count on the op to hold the + /// request. (No additional references will be added.) + void addOp(HttpOpRequest * op); + + /// One-time call to set the number of policy classes to be + /// serviced and to create the resources for each. Value + /// must agree with HttpPolicy::setPolicies() call. + void start(int policy_count); + + /// Synchronously stop libcurl operations. All active requests + /// are canceled and removed from libcurl's handling. Easy + /// handles are detached from their multi handles and released. + /// Multi handles are also released. Canceled requests are + /// completed with canceled status and made available on their + /// respective reply queues. + /// + /// Can be restarted with a start() call. + void shutdown(); + + /// Return global and per-class counts of active requests. + int getActiveCount() const; + int getActiveCountInClass(int policy_class) const; + + /// Attempt to cancel a request identified by handle. + /// + /// Interface shadows HttpService's method. + /// + /// @return True if handle was found and operation canceled. + /// + bool cancel(HttpHandle handle); + +protected: + /// Invoked when libcurl has indicated a request has been processed + /// to completion and we need to move the request to a new state. + bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + + /// Invoked to cancel an active request, mainly during shutdown + /// and destroy. + void cancelRequest(HttpOpRequest * op); + +protected: + typedef std::set active_set_t; + +protected: + HttpService * mService; // Simple reference, not owner + active_set_t mActiveOps; + int mPolicyCount; + CURLM ** mMultiHandles; +}; // end class HttpLibcurl + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_LIBCURL_H_ diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp new file mode 100644 index 0000000000..c1912eb3db --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -0,0 +1,73 @@ +/** + * @file _httpopcancel.cpp + * @brief Definitions for internal class HttpOpCancel + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httpopcancel.h" + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httpservice.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpCancel +// ================================== + + +HttpOpCancel::HttpOpCancel(HttpHandle handle) + : HttpOperation(), + mHandle(handle) +{} + + +HttpOpCancel::~HttpOpCancel() +{} + + +// Immediately search for the request on various queues +// and cancel operations if found. Return the status of +// the search and cancel as the status of this request. +// The canceled request will return a canceled status to +// its handler. +void HttpOpCancel::stageFromRequest(HttpService * service) +{ + if (! service->cancel(mHandle)) + { + mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND); + } + + addAsReply(); +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h new file mode 100644 index 0000000000..336dfdc573 --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.h @@ -0,0 +1,78 @@ +/** + * @file _httpopcancel.h + * @brief Internal declarations for the HttpOpCancel subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_OPCANCEL_H_ +#define _LLCORE_HTTP_OPCANCEL_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpCancel requests that a previously issued request +/// be canceled, if possible. This includes active requests +/// that may be in the middle of an HTTP transaction. Any +/// completed request will not be canceled and will return +/// its final status unchanged and *this* request will complete +/// with an HE_HANDLE_NOT_FOUND error status. + +class HttpOpCancel : public HttpOperation +{ +public: + /// @param handle Handle of previously-issued request to + /// be canceled. + HttpOpCancel(HttpHandle handle); + +protected: + virtual ~HttpOpCancel(); // Use release() + +private: + HttpOpCancel(const HttpOpCancel &); // Not defined + void operator=(const HttpOpCancel &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +public: + // Request data + HttpHandle mHandle; +}; // end class HttpOpCancel + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPCANCEL_H_ + diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp new file mode 100644 index 0000000000..5cf5bc5930 --- /dev/null +++ b/indra/llcorehttp/_httpoperation.cpp @@ -0,0 +1,248 @@ +/** + * @file _httpoperation.cpp + * @brief Definitions for internal classes based on HttpOperation + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httpoperation.h" + +#include "httphandler.h" +#include "httpresponse.h" +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httpinternal.h" + +#include "lltimer.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOperation +// ================================== + + +HttpOperation::HttpOperation() + : LLCoreInt::RefCounted(true), + mReplyQueue(NULL), + mUserHandler(NULL), + mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), + mReqPriority(0U), + mTracing(0) +{ + mMetricCreated = totalTime(); +} + + +HttpOperation::~HttpOperation() +{ + setReplyPath(NULL, NULL); +} + + +void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * user_handler) +{ + if (reply_queue != mReplyQueue) + { + if (mReplyQueue) + { + mReplyQueue->release(); + } + + if (reply_queue) + { + reply_queue->addRef(); + } + + mReplyQueue = reply_queue; + } + + // Not refcounted + mUserHandler = user_handler; +} + + + +void HttpOperation::stageFromRequest(HttpService *) +{ + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LL_ERRS("HttpCore") << "Default stageFromRequest method may not be called." + << LL_ENDL; +} + + +void HttpOperation::stageFromReady(HttpService *) +{ + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LL_ERRS("HttpCore") << "Default stageFromReady method may not be called." + << LL_ENDL; +} + + +void HttpOperation::stageFromActive(HttpService *) +{ + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LL_ERRS("HttpCore") << "Default stageFromActive method may not be called." + << LL_ENDL; +} + + +void HttpOperation::visitNotifier(HttpRequest *) +{ + if (mUserHandler) + { + HttpResponse * response = new HttpResponse(); + + response->setStatus(mStatus); + mUserHandler->onCompleted(static_cast(this), response); + + response->release(); + } +} + + +HttpStatus HttpOperation::cancel() +{ + HttpStatus status; + + return status; +} + + +void HttpOperation::addAsReply() +{ + if (mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " + << static_cast(this) + << LL_ENDL; + } + + if (mReplyQueue) + { + addRef(); + mReplyQueue->addOp(this); + } +} + + +// ================================== +// HttpOpStop +// ================================== + + +HttpOpStop::HttpOpStop() + : HttpOperation() +{} + + +HttpOpStop::~HttpOpStop() +{} + + +void HttpOpStop::stageFromRequest(HttpService * service) +{ + // Do operations + service->stopRequested(); + + // Prepare response if needed + addAsReply(); +} + + +// ================================== +// HttpOpNull +// ================================== + + +HttpOpNull::HttpOpNull() + : HttpOperation() +{} + + +HttpOpNull::~HttpOpNull() +{} + + +void HttpOpNull::stageFromRequest(HttpService * service) +{ + // Perform op + // Nothing to perform. This doesn't fall into the libcurl + // ready/active queues, it just bounces over to the reply + // queue directly. + + // Prepare response if needed + addAsReply(); +} + + +// ================================== +// HttpOpSpin +// ================================== + + +HttpOpSpin::HttpOpSpin(int mode) + : HttpOperation(), + mMode(mode) +{} + + +HttpOpSpin::~HttpOpSpin() +{} + + +void HttpOpSpin::stageFromRequest(HttpService * service) +{ + if (0 == mMode) + { + // Spin forever + while (true) + { + ms_sleep(100); + } + } + else + { + ms_sleep(1); // backoff interlock plumbing a bit + this->addRef(); + if (! service->getRequestQueue().addOp(this)) + { + this->release(); + } + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h new file mode 100644 index 0000000000..914627fad0 --- /dev/null +++ b/indra/llcorehttp/_httpoperation.h @@ -0,0 +1,262 @@ +/** + * @file _httpoperation.h + * @brief Internal declarations for HttpOperation and sub-classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_OPERATION_H_ +#define _LLCORE_HTTP_OPERATION_H_ + + +#include "httpcommon.h" +#include "httprequest.h" +#include "_refcounted.h" + + +namespace LLCore +{ + +class HttpReplyQueue; +class HttpHandler; +class HttpService; + +/// HttpOperation is the base class for all request/reply +/// pairs. +/// +/// Operations are expected to be of two types: immediate +/// and queued. Immediate requests go to the singleton +/// request queue and when picked up by the worker thread +/// are executed immediately and there results placed on +/// the supplied reply queue. Queued requests (namely for +/// HTTP operations), go to the request queue, are picked +/// up and moved to a ready queue where they're ordered by +/// priority and managed by the policy component, are +/// then activated issuing HTTP requests and moved to an +/// active list managed by the transport (libcurl) component +/// and eventually finalized when a response is available +/// and status and data return via reply queue. +/// +/// To manage these transitions, derived classes implement +/// three methods: stageFromRequest, stageFromReady and +/// stageFromActive. Immediate requests will only override +/// stageFromRequest which will perform the operation and +/// return the result by invoking addAsReply() to put the +/// request on a reply queue. Queued requests will involve +/// all three stage methods. +/// +/// Threading: not thread-safe. Base and derived classes +/// provide no locking. Instances move across threads +/// via queue-like interfaces that are thread compatible +/// and those interfaces establish the access rules. + +class HttpOperation : public LLCoreInt::RefCounted +{ +public: + /// Threading: called by a consumer/application thread. + HttpOperation(); + +protected: + /// Threading: called by any thread. + virtual ~HttpOperation(); // Use release() + +private: + HttpOperation(const HttpOperation &); // Not defined + void operator=(const HttpOperation &); // Not defined + +public: + /// Register a reply queue and a handler for completion notifications. + /// + /// Invokers of operations that want to receive notification that an + /// operation has been completed do so by binding a reply queue and + /// a handler object to the request. + /// + /// @param reply_queue Pointer to the reply queue where completion + /// notifications are to be queued (typically + /// by addAsReply()). This will typically be + /// the reply queue referenced by the request + /// object. This method will increment the + /// refcount on the queue holding the queue + /// until delivery is complete. Using a reply_queue + /// even if the handler is NULL has some benefits + /// for memory deallocation by keeping it in the + /// originating thread. + /// + /// @param handler Possibly NULL pointer to a non-refcounted + //// handler object to be invoked (onCompleted) + /// when the operation is finished. Note that + /// the handler object is never dereferenced + /// by the worker thread. This is passible data + /// until notification is performed. + /// + /// Threading: called by application thread. + /// + void setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * handler); + + /// The three possible staging steps in an operation's lifecycle. + /// Asynchronous requests like HTTP operations move from the + /// request queue to the ready queue via stageFromRequest. Then + /// from the ready queue to the active queue by stageFromReady. And + /// when complete, to the reply queue via stageFromActive and the + /// addAsReply utility. + /// + /// Immediate mode operations (everything else) move from the + /// request queue to the reply queue directly via stageFromRequest + /// and addAsReply with no existence on the ready or active queues. + /// + /// These methods will take out a reference count on the request, + /// caller only needs to dispose of its reference when done with + /// the request. + /// + /// Threading: called by worker thread. + /// + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + /// Delivers a notification to a handler object on completion. + /// + /// Once a request is complete and it has been removed from its + /// reply queue, a handler notification may be delivered by a + /// call to HttpRequest::update(). This method does the necessary + /// dispatching. + /// + /// Threading: called by application thread. + /// + virtual void visitNotifier(HttpRequest *); + + /// Cancels the operation whether queued or active. + /// Final status of the request becomes canceled (an error) and + /// that will be delivered to caller via notification scheme. + /// + /// Threading: called by worker thread. + /// + virtual HttpStatus cancel(); + +protected: + /// Delivers request to reply queue on completion. After this + /// call, worker thread no longer accesses the object and it + /// is owned by the reply queue. + /// + /// Threading: called by worker thread. + /// + void addAsReply(); + +protected: + HttpReplyQueue * mReplyQueue; // Have refcount + HttpHandler * mUserHandler; // Naked pointer + +public: + // Request Data + HttpRequest::policy_t mReqPolicy; + HttpRequest::priority_t mReqPriority; + + // Reply Data + HttpStatus mStatus; + + // Tracing, debug and metrics + HttpTime mMetricCreated; + int mTracing; +}; // end class HttpOperation + + +/// HttpOpStop requests the servicing thread to shutdown +/// operations, cease pulling requests from the request +/// queue and release shared resources (particularly +/// those shared via reference count). The servicing +/// thread will then exit. The underlying thread object +/// remains so that another thread can join on the +/// servicing thread prior to final cleanup. The +/// request *does* generate a reply on the response +/// queue, if requested. + +class HttpOpStop : public HttpOperation +{ +public: + HttpOpStop(); + +protected: + virtual ~HttpOpStop(); + +private: + HttpOpStop(const HttpOpStop &); // Not defined + void operator=(const HttpOpStop &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpStop + + +/// HttpOpNull is a do-nothing operation used for testing via +/// a basic loopback pattern. It's executed immediately by +/// the servicing thread which bounces a reply back to the +/// caller without any further delay. + +class HttpOpNull : public HttpOperation +{ +public: + HttpOpNull(); + +protected: + virtual ~HttpOpNull(); + +private: + HttpOpNull(const HttpOpNull &); // Not defined + void operator=(const HttpOpNull &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpNull + + +/// HttpOpSpin is a test-only request that puts the worker +/// thread into a cpu spin. Used for unit tests and cleanup +/// evaluation. You do not want to use this in production. +class HttpOpSpin : public HttpOperation +{ +public: + // 0 does a hard spin in the operation + // 1 does a soft spin continuously requeuing itself + HttpOpSpin(int mode); + +protected: + virtual ~HttpOpSpin(); + +private: + HttpOpSpin(const HttpOpSpin &); // Not defined + void operator=(const HttpOpSpin &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +protected: + int mMode; +}; // end class HttpOpSpin + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPERATION_H_ + diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp new file mode 100644 index 0000000000..7db19b1841 --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -0,0 +1,906 @@ +/** + * @file _httpoprequest.cpp + * @brief Definitions for internal class HttpOpRequest + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httpoprequest.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" +#include "httpoptions.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httppolicyglobal.h" +#include "_httplibcurl.h" +#include "_httpinternal.h" + +#include "llhttpstatuscodes.h" +#include "llproxy.h" + +namespace +{ + +// Attempts to parse a 'Content-Range:' header. Caller must already +// have verified that the header tag is present. The 'buffer' argument +// will be processed by strtok_r calls which will modify the buffer. +// +// @return -1 if invalid and response should be dropped, 0 if valid an +// correct, 1 if couldn't be parsed. If 0, the first, last, +// and length arguments are also written. 'length' may be +// 0 if the length wasn't available to the server. +// +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length); + + +// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and +// escape and format it for a tracing line in logging. Absolutely +// anything including NULs can be in the data. If @scrub is true, +// non-printing or non-ascii characters are replaced with spaces +// otherwise a %XX form of escaping is used. +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, + std::string & safe_line); + + +// OS-neutral string comparisons of various types +int os_strncasecmp(const char *s1, const char *s2, size_t n); +int os_strcasecmp(const char *s1, const char *s2); +char * os_strtok_r(char *str, const char *delim, char **saveptr); + + +static const char * const hdr_whitespace(" \t"); +static const char * const hdr_separator(": \t"); + +} // end anonymous namespace + + +namespace LLCore +{ + + +HttpOpRequest::HttpOpRequest() + : HttpOperation(), + mProcFlags(0U), + mReqMethod(HOR_GET), + mReqBody(NULL), + mReqOffset(0), + mReqLength(0), + mReqHeaders(NULL), + mReqOptions(NULL), + mCurlActive(false), + mCurlHandle(NULL), + mCurlService(NULL), + mCurlHeaders(NULL), + mCurlBodyPos(0), + mReplyBody(NULL), + mReplyOffset(0), + mReplyLength(0), + mReplyFullLength(0), + mReplyHeaders(NULL), + mPolicyRetries(0), + mPolicyRetryAt(HttpTime(0)), + mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT) +{ + // *NOTE: As members are added, retry initialization/cleanup + // may need to be extended in @see prepareRequest(). +} + + + +HttpOpRequest::~HttpOpRequest() +{ + if (mReqBody) + { + mReqBody->release(); + mReqBody = NULL; + } + + if (mReqOptions) + { + mReqOptions->release(); + mReqOptions = NULL; + } + + if (mReqHeaders) + { + mReqHeaders->release(); + mReqHeaders = NULL; + } + + if (mCurlHandle) + { + curl_easy_cleanup(mCurlHandle); + mCurlHandle = NULL; + } + + mCurlService = NULL; + + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } +} + + +void HttpOpRequest::stageFromRequest(HttpService * service) +{ + addRef(); + service->getPolicy().addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromReady(HttpService * service) +{ + addRef(); + service->getTransport().addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromActive(HttpService * service) +{ + if (mReplyLength) + { + // If non-zero, we received and processed a Content-Range + // header with the response. Verify that what it says + // is consistent with the received data. + if (mReplyLength != mReplyBody->size()) + { + // Not as expected, fail the request + mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + } + + if (mCurlHeaders) + { + // We take these headers out of the request now as they were + // allocated originally in this thread and the notifier doesn't + // need them. This eliminates one source of heap moving across + // threads. + + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + + addAsReply(); +} + + +void HttpOpRequest::visitNotifier(HttpRequest * request) +{ + if (mUserHandler) + { + HttpResponse * response = new HttpResponse(); + response->setStatus(mStatus); + response->setBody(mReplyBody); + response->setHeaders(mReplyHeaders); + if (mReplyOffset || mReplyLength) + { + // Got an explicit offset/length in response + response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); + } + response->setContentType(mReplyConType); + + mUserHandler->onCompleted(static_cast(this), response); + + response->release(); + } +} + + +HttpStatus HttpOpRequest::cancel() +{ + mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED); + + addAsReply(); + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_GET; + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_GET; + mReqOffset = offset; + mReqLength = len; + if (offset || len) + { + mProcFlags |= PF_SCAN_RANGE_HEADER; + } + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_POST; + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_PUT; + + return HttpStatus(); +} + + +void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + mProcFlags = 0U; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqURL = url; + if (body) + { + body->addRef(); + mReqBody = body; + } + if (headers && ! mReqHeaders) + { + headers->addRef(); + mReqHeaders = headers; + } + if (options && ! mReqOptions) + { + options->addRef(); + mReqOptions = options; + if (options->getWantHeaders()) + { + mProcFlags |= PF_SAVE_HEADERS; + } + mPolicyRetryLimit = options->getRetries(); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, HTTP_RETRY_COUNT_MIN, HTTP_RETRY_COUNT_MAX); + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), HTTP_TRACE_MIN, HTTP_TRACE_MAX)); + } +} + + +// Sets all libcurl options and data for a request. +// +// Used both for initial requests and to 'reload' for +// a retry, generally with a different CURL handle. +// Junk may be left around from a failed request and that +// needs to be cleaned out. +// +HttpStatus HttpOpRequest::prepareRequest(HttpService * service) +{ + // Scrub transport and result data for retried op case + mCurlActive = false; + mCurlHandle = NULL; + mCurlService = NULL; + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + mCurlBodyPos = 0; + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + mReplyOffset = 0; + mReplyLength = 0; + mReplyFullLength = 0; + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } + mReplyConType.clear(); + + // *FIXME: better error handling later + HttpStatus status; + + // Get policy options + HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); + + mCurlHandle = curl_easy_init(); + curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); + + if (HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) + { + // The Linksys WRT54G V5 router has an issue with frequent + // DNS lookups from LAN machines. If they happen too often, + // like for every HTTP request, the router gets annoyed after + // about 700 or so requests and starts issuing TCP RSTs to + // new connections. Reuse the DNS lookups for even a few + // seconds and no RSTs. + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); + } + else + { + // *TODO: Revisit this old DNS timeout setting - may no longer be valid + // I don't think this is valid anymore, the Multi shared DNS + // cache is working well. For the case of naked easy handles, + // consider using a shared DNS object. + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + } + curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); + curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); + + const std::string * opt_value(NULL); + long opt_long(0L); + policy.get(HttpRequest::GP_LLPROXY, &opt_long); + if (opt_long) + { + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); + } + else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value)) + { + // *TODO: This is fine for now but get fuller socks5/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + if (policy.get(HttpRequest::GP_CA_PATH, &opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + } + if (policy.get(HttpRequest::GP_CA_FILE, &opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); + } + + switch (mReqMethod) + { + case HOR_GET: + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); + break; + + case HOR_POST: + { + curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); + long data_size(0); + if (mReqBody) + { + data_size = mReqBody->size(); + } + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); + } + break; + + case HOR_PUT: + { + curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1); + long data_size(0); + if (mReqBody) + { + data_size = mReqBody->size(); + } + curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); + } + break; + + default: + LL_ERRS("CoreHttp") << "Invalid HTTP method in request: " + << int(mReqMethod) << ". Can't recover." + << LL_ENDL; + break; + } + + // Tracing + if (mTracing >= HTTP_TRACE_CURL_HEADERS) + { + curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback); + } + + // There's a CURLOPT for this now... + if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) + { + static const char * const fmt1("Range: bytes=%lu-%lu"); + static const char * const fmt2("Range: bytes=%lu-"); + + char range_line[64]; + +#if LL_WINDOWS + _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, + (mReqLength ? fmt1 : fmt2), + (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); +#else + snprintf(range_line, sizeof(range_line), + (mReqLength ? fmt1 : fmt2), + (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); +#endif // LL_WINDOWS + range_line[sizeof(range_line) - 1] = '\0'; + mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); + } + + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + + // Request options + long timeout(HTTP_REQUEST_TIMEOUT_DEFAULT); + if (mReqOptions) + { + timeout = mReqOptions->getTimeout(); + timeout = llclamp(timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX); + } + curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); + curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); + + // Request headers + if (mReqHeaders) + { + // Caller's headers last to override + mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); + } + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); + + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this); + } + + if (status) + { + mCurlService = service; + } + return status; +} + + +size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + HttpOpRequest * op(static_cast(userdata)); + + if (! op->mReplyBody) + { + op->mReplyBody = new BufferArray(); + } + const size_t req_size(size * nmemb); + const size_t write_size(op->mReplyBody->append(static_cast(data), req_size)); + return write_size; +} + + +size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + HttpOpRequest * op(static_cast(userdata)); + + if (! op->mReqBody) + { + return 0; + } + const size_t req_size(size * nmemb); + const size_t body_size(op->mReqBody->size()); + if (body_size <= op->mCurlBodyPos) + { + LL_WARNS("HttpCore") << "Request body position beyond body size. Aborting request." + << LL_ENDL; + return 0; + } + + const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos)); + const size_t read_size(op->mReqBody->read(op->mCurlBodyPos, static_cast(data), do_size)); + op->mCurlBodyPos += read_size; + return read_size; +} + + +size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + static const char status_line[] = "HTTP/"; + static const size_t status_line_len = sizeof(status_line) - 1; + + static const char con_ran_line[] = "content-range:"; + static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; + + HttpOpRequest * op(static_cast(userdata)); + + const size_t hdr_size(size * nmemb); + const char * hdr_data(static_cast(data)); // Not null terminated + + if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) + { + // One of possibly several status lines. Reset what we know and start over + // taking results from the last header stanza we receive. + op->mReplyOffset = 0; + op->mReplyLength = 0; + op->mReplyFullLength = 0; + op->mStatus = HttpStatus(); + if (op->mReplyHeaders) + { + op->mReplyHeaders->mHeaders.clear(); + } + } + + // Nothing in here wants a final CR/LF combination. Remove + // it as much as possible. + size_t wanted_hdr_size(hdr_size); + if (wanted_hdr_size && '\n' == hdr_data[wanted_hdr_size - 1]) + { + if (--wanted_hdr_size && '\r' == hdr_data[wanted_hdr_size - 1]) + { + --wanted_hdr_size; + } + } + + // Save header if caller wants them in the response + if (op->mProcFlags & PF_SAVE_HEADERS) + { + // Save headers in response + if (! op->mReplyHeaders) + { + op->mReplyHeaders = new HttpHeaders; + } + op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size)); + } + + // Detect and parse 'Content-Range' headers + if (op->mProcFlags & PF_SCAN_RANGE_HEADER) + { + char hdr_buffer[128]; // Enough for a reasonable header + size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1)); + + memcpy(hdr_buffer, hdr_data, frag_size); + hdr_buffer[frag_size] = '\0'; + if (frag_size > con_ran_line_len && + ! os_strncasecmp(hdr_buffer, con_ran_line, con_ran_line_len)) + { + unsigned int first(0), last(0), length(0); + int status; + + if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length))) + { + // Success, record the fragment position + op->mReplyOffset = first; + op->mReplyLength = last - first + 1; + op->mReplyFullLength = length; + } + else if (-1 == status) + { + // Response is badly formed and shouldn't be accepted + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + else + { + // Ignore the unparsable. + LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header: '" + << std::string(hdr_data, frag_size) + << "'. Ignoring." + << LL_ENDL; + } + } + } + + return hdr_size; +} + + +int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) +{ + HttpOpRequest * op(static_cast(userdata)); + + std::string safe_line; + std::string tag; + bool logit(false); + len = (std::min)(len, size_t(256)); // Keep things reasonable in all cases + + switch (info) + { + case CURLINFO_TEXT: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "TEXT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_IN: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "HEADERIN"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_OUT: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "HEADEROUT"; + escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line + logit = true; + } + break; + + case CURLINFO_DATA_IN: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "DATAIN"; + logit = true; + if (op->mTracing >= HTTP_TRACE_CURL_BODIES) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + case CURLINFO_DATA_OUT: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "DATAOUT"; + logit = true; + if (op->mTracing >= HTTP_TRACE_CURL_BODIES) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + default: + logit = false; + break; + } + + if (logit) + { + LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: " + << static_cast(op) + << ", Type: " << tag + << ", Data: " << safe_line + << LL_ENDL; + } + + return 0; +} + + +} // end namespace LLCore + + +// ======================================= +// Anonymous Namespace +// ======================================= + +namespace +{ + +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length) +{ + char * tok_state(NULL), * tok(NULL); + bool match(true); + + if (! os_strtok_r(buffer, hdr_separator, &tok_state)) + match = false; + if (match && (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state))) + match = 0 == os_strcasecmp("bytes", tok); + if (match && ! (tok = os_strtok_r(NULL, " \t", &tok_state))) + match = false; + if (match) + { + unsigned int lcl_first(0), lcl_last(0), lcl_len(0); + +#if LL_WINDOWS + if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#else + if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#endif // LL_WINDOWS + { + if (lcl_first > lcl_last || lcl_last >= lcl_len) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = lcl_len; + return 0; + } +#if LL_WINDOWS + if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#else + if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#endif // LL_WINDOWS + { + if (lcl_first > lcl_last) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = 0; + return 0; + } + } + + // Header is there but badly/unexpectedly formed, try to ignore it. + return 1; +} + + +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) +{ + std::string out; + len = (std::min)(len, size_t(200)); + out.reserve(3 * len); + for (int i(0); i < len; ++i) + { + unsigned char uc(static_cast(buffer[i])); + + if (uc < 32 || uc > 126) + { + if (scrub) + { + out.append(1, ' '); + } + else + { + static const char hex[] = "0123456789ABCDEF"; + char convert[4]; + + convert[0] = '%'; + convert[1] = hex[(uc >> 4) % 16]; + convert[2] = hex[uc % 16]; + convert[3] = '\0'; + out.append(convert); + } + } + else + { + out.append(1, buffer[i]); + } + } + safe_line.swap(out); +} + + +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ +#if LL_WINDOWS + return _strnicmp(s1, s2, n); +#else + return strncasecmp(s1, s2, n); +#endif // LL_WINDOWS +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ +#if LL_WINDOWS + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif // LL_WINDOWS +} + + +char * os_strtok_r(char *str, const char *delim, char ** savestate) +{ +#if LL_WINDOWS + return strtok_s(str, delim, savestate); +#else + return strtok_r(str, delim, savestate); +#endif +} + + +} // end anonymous namespace + + diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h new file mode 100644 index 0000000000..7b65d17783 --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.h @@ -0,0 +1,219 @@ +/** + * @file _httpoprequest.h + * @brief Internal declarations for the HttpOpRequest subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_OPREQUEST_H_ +#define _LLCORE_HTTP_OPREQUEST_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include +#include + +#include "httpcommon.h" +#include "httprequest.h" +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +class BufferArray; +class HttpHeaders; +class HttpOptions; + + +/// HttpOpRequest requests a supported HTTP method invocation with +/// option and header overrides. +/// +/// Essentially an RPC to get an HTTP GET, POST or PUT executed +/// asynchronously with options to override behaviors and HTTP +/// headers. +/// +/// Constructor creates a raw object incapable of useful work. +/// A subsequent call to one of the setupXXX() methods provides +/// the information needed to make a working request which can +/// then be enqueued to a request queue. +/// + +class HttpOpRequest : public HttpOperation +{ +public: + HttpOpRequest(); + +protected: + virtual ~HttpOpRequest(); // Use release() + +private: + HttpOpRequest(const HttpOpRequest &); // Not defined + void operator=(const HttpOpRequest &); // Not defined + +public: + enum EMethod + { + HOR_GET, + HOR_POST, + HOR_PUT + }; + + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + virtual void visitNotifier(HttpRequest * request); + +public: + /// Setup Methods + /// + /// Basically an RPC setup for each type of HTTP method + /// invocation with one per method type. These are + /// generally invoked right after construction. + /// + /// Threading: called by application thread + /// + HttpStatus setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus setupPost(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + + // Internal method used to setup the libcurl options for a request. + // Does all the libcurl handle setup in one place. + // + // Threading: called by worker thread + // + HttpStatus prepareRequest(HttpService * service); + + virtual HttpStatus cancel(); + +protected: + // Common setup for all the request methods. + // + // Threading: called by application thread + // + void setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + + // libcurl operational callbacks + // + // Threading: called by worker thread + // + static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); + static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); + static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata); + +protected: + unsigned int mProcFlags; + static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; + static const unsigned int PF_SAVE_HEADERS = 0x00000002U; + +public: + // Request data + EMethod mReqMethod; + std::string mReqURL; + BufferArray * mReqBody; + off_t mReqOffset; + size_t mReqLength; + HttpHeaders * mReqHeaders; + HttpOptions * mReqOptions; + + // Transport data + bool mCurlActive; + CURL * mCurlHandle; + HttpService * mCurlService; + curl_slist * mCurlHeaders; + size_t mCurlBodyPos; + + // Result data + HttpStatus mStatus; + BufferArray * mReplyBody; + off_t mReplyOffset; + size_t mReplyLength; + size_t mReplyFullLength; + HttpHeaders * mReplyHeaders; + std::string mReplyConType; + + // Policy data + int mPolicyRetries; + HttpTime mPolicyRetryAt; + int mPolicyRetryLimit; +}; // end class HttpOpRequest + + +/// HttpOpRequestCompare isn't an operation but a uniform comparison +/// functor for STL containers that order by priority. Mainly +/// used for the ready queue container but defined here. +class HttpOpRequestCompare +{ +public: + bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + { + return lhs->mReqPriority > rhs->mReqPriority; + } +}; // end class HttpOpRequestCompare + + +// --------------------------------------- +// Free functions +// --------------------------------------- + +// Internal function to append the contents of an HttpHeaders +// instance to a curl_slist object. +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPREQUEST_H_ + diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp new file mode 100644 index 0000000000..8198528a9b --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -0,0 +1,97 @@ +/** + * @file _httpopsetget.cpp + * @brief Definitions for internal class HttpOpSetGet + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httpopsetget.h" + +#include "httpcommon.h" + +#include "_httpservice.h" +#include "_httppolicy.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpSetget +// ================================== + + +HttpOpSetGet::HttpOpSetGet() + : HttpOperation(), + mIsGlobal(false), + mDoSet(false), + mSetting(-1), // Nothing requested + mLongValue(0L) +{} + + +HttpOpSetGet::~HttpOpSetGet() +{} + + +void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting) +{ + mIsGlobal = true; + mSetting = setting; +} + + +void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value) +{ + mIsGlobal = true; + mDoSet = true; + mSetting = setting; + mStrValue = value; +} + + +void HttpOpSetGet::stageFromRequest(HttpService * service) +{ + HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions()); + HttpRequest::EGlobalPolicy setting(static_cast(mSetting)); + + if (mDoSet) + { + mStatus = pol_opt.set(setting, mStrValue); + } + if (mStatus) + { + const std::string * value(NULL); + if ((mStatus = pol_opt.get(setting, &value))) + { + mStrValue = *value; + } + } + + addAsReply(); +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h new file mode 100644 index 0000000000..6966b9d94e --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.h @@ -0,0 +1,83 @@ +/** + * @file _httpopsetget.h + * @brief Internal declarations for the HttpOpSetGet subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_OPSETGET_H_ +#define _LLCORE_HTTP_OPSETGET_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetGet requests dynamic changes to policy and +/// configuration settings. +/// +/// *NOTE: Expect this to change. Don't really like it yet. + +class HttpOpSetGet : public HttpOperation +{ +public: + HttpOpSetGet(); + +protected: + virtual ~HttpOpSetGet(); // Use release() + +private: + HttpOpSetGet(const HttpOpSetGet &); // Not defined + void operator=(const HttpOpSetGet &); // Not defined + +public: + /// Threading: called by application thread + void setupGet(HttpRequest::EGlobalPolicy setting); + void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value); + + virtual void stageFromRequest(HttpService *); + +public: + // Request data + bool mIsGlobal; + bool mDoSet; + int mSetting; + long mLongValue; + std::string mStrValue; + +}; // end class HttpOpSetGet + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPSETGET_H_ + diff --git a/indra/llcorehttp/_httpopsetpriority.cpp b/indra/llcorehttp/_httpopsetpriority.cpp new file mode 100644 index 0000000000..d48c7a0b7d --- /dev/null +++ b/indra/llcorehttp/_httpopsetpriority.cpp @@ -0,0 +1,63 @@ +/** + * @file _httpopsetpriority.cpp + * @brief Definitions for internal classes based on HttpOpSetPriority + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httpopsetpriority.h" + +#include "httpresponse.h" +#include "httphandler.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpOpSetPriority::HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority) + : HttpOperation(), + mHandle(handle), + mPriority(priority) +{} + + +HttpOpSetPriority::~HttpOpSetPriority() +{} + + +void HttpOpSetPriority::stageFromRequest(HttpService * service) +{ + // Do operations + if (! service->changePriority(mHandle, mPriority)) + { + // Request not found, fail the final status + mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND); + } + + // Move directly to response queue + addAsReply(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h new file mode 100644 index 0000000000..31706b737c --- /dev/null +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -0,0 +1,73 @@ +/** + * @file _httpsetpriority.h + * @brief Internal declarations for HttpSetPriority + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_SETPRIORITY_H_ +#define _LLCORE_HTTP_SETPRIORITY_H_ + + +#include "httpcommon.h" +#include "httprequest.h" +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetPriority is an immediate request that +/// searches the various queues looking for a given +/// request handle and changing it's priority if +/// found. +/// +/// *NOTE: This will very likely be removed in the near future +/// when priority is removed from the library. + +class HttpOpSetPriority : public HttpOperation +{ +public: + HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority); + +protected: + virtual ~HttpOpSetPriority(); + +private: + HttpOpSetPriority(const HttpOpSetPriority &); // Not defined + void operator=(const HttpOpSetPriority &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +protected: + // Request Data + HttpHandle mHandle; + HttpRequest::priority_t mPriority; +}; // end class HttpOpSetPriority + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_SETPRIORITY_H_ + diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp new file mode 100644 index 0000000000..76c1e22431 --- /dev/null +++ b/indra/llcorehttp/_httppolicy.cpp @@ -0,0 +1,387 @@ +/** + * @file _httppolicy.cpp + * @brief Internal definitions of the Http policy thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httppolicy.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" +#include "_httplibcurl.h" +#include "_httppolicyclass.h" + +#include "lltimer.h" + + +namespace LLCore +{ + + +// Per-policy-class data for a running system. +// Collection of queues, parameters, history, metrics, etc. +// for a single policy class. +// +// Threading: accessed only by worker thread +struct HttpPolicy::State +{ +public: + State() + : mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT), + mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT), + mConnMin(1), + mNextSample(0), + mErrorCount(0), + mErrorFactor(0) + {} + + HttpReadyQueue mReadyQueue; + HttpRetryQueue mRetryQueue; + + HttpPolicyClass mOptions; + + long mConnMax; + long mConnAt; + long mConnMin; + + HttpTime mNextSample; + unsigned long mErrorCount; + unsigned long mErrorFactor; +}; + + +HttpPolicy::HttpPolicy(HttpService * service) + : mActiveClasses(0), + mState(NULL), + mService(service) +{} + + +HttpPolicy::~HttpPolicy() +{ + shutdown(); + + mService = NULL; +} + + +void HttpPolicy::shutdown() +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); + while (! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + retryq.pop(); + + op->cancel(); + op->release(); + } + + HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); + while (! readyq.empty()) + { + HttpOpRequest * op(readyq.top()); + readyq.pop(); + + op->cancel(); + op->release(); + } + } + delete [] mState; + mState = NULL; + mActiveClasses = 0; +} + + +void HttpPolicy::start(const HttpPolicyGlobal & global, + const std::vector & classes) +{ + llassert_always(! mState); + + mGlobalOptions = global; + mActiveClasses = classes.size(); + mState = new State [mActiveClasses]; + for (int i(0); i < mActiveClasses; ++i) + { + mState[i].mOptions = classes[i]; + mState[i].mConnMax = classes[i].mConnectionLimit; + mState[i].mConnAt = mState[i].mConnMax; + mState[i].mConnMin = 2; + } +} + + +void HttpPolicy::addOp(HttpOpRequest * op) +{ + const int policy_class(op->mReqPolicy); + + op->mPolicyRetries = 0; + mState[policy_class].mReadyQueue.push(op); +} + + +void HttpPolicy::retryOp(HttpOpRequest * op) +{ + static const HttpTime retry_deltas[] = + { + 250000, // 1st retry in 0.25 S, etc... + 500000, + 1000000, + 2000000, + 5000000 // ... to every 5.0 S. + }; + static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1); + + const HttpTime now(totalTime()); + const int policy_class(op->mReqPolicy); + + const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]); + op->mPolicyRetryAt = now + delta; + ++op->mPolicyRetries; + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " retry " << op->mPolicyRetries + << " scheduled for +" << (delta / HttpTime(1000)) + << " mS. Status: " << op->mStatus.toHex() + << LL_ENDL; + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } + mState[policy_class].mRetryQueue.push(op); +} + + +// Attempt to deliver requests to the transport layer. +// +// Tries to find HTTP requests for each policy class with +// available capacity. Starts with the retry queue first +// looking for requests that have waited long enough then +// moves on to the ready queue. +// +// If all queues are empty, will return an indication that +// the worker thread may sleep hard otherwise will ask for +// normal polling frequency. +// +HttpService::ELoopSpeed HttpPolicy::processReadyQueue() +{ + const HttpTime now(totalTime()); + HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); + HttpLibcurl & transport(mService->getTransport()); + + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + int active(transport.getActiveCountInClass(policy_class)); + int needed(state.mConnAt - active); // Expect negatives here + + HttpRetryQueue & retryq(state.mRetryQueue); + HttpReadyQueue & readyq(state.mReadyQueue); + + if (needed > 0) + { + // First see if we have any retries... + while (needed > 0 && ! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + if (op->mPolicyRetryAt > now) + break; + + retryq.pop(); + + op->stageFromReady(mService); + op->release(); + + --needed; + } + + // Now go on to the new requests... + while (needed > 0 && ! readyq.empty()) + { + HttpOpRequest * op(readyq.top()); + readyq.pop(); + + op->stageFromReady(mService); + op->release(); + + --needed; + } + } + + if (! readyq.empty() || ! retryq.empty()) + { + // If anything is ready, continue looping... + result = HttpService::NORMAL; + } + } // end foreach policy_class + + return result; +} + + +bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + // We don't scan retry queue because a priority change there + // is meaningless. The request will be issued based on retry + // intervals not priority value, which is now moot. + + // Scan ready queue for requests that match policy + HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); + for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) + { + HttpReadyQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c.erase(cur); // All iterators are now invalidated + op->mReqPriority = priority; + state.mReadyQueue.push(op); // Re-insert using adapter class + return true; + } + } + } + + return false; +} + + +bool HttpPolicy::cancel(HttpHandle handle) +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + + // Scan retry queue + HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container()); + for (HttpRetryQueue::container_type::iterator iter(c1.begin()); c1.end() != iter;) + { + HttpRetryQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c1.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + + // Scan ready queue + HttpReadyQueue::container_type & c2(state.mReadyQueue.get_container()); + for (HttpReadyQueue::container_type::iterator iter(c2.begin()); c2.end() != iter;) + { + HttpReadyQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c2.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + } + + return false; +} + + +bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) +{ + static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); + static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); + static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); + static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); + static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR); + + // Retry or finalize + if (! op->mStatus) + { + // If this failed, we might want to retry. Have to inspect + // the status a little more deeply for those reasons worth retrying... + if (op->mPolicyRetries < op->mPolicyRetryLimit && + ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || + cant_connect == op->mStatus || + cant_res_proxy == op->mStatus || + cant_res_host == op->mStatus || + send_error == op->mStatus || + recv_error == op->mStatus || + upload_failed == op->mStatus || + op_timedout == op->mStatus || + post_error == op->mStatus)) + { + // Okay, worth a retry. We include 499 in this test as + // it's the old 'who knows?' error from many grid services... + retryOp(op); + return true; // still active/ready + } + } + + // This op is done, finalize it delivering it to the reply queue... + if (! op->mStatus) + { + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " failed after " << op->mPolicyRetries + << " retries. Reason: " << op->mStatus.toString() + << " (" << op->mStatus.toHex() << ")" + << LL_ENDL; + } + else if (op->mPolicyRetries) + { + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " succeeded on retry " << op->mPolicyRetries << "." + << LL_ENDL; + } + + op->stageFromActive(mService); + op->release(); + return false; // not active +} + + +int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const +{ + if (policy_class < mActiveClasses) + { + return (mState[policy_class].mReadyQueue.size() + + mState[policy_class].mRetryQueue.size()); + } + return 0; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h new file mode 100644 index 0000000000..03d92c0b8e --- /dev/null +++ b/indra/llcorehttp/_httppolicy.h @@ -0,0 +1,161 @@ +/** + * @file _httppolicy.h + * @brief Declarations for internal class enforcing policy decisions. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_POLICY_H_ +#define _LLCORE_HTTP_POLICY_H_ + + +#include "httprequest.h" +#include "_httpservice.h" +#include "_httpreadyqueue.h" +#include "_httpretryqueue.h" +#include "_httppolicyglobal.h" +#include "_httppolicyclass.h" +#include "_httpinternal.h" + + +namespace LLCore +{ + +class HttpReadyQueue; +class HttpOpRequest; + + +/// Implements class-based queuing policies for an HttpService instance. +/// +/// Threading: Single-threaded. Other than for construction/destruction, +/// all methods are expected to be invoked in a single thread, typically +/// a worker thread of some sort. +class HttpPolicy +{ +public: + HttpPolicy(HttpService *); + virtual ~HttpPolicy(); + +private: + HttpPolicy(const HttpPolicy &); // Not defined + void operator=(const HttpPolicy &); // Not defined + +public: + /// Cancel all ready and retry requests sending them to + /// their notification queues. Release state resources + /// making further request handling impossible. + /// + /// Threading: called by worker thread + void shutdown(); + + /// Deliver policy definitions and enable handling of + /// requests. One-time call invoked before starting + /// the worker thread. + /// + /// Threading: called by application thread + void start(const HttpPolicyGlobal & global, + const std::vector & classes); + + /// Give the policy layer some cycles to scan the ready + /// queue promoting higher-priority requests to active + /// as permited. + /// + /// @return Indication of how soon this method + /// should be called again. + /// + /// Threading: called by worker thread + HttpService::ELoopSpeed processReadyQueue(); + + /// Add request to a ready queue. Caller is expected to have + /// provided us with a reference count to hold the request. (No + /// additional references will be added.) + /// + /// OpRequest is owned by the request queue after this call + /// and should not be modified by anyone until retrieved + /// from queue. + /// + /// Threading: called by any thread + void addOp(HttpOpRequest *); + + /// Similar to addOp, used when a caller wants to retry a + /// request that has failed. It's placed on a special retry + /// queue but ordered by retry time not priority. Otherwise, + /// handling is the same and retried operations are considered + /// before new ones but that doesn't guarantee completion + /// order. + /// + /// Threading: called by worker thread + void retryOp(HttpOpRequest *); + + /// Attempt to change the priority of an earlier request. + /// Request that Shadows HttpService's method + /// + /// Threading: called by worker thread + bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + + /// Attempt to cancel a previous request. + /// Shadows HttpService's method as well + /// + /// Threading: called by worker thread + bool cancel(HttpHandle handle); + + /// When transport is finished with an op and takes it off the + /// active queue, it is delivered here for dispatch. Policy + /// may send it back to the ready/retry queues if it needs another + /// go or we may finalize it and send it on to the reply queue. + /// + /// @return Returns true of the request is still active + /// or ready after staging, false if has been + /// sent on to the reply queue. + /// + /// Threading: called by worker thread + bool stageAfterCompletion(HttpOpRequest * op); + + // Get pointer to global policy options. Caller is expected + // to do context checks like no setting once running. + /// + /// Threading: called by any thread *but* the object may + /// only be modified by the worker thread once running. + /// + HttpPolicyGlobal & getGlobalOptions() + { + return mGlobalOptions; + } + + /// Get ready counts for a particular policy class + /// + /// Threading: called by worker thread + int getReadyCount(HttpRequest::policy_t policy_class) const; + +protected: + struct State; + + int mActiveClasses; + State * mState; + HttpService * mService; // Naked pointer, not refcounted, not owner + HttpPolicyGlobal mGlobalOptions; + +}; // end class HttpPolicy + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_H_ diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp new file mode 100644 index 0000000000..a23b81322c --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -0,0 +1,125 @@ +/** + * @file _httppolicyclass.cpp + * @brief Definitions for internal class defining class policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httppolicyclass.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpPolicyClass::HttpPolicyClass() + : mSetMask(0UL), + mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mPipelining(0) +{} + + +HttpPolicyClass::~HttpPolicyClass() +{} + + +HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mPerHostConnectionLimit = other.mPerHostConnectionLimit; + mPipelining = other.mPipelining; + } + return *this; +} + + +HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other) + : mSetMask(other.mSetMask), + mConnectionLimit(other.mConnectionLimit), + mPerHostConnectionLimit(other.mPerHostConnectionLimit), + mPipelining(other.mPipelining) +{} + + +HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX)); + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit); + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + mPipelining = llclamp(value, 0L, 1L); + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); + + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + src = &mConnectionLimit; + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + src = &mPerHostConnectionLimit; + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + src = &mPipelining; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; + return HttpStatus(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h new file mode 100644 index 0000000000..d175413cbd --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.h @@ -0,0 +1,59 @@ +/** + * @file _httppolicyclass.h + * @brief Declarations for internal class defining policy class options. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_POLICY_CLASS_H_ +#define _LLCORE_HTTP_POLICY_CLASS_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyClass +{ +public: + HttpPolicyClass(); + ~HttpPolicyClass(); + + HttpPolicyClass & operator=(const HttpPolicyClass &); + HttpPolicyClass(const HttpPolicyClass &); // Not defined + +public: + HttpStatus set(HttpRequest::EClassPolicy opt, long value); + HttpStatus get(HttpRequest::EClassPolicy opt, long * value); + +public: + unsigned long mSetMask; + long mConnectionLimit; + long mPerHostConnectionLimit; + long mPipelining; +}; // end class HttpPolicyClass + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_CLASS_H_ diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp new file mode 100644 index 0000000000..72f409d3b1 --- /dev/null +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -0,0 +1,175 @@ +/** + * @file _httppolicyglobal.cpp + * @brief Definitions for internal class defining global policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httppolicyglobal.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpPolicyGlobal::HttpPolicyGlobal() + : mSetMask(0UL), + mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mTrace(HTTP_TRACE_OFF), + mUseLLProxy(0) +{} + + +HttpPolicyGlobal::~HttpPolicyGlobal() +{} + + +HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mCAPath = other.mCAPath; + mCAFile = other.mCAFile; + mHttpProxy = other.mHttpProxy; + mTrace = other.mTrace; + mUseLLProxy = other.mUseLLProxy; + } + return *this; +} + + +HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::GP_CONNECTION_LIMIT: + mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX)); + break; + + case HttpRequest::GP_TRACE: + mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX)); + break; + + case HttpRequest::GP_LLPROXY: + mUseLLProxy = llclamp(value, 0L, 1L); + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value) +{ + switch (opt) + { + case HttpRequest::GP_CA_PATH: + mCAPath = value; + break; + + case HttpRequest::GP_CA_FILE: + mCAFile = value; + break; + + case HttpRequest::GP_HTTP_PROXY: + mCAFile = value; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); + + switch (opt) + { + case HttpRequest::GP_CONNECTION_LIMIT: + src = &mConnectionLimit; + break; + + case HttpRequest::GP_TRACE: + src = &mTrace; + break; + + case HttpRequest::GP_LLPROXY: + src = &mUseLLProxy; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + const std::string * src(NULL); + + switch (opt) + { + case HttpRequest::GP_CA_PATH: + src = &mCAPath; + break; + + case HttpRequest::GP_CA_FILE: + src = &mCAFile; + break; + + case HttpRequest::GP_HTTP_PROXY: + src = &mHttpProxy; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = src; + return HttpStatus(); +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h new file mode 100644 index 0000000000..a50d0e4188 --- /dev/null +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -0,0 +1,66 @@ +/** + * @file _httppolicyglobal.h + * @brief Declarations for internal class defining global policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_POLICY_GLOBAL_H_ +#define _LLCORE_HTTP_POLICY_GLOBAL_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyGlobal +{ +public: + HttpPolicyGlobal(); + ~HttpPolicyGlobal(); + + HttpPolicyGlobal & operator=(const HttpPolicyGlobal &); + +private: + HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined + +public: + HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); + HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value); + +public: + unsigned long mSetMask; + long mConnectionLimit; + std::string mCAPath; + std::string mCAFile; + std::string mHttpProxy; + long mTrace; + long mUseLLProxy; +}; // end class HttpPolicyGlobal + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_GLOBAL_H_ diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h new file mode 100644 index 0000000000..5f19a9c5f9 --- /dev/null +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -0,0 +1,124 @@ +/** + * @file _httpreadyqueue.h + * @brief Internal declaration for the operation ready queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_READY_QUEUE_H_ +#define _LLCORE_HTTP_READY_QUEUE_H_ + + +#include + +#include "_httpinternal.h" +#include "_httpoprequest.h" + + +namespace LLCore +{ + +/// HttpReadyQueue provides a simple priority queue for HttpOpRequest objects. +/// +/// This uses the priority_queue adaptor class to provide the queue +/// as well as the ordering scheme while allowing us access to the +/// raw container if we follow a few simple rules. One of the more +/// important of those rules is that any iterator becomes invalid +/// on element erasure. So pay attention. +/// +/// If LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY tests true, the class +/// implements a std::priority_queue interface but on std::deque +/// behavior to eliminate sensitivity to priority. In the future, +/// this will likely become the only behavior or it may become +/// a run-time election. +/// +/// Threading: not thread-safe. Expected to be used entirely by +/// a single thread, typically a worker thread of some sort. + +#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + +typedef std::deque HttpReadyQueueBase; + +#else + +typedef std::priority_queue, + LLCore::HttpOpRequestCompare> HttpReadyQueueBase; + +#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + +class HttpReadyQueue : public HttpReadyQueueBase +{ +public: + HttpReadyQueue() + : HttpReadyQueueBase() + {} + + ~HttpReadyQueue() + {} + +protected: + HttpReadyQueue(const HttpReadyQueue &); // Not defined + void operator=(const HttpReadyQueue &); // Not defined + +public: + +#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + // Types and methods needed to make a std::deque look + // more like a std::priority_queue, at least for our + // purposes. + typedef HttpReadyQueueBase container_type; + + const_reference top() const + { + return front(); + } + + void pop() + { + pop_front(); + } + + void push(const value_type & v) + { + push_back(v); + } + +#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + + const container_type & get_container() const + { + return *this; + } + + container_type & get_container() + { + return *this; + } + +}; // end class HttpReadyQueue + + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_READY_QUEUE_H_ diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp new file mode 100644 index 0000000000..558b7bdee9 --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -0,0 +1,107 @@ +/** + * @file _httpreplyqueue.cpp + * @brief Internal definitions for the operation reply queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httpreplyqueue.h" + + +#include "_mutex.h" +#include "_thread.h" +#include "_httpoperation.h" + +using namespace LLCoreInt; + + +namespace LLCore +{ + + +HttpReplyQueue::HttpReplyQueue() + : RefCounted(true) +{ +} + + +HttpReplyQueue::~HttpReplyQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpReplyQueue::addOp(HttpOperation * op) +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueue.push_back(op); + } + mQueueCV.notify_all(); +} + + +HttpOperation * HttpReplyQueue::fetchOp() +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + if (mQueue.empty()) + return NULL; + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + + +void HttpReplyQueue::fetchAll(OpContainer & ops) +{ + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + + { + HttpScopedLock lock(mQueueMutex); + + if (! mQueue.empty()) + { + mQueue.swap(ops); + } + } + + // Caller also acquires the reference counts on each op. + return; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h new file mode 100644 index 0000000000..4220a09a3b --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -0,0 +1,108 @@ +/** + * @file _httpreplyqueue.h + * @brief Internal declarations for the operation reply queue. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_REPLY_QUEUE_H_ +#define _LLCORE_HTTP_REPLY_QUEUE_H_ + + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Almost identical to the HttpRequestQueue class but +/// whereas that class is a singleton and is known to the +/// HttpService object, this queue is 1:1 with HttpRequest +/// instances and isn't explicitly referenced by the +/// service object. Instead, HttpOperation objects that +/// want to generate replies back to their creators also +/// keep references to the corresponding HttpReplyQueue. +/// The HttpService plumbing then simply delivers replies +/// to the requested reply queue. +/// +/// One result of that is that the fetch operations do +/// not have a wait forever option. The service object +/// doesn't keep handles on everything it would need to +/// notify so it can't wake up sleepers should it need to +/// shutdown. So only non-blocking or timed-blocking modes +/// are anticipated. These are how most application consumers +/// will be coded anyway so it shouldn't be too much of a +/// burden. + +class HttpReplyQueue : public LLCoreInt::RefCounted +{ +public: + /// Caller acquires a Refcount on construction + HttpReplyQueue(); + +protected: + virtual ~HttpReplyQueue(); // Use release() + +private: + HttpReplyQueue(const HttpReplyQueue &); // Not defined + void operator=(const HttpReplyQueue &); // Not defined + +public: + typedef std::vector OpContainer; + + /// Insert an object at the back of the reply queue. + /// + /// Library also takes possession of one reference count to pass + /// through the queue. + /// + /// Threading: callable by any thread. + void addOp(HttpOperation * op); + + /// Fetch an operation from the head of the queue. Returns + /// NULL if none exists. + /// + /// Caller acquires reference count on returned operation. + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(); + + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(OpContainer & ops); + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + +}; // end class HttpReplyQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REPLY_QUEUE_H_ diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp new file mode 100644 index 0000000000..c16966d078 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -0,0 +1,161 @@ +/** + * @file _httprequestqueue.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httprequestqueue.h" + +#include "_httpoperation.h" +#include "_mutex.h" + + +using namespace LLCoreInt; + +namespace LLCore +{ + +HttpRequestQueue * HttpRequestQueue::sInstance(NULL); + + +HttpRequestQueue::HttpRequestQueue() + : RefCounted(true), + mQueueStopped(false) +{ +} + + +HttpRequestQueue::~HttpRequestQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpRequestQueue::init() +{ + llassert_always(! sInstance); + sInstance = new HttpRequestQueue(); +} + + +void HttpRequestQueue::term() +{ + if (sInstance) + { + sInstance->release(); + sInstance = NULL; + } +} + + +HttpStatus HttpRequestQueue::addOp(HttpOperation * op) +{ + bool wake(false); + { + HttpScopedLock lock(mQueueMutex); + + if (mQueueStopped) + { + // Return op and error to caller + return HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN); + } + wake = mQueue.empty(); + mQueue.push_back(op); + } + if (wake) + { + mQueueCV.notify_all(); + } + return HttpStatus(); +} + + +HttpOperation * HttpRequestQueue::fetchOp(bool wait) +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait || mQueueStopped) + return NULL; + mQueueCV.wait(lock); + } + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + + +void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) +{ + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait || mQueueStopped) + return; + mQueueCV.wait(lock); + } + + mQueue.swap(ops); + } + + // Caller also acquires the reference counts on each op. + return; +} + + +void HttpRequestQueue::wakeAll() +{ + mQueueCV.notify_all(); +} + + +void HttpRequestQueue::stopQueue() +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueueStopped = true; + wakeAll(); + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h new file mode 100644 index 0000000000..c9c52b7233 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.h @@ -0,0 +1,141 @@ +/** + * @file _httprequestqueue.h + * @brief Internal declaration for the operation request queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_REQUEST_QUEUE_H_ +#define _LLCORE_HTTP_REQUEST_QUEUE_H_ + + +#include + +#include "httpcommon.h" +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Thread-safe queue of HttpOperation objects. Just +/// a simple queue that handles the transfer of operation +/// requests from all HttpRequest instances into the +/// singleton HttpService instance. + +class HttpRequestQueue : public LLCoreInt::RefCounted +{ +protected: + /// Caller acquires a Refcount on construction + HttpRequestQueue(); + +protected: + virtual ~HttpRequestQueue(); // Use release() + +private: + HttpRequestQueue(const HttpRequestQueue &); // Not defined + void operator=(const HttpRequestQueue &); // Not defined + +public: + static void init(); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpRequestQueue * instanceOf() + { + return sInstance; + } + +public: + typedef std::vector OpContainer; + + /// Insert an object at the back of the request queue. + /// + /// Caller must provide one refcount to the queue which takes + /// possession of the count on success. + /// + /// @return Standard status. On failure, caller + /// must dispose of the operation with + /// an explicit release() call. + /// + /// Threading: callable by any thread. + HttpStatus addOp(HttpOperation * op); + + /// Return the operation on the front of the queue. If + /// the queue is empty and @wait is false, call returns + /// immediately and a NULL pointer is returned. If true, + /// caller will sleep until explicitly woken. Wakeups + /// can be spurious and callers must expect NULL pointers + /// even if waiting is indicated. + /// + /// Caller acquires reference count any returned operation + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(bool wait); + + /// Return all queued requests to caller. The @ops argument + /// should be empty when called and will be swap()'d with + /// current contents. Handling of the @wait argument is + /// identical to @fetchOp. + /// + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(bool wait, OpContainer & ops); + + /// Wake any sleeping threads. Normal queuing operations + /// won't require this but it may be necessary for termination + /// requests. + /// + /// Threading: callable by any thread. + void wakeAll(); + + /// Disallow further request queuing. Callers to @addOp will + /// get a failure status (LLCORE, HE_SHUTTING_DOWN). Callers + /// to @fetchAll or @fetchOp will get requests that are on the + /// queue but the calls will no longer wait. Instead they'll + /// return immediately. Also wakes up all sleepers to send + /// them on their way. + /// + /// Threading: callable by any thread. + void stopQueue(); + +protected: + static HttpRequestQueue * sInstance; + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + bool mQueueStopped; + +}; // end class HttpRequestQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REQUEST_QUEUE_H_ diff --git a/indra/llcorehttp/_httpretryqueue.h b/indra/llcorehttp/_httpretryqueue.h new file mode 100644 index 0000000000..745adec09d --- /dev/null +++ b/indra/llcorehttp/_httpretryqueue.h @@ -0,0 +1,94 @@ +/** + * @file _httpretryqueue.h + * @brief Internal declaration for the operation retry queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_RETRY_QUEUE_H_ +#define _LLCORE_HTTP_RETRY_QUEUE_H_ + + +#include + +#include "_httpoprequest.h" + + +namespace LLCore +{ + +/// HttpRetryQueue provides a simple priority queue for HttpOpRequest objects. +/// +/// This uses the priority_queue adaptor class to provide the queue +/// as well as the ordering scheme while allowing us access to the +/// raw container if we follow a few simple rules. One of the more +/// important of those rules is that any iterator becomes invalid +/// on element erasure. So pay attention. +/// +/// Threading: not thread-safe. Expected to be used entirely by +/// a single thread, typically a worker thread of some sort. + +struct HttpOpRetryCompare +{ + bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + { + return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt; + } +}; + + +typedef std::priority_queue, + LLCore::HttpOpRetryCompare> HttpRetryQueueBase; + +class HttpRetryQueue : public HttpRetryQueueBase +{ +public: + HttpRetryQueue() + : HttpRetryQueueBase() + {} + + ~HttpRetryQueue() + {} + +protected: + HttpRetryQueue(const HttpRetryQueue &); // Not defined + void operator=(const HttpRetryQueue &); // Not defined + +public: + const container_type & get_container() const + { + return c; + } + + container_type & get_container() + { + return c; + } + +}; // end class HttpRetryQueue + + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_RETRY_QUEUE_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp new file mode 100644 index 0000000000..0825888d0f --- /dev/null +++ b/indra/llcorehttp/_httpservice.cpp @@ -0,0 +1,348 @@ +/** + * @file _httpservice.cpp + * @brief Internal definitions of the Http service thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_httpservice.h" + +#include +#include + +#include "_httpoperation.h" +#include "_httprequestqueue.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" +#include "_thread.h" +#include "_httpinternal.h" + +#include "lltimer.h" +#include "llthread.h" + + +namespace LLCore +{ + +HttpService * HttpService::sInstance(NULL); +volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); + +HttpService::HttpService() + : mRequestQueue(NULL), + mExitRequested(0U), + mThread(NULL), + mPolicy(NULL), + mTransport(NULL) +{ + // Create the default policy class + HttpPolicyClass pol_class; + pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT); + pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT); + pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); + mPolicyClasses.push_back(pol_class); +} + + +HttpService::~HttpService() +{ + mExitRequested = 1U; + if (RUNNING == sState) + { + // Trying to kill the service object with a running thread + // is a bit tricky. + if (mRequestQueue) + { + mRequestQueue->stopQueue(); + } + + if (mThread) + { + if (! mThread->timedJoin(250)) + { + // Failed to join, expect problems ahead so do a hard termination. + mThread->cancel(); + + LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems." + << LL_ENDL; + } + } + } + + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + delete mTransport; + mTransport = NULL; + + delete mPolicy; + mPolicy = NULL; + + if (mThread) + { + mThread->release(); + mThread = NULL; + } +} + + +void HttpService::init(HttpRequestQueue * queue) +{ + llassert_always(! sInstance); + llassert_always(NOT_INITIALIZED == sState); + sInstance = new HttpService(); + + queue->addRef(); + sInstance->mRequestQueue = queue; + sInstance->mPolicy = new HttpPolicy(sInstance); + sInstance->mTransport = new HttpLibcurl(sInstance); + sState = INITIALIZED; +} + + +void HttpService::term() +{ + if (sInstance) + { + if (RUNNING == sState && sInstance->mThread) + { + // Unclean termination. Thread appears to be running. We'll + // try to give the worker thread a chance to cancel using the + // exit flag... + sInstance->mExitRequested = 1U; + sInstance->mRequestQueue->stopQueue(); + + // And a little sleep + for (int i(0); i < 10 && RUNNING == sState; ++i) + { + ms_sleep(100); + } + } + + delete sInstance; + sInstance = NULL; + } + sState = NOT_INITIALIZED; +} + + +HttpRequest::policy_t HttpService::createPolicyClass() +{ + const HttpRequest::policy_t policy_class(mPolicyClasses.size()); + if (policy_class >= HTTP_POLICY_CLASS_LIMIT) + { + return 0; + } + mPolicyClasses.push_back(HttpPolicyClass()); + return policy_class; +} + + +bool HttpService::isStopped() +{ + // What is really wanted here is something like: + // + // HttpService * service = instanceOf(); + // return STOPPED == sState && (! service || ! service->mThread || ! service->mThread->joinable()); + // + // But boost::thread is not giving me a consistent story on joinability + // of a thread after it returns. Debug and non-debug builds are showing + // different behavior on Linux/Etch so we do a weaker test that may + // not be globally correct (i.e. thread *is* stopping, may not have + // stopped but will very soon): + + return STOPPED == sState; +} + + +/// Threading: callable by consumer thread *once*. +void HttpService::startThread() +{ + llassert_always(! mThread || STOPPED == sState); + llassert_always(INITIALIZED == sState || STOPPED == sState); + + if (mThread) + { + mThread->release(); + } + + // Push current policy definitions, enable policy & transport components + mPolicy->start(mPolicyGlobal, mPolicyClasses); + mTransport->start(mPolicyClasses.size()); + + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); + sState = RUNNING; +} + + +/// Threading: callable by worker thread. +void HttpService::stopRequested() +{ + mExitRequested = 1U; +} + + +/// Threading: callable by worker thread. +bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority) +{ + bool found(false); + + // Skip the request queue as we currently don't leave earlier + // requests sitting there. Start with the ready queue... + found = mPolicy->changePriority(handle, priority); + + // If not there, we could try the transport/active queue but priority + // doesn't really have much effect there so we don't waste cycles. + + return found; +} + + +/// Try to find the given request handle on any of the request +/// queues and cancel the operation. +/// +/// @return True if the request was canceled. +/// +/// Threading: callable by worker thread. +bool HttpService::cancel(HttpHandle handle) +{ + bool canceled(false); + + // Request can't be on request queue so skip that. + + // Check the policy component's queues first + canceled = mPolicy->cancel(handle); + + if (! canceled) + { + // If that didn't work, check transport's. + canceled = mTransport->cancel(handle); + } + + return canceled; +} + + +/// Threading: callable by worker thread. +void HttpService::shutdown() +{ + // Disallow future enqueue of requests + mRequestQueue->stopQueue(); + + // Cancel requests already on the request queue + HttpRequestQueue::OpContainer ops; + mRequestQueue->fetchAll(false, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + op->cancel(); + op->release(); + } + + // Shutdown transport canceling requests, freeing resources + mTransport->shutdown(); + + // And now policy + mPolicy->shutdown(); +} + + +// Working thread loop-forever method. Gives time to +// each of the request queue, policy layer and transport +// layer pieces and then either sleeps for a small time +// or waits for a request to come in. Repeats until +// requested to stop. +void HttpService::threadRun(LLCoreInt::HttpThread * thread) +{ + boost::this_thread::disable_interruption di; + + LLThread::registerThreadID(); + + ELoopSpeed loop(REQUEST_SLEEP); + while (! mExitRequested) + { + loop = processRequestQueue(loop); + + // Process ready queue issuing new requests as needed + ELoopSpeed new_loop = mPolicy->processReadyQueue(); + loop = (std::min)(loop, new_loop); + + // Give libcurl some cycles + new_loop = mTransport->processTransport(); + loop = (std::min)(loop, new_loop); + + // Determine whether to spin, sleep briefly or sleep for next request + if (REQUEST_SLEEP != loop) + { + ms_sleep(HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS); + } + } + + shutdown(); + sState = STOPPED; +} + + +HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) +{ + HttpRequestQueue::OpContainer ops; + const bool wait_for_req(REQUEST_SLEEP == loop); + + mRequestQueue->fetchAll(wait_for_req, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + // Process operation + if (! mExitRequested) + { + // Setup for subsequent tracing + long tracing(HTTP_TRACE_OFF); + mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); + op->mTracing = (std::max)(op->mTracing, int(tracing)); + + if (op->mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } + + // Stage + op->stageFromRequest(this); + } + + // Done with operation + op->release(); + } + + // Queue emptied, allow polling loop to sleep + return REQUEST_SLEEP; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h new file mode 100644 index 0000000000..ffe0349d4d --- /dev/null +++ b/indra/llcorehttp/_httpservice.h @@ -0,0 +1,224 @@ +/** + * @file _httpservice.h + * @brief Declarations for internal class providing HTTP service. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_SERVICE_H_ +#define _LLCORE_HTTP_SERVICE_H_ + + +#include + +#include "linden_common.h" +#include "llapr.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "_httppolicyglobal.h" +#include "_httppolicyclass.h" + + +namespace LLCoreInt +{ + +class HttpThread; + +} + + +namespace LLCore +{ + + +class HttpRequestQueue; +class HttpPolicy; +class HttpLibcurl; + + +/// The HttpService class does the work behind the request queue. It +/// oversees the HTTP workflow carrying out a number of tasks: +/// - Pulling requests from the global request queue +/// - Executing 'immediate' requests directly +/// - Prioritizing and re-queuing on internal queues the slower requests +/// - Providing cpu cycles to the libcurl plumbing +/// - Overseeing retry operations +/// +/// Note that the service object doesn't have a pointer to any +/// reply queue. These are kept by HttpRequest and HttpOperation +/// only. +/// +/// Service, Policy and Transport +/// +/// HttpService could have been a monolithic class combining a request +/// queue servicer, request policy manager and network transport. +/// Instead, to prevent monolithic growth and allow for easier +/// replacement, it was developed as three separate classes: HttpService, +/// HttpPolicy and HttpLibcurl (transport). These always exist in a +/// 1:1:1 relationship with HttpService managing instances of the other +/// two. So, these classes do not use reference counting to refer +/// to one another, their lifecycles are always managed together. + +class HttpService +{ +protected: + HttpService(); + virtual ~HttpService(); + +private: + HttpService(const HttpService &); // Not defined + void operator=(const HttpService &); // Not defined + +public: + enum EState + { + NOT_INITIALIZED = -1, + INITIALIZED, ///< init() has been called + RUNNING, ///< thread created and running + STOPPED ///< thread has committed to exiting + }; + + // Ordered enumeration of idling strategies available to + // threadRun's loop. Ordered so that std::min on values + // produces the most conservative result of multiple + // requests. + enum ELoopSpeed + { + NORMAL, ///< continuous polling of request, ready, active queues + REQUEST_SLEEP ///< can sleep indefinitely waiting for request queue write + }; + + static void init(HttpRequestQueue *); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpService * instanceOf() + { + return sInstance; + } + + /// Return the state of the worker thread. Note that the + /// transition from RUNNING to STOPPED is performed by the + /// worker thread itself. This has two weaknesses: + /// - race where the thread hasn't really stopped but will + /// - data ordering between threads where a non-worker thread + /// may see a stale RUNNING status. + /// + /// This transition is generally of interest only to unit tests + /// and these weaknesses shouldn't be any real burden. + /// + /// Threading: callable by any thread with above exceptions. + static EState getState() + { + return sState; + } + + /// Threading: callable by any thread but uses @see getState() and + /// acquires its weaknesses. + static bool isStopped(); + + /// Threading: callable by consumer thread *once*. + void startThread(); + + /// Threading: callable by worker thread. + void stopRequested(); + + /// Threading: callable by worker thread. + void shutdown(); + + /// Try to find the given request handle on any of the request + /// queues and reset the priority (and queue position) of the + /// request if found. + /// + /// @return True if the request was found somewhere. + /// + /// Threading: callable by worker thread. + bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + + /// Try to find the given request handle on any of the request + /// queues and cancel the operation. + /// + /// @return True if the request was found and canceled. + /// + /// Threading: callable by worker thread. + bool cancel(HttpHandle handle); + + /// Threading: callable by worker thread. + HttpPolicy & getPolicy() + { + return *mPolicy; + } + + /// Threading: callable by worker thread. + HttpLibcurl & getTransport() + { + return *mTransport; + } + + /// Threading: callable by worker thread. + HttpRequestQueue & getRequestQueue() + { + return *mRequestQueue; + } + + /// Threading: callable by consumer thread. + HttpPolicyGlobal & getGlobalOptions() + { + return mPolicyGlobal; + } + + /// Threading: callable by consumer thread. + HttpRequest::policy_t createPolicyClass(); + + /// Threading: callable by consumer thread. + HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) + { + llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); + return mPolicyClasses[policy_class]; + } + +protected: + void threadRun(LLCoreInt::HttpThread * thread); + + ELoopSpeed processRequestQueue(ELoopSpeed loop); + +protected: + static HttpService * sInstance; + + // === shared data === + static volatile EState sState; + HttpRequestQueue * mRequestQueue; // Refcounted + LLAtomicU32 mExitRequested; + LLCoreInt::HttpThread * mThread; + + // === consumer-thread-only data === + HttpPolicyGlobal mPolicyGlobal; + std::vector mPolicyClasses; + + // === working-thread-only data === + HttpPolicy * mPolicy; // Simple pointer, has ownership + HttpLibcurl * mTransport; // Simple pointer, has ownership +}; // end class HttpService + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_SERVICE_H_ diff --git a/indra/llcorehttp/_mutex.h b/indra/llcorehttp/_mutex.h new file mode 100644 index 0000000000..4be4d016d4 --- /dev/null +++ b/indra/llcorehttp/_mutex.h @@ -0,0 +1,55 @@ +/** + * @file _mutex.hpp + * @brief mutex type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 LLCOREINT_MUTEX_H_ +#define LLCOREINT_MUTEX_H_ + + +#include + + +namespace LLCoreInt +{ + +// MUTEX TYPES + +// unique mutex type +typedef boost::mutex HttpMutex; + +// CONDITION VARIABLES + +// standard condition variable +typedef boost::condition_variable HttpConditionVariable; + +// LOCKS AND FENCES + +// scoped unique lock +typedef boost::unique_lock HttpScopedLock; + +} + +#endif // LLCOREINT_MUTEX_H + diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp new file mode 100644 index 0000000000..e7d0b72741 --- /dev/null +++ b/indra/llcorehttp/_refcounted.cpp @@ -0,0 +1,45 @@ +/** + * @file _refcounted.cpp + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "_refcounted.h" + + +namespace LLCoreInt +{ + +#if ! LL_WINDOWS + +const S32 RefCounted::NOT_REF_COUNTED; + +#endif // ! LL_WINDOWS + +RefCounted::~RefCounted() +{} + + +} // end namespace LLCoreInt + + diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h new file mode 100644 index 0000000000..21a916b13b --- /dev/null +++ b/indra/llcorehttp/_refcounted.h @@ -0,0 +1,126 @@ +/** + * @file _refcounted.h + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 LLCOREINT__REFCOUNTED_H_ +#define LLCOREINT__REFCOUNTED_H_ + + +#include "linden_common.h" + +#include "fix_macros.h" +#include + +#include "llapr.h" + + +namespace LLCoreInt +{ + + +class RefCounted +{ +private: + RefCounted(); // Not defined - may not be default constructed + void operator=(const RefCounted &); // Not defined + +public: + explicit RefCounted(bool const implicit) + : mRefCount(implicit) + {} + + // ref-count interface + void addRef() const; + void release() const; + bool isLastRef() const; + S32 getRefCount() const; + void noRef() const; + + static const S32 NOT_REF_COUNTED = -1; + +protected: + virtual ~RefCounted(); + virtual void destroySelf(); + +private: + mutable LLAtomicS32 mRefCount; + +}; // end class RefCounted + + +inline void RefCounted::addRef() const +{ + S32 count(mRefCount++); + llassert_always(count >= 0); +} + + +inline void RefCounted::release() const +{ + S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count > 0); + count = mRefCount--; + + // clean ourselves up if that was the last reference + if (0 == count) + { + const_cast(this)->destroySelf(); + } +} + + +inline bool RefCounted::isLastRef() const +{ + const S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count >= 1); + return (1 == count); +} + + +inline S32 RefCounted::getRefCount() const +{ + const S32 result(mRefCount); + return result; +} + + +inline void RefCounted::noRef() const +{ + llassert_always(mRefCount <= 1); + mRefCount = NOT_REF_COUNTED; +} + + +inline void RefCounted::destroySelf() +{ + delete this; +} + +} // end namespace LLCoreInt + +#endif // LLCOREINT__REFCOUNTED_H_ + diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h new file mode 100644 index 0000000000..e058d660e5 --- /dev/null +++ b/indra/llcorehttp/_thread.h @@ -0,0 +1,123 @@ +/** + * @file _thread.h + * @brief thread type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 LLCOREINT_THREAD_H_ +#define LLCOREINT_THREAD_H_ + +#include "linden_common.h" + +#include +#include +#include + +#include "_refcounted.h" + +namespace LLCoreInt +{ + +class HttpThread : public RefCounted +{ +private: + HttpThread(); // Not defined + void operator=(const HttpThread &); // Not defined + + void at_exit() + { + // the thread function has exited so we need to release our reference + // to ourself so that we will be automagically cleaned up. + release(); + } + + void run() + { // THREAD CONTEXT + + // Take out additional reference for the at_exit handler + addRef(); + boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this)); + + // run the thread function + mThreadFunc(this); + + } // THREAD CONTEXT + +protected: + virtual ~HttpThread() + { + delete mThread; + } + +public: + /// Constructs a thread object for concurrent execution but does + /// not start running. Caller receives on refcount on the thread + /// instance. If the thread is started, another will be taken + /// out for the exit handler. + explicit HttpThread(boost::function threadFunc) + : RefCounted(true), // implicit reference + mThreadFunc(threadFunc) + { + // this creates a boost thread that will call HttpThread::run on this instance + // and pass it the threadfunc callable... + boost::function f = boost::bind(&HttpThread::run, this); + + mThread = new boost::thread(f); + } + + inline void join() + { + mThread->join(); + } + + inline bool timedJoin(S32 millis) + { + return mThread->timed_join(boost::posix_time::milliseconds(millis)); + } + + inline bool joinable() const + { + return mThread->joinable(); + } + + // A very hostile method to force a thread to quit + inline void cancel() + { + boost::thread::native_handle_type thread(mThread->native_handle()); +#if LL_WINDOWS + TerminateThread(thread, 0); +#else + pthread_cancel(thread); +#endif + } + +private: + boost::function mThreadFunc; + boost::thread * mThread; +}; // end class HttpThread + +} // end namespace LLCoreInt + +#endif // LLCOREINT_THREAD_H_ + + diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp new file mode 100644 index 0000000000..8eaaeed710 --- /dev/null +++ b/indra/llcorehttp/bufferarray.cpp @@ -0,0 +1,352 @@ +/** + * @file bufferarray.cpp + * @brief Implements the BufferArray scatter/gather buffer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "bufferarray.h" + + +// BufferArray is a list of chunks, each a BufferArray::Block, of contiguous +// data presented as a single array. Chunks are at least BufferArray::BLOCK_ALLOC_SIZE +// in length and can be larger. Any chunk may be partially filled or even +// empty. +// +// The BufferArray itself is sharable as a RefCounted entity. As shared +// reads don't work with the concept of a current position/seek value, +// none is kept with the object. Instead, the read and write operations +// all take position arguments. Single write/shared read isn't supported +// directly and any such attempts have to be serialized outside of this +// implementation. + +namespace LLCore +{ + + +// ================================== +// BufferArray::Block Declaration +// ================================== + +class BufferArray::Block +{ +public: + ~Block(); + + void operator delete(void *); + void operator delete(void *, size_t len); + +protected: + Block(size_t len); + + Block(const Block &); // Not defined + void operator=(const Block &); // Not defined + + // Allocate the block with the additional space for the + // buffered data at the end of the object. + void * operator new(size_t len, size_t addl_len); + +public: + // Only public entry to get a block. + static Block * alloc(size_t len); + +public: + size_t mUsed; + size_t mAlloced; + + // *NOTE: Must be last member of the object. We'll + // overallocate as requested via operator new and index + // into the array at will. + char mData[1]; +}; + + +// ================================== +// BufferArray Definitions +// ================================== + + +#if ! LL_WINDOWS +const size_t BufferArray::BLOCK_ALLOC_SIZE; +#endif // ! LL_WINDOWS + +BufferArray::BufferArray() + : LLCoreInt::RefCounted(true), + mLen(0) +{} + + +BufferArray::~BufferArray() +{ + for (container_t::iterator it(mBlocks.begin()); + it != mBlocks.end(); + ++it) + { + delete *it; + *it = NULL; + } + mBlocks.clear(); +} + + +size_t BufferArray::append(const void * src, size_t len) +{ + const size_t ret(len); + const char * c_src(static_cast(src)); + + // First, try to copy into the last block + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], c_src, copy_len); + last.mUsed += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + c_src += copy_len; + len -= copy_len; + } + } + + // Then get new blocks as needed + while (len) + { + const size_t copy_len((std::min)(len, BLOCK_ALLOC_SIZE)); + + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(BLOCK_ALLOC_SIZE); + memcpy(block->mData, c_src, copy_len); + block->mUsed = copy_len; + llassert_always(block->mUsed <= block->mAlloced); + mBlocks.push_back(block); + mLen += copy_len; + c_src += copy_len; + len -= copy_len; + } + return ret; +} + + +void * BufferArray::appendBufferAlloc(size_t len) +{ + // If someone asks for zero-length, we give them a valid pointer. + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc((std::max)(BLOCK_ALLOC_SIZE, len)); + block->mUsed = len; + mBlocks.push_back(block); + mLen += len; + return block->mData; +} + + +size_t BufferArray::read(size_t pos, void * dst, size_t len) +{ + char * c_dst(static_cast(dst)); + + if (pos >= mLen) + return 0; + size_t len_limit(mLen - pos); + len = (std::min)(len, len_limit); + if (0 == len) + return 0; + + size_t result(0), offset(0); + const int block_limit(mBlocks.size()); + int block_start(findBlock(pos, &offset)); + if (block_start < 0) + return 0; + + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mUsed - offset); + size_t block_len((std::min)(block_limit, len)); + + memcpy(c_dst, &block.mData[offset], block_len); + result += block_len; + len -= block_len; + c_dst += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + + return result; +} + + +size_t BufferArray::write(size_t pos, const void * src, size_t len) +{ + const char * c_src(static_cast(src)); + + if (pos > mLen || 0 == len) + return 0; + + size_t result(0), offset(0); + const int block_limit(mBlocks.size()); + int block_start(findBlock(pos, &offset)); + + if (block_start >= 0) + { + // Some or all of the write will be on top of + // existing data. + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mUsed - offset); + size_t block_len((std::min)(block_limit, len)); + + memcpy(&block.mData[offset], c_src, block_len); + result += block_len; + c_src += block_len; + len -= block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + } + + // Something left, see if it will fit in the free + // space of the last block. + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], c_src, copy_len); + last.mUsed += copy_len; + result += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + c_src += copy_len; + len -= copy_len; + } + } + + if (len) + { + // Some or all of the remaining write data will + // be an append. + result += append(c_src, len); + } + + return result; +} + + +int BufferArray::findBlock(size_t pos, size_t * ret_offset) +{ + *ret_offset = 0; + if (pos >= mLen) + return -1; // Doesn't exist + + const int block_limit(mBlocks.size()); + for (int i(0); i < block_limit; ++i) + { + if (pos < mBlocks[i]->mUsed) + { + *ret_offset = pos; + return i; + } + pos -= mBlocks[i]->mUsed; + } + + // Shouldn't get here but... + return -1; +} + + +bool BufferArray::getBlockStartEnd(int block, const char ** start, const char ** end) +{ + if (block < 0 || block >= mBlocks.size()) + { + return false; + } + + const Block & b(*mBlocks[block]); + *start = &b.mData[0]; + *end = &b.mData[b.mUsed]; + return true; +} + + +// ================================== +// BufferArray::Block Definitions +// ================================== + + +BufferArray::Block::Block(size_t len) + : mUsed(0), + mAlloced(len) +{ + memset(mData, 0, len); +} + + +BufferArray::Block::~Block() +{ + mUsed = 0; + mAlloced = 0; +} + + +void * BufferArray::Block::operator new(size_t len, size_t addl_len) +{ + void * mem = new char[len + addl_len + sizeof(void *)]; + return mem; +} + + +void BufferArray::Block::operator delete(void * mem) +{ + char * cmem = static_cast(mem); + delete [] cmem; +} + + +void BufferArray::Block::operator delete(void * mem, size_t) +{ + operator delete(mem); +} + + +BufferArray::Block * BufferArray::Block::alloc(size_t len) +{ + Block * block = new (len) Block(len); + return block; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h new file mode 100644 index 0000000000..1094a435b4 --- /dev/null +++ b/indra/llcorehttp/bufferarray.h @@ -0,0 +1,137 @@ +/** + * @file bufferarray.h + * @brief Public-facing declaration for the BufferArray scatter/gather class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_BUFFER_ARRAY_H_ +#define _LLCORE_BUFFER_ARRAY_H_ + + +#include +#include + +#include "_refcounted.h" + + +namespace LLCore +{ + +class BufferArrayStreamBuf; + +/// A very simple scatter/gather type map for bulk data. The motivation +/// for this class is the writedata callback used by libcurl. Response +/// bodies are delivered to the caller in a sequence of sequential write +/// operations and this class captures them without having to reallocate +/// and move data. +/// +/// The interface looks a little like a unix file descriptor but only +/// just. There is a notion of a current position, starting from 0, +/// which is used as the position in the data when performing read and +/// write operations. The position also moves after various operations: +/// - seek(...) +/// - read(...) +/// - write(...) +/// - append(...) +/// - appendBufferAlloc(...) +/// The object also keeps a total length value which is updated after +/// write and append operations and beyond which the current position +/// cannot be set. +/// +/// Threading: not thread-safe +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a single refcount. +/// +class BufferArray : public LLCoreInt::RefCounted +{ +public: + // BufferArrayStreamBuf has intimate knowledge of this + // implementation to implement a buffer-free adapter. + // Changes here will likely need to be reflected there. + friend class BufferArrayStreamBuf; + + BufferArray(); + +protected: + virtual ~BufferArray(); // Use release() + +private: + BufferArray(const BufferArray &); // Not defined + void operator=(const BufferArray &); // Not defined + +public: + // Internal magic number, may be used by unit tests. + static const size_t BLOCK_ALLOC_SIZE = 65540; + + /// Appends the indicated data to the BufferArray + /// modifying current position and total size. New + /// position is one beyond the final byte of the buffer. + /// + /// @return Count of bytes copied to BufferArray + size_t append(const void * src, size_t len); + + /// Similar to @see append(), this call guarantees a + /// contiguous block of memory of requested size placed + /// at the current end of the BufferArray. On return, + /// the data in the memory is considered valid whether + /// the caller writes to it or not. + /// + /// @return Pointer to contiguous region at end + /// of BufferArray of 'len' size. + void * appendBufferAlloc(size_t len); + + /// Current count of bytes in BufferArray instance. + size_t size() const + { + return mLen; + } + + /// Copies data from the given position in the instance + /// to the caller's buffer. Will return a short count of + /// bytes copied if the 'len' extends beyond the data. + size_t read(size_t pos, void * dst, size_t len); + + /// Copies data from the caller's buffer to the instance + /// at the current position. May overwrite existing data, + /// append data when current position is equal to the + /// size of the instance or do a mix of both. + size_t write(size_t pos, const void * src, size_t len); + +protected: + int findBlock(size_t pos, size_t * ret_offset); + + bool getBlockStartEnd(int block, const char ** start, const char ** end); + +protected: + class Block; + typedef std::vector container_t; + + container_t mBlocks; + size_t mLen; +}; // end class BufferArray + + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/bufferstream.cpp b/indra/llcorehttp/bufferstream.cpp new file mode 100644 index 0000000000..6553900eef --- /dev/null +++ b/indra/llcorehttp/bufferstream.cpp @@ -0,0 +1,285 @@ +/** + * @file bufferstream.cpp + * @brief Implements the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "bufferstream.h" + +#include "bufferarray.h" + + +namespace LLCore +{ + +BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array) + : mBufferArray(array), + mReadCurPos(0), + mReadCurBlock(-1), + mReadBegin(NULL), + mReadCur(NULL), + mReadEnd(NULL), + mWriteCurPos(0) +{ + if (array) + { + array->addRef(); + mWriteCurPos = array->mLen; + } +} + + +BufferArrayStreamBuf::~BufferArrayStreamBuf() +{ + if (mBufferArray) + { + mBufferArray->release(); + mBufferArray = NULL; + } +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow() +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadEnd) + { + // Find the next block with actual data or leave + // mCurBlock/mCur/mEnd unchanged if we're at the end + // of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock + 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + ++new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = mReadCur = new_begin; + mReadEnd = new_end; + } + + return traits_type::to_int_type(*mReadCur); +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow() +{ + const int_type ret(underflow()); + + if (traits_type::eof() != ret) + { + ++mReadCur; + ++mReadCurPos; + } + return ret; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch) +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadBegin) + { + // Find the previous block with actual data or leave + // mCurBlock/mBegin/mCur/mEnd unchanged if we're at the + // beginning of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock - 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + --new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = new_begin; + mReadEnd = mReadCur = new_end; + } + + if (traits_type::eof() != ch && mReadCur[-1] != ch) + { + return traits_type::eof(); + } + --mReadCurPos; + return traits_type::to_int_type(*--mReadCur); +} + + +std::streamsize BufferArrayStreamBuf::showmanyc() +{ + if (! mBufferArray) + { + return -1; + } + return mBufferArray->mLen - mReadCurPos; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return traits_type::eof(); + } + const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1)); + mWriteCurPos += wrote; + return wrote ? c : traits_type::eof(); +} + + +std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return 0; + } + const size_t wrote(mBufferArray->write(mWriteCurPos, src, count)); + mWriteCurPos += wrote; + return wrote; +} + + +std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off, + std::ios_base::seekdir way, + std::ios_base::openmode which) +{ + std::streampos ret(-1); + + if (! mBufferArray) + { + return ret; + } + + if (std::ios_base::in == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mReadCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos >= mBufferArray->size()) + { + pos = (std::max)(size_t(0), mBufferArray->size() - 1); + } + size_t ba_offset(0); + int block(mBufferArray->findBlock(pos, &ba_offset)); + if (block < 0) + return ret; + const char * start(NULL), * end(NULL); + if (! mBufferArray->getBlockStartEnd(block, &start, &end)) + return ret; + mReadCurBlock = block; + mReadBegin = start; + mReadCur = start + ba_offset; + mReadEnd = end; + ret = mReadCurPos = pos; + } + else if (std::ios_base::out == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mWriteCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos < 0) + return ret; + if (pos > mBufferArray->size()) + { + pos = mBufferArray->size(); + } + ret = mWriteCurPos = pos; + } + + return ret; +} + + +BufferArrayStream::BufferArrayStream(BufferArray * ba) + : std::iostream(&mStreamBuf), + mStreamBuf(ba) +{} + + +BufferArrayStream::~BufferArrayStream() +{} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h new file mode 100644 index 0000000000..9327a798aa --- /dev/null +++ b/indra/llcorehttp/bufferstream.h @@ -0,0 +1,153 @@ +/** + * @file bufferstream.h + * @brief Public-facing declaration for the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_BUFFER_STREAM_H_ +#define _LLCORE_BUFFER_STREAM_H_ + + +#include +#include + +#include "bufferarray.h" + + +/// @file bufferstream.h +/// +/// std::streambuf and std::iostream adapters for BufferArray +/// objects. +/// +/// BufferArrayStreamBuf inherits std::streambuf and implements +/// an unbuffered interface for streambuf. This may or may not +/// be the most time efficient implementation and it is a little +/// challenging. +/// +/// BufferArrayStream inherits std::iostream and will be the +/// adapter object most callers will be interested in (though +/// it uses BufferArrayStreamBuf internally). Instances allow +/// for the usual streaming operators ('<<', '>>') and serialization +/// methods. +/// +/// Example of LLSD serialization to a BufferArray: +/// +/// BufferArray * ba = new BufferArray; +/// BufferArrayStream bas(ba); +/// LLSDSerialize::toXML(llsd, bas); +/// operationOnBufferArray(ba); +/// ba->release(); +/// ba = NULL; +/// // operationOnBufferArray and bas are each holding +/// // references to the ba instance at this point. +/// + +namespace LLCore +{ + + +// ===================================================== +// BufferArrayStreamBuf +// ===================================================== + +/// Adapter class to put a std::streambuf interface on a BufferArray +/// +/// Application developers will rarely be interested in anything +/// other than the constructor and even that will rarely be used +/// except indirectly via the @BufferArrayStream class. The +/// choice of interfaces implemented yields a bufferless adapter +/// that doesn't used either the input or output pointer triplets +/// of the more common buffered implementations. This may or may +/// not be faster and that question could stand to be looked at +/// sometime. +/// + +class BufferArrayStreamBuf : public std::streambuf +{ +public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. + BufferArrayStreamBuf(BufferArray * array); + virtual ~BufferArrayStreamBuf(); + +private: + BufferArrayStreamBuf(const BufferArrayStreamBuf &); // Not defined + void operator=(const BufferArrayStreamBuf &); // Not defined + +public: + // Input interfaces from std::streambuf + int_type underflow(); + int_type uflow(); + int_type pbackfail(int_type ch); + std::streamsize showmanyc(); + + // Output interfaces from std::streambuf + int_type overflow(int c); + std::streamsize xsputn(const char * src, std::streamsize count); + + // Common/misc interfaces from std::streambuf + std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which); + +protected: + BufferArray * mBufferArray; // Ref counted + size_t mReadCurPos; + int mReadCurBlock; + const char * mReadBegin; + const char * mReadCur; + const char * mReadEnd; + size_t mWriteCurPos; + +}; // end class BufferArrayStreamBuf + + +// ===================================================== +// BufferArrayStream +// ===================================================== + +/// Adapter class that supplies streaming operators to BufferArray +/// +/// Provides a streaming adapter to an existing BufferArray +/// instance so that the convenient '<<' and '>>' conversions +/// can be applied to a BufferArray. Very convenient for LLSD +/// serialization and parsing as well. + +class BufferArrayStream : public std::iostream +{ +public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. + BufferArrayStream(BufferArray * ba); + ~BufferArrayStream(); + +protected: + BufferArrayStream(const BufferArrayStream &); + void operator=(const BufferArrayStream &); + +protected: + BufferArrayStreamBuf mStreamBuf; +}; // end class BufferArrayStream + + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp new file mode 100644 index 0000000000..40ad4f047d --- /dev/null +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -0,0 +1,947 @@ +/** + * @file http_texture_load.cpp + * @brief Texture download example for core-http library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 +#include +#include +#include +#include +#if !defined(WIN32) +#include +#endif + +#include "linden_common.h" + +#include "httpcommon.h" +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "httpheaders.h" +#include "bufferarray.h" +#include "_mutex.h" + +#include +#include + +#include "lltimer.h" + + +void init_curl(); +void term_curl(); +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); +void usage(std::ostream & out); + +// Default command line settings +static int concurrency_limit(40); +static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture"; + +#if defined(WIN32) + +#define strncpy(_a, _b, _c) strncpy_s(_a, _b, _c) +#define strtok_r(_a, _b, _c) strtok_s(_a, _b, _c) + +int getopt(int argc, char * const argv[], const char *optstring); +char *optarg(NULL); +int optind(1); + +#endif + + +// Mostly just a container for the texture IDs and fetch +// parameters.... +class WorkingSet : public LLCore::HttpHandler +{ +public: + WorkingSet(); + ~WorkingSet(); + + bool reload(LLCore::HttpRequest *); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + void loadTextureUuids(FILE * in); + +public: + struct Spec + { + std::string mUuid; + int mOffset; + int mLength; + }; + typedef std::set handle_set_t; + typedef std::vector texture_list_t; + +public: + bool mVerbose; + bool mRandomRange; + int mMaxConcurrency; + handle_set_t mHandles; + int mRemaining; + int mLimit; + int mAt; + std::string mUrl; + texture_list_t mTextures; + int mErrorsApi; + int mErrorsHttp; + int mErrorsHttp404; + int mErrorsHttp416; + int mErrorsHttp500; + int mErrorsHttp503; + int mSuccesses; + long mByteCount; + LLCore::HttpHeaders * mHeaders; +}; + + +// Gather process information while we run. Process +// size, cpu consumed, wallclock time. + +class Metrics +{ +public: + class MetricsImpl; + +public: + Metrics(); + ~Metrics(); + + void init(); + void sample(); + void term(); + +protected: + MetricsImpl * mImpl; + +public: + U64 mMaxVSZ; + U64 mMinVSZ; + U64 mStartWallTime; + U64 mEndWallTime; + U64 mStartUTime; + U64 mEndUTime; + U64 mStartSTime; + U64 mEndSTime; +}; + + +// +// +// +int main(int argc, char** argv) +{ + LLCore::HttpStatus status; + bool do_random(false); + bool do_verbose(false); + + int option(-1); + while (-1 != (option = getopt(argc, argv, "u:c:h?Rv"))) + { + switch (option) + { + case 'u': + strncpy(url_format, optarg, sizeof(url_format)); + url_format[sizeof(url_format) - 1] = '\0'; + break; + + case 'c': + { + unsigned long value; + char * end; + + value = strtoul(optarg, &end, 10); + if (value < 1 || value > 100 || *end != '\0') + { + usage(std::cerr); + return 1; + } + concurrency_limit = value; + } + break; + + case 'R': + do_random = true; + break; + + case 'v': + do_verbose = true; + break; + + case 'h': + case '?': + usage(std::cout); + return 0; + } + } + + if ((optind + 1) != argc) + { + usage(std::cerr); + return 1; + } + + FILE * uuids(fopen(argv[optind], "r")); + if (! uuids) + { + const char * errstr(strerror(errno)); + + std::cerr << "Couldn't open UUID file '" << argv[optind] << "'. Reason: " + << errstr << std::endl; + return 1; + } + + // Initialization + init_curl(); + LLCore::HttpRequest::createService(); + LLCore::HttpRequest::setPolicyClassOption(LLCore::HttpRequest::DEFAULT_POLICY_ID, + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + concurrency_limit); + LLCore::HttpRequest::startThread(); + + // Get service point + LLCore::HttpRequest * hr = new LLCore::HttpRequest(); + + // Get a handler/working set + WorkingSet ws; + + // Fill the working set with work + ws.mUrl = url_format; + ws.loadTextureUuids(uuids); + ws.mRandomRange = do_random; + ws.mVerbose = do_verbose; + ws.mMaxConcurrency = 100; + + if (! ws.mTextures.size()) + { + std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl; + return 1; + } + + // Setup metrics + Metrics metrics; + metrics.init(); + + // Run it + int passes(0); + while (! ws.reload(hr)) + { + hr->update(5000000); + ms_sleep(2); + if (0 == (++passes % 200)) + { + metrics.sample(); + } + } + metrics.sample(); + metrics.term(); + + // Report + std::cout << "HTTP errors: " << ws.mErrorsHttp << " API errors: " << ws.mErrorsApi + << " Successes: " << ws.mSuccesses << " Byte count: " << ws.mByteCount + << std::endl; + std::cout << "HTTP 404 errors: " << ws.mErrorsHttp404 << " HTTP 416 errors: " << ws.mErrorsHttp416 + << " HTTP 500 errors: " << ws.mErrorsHttp500 << " HTTP 503 errors: " << ws.mErrorsHttp503 + << std::endl; + std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime) + << " uS System CPU: " << (metrics.mEndSTime - metrics.mStartSTime) + << " uS Wall Time: " << (metrics.mEndWallTime - metrics.mStartWallTime) + << " uS Maximum VSZ: " << metrics.mMaxVSZ + << " Bytes Minimum VSZ: " << metrics.mMinVSZ << " Bytes" + << std::endl; + + // Clean up + hr->requestStopThread(NULL); + ms_sleep(1000); + delete hr; + LLCore::HttpRequest::destroyService(); + term_curl(); + + return 0; +} + + +void usage(std::ostream & out) +{ + out << "\n" + "usage:\thttp_texture_load [options] uuid_file\n" + "\n" + "This is a standalone program to drive the New Platform HTTP Library.\n" + "The program is supplied with a file of texture UUIDs, one per line\n" + "These are fetched sequentially using a pool of concurrent connection\n" + "until all are fetched. The default URL format is only useful from\n" + "within Linden Lab but this can be overriden with a printf-style\n" + "URL formatting string on the command line.\n" + "\n" + "Options:\n" + "\n" + " -u printf-style format string for URL generation\n" + " Default: " << url_format << "\n" + " -R Issue GETs with random Range: headers\n" + " -c Maximum request concurrency. Range: [1..100]\n" + " Default: " << concurrency_limit << "\n" + " -v Verbose mode. Issue some chatter while running\n" + " -h print this help\n" + "\n" + << std::endl; +} + + +WorkingSet::WorkingSet() + : LLCore::HttpHandler(), + mVerbose(false), + mRandomRange(false), + mRemaining(200), + mLimit(200), + mAt(0), + mErrorsApi(0), + mErrorsHttp(0), + mErrorsHttp404(0), + mErrorsHttp416(0), + mErrorsHttp500(0), + mErrorsHttp503(0), + mSuccesses(0), + mByteCount(0L) +{ + mTextures.reserve(30000); + + mHeaders = new LLCore::HttpHeaders; + mHeaders->mHeaders.push_back("Accept: image/x-j2c"); +} + + +WorkingSet::~WorkingSet() +{ + if (mHeaders) + { + mHeaders->release(); + mHeaders = NULL; + } +} + + +bool WorkingSet::reload(LLCore::HttpRequest * hr) +{ + int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size()))); + + for (int i(0); i < to_do; ++i) + { + char buffer[1024]; +#if defined(WIN32) + _snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str()); +#else + snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str()); +#endif + int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset); + int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength); + + LLCore::HttpHandle handle; + if (offset || length) + { + handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this); + } + else + { + handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this); + } + if (! handle) + { + // Fatal. Couldn't queue up something. + std::cerr << "Failed to queue work to HTTP Service. Reason: " + << hr->getStatus().toString() << std::endl; + exit(1); + } + else + { + mHandles.insert(handle); + } + mAt++; + mRemaining--; + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Queued " << count << std::endl; + } + } + + // Are we done? + return (! mRemaining) && mHandles.empty(); +} + + +void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + handle_set_t::iterator it(mHandles.find(handle)); + if (mHandles.end() == it) + { + // Wha? + std::cerr << "Failed to find handle in request list. Fatal." << std::endl; + exit(1); + } + else + { + LLCore::HttpStatus status(response->getStatus()); + if (status) + { + // More success + LLCore::BufferArray * data(response->getBody()); + mByteCount += data->size(); + ++mSuccesses; + } + else + { + // Something in this library or libcurl + if (status.isHttpStatus()) + { + static const LLCore::HttpStatus hs404(404); + static const LLCore::HttpStatus hs416(416); + static const LLCore::HttpStatus hs500(500); + static const LLCore::HttpStatus hs503(503); + + ++mErrorsHttp; + if (hs404 == status) + { + ++mErrorsHttp404; + } + else if (hs416 == status) + { + ++mErrorsHttp416; + } + else if (hs500 == status) + { + ++mErrorsHttp500; + } + else if (hs503 == status) + { + ++mErrorsHttp503; + } + } + else + { + ++mErrorsApi; + } + } + mHandles.erase(it); + } + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Handled " << count << std::endl; + } +} + + +void WorkingSet::loadTextureUuids(FILE * in) +{ + char buffer[1024]; + + while (fgets(buffer, sizeof(buffer), in)) + { + WorkingSet::Spec texture; + char * state(NULL); + char * token = strtok_r(buffer, " \t\n,", &state); + if (token && 36 == strlen(token)) + { + // Close enough for this function + texture.mUuid = token; + texture.mOffset = 0; + texture.mLength = 0; + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int offset(atoi(token)); + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int length(atoi(token)); + texture.mOffset = offset; + texture.mLength = length; + } + } + mTextures.push_back(texture); + } + } + mRemaining = mLimit = mTextures.size(); +} + + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +#if defined(WIN32) + +// Very much a subset of posix functionality. Don't push +// it too hard... +int getopt(int argc, char * const argv[], const char *optstring) +{ + static int pos(0); + while (optind < argc) + { + if (pos == 0) + { + if (argv[optind][0] != '-') + return -1; + pos = 1; + } + if (! argv[optind][pos]) + { + ++optind; + pos = 0; + continue; + } + const char * thing(strchr(optstring, argv[optind][pos])); + if (! thing) + { + ++optind; + return -1; + } + if (thing[1] == ':') + { + optarg = argv[++optind]; + ++optind; + pos = 0; + } + else + { + optarg = NULL; + ++pos; + } + return *thing; + } + return -1; +} + +#endif + + + +#if LL_WINDOWS + +#define PSAPI_VERSION 1 +#include "windows.h" +#include "psapi.h" + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void init(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mStartSTime = uli.QuadPart / U64L(10); // Convert to uS + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mStartUTime = uli.QuadPart / U64L(10); + metrics->mStartWallTime = totalTime(); + } + + void sample(Metrics * metrics) + { + PROCESS_MEMORY_COUNTERS_EX counters; + + GetProcessMemoryInfo(GetCurrentProcess(), + (PROCESS_MEMORY_COUNTERS *) &counters, + sizeof(counters)); + // Okay, PrivateUsage isn't truly VSZ but it will be + // a good tracker for leaks and fragmentation. Work on + // a better estimator later... + SIZE_T vsz(counters.PrivateUsage); + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, U64(vsz)); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, U64(vsz)); + } + + void term(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mEndSTime = uli.QuadPart / U64L(10); + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mEndUTime = uli.QuadPart / U64L(10); + metrics->mEndWallTime = totalTime(); + } + +protected: +}; + +#elif LL_DARWIN + +#include +#include + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void init(Metrics * metrics) + { + U64 utime, stime; + + if (getTimes(&utime, &stime)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); + sample(metrics); + } + + void sample(Metrics * metrics) + { + U64 vsz; + + if (getVM(&vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + void term(Metrics * metrics) + { + U64 utime, stime; + + if (getTimes(&utime, &stime)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + } + +protected: + bool getVM(U64 * vsz) + { + task_basic_info task_info_block; + mach_msg_type_number_t task_info_count(TASK_BASIC_INFO_COUNT); + + if (KERN_SUCCESS != task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t) &task_info_block, + &task_info_count)) + { + return false; + } + * vsz = task_info_block.virtual_size; + return true; + } + + bool getTimes(U64 * utime, U64 * stime) + { + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + { + return false; + } + * utime = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + * stime = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; + return true; + } + +}; + +#else + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + : mProcFS(NULL), + mUsecsPerTick(U64L(0)) + {} + + + ~MetricsImpl() + { + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } + + void init(Metrics * metrics) + { + if (! mProcFS) + { + mProcFS = fopen("/proc/self/stat", "r"); + if (! mProcFS) + { + const int errnum(errno); + LL_ERRS("Main") << "Error opening proc fs: " << strerror(errnum) << LL_ENDL; + } + } + + long ticks_per_sec(sysconf(_SC_CLK_TCK)); + mUsecsPerTick = U64L(1000000) / ticks_per_sec; + U64 usecs_per_sec(mUsecsPerTick * ticks_per_sec); + if (900000 > usecs_per_sec || 1100000 < usecs_per_sec) + { + LL_ERRS("Main") << "Resolution problems using uSecs for ticks" << LL_ENDL; + } + + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); + + sample(metrics); + } + + + void sample(Metrics * metrics) + { + U64 vsz; + if (scanProcFS(NULL, NULL, &vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + + void term(Metrics * metrics) + { + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + + sample(metrics); + + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } + +protected: + bool scanProcFS(U64 * utime, U64 * stime, U64 * vsz) + { + if (mProcFS) + { + int i_dummy; + unsigned int ui_dummy; + unsigned long ul_dummy, user_ticks, sys_ticks, vsize; + long l_dummy, rss; + unsigned long long ull_dummy; + char c_dummy; + + char buffer[256]; + + static const char * format("%d %*s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld"); + + fseek(mProcFS, 0L, SEEK_SET); + size_t len = fread(buffer, 1, sizeof(buffer) - 1, mProcFS); + if (! len) + { + return false; + } + buffer[len] = '\0'; + if (23 == sscanf(buffer, format, + &i_dummy, // pid + // &s_dummy, // command name + &c_dummy, // state + &i_dummy, // ppid + &i_dummy, // pgrp + &i_dummy, // session + &i_dummy, // terminal + &i_dummy, // terminal group id + &ui_dummy, // flags + &ul_dummy, // minor faults + &ul_dummy, // minor faults in children + &ul_dummy, // major faults + &ul_dummy, // major faults in children + &user_ticks, + &sys_ticks, + &l_dummy, // cutime + &l_dummy, // cstime + &l_dummy, // process priority + &l_dummy, // nice value + &l_dummy, // thread count + &l_dummy, // time to SIGALRM + &ull_dummy, // start time + &vsize, + &rss)) + { + // Looks like we understand the line + if (utime) + { + *utime = user_ticks * mUsecsPerTick; + } + + if (stime) + { + *stime = sys_ticks * mUsecsPerTick; + } + + if (vsz) + { + *vsz = vsize; + } + return true; + } + } + return false; + } + +protected: + FILE * mProcFS; + U64 mUsecsPerTick; + +}; + + +#endif // LL_WINDOWS + +Metrics::Metrics() + : mMaxVSZ(U64(0)), + mMinVSZ(U64L(0xffffffffffffffff)), + mStartWallTime(U64(0)), + mEndWallTime(U64(0)), + mStartUTime(U64(0)), + mEndUTime(U64(0)), + mStartSTime(U64(0)), + mEndSTime(U64(0)) +{ + mImpl = new MetricsImpl(); +} + + +Metrics::~Metrics() +{ + delete mImpl; + mImpl = NULL; +} + + +void Metrics::init() +{ + mImpl->init(this); +} + + +void Metrics::sample() +{ + mImpl->sample(this); +} + + +void Metrics::term() +{ + mImpl->term(this); +} + + diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp new file mode 100644 index 0000000000..f2fcbf77a3 --- /dev/null +++ b/indra/llcorehttp/httpcommon.cpp @@ -0,0 +1,179 @@ +/** + * @file httpcommon.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "httpcommon.h" + +#include +#include +#include + + +namespace LLCore +{ + +HttpStatus::type_enum_t EXT_CURL_EASY; +HttpStatus::type_enum_t EXT_CURL_MULTI; +HttpStatus::type_enum_t LLCORE; + +HttpStatus::operator unsigned long() const +{ + static const int shift(sizeof(unsigned long) * 4); + + unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus); + return result; +} + + +std::string HttpStatus::toHex() const +{ + std::ostringstream result; + result.width(8); + result.fill('0'); + result << std::hex << operator unsigned long(); + return result.str(); +} + + +std::string HttpStatus::toString() const +{ + static const char * llcore_errors[] = + { + "", + "HTTP error reply status", + "Services shutting down", + "Operation canceled", + "Invalid Content-Range header encountered", + "Request handle not found", + "Invalid datatype for argument or option", + "Option has not been explicitly set", + "Option is not dynamic and must be set early", + "Invalid HTTP status code received from server" + }; + static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); + + static const struct + { + type_enum_t mCode; + const char * mText; + } + http_errors[] = + { + // Keep sorted by mCode, we binary search this list. + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 300, "Multiple Choices" }, + { 301, "Moved Permanently" }, + { 302, "Found" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 307, "Temporary Redirect" }, + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Time-out" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Request Entity Too Large" }, + { 414, "Request-URI Too Large" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested range not satisfiable" }, + { 417, "Expectation Failed" }, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Time-out" }, + { 505, "HTTP Version not supported" } + }; + static const int http_errors_count(sizeof(http_errors) / sizeof(http_errors[0])); + + if (*this) + { + return std::string(""); + } + switch (mType) + { + case EXT_CURL_EASY: + return std::string(curl_easy_strerror(CURLcode(mStatus))); + + case EXT_CURL_MULTI: + return std::string(curl_multi_strerror(CURLMcode(mStatus))); + + case LLCORE: + if (mStatus >= 0 && mStatus < llcore_errors_count) + { + return std::string(llcore_errors[mStatus]); + } + break; + + default: + if (isHttpStatus()) + { + // Binary search for the error code and string + int bottom(0), top(http_errors_count); + while (true) + { + int at((bottom + top) / 2); + if (mType == http_errors[at].mCode) + { + return std::string(http_errors[at].mText); + } + if (at == bottom) + { + break; + } + else if (mType < http_errors[at].mCode) + { + top = at; + } + else + { + bottom = at; + } + } + } + break; + } + return std::string("Unknown error"); +} + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h new file mode 100644 index 0000000000..c0d4ec5aad --- /dev/null +++ b/indra/llcorehttp/httpcommon.h @@ -0,0 +1,311 @@ +/** + * @file httpcommon.h + * @brief Public-facing declarations and definitions of common types + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_COMMON_H_ +#define _LLCORE_HTTP_COMMON_H_ + +/// @package LLCore::HTTP +/// +/// This library implements a high-level, Indra-code-free client interface to +/// HTTP services based on actual patterns found in the viewer and simulator. +/// Interfaces are similar to those supplied by the legacy classes +/// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that +/// allows an application to specify connection behaviors: limits on +/// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc. +/// +/// Features of the library include: +/// - Single, private working thread where all transport and processing occurs. +/// - Support for multiple consumers running in multiple threads. +/// - Scatter/gather (a.k.a. buffer array) model for bulk data movement. +/// - Reference counting used for many object instance lifetimes. +/// - Minimal data sharing across threads for correctness and low latency. +/// +/// The public interface is declared in a few key header files: +/// - "llcorehttp/bufferarray.h" +/// - "llcorehttp/httpcommon.h" +/// - "llcorehttp/httphandler.h" +/// - "llcorehttp/httpheaders.h" +/// - "llcorehttp/httpoptions.h" +/// - "llcorehttp/httprequest.h" +/// - "llcorehttp/httpresponse.h" +/// +/// The library is still under early development and particular users +/// may need access to internal implementation details that are found +/// in the _*.h header files. But this is a crutch to be avoided if at +/// all possible and probably indicates some interface work is neeeded. +/// +/// Using the library is fairly easy. Global setup needs a few +/// steps: +/// +/// - libcurl initialization including thread-safely callbacks for SSL: +/// . curl_global_init(...) +/// . CRYPTO_set_locking_callback(...) +/// . CRYPTO_set_id_callback(...) +/// - HttpRequest::createService() called to instantiate singletons +/// and support objects. +/// +/// An HTTP consumer in an application, and an application may have many +/// consumers, does a few things: +/// +/// - Instantiate and retain an object based on HttpRequest. This +/// object becomes the portal into runtime services for the consumer. +/// - Derive or mixin the HttpHandler class if you want notification +/// when requests succeed or fail. This object's onCompleted() +/// method is invoked and an instance can be shared across +/// requests. +/// +/// Issuing a request is straightforward: +/// - Construct a suitable URL. +/// - Configure HTTP options for the request. (optional) +/// - Build a list of additional headers. (optional) +/// - Invoke one of the requestXXXX() methods (requestGetByteRange, +/// requestPost, etc.) on the HttpRequest instance supplying the +/// above along with a policy class, a priority and an optional +/// pointer to an HttpHandler instance. Work is then queued to +/// the worker thread and occurs asynchronously. +/// - Periodically invoke the update() method on the HttpRequest +/// instance which performs completion notification to HttpHandler +/// objects. +/// - Do completion processing in your onCompletion() method. +/// +/// Code fragments: +/// Rather than a poorly-maintained example in comments, look in the +/// example subdirectory which is a minimal yet functional tool to do +/// GET request performance testing. With four calls: +/// +/// init_curl(); +/// LLCore::HttpRequest::createService(); +/// LLCore::HttpRequest::startThread(); +/// LLCore::HttpRequest * hr = new LLCore::HttpRequest(); +/// +/// the program is basically ready to issue requests. +/// + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include + + +namespace LLCore +{ + + +/// All queued requests are represented by an HttpHandle value. +/// The invalid value is returned when a request failed to queue. +/// The actual status for these failures is then fetched with +/// HttpRequest::getStatus(). +/// +/// The handle is valid only for the life of a request. On +/// return from any HttpHandler notification, the handle immediately +/// becomes invalid and may be recycled for other queued requests. + +typedef void * HttpHandle; +#define LLCORE_HTTP_HANDLE_INVALID (NULL) + +/// For internal scheduling and metrics, we use a microsecond +/// timebase compatible with the environment. +typedef U64 HttpTime; + +/// Error codes defined by the library itself as distinct from +/// libcurl (or any other transport provider). +enum HttpError +{ + // Successful value compatible with the libcurl codes. + HE_SUCCESS = 0, + + // Intended for HTTP reply codes 100-999, indicates that + // the reply should be considered an error by the application. + HE_REPLY_ERROR = 1, + + // Service is shutting down and requested operation will + // not be queued or performed. + HE_SHUTTING_DOWN = 2, + + // Operation was canceled by request. + HE_OP_CANCELED = 3, + + // Invalid content range header received. + HE_INV_CONTENT_RANGE_HDR = 4, + + // Request handle not found + HE_HANDLE_NOT_FOUND = 5, + + // Invalid datatype for option/setting + HE_INVALID_ARG = 6, + + // Option hasn't been explicitly set + HE_OPT_NOT_SET = 7, + + // Option not dynamic, must be set during init phase + HE_OPT_NOT_DYNAMIC = 8, + + // Invalid HTTP status code returned by server + HE_INVALID_HTTP_STATUS = 9 + +}; // end enum HttpError + + +/// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP +/// reply status codes and internal errors as well. The encapsulation +/// isn't expected to completely isolate the caller from libcurl but +/// basic operational tests (success or failure) are provided. +/// +/// Non-HTTP status are encoded as (type, status) with type being +/// one of: EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status +/// being the success/error code from that domain. HTTP status +/// is encoded as (status, error_flag). Status should be in the +/// range [100, 999] and error_flag is either HE_SUCCESS or +/// HE_REPLY_ERROR to indicate whether this should be treated as +/// a successful status or an error. The application is responsible +/// for making that determination and a range like [200, 299] isn't +/// automatically assumed to be definitive. +/// +/// Examples: +/// +/// 1. Construct a default, successful status code: +/// HttpStatus(); +/// +/// 2. Construct a successful, HTTP 200 status code: +/// HttpStatus(200); +/// +/// 3. Construct a failed, HTTP 404 not-found status code: +/// HttpStatus(404); +/// +/// 4. Construct a failed libcurl couldn't connect status code: +/// HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); +/// +/// 5. Construct an HTTP 301 status code to be treated as success: +/// HttpStatus(301, HE_SUCCESS); +/// + +struct HttpStatus +{ + typedef unsigned short type_enum_t; + + HttpStatus() + : mType(LLCORE), + mStatus(HE_SUCCESS) + {} + + HttpStatus(type_enum_t type, short status) + : mType(type), + mStatus(status) + {} + + HttpStatus(int http_status) + : mType(http_status), + mStatus(http_status >= 200 && http_status <= 299 + ? HE_SUCCESS + : HE_REPLY_ERROR) + { + llassert(http_status >= 100 && http_status <= 999); + } + + HttpStatus(const HttpStatus & rhs) + : mType(rhs.mType), + mStatus(rhs.mStatus) + {} + + HttpStatus & operator=(const HttpStatus & rhs) + { + // Don't care if lhs & rhs are the same object + + mType = rhs.mType; + mStatus = rhs.mStatus; + return *this; + } + + static const type_enum_t EXT_CURL_EASY = 0; + static const type_enum_t EXT_CURL_MULTI = 1; + static const type_enum_t LLCORE = 2; + + type_enum_t mType; + short mStatus; + + /// Test for successful status in the code regardless + /// of error source (internal, libcurl). + /// + /// @return 'true' when status is successful. + /// + operator bool() const + { + return 0 == mStatus; + } + + /// Inverse of previous operator. + /// + /// @return 'true' on any error condition + bool operator !() const + { + return 0 != mStatus; + } + + /// Equality and inequality tests to bypass bool conversion + /// which will do the wrong thing in conditional expressions. + bool operator==(const HttpStatus & rhs) const + { + return mType == rhs.mType && mStatus == rhs.mStatus; + } + + bool operator!=(const HttpStatus & rhs) const + { + return ! operator==(rhs); + } + + /// Convert to single numeric representation. Mainly + /// for logging or other informal purposes. Also + /// creates an ambiguous second path to integer conversion + /// which tends to find programming errors such as formatting + /// the status to a stream (operator<<). + operator unsigned long() const; + unsigned long toULong() const + { + return operator unsigned long(); + } + + /// And to convert to a hex string. + std::string toHex() const; + + /// Convert status to a string representation. For + /// success, returns an empty string. For failure + /// statuses, a string as appropriate for the source of + /// the error code (libcurl easy, libcurl multi, or + /// LLCore itself). + std::string toString() const; + + /// Returns true if the status value represents an + /// HTTP response status (100 - 999). + bool isHttpStatus() const + { + return mType >= type_enum_t(100) && mType <= type_enum_t(999); + } + +}; // end struct HttpStatus + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_COMMON_H_ diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h new file mode 100644 index 0000000000..9171e4e7b9 --- /dev/null +++ b/indra/llcorehttp/httphandler.h @@ -0,0 +1,88 @@ +/** + * @file httphandler.h + * @brief Public-facing declarations for the HttpHandler class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_HANDLER_H_ +#define _LLCORE_HTTP_HANDLER_H_ + + +#include "httpcommon.h" + + +namespace LLCore +{ + +class HttpResponse; + + +/// HttpHandler defines an interface used by the library to +/// notify library callers of significant events, currently +/// request completion. Callers must derive or mixin this class +/// then provide an implementation of the @see onCompleted +/// method to receive such notifications. An instance may +/// be shared by any number of requests and across instances +/// of HttpRequest running in the same thread. +/// +/// Threading: HttpHandler itself is pure interface and is +/// tread-compatible. Most derivations, however, will have +/// different constraints. +/// +/// Allocation: Not refcounted, may be stack allocated though +/// that is rarely a good idea. Queued requests and replies keep +/// a naked pointer to the handler and this can result in a +/// dangling pointer if lifetimes aren't managed correctly. + +class HttpHandler +{ +public: + virtual ~HttpHandler() + {} + + /// Method invoked during calls to @see update(). Each invocation + /// represents the completion of some requested operation. Caller + /// can identify the request from the handle and interrogate the + /// response argument for success/failure, data and other information. + /// + /// @param handle Identifier of the request generating + /// the notification. + /// @param response Supplies detailed information about + /// the request including status codes + /// (both programming and HTTP), HTTP body + /// data and encodings, headers, etc. + /// The response object is refcounted and + /// the called code may retain the object + /// by invoking @see addRef() on it. The + /// library itself drops all references to + /// to object on return and never touches + /// it again. + /// + virtual void onCompleted(HttpHandle handle, HttpResponse * response) = 0; + +}; // end class HttpHandler + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_HANDLER_H_ diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp new file mode 100644 index 0000000000..2832696271 --- /dev/null +++ b/indra/llcorehttp/httpheaders.cpp @@ -0,0 +1,44 @@ +/** + * @file httpheaders.cpp + * @brief Implementation of the HTTPHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "httpheaders.h" + + +namespace LLCore +{ + + +HttpHeaders::HttpHeaders() + : RefCounted(true) +{} + + +HttpHeaders::~HttpHeaders() +{} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h new file mode 100644 index 0000000000..3449daa3a1 --- /dev/null +++ b/indra/llcorehttp/httpheaders.h @@ -0,0 +1,87 @@ +/** + * @file httpheaders.h + * @brief Public-facing declarations for the HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_HEADERS_H_ +#define _LLCORE_HTTP_HEADERS_H_ + + +#include + +#include "_refcounted.h" + + +namespace LLCore +{ + +/// +/// Maintains an ordered list of name/value pairs representing +/// HTTP header lines. This is used both to provide additional +/// headers when making HTTP requests and in responses when the +/// caller has asked that headers be returned (not the default +/// option). +/// +/// @note +/// This is a minimally-functional placeholder at the moment +/// to fill out the class hierarchy. The final class will be +/// something else, probably more pair-oriented. It's also +/// an area where shared values are desirable so refcounting is +/// already specced and a copy-on-write scheme imagined. +/// Expect changes here. +/// +/// Threading: Not intrinsically thread-safe. It *is* expected +/// that callers will build these objects and then share them +/// via reference counting with the worker thread. The implication +/// is that once an HttpHeader instance is handed to a request, +/// the object must be treated as read-only. +/// +/// Allocation: Refcounted, heap only. Caller of the +/// constructor is given a refcount. +/// + +class HttpHeaders : public LLCoreInt::RefCounted +{ +public: + /// @post In addition to the instance, caller has a refcount + /// to the instance. A call to @see release() will destroy + /// the instance. + HttpHeaders(); + +protected: + virtual ~HttpHeaders(); // Use release() + + HttpHeaders(const HttpHeaders &); // Not defined + void operator=(const HttpHeaders &); // Not defined + +public: + typedef std::vector container_t; + container_t mHeaders; + +}; // end class HttpHeaders + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp new file mode 100644 index 0000000000..1699d19f8d --- /dev/null +++ b/indra/llcorehttp/httpoptions.cpp @@ -0,0 +1,73 @@ +/** + * @file httpoptions.cpp + * @brief Implementation of the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "httpoptions.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpOptions::HttpOptions() + : RefCounted(true), + mWantHeaders(false), + mTracing(HTTP_TRACE_OFF), + mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT), + mRetries(HTTP_RETRY_COUNT_DEFAULT) +{} + + +HttpOptions::~HttpOptions() +{} + + +void HttpOptions::setWantHeaders(bool wanted) +{ + mWantHeaders = wanted; +} + + +void HttpOptions::setTrace(long level) +{ + mTracing = int(level); +} + + +void HttpOptions::setTimeout(unsigned int timeout) +{ + mTimeout = timeout; +} + + +void HttpOptions::setRetries(unsigned int retries) +{ + mRetries = retries; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h new file mode 100644 index 0000000000..97e46a8cd3 --- /dev/null +++ b/indra/llcorehttp/httpoptions.h @@ -0,0 +1,106 @@ +/** + * @file httpoptions.h + * @brief Public-facing declarations for the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_OPTIONS_H_ +#define _LLCORE_HTTP_OPTIONS_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// Really a struct in spirit, it provides options that +/// modify HTTP requests. +/// +/// Sharing instances across requests. It's intended that +/// these be shared across requests: caller can create one +/// of these, set it up as needed and then reference it +/// repeatedly in HTTP operations. But see the Threading +/// note about references. +/// +/// Threading: While this class does nothing to ensure thread +/// safety, it *is* intended to be shared between the application +/// thread and the worker thread. This means that once an instance +/// is delivered to the library in request operations, the +/// option data must not be written until all such requests +/// complete and relinquish their references. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpOptions : public LLCoreInt::RefCounted +{ +public: + HttpOptions(); + +protected: + virtual ~HttpOptions(); // Use release() + + HttpOptions(const HttpOptions &); // Not defined + void operator=(const HttpOptions &); // Not defined + +public: + void setWantHeaders(bool wanted); + bool getWantHeaders() const + { + return mWantHeaders; + } + + void setTrace(int long); + int getTrace() const + { + return mTracing; + } + + void setTimeout(unsigned int timeout); + unsigned int getTimeout() const + { + return mTimeout; + } + + void setRetries(unsigned int retries); + unsigned int getRetries() const + { + return mRetries; + } + +protected: + bool mWantHeaders; + int mTracing; + unsigned int mTimeout; + unsigned int mRetries; + +}; // end class HttpOptions + + +} // end namespace HttpOptions + +#endif // _LLCORE_HTTP_OPTIONS_H_ diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp new file mode 100644 index 0000000000..9b739a8825 --- /dev/null +++ b/indra/llcorehttp/httprequest.cpp @@ -0,0 +1,504 @@ +/** + * @file httprequest.cpp + * @brief Implementation of the HTTPRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httpoperation.h" +#include "_httpoprequest.h" +#include "_httpopsetpriority.h" +#include "_httpopcancel.h" +#include "_httpopsetget.h" + +#include "lltimer.h" + + +namespace +{ + +bool has_inited(false); + +} + +namespace LLCore +{ + +// ==================================== +// HttpRequest Implementation +// ==================================== + + +HttpRequest::policy_t HttpRequest::sNextPolicyID(1); + + +HttpRequest::HttpRequest() + : //HttpHandler(), + mReplyQueue(NULL), + mRequestQueue(NULL) +{ + mRequestQueue = HttpRequestQueue::instanceOf(); + mRequestQueue->addRef(); + + mReplyQueue = new HttpReplyQueue(); +} + + +HttpRequest::~HttpRequest() +{ + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + if (mReplyQueue) + { + mReplyQueue->release(); + mReplyQueue = NULL; + } +} + + +// ==================================== +// Policy Methods +// ==================================== + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); +} + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); +} + + +HttpRequest::policy_t HttpRequest::createPolicyClass() +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return 0; + } + return HttpService::instanceOf()->createPolicyClass(); +} + + +HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id, + EClassPolicy opt, + long value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value); +} + + +// ==================================== +// Request Methods +// ==================================== + + +HttpStatus HttpRequest::getStatus() const +{ + return mLastReqStatus; +} + + +HttpHandle HttpRequest::requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGet(policy_id, priority, url, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, + priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpHandle HttpRequest::requestPost(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPost(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpHandle HttpRequest::requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPut(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpNull * op = new HttpOpNull(); + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpStatus HttpRequest::update(long usecs) +{ + HttpOperation * op(NULL); + + if (usecs) + { + const HttpTime limit(totalTime() + HttpTime(usecs)); + while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) + { + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + } + else + { + // Same as above, just no time limit + HttpReplyQueue::OpContainer replies; + mReplyQueue->fetchAll(replies); + if (! replies.empty()) + { + for (HttpReplyQueue::OpContainer::iterator iter(replies.begin()); + replies.end() != iter; + ++iter) + { + // Swap op pointer for NULL; + op = *iter; *iter = NULL; + + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + } + } + + return HttpStatus(); +} + + + + +// ==================================== +// Request Management Methods +// ==================================== + +HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpCancel * op = new HttpOpCancel(request); + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + +HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, + HttpHandler * handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); + op->setReplyPath(mReplyQueue, handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + +// ==================================== +// Utility Methods +// ==================================== + +HttpStatus HttpRequest::createService() +{ + HttpStatus status; + + if (! has_inited) + { + HttpRequestQueue::init(); + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + HttpService::init(rq); + has_inited = true; + } + + return status; +} + + +HttpStatus HttpRequest::destroyService() +{ + HttpStatus status; + + if (has_inited) + { + HttpService::term(); + HttpRequestQueue::term(); + has_inited = false; + } + + return status; +} + + +HttpStatus HttpRequest::startThread() +{ + HttpStatus status; + + HttpService::instanceOf()->startThread(); + + return status; +} + + +HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpStop * op = new HttpOpStop(); + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpHandle HttpRequest::requestSpin(int mode) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSpin * op = new HttpOpSpin(mode); + op->setReplyPath(mReplyQueue, NULL); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + +// ==================================== +// Dynamic Policy Methods +// ==================================== + +HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetGet * op = new HttpOpSetGet(); + op->setupSet(GP_HTTP_PROXY, proxy); + op->setReplyPath(mReplyQueue, handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h new file mode 100644 index 0000000000..ab2f302d34 --- /dev/null +++ b/indra/llcorehttp/httprequest.h @@ -0,0 +1,535 @@ +/** + * @file httprequest.h + * @brief Public-facing declarations for HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_REQUEST_H_ +#define _LLCORE_HTTP_REQUEST_H_ + + +#include "httpcommon.h" +#include "httphandler.h" + + +namespace LLCore +{ + +class HttpRequestQueue; +class HttpReplyQueue; +class HttpService; +class HttpOptions; +class HttpHeaders; +class HttpOperation; +class BufferArray; + +/// HttpRequest supplies the entry into the HTTP transport +/// services in the LLCore libraries. Services provided include: +/// +/// - Some, but not all, global initialization of libcurl. +/// - Starting asynchronous, threaded HTTP requests. +/// - Definition of policy classes affect request handling. +/// - Utilities to control request options and headers +/// +/// Requests +/// +/// The class supports the current HTTP request operations: +/// +/// - requestGetByteRange: GET with Range header for a single range of bytes +/// +/// Policy Classes +/// +/// +/// +/// Usage +/// +/// +/// +/// Threading: An instance may only be used by one application/ +/// consumer thread. But a thread may have as many instances of +/// this as it likes. +/// +/// Allocation: Not refcounted, may be stack allocated though that +/// hasn't been tested. Queued requests can still run and any +/// queued replies will keep refcounts to the reply queue leading +/// to memory leaks. +/// +/// @pre Before using this class (static or instances), some global +/// initialization is required. See @see httpcommon.h for more information. +/// +/// @nosubgrouping +/// + +class HttpRequest +{ +public: + HttpRequest(); + virtual ~HttpRequest(); + +private: + HttpRequest(const HttpRequest &); // Disallowed + void operator=(const HttpRequest &); // Disallowed + +public: + typedef unsigned int policy_t; + typedef unsigned int priority_t; + +public: + /// @name PolicyMethods + /// @{ + + /// Represents a default, catch-all policy class that guarantees + /// eventual service for any HTTP request. + static const int DEFAULT_POLICY_ID = 0; + + enum EGlobalPolicy + { + /// Maximum number of connections the library will use to + /// perform operations. This is somewhat soft as the underlying + /// transport will cache some connections (up to 5). + + /// A long value setting the maximum number of connections + /// allowed over all policy classes. Note that this will be + /// a somewhat soft value. There may be an additional five + /// connections per policy class depending upon runtime + /// behavior. + GP_CONNECTION_LIMIT, + + /// String containing a system-appropriate directory name + /// where SSL certs are stored. + GP_CA_PATH, + + /// String giving a full path to a file containing SSL certs. + GP_CA_FILE, + + /// String of host/port to use as simple HTTP proxy. This is + /// going to change in the future into something more elaborate + /// that may support richer schemes. + GP_HTTP_PROXY, + + /// Long value that if non-zero enables the use of the + /// traditional LLProxy code for http/socks5 support. If + /// enabled, has priority over GP_HTTP_PROXY. + GP_LLPROXY, + + /// Long value setting the logging trace level for the + /// library. Possible values are: + /// 0 - No tracing (default) + /// 1 - Basic tracing of request start, stop and major events. + /// 2 - Connection, header and payload size information from + /// HTTP transactions. + /// 3 - Partial logging of payload itself. + /// + /// These values are also used in the trace modes for + /// individual requests in HttpOptions. Also be aware that + /// tracing tends to impact performance of the viewer. + GP_TRACE + }; + + /// Set a parameter on a global policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); + + /// Create a new policy class into which requests can be made. + /// + /// @return If positive, the policy_id used to reference + /// the class in other methods. If 0, an error + /// occurred and @see getStatus() may provide more + /// detail on the reason. + static policy_t createPolicyClass(); + + enum EClassPolicy + { + /// Limits the number of connections used for the class. + CP_CONNECTION_LIMIT, + + /// Limits the number of connections used for a single + /// literal address/port pair within the class. + CP_PER_HOST_CONNECTION_LIMIT, + + /// Suitable requests are allowed to pipeline on their + /// connections when they ask for it. + CP_ENABLE_PIPELINING + }; + + /// Set a parameter on a class-based policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param policy_id ID of class as returned by @see createPolicyClass(). + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); + + /// @} + + /// @name RequestMethods + /// + /// @{ + + /// Some calls expect to succeed as the normal part of operation and so + /// return a useful value rather than a status. When they do fail, the + /// status is saved and can be fetched with this method. + /// + /// @return Status of the failing method invocation. If the + /// preceding call succeeded or other HttpStatus + /// returning calls immediately preceded this method, + /// the returned value may not be reliable. + /// + HttpStatus getStatus() const; + + /// Queue a full HTTP GET request to be issued for entire entity. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-alive: 300 + /// - Host: + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-control: + /// - Range: + /// - Transfer-Encoding: + /// - Referer: + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base (U32-type scheme). + /// @param url URL with any encoded query parameters to + /// be accessed. + /// @param options Optional instance of an HttpOptions object + /// to provide additional controls over the request + /// function for this request only. Any such + /// object then becomes shared-read across threads + /// and no code should modify the HttpOptions + /// instance. + /// @param headers Optional instance of an HttpHeaders object + /// to provide additional and/or overridden + /// headers for the request. As with options, + /// the instance becomes shared-read across threads + /// and no code should modify the HttpHeaders + /// instance. + /// @param handler Optional pointer to an HttpHandler instance + /// whose onCompleted() method will be invoked + /// during calls to update(). This is a non- + /// reference-counted object which would be a + /// problem for shutdown and other edge cases but + /// the pointer is only dereferenced during + /// calls to update(). + /// + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a full HTTP GET request to be issued with a 'Range' header. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-alive: 300 + /// - Host: + /// - Range: (will be omitted if offset == 0 and len == 0) + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-control: + /// - Transfer-Encoding: + /// - Referer: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param offset Offset of first byte into resource to be returned. + /// @param len Count of bytes to be returned + /// @param options @see requestGet() + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestGetByteRange(policy_t policy_id, + priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a full HTTP POST. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-Alive: 300 + /// - Host: + /// - Content-Length: + /// - Content-Type: application/x-www-form-urlencoded + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-Control: + /// - Transfer-Encoding: ... chunked ... + /// - Referer: + /// - Content-Encoding: + /// - Expect: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestPost(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a full HTTP PUT. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-Alive: 300 + /// - Host: + /// - Content-Length: + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-Control: + /// - Transfer-Encoding: ... chunked ... + /// - Referer: + /// - Content-Encoding: + /// - Expect: + /// - Content-Type: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a NoOp request. + /// The request is queued and serviced by the working thread which + /// immediately processes it and returns the request to the reply + /// queue. + /// + /// @param handler @see requestGet() + /// @return " + /// + HttpHandle requestNoOp(HttpHandler * handler); + + /// While all the heavy work is done by the worker thread, notifications + /// must be performed in the context of the application thread. These + /// are done synchronously during calls to this method which gives the + /// library control so notification can be performed. Application handlers + /// are expected to return 'quickly' and do any significant processing + /// outside of the notification callback to onCompleted(). + /// + /// @param usecs Maximum number of wallclock microseconds to + /// spend in the call. As hinted at above, this + /// is partly a function of application code so it's + /// a soft limit. A '0' value will run without + /// time limit until everything queued has been + /// delivered. + /// + /// @return Standard status code. + HttpStatus update(long usecs); + + /// @} + + /// @name RequestMgmtMethods + /// + /// @{ + + HttpHandle requestCancel(HttpHandle request, HttpHandler *); + + /// Request that a previously-issued request be reprioritized. + /// The status of whether the change itself succeeded arrives + /// via notification. + /// + /// @param request Handle of previously-issued request to + /// be changed. + /// @param priority New priority value. + /// @param handler @see requestGet() + /// @return " + /// + HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler); + + /// @} + + /// @name UtilityMethods + /// + /// @{ + + /// Initialization method that needs to be called before queueing any + /// requests. Doesn't start the worker thread and may be called befoer + /// or after policy setup. + static HttpStatus createService(); + + /// Mostly clean shutdown of services prior to exit. Caller is expected + /// to have stopped a running worker thread before calling this. + static HttpStatus destroyService(); + + /// Called once after @see createService() to start the worker thread. + /// Stopping the thread is achieved by requesting it via @see requestStopThread(). + /// May be called before or after requests are issued. + static HttpStatus startThread(); + + /// Queues a request to the worker thread to have it stop processing + /// and exit (without exiting the program). When the operation is + /// picked up by the worker thread, it immediately processes it and + /// begins detaching from refcounted resources like request and + /// reply queues and then returns to the host OS. It *does* queue a + /// reply to give the calling application thread a notification that + /// the operation has been performed. + /// + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// As the request cannot be cancelled, the handle + /// is generally not useful. + /// + HttpHandle requestStopThread(HttpHandler * handler); + + /// Queue a Spin request. + /// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for + /// test purposes. + /// + /// @param mode 0 for hard spin, 1 for soft spin + /// @return Standard handle return cases. + /// + HttpHandle requestSpin(int mode); + + /// @} + + /// @name DynamicPolicyMethods + /// + /// @{ + + /// Request that a running transport pick up a new proxy setting. + /// An empty string will indicate no proxy is to be used. + HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler); + + /// @} + +protected: + void generateNotification(HttpOperation * op); + +private: + /// @name InstanceData + /// + /// @{ + HttpStatus mLastReqStatus; + HttpReplyQueue * mReplyQueue; + HttpRequestQueue * mRequestQueue; + + /// @} + + // ==================================== + /// @name GlobalState + /// + /// @{ + /// + /// Must be established before any threading is allowed to + /// start. + /// + static policy_t sNextPolicyID; + + /// @} + // End Global State + // ==================================== + +}; // end class HttpRequest + + +} // end namespace LLCore + + + +#endif // _LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp new file mode 100644 index 0000000000..a552e48a1b --- /dev/null +++ b/indra/llcorehttp/httpresponse.cpp @@ -0,0 +1,91 @@ +/** + * @file httpresponse.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpResponse::HttpResponse() + : LLCoreInt::RefCounted(true), + mReplyOffset(0U), + mReplyLength(0U), + mReplyFullLength(0U), + mBufferArray(NULL), + mHeaders(NULL) +{} + + +HttpResponse::~HttpResponse() +{ + setBody(NULL); + setHeaders(NULL); +} + + +void HttpResponse::setBody(BufferArray * ba) +{ + if (mBufferArray == ba) + return; + + if (mBufferArray) + { + mBufferArray->release(); + } + + if (ba) + { + ba->addRef(); + } + + mBufferArray = ba; +} + + +void HttpResponse::setHeaders(HttpHeaders * headers) +{ + if (mHeaders == headers) + return; + + if (mHeaders) + { + mHeaders->release(); + } + + if (headers) + { + headers->addRef(); + } + + mHeaders = headers; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h new file mode 100644 index 0000000000..4a481db6ac --- /dev/null +++ b/indra/llcorehttp/httpresponse.h @@ -0,0 +1,161 @@ +/** + * @file httpresponse.h + * @brief Public-facing declarations for the HttpResponse class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCORE_HTTP_RESPONSE_H_ +#define _LLCORE_HTTP_RESPONSE_H_ + + +#include + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class BufferArray; +class HttpHeaders; + +/// HttpResponse is instantiated by the library and handed to +/// the caller during callbacks to the handler. It supplies +/// all the status, header and HTTP body data the caller is +/// interested in. Methods provide simple getters to return +/// individual pieces of the response. +/// +/// Typical usage will have the caller interrogate the object +/// and return from the handler callback. Instances are refcounted +/// and callers can bump the count and retain the object as needed. +/// +/// Threading: Not intrinsically thread-safe. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpResponse : public LLCoreInt::RefCounted +{ +public: + HttpResponse(); + +protected: + virtual ~HttpResponse(); // Use release() + + HttpResponse(const HttpResponse &); // Not defined + void operator=(const HttpResponse &); // Not defined + +public: + /// Returns the final status of the requested operation. + /// + HttpStatus getStatus() const + { + return mStatus; + } + + void setStatus(const HttpStatus & status) + { + mStatus = status; + } + + /// Simple getter for the response body returned as a scatter/gather + /// buffer. If the operation doesn't produce data (such as the Null + /// or StopThread operations), this may be NULL. + /// + /// Caller can hold onto the response by incrementing the reference + /// count of the returned object. + BufferArray * getBody() const + { + return mBufferArray; + } + + /// Set the response data in the instance. Will drop the reference + /// count to any existing data and increment the count of that passed + /// in. It is legal to set the data to NULL. + void setBody(BufferArray * ba); + + /// And a getter for the headers. And as with @see getResponse(), + /// if headers aren't available because the operation doesn't produce + /// any or delivery of headers wasn't requested in the options, this + /// will be NULL. + /// + /// Caller can hold onto the headers by incrementing the reference + /// count of the returned object. + HttpHeaders * getHeaders() const + { + return mHeaders; + } + + /// Behaves like @see setResponse() but for header data. + void setHeaders(HttpHeaders * headers); + + /// If a 'Range:' header was used, these methods are involved + /// in setting and returning data about the actual response. + /// If both @offset and @length are returned as 0, we probably + /// didn't get a Content-Range header in the response. This + /// occurs with various Capabilities-based services and the + /// caller is going to have to make assumptions on receipt of + /// a 206 status. The @full value may also be zero in cases of + /// parsing problems or a wild-carded length response. + void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const + { + *offset = mReplyOffset; + *length = mReplyLength; + *full = mReplyFullLength; + } + + void setRange(unsigned int offset, unsigned int length, unsigned int full_length) + { + mReplyOffset = offset; + mReplyLength = length; + mReplyFullLength = full_length; + } + + /// + const std::string & getContentType() const + { + return mContentType; + } + + void setContentType(const std::string & con_type) + { + mContentType = con_type; + } + +protected: + // Response data here + HttpStatus mStatus; + unsigned int mReplyOffset; + unsigned int mReplyLength; + unsigned int mReplyFullLength; + BufferArray * mBufferArray; + HttpHeaders * mHeaders; + std::string mContentType; +}; + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_RESPONSE_H_ diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp new file mode 100644 index 0000000000..e863ddd13f --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -0,0 +1,175 @@ +/** + * @file llcorehttp_test + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llcorehttp_test.h" + +#include +#include + +// These are not the right way in viewer for some reason: +// #include +// #include +// This works: +#include "../test/lltut.h" + +// Pull in each of the test sets +#include "test_bufferarray.hpp" +#include "test_bufferstream.hpp" +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_httprequestqueue.hpp" + +#include "llproxy.h" + +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); + +#if 0 // lltut provides main and runner + +namespace tut +{ + test_runner_singleton runner; +} + +int main() +{ + curl_global_init(CURL_GLOBAL_ALL); + + // *FIXME: Need threaded/SSL curl setup here. + + tut::reporter reporter; + + tut::runner.get().set_callback(&reporter); + tut::runner.get().run_tests(); + return !reporter.all_ok(); + + curl_global_cleanup(); +} + +#endif // 0 + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } + + LLProxy::getInstance(); +} + + +void term_curl() +{ + LLProxy::cleanupClass(); + + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +std::string get_base_url() +{ + const char * env(getenv("LL_TEST_PORT")); + + if (! env) + { + std::cerr << "LL_TEST_PORT environment variable missing." << std::endl; + std::cerr << "Test expects to run in test_llcorehttp_peer.py script." << std::endl; + tut::ensure("LL_TEST_PORT set in environment", NULL != env); + } + + int port(atoi(env)); + std::ostringstream out; + out << "http://localhost:" << port << "/"; + return out.str(); +} + + +void stop_thread(LLCore::HttpRequest * req) +{ + if (req) + { + req->requestStopThread(NULL); + + int count = 0; + int limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + req->update(1000); + usleep(100000); + } + } +} + + diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h new file mode 100644 index 0000000000..a9567435ce --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -0,0 +1,64 @@ +/** + * @file llcorehttp_test.h + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 _LLCOREHTTP_TEST_H_ +#define _LLCOREHTTP_TEST_H_ + +#include "linden_common.h" // Modifies curl interfaces + +#include +#include +#include + +#include "httprequest.h" + +// Initialization and cleanup for libcurl. Mainly provides +// a mutex callback for SSL and a thread ID hash for libcurl. +// If you don't use these (or equivalent) and do use libcurl, +// you'll see stalls and other anomalies when performing curl +// operations. +extern void init_curl(); +extern void term_curl(); +extern std::string get_base_url(); +extern void stop_thread(LLCore::HttpRequest * req); + +class ScopedCurlInit +{ +public: + ScopedCurlInit() + { + init_curl(); + } + + ~ScopedCurlInit() + { + term_curl(); + } +}; + + +#endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp new file mode 100644 index 0000000000..ea12dc58eb --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -0,0 +1,184 @@ +/** + * @file test_allocator.cpp + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "test_allocator.h" + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +#include +#elif defined(_MSC_VER) +#include +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +// atomic extensions are built into GCC on posix platforms +#endif + +#include +#include +#include +#include +#include +#include + +#include + + +#if defined(WIN32) +#define THROW_BAD_ALLOC() _THROW1(std::bad_alloc) +#define THROW_NOTHING() _THROW0() +#else +#define THROW_BAD_ALLOC() throw(std::bad_alloc) +#define THROW_NOTHING() throw() +#endif + + +struct BlockHeader +{ + struct Block * next; + std::size_t size; + bool in_use; +}; + +struct Block +{ + BlockHeader hdr; + unsigned char data[1]; +}; + +#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl; + +static unsigned char MemBuf[ 4096 * 1024 ]; +Block * pNext = static_cast(static_cast(MemBuf)); +volatile std::size_t MemTotal = 0; + +// cross-platform compare and swap operation +static bool CAS(void * volatile * ptr, void * expected, void * new_value) +{ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + return OSAtomicCompareAndSwapPtr( expected, new_value, ptr ); +#elif defined(_MSC_VER) + return expected == InterlockedCompareExchangePointer( ptr, new_value, expected ); +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 + return __sync_bool_compare_and_swap( ptr, expected, new_value ); +#endif +} + +static void * GetMem(std::size_t size) +{ + // TRACE_MSG(size); + volatile Block * pBlock = NULL; + volatile Block * pNewNext = NULL; + + // do a lock-free update of the global next pointer + do + { + pBlock = pNext; + pNewNext = (volatile Block *)(pBlock->data + size); + + } while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext)); + + // if we get here, we safely carved out a block of memory in the + // memory pool... + + // initialize our block + pBlock->hdr.next = (Block *)(pBlock->data + size); + pBlock->hdr.size = size; + pBlock->hdr.in_use = true; + memset((void *) pBlock->data, 0, pBlock->hdr.size); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total + size; + + } while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total)); + + return (void *) pBlock->data; +} + + +static void FreeMem(void * p) +{ + // get the pointer to the block record + Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader)); + + // TRACE_MSG(pBlock->hdr.size); + bool * cur_in_use = &(pBlock->hdr.in_use); + volatile bool in_use = false; + bool new_in_use = false; + do + { + in_use = pBlock->hdr.in_use; + } while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use)); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total - pBlock->hdr.size; + } while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total)); +} + + +std::size_t GetMemTotal() +{ + return MemTotal; +} + + +void * operator new(std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void * operator new[](std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void operator delete(void * p) THROW_NOTHING() +{ + if (p) + { + FreeMem( p ); + } +} + + +void operator delete[](void * p) THROW_NOTHING() +{ + if (p) + { + FreeMem( p ); + } +} + + diff --git a/indra/llcorehttp/tests/test_allocator.h b/indra/llcorehttp/tests/test_allocator.h new file mode 100644 index 0000000000..3572bbc5c5 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.h @@ -0,0 +1,47 @@ +/** + * @file test_allocator.h + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_ALLOCATOR_H +#define TEST_ALLOCATOR_H + +#include +#include + +size_t GetMemTotal(); +#if defined(WIN32) +void * operator new(std::size_t size) _THROW1(std::bad_alloc); +void * operator new[](std::size_t size) _THROW1(std::bad_alloc); +void operator delete(void * p) _THROW0(); +void operator delete[](void * p) _THROW0(); +#else +void * operator new(std::size_t size) throw (std::bad_alloc); +void * operator new[](std::size_t size) throw (std::bad_alloc); +void operator delete(void * p) throw (); +void operator delete[](void * p) throw (); +#endif + +#endif // TEST_ALLOCATOR_H + diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp new file mode 100644 index 0000000000..8a2a64d970 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -0,0 +1,432 @@ +/** + * @file test_bufferarray.hpp + * @brief unit tests for the LLCore::BufferArray class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_LLCORE_BUFFER_ARRAY_H_ +#define TEST_LLCORE_BUFFER_ARRAY_H_ + +#include "bufferarray.h" + +#include + +#include "test_allocator.h" + + +using namespace LLCore; + + + +namespace tut +{ + +struct BufferArrayTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group BufferArrayTestGroupType; +typedef BufferArrayTestGroupType::object BufferArrayTestObjectType; +BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests"); + +template <> template <> +void BufferArrayTestObjectType::test<1>() +{ + set_test_name("BufferArray construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + ensure("One ref on construction of BufferArray", ba->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in BA", 0 == ba->size()); + + // Try to read + char buffer[20]; + size_t read_len(ba->read(0, buffer, sizeof(buffer))); + ensure("Read returns empty", 0 == read_len); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<2>() +{ + set_test_name("BufferArray single write"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + char buffer[256]; + + size_t len = ba->write(0, str1, strlen(str1)); + ensure("Wrote length correct", strlen(str1) == len); + ensure("Recorded size correct", strlen(str1) == ba->size()); + + // read some data back + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(2, buffer, 2); + ensure("Read length correct", 2 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read didn't overwrite", 'X' == buffer[2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferArrayTestObjectType::test<3>() +{ + set_test_name("BufferArray multiple writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(8, buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read whole thing + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Read length correct", (2 * str1_len) == len); + ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); + ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<4>() +{ + set_test_name("BufferArray overwriting"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJ"; + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->write(8, str2, 4); + ensure("Overwrite length correct", 4 == len); + + // Leave position and read verifying content (stale really from seek() days) + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(12, buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // reposition and check + len = ba->read(6, buffer, 8); + ensure("Read length correct.2", 8 == len); + ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); + ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); + ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]); + ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]); + ensure("Read didn't overwrite.7", 'X' == buffer[8]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<5>() +{ + set_test_name("BufferArray multiple writes - sequential reads"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(8, buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read some more without repositioning + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(12, buffer, sizeof(buffer)); + ensure("Read length correct", (str1_len - 2) == len); + ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<6>() +{ + set_test_name("BufferArray overwrite spanning blocks and appending"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->write(8, str2, str2_len); + ensure("Overwrite length correct", str2_len == len); + + // Leave position and read verifying content + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(8 + str2_len, buffer, 0); + ensure("Read length correct", 0 == len); + ensure("Read didn't overwrite", 'X' == buffer[0]); + + // reposition and check + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Read length correct.2", (str1_len + str2_len - 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); + ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<7>() +{ + set_test_name("BufferArray overwrite spanning blocks and sequential writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); + + // reposition and overwrite + len = ba->write(6, str2, 2); + ensure("Overwrite length correct", 2 == len); + + len = ba->write(8, str2, 2); + ensure("Overwrite length correct.2", 2 == len); + + len = ba->write(10, str2, 2); + ensure("Overwrite length correct.3", 2 == len); + + // append some data + len = ba->append(str2, str2_len); + ensure("Append length correct", str2_len == len); + + // append some more + void * out_buf(ba->appendBufferAlloc(str1_len)); + memcpy(out_buf, str1, str1_len); + + // And some final writes + len = ba->write(3 * str1_len + str2_len, str2, 2); + ensure("Write length correct.2", 2 == len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); + ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); + ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2)); + ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2)); + ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2)); + ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len)); + ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<8>() +{ + set_test_name("BufferArray zero-length appendBufferAlloc"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); + + // zero-length allocate (we allow this with a valid pointer returned) + void * out_buf(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); + + // Do it again + void * out_buf2(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); + ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); + + // And some final writes + len = ba->write(2 * str1_len, str2, str2_len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); + ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); + ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp new file mode 100644 index 0000000000..831c901b9d --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -0,0 +1,304 @@ +/** + * @file test_bufferstream.hpp + * @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_LLCORE_BUFFER_STREAM_H_ +#define TEST_LLCORE_BUFFER_STREAM_H_ + +#include "bufferstream.h" + +#include + +#include "test_allocator.h" +#include "llsd.h" +#include "llsdserialize.h" + + +using namespace LLCore; + + +namespace tut +{ + +struct BufferStreamTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group BufferStreamTestGroupType; +typedef BufferStreamTestGroupType::object BufferStreamTestObjectType; +BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests"); +typedef BufferArrayStreamBuf::traits_type tst_traits_t; + + +template <> template <> +void BufferStreamTestObjectType::test<1>() +{ + set_test_name("BufferArrayStreamBuf construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL + ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow()); + ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow()); + ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c')); + ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1); + ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c')); + ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0); + ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1)); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<2>() +{ + set_test_name("BufferArrayStream construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStream * bas = new BufferArrayStream(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL here + ensure("eof() is false on NULL", ! bas->eof()); + ensure("fail() is false on NULL", ! bas->fail()); + ensure("good() on NULL", bas->good()); + + // release the implicit reference, causing the object to be released + delete bas; + bas = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<3>() +{ + set_test_name("BufferArrayStreamBuf construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<4>() +{ + set_test_name("BufferArrayStream construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // create a new ref counted object with an implicit reference + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<5>() +{ + set_test_name("BufferArrayStreamBuf construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + const char * content("This is a string. A fragment."); + const size_t c_len(strlen(content)); + ba->append(content, c_len); + + // Creat an adapter for the BufferArray + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // Various static state + ensure("underflow() returns 'T'", bsb->underflow() == 'T'); + ensure("underflow() returns 'T' again", bsb->underflow() == 'T'); + ensure("uflow() returns 'T'", bsb->uflow() == 'T'); + ensure("uflow() returns 'h'", bsb->uflow() == 'h'); + ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i')); + ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T')); + ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h'); + ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1)); + ensure("overflow() appends", bsb->overflow('c') == 'c'); + ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1)); + ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4); + ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5)); + ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0)); + ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4)); + ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4)); + ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4)); + ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!')); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<6>() +{ + set_test_name("BufferArrayStream construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + //const char * content("This is a string. A fragment."); + //const size_t c_len(strlen(content)); + //ba->append(content, strlen(content)); + + { + // Creat an adapter for the BufferArray + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Basic operations + bas << "Hello" << 27 << "."; + ensure("BA length 8", ba->size() == 8); + + std::string str; + bas >> str; + ensure("reads correctly", str == "Hello27."); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); + // static U64 mem = GetMemTotal(); +} + + +template <> template <> +void BufferStreamTestObjectType::test<7>() +{ + set_test_name("BufferArrayStream with LLSD serialization"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // Creat an adapter for the BufferArray + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // LLSD + LLSD llsd = LLSD::emptyMap(); + + llsd["int"] = LLSD::Integer(3); + llsd["float"] = LLSD::Real(923289.28992); + llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj"); + + LLSD llsd_map = LLSD::emptyMap(); + llsd_map["int"] = LLSD::Integer(-2889); + llsd_map["float"] = LLSD::Real(2.37829e32); + llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP"); + + llsd["map"] = llsd_map; + + // Serialize it + LLSDSerialize::toXML(llsd, bas); + + std::string str; + bas >> str; + // std::cout << "SERIALIZED LLSD: " << str << std::endl; + ensure("Extracted string has reasonable length", str.size() > 60); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp new file mode 100644 index 0000000000..ce0d19b058 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -0,0 +1,108 @@ +/** + * @file test_httpheaders.hpp + * @brief unit tests for the LLCore::HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_LLCORE_HTTP_HEADERS_H_ +#define TEST_LLCORE_HTTP_HEADERS_H_ + +#include "httpheaders.h" + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpHeadersTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group HttpHeadersTestGroupType; +typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType; +HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests"); + +template <> template <> +void HttpHeadersTestObjectType::test<1>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in headers", 0 == headers->mHeaders.size()); + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpHeadersTestObjectType::test<2>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + + { + // Append a few strings + std::string str1("Pragma:"); + headers->mHeaders.push_back(str1); + std::string str2("Accept: application/json"); + headers->mHeaders.push_back(str2); + + ensure("Headers retained", 2 == headers->mHeaders.size()); + ensure("First is first", headers->mHeaders[0] == str1); + ensure("Second is second", headers->mHeaders[1] == str2); + } + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp new file mode 100644 index 0000000000..17b1a96878 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -0,0 +1,125 @@ +/** + * @file test_httpoperation.hpp + * @brief unit tests for the LLCore::HttpOperation-derived classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_LLCORE_HTTP_OPERATION_H_ +#define TEST_LLCORE_HTTP_OPERATION_H_ + +#include "_httpoperation.h" +#include "httphandler.h" + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +class TestHandler : public LLCore::HttpHandler +{ +public: + virtual void onCompleted(HttpHandle, HttpResponse *) + { + std::cout << "TestHandler::onCompleted() invoked" << std::endl; + } + +}; + + +} // end namespace anonymous + + +namespace tut +{ + struct HttpOperationTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group HttpOperationTestGroupType; + typedef HttpOperationTestGroupType::object HttpOperationTestObjectType; + HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests"); + + template <> template <> + void HttpOperationTestObjectType::test<1>() + { + set_test_name("HttpOpNull construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + ensure(op->getRefCount() == 1); + ensure(mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + op->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void HttpOperationTestObjectType::test<2>() + { + set_test_name("HttpOpNull construction with handlers"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get some handlers + TestHandler * h1 = new TestHandler(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + + // Add the handlers + op->setReplyPath(NULL, h1); + + // Check ref count + ensure(op->getRefCount() == 1); + + // release the reference, releasing the operation but + // not the handlers. + op->release(); + op = NULL; + ensure(mMemTotal != GetMemTotal()); + + // release the handlers + delete h1; + h1 = NULL; + + ensure(mMemTotal == GetMemTotal()); + } + +} + +#endif // TEST_LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp new file mode 100644 index 0000000000..e5488cf941 --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -0,0 +1,2670 @@ +/** + * @file test_httprequest.hpp + * @brief unit tests for the LLCore::HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_LLCORE_HTTP_REQUEST_H_ +#define TEST_LLCORE_HTTP_REQUEST_H_ + +#include "httprequest.h" +#include "bufferarray.h" +#include "httphandler.h" +#include "httpheaders.h" +#include "httpresponse.h" +#include "httpoptions.h" +#include "_httpservice.h" +#include "_httprequestqueue.h" + +#include +#include +#include + +#include "test_allocator.h" +#include "llcorehttp_test.h" + + +using namespace LLCoreInt; + + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec); + +#endif + +} + +namespace tut +{ + +struct HttpRequestTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + int mHandlerCalls; + HttpStatus mStatus; +}; + +class TestHandler2 : public LLCore::HttpHandler +{ +public: + TestHandler2(HttpRequestTestData * state, + const std::string & name) + : mState(state), + mName(name), + mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) + {} + + virtual void onCompleted(HttpHandle handle, HttpResponse * response) + { + if (LLCORE_HTTP_HANDLE_INVALID != mExpectHandle) + { + ensure("Expected handle received in handler", mExpectHandle == handle); + } + ensure("Handler got a response", NULL != response); + if (response && mState) + { + const HttpStatus actual_status(response->getStatus()); + std::ostringstream test; + test << "Expected HttpStatus received in response. Wanted: " + << mState->mStatus.toHex() << " Received: " << actual_status.toHex(); + ensure(test.str().c_str(), actual_status == mState->mStatus); + } + if (mState) + { + mState->mHandlerCalls++; + } + if (! mHeadersRequired.empty() || ! mHeadersDisallowed.empty()) + { + ensure("Response required with header check", response != NULL); + HttpHeaders * header(response->getHeaders()); // Will not hold onto this + ensure("Some quantity of headers returned", header != NULL); + + if (! mHeadersRequired.empty()) + { + for (int i(0); i < mHeadersRequired.size(); ++i) + { + bool found = false; + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (boost::regex_match(*iter, mHeadersRequired[i])) + { + found = true; + break; + } + } + std::ostringstream str; + str << "Required header # " << i << " found in response"; + ensure(str.str(), found); + } + } + + if (! mHeadersDisallowed.empty()) + { + for (int i(0); i < mHeadersDisallowed.size(); ++i) + { + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (boost::regex_match(*iter, mHeadersDisallowed[i])) + { + std::ostringstream str; + str << "Disallowed header # " << i << " not found in response"; + ensure(str.str(), false); + } + } + } + } + } + + if (! mCheckContentType.empty()) + { + ensure("Response required with content type check", response != NULL); + std::string con_type(response->getContentType()); + ensure("Content-Type as expected (" + mCheckContentType + ")", + mCheckContentType == con_type); + } + + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; + } + + HttpRequestTestData * mState; + std::string mName; + HttpHandle mExpectHandle; + std::string mCheckContentType; + std::vector mHeadersRequired; + std::vector mHeadersDisallowed; +}; + +typedef test_group HttpRequestTestGroupType; +typedef HttpRequestTestGroupType::object HttpRequestTestObjectType; +HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); + +template <> template <> +void HttpRequestTestObjectType::test<1>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest construction"); + + HttpRequest * req = NULL; + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + try + { + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // release the request object + delete req; + req = NULL; + + HttpRequest::destroyService(); + + // make sure we didn't leak any memory + ensure("Memory returned", mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<2>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest and Null Op queued"); + + HttpRequest * req = NULL; + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + try + { + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(NULL); + ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID); + + // release the request object + delete req; + req = NULL; + + // We're still holding onto the operation which is + // sitting, unserviced, on the request queue so... + ensure("Memory being used 2", mMemTotal < GetMemTotal()); + + // Request queue should have two references: global singleton & service object + ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + + // Okay, tear it down + HttpRequest::destroyService(); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory returned", mMemTotal == GetMemTotal()); + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<3>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest NoOp + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(&handler); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<4>() +{ + ScopedCurlInit ready; + + set_test_name("2 HttpRequest instances, one thread"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + TestHandler2 handler1(this, "handler1"); + TestHandler2 handler2(this, "handler2"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req1 = NULL; + HttpRequest * req2 = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req1 = new HttpRequest(); + req2 = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue some NoOps + HttpHandle handle = req1->requestNoOp(&handler1); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler1.mExpectHandle = handle; + + handle = req2->requestNoOp(&handler2); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 2) + { + req1->update(1000000); + req2->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); + + // Okay, request a shutdown of the servicing thread + handle = req2->requestStopThread(&handler2); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 3) + { + req1->update(1000000); + req2->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req1; + req1 = NULL; + delete req2; + req2 = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 3 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + stop_thread(req1); + delete req1; + delete req2; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<5>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(1); + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("NoOp notification received", mHandlerCalls == 1); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test should work but could give problems as it + // relies on the worker thread picking up a friendly request + // to shutdown. Doing so, it drops references to things and + // we should go back to where we started. If it gives you + // problems, look into the code before commenting things out. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<6>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(0); // Hard spin + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("No notifications received", mHandlerCalls == 0); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test won't work because we're killing the thread + // hard with the hard spinner. There's no opportunity to join + // nicely so many things leak or get destroyed unilaterally. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest GET to dead port + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setRetries(1); // Don't try for too long - default retries take about 18S + + // Issue a GET that can't connect + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + "http://127.0.0.1:2/nothing/here", + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(50); // With one retry, should fail quickish + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options + opts->release(); + opts = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<8>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<9>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Range: header to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<10>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest PUT to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<11>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest POST to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with some tracing"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<13>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with returned headers"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + handler.mHeadersRequired.reserve(20); // Avoid memory leak test failure + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setWantHeaders(true); + + // Issue a GET that succeeds + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase)); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // release options + opts->release(); + opts = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<14>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest GET timeout"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setRetries(0); // Don't retry + opts->setTimeout(2); + + // Issue a GET that sleeps + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(50); // With one retry, should fail quickish + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options + opts->release(); + opts = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + +// Test retrieval of Content-Type/Content-Encoding headers +template <> template <> +void HttpRequestTestObjectType::test<15>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Content-Type"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // Load and clear the string setting to preload std::string object + // for memory return tests. + handler.mCheckContentType = "application/llsd+xml"; + handler.mCheckContentType.clear(); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + handler.mCheckContentType = "application/llsd+xml"; + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mCheckContentType.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on GET requests +template <> template <> +void HttpRequestTestObjectType::test<16>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest GET"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Do a texture-style fetch + headers = new HttpHeaders; + headers->mHeaders.push_back("Accept: image/x-j2c"); + + mStatus = HttpStatus(200); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + 0, + 47, + options, + headers, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 3) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on POST requests +template <> template <> +void HttpRequestTestObjectType::test<17>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest POST"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default POST + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on PUT requests +template <> template <> +void HttpRequestTestObjectType::test<18>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest PUT"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default PUT + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase)); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on GET requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<19>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest GET with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders; + headers->mHeaders.push_back("Keep-Alive: 120"); + headers->mHeaders.push_back("Accept-encoding: deflate"); + headers->mHeaders.push_back("Accept: text/plain"); + + // Issue a GET with modified headers + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on POST requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<20>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest POST with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders(); + headers->mHeaders.push_back("keep-Alive: 120"); + headers->mHeaders.push_back("Accept: text/html"); + headers->mHeaders.push_back("content-type: application/llsd+xml"); + headers->mHeaders.push_back("cache-control: no-store"); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default POST + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on PUT requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<21>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest PUT with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders; + headers->mHeaders.push_back("content-type: text/plain"); + headers->mHeaders.push_back("content-type: text/html"); + headers->mHeaders.push_back("content-type: application/llsd+xml"); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default PUT + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase)); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +} // end namespace tut + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec) +{ + Sleep((DWORD) (usec / 1000UL)); +} + +#endif + +} + +#endif // TEST_LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp new file mode 100644 index 0000000000..1de2d8f9ab --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -0,0 +1,186 @@ +/** + * @file test_httprequestqueue.hpp + * @brief unit tests for the LLCore::HttpRequestQueue class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_LLCORE_HTTP_REQUESTQUEUE_H_ +#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_ + +#include "_httprequestqueue.h" + +#include + +#include "test_allocator.h" +#include "_httpoperation.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpRequestqueueTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group HttpRequestqueueTestGroupType; +typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType; +HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests"); + +template <> template <> +void HttpRequestqueueTestObjectType::test<1>() +{ + set_test_name("HttpRequestQueue construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<2>() +{ + set_test_name("HttpRequestQueue refcount works"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + rq->addRef(); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + ensure("One ref after term() called", rq->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Drop ref + rq->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<3>() +{ + set_test_name("HttpRequestQueue addOp/fetchOp work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + + rq->addOp(op); // transfer my refcount + + op = rq->fetchOp(true); // Potentially hangs the test on failure + ensure("One goes in, one comes out", NULL != op); + op->release(); + + op = rq->fetchOp(false); + ensure("Better not be two of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<4>() +{ + set_test_name("HttpRequestQueue addOp/fetchAll work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + { + HttpRequestQueue::OpContainer ops; + rq->fetchAll(true, ops); // Potentially hangs the test on failure + ensure("Three go in, three come out", 3 == ops.size()); + + op = rq->fetchOp(false); + ensure("Better not be any more of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // We're still holding onto the ops. + ensure(mMemTotal < GetMemTotal()); + + // Release them + while (! ops.empty()) + { + HttpOperation * op = ops.front(); + ops.erase(ops.begin()); + op->release(); + } + } + + // Should be clean + ensure("All memory returned", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_REQUESTQUEUE_H_ diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp new file mode 100644 index 0000000000..f7b542d3b5 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -0,0 +1,265 @@ +/** + * @file test_llrefcounted + * @brief unit tests for HttpStatus struct + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_HTTP_STATUS_H_ +#define TEST_HTTP_STATUS_H_ + +#include "httpcommon.h" + +#include +#include + +using namespace LLCore; + +namespace tut +{ + +struct HttpStatusTestData +{ + HttpStatusTestData() + {} +}; + +typedef test_group HttpStatusTestGroupType; +typedef HttpStatusTestGroupType::object HttpStatusTestObjectType; + +HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests"); + +template <> template <> +void HttpStatusTestObjectType::test<1>() +{ + set_test_name("HttpStatus construction"); + + // auto allocation fine for this + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SUCCESS; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -1; + + ensure(false == bool(status)); + ensure(!(status)); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_DOWNLOAD_RESUME; + + ensure(false == bool(status)); + ensure(!(status)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<2>() +{ + set_test_name("HttpStatus memory structure"); + + // Require that an HttpStatus object can be trivially + // returned as a function return value in registers. + // One should fit in an int on all platforms. + + ensure(sizeof(HttpStatus) <= sizeof(int)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<3>() +{ + set_test_name("HttpStatus valid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(msg.empty()); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = CURLM_OUT_OF_MEMORY; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SHUTTING_DOWN; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + + +template <> template <> +void HttpStatusTestObjectType::test<4>() +{ + set_test_name("HttpStatus invalid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 32726; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -470; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = 923; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + +template <> template <> +void HttpStatusTestObjectType::test<5>() +{ + set_test_name("HttpStatus equality/inequality testing"); + + // Make certain equality/inequality tests do not pass + // through the bool conversion. Distinct successful + // and error statuses should compare unequal. + + HttpStatus status1(HttpStatus::LLCORE, HE_SUCCESS); + HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS); + ensure(status1 != status2); + + status1.mType = HttpStatus::LLCORE; + status1.mStatus = HE_REPLY_ERROR; + status2.mType = HttpStatus::LLCORE; + status2.mStatus= HE_SHUTTING_DOWN; + ensure(status1 != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<6>() +{ + set_test_name("HttpStatus basic HTTP status encoding"); + + HttpStatus status; + status.mType = 200; + status.mStatus = HE_SUCCESS; + std::string msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Normally a success but application says error + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(! msg.empty()); + ensure(! bool(status)); + ensure(status.toULong() > 1UL); // Biggish number, not a bool-to-ulong + + // Same statuses with distinct success/fail are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + HttpStatus status2(200, HE_REPLY_ERROR); + ensure(status != status2); + + // Normally an error but application says okay + status.mType = 406; + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Different statuses but both successful are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + status2.mType = 201; + status2.mStatus = HE_SUCCESS; + ensure(status != status2); + + // Different statuses but both failed are distinct + status.mType = 200; + status.mStatus = HE_REPLY_ERROR; + status2.mType = 201; + status2.mStatus = HE_REPLY_ERROR; + ensure(status != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<7>() +{ + set_test_name("HttpStatus HTTP error text strings"); + + HttpStatus status(100, HE_REPLY_ERROR); + std::string msg(status.toString()); + ensure(! msg.empty()); // Should be something + ensure(msg == "Continue"); + + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); // Success is empty + + status.mType = 199; + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 505; // Last defined string + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "HTTP Version not supported"); + + status.mType = 506; // One beyond + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 999; // Last HTTP status + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); +} + +} // end namespace tut + +#endif // TEST_HTTP_STATUS_H + diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py new file mode 100644 index 0000000000..75a3c39ef2 --- /dev/null +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +"""\ +@file test_llsdmessage_peer.py +@author Nat Goodspeed +@date 2008-10-09 +@brief This script asynchronously runs the executable (with args) specified on + the command line, returning its result code. While that executable is + running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2012, 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$ +""" + +import os +import sys +import time +import select +import getopt +from threading import Thread +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from SocketServer import ThreadingMixIn + +mydir = os.path.dirname(__file__) # expected to be .../indra/llcorehttp/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +from indra.util.fastest_elementtree import parse as xml_parse +from indra.base import llsd +from testrunner import freeport, run, debug, VERBOSE + +class TestHTTPRequestHandler(BaseHTTPRequestHandler): + """This subclass of BaseHTTPRequestHandler is to receive and echo + LLSD-flavored messages sent by the C++ LLHTTPClient. + """ + def read(self): + # The following logic is adapted from the library module + # SimpleXMLRPCServer.py. + # Get arguments by reading body of request. + # We read this in chunks to avoid straining + # socket.read(); around the 10 or 15Mb mark, some platforms + # begin to have problems (bug #792570). + try: + size_remaining = int(self.headers["content-length"]) + except (KeyError, ValueError): + return "" + max_chunk_size = 10*1024*1024 + L = [] + while size_remaining: + chunk_size = min(size_remaining, max_chunk_size) + chunk = self.rfile.read(chunk_size) + L.append(chunk) + size_remaining -= len(chunk) + return ''.join(L) + # end of swiped read() logic + + def read_xml(self): + # This approach reads the entire POST data into memory first + return llsd.parse(self.read()) +## # This approach attempts to stream in the LLSD XML from self.rfile, +## # assuming that the underlying XML parser reads its input file +## # incrementally. Unfortunately I haven't been able to make it work. +## tree = xml_parse(self.rfile) +## debug("Finished raw parse") +## debug("parsed XML tree %s", tree) +## debug("parsed root node %s", tree.getroot()) +## debug("root node tag %s", tree.getroot().tag) +## return llsd.to_python(tree.getroot()) + + def do_HEAD(self): + self.do_GET(withdata=False) + + def do_GET(self, withdata=True): + # Of course, don't attempt to read data. + self.answer(dict(reply="success", status=200, + reason="Your GET operation worked")) + + def do_POST(self): + # Read the provided POST data. + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) + + def do_PUT(self): + # Read the provided PUT data. + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) + + def answer(self, data, withdata=True): + debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) + if "/sleep/" in self.path: + time.sleep(30) + + if "fail" not in self.path: + data = data.copy() # we're going to modify + # Ensure there's a "reply" key in data, even if there wasn't before + data["reply"] = data.get("reply", llsd.LLSD("success")) + response = llsd.format_xml(data) + debug("success: %s", response) + self.send_response(200) + if "/reflect/" in self.path: + self.reflect_headers() + self.send_header("Content-type", "application/llsd+xml") + self.send_header("Content-Length", str(len(response))) + self.send_header("X-LL-Special", "Mememememe"); + self.end_headers() + if withdata: + self.wfile.write(response) + else: # fail requested + status = data.get("status", 500) + # self.responses maps an int status to a (short, long) pair of + # strings. We want the longer string. That's why we pass a string + # pair to get(): the [1] will select the second string, whether it + # came from self.responses or from our default pair. + reason = data.get("reason", + self.responses.get(status, + ("fail requested", + "Your request specified failure status %s " + "without providing a reason" % status))[1]) + debug("fail requested: %s: %r", status, reason) + self.send_error(status, reason) + if "/reflect/" in self.path: + self.reflect_headers() + self.end_headers() + + def reflect_headers(self): + for name in self.headers.keys(): + # print "Header: %s: %s" % (name, self.headers[name]) + self.send_header("X-Reflect-" + name, self.headers[name]) + + if not VERBOSE: + # When VERBOSE is set, skip both these overrides because they exist to + # suppress output. + + def log_request(self, code, size=None): + # For present purposes, we don't want the request splattered onto + # stderr, as it would upset devs watching the test run + pass + + def log_error(self, format, *args): + # Suppress error output as well + pass + +class Server(ThreadingMixIn, HTTPServer): + # This pernicious flag is on by default in HTTPServer. But proper + # operation of freeport() absolutely depends on it being off. + allow_reuse_address = False + +if __name__ == "__main__": + do_valgrind = False + path_search = False + options, args = getopt.getopt(sys.argv[1:], "V", ["valgrind"]) + for option, value in options: + if option == "-V" or option == "--valgrind": + do_valgrind = True + + # Instantiate a Server(TestHTTPRequestHandler) on the first free port + # in the specified port range. Doing this inline is better than in a + # daemon thread: if it blows up here, we'll get a traceback. If it blew up + # in some other thread, the traceback would get eaten and we'd run the + # subject test program anyway. + httpd, port = freeport(xrange(8000, 8020), + lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) + + # Pass the selected port number to the subject test program via the + # environment. We don't want to impose requirements on the test program's + # command-line parsing -- and anyway, for C++ integration tests, that's + # performed in TUT code rather than our own. + os.environ["LL_TEST_PORT"] = str(port) + debug("$LL_TEST_PORT = %s", port) + if do_valgrind: + args = ["valgrind", "--log-file=./valgrind.log"] + args + path_search = True + sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args)) diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp new file mode 100644 index 0000000000..cb4b50287a --- /dev/null +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -0,0 +1,156 @@ +/** + * @file test_refcounted.hpp + * @brief unit tests for the LLCoreInt::RefCounted class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 TEST_LLCOREINT_REF_COUNTED_H_ +#define TEST_LLCOREINT_REF_COUNTED_H_ + +#include "_refcounted.h" + +#include "test_allocator.h" + +using namespace LLCoreInt; + +namespace tut +{ + struct RefCountedTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group RefCountedTestGroupType; + typedef RefCountedTestGroupType::object RefCountedTestObjectType; + RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests"); + + template <> template <> + void RefCountedTestObjectType::test<1>() + { + set_test_name("RefCounted construction with implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(true); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<2>() + { + set_test_name("RefCounted construction without implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(false); + ensure(rc->getRefCount() == 0); + + // add a reference + rc->addRef(); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<3>() + { + set_test_name("RefCounted addRef and release"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + for (int i = 0; i < 1024; ++i) + { + rc->addRef(); + } + + ensure(rc->getRefCount() == 1024); + + for (int i = 0; i < 1024; ++i) + { + rc->release(); + } + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<4>() + { + set_test_name("RefCounted isLastRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(true); + + // with only one reference, isLastRef should be true + ensure(rc->isLastRef()); + + // release it to clean up memory + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<5>() + { + set_test_name("RefCounted noRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + // set the noRef + rc->noRef(); + + // with only one reference, isLastRef should be true + ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED); + + // allow this memory leak, but check that we're leaking a known amount + ensure(mMemTotal == (GetMemTotal() - sizeof(RefCounted))); + } +} + +#endif // TEST_LLCOREINT_REF_COUNTED_H_ diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py new file mode 100644 index 0000000000..9a2de71142 --- /dev/null +++ b/indra/llcorehttp/tests/testrunner.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +"""\ +@file testrunner.py +@author Nat Goodspeed +@date 2009-03-20 +@brief Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$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$ +""" + +from __future__ import with_statement + +import os +import sys +import re +import errno +import socket + +VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "0") # default to quiet +# Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if +# that construct actually turns on verbosity... +VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE) + +if VERBOSE: + def debug(fmt, *args): + print fmt % args + sys.stdout.flush() +else: + debug = lambda *args: None + +def freeport(portlist, expr): + """ + Find a free server port to use. Specifically, evaluate 'expr' (a + callable(port)) until it stops raising EADDRINUSE exception. + + Pass: + + portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the + range, freeport() lets the socket.error exception propagate. If you want + unbounded, you could pass itertools.count(baseport), though of course in + practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain + the range much more sharply: if we're iterating an absurd number of times, + probably something else is wrong. + + expr: a callable accepting a port number, specifically one of the items + from portlist. If calling that callable raises socket.error with + EADDRINUSE, freeport() retrieves the next item from portlist and retries. + + Returns: (expr(port), port) + + port: the value from portlist for which expr(port) succeeded + + Raises: + + Any exception raised by expr(port) other than EADDRINUSE. + + socket.error if, for every item from portlist, expr(port) raises + socket.error. The exception you see is the one from the last item in + portlist. + + StopIteration if portlist is completely empty. + + Example: + + class Server(HTTPServer): + # If you use BaseHTTPServer.HTTPServer, turning off this flag is + # essential for proper operation of freeport()! + allow_reuse_address = False + # ... + server, port = freeport(xrange(8000, 8010), + lambda port: Server(("localhost", port), + MyRequestHandler)) + # pass 'port' to client code + # call server.serve_forever() + """ + try: + # If portlist is completely empty, let StopIteration propagate: that's an + # error because we can't return meaningful values. We have no 'port', + # therefore no 'expr(port)'. + portiter = iter(portlist) + port = portiter.next() + + while True: + try: + # If this value of port works, return as promised. + value = expr(port) + + except socket.error, err: + # Anything other than 'Address already in use', propagate + if err.args[0] != errno.EADDRINUSE: + raise + + # Here we want the next port from portiter. But on StopIteration, + # we want to raise the original exception rather than + # StopIteration. So save the original exc_info(). + type, value, tb = sys.exc_info() + try: + try: + port = portiter.next() + except StopIteration: + raise type, value, tb + finally: + # Clean up local traceback, see docs for sys.exc_info() + del tb + + else: + debug("freeport() returning %s on port %s", value, port) + return value, port + + # Recap of the control flow above: + # If expr(port) doesn't raise, return as promised. + # If expr(port) raises anything but EADDRINUSE, propagate that + # exception. + # If portiter.next() raises StopIteration -- that is, if the port + # value we just passed to expr(port) was the last available -- reraise + # the EADDRINUSE exception. + # If we've actually arrived at this point, portiter.next() delivered a + # new port value. Loop back to pass that to expr(port). + + except Exception, err: + debug("*** freeport() raising %s: %s", err.__class__.__name__, err) + raise + +def run(*args, **kwds): + """All positional arguments collectively form a command line, executed as + a synchronous child process. + In addition, pass server=new_thread_instance as an explicit keyword (to + differentiate it from an additional command-line argument). + new_thread_instance should be an instantiated but not yet started Thread + subclass instance, e.g.: + run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) + """ + # If there's no server= keyword arg, don't start a server thread: simply + # run a child process. + try: + thread = kwds.pop("server") + except KeyError: + pass + else: + # Start server thread. Note that this and all other comm server + # threads should be daemon threads: we'll let them run "forever," + # confident that the whole process will terminate when the main thread + # terminates, which will be when the child process terminates. + thread.setDaemon(True) + thread.start() + # choice of os.spawnv(): + # - [v vs. l] pass a list of args vs. individual arguments, + # - [no p] don't use the PATH because we specifically want to invoke the + # executable passed as our first arg, + # - [no e] child should inherit this process's environment. + debug("Running %s...", " ".join(args)) + if kwds.get("use_path", False): + rc = os.spawnvp(os.P_WAIT, args[0], args) + else: + rc = os.spawnv(os.P_WAIT, args[0], args) + debug("%s returned %s", args[0], rc) + return rc + +# **************************************************************************** +# test code -- manual at this point, see SWAT-564 +# **************************************************************************** +def test_freeport(): + # ------------------------------- Helpers -------------------------------- + from contextlib import contextmanager + # helper Context Manager for expecting an exception + # with exc(SomeError): + # raise SomeError() + # raises AssertionError otherwise. + @contextmanager + def exc(exception_class, *args): + try: + yield + except exception_class, err: + for i, expected_arg in enumerate(args): + assert expected_arg == err.args[i], \ + "Raised %s, but args[%s] is %r instead of %r" % \ + (err.__class__.__name__, i, err.args[i], expected_arg) + print "Caught expected exception %s(%s)" % \ + (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args)) + else: + assert False, "Failed to raise " + exception_class.__class__.__name__ + + # helper to raise specified exception + def raiser(exception): + raise exception + + # the usual + def assert_equals(a, b): + assert a == b, "%r != %r" % (a, b) + + # ------------------------ Sanity check the above ------------------------ + class SomeError(Exception): pass + # Without extra args, accept any err.args value + with exc(SomeError): + raiser(SomeError("abc")) + # With extra args, accept only the specified value + with exc(SomeError, "abc"): + raiser(SomeError("abc")) + with exc(AssertionError): + with exc(SomeError, "abc"): + raiser(SomeError("def")) + with exc(AssertionError): + with exc(socket.error, errno.EADDRINUSE): + raiser(socket.error(errno.ECONNREFUSED, 'Connection refused')) + + # ----------- freeport() without engaging socket functionality ----------- + # If portlist is empty, freeport() raises StopIteration. + with exc(StopIteration): + freeport([], None) + + assert_equals(freeport([17], str), ("17", 17)) + + # This is the magic exception that should prompt us to retry + inuse = socket.error(errno.EADDRINUSE, 'Address already in use') + # Get the iterator to our ports list so we can check later if we've used all + ports = iter(xrange(5)) + with exc(socket.error, errno.EADDRINUSE): + freeport(ports, lambda port: raiser(inuse)) + # did we entirely exhaust 'ports'? + with exc(StopIteration): + ports.next() + + ports = iter(xrange(2)) + # Any exception but EADDRINUSE should quit immediately + with exc(SomeError): + freeport(ports, lambda port: raiser(SomeError())) + assert_equals(ports.next(), 1) + + # ----------- freeport() with platform-dependent socket stuff ------------ + # This is what we should've had unit tests to begin with (see CHOP-661). + def newbind(port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('127.0.0.1', port)) + return sock + + bound0, port0 = freeport(xrange(7777, 7780), newbind) + assert_equals(port0, 7777) + bound1, port1 = freeport(xrange(7777, 7780), newbind) + assert_equals(port1, 7778) + bound2, port2 = freeport(xrange(7777, 7780), newbind) + assert_equals(port2, 7779) + with exc(socket.error, errno.EADDRINUSE): + bound3, port3 = freeport(xrange(7777, 7780), newbind) + +if __name__ == "__main__": + test_freeport() diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index d6dcde4b9f..34e25a8a71 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -366,7 +366,7 @@ bool LLCrashLogger::sendCrashLogs() { sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5); } - + mSentCrashLogs = sent; return true; diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 825878c5ef..916c346b7a 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -292,11 +292,16 @@ LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components) ++sRawImageCount; } -LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components) +LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy) : LLImageBase() { mMemType = LLMemType::MTYPE_IMAGERAW; - if(allocateDataSize(width, height, components)) + + if(no_copy) + { + setDataAndSize(data, width, height, components); + } + else if(allocateDataSize(width, height, components)) { memcpy(getData(), data, width*height*components); } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 9ce77a0d01..5f54585005 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -189,7 +189,7 @@ protected: public: LLImageRaw(); LLImageRaw(U16 width, U16 height, S8 components); - LLImageRaw(U8 *data, U16 width, U16 height, S8 components); + LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy = false); // Construct using createFromFile (used by tools) //LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false); diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp old mode 100644 new mode 100755 diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 433076c7a9..a871bcbb25 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -193,8 +193,6 @@ void LLParcel::init(const LLUUID &owner_id, mMediaWidth = 0; mMediaHeight = 0; setMediaCurrentURL(LLStringUtil::null); - mMediaURLFilterEnable = FALSE; - mMediaURLFilterList = LLSD::emptyArray(); mMediaAllowNavigate = TRUE; mMediaURLTimeout = 0.0f; mMediaPreventCameraZoom = FALSE; @@ -336,38 +334,6 @@ void LLParcel::setMediaURLResetTimer(F32 time) mMediaResetTimer.setTimerExpirySec(time); } -void LLParcel::setMediaURLFilterList(LLSD list) -{ - // sanity check LLSD - // must be array of strings - if (!list.isArray()) - { - return; - } - - for (S32 i = 0; i < list.size(); i++) - { - if (!list[i].isString()) - return; - } - - // can't be too big - const S32 MAX_SIZE = 50; - if (list.size() > MAX_SIZE) - { - LLSD new_list = LLSD::emptyArray(); - - for (S32 i = 0; i < llmin(list.size(), MAX_SIZE); i++) - { - new_list.append(list[i]); - } - - list = new_list; - } - - mMediaURLFilterList = list; -} - // virtual void LLParcel::setLocalID(S32 local_id) { @@ -622,34 +588,6 @@ BOOL LLParcel::importAccessEntry(std::istream& input_stream, LLAccessEntry* entr return input_stream.good(); } -BOOL LLParcel::importMediaURLFilter(std::istream& input_stream, std::string& url) -{ - skip_to_end_of_next_keyword("{", input_stream); - - while(input_stream.good()) - { - skip_comments_and_emptyspace(input_stream); - std::string line, keyword, value; - get_line(line, input_stream, MAX_STRING); - get_keyword_and_value(keyword, value, line); - - if ("}" == keyword) - { - break; - } - else if ("url" == keyword) - { - url = value; - } - else - { - llwarns << "Unknown keyword in parcel media url filter section: <" - << keyword << ">" << llendl; - } - } - return input_stream.good(); -} - // Assumes we are in a block "ParcelData" void LLParcel::packMessage(LLMessageSystem* msg) { @@ -696,8 +634,6 @@ void LLParcel::packMessage(LLSD& msg) msg["media_allow_navigate"] = getMediaAllowNavigate(); msg["media_prevent_camera_zoom"] = getMediaPreventCameraZoom(); msg["media_url_timeout"] = getMediaURLTimeout(); - msg["media_url_filter_enable"] = getMediaURLFilterEnable(); - msg["media_url_filter_list"] = getMediaURLFilterList(); msg["group_id"] = getGroupID(); msg["pass_price"] = mPassPrice; msg["pass_hours"] = mPassHours; @@ -789,7 +725,6 @@ void LLParcel::unpackMessage(LLMessageSystem* msg) msg->getString("MediaLinkSharing", "MediaCurrentURL", buffer); setMediaCurrentURL(buffer); msg->getU8 ( "MediaLinkSharing", "MediaAllowNavigate", mMediaAllowNavigate ); - msg->getU8 ( "MediaLinkSharing", "MediaURLFilterEnable", mMediaURLFilterEnable ); msg->getU8 ( "MediaLinkSharing", "MediaPreventCameraZoom", mMediaPreventCameraZoom ); msg->getF32( "MediaLinkSharing", "MediaURLTimeout", mMediaURLTimeout); } @@ -1250,8 +1185,6 @@ void LLParcel::clearParcel() mMediaWidth = 0; mMediaHeight = 0; setMediaCurrentURL(LLStringUtil::null); - setMediaURLFilterList(LLSD::emptyArray()); - setMediaURLFilterEnable(FALSE); setMediaAllowNavigate(TRUE); setMediaPreventCameraZoom(FALSE); setMediaURLTimeout(0.0f); diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index f0b2caca3d..499f690e76 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -247,8 +247,6 @@ public: void setMediaWidth(S32 width); void setMediaHeight(S32 height); void setMediaCurrentURL(const std::string& url); - void setMediaURLFilterEnable(U8 enable) { mMediaURLFilterEnable = enable; } - void setMediaURLFilterList(LLSD list); void setMediaAllowNavigate(U8 enable) { mMediaAllowNavigate = enable; } void setMediaURLTimeout(F32 timeout) { mMediaURLTimeout = timeout; } void setMediaPreventCameraZoom(U8 enable) { mMediaPreventCameraZoom = enable; } @@ -310,7 +308,6 @@ public: // BOOL importStream(std::istream& input_stream); BOOL importAccessEntry(std::istream& input_stream, LLAccessEntry* entry); - BOOL importMediaURLFilter(std::istream& input_stream, std::string& url); // BOOL exportStream(std::ostream& output_stream); void packMessage(LLMessageSystem* msg); @@ -354,8 +351,6 @@ public: U8 getMediaAutoScale() const { return mMediaAutoScale; } U8 getMediaLoop() const { return mMediaLoop; } const std::string& getMediaCurrentURL() const { return mMediaCurrentURL; } - U8 getMediaURLFilterEnable() const { return mMediaURLFilterEnable; } - LLSD getMediaURLFilterList() const { return mMediaURLFilterList; } U8 getMediaAllowNavigate() const { return mMediaAllowNavigate; } F32 getMediaURLTimeout() const { return mMediaURLTimeout; } U8 getMediaPreventCameraZoom() const { return mMediaPreventCameraZoom; } @@ -651,8 +646,6 @@ protected: U8 mMediaLoop; std::string mMediaCurrentURL; LLUUID mMediaID; - U8 mMediaURLFilterEnable; - LLSD mMediaURLFilterList; U8 mMediaAllowNavigate; U8 mMediaPreventCameraZoom; F32 mMediaURLTimeout; diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index cf88de12b4..0c0a844b73 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1179,7 +1179,7 @@ LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap) llassert(mDims == comp_dims); // Safety check; the caller has ensured this } bool use_shorts = (mComps[c].get_bit_depth(true) <= 16); - mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts); + mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts,0,0); if (res.which() == 0) // No DWT levels used { mEngines[c] = kdu_decoder(res.access_subband(LL_BAND),&mAllocator,use_shorts); @@ -1223,7 +1223,7 @@ separation between consecutive rows in the real buffer. */ { for (c = 0; c < mNumComponents; c++) { - mEngines[c].pull(mLines[c],true); + mEngines[c].pull(mLines[c]); } if ((mNumComponents >= 3) && mUseYCC) { diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index 9ab0b9e4a7..fb1f6535ba 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -32,6 +32,7 @@ // // KDU core header files // +#define KDU_NO_THREADS #include "kdu_elementary.h" #include "kdu_messaging.h" #include "kdu_params.h" diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h index 9d923fc367..dbdf88b2d9 100644 --- a/indra/llkdu/llkdumem.h +++ b/indra/llkdu/llkdumem.h @@ -28,6 +28,7 @@ #define LL_LLKDUMEM_H // Support classes for reading and writing from memory buffers in KDU +#define KDU_NO_THREADS #include "kdu_image.h" #include "kdu_elementary.h" #include "kdu_messaging.h" diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp old mode 100644 new mode 100755 index beee99a522..b675153b2e --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -127,7 +127,6 @@ kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; } void kdu_resolution::get_dims(kdu_dims& ) { } int kdu_resolution::which() { return 0; } int kdu_resolution::get_valid_band_indices(int &) { return 1; } -kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { } kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { } kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { } kdu_params::~kdu_params() { } @@ -153,7 +152,6 @@ void kdu_codestream::destroy() { } void kdu_codestream::collect_timing_stats(int ) { } void kdu_codestream::set_max_bytes(kdu_long, bool, bool ) { } void kdu_codestream::get_valid_tiles(kdu_dims& ) { } -void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long ) { } void kdu_codestream::create(kdu_compressed_source*, kdu_thread_env*) { } void kdu_codestream::apply_input_restrictions( int, int, int, int, kdu_dims*, kdu_component_access_mode ) { } void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { } @@ -175,7 +173,7 @@ kdu_block* kdu_subband::open_block(kdu_coords, int*, kdu_thread_env*) { return N bool kdu_codestream_comment::put_text(const char*) { return false; } void kdu_customize_warnings(kdu_message*) { } void kdu_customize_errors(kdu_message*) { } -void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { } + kdu_long kdu_multi_analysis::create(kdu_codestream, kdu_tile, bool, kdu_roi_image*, bool, int, kdu_thread_env*, kdu_thread_queue*, bool ) { kdu_long a = 0; return a; } siz_params::siz_params() : kdu_params(NULL, false, false, false, false, false) { } void siz_params::finalize(bool ) { } @@ -184,6 +182,21 @@ int siz_params::write_marker_segment(kdu_output*, kdu_params*, int) { return 0; bool siz_params::check_marker_segment(kdu_uint16, int, kdu_byte a[], int&) { return false; } bool siz_params::read_marker_segment(kdu_uint16, int, kdu_byte a[], int) { return false; } +#ifdef LL_LINUX +// Linux use the old pre KDU v7.0.0 +// *TODO: Supress this legacy stubbs once Linux migrates to v7.0.0 +kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { } +void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long ) { } +void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { } +#else +kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*, int) { } +void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long, kdu_thread_env* ) { } +void (*kdu_convert_ycc_to_rgb_rev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); +void (*kdu_convert_ycc_to_rgb_irrev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); +void (*kdu_convert_ycc_to_rgb_rev32)(kdu_int32*,kdu_int32*,kdu_int32*,int); +void (*kdu_convert_ycc_to_rgb_irrev32)(float*,float*,float*,int); +#endif + // ------------------------------------------------------------------------------------------- // TUT // ------------------------------------------------------------------------------------------- diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index 56cb2ae73e..daf3a6857b 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -33,6 +33,7 @@ class LLVector4; #include "llerror.h" #include "llmath.h" #include "llsd.h" +#include // LLColor3 = |r g b| diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index ff31f7665e..a6e2c89ba4 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -87,6 +87,9 @@ namespace LLAvatarNameCache /// Time when unrefreshed cached names were checked last static F64 sLastExpireCheck; + /// Time-to-live for a temp cache entry. + const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; + //----------------------------------------------------------------------- // Internal methods //----------------------------------------------------------------------- @@ -274,7 +277,7 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) { // there is no existing cache entry, so make a temporary name from legacy LL_WARNS("AvNameCache") << "LLAvatarNameCache get legacy for agent " - << agent_id << LL_ENDL; + << agent_id << LL_ENDL; gCacheName->get(agent_id, false, // legacy compatibility boost::bind(&LLAvatarNameCache::legacyNameCallback, _1, _2, _3)); @@ -287,13 +290,14 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) // Clear this agent from the pending list LLAvatarNameCache::sPendingQueue.erase(agent_id); - const LLAvatarName& av_name = existing->second; + LLAvatarName& av_name = existing->second; LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent " << agent_id << "user '" << av_name.mUsername << "' " << "display '" << av_name.mDisplayName << "' " << "expires in " << av_name.mExpires - LLFrameTimer::getTotalSeconds() << " seconds" << LL_ENDL; + av_name.mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; // reset expiry time so we don't constantly rerequest. } } @@ -330,8 +334,9 @@ void LLAvatarNameCache::requestNamesViaCapability() // http://pdp60.lindenlab.com:8000/agents/?ids=3941037e-78ab-45f0-b421-bd6e77c1804d&ids=0012809d-7d2d-4c24-9609-af1230a37715&ids=0019aaba-24af-4f0a-aa72-6457953cf7f0 // // Apache can handle URLs of 4096 chars, but let's be conservative - const U32 NAME_URL_MAX = 4096; - const U32 NAME_URL_SEND_THRESHOLD = 3000; + static const U32 NAME_URL_MAX = 4096; + static const U32 NAME_URL_SEND_THRESHOLD = 3500; + std::string url; url.reserve(NAME_URL_MAX); @@ -339,10 +344,12 @@ void LLAvatarNameCache::requestNamesViaCapability() agent_ids.reserve(128); U32 ids = 0; - ask_queue_t::const_iterator it = sAskQueue.begin(); - for ( ; it != sAskQueue.end(); ++it) + ask_queue_t::const_iterator it; + while(!sAskQueue.empty()) { - const LLUUID& agent_id = *it; + it = sAskQueue.begin(); + LLUUID agent_id = *it; + sAskQueue.erase(it); if (url.empty()) { @@ -365,27 +372,17 @@ void LLAvatarNameCache::requestNamesViaCapability() if (url.size() > NAME_URL_SEND_THRESHOLD) { - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability first " - << ids << " ids" - << LL_ENDL; - LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); - url.clear(); - agent_ids.clear(); + break; } } if (!url.empty()) { - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability all " + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability requested " << ids << " ids" << LL_ENDL; LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); - url.clear(); - agent_ids.clear(); } - - // We've moved all asks to the pending request queue - sAskQueue.clear(); } void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, @@ -402,20 +399,25 @@ void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, << LL_ENDL; buildLegacyName(full_name, &av_name); - // Don't add to cache, the data already exists in the legacy name system - // cache and we don't want or need duplicate storage, because keeping the - // two copies in sync is complex. - processName(agent_id, av_name, false); + // Add to cache, because if we don't we'll keep rerequesting the + // same record forever. buildLegacyName should always guarantee + // that these records expire reasonably soon + // (in TEMP_CACHE_ENTRY_LIFETIME seconds), so if the failure was due + // to something temporary we will eventually request and get the right data. + processName(agent_id, av_name, true); } void LLAvatarNameCache::requestNamesViaLegacy() { + static const S32 MAX_REQUESTS = 100; F64 now = LLFrameTimer::getTotalSeconds(); std::string full_name; - ask_queue_t::const_iterator it = sAskQueue.begin(); - for (; it != sAskQueue.end(); ++it) + ask_queue_t::const_iterator it; + for (S32 requests = 0; !sAskQueue.empty() && requests < MAX_REQUESTS; ++requests) { - const LLUUID& agent_id = *it; + it = sAskQueue.begin(); + LLUUID agent_id = *it; + sAskQueue.erase(it); // Mark as pending first, just in case the callback is immediately // invoked below. This should never happen in practice. @@ -427,10 +429,6 @@ void LLAvatarNameCache::requestNamesViaLegacy() boost::bind(&LLAvatarNameCache::legacyNameCallback, _1, _2, _3)); } - - // We've either answered immediately or moved all asks to the - // pending queue - sAskQueue.clear(); } void LLAvatarNameCache::initClass(bool running) @@ -507,11 +505,11 @@ void LLAvatarNameCache::idle() // *TODO: Possibly re-enabled this based on People API load measurements // 100 ms is the threshold for "user speed" operations, so we can // stall for about that long to batch up requests. - //const F32 SECS_BETWEEN_REQUESTS = 0.1f; - //if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) - //{ - // return; - //} + const F32 SECS_BETWEEN_REQUESTS = 0.1f; + if (!sRequestTimer.hasExpired()) + { + return; + } if (!sAskQueue.empty()) { @@ -526,6 +524,12 @@ void LLAvatarNameCache::idle() } } + if (sAskQueue.empty()) + { + // cleared the list, reset the request timer. + sRequestTimer.resetWithExpiry(SECS_BETWEEN_REQUESTS); + } + // erase anything that has not been refreshed for more than MAX_UNREFRESHED_TIME eraseUnrefreshed(); } @@ -559,8 +563,7 @@ void LLAvatarNameCache::eraseUnrefreshed() const LLAvatarName& av_name = it->second; if (av_name.mExpires < max_unrefreshed) { - const LLUUID& agent_id = it->first; - LL_DEBUGS("AvNameCache") << agent_id + LL_DEBUGS("AvNameCache") << it->first << " user '" << av_name.mUsername << "' " << "expired " << now - av_name.mExpires << " secs ago" << LL_ENDL; @@ -583,7 +586,7 @@ void LLAvatarNameCache::buildLegacyName(const std::string& full_name, av_name->mDisplayName = full_name; av_name->mIsDisplayNameDefault = true; av_name->mIsTemporaryName = true; - av_name->mExpires = F64_MAX; // not used because these are not cached + av_name->mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::buildLegacyName " << full_name << LL_ENDL; @@ -737,12 +740,6 @@ void LLAvatarNameCache::erase(const LLUUID& agent_id) sCache.erase(agent_id); } -void LLAvatarNameCache::fetch(const LLUUID& agent_id) -{ - // re-request, even if request is already pending - sAskQueue.insert(agent_id); -} - void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_name) { // *TODO: update timestamp if zero? diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 064942fe53..79f170f7c8 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -86,10 +86,6 @@ namespace LLAvatarNameCache /// Provide some fallback for agents that return errors void handleAgentError(const LLUUID& agent_id); - // Force a re-fetch of the most recent data, but keep the current - // data in cache - void fetch(const LLUUID& agent_id); - void insert(const LLUUID& agent_id, const LLAvatarName& av_name); // Compute name expiration time from HTTP Cache-Control header, diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 10c83e6572..8ffa8e4271 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -294,6 +294,8 @@ LLCurl::Easy* LLCurl::Easy::getEasy() // multi handles cache if they are added to one. CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); check_curl_code(result); + result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + check_curl_code(result); ++gCurlEasyCount; return easy; @@ -482,7 +484,8 @@ void LLCurl::Easy::prepRequest(const std::string& url, //setopt(CURLOPT_VERBOSE, 1); // useful for debugging setopt(CURLOPT_NOSIGNAL, 1); - + setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + // Set the CURL options for either Socks or HTTP proxy LLProxy::getInstance()->applyProxySettings(this); @@ -938,8 +941,8 @@ bool LLCurlThread::CurlRequest::processRequest() if(!completed) { - setPriority(LLQueuedThread::PRIORITY_LOW) ; - } + setPriority(LLQueuedThread::PRIORITY_LOW) ; + } } return completed ; @@ -949,7 +952,7 @@ void LLCurlThread::CurlRequest::finishRequest(bool completed) { if(mMulti->isDead()) { - mCurlThread->deleteMulti(mMulti) ; + mCurlThread->deleteMulti(mMulti) ; } else { @@ -993,6 +996,7 @@ void LLCurlThread::killMulti(LLCurl::Multi* multi) return ; } + multi->markDead() ; } @@ -1098,7 +1102,9 @@ void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) { getByteRange(url, headers_t(), 0, -1, responder); } - + +// Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or +// the remainder of the file if not. bool LLCurlRequest::getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, @@ -1117,6 +1123,11 @@ bool LLCurlRequest::getByteRange(const std::string& url, std::string range = llformat("Range: bytes=%d-%d", offset,offset+length-1); easy->slist_append(range.c_str()); } + else if (offset > 0) + { + std::string range = llformat("Range: bytes=%d-", offset); + easy->slist_append(range.c_str()); + } easy->setHeaders(); bool res = addEasy(easy); return res; @@ -1244,6 +1255,208 @@ S32 LLCurlRequest::getQueued() return queued; } +LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : + LLCurlRequest(), + mConcurrency(concurrency), + mInQueue(0), + mMutex(NULL), + mHandleCounter(1), + mTotalIssuedRequests(0), + mTotalReceivedBits(0) +{ + mGlobalTimer.reset(); +} + +LLCurlTextureRequest::~LLCurlTextureRequest() +{ + mRequestMap.clear(); + + for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) + { + delete *iter; + } + mCachedRequests.clear(); +} + +//return 0: success +// > 0: cached handle +U32 LLCurlTextureRequest::getByteRange(const std::string& url, + const headers_t& headers, + S32 offset, S32 length, U32 pri, + LLCurl::ResponderPtr responder, F32 delay_time) +{ + U32 ret_val = 0; + bool success = false; + + if(mInQueue < mConcurrency && delay_time < 0.f) + { + success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); + } + + LLMutexLock lock(&mMutex); + + if(success) + { + mInQueue++; + mTotalIssuedRequests++; + } + else + { + request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); + if(delay_time > 0.f) + { + request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; + } + + mCachedRequests.insert(request); + mRequestMap[mHandleCounter] = request; + ret_val = mHandleCounter; + mHandleCounter++; + + if(!mHandleCounter) + { + mHandleCounter = 1; + } + } + + return ret_val; +} + +void LLCurlTextureRequest::completeRequest(S32 received_bytes) +{ + LLMutexLock lock(&mMutex); + + llassert_always(mInQueue > 0); + + mInQueue--; + mTotalReceivedBits += received_bytes * 8; +} + +void LLCurlTextureRequest::nextRequests() +{ + if(mCachedRequests.empty() || mInQueue >= mConcurrency) + { + return; + } + + F32 cur_time = mGlobalTimer.getElapsedTimeF32(); + + req_queue_t::iterator iter; + { + LLMutexLock lock(&mMutex); + iter = mCachedRequests.begin(); + } + while(1) + { + request_t* request = *iter; + if(request->mStartTime < cur_time) + { + if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) + { + break; + } + + LLMutexLock lock(&mMutex); + ++iter; + mInQueue++; + mTotalIssuedRequests++; + mCachedRequests.erase(request); + mRequestMap.erase(request->mHandle); + delete request; + + if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) + { + break; + } + } + else + { + LLMutexLock lock(&mMutex); + ++iter; + if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) + { + break; + } + } + } + + return; +} + +void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) +{ + if(!handle) + { + return; + } + + LLMutexLock lock(&mMutex); + + std::map::iterator iter = mRequestMap.find(handle); + if(iter != mRequestMap.end()) + { + request_t* req = iter->second; + + if(req->mPriority != pri) + { + mCachedRequests.erase(req); + req->mPriority = pri; + mCachedRequests.insert(req); + } + } +} + +void LLCurlTextureRequest::removeRequest(U32 handle) +{ + if(!handle) + { + return; + } + + LLMutexLock lock(&mMutex); + + std::map::iterator iter = mRequestMap.find(handle); + if(iter != mRequestMap.end()) + { + request_t* req = iter->second; + mRequestMap.erase(iter); + mCachedRequests.erase(req); + delete req; + } +} + +bool LLCurlTextureRequest::isWaiting(U32 handle) +{ + if(!handle) + { + return false; + } + + LLMutexLock lock(&mMutex); + return mRequestMap.find(handle) != mRequestMap.end(); +} + +U32 LLCurlTextureRequest::getTotalReceivedBits() +{ + LLMutexLock lock(&mMutex); + + U32 bits = mTotalReceivedBits; + mTotalReceivedBits = 0; + return bits; +} + +U32 LLCurlTextureRequest::getTotalIssuedRequests() +{ + LLMutexLock lock(&mMutex); + return mTotalIssuedRequests; +} + +S32 LLCurlTextureRequest::getNumRequests() +{ + LLMutexLock lock(&mMutex); + return mInQueue; +} + //////////////////////////////////////////////////////////////////////////// // For generating one easy request // associated with a single multi request diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 62947bdb9d..7bcf61e233 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -407,6 +407,71 @@ private: BOOL mProcessing; }; +//for texture fetch only +class LLCurlTextureRequest : public LLCurlRequest +{ +public: + LLCurlTextureRequest(S32 concurrency); + ~LLCurlTextureRequest(); + + U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f); + void nextRequests(); + void completeRequest(S32 received_bytes); + + void updatePriority(U32 handle, U32 pri); + void removeRequest(U32 handle); + + U32 getTotalReceivedBits(); + U32 getTotalIssuedRequests(); + S32 getNumRequests(); + bool isWaiting(U32 handle); + +private: + LLMutex mMutex; + S32 mConcurrency; + S32 mInQueue; //request currently in queue. + U32 mHandleCounter; + U32 mTotalIssuedRequests; + U32 mTotalReceivedBits; + + typedef struct _request_t + { + _request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) : + mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f) + {} + + U32 mHandle; + std::string mUrl; + LLCurlRequest::headers_t mHeaders; + S32 mOffset; + S32 mLength; + LLCurl::ResponderPtr mResponder; + U32 mPriority; + F32 mStartTime; //start time to issue this request + } request_t; + + struct request_compare + { + bool operator()(const request_t* lhs, const request_t* rhs) const + { + if(lhs->mPriority != rhs->mPriority) + { + return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set) + } + else + { + return (U32)lhs < (U32)rhs; + } + } + }; + + typedef std::set req_queue_t; + req_queue_t mCachedRequests; + std::map mRequestMap; + + LLFrameTimer mGlobalTimer; +}; + class LLCurlEasyRequest { public: diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp index 612d765969..d6ed08055e 100644 --- a/indra/llmessage/llhttpassetstorage.cpp +++ b/indra/llmessage/llhttpassetstorage.cpp @@ -749,7 +749,7 @@ LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list request_list_t::iterator pending_iter = pending.begin(); request_list_t::iterator pending_end = pending.end(); // Loop over all pending requests until we miss finding it in the running list. - for (; pending_iter != pending.end(); ++pending_iter) + for (; pending_iter != pending_end; ++pending_iter) { LLAssetRequest* req = *pending_iter; // Look for this pending request in the running list. diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index 3fc54573a7..8183467dc5 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -15,7 +15,6 @@ include_directories( if (DARWIN) include(CMakeFindFrameworks) - find_library(CARBON_LIBRARY Carbon) find_library(COCOA_LIBRARY Cocoa) endif (DARWIN) @@ -68,7 +67,7 @@ add_dependencies(SLPlugin if (DARWIN) # Mac version needs to link against Carbon - target_link_libraries(SLPlugin ${CARBON_LIBRARY} ${COCOA_LIBRARY}) + target_link_libraries(SLPlugin ${COCOA_LIBRARY}) # Make sure the app bundle has a Resources directory (it will get populated by viewer-manifest.py later) add_custom_command( TARGET SLPlugin POST_BUILD diff --git a/indra/llplugin/slplugin/slplugin-objc.h b/indra/llplugin/slplugin/slplugin-objc.h index 602d848f7e..f2c2b3239c 100644 --- a/indra/llplugin/slplugin/slplugin-objc.h +++ b/indra/llplugin/slplugin/slplugin-objc.h @@ -28,8 +28,26 @@ * @endcond */ +//Protos for ObjectiveC classes (cannot import cocoa here due to BOOL conflict) +class NSWindow; /* Defined in slplugin-objc.mm: */ -void setupCocoa(); -void createAutoReleasePool(); -void deleteAutoReleasePool(); + +class LLCocoaPlugin +{ +public: + LLCocoaPlugin(); + void setupCocoa(); + void createAutoReleasePool(); + void deleteAutoReleasePool(); + void setupGroup(); + void updateWindows(); + void processEvents(); +public: + //EventTargetRef mEventTarget; + NSWindow* mFrontWindow; + NSWindow* mPluginWindow; + int mHackState; +}; + + diff --git a/indra/llplugin/slplugin/slplugin-objc.mm b/indra/llplugin/slplugin/slplugin-objc.mm index 646416b9d2..a434739350 100644 --- a/indra/llplugin/slplugin/slplugin-objc.mm +++ b/indra/llplugin/slplugin/slplugin-objc.mm @@ -30,11 +30,13 @@ #include +#import #include "slplugin-objc.h" +//Note: NSApp is a global defined by cocoa which is an id to the application. -void setupCocoa() +void LLCocoaPlugin::setupCocoa() { static bool inited = false; @@ -56,6 +58,8 @@ void setupCocoa() // Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image [[[NSWindow alloc] init] release]; + mPluginWindow = [NSApp mainWindow]; + deleteAutoReleasePool(); inited = true; @@ -64,7 +68,7 @@ void setupCocoa() static NSAutoreleasePool *sPool = NULL; -void createAutoReleasePool() +void LLCocoaPlugin::createAutoReleasePool() { if(!sPool) { @@ -72,7 +76,7 @@ void createAutoReleasePool() } } -void deleteAutoReleasePool() +void LLCocoaPlugin::deleteAutoReleasePool() { if(sPool) { @@ -80,3 +84,94 @@ void deleteAutoReleasePool() sPool = NULL; } } + +LLCocoaPlugin::LLCocoaPlugin():mHackState(0) +{ + NSArray* window_list = [NSApp orderedWindows]; + mFrontWindow = [window_list objectAtIndex:0]; +} + +void LLCocoaPlugin::processEvents() +{ + // Some plugins (webkit at least) will want an event loop. This qualifies. + NSEvent * event; + event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; + [NSApp sendEvent: event]; +} + + +//Turns out the window ordering stuff never gets hit with any of the current plugins. +//Leaving the following code here 'just in case' for the time being. + +void LLCocoaPlugin::setupGroup() +{ + // CreateWindowGroup(kWindowGroupAttrFixedLevel, &layer_group); + // if(layer_group) + // { + // // Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube) + // SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer")); + // SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel); + // } + +} + +void LLCocoaPlugin::updateWindows() //SPATTERS give this a better name. +{ +// NSArray* window_list = [NSApp orderedWindows]; +// NSWindow* current_window = [window_list objectAtIndex:0]; +// NSWindow* parent_window = [ current_window parentWindow ]; +// bool this_is_front_process = false; +// bool parent_is_front_process = false; +// +// +// // Check for a change in this process's frontmost window. +// if ( current_window != mFrontWindow ) +// { +// // and figure out whether this process or its parent are currently frontmost +// if ( current_window == parent_window ) parent_is_front_process = true; +// if ( current_window == mPluginWindow ) this_is_front_process = true; +// +// if (current_window != NULL && mFrontWindow == NULL) +// { +// // Opening the first window +// +// if(mHackState == 0) +// { +// // Next time through the event loop, lower the window group layer +// mHackState = 1; +// } +// +// if(parent_is_front_process) +// { +// // Bring this process's windows to the front. +// [mPluginWindow makeKeyAndOrderFront:NSApp]; +// [mPluginWindow setOrderedIndex:0]; +// } +// +// [NSApp activateIgnoringOtherApps:YES]; +// } +// +// else if (( current_window == NULL) && (mFrontWindow != NULL)) +// { +// // Closing the last window +// +// if(this_is_front_process) +// { +// // Try to bring this process's parent to the front +// [parent_window makeKeyAndOrderFront:NSApp]; +// [parent_window setOrderedIndex:0]; +// } +// } +// else if(mHackState == 1) +// { +//// if(layer_group) +//// { +//// // Set the window group level back to something less extreme +//// SetWindowGroupLevel(layer_group, kCGNormalWindowLevel); +//// } +// mHackState = 2; +// } +// +// mFrontWindow = [window_list objectAtIndex:0]; +// } + } diff --git a/indra/llplugin/slplugin/slplugin.cpp b/indra/llplugin/slplugin/slplugin.cpp index 516a58db88..6c9ba0ae52 100644 --- a/indra/llplugin/slplugin/slplugin.cpp +++ b/indra/llplugin/slplugin/slplugin.cpp @@ -37,8 +37,12 @@ #include "llapr.h" #include "llstring.h" +#include +#include +using namespace std; + + #if LL_DARWIN - #include #include "slplugin-objc.h" #endif @@ -176,6 +180,7 @@ int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdL int main(int argc, char **argv) #endif { + ll_init_apr(); // Set up llerror logging @@ -216,26 +221,25 @@ int main(int argc, char **argv) // Catch signals that most kinds of crashes will generate, and exit cleanly so the system crash dialog isn't shown. signal(SIGILL, &crash_handler); // illegal instruction -# if LL_DARWIN - signal(SIGEMT, &crash_handler); // emulate instruction executed -# endif // LL_DARWIN signal(SIGFPE, &crash_handler); // floating-point exception signal(SIGBUS, &crash_handler); // bus error signal(SIGSEGV, &crash_handler); // segmentation violation signal(SIGSYS, &crash_handler); // non-existent system call invoked #endif +# if LL_DARWIN + signal(SIGEMT, &crash_handler); // emulate instruction executed -#if LL_DARWIN - setupCocoa(); - createAutoReleasePool(); -#endif + LLCocoaPlugin cocoa_interface; + cocoa_interface.setupCocoa(); + cocoa_interface.createAutoReleasePool(); +#endif //LL_DARWIN LLPluginProcessChild *plugin = new LLPluginProcessChild(); plugin->init(port); #if LL_DARWIN - deleteAutoReleasePool(); + cocoa_interface.deleteAutoReleasePool(); #endif LLTimer timer; @@ -246,114 +250,22 @@ int main(int argc, char **argv) #endif #if LL_DARWIN + // If the plugin opens a new window (such as the Flash plugin's fullscreen player), we may need to bring this plugin process to the foreground. // Use this to track the current frontmost window and bring this process to the front if it changes. - WindowRef front_window = NULL; - WindowGroupRef layer_group = NULL; - int window_hack_state = 0; - CreateWindowGroup(kWindowGroupAttrFixedLevel, &layer_group); - if(layer_group) - { - // Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube) - SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer")); - SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel); - } -#endif - -#if LL_DARWIN - EventTargetRef event_target = GetEventDispatcherTarget(); + // cocoa_interface.mEventTarget = GetEventDispatcherTarget(); #endif while(!plugin->isDone()) { #if LL_DARWIN - createAutoReleasePool(); + cocoa_interface.createAutoReleasePool(); #endif timer.reset(); plugin->idle(); #if LL_DARWIN { - // Some plugins (webkit at least) will want an event loop. This qualifies. - EventRef event; - if(ReceiveNextEvent(0, 0, kEventDurationNoWait, true, &event) == noErr) - { - SendEventToEventTarget (event, event_target); - ReleaseEvent(event); - } - - // Check for a change in this process's frontmost window. - if(GetFrontWindowOfClass(kAllWindowClasses, true) != front_window) - { - ProcessSerialNumber self = { 0, kCurrentProcess }; - ProcessSerialNumber parent = { 0, kNoProcess }; - ProcessSerialNumber front = { 0, kNoProcess }; - Boolean this_is_front_process = false; - Boolean parent_is_front_process = false; - { - // Get this process's parent - ProcessInfoRec info; - info.processInfoLength = sizeof(ProcessInfoRec); - info.processName = NULL; - info.processAppSpec = NULL; - if(GetProcessInformation( &self, &info ) == noErr) - { - parent = info.processLauncher; - } - - // and figure out whether this process or its parent are currently frontmost - if(GetFrontProcess(&front) == noErr) - { - (void) SameProcess(&self, &front, &this_is_front_process); - (void) SameProcess(&parent, &front, &parent_is_front_process); - } - } - - if((GetFrontWindowOfClass(kAllWindowClasses, true) != NULL) && (front_window == NULL)) - { - // Opening the first window - - if(window_hack_state == 0) - { - // Next time through the event loop, lower the window group layer - window_hack_state = 1; - } - - if(layer_group) - { - SetWindowGroup(GetFrontWindowOfClass(kAllWindowClasses, true), layer_group); - } - - if(parent_is_front_process) - { - // Bring this process's windows to the front. - (void) SetFrontProcess( &self ); - } - - ActivateWindow(GetFrontWindowOfClass(kAllWindowClasses, true), true); - } - else if((GetFrontWindowOfClass(kAllWindowClasses, true) == NULL) && (front_window != NULL)) - { - // Closing the last window - - if(this_is_front_process) - { - // Try to bring this process's parent to the front - (void) SetFrontProcess(&parent); - } - } - else if(window_hack_state == 1) - { - if(layer_group) - { - // Set the window group level back to something less extreme - SetWindowGroupLevel(layer_group, kCGNormalWindowLevel); - } - window_hack_state = 2; - } - - front_window = GetFrontWindowOfClass(kAllWindowClasses, true); - - } - } + cocoa_interface.processEvents(); + } #endif F64 elapsed = timer.getElapsedTimeF64(); F64 remaining = plugin->getSleepTime() - elapsed; @@ -377,7 +289,8 @@ int main(int argc, char **argv) // LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" << LL_ENDL; } - + + #if LL_WINDOWS // More agressive checking of interfering exception handlers. // Doesn't appear to be required so far - even for plugins @@ -387,14 +300,14 @@ int main(int argc, char **argv) #endif #if LL_DARWIN - deleteAutoReleasePool(); + cocoa_interface.deleteAutoReleasePool(); #endif } - delete plugin; ll_cleanup_apr(); + return 0; } diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp old mode 100644 new mode 100755 index 30532247ac..6dee192783 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -149,7 +149,8 @@ bool LLPrimitive::cleanupVolumeManager() LLPrimitive::LLPrimitive() : mTextureList(), mNumTEs(0), - mMiscFlags(0) + mMiscFlags(0), + mNumBumpmapTEs(0) { mPrimitiveCode = 0; @@ -237,7 +238,10 @@ void LLPrimitive::setAllTETextures(const LLUUID &tex_id) //=============================================================== void LLPrimitive::setTE(const U8 index, const LLTextureEntry& te) { - mTextureList.copyTexture(index, te); + if(mTextureList.copyTexture(index, te) != TEM_CHANGE_NONE && te.getBumpmap() > 0) + { + mNumBumpmapTEs++; + } } S32 LLPrimitive::setTETexture(const U8 index, const LLUUID &id) @@ -316,6 +320,7 @@ S32 LLPrimitive::setTERotation(const U8 index, const F32 r) //=============================================================== S32 LLPrimitive::setTEBumpShinyFullbright(const U8 index, const U8 bump) { + updateNumBumpmap(index, bump); return mTextureList.setBumpShinyFullbright(index, bump); } @@ -326,11 +331,13 @@ S32 LLPrimitive::setTEMediaTexGen(const U8 index, const U8 media) S32 LLPrimitive::setTEBumpmap(const U8 index, const U8 bump) { + updateNumBumpmap(index, bump); return mTextureList.setBumpMap(index, bump); } S32 LLPrimitive::setTEBumpShiny(const U8 index, const U8 bump_shiny) { + updateNumBumpmap(index, bump_shiny); return mTextureList.setBumpShiny(index, bump_shiny); } @@ -1445,6 +1452,26 @@ void LLPrimitive::takeTextureList(LLPrimTextureList& other_list) mTextureList.take(other_list); } +void LLPrimitive::updateNumBumpmap(const U8 index, const U8 bump) +{ + LLTextureEntry* te = getTE(index); + if(!te) + { + return; + } + + U8 old_bump = te->getBumpmap(); + if(old_bump > 0) + { + mNumBumpmapTEs--; + } + if((bump & TEM_BUMP_MASK) > 0) + { + mNumBumpmapTEs++; + } + + return; +} //============================================================================ // Moved from llselectmgr.cpp diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 998016f8f6..8dcaa8c740 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -421,7 +421,8 @@ public: inline BOOL isAvatar() const; inline BOOL isSittingAvatar() const; inline BOOL isSittingAvatarOnGround() const; - + inline bool hasBumpmap() const { return mNumBumpmapTEs > 0;} + void setFlags(U32 flags) { mMiscFlags = flags; } void addFlags(U32 flags) { mMiscFlags |= flags; } void removeFlags(U32 flags) { mMiscFlags &= ~flags; } @@ -435,6 +436,9 @@ public: inline static BOOL isPrimitive(const LLPCode pcode); inline static BOOL isApp(const LLPCode pcode); +private: + void updateNumBumpmap(const U8 index, const U8 bump); + protected: LLPCode mPrimitiveCode; // Primitive code LLVector3 mVelocity; // how fast are we moving? @@ -444,6 +448,7 @@ protected: LLPrimTextureList mTextureList; // list of texture GUIDs, scales, offsets U8 mMaterial; // Material code U8 mNumTEs; // # of faces on the primitve + U8 mNumBumpmapTEs; // number of bumpmap TEs. U32 mMiscFlags; // home for misc bools public: diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 4dc2fcd714..647512eb2e 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -789,7 +789,7 @@ const LLFontDescriptor& LLFontGL::getFontDesc() const } // static -void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector& xui_paths, bool create_gl_textures) +void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures) { sVertDPI = (F32)llfloor(screen_dpi * y_scale); sHorizDPI = (F32)llfloor(screen_dpi * x_scale); @@ -800,7 +800,7 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st // Font registry init if (!sFontRegistry) { - sFontRegistry = new LLFontRegistry(xui_paths, create_gl_textures); + sFontRegistry = new LLFontRegistry(create_gl_textures); sFontRegistry->parseFontInfo("fonts.xml"); } else diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 5ed5d2c4eb..0988e99deb 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -150,7 +150,7 @@ public: const LLFontDescriptor& getFontDesc() const; - static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector& xui_paths, bool create_gl_textures = true); + static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true); // Load sans-serif, sans-serif-small, etc. // Slow, requires multiple seconds to load fonts. diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 4d22eba3d9..b5bdba996f 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -163,14 +163,9 @@ LLFontDescriptor LLFontDescriptor::normalize() const return LLFontDescriptor(new_name,new_size,new_style,getFileNames()); } -LLFontRegistry::LLFontRegistry(const string_vec_t& xui_paths, - bool create_gl_textures) +LLFontRegistry::LLFontRegistry(bool create_gl_textures) : mCreateGLTextures(create_gl_textures) { - // Propagate this down from LLUICtrlFactory so LLRender doesn't - // need an upstream dependency on LLUI. - mXUIPaths = xui_paths; - // This is potentially a slow directory traversal, so we want to // cache the result. mUltimateFallbackList = LLWindow::getDynamicFallbackFontList(); @@ -183,27 +178,30 @@ LLFontRegistry::~LLFontRegistry() bool LLFontRegistry::parseFontInfo(const std::string& xml_filename) { - bool success = false; // Succeed if we find at least one XUI file - const string_vec_t& xml_paths = mXUIPaths; + bool success = false; // Succeed if we find and read at least one XUI file + const string_vec_t xml_paths = gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename); + if (xml_paths.empty()) + { + // We didn't even find one single XUI file + return false; + } + for (string_vec_t::const_iterator path_it = xml_paths.begin(); path_it != xml_paths.end(); ++path_it) { - LLXMLNodePtr root; - std::string full_filename = gDirUtilp->findSkinnedFilename(*path_it, xml_filename); - bool parsed_file = LLXMLNode::parseFile(full_filename, root, NULL); + bool parsed_file = LLXMLNode::parseFile(*path_it, root, NULL); if (!parsed_file) continue; - + if ( root.isNull() || ! root->hasName( "fonts" ) ) { - llwarns << "Bad font info file: " - << full_filename << llendl; + llwarns << "Bad font info file: " << *path_it << llendl; continue; } - + std::string root_name; root->getAttributeString("name",root_name); if (root->hasName("fonts")) @@ -215,7 +213,7 @@ bool LLFontRegistry::parseFontInfo(const std::string& xml_filename) } //if (success) // dump(); - + return success; } diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h index 8b06191c56..059248fbbd 100644 --- a/indra/llrender/llfontregistry.h +++ b/indra/llrender/llfontregistry.h @@ -67,8 +67,7 @@ class LLFontRegistry public: // create_gl_textures - set to false for test apps with no OpenGL window, // such as llui_libtest - LLFontRegistry(const string_vec_t& xui_paths, - bool create_gl_textures); + LLFontRegistry(bool create_gl_textures); ~LLFontRegistry(); // Load standard font info from XML file(s). @@ -105,7 +104,6 @@ private: font_size_map_t mFontSizes; string_vec_t mUltimateFallbackList; - string_vec_t mXUIPaths; bool mCreateGLTextures; }; diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 964495a3ab..d70e764769 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -424,6 +424,10 @@ const U32 FENCE_WAIT_TIME_NANOSECONDS = 1000; //1 ms class LLGLFence { public: + virtual ~LLGLFence() + { + } + virtual void placeFence() = 0; virtual bool isCompleted() = 0; virtual void wait() = 0; diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h index a0727b8686..509de51f4d 100644 --- a/indra/llrender/llglheaders.h +++ b/indra/llrender/llglheaders.h @@ -993,7 +993,12 @@ extern void glGetBufferPointervARB (GLenum, GLenum, GLvoid* *); } #endif +#if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1070 +#include +#else #include +#endif + #endif // LL_MESA / LL_WINDOWS / LL_DARWIN diff --git a/indra/llrender/llglstates.h b/indra/llrender/llglstates.h index e26aead676..0e2c3bcb44 100644 --- a/indra/llrender/llglstates.h +++ b/indra/llrender/llglstates.h @@ -59,7 +59,6 @@ protected: LLGLEnable mColorMaterial; LLGLDisable mAlphaTest, mBlend, mCullFace, mDither, mFog, mLineSmooth, mLineStipple, mNormalize, mPolygonSmooth, - mTextureGenQ, mTextureGenR, mTextureGenS, mTextureGenT, mGLMultisample; public: LLGLSDefault() @@ -76,10 +75,6 @@ public: mLineStipple(GL_LINE_STIPPLE), mNormalize(GL_NORMALIZE), mPolygonSmooth(GL_POLYGON_SMOOTH), - mTextureGenQ(GL_TEXTURE_GEN_Q), - mTextureGenR(GL_TEXTURE_GEN_R), - mTextureGenS(GL_TEXTURE_GEN_S), - mTextureGenT(GL_TEXTURE_GEN_T), mGLMultisample(GL_MULTISAMPLE_ARB) { } }; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp old mode 100644 new mode 100755 index a842211764..a4d7872ec2 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -478,7 +478,7 @@ bool LLImageGL::checkSize(S32 width, S32 height) return check_power_of_two(width) && check_power_of_two(height); } -void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents) +void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level) { if (width != mWidth || height != mHeight || ncomponents != mComponents) { @@ -511,6 +511,11 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents) width >>= 1; height >>= 1; } + + if(discard_level > 0) + { + mMaxDiscardLevel = llmax(mMaxDiscardLevel, (S8)discard_level); + } } else { @@ -860,14 +865,13 @@ BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) llassert(mCurrentDiscardLevel >= 0); discard_level = mCurrentDiscardLevel; } - discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); - + // Actual image width/height = raw image width/height * 2^discard_level S32 w = raw_image->getWidth() << discard_level; S32 h = raw_image->getHeight() << discard_level; // setSize may call destroyGLTexture if the size does not match - setSize(w, h, raw_image->getComponents()); + setSize(w, h, raw_image->getComponents(), discard_level); if( !mHasExplicitFormat ) { @@ -1264,8 +1268,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S llassert(mCurrentDiscardLevel >= 0); discard_level = mCurrentDiscardLevel; } - discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); - + // Actual image width/height = raw image width/height * 2^discard_level S32 raw_w = imageraw->getWidth() ; S32 raw_h = imageraw->getHeight() ; @@ -1273,7 +1276,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S S32 h = raw_h << discard_level; // setSize may call destroyGLTexture if the size does not match - setSize(w, h, imageraw->getComponents()); + setSize(w, h, imageraw->getComponents(), discard_level); if( !mHasExplicitFormat ) { diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h old mode 100644 new mode 100755 index e118c28c1b..cf3c484c79 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -100,7 +100,7 @@ protected: public: virtual void dump(); // debugging info to llinfos - void setSize(S32 width, S32 height, S32 ncomponents); + void setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level = -1); void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;} void setAllowCompression(bool allow) { mAllowCompression = allow; } diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 348c1eb1b7..4597d06260 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -648,7 +648,7 @@ void LLTexUnit::setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eT gGL.flush(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); } - + // We want an early out, because this function does a LOT of stuff. if ( ( (isAlpha && (mCurrAlphaOp == op) && (mCurrAlphaSrc1 == src1) && (mCurrAlphaSrc2 == src2)) || (!isAlpha && (mCurrColorOp == op) && (mCurrColorSrc1 == src1) && (mCurrColorSrc2 == src2)) ) && !gGL.mDirty) @@ -1437,6 +1437,17 @@ void LLRender::matrixMode(U32 mode) mMatrixMode = mode; } +U32 LLRender::getMatrixMode() +{ + if (mMatrixMode >= MM_TEXTURE0 && mMatrixMode <= MM_TEXTURE3) + { //always return MM_TEXTURE if current matrix mode points at any texture matrix + return MM_TEXTURE; + } + + return mMatrixMode; +} + + void LLRender::loadIdentity() { flush(); diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index fa5f7f311d..78a310e525 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -346,6 +346,7 @@ public: void loadIdentity(); void multMatrix(const GLfloat* m); void matrixMode(U32 mode); + U32 getMatrixMode(); const glh::matrix4f& getModelviewMatrix(); const glh::matrix4f& getProjectionMatrix(); diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index cc5c232380..c1b96a43da 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -51,11 +51,13 @@ void check_framebuffer_status() } bool LLRenderTarget::sUseFBO = false; +U32 LLRenderTarget::sCurFBO = 0; LLRenderTarget::LLRenderTarget() : mResX(0), mResY(0), mFBO(0), + mPreviousFBO(0), mDepth(0), mStencil(0), mUseDepth(false), @@ -107,6 +109,9 @@ void LLRenderTarget::resize(U32 resx, U32 resy, U32 color_fmt) bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, S32 samples) { + resx = llmin(resx, (U32) 4096); + resy = llmin(resy, (U32) 4096); + stop_glerror(); release(); stop_glerror(); @@ -146,7 +151,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0); stop_glerror(); } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); } stop_glerror(); @@ -163,10 +168,19 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) } U32 offset = mTex.size(); - if (offset >= 4 || - (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) + + if( offset >= 4 ) { - llerrs << "Too many color attachments!" << llendl; + llwarns << "Too many color attachments" << llendl; + llassert( offset < 4 ); + return false; + } + if( offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers) ) + { + llwarns << "FBO not used or no drawbuffers available; mFBO=" << (U32)mFBO << " gGLManager.mHasDrawBuffers=" << (U32)gGLManager.mHasDrawBuffers << llendl; + llassert( mFBO != 0 ); + llassert( gGLManager.mHasDrawBuffers ); + return false; } U32 tex; @@ -224,7 +238,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) check_framebuffer_status(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); } mTex.push_back(tex); @@ -313,7 +327,7 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target) check_framebuffer_status(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); target.mUseDepth = true; } @@ -376,9 +390,13 @@ void LLRenderTarget::bindTarget() { if (mFBO) { + mPreviousFBO = sCurFBO; + stop_glerror(); glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + sCurFBO = mFBO; + stop_glerror(); if (gGLManager.mHasDrawBuffers) { //setup multiple render targets @@ -404,16 +422,6 @@ void LLRenderTarget::bindTarget() sBoundTarget = this; } -// static -void LLRenderTarget::unbindTarget() -{ - if (gGLManager.mHasFramebufferObject) - { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - sBoundTarget = NULL; -} - void LLRenderTarget::clear(U32 mask_in) { U32 mask = GL_COLOR_BUFFER_BIT; @@ -479,7 +487,8 @@ void LLRenderTarget::flush(bool fetch_depth) else { stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, mPreviousFBO); + sCurFBO = mPreviousFBO; stop_glerror(); } } @@ -509,7 +518,7 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, stop_glerror(); glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1); stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); stop_glerror(); } else @@ -526,7 +535,7 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, stop_glerror(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); stop_glerror(); } } @@ -552,7 +561,7 @@ void LLRenderTarget::copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0 stop_glerror(); glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); stop_glerror(); } } diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index e1a51304f1..cf15f66d31 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -63,6 +63,7 @@ public: //whether or not to use FBO implementation static bool sUseFBO; static U32 sBytesAllocated; + static U32 sCurFBO; LLRenderTarget(); ~LLRenderTarget(); @@ -96,9 +97,6 @@ public: //applies appropriate viewport void bindTarget(); - //unbind target for rendering - static void unbindTarget(); - //clear render targer, clears depth buffer if present, //uses scissor rect if in copy-to-texture mode void clear(U32 mask = 0xFFFFFFFF); @@ -148,6 +146,7 @@ protected: std::vector mTex; std::vector mInternalFormat; U32 mFBO; + U32 mPreviousFBO; U32 mDepth; bool mStencil; bool mUseDepth; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index a9248d4d73..b6a9a6b653 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -643,7 +643,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade text[count++] = strdup("#define textureCube texture\n"); text[count++] = strdup("#define texture2DLod textureLod\n"); text[count++] = strdup("#define shadow2D(a,b) vec2(texture(a,b))\n"); - + if (major_version > 1 || minor_version >= 40) { //GLSL 1.40 replaces texture2DRect et al with texture text[count++] = strdup("#define texture2DRect texture\n"); diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 80752231d7..2fe0aa0b72 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -38,10 +38,6 @@ #include "llglslshader.h" #include "llmemory.h" -#if LL_DARWIN -#define LL_VBO_POOLING 1 -#else -#endif //Next Highest Power Of Two //helper function, returns first number > v that is a power of 2, or v if v is already a power of 2 U32 nhpo2(U32 v) @@ -294,6 +290,7 @@ void LLVBOPool::seedPool() } + void LLVBOPool::cleanup() { U32 size = LL_VBO_BLOCK_SIZE; @@ -558,8 +555,21 @@ void LLVertexBuffer::drawArrays(U32 mode, const std::vector& pos, con gGL.syncMatrices(); U32 count = pos.size(); - llassert_always(norm.size() >= pos.size()); - llassert_always(count > 0); + + llassert(norm.size() >= pos.size()); + llassert(count > 0); + + if( count == 0 ) + { + llwarns << "Called drawArrays with 0 vertices" << llendl; + return; + } + + if( norm.size() < pos.size() ) + { + llwarns << "Called drawArrays with #" << norm.size() << " normals and #" << pos.size() << " vertices" << llendl; + return; + } unbind(); diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index cca4ca3981..d92b6aa1c0 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -155,7 +155,6 @@ set(llui_HEADER_FILES llflyoutbutton.h llfocusmgr.h llfunctorregistry.h - llhandle.h llhelp.h lliconctrl.h llkeywords.h diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 806d2ef3f6..41e5d74042 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -563,8 +563,7 @@ void LLComboBox::showList() S32 min_width = getRect().getWidth(); S32 max_width = llmax(min_width, MAX_COMBO_WIDTH); // make sure we have up to date content width metrics - mList->calcColumnWidths(); - S32 list_width = llclamp(mList->getMaxContentWidth(), min_width, max_width); + S32 list_width = llclamp(mList->calcMaxContentWidth(), min_width, max_width); if (mListPosition == BELOW) { diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 8ca1e685a9..054b9173d3 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -748,6 +748,10 @@ void LLFloater::closeFloater(bool app_quitting) dependee->setFocus(TRUE); } } + + // STORM-1879: since this floater has focus, treat the closeFloater- call + // like a click on the close-button, and close gear- and contextmenus + LLMenuGL::sMenuContainer->hideMenus(); } dirtyRect(); @@ -3229,24 +3233,14 @@ bool LLFloater::isVisible(const LLFloater* floater) static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters"); -bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_node) +bool LLFloater::buildFromFile(const std::string& filename) { LLFastTimer timer(FTM_BUILD_FLOATERS); LLXMLNodePtr root; - //if exporting, only load the language being exported, - //instead of layering localized version on top of english - if (output_node) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { - if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) - { - llwarns << "Couldn't parse floater from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; - return false; - } - } - else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) - { - llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; + llwarns << "Couldn't find (or parse) floater from: " << filename << llendl; return false; } @@ -3271,7 +3265,7 @@ bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_n getCommitCallbackRegistrar().pushScope(); getEnableCallbackRegistrar().pushScope(); - res = initFloaterXML(root, getParent(), filename, output_node); + res = initFloaterXML(root, getParent(), filename, NULL); setXMLFilename(filename); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 64d6dcea04..e64b6d04d3 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -202,7 +202,7 @@ public: // Don't export top/left for rect, only height/width static void setupParamsForExport(Params& p, LLView* parent); - bool buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL); + bool buildFromFile(const std::string &filename); boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 9115eb7174..306caf2b91 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -154,7 +154,7 @@ LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key) llwarns << "Failed to build floater type: '" << name << "'." << llendl; return NULL; } - bool success = res->buildFromFile(xui_file, NULL); + bool success = res->buildFromFile(xui_file); if (!success) { llwarns << "Failed to build floater type: '" << name << "'." << llendl; diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 629eef2c3b..210a320f41 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1424,25 +1424,19 @@ void addPathIfExists(const std::string& new_path, std::vector& path bool LLNotifications::loadTemplates() { llinfos << "Reading notifications template" << llendl; - std::vector search_paths; - - std::string skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml"; - std::string localized_skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml"; - - addPathIfExists(gDirUtilp->getDefaultSkinDir() + skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getDefaultSkinDir() + localized_skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getSkinDir() + skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getSkinDir() + localized_skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getUserSkinDir() + skin_relative_path, search_paths); - addPathIfExists(gDirUtilp->getUserSkinDir() + localized_skin_relative_path, search_paths); + // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it + // output all relevant pathnames instead of just the ones from the most + // specific skin. + std::vector search_paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS); std::string base_filename = search_paths.front(); LLXMLNodePtr root; BOOL success = LLXMLNode::getLayeredXMLNode(root, search_paths); - + if (!success || root.isNull() || !root->hasName( "notifications" )) { - llerrs << "Problem reading UI Notifications file: " << base_filename << llendl; + llerrs << "Problem reading XML from UI Notifications file: " << base_filename << llendl; return false; } @@ -1452,7 +1446,7 @@ bool LLNotifications::loadTemplates() if(!params.validateBlock()) { - llerrs << "Problem reading UI Notifications file: " << base_filename << llendl; + llerrs << "Problem reading XUI from UI Notifications file: " << base_filename << llendl; return false; } @@ -1508,7 +1502,9 @@ bool LLNotifications::loadTemplates() bool LLNotifications::loadVisibilityRules() { const std::string xml_filename = "notification_visibility.xml"; - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename); + // Note that here we're looking for the "en" version, the default + // language, rather than the most localized version of this file. + std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename); LLNotificationVisibilityRule::Rules params; LLSimpleXUIParser parser; diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 00318cec6b..67472ad166 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -968,25 +968,15 @@ static LLFastTimer::DeclareTimer FTM_BUILD_PANELS("Build Panels"); //----------------------------------------------------------------------------- // buildPanel() //----------------------------------------------------------------------------- -BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_node, const LLPanel::Params& default_params) +BOOL LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params) { LLFastTimer timer(FTM_BUILD_PANELS); BOOL didPost = FALSE; LLXMLNodePtr root; - //if exporting, only load the language being exported, - //instead of layering localized version on top of english - if (output_node) - { - if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) - { - llwarns << "Couldn't parse panel from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; - return didPost; - } - } - else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { - llwarns << "Couldn't parse panel from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; + llwarns << "Couldn't parse panel from: " << filename << llendl; return didPost; } @@ -1010,7 +1000,7 @@ BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_nod getCommitCallbackRegistrar().pushScope(); getEnableCallbackRegistrar().pushScope(); - didPost = initPanelXML(root, NULL, output_node, default_params); + didPost = initPanelXML(root, NULL, NULL, default_params); getCommitCallbackRegistrar().popScope(); getEnableCallbackRegistrar().popScope(); diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index f620201020..e63b41f97c 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -105,7 +105,7 @@ protected: LLPanel(const LLPanel::Params& params = getDefaultParams()); public: - BOOL buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL, const LLPanel::Params&default_params = getDefaultParams()); + BOOL buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams()); static LLPanel* createFactoryPanel(const std::string& name); diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp index 07a6dfaa10..af124d9826 100644 --- a/indra/llui/llscrolllistcolumn.cpp +++ b/indra/llui/llscrolllistcolumn.cpp @@ -98,6 +98,7 @@ BOOL LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) if (canResize() && mResizeBar->getRect().pointInRect(x, y)) { // reshape column to max content width + mColumn->mParentCtrl->calcMaxContentWidth(); LLRect column_rect = getRect(); column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth; setShape(column_rect, true); @@ -127,6 +128,8 @@ LLView* LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& m LLRect snap_rect = getSnapRect(); + mColumn->mParentCtrl->calcMaxContentWidth(); + S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth(); // x coord growing means column growing, so same signs mean we're going in right direction diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 802914e25b..d332aa933e 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -35,6 +35,7 @@ #include "llboost.h" //#include "indra_constants.h" +#include "llavatarnamecache.h" #include "llcheckboxctrl.h" #include "llclipboard.h" #include "llfocusmgr.h" @@ -158,15 +159,14 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) mMouseWheelOpaque(p.mouse_wheel_opaque), mPageLines(p.page_lines), mMaxSelectable(0), - mAllowKeyboardMovement(TRUE), + mAllowKeyboardMovement(true), mCommitOnKeyboardMovement(p.commit_on_keyboard_movement), - mCommitOnSelectionChange(FALSE), - mSelectionChanged(FALSE), - mNeedsScroll(FALSE), - mCanSelect(TRUE), - mColumnsDirty(FALSE), + mCommitOnSelectionChange(false), + mSelectionChanged(false), + mNeedsScroll(false), + mCanSelect(true), + mColumnsDirty(false), mMaxItemCount(INT_MAX), - mMaxContentWidth(0), mBorderThickness( 2 ), mOnDoubleClickCallback( NULL ), mOnMaximumSelectCallback( NULL ), @@ -180,7 +180,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) mTotalStaticColumnWidth(0), mTotalColumnPadding(0), mSorted(false), - mDirty(FALSE), + mDirty(false), mOriginalSelection(-1), mLastSelected(NULL), mHeadingHeight(p.heading_height), @@ -356,7 +356,7 @@ void LLScrollListCtrl::clearRows() mScrollLines = 0; mLastSelected = NULL; updateLayout(); - mDirty = FALSE; + mDirty = false; } @@ -580,6 +580,15 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r addColumn(col_params); } + S32 num_cols = item->getNumColumns(); + S32 i = 0; + for (LLScrollListCell* cell = item->getColumn(i); i < num_cols; cell = item->getColumn(++i)) + { + if (i >= (S32)mColumnsIndexed.size()) break; + + cell->setWidth(mColumnsIndexed[i]->getWidth()); + } + updateLineHeightInsert(item); updateLayout(); @@ -591,15 +600,42 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r // NOTE: This is *very* expensive for large lists, especially when we are dirtying the list every frame // while receiving a long list of names. // *TODO: Use bookkeeping to make this an incramental cost with item additions -void LLScrollListCtrl::calcColumnWidths() +S32 LLScrollListCtrl::calcMaxContentWidth() { const S32 HEADING_TEXT_PADDING = 25; const S32 COLUMN_TEXT_PADDING = 10; - mMaxContentWidth = 0; - S32 max_item_width = 0; + ordered_columns_t::iterator column_itor; + for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) + { + LLScrollListColumn* column = *column_itor; + if (!column) continue; + + if (mColumnWidthsDirty) + { + mColumnWidthsDirty = false; + // update max content width for this column, by looking at all items + column->mMaxContentWidth = column->mHeader ? LLFontGL::getFontSansSerifSmall()->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); + if (!cellp) continue; + + column->mMaxContentWidth = llmax(LLFontGL::getFontSansSerifSmall()->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + } + } + max_item_width += column->mMaxContentWidth; + } + + return max_item_width; +} + +bool LLScrollListCtrl::updateColumnWidths() +{ + bool width_changed = false; ordered_columns_t::iterator column_itor; for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) { @@ -617,23 +653,13 @@ void LLScrollListCtrl::calcColumnWidths() new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns; } - column->setWidth(new_width); - - // update max content width for this column, by looking at all items - column->mMaxContentWidth = column->mHeader ? LLFontGL::getFontSansSerifSmall()->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + if (column->getWidth() != new_width) { - LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); - if (!cellp) continue; - - column->mMaxContentWidth = llmax(LLFontGL::getFontSansSerifSmall()->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + column->setWidth(new_width); + width_changed = true; } - - max_item_width += column->mMaxContentWidth; } - - mMaxContentWidth = max_item_width; + return width_changed; } const S32 SCROLL_LIST_ROW_PAD = 2; @@ -669,7 +695,12 @@ void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp) void LLScrollListCtrl::updateColumns() { - calcColumnWidths(); + if (!mColumnsDirty) + return; + + mColumnsDirty = false; + + bool columns_changed_width = updateColumnWidths(); // update column headers std::vector::iterator column_ordered_it; @@ -718,20 +749,22 @@ void LLScrollListCtrl::updateColumns() } // propagate column widths to individual cells - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + if (columns_changed_width) { - LLScrollListItem *itemp = *iter; - S32 num_cols = itemp->getNumColumns(); - S32 i = 0; - for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { - if (i >= (S32)mColumnsIndexed.size()) break; + LLScrollListItem *itemp = *iter; + S32 num_cols = itemp->getNumColumns(); + S32 i = 0; + for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) + { + if (i >= (S32)mColumnsIndexed.size()) break; - cell->setWidth(mColumnsIndexed[i]->getWidth()); + cell->setWidth(mColumnsIndexed[i]->getWidth()); + } } } - } void LLScrollListCtrl::setHeadingHeight(S32 heading_height) @@ -772,7 +805,7 @@ BOOL LLScrollListCtrl::selectFirstItem() { deselectItem(itemp); } - first_item = FALSE; + first_item = false; } if (mCommitOnSelectionChange) { @@ -1406,17 +1439,23 @@ void LLScrollListCtrl::drawItems() S32 cur_y = y; - S32 line = 0; S32 max_columns = 0; LLColor4 highlight_color = LLColor4::white; static LLUICachedControl type_ahead_timeout ("TypeAheadTimeout", 0); highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f); - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + S32 first_line = mScrollLines; + S32 last_line = llmin((S32)mItemList.size() - 1, mScrollLines + getLinesPerPage()); + + if (first_line >= mItemList.size()) { - LLScrollListItem* item = *iter; + return; + } + item_list::iterator iter; + for (S32 line = first_line; line <= last_line; line++) + { + LLScrollListItem* item = mItemList[line]; item_rect.setOriginAndSize( x, @@ -1480,7 +1519,6 @@ void LLScrollListCtrl::drawItems() cur_y -= mLineHeight; } - line++; } } } @@ -1496,7 +1534,7 @@ void LLScrollListCtrl::draw() if (mNeedsScroll) { scrollToShowSelected(); - mNeedsScroll = FALSE; + mNeedsScroll = false; } LLRect background(0, getRect().getHeight(), getRect().getWidth(), 0); // Draw background @@ -1507,11 +1545,7 @@ void LLScrollListCtrl::draw() gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha ); } - if (mColumnsDirty) - { - updateColumns(); - mColumnsDirty = FALSE; - } + updateColumns(); getChildView("comment_text")->setVisible(mItemList.empty()); @@ -1719,7 +1753,7 @@ BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) setFocus(TRUE); // clear selection changed flag because user is starting a selection operation - mSelectionChanged = FALSE; + mSelectionChanged = false; handleClick(x, y, mask); } @@ -1737,15 +1771,15 @@ BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) if(mask == MASK_NONE) { selectItemAt(x, y, mask); - mNeedsScroll = TRUE; + mNeedsScroll = true; } } // always commit when mouse operation is completed inside list if (mItemListRect.pointInRect(x,y)) { - mDirty |= mSelectionChanged; - mSelectionChanged = FALSE; + mDirty = mDirty || mSelectionChanged; + mSelectionChanged = false; onCommit(); } @@ -1805,7 +1839,9 @@ void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group) } else { - gCacheName->getFullName(LLUUID(id), name); + LLAvatarName av_name; + LLAvatarNameCache::get(LLUUID(id), &av_name); + name = av_name.getLegacyName(); } LLUrlAction::copyURLToClipboard(name); } @@ -1859,7 +1895,7 @@ BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) { selectItemAt(x, y, mask); gFocusMgr.setMouseCapture(this); - mNeedsScroll = TRUE; + mNeedsScroll = true; } // propagate state of cell to rest of selected column @@ -1888,7 +1924,7 @@ BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) // treat this as a normal single item selection selectItemAt(x, y, mask); gFocusMgr.setMouseCapture(this); - mNeedsScroll = TRUE; + mNeedsScroll = true; // do not eat click (allow double click callback) return FALSE; } @@ -1994,7 +2030,7 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) if(mask == MASK_NONE) { selectItemAt(x, y, mask); - mNeedsScroll = TRUE; + mNeedsScroll = true; } } else @@ -2041,7 +2077,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { // commit implicit in call selectPrevItem(FALSE); - mNeedsScroll = TRUE; + mNeedsScroll = true; handled = TRUE; } break; @@ -2050,7 +2086,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { // commit implicit in call selectNextItem(FALSE); - mNeedsScroll = TRUE; + mNeedsScroll = true; handled = TRUE; } break; @@ -2058,7 +2094,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) if (mAllowKeyboardMovement || hasFocus()) { selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1)); - mNeedsScroll = TRUE; + mNeedsScroll = true; if (mCommitOnKeyboardMovement && !mCommitOnSelectionChange) { @@ -2071,7 +2107,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) if (mAllowKeyboardMovement || hasFocus()) { selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1)); - mNeedsScroll = TRUE; + mNeedsScroll = true; if (mCommitOnKeyboardMovement && !mCommitOnSelectionChange) { @@ -2084,7 +2120,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) if (mAllowKeyboardMovement || hasFocus()) { selectFirstItem(); - mNeedsScroll = TRUE; + mNeedsScroll = true; if (mCommitOnKeyboardMovement && !mCommitOnSelectionChange) { @@ -2097,7 +2133,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) if (mAllowKeyboardMovement || hasFocus()) { selectNthItem(getItemCount() - 1); - mNeedsScroll = TRUE; + mNeedsScroll = true; if (mCommitOnKeyboardMovement && !mCommitOnSelectionChange) { @@ -2136,7 +2172,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) } else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), FALSE)) { - mNeedsScroll = TRUE; + mNeedsScroll = true; // update search string only on successful match mSearchTimer.reset(); @@ -2177,7 +2213,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE)) { // update search string only on successful match - mNeedsScroll = TRUE; + mNeedsScroll = true; mSearchString += uni_char; mSearchTimer.reset(); @@ -2223,7 +2259,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) { selectItem(item); - mNeedsScroll = TRUE; + mNeedsScroll = true; cellp->highlightText(0, 1); mSearchTimer.reset(); @@ -2294,7 +2330,7 @@ void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_it } itemp->setSelected(TRUE); mLastSelected = itemp; - mSelectionChanged = TRUE; + mSelectionChanged = true; } } @@ -2315,7 +2351,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) { cellp->highlightText(0, 0); } - mSelectionChanged = TRUE; + mSelectionChanged = true; } } @@ -2323,7 +2359,7 @@ void LLScrollListCtrl::commitIfChanged() { if (mSelectionChanged) { - mDirty = TRUE; + mDirty = true; mSelectionChanged = FALSE; onCommit(); } @@ -2434,7 +2470,8 @@ void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending) void LLScrollListCtrl::dirtyColumns() { - mColumnsDirty = TRUE; + mColumnsDirty = true; + mColumnWidthsDirty = true; // need to keep mColumnsIndexed up to date // just in case someone indexes into it immediately @@ -2720,6 +2757,11 @@ BOOL LLScrollListCtrl::hasSortOrder() const return !mSortColumns.empty(); } +void LLScrollListCtrl::clearSortOrder() +{ + mSortColumns.clear(); +} + void LLScrollListCtrl::clearColumns() { column_map_t::iterator itor; @@ -2998,7 +3040,7 @@ void LLScrollListCtrl::resetDirty() void LLScrollListCtrl::onFocusReceived() { // forget latent selection changes when getting focus - mSelectionChanged = FALSE; + mSelectionChanged = false; LLUICtrl::onFocusReceived(); } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 44d9635838..38450b6313 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -343,8 +343,8 @@ public: static void onClickColumn(void *userdata); virtual void updateColumns(); - void calcColumnWidths(); - S32 getMaxContentWidth() { return mMaxContentWidth; } + S32 calcMaxContentWidth(); + bool updateColumnWidths(); void setHeadingHeight(S32 heading_height); /** @@ -374,6 +374,7 @@ public: std::string getSortColumnName(); BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } BOOL hasSortOrder() const; + void clearSortOrder(); S32 selectMultiple( uuid_vec_t ids ); // conceptually const, but mutates mItemList @@ -439,16 +440,17 @@ private: S32 mHeadingHeight; // the height of the column header buttons, if visible U32 mMaxSelectable; LLScrollbar* mScrollbar; - BOOL mAllowMultipleSelection; - BOOL mAllowKeyboardMovement; - BOOL mCommitOnKeyboardMovement; - BOOL mCommitOnSelectionChange; - BOOL mSelectionChanged; - BOOL mNeedsScroll; - BOOL mMouseWheelOpaque; - BOOL mCanSelect; - const BOOL mDisplayColumnHeaders; - BOOL mColumnsDirty; + bool mAllowMultipleSelection; + bool mAllowKeyboardMovement; + bool mCommitOnKeyboardMovement; + bool mCommitOnSelectionChange; + bool mSelectionChanged; + bool mNeedsScroll; + bool mMouseWheelOpaque; + bool mCanSelect; + bool mDisplayColumnHeaders; + bool mColumnsDirty; + bool mColumnWidthsDirty; mutable item_list mItemList; @@ -457,7 +459,6 @@ private: S32 mMaxItemCount; LLRect mItemListRect; - S32 mMaxContentWidth; S32 mColumnPadding; BOOL mBackgroundVisible; @@ -497,7 +498,7 @@ private: typedef std::map column_map_t; column_map_t mColumns; - BOOL mDirty; + bool mDirty; S32 mOriginalSelection; ContextMenuType mContextMenuType; diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp index d29260750f..e4d1a37569 100644 --- a/indra/llui/lltoggleablemenu.cpp +++ b/indra/llui/lltoggleablemenu.cpp @@ -57,7 +57,9 @@ void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn) S32 x,y; LLUI::getMousePositionLocal(LLUI::getRootView(), &x, &y); - if (!curVisibilityIn && mButtonRect.pointInRect(x, y)) + // STORM-1879: also check MouseCapture to see if the button was really + // clicked (otherwise the VisibilityChange was triggered via keyboard shortcut) + if (!curVisibilityIn && mButtonRect.pointInRect(x, y) && gFocusMgr.getMouseCapture()) { mClosedByButtonClick = true; } diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp index 58fa8a0828..80d079cbc8 100644 --- a/indra/llui/lltransutil.cpp +++ b/indra/llui/lltransutil.cpp @@ -31,15 +31,20 @@ #include "lltrans.h" #include "lluictrlfactory.h" #include "llxmlnode.h" - +#include "lldir.h" bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set& default_args) { LLXMLNodePtr root; - BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); + // Pass LLDir::ALL_SKINS to load a composite of all the individual string + // definitions in the default skin and the current skin. This means an + // individual skin can provide an xml_filename that overrides only a + // subset of the available string definitions; any string definition not + // overridden by that skin will be sought in the default skin. + bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS); if (!success) { - llerrs << "Couldn't load string table" << llendl; + llerrs << "Couldn't load string table " << xml_filename << llendl; return false; } @@ -54,7 +59,7 @@ bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename) if (!success) { - llerrs << "Couldn't load string table " << xml_filename << llendl; + llerrs << "Couldn't load localization table " << xml_filename << llendl; return false; } diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 87bf518aa1..6d2bc1837c 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1836,88 +1836,37 @@ struct Paths : public LLInitParam::Block {} }; -//static -void LLUI::setupPaths() -{ - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "paths.xml"); - - LLXMLNodePtr root; - BOOL success = LLXMLNode::parseFile(filename, root, NULL); - Paths paths; - - if(success) - { - LLXUIParser parser; - parser.readXUI(root, paths, filename); - } - sXUIPaths.clear(); - - if (success && paths.validateBlock()) - { - LLStringUtil::format_map_t path_args; - path_args["[LANGUAGE]"] = LLUI::getLanguage(); - - for (LLInitParam::ParamIterator::const_iterator it = paths.directories.begin(), - end_it = paths.directories.end(); - it != end_it; - ++it) - { - std::string path_val_ui; - for (LLInitParam::ParamIterator::const_iterator subdir_it = it->subdirs.begin(), - subdir_end_it = it->subdirs.end(); - subdir_it != subdir_end_it;) - { - path_val_ui += subdir_it->value(); - if (++subdir_it != subdir_end_it) - path_val_ui += gDirUtilp->getDirDelimiter(); - } - LLStringUtil::format(path_val_ui, path_args); - if (std::find(sXUIPaths.begin(), sXUIPaths.end(), path_val_ui) == sXUIPaths.end()) - { - sXUIPaths.push_back(path_val_ui); - } - - } - } - else // parsing failed - { - std::string slash = gDirUtilp->getDirDelimiter(); - std::string dir = "xui" + slash + "en"; - llwarns << "XUI::config file unable to open: " << filename << llendl; - sXUIPaths.push_back(dir); - } -} - //static std::string LLUI::locateSkin(const std::string& filename) { - std::string slash = gDirUtilp->getDirDelimiter(); std::string found_file = filename; - if (!gDirUtilp->fileExists(found_file)) + if (gDirUtilp->fileExists(found_file)) { - found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? + return found_file; } - if (sSettingGroups["config"] && sSettingGroups["config"]->controlExists("Language")) + + found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? + if (gDirUtilp->fileExists(found_file)) { - if (!gDirUtilp->fileExists(found_file)) - { - std::string localization = getLanguage(); - std::string local_skin = "xui" + slash + localization + slash + filename; - found_file = gDirUtilp->findSkinnedFilename(local_skin); - } + return found_file; } - if (!gDirUtilp->fileExists(found_file)) + + found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename); + if (! found_file.empty()) { - std::string local_skin = "xui" + slash + "en" + slash + filename; - found_file = gDirUtilp->findSkinnedFilename(local_skin); + return found_file; } - if (!gDirUtilp->fileExists(found_file)) + + found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); + if (gDirUtilp->fileExists(found_file)) { - found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); + return found_file; } - return found_file; -} + LL_WARNS("LLUI") << "Can't find '" << filename + << "' in user settings, any skin directory or app_settings" << LL_ENDL; + return ""; +} //static LLVector2 LLUI::getWindowSize() diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 28e84fa444..c5a12d2b31 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -292,11 +292,6 @@ public: // Return the ISO639 language name ("en", "ko", etc.) for the viewer UI. // http://www.loc.gov/standards/iso639-2/php/code_list.php static std::string getLanguage(); - - static void setupPaths(); - static const std::vector& getXUIPaths() { return sXUIPaths; } - static std::string getSkinPath() { return sXUIPaths.front(); } - static std::string getLocalizedSkinPath() { return sXUIPaths.back(); } //all files may not exist at the localized path //helper functions (should probably move free standing rendering helper functions here) static LLView* getRootView() { return sRootView; } diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 9455d09cc0..ffeff15968 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -32,6 +32,7 @@ #include "llui.h" #include "lluicolortable.h" #include "lluictrlfactory.h" +#include LLUIColorTable::ColorParams::ColorParams() : value("value"), @@ -206,19 +207,12 @@ bool LLUIColorTable::loadFromSettings() { bool result = false; - std::string default_filename = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "colors.xml"); - result |= loadFromFilename(default_filename, mLoadedColors); - - std::string current_filename = gDirUtilp->getExpandedFilename(LL_PATH_TOP_SKIN, "colors.xml"); - if(current_filename != default_filename) + // pass constraint=LLDir::ALL_SKINS because we want colors.xml from every + // skin dir + BOOST_FOREACH(std::string colors_path, + gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS)) { - result |= loadFromFilename(current_filename, mLoadedColors); - } - - current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml"); - if(current_filename != default_filename) - { - result |= loadFromFilename(current_filename, mLoadedColors); + result |= loadFromFilename(colors_path, mLoadedColors); } std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml"); diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 25e7a31e90..bd06476936 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -90,10 +90,12 @@ LLUICtrlFactory::~LLUICtrlFactory() void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block) { - std::string filename = std::string("widgets") + gDirUtilp->getDirDelimiter() + widget_tag + ".xml"; + std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml"); LLXMLNodePtr root_node; - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), filename); + // Here we're looking for the "en" version, the default-language version + // of the file, rather than the localized version. + std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, filename); if (!full_filename.empty()) { LLUICtrlFactory::instance().pushFileName(full_filename); @@ -149,22 +151,12 @@ static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing"); //----------------------------------------------------------------------------- // getLayeredXMLNode() //----------------------------------------------------------------------------- -bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) +bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root, + LLDir::ESkinConstraint constraint) { LLFastTimer timer(FTM_XML_PARSE); - - std::vector paths; - std::string path = gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), xui_filename); - if (!path.empty()) - { - paths.push_back(path); - } - - std::string localize_path = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename); - if (!localize_path.empty() && localize_path != path) - { - paths.push_back(localize_path); - } + std::vector paths = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint); if (paths.empty()) { @@ -176,23 +168,6 @@ bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNo } -//----------------------------------------------------------------------------- -// getLocalizedXMLNode() -//----------------------------------------------------------------------------- -bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) -{ - LLFastTimer timer(FTM_XML_PARSE); - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename); - if (!LLXMLNode::parseFile(full_filename, root, NULL)) - { - return false; - } - else - { - return true; - } -} - //----------------------------------------------------------------------------- // saveToXML() //----------------------------------------------------------------------------- @@ -239,8 +214,10 @@ std::string LLUICtrlFactory::getCurFileName() void LLUICtrlFactory::pushFileName(const std::string& name) -{ - mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), name)); +{ + // Here we seem to be looking for the default language file ("en") rather + // than the localized one, if any. + mFileNames.push_back(gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, name)); } void LLUICtrlFactory::popFileName() @@ -255,14 +232,6 @@ void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group) parent->addChild(view, tab_group); } - -// Avoid directly using LLUI and LLDir in the template code -//static -std::string LLUICtrlFactory::findSkinnedFilename(const std::string& filename) -{ - return gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename); -} - //static void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest) { diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index d612ad5005..f6971261d7 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -31,18 +31,11 @@ #include "llinitparam.h" #include "llregistry.h" #include "llxuiparser.h" +#include "llstl.h" +#include "lldir.h" class LLView; -// sort functor for typeid maps -struct LLCompareTypeID -{ - bool operator()(const std::type_info* lhs, const std::type_info* rhs) const - { - return lhs->before(*rhs); - } -}; - // lookup widget constructor funcs by widget name template class LLChildRegistry : public LLRegistrySingleton @@ -71,14 +64,14 @@ protected: // lookup widget name by type class LLWidgetNameRegistry -: public LLRegistrySingleton +: public LLRegistrySingleton {}; // lookup function for generating empty param block by widget type // this is used for schema generation //typedef const LLInitParam::BaseBlock& (*empty_param_block_func_t)(); //class LLDefaultParamBlockRegistry -//: public LLRegistrySingleton +//: public LLRegistrySingleton //{}; extern LLFastTimer::DeclareTimer FTM_WIDGET_SETUP; @@ -169,32 +162,21 @@ public: LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node ); template - static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry, LLXMLNodePtr output_node = NULL) + static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry) { T* widget = NULL; - - std::string skinned_filename = findSkinnedFilename(filename); + instance().pushFileName(filename); { LLXMLNodePtr root_node; - //if exporting, only load the language being exported, - //instead of layering localized version on top of english - if (output_node) - { - if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root_node)) - { - llwarns << "Couldn't parse XUI file: " << filename << llendl; - goto fail; - } - } - else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) + if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) { - llwarns << "Couldn't parse XUI file: " << skinned_filename << llendl; + llwarns << "Couldn't parse XUI file: " << instance().getCurFileName() << llendl; goto fail; } - - LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, output_node); + + LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, NULL); if (view) { widget = dynamic_cast(view); @@ -222,8 +204,8 @@ fail: static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL); - static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root); - static bool getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root); + static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root, + LLDir::ESkinConstraint constraint=LLDir::CURRENT_SKIN); private: //NOTE: both friend declarations are necessary to keep both gcc and msvc happy @@ -308,9 +290,6 @@ private: // this exists to get around dependency on llview static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group); - // Avoid directly using LLUI and LLDir in the template code - static std::string findSkinnedFilename(const std::string& filename); - class LLPanel* mDummyPanel; std::vector mFileNames; }; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 54843227b7..ad9bec9f61 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1549,16 +1549,18 @@ void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* l void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const { - *screen_x = local_x + getRect().mLeft; - *screen_y = local_y + getRect().mBottom; + *screen_x = local_x; + *screen_y = local_y; const LLView* cur = this; - while( cur->mParentView ) + do { + LLRect cur_rect = cur->getRect(); + *screen_x += cur_rect.mLeft; + *screen_y += cur_rect.mBottom; cur = cur->mParentView; - *screen_x += cur->getRect().mLeft; - *screen_y += cur->getRect().mBottom; } + while( cur ); } void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const diff --git a/indra/llvfs/CMakeLists.txt b/indra/llvfs/CMakeLists.txt index a819d12861..3c68b279f7 100644 --- a/indra/llvfs/CMakeLists.txt +++ b/indra/llvfs/CMakeLists.txt @@ -36,6 +36,8 @@ set(llvfs_HEADER_FILES if (DARWIN) LIST(APPEND llvfs_SOURCE_FILES lldir_mac.cpp) LIST(APPEND llvfs_HEADER_FILES lldir_mac.h) + LIST(APPEND llvfs_SOURCE_FILES llvfs_objc.mm) + LIST(APPEND llvfs_HEADER_FILES llvfs_objc.h) endif (DARWIN) if (LINUX) @@ -73,8 +75,8 @@ target_link_libraries(llvfs if (DARWIN) include(CMakeFindFrameworks) - find_library(CARBON_LIBRARY Carbon) - target_link_libraries(llvfs ${CARBON_LIBRARY}) + find_library(COCOA_LIBRARY Cocoa) + target_link_libraries(llvfs ${COCOA_LIBRARY}) endif (DARWIN) diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 32d081d552..5e5aeefba1 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -41,6 +41,17 @@ #include "lluuid.h" #include "lldiriterator.h" +#include "stringize.h" +#include +#include +#include +#include +#include +#include +#include + +using boost::assign::list_of; +using boost::assign::map_list_of; #if LL_WINDOWS #include "lldir_win32.h" @@ -58,6 +69,14 @@ LLDir_Linux gDirUtil; LLDir *gDirUtilp = (LLDir *)&gDirUtil; +/// Values for findSkinnedFilenames(subdir) parameter +const char + *LLDir::XUI = "xui", + *LLDir::TEXTURES = "textures", + *LLDir::SKINBASE = ""; + +static const char* const empty = ""; + LLDir::LLDir() : mAppName(""), mExecutablePathAndName(""), @@ -70,7 +89,8 @@ LLDir::LLDir() mOSCacheDir(""), mCAFile(""), mTempDir(""), - mDirDelimiter("/") // fallback to forward slash if not overridden + mDirDelimiter("/"), // fallback to forward slash if not overridden + mLanguage("en") { } @@ -96,9 +116,7 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask) LLDirIterator iter(dirname, mask); while (iter.next(filename)) { - fullpath = dirname; - fullpath += getDirDelimiter(); - fullpath += filename; + fullpath = add(dirname, filename); if(LLFile::isdir(fullpath)) { @@ -270,12 +288,12 @@ std::string LLDir::buildSLOSCacheDir() const } else { - res = getOSUserAppDir() + mDirDelimiter + "cache"; + res = add(getOSUserAppDir(), "cache"); } } else { - res = getOSCacheDir() + mDirDelimiter + "SecondLife"; + res = add(getOSCacheDir(), "SecondLife"); } return res; } @@ -298,21 +316,26 @@ const std::string &LLDir::getDirDelimiter() const return mDirDelimiter; } +const std::string& LLDir::getDefaultSkinDir() const +{ + return mDefaultSkinDir; +} + const std::string &LLDir::getSkinDir() const { return mSkinDir; } +const std::string &LLDir::getUserDefaultSkinDir() const +{ + return mUserDefaultSkinDir; +} + const std::string &LLDir::getUserSkinDir() const { return mUserSkinDir; } -const std::string& LLDir::getDefaultSkinDir() const -{ - return mDefaultSkinDir; -} - const std::string LLDir::getSkinBaseDir() const { return mSkinBaseDir; @@ -323,6 +346,39 @@ const std::string &LLDir::getLLPluginDir() const return mLLPluginDir; } +static std::string ELLPathToString(ELLPath location) +{ + typedef std::map ELLPathMap; +#define ENT(symbol) (symbol, #symbol) + static const ELLPathMap sMap = map_list_of + ENT(LL_PATH_NONE) + ENT(LL_PATH_USER_SETTINGS) + ENT(LL_PATH_APP_SETTINGS) + ENT(LL_PATH_PER_SL_ACCOUNT) // returns/expands to blank string if we don't know the account name yet + ENT(LL_PATH_CACHE) + ENT(LL_PATH_CHARACTER) + ENT(LL_PATH_HELP) + ENT(LL_PATH_LOGS) + ENT(LL_PATH_TEMP) + ENT(LL_PATH_SKINS) + ENT(LL_PATH_TOP_SKIN) + ENT(LL_PATH_CHAT_LOGS) + ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS) + ENT(LL_PATH_USER_SKIN) + ENT(LL_PATH_LOCAL_ASSETS) + ENT(LL_PATH_EXECUTABLE) + ENT(LL_PATH_DEFAULT_SKIN) + ENT(LL_PATH_FONTS) + ENT(LL_PATH_LAST) + ; +#undef ENT + + ELLPathMap::const_iterator found = sMap.find(location); + if (found != sMap.end()) + return found->second; + return STRINGIZE("Invalid ELLPath value " << location); +} + std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const { return getExpandedFilename(location, "", filename); @@ -343,15 +399,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_APP_SETTINGS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "app_settings"; + prefix = add(getAppRODataDir(), "app_settings"); break; case LL_PATH_CHARACTER: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "character"; + prefix = add(getAppRODataDir(), "character"); break; case LL_PATH_HELP: @@ -363,16 +415,22 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_USER_SETTINGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "user_settings"; + prefix = add(getOSUserAppDir(), "user_settings"); break; case LL_PATH_PER_SL_ACCOUNT: prefix = getLindenUserDir(); if (prefix.empty()) { - // if we're asking for the per-SL-account directory but we haven't logged in yet (or otherwise don't know the account name from which to build this string), then intentionally return a blank string to the caller and skip the below warning about a blank prefix. + // if we're asking for the per-SL-account directory but we haven't + // logged in yet (or otherwise don't know the account name from + // which to build this string), then intentionally return a blank + // string to the caller and skip the below warning about a blank + // prefix. + LL_DEBUGS("LLDir") << "getLindenUserDir() not yet set: " + << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "' => ''" << LL_ENDL; return std::string(); } break; @@ -386,9 +444,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "logs"; + prefix = add(getOSUserAppDir(), "logs"); break; case LL_PATH_TEMP: @@ -412,9 +468,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOCAL_ASSETS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "local_assets"; + prefix = add(getAppRODataDir(), "local_assets"); break; case LL_PATH_EXECUTABLE: @@ -422,56 +476,36 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_FONTS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "fonts"; + prefix = add(getAppRODataDir(), "fonts"); break; default: llassert(0); } - std::string filename = in_filename; - if (!subdir2.empty()) - { - filename = subdir2 + mDirDelimiter + filename; - } - - if (!subdir1.empty()) - { - filename = subdir1 + mDirDelimiter + filename; - } - if (prefix.empty()) { - llwarns << "prefix is empty, possible bad filename" << llendl; - } - - std::string expanded_filename; - if (!filename.empty()) - { - if (!prefix.empty()) - { - expanded_filename += prefix; - expanded_filename += mDirDelimiter; - expanded_filename += filename; - } - else - { - expanded_filename = filename; - } - } - else if (!prefix.empty()) - { - // Directory only, no file name. - expanded_filename = prefix; - } - else - { - expanded_filename.assign(""); + llwarns << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "': prefix is empty, possible bad filename" << llendl; } - //llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; + std::string expanded_filename = add(add(prefix, subdir1), subdir2); + if (expanded_filename.empty() && in_filename.empty()) + { + return ""; + } + // Use explicit concatenation here instead of another add() call. Callers + // passing in_filename as "" expect to obtain a pathname ending with + // mDirSeparator so they can later directly concatenate with a specific + // filename. A caller using add() doesn't care, but there's still code + // loose in the system that uses std::string::operator+(). + expanded_filename += mDirDelimiter; + expanded_filename += in_filename; + + LL_DEBUGS("LLDir") << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "' => '" << expanded_filename << "'" << LL_ENDL; return expanded_filename; } @@ -511,31 +545,207 @@ std::string LLDir::getExtension(const std::string& filepath) const return exten; } -std::string LLDir::findSkinnedFilename(const std::string &filename) const +std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, + const std::string &filename, + ESkinConstraint constraint) const { - return findSkinnedFilename("", "", filename); + // This implementation is basically just as described in the declaration comments. + std::vector found(findSkinnedFilenames(subdir, filename, constraint)); + if (found.empty()) + { + return ""; + } + return found.front(); } -std::string LLDir::findSkinnedFilename(const std::string &subdir, const std::string &filename) const +std::string LLDir::findSkinnedFilename(const std::string &subdir, + const std::string &filename, + ESkinConstraint constraint) const { - return findSkinnedFilename("", subdir, filename); + // This implementation is basically just as described in the declaration comments. + std::vector found(findSkinnedFilenames(subdir, filename, constraint)); + if (found.empty()) + { + return ""; + } + return found.back(); } -std::string LLDir::findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const +// This method exists because the two code paths for +// findSkinnedFilenames(ALL_SKINS) and findSkinnedFilenames(CURRENT_SKIN) must +// generate the list of candidate pathnames in identical ways. The only +// difference is in the body of the inner loop. +template +void LLDir::walkSearchSkinDirs(const std::string& subdir, + const std::vector& subsubdirs, + const std::string& filename, + const FUNCTION& function) const { - // generate subdirectory path fragment, e.g. "/foo/bar", "/foo", "" - std::string subdirs = ((subdir1.empty() ? "" : mDirDelimiter) + subdir1) - + ((subdir2.empty() ? "" : mDirDelimiter) + subdir2); + BOOST_FOREACH(std::string skindir, mSearchSkinDirs) + { + std::string subdir_path(add(skindir, subdir)); + BOOST_FOREACH(std::string subsubdir, subsubdirs) + { + std::string full_path(add(add(subdir_path, subsubdir), filename)); + if (fileExists(full_path)) + { + function(subsubdir, full_path); + } + } + } +} - std::vector search_paths; - - search_paths.push_back(getUserSkinDir() + subdirs); // first look in user skin override - search_paths.push_back(getSkinDir() + subdirs); // then in current skin - search_paths.push_back(getDefaultSkinDir() + subdirs); // then default skin - search_paths.push_back(getCacheDir() + subdirs); // and last in preload directory +// ridiculous little helper function that should go away when we can use lambda +inline void push_back(std::vector& vector, const std::string& value) +{ + vector.push_back(value); +} - std::string found_file = findFile(filename, search_paths); - return found_file; +typedef std::map StringMap; +// ridiculous little helper function that should go away when we can use lambda +inline void store_in_map(StringMap& map, const std::string& key, const std::string& value) +{ + map[key] = value; +} + +std::vector LLDir::findSkinnedFilenames(const std::string& subdir, + const std::string& filename, + ESkinConstraint constraint) const +{ + // Recognize subdirs that have no localization. + static const std::set sUnlocalized = list_of + ("") // top-level directory not localized + ("textures") // textures not localized + ; + + LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename + << "', constraint " + << ((constraint == CURRENT_SKIN)? "CURRENT_SKIN" : "ALL_SKINS") + << LL_ENDL; + + // Cache the default language directory for each subdir we've encountered. + // A cache entry whose value is the empty string means "not localized, + // don't bother checking again." + static StringMap sLocalized; + + // Check whether we've already discovered if this subdir is localized. + StringMap::const_iterator found = sLocalized.find(subdir); + if (found == sLocalized.end()) + { + // We have not yet determined that. Is it one of the subdirs "known" + // to be unlocalized? + if (sUnlocalized.find(subdir) != sUnlocalized.end()) + { + // This subdir is known to be unlocalized. Remember that. + found = sLocalized.insert(StringMap::value_type(subdir, "")).first; + } + else + { + // We do not recognize this subdir. Investigate. + std::string subdir_path(add(getDefaultSkinDir(), subdir)); + if (fileExists(add(subdir_path, "en"))) + { + // defaultSkinDir/subdir contains subdir "en". That's our + // default language; this subdir is localized. + found = sLocalized.insert(StringMap::value_type(subdir, "en")).first; + } + else if (fileExists(add(subdir_path, "en-us"))) + { + // defaultSkinDir/subdir contains subdir "en-us" but not "en". + // Set as default language; this subdir is localized. + found = sLocalized.insert(StringMap::value_type(subdir, "en-us")).first; + } + else + { + // defaultSkinDir/subdir contains neither "en" nor "en-us". + // Assume it's not localized. Remember that assumption. + found = sLocalized.insert(StringMap::value_type(subdir, "")).first; + } + } + } + // Every code path above should have resulted in 'found' becoming a valid + // iterator to an entry in sLocalized. + llassert(found != sLocalized.end()); + + // Now -- is this subdir localized, or not? The answer determines what + // subdirectories we check (under subdir) for the requested filename. + std::vector subsubdirs; + if (found->second.empty()) + { + // subdir is not localized. filename should be located directly within it. + subsubdirs.push_back(""); + } + else + { + // subdir is localized, and found->second is the default language + // directory within it. Check both the default language and the + // current language -- if it differs from the default, of course. + subsubdirs.push_back(found->second); + if (mLanguage != found->second) + { + subsubdirs.push_back(mLanguage); + } + } + + // Build results vector. + std::vector results; + // The process we use depends on 'constraint'. + if (constraint != CURRENT_SKIN) // meaning ALL_SKINS + { + // ALL_SKINS is simpler: just return every pathname generated by + // walkSearchSkinDirs(). Tricky bit: walkSearchSkinDirs() passes its + // FUNCTION the subsubdir as well as the full pathname. We just want + // the full pathname. + walkSearchSkinDirs(subdir, subsubdirs, filename, + boost::bind(push_back, boost::ref(results), _2)); + } + else // CURRENT_SKIN + { + // CURRENT_SKIN turns out to be a bit of a misnomer because we might + // still return files from two different skins. In any case, this + // value of 'constraint' means we will return at most two paths: one + // for the default language, one for the current language (supposing + // those differ). + // It is important to allow a user to override only the localization + // for a particular file, for all viewer installs, without also + // overriding the default-language file. + // It is important to allow a user to override only the default- + // language file, for all viewer installs, without also overriding the + // applicable localization of that file. + // Therefore, treat the default language and the current language as + // two separate cases. For each, capture the most-specialized file + // that exists. + // Use a map keyed by subsubdir (i.e. language code). This allows us + // to handle the case of a single subsubdirs entry with the same logic + // that handles two. For every real file path generated by + // walkSearchSkinDirs(), update the map entry for its subsubdir. + StringMap path_for; + walkSearchSkinDirs(subdir, subsubdirs, filename, + boost::bind(store_in_map, boost::ref(path_for), _1, _2)); + // Now that we have a path for each of the default language and the + // current language, copy them -- in proper order -- into results. + // Don't drive this by walking the map itself: it matters that we + // generate results in the same order as subsubdirs. + BOOST_FOREACH(std::string subsubdir, subsubdirs) + { + StringMap::const_iterator found(path_for.find(subsubdir)); + if (found != path_for.end()) + { + results.push_back(found->second); + } + } + } + + LL_DEBUGS("LLDir") << empty; + const char* comma = ""; + BOOST_FOREACH(std::string path, results) + { + LL_CONT << comma << "'" << path << "'"; + comma = ", "; + } + LL_CONT << LL_ENDL; + + return results; } std::string LLDir::getTempFilename() const @@ -546,12 +756,7 @@ std::string LLDir::getTempFilename() const random_uuid.generate(); random_uuid.toString(uuid_str); - std::string temp_filename = getTempDir(); - temp_filename += mDirDelimiter; - temp_filename += uuid_str; - temp_filename += ".tmp"; - - return temp_filename; + return add(getTempDir(), uuid_str + ".tmp"); } // static @@ -587,9 +792,7 @@ void LLDir::setLindenUserDir(const std::string &username) std::string userlower(username); LLStringUtil::toLower(userlower); LLStringUtil::replaceChar(userlower, ' ', '_'); - mLindenUserDir = getOSUserAppDir(); - mLindenUserDir += mDirDelimiter; - mLindenUserDir += userlower; + mLindenUserDir = add(getOSUserAppDir(), userlower); } else { @@ -621,9 +824,7 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) std::string userlower(username); LLStringUtil::toLower(userlower); LLStringUtil::replaceChar(userlower, ' ', '_'); - mPerAccountChatLogsDir = getChatLogsDir(); - mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += userlower; + mPerAccountChatLogsDir = add(getChatLogsDir(), userlower); } else { @@ -632,25 +833,59 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) } -void LLDir::setSkinFolder(const std::string &skin_folder) +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) { - mSkinDir = getSkinBaseDir(); - mSkinDir += mDirDelimiter; - mSkinDir += skin_folder; + LL_DEBUGS("LLDir") << "Setting skin '" << skin_folder << "', language '" << language << "'" + << LL_ENDL; + mSkinName = skin_folder; + mLanguage = language; - // user modifications to current skin - // e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle - mUserSkinDir = getOSUserAppDir(); - mUserSkinDir += mDirDelimiter; - mUserSkinDir += "skins"; - mUserSkinDir += mDirDelimiter; - mUserSkinDir += skin_folder; + // This method is called multiple times during viewer initialization. Each + // time it's called, reset mSearchSkinDirs. + mSearchSkinDirs.clear(); // base skin which is used as fallback for all skinned files // e.g. c:\program files\secondlife\skins\default mDefaultSkinDir = getSkinBaseDir(); - mDefaultSkinDir += mDirDelimiter; - mDefaultSkinDir += "default"; + append(mDefaultSkinDir, "default"); + // This is always the most general of the search skin directories. + addSearchSkinDir(mDefaultSkinDir); + + mSkinDir = getSkinBaseDir(); + append(mSkinDir, skin_folder); + // Next level of generality is a skin installed with the viewer. + addSearchSkinDir(mSkinDir); + + // user modifications to skins, current and default + // e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle + mUserSkinDir = getOSUserAppDir(); + append(mUserSkinDir, "skins"); + mUserDefaultSkinDir = mUserSkinDir; + append(mUserDefaultSkinDir, "default"); + append(mUserSkinDir, skin_folder); + // Next level of generality is user modifications to default skin... + addSearchSkinDir(mUserDefaultSkinDir); + // then user-defined skins. + addSearchSkinDir(mUserSkinDir); +} + +void LLDir::addSearchSkinDir(const std::string& skindir) +{ + if (std::find(mSearchSkinDirs.begin(), mSearchSkinDirs.end(), skindir) == mSearchSkinDirs.end()) + { + LL_DEBUGS("LLDir") << "search skin: '" << skindir << "'" << LL_ENDL; + mSearchSkinDirs.push_back(skindir); + } +} + +std::string LLDir::getSkinFolder() const +{ + return mSkinName; +} + +std::string LLDir::getLanguage() const +{ + return mLanguage; } bool LLDir::setCacheDir(const std::string &path) @@ -664,7 +899,7 @@ bool LLDir::setCacheDir(const std::string &path) else { LLFile::mkdir(path); - std::string tempname = path + mDirDelimiter + "temp"; + std::string tempname = add(path, "temp"); LLFILE* file = LLFile::fopen(tempname,"wt"); if (file) { @@ -697,6 +932,57 @@ void LLDir::dumpCurrentDirectories() LL_DEBUGS2("AppInit","Directories") << " SkinDir: " << getSkinDir() << LL_ENDL; } +std::string LLDir::add(const std::string& path, const std::string& name) const +{ + std::string destpath(path); + append(destpath, name); + return destpath; +} + +void LLDir::append(std::string& destpath, const std::string& name) const +{ + // Delegate question of whether we need a separator to helper method. + SepOff sepoff(needSep(destpath, name)); + if (sepoff.first) // do we need a separator? + { + destpath += mDirDelimiter; + } + // If destpath ends with a separator, AND name starts with one, skip + // name's leading separator. + destpath += name.substr(sepoff.second); +} + +LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) const +{ + if (path.empty() || name.empty()) + { + // If either path or name are empty, we do not need a separator + // between them. + return SepOff(false, 0); + } + // Here we know path and name are both non-empty. But if path already ends + // with a separator, or if name already starts with a separator, we need + // not add one. + std::string::size_type seplen(mDirDelimiter.length()); + bool path_ends_sep(path.substr(path.length() - seplen) == mDirDelimiter); + bool name_starts_sep(name.substr(0, seplen) == mDirDelimiter); + if ((! path_ends_sep) && (! name_starts_sep)) + { + // If neither path nor name brings a separator to the junction, then + // we need one. + return SepOff(true, 0); + } + if (path_ends_sep && name_starts_sep) + { + // But if BOTH path and name bring a separator, we need not add one. + // Moreover, we should actually skip the leading separator of 'name'. + return SepOff(false, seplen); + } + // Here we know that either path_ends_sep or name_starts_sep is true -- + // but not both. So don't add a separator, and don't skip any characters: + // simple concatenation will do the trick. + return SepOff(false, 0); +} void dir_exists_or_crash(const std::string &dir_name) { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 5ee8bdb542..300ff1eef6 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -1,4 +1,4 @@ -/** +/** * @file lldir.h * @brief Definition of directory utilities class * @@ -56,7 +56,7 @@ typedef enum ELLPath LL_PATH_LAST } ELLPath; - +/// Directory operations class LLDir { public: @@ -73,10 +73,8 @@ class LLDir virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask); // pure virtual functions - virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 0; - virtual std::string getCurPath() = 0; - virtual BOOL fileExists(const std::string &filename) const = 0; + virtual bool fileExists(const std::string &filename) const = 0; const std::string findFile(const std::string& filename, const std::vector filenames) const; const std::string findFile(const std::string& filename, const std::string& searchPath1 = "", const std::string& searchPath2 = "", const std::string& searchPath3 = "") const; @@ -100,9 +98,10 @@ class LLDir const std::string &getOSCacheDir() const; // location of OS-specific cache folder (may be empty string) const std::string &getCAFile() const; // File containing TLS certificate authorities const std::string &getDirDelimiter() const; // directory separator for platform (ie. '\' or '/' or ':') - const std::string &getSkinDir() const; // User-specified skin folder. - const std::string &getUserSkinDir() const; // User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin const std::string &getDefaultSkinDir() const; // folder for default skin. e.g. c:\program files\second life\skins\default + const std::string &getSkinDir() const; // User-specified skin folder. + const std::string &getUserDefaultSkinDir() const; // dir with user modifications to default skin + const std::string &getUserSkinDir() const; // User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin const std::string getSkinBaseDir() const; // folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins const std::string &getLLPluginDir() const; // Directory containing plugins and plugin shell @@ -117,10 +116,61 @@ class LLDir std::string getExtension(const std::string& filepath) const; // Excludes '.', e.g getExtension("foo.wav") == "wav" // these methods search the various skin paths for the specified file in the following order: - // getUserSkinDir(), getSkinDir(), getDefaultSkinDir() - std::string findSkinnedFilename(const std::string &filename) const; - std::string findSkinnedFilename(const std::string &subdir, const std::string &filename) const; - std::string findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const; + // getUserSkinDir(), getUserDefaultSkinDir(), getSkinDir(), getDefaultSkinDir() + /// param value for findSkinnedFilenames(), explained below + enum ESkinConstraint { CURRENT_SKIN, ALL_SKINS }; + /** + * Given a filename within skin, return an ordered sequence of paths to + * search. Nonexistent files will be filtered out -- which means that the + * vector might be empty. + * + * @param subdir Identify top-level skin subdirectory by passing one of + * LLDir::XUI (file lives under "xui" subtree), LLDir::TEXTURES (file + * lives under "textures" subtree), LLDir::SKINBASE (file lives at top + * level of skin subdirectory). + * @param filename Desired filename within subdir within skin, e.g. + * "panel_login.xml". DO NOT prepend (e.g.) "xui" or the desired language. + * @param constraint Callers perform two different kinds of processing. + * When fetching a XUI file, for instance, the existence of @a filename in + * the specified skin completely supercedes any @a filename in the default + * skin. For that case, leave the default @a constraint=CURRENT_SKIN. The + * returned vector will contain only + * ".../current_skin/xui/en/filename", + * ".../current_skin/xui/current_language/filename". + * But for (e.g.) "strings.xml", we want a given skin to be able to + * override only specific entries from the default skin. Any string not + * defined in the specified skin will be sought in the default skin. For + * that case, pass @a constraint=ALL_SKINS. The returned vector will + * contain at least ".../default/xui/en/strings.xml", + * ".../default/xui/current_language/strings.xml", + * ".../current_skin/xui/en/strings.xml", + * ".../current_skin/xui/current_language/strings.xml". + */ + std::vector findSkinnedFilenames(const std::string& subdir, + const std::string& filename, + ESkinConstraint constraint=CURRENT_SKIN) const; + /// Values for findSkinnedFilenames(subdir) parameter + static const char *XUI, *TEXTURES, *SKINBASE; + /** + * Return the base-language pathname from findSkinnedFilenames(), or + * the empty string if no such file exists. Parameters are identical to + * findSkinnedFilenames(). This is shorthand for capturing the vector + * returned by findSkinnedFilenames(), checking for empty() and then + * returning front(). + */ + std::string findSkinnedFilenameBaseLang(const std::string &subdir, + const std::string &filename, + ESkinConstraint constraint=CURRENT_SKIN) const; + /** + * Return the "most localized" pathname from findSkinnedFilenames(), or + * the empty string if no such file exists. Parameters are identical to + * findSkinnedFilenames(). This is shorthand for capturing the vector + * returned by findSkinnedFilenames(), checking for empty() and then + * returning back(). + */ + std::string findSkinnedFilename(const std::string &subdir, + const std::string &filename, + ESkinConstraint constraint=CURRENT_SKIN) const; // random filename in common temporary directory std::string getTempFilename() const; @@ -132,15 +182,37 @@ class LLDir virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir virtual void setPerAccountChatLogsDir(const std::string &username); // Set the per user chat log directory. virtual void setLindenUserDir(const std::string &username); // Set the linden user dir to this user's dir - virtual void setSkinFolder(const std::string &skin_folder); + virtual void setSkinFolder(const std::string &skin_folder, const std::string& language); + virtual std::string getSkinFolder() const; + virtual std::string getLanguage() const; virtual bool setCacheDir(const std::string &path); virtual void dumpCurrentDirectories(); - + // Utility routine std::string buildSLOSCacheDir() const; + /// Append specified @a name to @a destpath, separated by getDirDelimiter() + /// if both are non-empty. + void append(std::string& destpath, const std::string& name) const; + /// Append specified @a name to @a path, separated by getDirDelimiter() + /// if both are non-empty. Return result, leaving @a path unmodified. + std::string add(const std::string& path, const std::string& name) const; + protected: + // Does an add() or append() call need a directory delimiter? + typedef std::pair SepOff; + SepOff needSep(const std::string& path, const std::string& name) const; + // build mSearchSkinDirs without adding duplicates + void addSearchSkinDir(const std::string& skindir); + + // Internal to findSkinnedFilenames() + template + void walkSearchSkinDirs(const std::string& subdir, + const std::vector& subsubdirs, + const std::string& filename, + const FUNCTION& function) const; + std::string mAppName; // install directory under progams/ ie "SecondLife" std::string mExecutablePathAndName; // full path + Filename of .exe std::string mExecutableFilename; // Filename of .exe @@ -158,10 +230,18 @@ protected: std::string mDefaultCacheDir; // default cache diretory std::string mOSCacheDir; // operating system cache dir std::string mDirDelimiter; + std::string mSkinName; // caller-specified skin name std::string mSkinBaseDir; // Base for skins paths. - std::string mSkinDir; // Location for current skin info. std::string mDefaultSkinDir; // Location for default skin info. + std::string mSkinDir; // Location for current skin info. + std::string mUserDefaultSkinDir; // Location for default skin info. std::string mUserSkinDir; // Location for user-modified skin info. + // Skin directories to search, most general to most specific. This order + // works well for composing fine-grained files, in which an individual item + // in a specific file overrides the corresponding item in more general + // files. Of course, for a file-level search, iterate backwards. + std::vector mSearchSkinDirs; + std::string mLanguage; // Current viewer language std::string mLLPluginDir; // Location for plugins and plugin shell }; diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index 407f3b93fb..4edd078640 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -254,7 +254,7 @@ std::string LLDir_Linux::getCurPath() } -BOOL LLDir_Linux::fileExists(const std::string &filename) const +bool LLDir_Linux::fileExists(const std::string &filename) const { struct stat stat_data; // Check the age of the file diff --git a/indra/llvfs/lldir_linux.h b/indra/llvfs/lldir_linux.h index 7603239867..e83a020ba4 100644 --- a/indra/llvfs/lldir_linux.h +++ b/indra/llvfs/lldir_linux.h @@ -47,7 +47,7 @@ public: virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ bool fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); /*virtual*/ std::string getLLPluginFilename(std::string base_name); diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp index 489bc3e4a7..c5041d434c 100644 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llvfs/lldir_mac.cpp @@ -22,7 +22,7 @@ * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - */ + */ #if LL_DARWIN @@ -35,73 +35,27 @@ #include #include #include - -#include +#include +#include "llvfs_objc.h" // -------------------------------------------------------------------------------- -static OSStatus CFCreateDirectory(FSRef *parentRef, CFStringRef name, FSRef *newRef) +static bool CreateDirectory(const std::string &parent, + const std::string &child, + std::string *fullname) { - OSStatus result = noErr; - HFSUniStr255 uniStr; - - uniStr.length = CFStringGetLength(name); - CFStringGetCharacters(name, CFRangeMake(0, uniStr.length), uniStr.unicode); - result = FSMakeFSRefUnicode(parentRef, uniStr.length, uniStr.unicode, kTextEncodingMacRoman, newRef); - if (result != noErr) - { - result = FSCreateDirectoryUnicode(parentRef, uniStr.length, uniStr.unicode, 0, NULL, newRef, NULL, NULL); - } - - return result; -} - -// -------------------------------------------------------------------------------- - -static void CFStringRefToLLString(CFStringRef stringRef, std::string &llString, bool releaseWhenDone) -{ - if (stringRef) - { - long stringSize = CFStringGetLength(stringRef) + 1; - long bufferSize = CFStringGetMaximumSizeForEncoding(stringSize,kCFStringEncodingUTF8); - char* buffer = new char[bufferSize]; - memset(buffer, 0, bufferSize); - if (CFStringGetCString(stringRef, buffer, bufferSize, kCFStringEncodingUTF8)) - llString = buffer; - delete[] buffer; - if (releaseWhenDone) - CFRelease(stringRef); - } -} - -// -------------------------------------------------------------------------------- - -static void CFURLRefToLLString(CFURLRef urlRef, std::string &llString, bool releaseWhenDone) -{ - if (urlRef) - { - CFURLRef absoluteURLRef = CFURLCopyAbsoluteURL(urlRef); - if (absoluteURLRef) - { - CFStringRef stringRef = CFURLCopyFileSystemPath(absoluteURLRef, kCFURLPOSIXPathStyle); - CFStringRefToLLString(stringRef, llString, true); - CFRelease(absoluteURLRef); - } - if (releaseWhenDone) - CFRelease(urlRef); - } -} - -// -------------------------------------------------------------------------------- - -static void FSRefToLLString(FSRef *fsRef, std::string &llString) -{ - OSStatus error = noErr; - char path[MAX_PATH]; - - error = FSRefMakePath(fsRef, (UInt8*) path, sizeof(path)); - if (error == noErr) - llString = path; + + boost::filesystem::path p(parent); + p /= child; + + if (fullname) + *fullname = std::string(p.string()); + + if (! boost::filesystem::create_directory(p)) + { + return (boost::filesystem::is_directory(p)); + } + return true; } // -------------------------------------------------------------------------------- @@ -109,35 +63,28 @@ static void FSRefToLLString(FSRef *fsRef, std::string &llString) LLDir_Mac::LLDir_Mac() { mDirDelimiter = "/"; - mCurrentDirIndex = -1; - mCurrentDirCount = -1; - - CFBundleRef mainBundleRef = NULL; - CFURLRef executableURLRef = NULL; - CFStringRef stringRef = NULL; - OSStatus error = noErr; - FSRef fileRef; - CFStringRef secondLifeString = CFSTR("SecondLife"); - - mainBundleRef = CFBundleGetMainBundle(); - - executableURLRef = CFBundleCopyExecutableURL(mainBundleRef); - - if (executableURLRef != NULL) + + const std::string secondLifeString = "SecondLife"; + + std::string *executablepathstr = getSystemExecutableFolder(); + + //NOTE: LLINFOS/LLERRS will not output to log here. The streams are not initialized. + + if (executablepathstr) { // mExecutablePathAndName - CFURLRefToLLString(executableURLRef, mExecutablePathAndName, false); - - // mExecutableFilename - stringRef = CFURLCopyLastPathComponent(executableURLRef); - CFStringRefToLLString(stringRef, mExecutableFilename, true); - - // mExecutableDir - CFURLRef executableParentURLRef = CFURLCreateCopyDeletingLastPathComponent(NULL, executableURLRef); - CFURLRefToLLString(executableParentURLRef, mExecutableDir, true); + mExecutablePathAndName = *executablepathstr; + + boost::filesystem::path executablepath(*executablepathstr); + +# ifndef BOOST_SYSTEM_NO_DEPRECATED +#endif + mExecutableFilename = executablepath.filename().string(); + mExecutableDir = executablepath.parent_path().string(); // mAppRODataDir - + std::string *resourcepath = getSystemResourceFolder(); + mAppRODataDir = *resourcepath; // *NOTE: When running in a dev tree, use the copy of // skins in indra/newview/ rather than in the application bundle. This @@ -146,10 +93,7 @@ LLDir_Mac::LLDir_Mac() // MBW -- This keeps the mac application from finding other things. // If this is really for skins, it should JUST apply to skins. - - CFURLRef resourcesURLRef = CFBundleCopyResourcesDirectoryURL(mainBundleRef); - CFURLRefToLLString(resourcesURLRef, mAppRODataDir, true); - + U32 build_dir_pos = mExecutableDir.rfind("/build-darwin-"); if (build_dir_pos != std::string::npos) { @@ -166,55 +110,50 @@ LLDir_Mac::LLDir_Mac() } // mOSUserDir - error = FSFindFolder(kUserDomain, kApplicationSupportFolderType, true, &fileRef); - if (error == noErr) - { - FSRef newFileRef; - - // Create the directory - error = CFCreateDirectory(&fileRef, secondLifeString, &newFileRef); - if (error == noErr) - { - // Save the full path to the folder - FSRefToLLString(&newFileRef, mOSUserDir); - - // Create our sub-dirs - (void) CFCreateDirectory(&newFileRef, CFSTR("data"), NULL); - //(void) CFCreateDirectory(&newFileRef, CFSTR("cache"), NULL); - (void) CFCreateDirectory(&newFileRef, CFSTR("logs"), NULL); - (void) CFCreateDirectory(&newFileRef, CFSTR("user_settings"), NULL); - (void) CFCreateDirectory(&newFileRef, CFSTR("browser_profile"), NULL); - } - } - + std::string *appdir = getSystemApplicationSupportFolder(); + std::string rootdir; + + //Create root directory + if (CreateDirectory(*appdir, secondLifeString, &rootdir)) + { + + // Save the full path to the folder + mOSUserDir = rootdir; + + // Create our sub-dirs + CreateDirectory(rootdir, std::string("data"), NULL); + CreateDirectory(rootdir, std::string("logs"), NULL); + CreateDirectory(rootdir, std::string("user_settings"), NULL); + CreateDirectory(rootdir, std::string("browser_profile"), NULL); + } + //mOSCacheDir - FSRef cacheDirRef; - error = FSFindFolder(kUserDomain, kCachedDataFolderType, true, &cacheDirRef); - if (error == noErr) + std::string *cachedir = getSystemCacheFolder(); + + if (cachedir) + { - FSRefToLLString(&cacheDirRef, mOSCacheDir); - (void)CFCreateDirectory(&cacheDirRef, CFSTR("SecondLife"),NULL); + mOSCacheDir = *cachedir; + //SPATTERS TODO: This changes from ~/Library/Cache/Secondlife to ~/Library/Cache/com.app.secondlife/Secondlife. Last dir level could go away. + CreateDirectory(mOSCacheDir, secondLifeString, NULL); } // mOSUserAppDir mOSUserAppDir = mOSUserDir; // mTempDir - error = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, true, &fileRef); - if (error == noErr) - { - FSRef tempRef; - error = CFCreateDirectory(&fileRef, secondLifeString, &tempRef); - if (error == noErr) - FSRefToLLString(&tempRef, mTempDir); - } + //Aura 120920 boost::filesystem::temp_directory_path() not yet implemented on mac. :( + std::string *tmpdir = getSystemTempFolder(); + if (tmpdir) + { + + CreateDirectory(*tmpdir, secondLifeString, &mTempDir); + if (tmpdir) delete tmpdir; + } mWorkingDir = getCurPath(); mLLPluginDir = mAppRODataDir + mDirDelimiter + "llplugin"; - - CFRelease(executableURLRef); - executableURLRef = NULL; } } @@ -235,52 +174,18 @@ void LLDir_Mac::initAppDirs(const std::string &app_name, mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; } mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); - - //dumpCurrentDirectories(); -} - -U32 LLDir_Mac::countFilesInDir(const std::string &dirname, const std::string &mask) -{ - U32 file_count = 0; - glob_t g; - - std::string tmp_str; - tmp_str = dirname; - tmp_str += mask; - - if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0) - { - file_count = g.gl_pathc; - - globfree(&g); - } - - return (file_count); } std::string LLDir_Mac::getCurPath() { - char tmp_str[LL_MAX_PATH]; /* Flawfinder: ignore */ - getcwd(tmp_str, LL_MAX_PATH); - return tmp_str; + return boost::filesystem::path( boost::filesystem::current_path() ).string(); } -BOOL LLDir_Mac::fileExists(const std::string &filename) const +bool LLDir_Mac::fileExists(const std::string &filename) const { - struct stat stat_data; - // Check the age of the file - // Now, we see if the files we've gathered are recent... - int res = stat(filename.c_str(), &stat_data); - if (!res) - { - return TRUE; - } - else - { - return FALSE; - } + return boost::filesystem::exists(filename); } diff --git a/indra/llvfs/lldir_mac.h b/indra/llvfs/lldir_mac.h index d190d70be4..558727ebbc 100644 --- a/indra/llvfs/lldir_mac.h +++ b/indra/llvfs/lldir_mac.h @@ -22,7 +22,7 @@ * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - */ + */ #if !LL_DARWIN #error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead. @@ -45,16 +45,10 @@ public: const std::string& app_read_only_data_dir); virtual std::string getCurPath(); - virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL fileExists(const std::string &filename) const; + virtual bool fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); /*virtual*/ std::string getLLPluginFilename(std::string base_name); - -private: - int mCurrentDirIndex; - int mCurrentDirCount; - std::string mCurrentDir; }; #endif // LL_LLDIR_MAC_H diff --git a/indra/llvfs/lldir_solaris.cpp b/indra/llvfs/lldir_solaris.cpp index 21f8c3acdb..a97d72d539 100644 --- a/indra/llvfs/lldir_solaris.cpp +++ b/indra/llvfs/lldir_solaris.cpp @@ -272,7 +272,7 @@ std::string LLDir_Solaris::getCurPath() } -BOOL LLDir_Solaris::fileExists(const std::string &filename) const +bool LLDir_Solaris::fileExists(const std::string &filename) const { struct stat stat_data; // Check the age of the file diff --git a/indra/llvfs/lldir_solaris.h b/indra/llvfs/lldir_solaris.h index 0b58a45b15..c6dac57e14 100644 --- a/indra/llvfs/lldir_solaris.h +++ b/indra/llvfs/lldir_solaris.h @@ -47,7 +47,7 @@ public: virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ bool fileExists(const std::string &filename) const; private: DIR *mDirp; diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index 7709945123..462d1cce06 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -249,7 +249,7 @@ std::string LLDir_Win32::getCurPath() } -BOOL LLDir_Win32::fileExists(const std::string &filename) const +bool LLDir_Win32::fileExists(const std::string &filename) const { llstat stat_data; // Check the age of the file diff --git a/indra/llvfs/lldir_win32.h b/indra/llvfs/lldir_win32.h index 62fb4713ab..450efaf9da 100644 --- a/indra/llvfs/lldir_win32.h +++ b/indra/llvfs/lldir_win32.h @@ -44,7 +44,7 @@ public: /*virtual*/ std::string getCurPath(); /*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ bool fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); /*virtual*/ std::string getLLPluginFilename(std::string base_name); diff --git a/indra/llvfs/lldiriterator.cpp b/indra/llvfs/lldiriterator.cpp index ff92cbb7fd..460d2a8b4f 100644 --- a/indra/llvfs/lldiriterator.cpp +++ b/indra/llvfs/lldiriterator.cpp @@ -26,6 +26,7 @@ #include "lldiriterator.h" +#include "fix_macros.h" #include #include @@ -59,7 +60,7 @@ LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask) { is_dir = fs::is_directory(dir_path); } - catch (fs::basic_filesystem_error& e) + catch (const fs::filesystem_error& e) { llwarns << e.what() << llendl; return; @@ -76,7 +77,7 @@ LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask) { mIter = fs::directory_iterator(dir_path); } - catch (fs::basic_filesystem_error& e) + catch (const fs::filesystem_error& e) { llwarns << e.what() << llendl; return; @@ -121,7 +122,7 @@ bool LLDirIterator::Impl::next(std::string &fname) while (mIter != end_itr && !found) { boost::smatch match; - std::string name = mIter->path().filename(); + std::string name = mIter->path().filename().string(); if (found = boost::regex_match(name, match, mFilterExp)) { fname = name; diff --git a/indra/llvfs/llvfs_objc.h b/indra/llvfs/llvfs_objc.h new file mode 100644 index 0000000000..90101eb2e9 --- /dev/null +++ b/indra/llvfs/llvfs_objc.h @@ -0,0 +1,43 @@ +/** + * @file llvfs_objc.h + * @brief Definition of directory utilities class for Mac OS X + * + * $LicenseInfo:firstyear=2000&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_DARWIN +#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead. +#endif // !LL_DARWIN + +#ifndef LL_LLVFS_OBJC_H +#define LL_LLVFS_OBJC_H + +#include + +std::string* getSystemTempFolder(); +std::string* getSystemCacheFolder(); +std::string* getSystemApplicationSupportFolder(); +std::string* getSystemResourceFolder(); +std::string* getSystemExecutableFolder(); + + +#endif LL_LLVFS_OBJC_H diff --git a/indra/llvfs/llvfs_objc.mm b/indra/llvfs/llvfs_objc.mm new file mode 100644 index 0000000000..4f9e2f81e9 --- /dev/null +++ b/indra/llvfs/llvfs_objc.mm @@ -0,0 +1,108 @@ +/** + * @file llvfs_objc.cpp + * @brief Cocoa implementation of directory utilities for Mac OS X + * + * $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$ + */ +#if LL_DARWIN + +//WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL + +#include "llvfs_objc.h" +#import + +std::string* getSystemTempFolder() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString * tempDir = NSTemporaryDirectory(); + if (tempDir == nil) + tempDir = @"/tmp"; + std::string *result = ( new std::string([tempDir UTF8String]) ); + [pool release]; + + return result; +} + +//findSystemDirectory scoped exclusively to this file. +std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory, + NSSearchPathDomainMask domainMask) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + std::string *result; + NSString *path = nil; + + // Search for the path + NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory, + domainMask, + YES); + if ([paths count]) + { + path = [paths objectAtIndex:0]; + //SPATTERS HACK: Always attempt to create directory, ignore errors. + NSError *error = nil; + + [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; + + + result = new std::string([path UTF8String]); + } + [pool release]; + return result; +} + +std::string* getSystemExecutableFolder() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *bundlePath = [[NSBundle mainBundle] executablePath]; + std::string *result = (new std::string([bundlePath UTF8String])); + [pool release]; + + return result; +} + +std::string* getSystemResourceFolder() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *bundlePath = [[NSBundle mainBundle] resourcePath]; + std::string *result = (new std::string([bundlePath UTF8String])); + [pool release]; + + return result; +} + +std::string* getSystemCacheFolder() +{ + return findSystemDirectory (NSCachesDirectory, + NSUserDomainMask); +} + +std::string* getSystemApplicationSupportFolder() +{ + return findSystemDirectory (NSApplicationSupportDirectory, + NSUserDomainMask); + +} + +#endif // LL_DARWIN diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index ea321c5ae9..3cff622a4b 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -27,11 +27,167 @@ #include "linden_common.h" +#include "llstring.h" +#include "tests/StringVec.h" #include "../lldir.h" #include "../lldiriterator.h" #include "../test/lltut.h" +#include "stringize.h" +#include +#include +using boost::assign::list_of; + +// We use ensure_equals(..., vec(list_of(...))) not because it's functionally +// required, but because ensure_equals() knows how to format a StringVec. +// Turns out that when ensure_equals() displays a test failure with just +// list_of("string")("another"), you see 'stringanother' vs. '("string", +// "another")'. +StringVec vec(const StringVec& v) +{ + return v; +} + +// For some tests, use a dummy LLDir that uses memory data instead of touching +// the filesystem +struct LLDir_Dummy: public LLDir +{ + /*----------------------------- LLDir API ------------------------------*/ + LLDir_Dummy() + { + // Initialize important LLDir data members based on the filesystem + // data below. + mDirDelimiter = "/"; + mExecutableDir = "install"; + mExecutableFilename = "test"; + mExecutablePathAndName = add(mExecutableDir, mExecutableFilename); + mWorkingDir = mExecutableDir; + mAppRODataDir = "install"; + mSkinBaseDir = add(mAppRODataDir, "skins"); + mOSUserDir = "user"; + mOSUserAppDir = mOSUserDir; + mLindenUserDir = ""; + + // Make the dummy filesystem look more or less like what we expect in + // the real one. + static const char* preload[] = + { + // We group these fixture-data pathnames by basename, rather than + // sorting by full path as you might expect, because the outcome + // of each test strongly depends on which skins/languages provide + // a given basename. + "install/skins/default/colors.xml", + "install/skins/steam/colors.xml", + "user/skins/default/colors.xml", + "user/skins/steam/colors.xml", + + "install/skins/default/xui/en/strings.xml", + "install/skins/default/xui/fr/strings.xml", + "install/skins/steam/xui/en/strings.xml", + "install/skins/steam/xui/fr/strings.xml", + "user/skins/default/xui/en/strings.xml", + "user/skins/default/xui/fr/strings.xml", + "user/skins/steam/xui/en/strings.xml", + "user/skins/steam/xui/fr/strings.xml", + + "install/skins/default/xui/en/floater.xml", + "install/skins/default/xui/fr/floater.xml", + "user/skins/default/xui/fr/floater.xml", + + "install/skins/default/xui/en/newfile.xml", + "install/skins/default/xui/fr/newfile.xml", + "user/skins/default/xui/en/newfile.xml", + + "install/skins/default/html/en-us/welcome.html", + "install/skins/default/html/fr/welcome.html", + + "install/skins/default/textures/only_default.jpeg", + "install/skins/steam/textures/only_steam.jpeg", + "user/skins/default/textures/only_user_default.jpeg", + "user/skins/steam/textures/only_user_steam.jpeg", + + "install/skins/default/future/somefile.txt" + }; + BOOST_FOREACH(const char* path, preload) + { + buildFilesystem(path); + } + } + + virtual ~LLDir_Dummy() {} + + virtual void initAppDirs(const std::string& app_name, const std::string& app_read_only_data_dir) + { + // Implement this when we write a test that needs it + } + + virtual std::string getCurPath() + { + // Implement this when we write a test that needs it + return ""; + } + + virtual U32 countFilesInDir(const std::string& dirname, const std::string& mask) + { + // Implement this when we write a test that needs it + return 0; + } + + virtual bool fileExists(const std::string& pathname) const + { + // Record fileExists() calls so we can check whether caching is + // working right. Certain LLDir calls should be able to make decisions + // without calling fileExists() again, having already checked existence. + mChecked.insert(pathname); + // For our simple flat set of strings, see whether the identical + // pathname exists in our set. + return (mFilesystem.find(pathname) != mFilesystem.end()); + } + + virtual std::string getLLPluginLauncher() + { + // Implement this when we write a test that needs it + return ""; + } + + virtual std::string getLLPluginFilename(std::string base_name) + { + // Implement this when we write a test that needs it + return ""; + } + + /*----------------------------- Dummy data -----------------------------*/ + void clearFilesystem() { mFilesystem.clear(); } + void buildFilesystem(const std::string& path) + { + // Split the pathname on slashes, ignoring leading, trailing, doubles + StringVec components; + LLStringUtil::getTokens(path, components, "/"); + // Ensure we have an entry representing every level of this path + std::string partial; + BOOST_FOREACH(std::string component, components) + { + append(partial, component); + mFilesystem.insert(partial); + } + } + + void clear_checked() { mChecked.clear(); } + void ensure_checked(const std::string& pathname) const + { + tut::ensure(STRINGIZE(pathname << " was not checked but should have been"), + mChecked.find(pathname) != mChecked.end()); + } + void ensure_not_checked(const std::string& pathname) const + { + tut::ensure(STRINGIZE(pathname << " was checked but should not have been"), + mChecked.find(pathname) == mChecked.end()); + } + + std::set mFilesystem; + mutable std::set mChecked; +}; namespace tut { @@ -419,5 +575,193 @@ namespace tut LLFile::rmdir(dir1); LLFile::rmdir(dir2); } -} + template<> template<> + void LLDirTest_object_t::test<6>() + { + set_test_name("findSkinnedFilenames()"); + LLDir_Dummy lldir; + /*------------------------ "default", "en" -------------------------*/ + // Setting "default" means we shouldn't consider any "*/skins/steam" + // directories; setting "en" means we shouldn't consider any "xui/fr" + // directories. + lldir.setSkinFolder("default", "en"); + ensure_equals(lldir.getSkinFolder(), "default"); + ensure_equals(lldir.getLanguage(), "en"); + + // top-level directory of a skin isn't localized + ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), + vec(list_of("install/skins/default/colors.xml") + ("user/skins/default/colors.xml"))); + // We should not have needed to check for skins/default/en. We should + // just "know" that SKINBASE is not localized. + lldir.ensure_not_checked("install/skins/default/en"); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), + vec(list_of("install/skins/default/textures/only_default.jpeg"))); + // Nor should we have needed to check skins/default/textures/en + // because textures is known not to be localized. + lldir.ensure_not_checked("install/skins/default/textures/en"); + + StringVec expected(vec(list_of("install/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/en/strings.xml"))); + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + expected); + // The first time, we had to probe to find out whether xui was localized. + lldir.ensure_checked("install/skins/default/xui/en"); + lldir.clear_checked(); + // Now make the same call again -- should return same result -- + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + expected); + // but this time it should remember that xui is localized. + lldir.ensure_not_checked("install/skins/default/xui/en"); + + // localized subdir with "en-us" instead of "en" + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of("install/skins/default/html/en-us/welcome.html"))); + lldir.ensure_checked("install/skins/default/html/en"); + lldir.ensure_checked("install/skins/default/html/en-us"); + lldir.clear_checked(); + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of("install/skins/default/html/en-us/welcome.html"))); + lldir.ensure_not_checked("install/skins/default/html/en"); + lldir.ensure_not_checked("install/skins/default/html/en-us"); + + ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), + vec(list_of("install/skins/default/future/somefile.txt"))); + // Test probing for an unrecognized unlocalized future subdir. + lldir.ensure_checked("install/skins/default/future/en"); + lldir.clear_checked(); + ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), + vec(list_of("install/skins/default/future/somefile.txt"))); + // Second time it should remember that future is unlocalized. + lldir.ensure_not_checked("install/skins/default/future/en"); + + // When language is set to "en", requesting an html file pulls up the + // "en-us" version -- not because it magically matches those strings, + // but because there's no "en" localization and it falls back on the + // default "en-us"! Note that it would probably still be better to + // make the default localization be "en" and allow "en-gb" (or + // whatever) localizations, which would work much more the way you'd + // expect. + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of("install/skins/default/html/en-us/welcome.html"))); + + /*------------------------ "default", "fr" -------------------------*/ + // We start being able to distinguish localized subdirs from + // unlocalized when we ask for a non-English language. + lldir.setSkinFolder("default", "fr"); + ensure_equals(lldir.getLanguage(), "fr"); + + // pass merge=true to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + vec(list_of + ("install/skins/default/xui/en/strings.xml") + ("install/skins/default/xui/fr/strings.xml") + ("user/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/fr/strings.xml"))); + + // pass (or default) merge=false to request only most specific skin + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of + ("user/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/fr/strings.xml"))); + + // Our dummy floater.xml has a user localization (for "fr") but no + // English override. This is a case in which CURRENT_SKIN nonetheless + // returns paths from two different skins. + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"), + vec(list_of + ("install/skins/default/xui/en/floater.xml") + ("user/skins/default/xui/fr/floater.xml"))); + + // Our dummy newfile.xml has an English override but no user + // localization. This is another case in which CURRENT_SKIN + // nonetheless returns paths from two different skins. + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"), + vec(list_of + ("user/skins/default/xui/en/newfile.xml") + ("install/skins/default/xui/fr/newfile.xml"))); + + ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), + vec(list_of + ("install/skins/default/html/en-us/welcome.html") + ("install/skins/default/html/fr/welcome.html"))); + + /*------------------------ "default", "zh" -------------------------*/ + lldir.setSkinFolder("default", "zh"); + // Because strings.xml has only a "fr" override but no "zh" override + // in any skin, the most localized version we can find is "en". + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of("user/skins/default/xui/en/strings.xml"))); + + /*------------------------- "steam", "en" --------------------------*/ + lldir.setSkinFolder("steam", "en"); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), + vec(list_of + ("install/skins/default/colors.xml") + ("install/skins/steam/colors.xml") + ("user/skins/default/colors.xml") + ("user/skins/steam/colors.xml"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), + vec(list_of("install/skins/default/textures/only_default.jpeg"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_steam.jpeg"), + vec(list_of("install/skins/steam/textures/only_steam.jpeg"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_default.jpeg"), + vec(list_of("user/skins/default/textures/only_user_default.jpeg"))); + + ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg"), + vec(list_of("user/skins/steam/textures/only_user_steam.jpeg"))); + + // CURRENT_SKIN + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of("user/skins/steam/xui/en/strings.xml"))); + + // pass constraint=ALL_SKINS to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + vec(list_of + ("install/skins/default/xui/en/strings.xml") + ("install/skins/steam/xui/en/strings.xml") + ("user/skins/default/xui/en/strings.xml") + ("user/skins/steam/xui/en/strings.xml"))); + + /*------------------------- "steam", "fr" --------------------------*/ + lldir.setSkinFolder("steam", "fr"); + + // pass CURRENT_SKIN to request only the most specialized files + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of + ("user/skins/steam/xui/en/strings.xml") + ("user/skins/steam/xui/fr/strings.xml"))); + + // pass ALL_SKINS to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), + vec(list_of + ("install/skins/default/xui/en/strings.xml") + ("install/skins/default/xui/fr/strings.xml") + ("install/skins/steam/xui/en/strings.xml") + ("install/skins/steam/xui/fr/strings.xml") + ("user/skins/default/xui/en/strings.xml") + ("user/skins/default/xui/fr/strings.xml") + ("user/skins/steam/xui/en/strings.xml") + ("user/skins/steam/xui/fr/strings.xml"))); + } + + template<> template<> + void LLDirTest_object_t::test<7>() + { + set_test_name("add()"); + LLDir_Dummy lldir; + ensure_equals("both empty", lldir.add("", ""), ""); + ensure_equals("path empty", lldir.add("", "b"), "b"); + ensure_equals("name empty", lldir.add("a", ""), "a"); + ensure_equals("both simple", lldir.add("a", "b"), "a/b"); + ensure_equals("name leading slash", lldir.add("a", "/b"), "a/b"); + ensure_equals("path trailing slash", lldir.add("a/", "b"), "a/b"); + ensure_equals("both bring slashes", lldir.add("a/", "/b"), "a/b"); + } +} diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 52ba8b3bf3..af83b50097 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -36,8 +36,8 @@ #include // AssertMacros.h does bad things. +#include "fix_macros.h" #undef verify -#undef check #undef require diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 3bf4a48cb6..39f8a36a6e 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -2646,8 +2646,9 @@ std::vector LLWindowSDL::getDynamicFallbackFontList() if (sortpat) { // Sort the list of system fonts from most-to-least-desirable. + FcResult result; fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, - NULL, NULL); + NULL, &result); FcPatternDestroy(sortpat); } diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 4e2a269ea3..c5ce892a04 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -41,8 +41,8 @@ #endif // AssertMacros.h does bad things. +#include "fix_macros.h" #undef verify -#undef check #undef require diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 2ffb0d8503..b775249219 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -897,7 +897,8 @@ bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root, std::vector::const_iterator itor; - for (itor = paths.begin(), ++itor; itor != paths.end(); ++itor) + // We've already dealt with the first item, skip that one + for (itor = paths.begin() + 1; itor != paths.end(); ++itor) { std::string layer_filename = *itor; if(layer_filename.empty() || layer_filename == filename) diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index 420e836e36..3906a3bb8c 100644 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -23,12 +23,14 @@ include_directories( set(mac_crash_logger_SOURCE_FILES mac_crash_logger.cpp llcrashloggermac.cpp + llcrashloggermacdelegate.mm ) set(mac_crash_logger_HEADER_FILES CMakeLists.txt llcrashloggermac.h + llcrashloggermacdelegate.h ) set_source_files_properties(${mac_crash_logger_HEADER_FILES} @@ -55,9 +57,12 @@ set_target_properties(mac-crash-logger MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist ) +find_library(COCOA_LIBRARY Cocoa) + target_link_libraries(mac-crash-logger ${LLCRASHLOGGER_LIBRARIES} ${LLVFS_LIBRARIES} + ${COCOA_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} diff --git a/indra/mac_crash_logger/CrashReporter.nib b/indra/mac_crash_logger/CrashReporter.nib new file mode 100644 index 0000000000..a30d8d205c Binary files /dev/null and b/indra/mac_crash_logger/CrashReporter.nib differ diff --git a/indra/mac_crash_logger/CrashReporter.nib/classes.nib b/indra/mac_crash_logger/CrashReporter.nib/classes.nib deleted file mode 100644 index c4b887e72b..0000000000 --- a/indra/mac_crash_logger/CrashReporter.nib/classes.nib +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IBVersion - 1 - - diff --git a/indra/mac_crash_logger/CrashReporter.nib/info.nib b/indra/mac_crash_logger/CrashReporter.nib/info.nib deleted file mode 100644 index 06805c0e4f..0000000000 --- a/indra/mac_crash_logger/CrashReporter.nib/info.nib +++ /dev/null @@ -1,18 +0,0 @@ - - - - - IBFramework Version - 629 - IBLastKnownRelativeProjectPath - ../../build-darwin-i386/SecondLife.xcodeproj - IBOldestOS - 5 - IBOpenObjects - - IBSystem Version - 9E17 - targetFramework - IBCarbonFramework - - diff --git a/indra/mac_crash_logger/CrashReporter.nib/objects.xib b/indra/mac_crash_logger/CrashReporter.nib/objects.xib deleted file mode 100644 index 32647391b6..0000000000 --- a/indra/mac_crash_logger/CrashReporter.nib/objects.xib +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - ok - Send Report - 414 273 434 378 - - - not! - 2 - Don't Send - 414 390 434 487 - - - Second Life appears to have crashed or frozen the last time it ran. This crash reporter collects information about your computer's hardware configuration, operating system, and some Second Life logs, all of which are used for debugging purposes only. In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help! This report is NOT read by Customer Support. If you have billing or other questions, please go to: http://www.secondlife.com/support/ If you don't wish to send Linden Lab a crash report, press Don't Send. - 20 20 231 487 - - - 2 - 3 - 7 - Second Life Crash Logger - - - - - - - text - TRUE - 242 23 391 484 - - - remb - Remember This Choice - 415 20 433 186 - - - 0 0 454 507 - - 257 653 711 1160 - 0 0 768 1024 - - - - - - - - - - - - - - - - CrashReporter - - File's Owner - - - IBCarbonFramework - 194 - diff --git a/indra/mac_crash_logger/CrashReporter.xib b/indra/mac_crash_logger/CrashReporter.xib new file mode 100644 index 0000000000..f6d4776d51 --- /dev/null +++ b/indra/mac_crash_logger/CrashReporter.xib @@ -0,0 +1,3895 @@ + + + + 1070 + 11G63 + 2182 + 1138.51 + 569.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2182 + + + NSTextField + NSView + NSWindowTemplate + NSMenu + NSMenuItem + NSTextFieldCell + NSButtonCell + IBNSLayoutConstraint + NSButton + NSCustomObject + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + + + Second Life Crash Logger + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + Second Life Crash Logger + + + + About Second Life Crash Logger + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + Services + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide Second Life Crash Logger + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit Second Life Crash Logger + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + File + + + + New + n + 1048576 + 2147483647 + + + + + + Open… + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + Open Recent + + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save… + s + 1048576 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + Print… + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + Edit + + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + Find + + + + Find… + f + 1048576 + 2147483647 + + + 1 + + + + Find and Replace… + f + 1572864 + 2147483647 + + + 12 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1179648 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 1048576 + 2147483647 + + + submenuAction: + + Spelling and Grammar + + + + Show Spelling and Grammar + : + 1048576 + 2147483647 + + + + + + Check Document Now + ; + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Check Spelling While Typing + + 1048576 + 2147483647 + + + + + + Check Grammar With Spelling + + 1048576 + 2147483647 + + + + + + Correct Spelling Automatically + + 2147483647 + + + + + + + + + Substitutions + + 1048576 + 2147483647 + + + submenuAction: + + Substitutions + + + + Show Substitutions + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Smart Copy/Paste + f + 1048576 + 2147483647 + + + 1 + + + + Smart Quotes + g + 1048576 + 2147483647 + + + 2 + + + + Smart Dashes + + 2147483647 + + + + + + Smart Links + G + 1179648 + 2147483647 + + + 3 + + + + Text Replacement + + 2147483647 + + + + + + + + + Transformations + + 2147483647 + + + submenuAction: + + Transformations + + + + Make Upper Case + + 2147483647 + + + + + + Make Lower Case + + 2147483647 + + + + + + Capitalize + + 2147483647 + + + + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + Speech + + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Format + + 2147483647 + + + submenuAction: + + Format + + + + Font + + 2147483647 + + + submenuAction: + + Font + + + + Show Fonts + t + 1048576 + 2147483647 + + + + + + Bold + b + 1048576 + 2147483647 + + + 2 + + + + Italic + i + 1048576 + 2147483647 + + + 1 + + + + Underline + u + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Bigger + + + 1048576 + 2147483647 + + + 3 + + + + Smaller + - + 1048576 + 2147483647 + + + 4 + + + + YES + YES + + + 2147483647 + + + + + + Kern + + 2147483647 + + + submenuAction: + + Kern + + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Tighten + + 2147483647 + + + + + + Loosen + + 2147483647 + + + + + + + + + Ligature + + 2147483647 + + + submenuAction: + + Ligature + + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Use All + + 2147483647 + + + + + + + + + Baseline + + 2147483647 + + + submenuAction: + + Baseline + + + + Use Default + + 2147483647 + + + + + + Superscript + + 2147483647 + + + + + + Subscript + + 2147483647 + + + + + + Raise + + 2147483647 + + + + + + Lower + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Colors + C + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Copy Style + c + 1572864 + 2147483647 + + + + + + Paste Style + v + 1572864 + 2147483647 + + + + + _NSFontMenu + + + + + Text + + 2147483647 + + + submenuAction: + + Text + + + + Align Left + { + 1048576 + 2147483647 + + + + + + Center + | + 1048576 + 2147483647 + + + + + + Justify + + 2147483647 + + + + + + Align Right + } + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Writing Direction + + 2147483647 + + + submenuAction: + + Writing Direction + + + + YES + Paragraph + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + YES + Selection + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Ruler + + 2147483647 + + + + + + Copy Ruler + c + 1310720 + 2147483647 + + + + + + Paste Ruler + v + 1310720 + 2147483647 + + + + + + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + View + + + + Show Toolbar + t + 1572864 + 2147483647 + + + + + + Customize Toolbar… + + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + Window + + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + + + Second Life Crash Logger Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 15 + 2 + {{335, 390}, {508, 477}} + 1954021376 + Second Life Crash Logger + NSWindow + + + + + 256 + + + + 268 + {{17, 228}, {474, 229}} + + + + _NS:9 + {250, 750} + YES + + 67239424 + 272891904 + + U2Vjb25kIExpZmUgYXBwZWFycyB0byBoYXZlIGNyYXNoZWQgb3IgZnJvemVuIHRoZSBsYXN0IHRpbWUg +aXQgcmFuLgoKVGhpcyBjcmFzaCByZXBvcnRlciBjb2xsZWN0cyBpbmZvcm1hdGlvbiBhYm91dCB5b3Vy +IGNvbXB1dGVyJ3MgaGFyZHdhcmUgY29uZmlndXJhdGlvbiwgb3BlcmF0aW5nIHN5c3RlbSwgYW5kIHNv +bWUgU2Vjb25kIExpZmUgbG9ncywgYWxsIG9mIHdoaWNoIGFyZSB1c2VkIGZvciBkZWJ1Z2dpbmcgcHVy +cG9zZXMgb25seS4KCkluIHRoZSBzcGFjZSBiZWxvdywgcGxlYXNlIGJyaWVmbHkgZGVzY3JpYmUgd2hh +dCB5b3Ugd2VyZSBkb2luZyBvciB0cnlpbmcgdG8gZG8ganVzdCBwcmlvciB0byB0aGUgY3Jhc2guICBU +aGFuayB5b3UgZm9yIHlvdXIgaGVscCEKClRoaXMgcmVwb3J0IGlzIE5PVCByZWFkIGJ5IEN1c3RvbWVy +IFN1cHBvcnQuICBJZiB5b3UgaGF2ZSBiaWxsaW5nIG9yIG90aGVyIHF1ZXN0aW9ucywgcGxlYXNlIGdv +IHRvOiBodHRwOi8vd3d3LnNlY29uZGxpZmUuY29tL3N1cHBvcnQvCgpJZiB5b3UgZG9uJ3Qgd2lzaCB0 +byBzZW5kIExpbmRlbiBMYWIgYSBjcmFzaCByZXBvcnQsIHByZXNzIENhbmNlbC4 + + + LucidaGrande + 13 + 16 + + _NS:9 + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + + + + 268 + {{20, 64}, {468, 163}} + + + + _NS:9 + {250, 750} + YES + + -1805517311 + 272891904 + + + LucidaGrande + 9 + 3614 + + _NS:9 + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + + + + + + 268 + {{16, 18}, {189, 30}} + + + + _NS:9 + YES + + -2080244224 + 262144 + Remember This Choice + + _NS:9 + + 1211912703 + 2 + + NSImage + NSSwitch + + + NSSwitch + + + + 200 + 25 + + + + + 268 + {{285, 23}, {91, 17}} + + + + _NS:9 + YES + + -2080244224 + 134479872 + Send Report + + _NS:9 + + -2038152961 + 164 + + + 400 + 75 + + + + + 268 + {{388, 23}, {100, 17}} + + + + _NS:9 + YES + + -2080244224 + 134479872 + Don't Send + + _NS:9 + + -2038152961 + 164 + + + 400 + 75 + + + + {508, 477} + + + + + {{0, 0}, {1680, 1028}} + {10000000000000, 10000000000000} + YES + + + LLCrashLoggerMacDelegate + + + + + + + terminate: + + + + 449 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + delegate + + + + 495 + + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + performClose: + + + + 193 + + + + toggleContinuousSpellChecking: + + + + 222 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + checkSpelling: + + + + 225 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + showGuessPanel: + + + + 230 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performZoom: + + + + 240 + + + + performFindPanelAction: + + + + 241 + + + + centerSelectionInVisibleArea: + + + + 245 + + + + toggleGrammarChecking: + + + + 347 + + + + toggleSmartInsertDelete: + + + + 355 + + + + toggleAutomaticQuoteSubstitution: + + + + 356 + + + + toggleAutomaticLinkDetection: + + + + 357 + + + + saveDocument: + + + + 362 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + unhideAllApplications: + + + + 370 + + + + newDocument: + + + + 373 + + + + openDocument: + + + + 374 + + + + raiseBaseline: + + + + 426 + + + + lowerBaseline: + + + + 427 + + + + copyFont: + + + + 428 + + + + subscript: + + + + 429 + + + + superscript: + + + + 430 + + + + tightenKerning: + + + + 431 + + + + underline: + + + + 432 + + + + orderFrontColorPanel: + + + + 433 + + + + useAllLigatures: + + + + 434 + + + + loosenKerning: + + + + 435 + + + + pasteFont: + + + + 436 + + + + unscript: + + + + 437 + + + + useStandardKerning: + + + + 438 + + + + useStandardLigatures: + + + + 439 + + + + turnOffLigatures: + + + + 440 + + + + turnOffKerning: + + + + 441 + + + + toggleAutomaticSpellingCorrection: + + + + 456 + + + + orderFrontSubstitutionsPanel: + + + + 458 + + + + toggleAutomaticDashSubstitution: + + + + 461 + + + + toggleAutomaticTextReplacement: + + + + 463 + + + + uppercaseWord: + + + + 464 + + + + capitalizeWord: + + + + 467 + + + + lowercaseWord: + + + + 468 + + + + pasteAsPlainText: + + + + 486 + + + + performFindPanelAction: + + + + 487 + + + + performFindPanelAction: + + + + 488 + + + + performFindPanelAction: + + + + 489 + + + + showHelp: + + + + 493 + + + + alignCenter: + + + + 518 + + + + pasteRuler: + + + + 519 + + + + toggleRuler: + + + + 520 + + + + alignRight: + + + + 521 + + + + copyRuler: + + + + 522 + + + + alignJustified: + + + + 523 + + + + alignLeft: + + + + 524 + + + + makeBaseWritingDirectionNatural: + + + + 525 + + + + makeBaseWritingDirectionLeftToRight: + + + + 526 + + + + makeBaseWritingDirectionRightToLeft: + + + + 527 + + + + makeTextWritingDirectionNatural: + + + + 528 + + + + makeTextWritingDirectionLeftToRight: + + + + 529 + + + + makeTextWritingDirectionRightToLeft: + + + + 530 + + + + performFindPanelAction: + + + + 535 + + + + window + + + + 532 + + + + remember: + + + + 1176 + + + + send: + + + + 1177 + + + + cancel: + + + + 1178 + + + + crashText + + + + 1179 + + + + rememberCheck + + + + 1187 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + + + + + + + + + + + + 19 + + + + + + + + 56 + + + + + + + + 217 + + + + + + + + 83 + + + + + + + + 81 + + + + + + + + + + + + + + + + + 75 + + + + + 78 + + + + + 72 + + + + + 82 + + + + + 124 + + + + + + + + 77 + + + + + 73 + + + + + 79 + + + + + 112 + + + + + 74 + + + + + 125 + + + + + + + + 126 + + + + + 205 + + + + + + + + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 218 + + + + + + + + 216 + + + + + + + + 200 + + + + + + + + + + + + + 219 + + + + + 201 + + + + + 204 + + + + + 220 + + + + + + + + + + + + + 213 + + + + + 210 + + + + + 221 + + + + + 208 + + + + + 209 + + + + + 57 + + + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 144 + + + + + 129 + + + + + 143 + + + + + 236 + + + + + 131 + + + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + + + + + + 296 + + + + + + + + + 297 + + + + + 298 + + + + + 211 + + + + + + + + 212 + + + + + + + + + 195 + + + + + 196 + + + + + 346 + + + + + 348 + + + + + + + + 349 + + + + + + + + + + + + + + 350 + + + + + 351 + + + + + 354 + + + + + 371 + + + + + + + + 372 + + + + + + + + 3 + 0 + + 3 + 1 + + 20 + + 1000 + 8 + 29 + 3 + + + + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + 8 + 29 + 3 + + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + 8 + 29 + 3 + + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + 8 + 29 + 3 + + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + 8 + 29 + 3 + + + + + 4 + 0 + + 4 + 1 + + 64 + + 1000 + 3 + 9 + 3 + + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + 8 + 29 + 3 + + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + 8 + 29 + 3 + + + + + 4 + 0 + + 4 + 1 + + 21 + + 1000 + 3 + 9 + 3 + + + + + 6 + 0 + + 6 + 1 + + 132 + + 1000 + 3 + 9 + 3 + + + + + 11 + 0 + + 11 + 1 + + 0.0 + + 1000 + 6 + 24 + 2 + + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + 6 + 24 + 2 + + + + + + + 375 + + + + + + + + 376 + + + + + + + + + 377 + + + + + + + + 388 + + + + + + + + + + + + + + + + + + + + + + + 389 + + + + + 390 + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 396 + + + + + 397 + + + + + + + + 398 + + + + + + + + 399 + + + + + + + + 400 + + + + + 401 + + + + + 402 + + + + + 403 + + + + + 404 + + + + + 405 + + + + + + + + + + + + 406 + + + + + 407 + + + + + 408 + + + + + 409 + + + + + 410 + + + + + 411 + + + + + + + + + + 412 + + + + + 413 + + + + + 414 + + + + + 415 + + + + + + + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 419 + + + + + 450 + + + + + + + + 451 + + + + + + + + + + 452 + + + + + 453 + + + + + 454 + + + + + 457 + + + + + 459 + + + + + 460 + + + + + 462 + + + + + 465 + + + + + 466 + + + + + 485 + + + + + 490 + + + + + + + + 491 + + + + + + + + 492 + + + + + 494 + + + + + 496 + + + + + + + + 497 + + + + + + + + + + + + + + + + + 498 + + + + + 499 + + + + + 500 + + + + + 501 + + + + + 502 + + + + + 503 + + + + + + + + 504 + + + + + 505 + + + + + 506 + + + + + 507 + + + + + 508 + + + + + + + + + + + + + + + + 509 + + + + + 510 + + + + + 511 + + + + + 512 + + + + + 513 + + + + + 514 + + + + + 515 + + + + + 516 + + + + + 517 + + + + + 534 + + + + + 536 + + + + + + 8 + 0 + + 0 + 1 + + 229 + + 1000 + 3 + 9 + 1 + + + + + + + 537 + + + + + 593 + + + + + + 8 + 0 + + 0 + 1 + + 163 + + 1000 + 3 + 9 + 1 + + + + + + + 594 + + + + + 727 + + + + + + 7 + 0 + + 0 + 1 + + 183 + + 1000 + 3 + 9 + 1 + + + + + 8 + 0 + + 0 + 1 + + 22 + + 1000 + 3 + 9 + 1 + + + + + + + 728 + + + + + 775 + + + + + + + + 776 + + + + + 780 + + + + + + 7 + 0 + + 0 + 1 + + 100 + + 1000 + 3 + 9 + 1 + + + + + + + 781 + + + + + 884 + + + + + 981 + + + + + 982 + + + + + 1022 + + + + + 1026 + + + + + 979 + + + + + 985 + + + + + 977 + + + + + 1099 + + + + + 1093 + + + + + 1100 + + + + + 1098 + + + + + 1168 + + + + + 1167 + + + + + 1095 + + + + + 1166 + + + + + 1076 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{380, 496}, {480, 360}} + + + + + + + + + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 1187 + + + + + LLCrashLoggerMacDelegate + NSObject + + id + id + id + + + + cancel: + id + + + remember: + id + + + send: + id + + + + NSTextField + NSButton + NSWindow + + + + crashText + NSTextField + + + rememberCheck + NSButton + + + window + NSWindow + + + + IBProjectSource + ./Classes/LLCrashLoggerMacDelegate.h + + + + NSLayoutConstraint + NSObject + + IBProjectSource + ./Classes/NSLayoutConstraint.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + YES + 3 + + {11, 11} + {10, 3} + {15, 15} + + YES + + diff --git a/indra/mac_crash_logger/Info.plist b/indra/mac_crash_logger/Info.plist index f48293e825..2ebed11c3f 100644 --- a/indra/mac_crash_logger/Info.plist +++ b/indra/mac_crash_logger/Info.plist @@ -1,5 +1,5 @@ - + CFBundleDevelopmentRegion @@ -10,8 +10,6 @@ CFBundleIconFile - CFBundleIdentifier - com.secondlife.indra.crashreporter CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType @@ -22,5 +20,9 @@ ???? CFBundleVersion 1.0.0 + NSMainNibFile + CrashReporter + NSPrincipalClass + NSApplication diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp index 8f1c1a2dd0..c5f660ca6e 100644 --- a/indra/mac_crash_logger/llcrashloggermac.cpp +++ b/indra/mac_crash_logger/llcrashloggermac.cpp @@ -27,7 +27,6 @@ #include "llcrashloggermac.h" -#include #include #include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME @@ -38,102 +37,14 @@ #include "lldir.h" #include "llsdserialize.h" -#define MAX_LOADSTRING 100 -const char* const SETTINGS_FILE_HEADER = "version"; -const S32 SETTINGS_FILE_VERSION = 101; - // Windows Message Handlers -BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog? +BOOL gFirstDialog = TRUE; LLFILE *gDebugFile = NULL; -WindowRef gWindow = NULL; -EventHandlerRef gEventHandler = NULL; std::string gUserNotes = ""; bool gSendReport = false; bool gRememberChoice = false; -IBNibRef nib = NULL; - -OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata) -{ - OSStatus result = eventNotHandledErr; - OSStatus err; - UInt32 evtClass = GetEventClass(event); - UInt32 evtKind = GetEventKind(event); - if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess)) - { - HICommand cmd; - err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd); - - - - if(err == noErr) - { - //Get the value of the checkbox - ControlID id; - ControlRef checkBox = NULL; - id.signature = 'remb'; - id.id = 0; - err = GetControlByID(gWindow, &id, &checkBox); - - if(err == noErr) - { - if(GetControl32BitValue(checkBox) == kControlCheckBoxCheckedValue) - { - gRememberChoice = true; - } - else - { - gRememberChoice = false; - } - } - switch(cmd.commandID) - { - case kHICommandOK: - { - char buffer[65535]; /* Flawfinder: ignore */ - Size size = sizeof(buffer) - 1; - ControlRef textField = NULL; - - id.signature = 'text'; - id.id = 0; - - err = GetControlByID(gWindow, &id, &textField); - if(err == noErr) - { - // Get the user response text - err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size); - } - if(err == noErr) - { - // Make sure the string is terminated. - buffer[size] = 0; - gUserNotes = buffer; - - llinfos << buffer << llendl; - } - - // Send the report. - - QuitAppModalLoopForWindow(gWindow); - gSendReport = true; - result = noErr; - } - break; - - case kHICommandCancel: - QuitAppModalLoopForWindow(gWindow); - result = noErr; - break; - default: - result = eventNotHandledErr; - } - } - } - - return(result); -} - LLCrashLoggerMac::LLCrashLoggerMac(void) { @@ -146,73 +57,16 @@ LLCrashLoggerMac::~LLCrashLoggerMac(void) bool LLCrashLoggerMac::init(void) { bool ok = LLCrashLogger::init(); - if(!ok) return false; - if(mCrashBehavior != CRASH_BEHAVIOR_ASK) return true; - - // Real UI... - OSStatus err; - - err = CreateNibReference(CFSTR("CrashReporter"), &nib); - - if(err == noErr) - { - err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow); - } - - if(err == noErr) - { - // Set focus to the edit text area - ControlRef textField = NULL; - ControlID id; - - id.signature = 'text'; - id.id = 0; - - // Don't set err if any of this fails, since it's non-critical. - if(GetControlByID(gWindow, &id, &textField) == noErr) - { - SetKeyboardFocus(gWindow, textField, kControlFocusNextPart); - } - } - - if(err == noErr) - { - ShowWindow(gWindow); - } - - if(err == noErr) - { - // Set up an event handler for the window. - EventTypeSpec handlerEvents[] = - { - { kEventClassCommand, kEventCommandProcess } - }; - - InstallWindowEventHandler( - gWindow, - NewEventHandlerUPP(dialogHandler), - GetEventTypeCount (handlerEvents), - handlerEvents, - 0, - &gEventHandler); - } - return true; + return ok; } void LLCrashLoggerMac::gatherPlatformSpecificFiles() { - updateApplication("Gathering hardware information..."); } bool LLCrashLoggerMac::mainLoop() { - OSStatus err = noErr; - - if(err == noErr && mCrashBehavior == CRASH_BEHAVIOR_ASK) - { - RunAppModalLoopForWindow(gWindow); - } - else if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND) + if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND) { gSendReport = true; } @@ -227,26 +81,11 @@ bool LLCrashLoggerMac::mainLoop() { setUserText(gUserNotes); sendCrashLogs(); - } - - if(gWindow != NULL) - { - DisposeWindow(gWindow); - } - - if(nib != NULL) - { - DisposeNibReference(nib); - } - + } + return true; } -void LLCrashLoggerMac::updateApplication(const std::string& message) -{ - LLCrashLogger::updateApplication(message); -} - bool LLCrashLoggerMac::cleanup() { commonCleanup(); diff --git a/indra/mac_crash_logger/llcrashloggermac.h b/indra/mac_crash_logger/llcrashloggermac.h index 4b1d235f24..6d8f63ecac 100644 --- a/indra/mac_crash_logger/llcrashloggermac.h +++ b/indra/mac_crash_logger/llcrashloggermac.h @@ -38,7 +38,6 @@ public: ~LLCrashLoggerMac(void); virtual bool init(); virtual bool mainLoop(); - virtual void updateApplication(const std::string& message = LLStringUtil::null); virtual bool cleanup(); virtual void gatherPlatformSpecificFiles(); }; diff --git a/indra/mac_crash_logger/llcrashloggermacdelegate.h b/indra/mac_crash_logger/llcrashloggermacdelegate.h new file mode 100644 index 0000000000..c998a8efe2 --- /dev/null +++ b/indra/mac_crash_logger/llcrashloggermacdelegate.h @@ -0,0 +1,52 @@ +/** + * @file llcrashloggermacdelegate.h + * @brief Mac OSX crash logger implementation + * + * $LicenseInfo:firstyear=2003&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$ + */ + +/* +#import + +@interface LLCrashLoggerMacDelegate : NSObject +{ + IBOutlet NSTextField *crashText; + IBOutlet NSButton *rememberCheck; + + NSWindow *_window; + bool mRemember; + +} + +- (void)setWindow:(NSWindow *)newWindow; +- (NSWindow *)window; + +- (IBAction)remember:(id)sender; +- (IBAction)send:(id)sender; +- (IBAction)cancel:(id)sender; + +@property (assign) IBOutlet NSWindow *window; + +@end +*/ + + diff --git a/indra/mac_crash_logger/llcrashloggermacdelegate.mm b/indra/mac_crash_logger/llcrashloggermacdelegate.mm new file mode 100644 index 0000000000..b2af76a47c --- /dev/null +++ b/indra/mac_crash_logger/llcrashloggermacdelegate.mm @@ -0,0 +1,75 @@ +/** + * @file llcrashloggermacdelegate.mm + * @brief Mac OSX crash logger implementation + * + * $LicenseInfo:firstyear=2003&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$ + */ + + +/* +#import "llcrashloggermacdelegate.h" +#include + +extern std::string gUserNotes; +extern bool gSendReport; +extern bool gRememberChoice; + +@implementation LLCrashLoggerMacDelegate + +- (void)setWindow:(NSWindow *)window +{ + _window = window; +} + +- (NSWindow *)window +{ + return _window; +} + +- (void)dealloc +{ + [super dealloc]; +} + +std::string* NSToString( NSString *ns_str ) +{ + return ( new std::string([ns_str UTF8String]) ); +} + +- (IBAction)remember:(id)sender +{ + gRememberChoice = [rememberCheck state]; +} + +- (IBAction)send:(id)sender +{ + std::string* user_input = NSToString([crashText stringValue]); + gUserNotes = *user_input; + gSendReport = true; +} + +- (IBAction)cancel:(id)sender +{ + [ _window close]; +} +@end +*/ \ No newline at end of file diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp index 6571b35241..6add74556f 100644 --- a/indra/mac_crash_logger/mac_crash_logger.cpp +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -26,11 +26,12 @@ #include "linden_common.h" #include "llcrashloggermac.h" +#include "indra_constants.h" +#include + int main(int argc, char **argv) { - llinfos << "Starting crash reporter." << llendl; - LLCrashLoggerMac app; app.parseCommandOptions(argc, argv); @@ -39,9 +40,16 @@ int main(int argc, char **argv) llwarns << "Unable to initialize application." << llendl; return 1; } + if (app.getCrashBehavior() != CRASH_BEHAVIOR_ALWAYS_SEND) + { +// return NSApplicationMain(argc, (const char **)argv); + } app.mainLoop(); + app.cleanup(); + llinfos << "Crash reporter finished normally." << llendl; + return 0; } diff --git a/indra/mac_updater/mac_updater.h b/indra/mac_updater/mac_updater.h new file mode 100644 index 0000000000..f65b481cb6 --- /dev/null +++ b/indra/mac_updater/mac_updater.h @@ -0,0 +1,91 @@ +/** + * @file mac_updater.h + * @brief + * + * $LicenseInfo:firstyear=2006&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 +#include +#include + +#ifndef LL_MAC_UPDATER_H +#define LL_MAC_UPDATER_H +extern bool gCancelled; +extern bool gFailure; + +void *updatethreadproc(void*); +std::string* walkParents( signed int depth, std::string* childpath ); +std::string* getUserTrashFolder(); + +void setProgress(int cur, int max); +void setProgressText(const std::string& str); +void sendProgress(int cur, int max, std::string str); +void sendDone(); +void sendStopAlert(); + +bool isFSRefViewerBundle(const std::string& targetURL); +bool isDirWritable(const std::string& dir_name); +bool mkTempDir(boost::filesystem::path& temp_dir); +bool copyDir(const std::string& src_dir, const std::string& dest_dir); + +int oldmain(); + +class LLMacUpdater +{ +public: + LLMacUpdater(); + void doUpdate(); + const std::string walkParents( signed int depth, const std::string& childpath ); + bool isApplication(const std::string& app_str); + void filterFile(const char* filename); + + bool findAppBundleOnDiskImage(const boost::filesystem::path& dir_path, + boost::filesystem::path& path_found); + + bool verifyDirectory(const boost::filesystem::path* directory, bool isParent=false); + bool getViewerDir(boost::filesystem::path &app_dir); + bool downloadDMG(const std::string& dmgName, boost::filesystem::path* temp_dir); + bool doMount(const std::string& dmgName, char* deviceNode, const boost::filesystem::path& temp_dir); + bool moveApplication (const boost::filesystem::path& app_dir, + const boost::filesystem::path& temp_dir, + boost::filesystem::path& aside_dir); + bool doInstall(const boost::filesystem::path& app_dir, + const boost::filesystem::path& temp_dir, + boost::filesystem::path& mount_dir, + bool replacingTarget); + void* updatethreadproc(void*); + static void* sUpdatethreadproc(void*); + +public: + std::string *mUpdateURL; + std::string *mProductName; + std::string *mBundleID; + std::string *mDmgFile; + std::string *mMarkerPath; + std::string *mApplicationPath; + static LLMacUpdater *sInstance; + +}; +#endif + + diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a8a7e05fd5..dff2c04fbc 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -18,6 +18,7 @@ include(JsonCpp) include(LLAudio) include(LLCharacter) include(LLCommon) +include(LLCoreHttp) include(LLImage) include(LLImageJ2COJ) include(LLInventory) @@ -57,6 +58,7 @@ include_directories( ${LLAUDIO_INCLUDE_DIRS} ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${LLPHYSICS_INCLUDE_DIRS} ${FMOD_INCLUDE_DIR} ${LLIMAGE_INCLUDE_DIRS} @@ -97,6 +99,7 @@ set(viewer_SOURCE_FILES llagentwearables.cpp llagentwearablesfetch.cpp llanimstatelabels.cpp + llappcorehttp.cpp llappearancemgr.cpp llappviewer.cpp llappviewerlistener.cpp @@ -334,6 +337,7 @@ set(viewer_SOURCE_FILES llmediactrl.cpp llmediadataclient.cpp llmemoryview.cpp + llmenuoptionpathfindingrebakenavmesh.cpp llmeshrepository.cpp llmimetypes.cpp llmorphview.cpp @@ -398,7 +402,6 @@ set(viewer_SOURCE_FILES llpanelonlinestatus.cpp llpaneloutfitedit.cpp llpaneloutfitsinventory.cpp - llpanelpathfindingrebakenavmesh.cpp llpanelpeople.cpp llpanelpeoplemenus.cpp llpanelpermissions.cpp @@ -672,6 +675,7 @@ set(viewer_HEADER_FILES llagentwearables.h llagentwearablesfetch.h llanimstatelabels.h + llappcorehttp.h llappearance.h llappearancemgr.h llappviewer.h @@ -910,6 +914,7 @@ set(viewer_HEADER_FILES llmediactrl.h llmediadataclient.h llmemoryview.h + llmenuoptionpathfindingrebakenavmesh.h llmeshrepository.h llmimetypes.h llmorphview.h @@ -968,7 +973,6 @@ set(viewer_HEADER_FILES llpanelonlinestatus.h llpaneloutfitedit.h llpaneloutfitsinventory.h - llpanelpathfindingrebakenavmesh.h llpanelpeople.h llpanelpeoplemenus.h llpanelpermissions.h @@ -1565,9 +1569,9 @@ if (WINDOWS) set_target_properties(${VIEWER_BINARY_NAME} PROPERTIES # *TODO -reenable this once we get server usage sorted out - LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS ${TCMALLOC_LINK_FLAGS}" - LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO" - LINK_FLAGS_RELEASE "/FORCE:MULTIPLE /MAP\"secondlife-bin.MAP\" /OPT:REF" + LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS ${TCMALLOC_LINK_FLAGS} /LARGEADDRESSAWARE" + LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO /LARGEADDRESSAWARE" + LINK_FLAGS_RELEASE "/FORCE:MULTIPLE /MAP\"secondlife-bin.MAP\" /OPT:REF /LARGEADDRESSAWARE" ) if(USE_PRECOMPILED_HEADERS) set_target_properties( @@ -1812,6 +1816,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${HUNSPELL_LIBRARY} @@ -1956,6 +1961,12 @@ if (DARWIN) ) add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit mac-updater mac-crash-logger) + + if (ENABLE_SIGNING) + set(SIGNING_SETTING "--signature=${SIGNING_IDENTITY}") + else (ENABLE_SIGNING) + set(SIGNING_SETTING "") + endif (ENABLE_SIGNING) if (PACKAGE) add_custom_target(package ALL DEPENDS ${VIEWER_BINARY_NAME}) @@ -1975,6 +1986,7 @@ if (DARWIN) --login_channel=${VIEWER_LOGIN_CHANNEL} --source=${CMAKE_CURRENT_SOURCE_DIR} --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.touched + ${SIGNING_SETTING} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) @@ -2067,6 +2079,12 @@ if (LL_TESTS) LL_TEST_ADDITIONAL_LIBRARIES "${JSONCPP_LIBRARIES}" ) + set_source_files_properties( + lllogininstance.cpp + PROPERTIES + LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_SYSTEM_LIBRARY}" + ) + ################################################## # DISABLING PRECOMPILED HEADERS USAGE FOR TESTS ################################################## diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml index 711191128b..7ab7787d77 100644 --- a/indra/newview/app_settings/cmd_line.xml +++ b/indra/newview/app_settings/cmd_line.xml @@ -357,7 +357,7 @@ count 1 map-to - SkinFolder + SkinCurrent slurl diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml old mode 100644 new mode 100755 index 1bf773bb9e..7deb1284b9 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3269,6 +3269,17 @@ Boolean Value 0 + + FastCacheFetchEnabled + + Comment + Enable texture fast cache fetching if set + Persist + 1 + Type + Boolean + Value + 1 FeatureManagerHTTPTable @@ -4040,17 +4051,6 @@ Value 305 - HelpUseLocal - - Comment - If set, always use this for help: skins/default/html/[LANGUAGE]/help-offline/index.html - Persist - 0 - Type - Boolean - Value - 0 - HelpURLFormat Comment @@ -5755,7 +5755,7 @@ Type Boolean Value - 1 + 0 MemoryPrivatePoolSize @@ -8090,7 +8090,7 @@ Type F32 Value - 0 + -0.007 RenderShadowOffsetError @@ -10778,6 +10778,17 @@ Value 0 + TextureFetchConcurrency + + Comment + Maximum number of HTTP connections used for texture fetches + Persist + 1 + Type + U32 + Value + 0 + TextureFetchDebuggerEnabled Comment @@ -10789,6 +10800,83 @@ Value 0 + TextureFetchSource + + Comment + Debug use: Source to fetch textures + Persist + 1 + Type + S32 + Value + 0 + + TextureFetchUpdateHighPriority + + Comment + Number of high priority textures to update per frame + Persist + 1 + Type + S32 + Value + 32 + + TextureFetchUpdateMaxMediumPriority + + Comment + Maximum number of medium priority textures to update per frame + Persist + 1 + Type + S32 + Value + 256 + + TextureFetchUpdateMinMediumPriority + + Comment + Minimum number of medium priority textures to update per frame + Persist + 1 + Type + S32 + Value + 32 + + TextureFetchUpdatePriorityThreshold + + Comment + Threshold under which textures will be considered too low priority and skipped for update + Persist + 1 + Type + F32 + Value + 0.0 + + TextureFetchUpdateSkipLowPriority + + Comment + Flag indicating if we want to skip textures with too low of a priority + Persist + 1 + Type + Boolean + Value + 0 + + TextureFetchUpdatePriorities + + Comment + Number of priority texture to update per frame + Persist + 1 + Type + S32 + Value + 32 + TextureLoadFullRes Comment @@ -12285,6 +12373,17 @@ Value 0 + sourceid + + Comment + Identify referring agency to Linden web servers + Persist + 1 + Type + String + Value + + SpeakerParticipantDefaultOrder Comment @@ -13971,5 +14070,17 @@ 1.0 + + SimulateFBOFailure + + Comment + [DEBUG] Make allocateScreenBuffer return false. Used to test error handling. + Persist + 0 + Type + Boolean + Value + 0 + diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl index 46d2aa4877..bfd9b9b3eb 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl @@ -46,6 +46,6 @@ void main() frag_data[0] = vec4(diff.rgb, 0.0); frag_data[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl index 680eadb852..23c4ea2fff 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl @@ -52,5 +52,5 @@ void main() frag_data[1] = vertex_color.aaaa; // spec //frag_data[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested vec3 nvn = normalize(tnorm); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl index b2027d3a5d..c1fa9e4aac 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl @@ -49,6 +49,6 @@ void main() frag_data[0] = vec4(col.rgb, 0.0); frag_data[1] = vec4(0,0,0,0); // spec vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl index ead384b07c..4c68123fac 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl @@ -48,5 +48,5 @@ void main() frag_data[0] = vec4(col.rgb, 0.0); frag_data[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl index f73fa6f231..ad65c7d330 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl @@ -49,6 +49,6 @@ void main() frag_data[0] = vec4(col.rgb, 0.0); frag_data[1] = vec4(0,0,0,0); // spec vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl index 227aa2aae3..86390bdd83 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl @@ -42,6 +42,6 @@ void main() frag_data[1] = vertex_color.aaaa; // spec //frag_data[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl index d442e5403a..788b966af8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl @@ -41,5 +41,5 @@ void main() frag_data[1] = vertex_color.aaaa; // spec //frag_data[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/fxaaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/fxaaF.glsl index e02a7b405b..2cef8f2a5d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/fxaaF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/fxaaF.glsl @@ -2093,7 +2093,6 @@ uniform sampler2D diffuseMap; uniform vec2 rcp_screen_res; uniform vec4 rcp_frame_opt; uniform vec4 rcp_frame_opt2; -uniform vec2 screen_res; VARYING vec2 vary_fragcoord; VARYING vec2 vary_tc; diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl index 4d01eeb64e..7e79317543 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl @@ -79,7 +79,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm norm = normalize(norm); vec4 spec = texture2DRect(specularRect, frag.xy); vec3 diff = texture2DRect(diffuseRect, frag.xy).rgb; diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl index f671d5b750..75757b26c8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl @@ -84,7 +84,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm float da = dot(norm, lv); if (da < 0.0) { diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 66e3cf6d13..89448e2167 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -277,7 +277,7 @@ void main() float depth = texture2DRect(depthMap, tc.xy).r; vec3 pos = getPosition_d(tc, depth).xyz; vec3 norm = texture2DRect(normalMap, tc).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm float da = max(dot(norm.xyz, sun_dir.xyz), 0.0); diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl index 2422d73a3e..bac74cbbef 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl @@ -123,7 +123,7 @@ void main() vec4 pos = getPosition(pos_screen); vec3 norm = texture2DRect(normalMap, pos_screen).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm frag_color[0] = 1.0; frag_color[1] = calcAmbientOcclusion(pos, norm); diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl index 8a5e482e80..daf1cc7ea2 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl @@ -56,6 +56,6 @@ void main() frag_data[0] = vec4(outColor.rgb, 0.0); frag_data[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl index 6cf6106b51..da253846ef 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl @@ -48,5 +48,5 @@ void main() frag_data[0] = vec4(vertex_color.rgb*col.rgb, 0.0); frag_data[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - frag_data[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + frag_data[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index 42dc7c0980..3427d6db57 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -161,5 +161,5 @@ void main() frag_data[0] = vec4(color.rgb, 0.5); // diffuse frag_data[1] = vec4(0.5,0.5,0.5, 0.95); // speccolor*spec, spec - frag_data[2] = vec4(screenspacewavef.xy*0.5+0.5, screenspacewavef.z, screenspacewavef.z*0.5); // normalxyz, displace + frag_data[2] = vec4(screenspacewavef.xyz*0.5+0.5, screenspacewavef.z*0.5); // normalxyz, displace } diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index 8db4cb58cf..12706f130b 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -34,10 +34,10 @@ out vec4 frag_color; VARYING vec4 vertex_color; VARYING vec2 vary_texcoord0; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform mat4 shadow_matrix[6]; @@ -58,22 +58,22 @@ uniform float shadow_bias; uniform mat4 inv_proj; -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc) { stc.xyz /= stc.w; stc.z += shadow_bias; - - stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + + stc.x = floor(stc.x*shadow_res.x + fract(stc.y*shadow_res.y*12345))/shadow_res.x; // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here - float cs = shadow2DRect(shadowMap, stc.xyz).x; + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; - - return shadow*0.2; + + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + + return shadow*0.2; } @@ -99,8 +99,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos)*w; @@ -111,8 +110,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -123,8 +121,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -135,8 +132,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl index 33958a5010..228dc104ac 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl @@ -31,17 +31,16 @@ out vec4 frag_color; #define frag_color gl_FragColor #endif -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform sampler2D diffuseMap; uniform mat4 shadow_matrix[6]; uniform vec4 shadow_clip; uniform vec2 screen_res; -uniform vec2 shadow_res; vec3 atmosLighting(vec3 light); vec3 scaleSoftClip(vec3 light); @@ -54,6 +53,7 @@ VARYING vec3 vary_pointlight_col; VARYING vec2 vary_texcoord0; VARYING vec4 vertex_color; +uniform vec2 shadow_res; uniform float shadow_bias; uniform mat4 inv_proj; @@ -71,22 +71,22 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc) { stc.xyz /= stc.w; stc.z += shadow_bias; - stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + stc.x = floor(stc.x*shadow_res.x + fract(stc.y*12345))/shadow_res.x; // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here - float cs = shadow2DRect(shadowMap, stc.xyz).x; + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; + + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; - return shadow*0.2; + return shadow*0.2; } @@ -112,8 +112,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos)*w; @@ -124,8 +123,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -136,8 +134,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -148,8 +145,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl index 2093fc37dc..c3950a10e1 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl @@ -33,17 +33,16 @@ out vec4 frag_color; uniform float minimum_alpha; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform sampler2D diffuseMap; uniform mat4 shadow_matrix[6]; uniform vec4 shadow_clip; uniform vec2 screen_res; -uniform vec2 shadow_res; vec3 atmosLighting(vec3 light); vec3 scaleSoftClip(vec3 light); @@ -55,6 +54,8 @@ VARYING vec3 vary_position; VARYING vec3 vary_pointlight_col; VARYING vec2 vary_texcoord0; +uniform vec2 shadow_res; + uniform float shadow_bias; uniform mat4 inv_proj; @@ -72,20 +73,20 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc) { stc.xyz /= stc.w; stc.z += shadow_bias; - stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + stc.x = floor(stc.x*shadow_res.x + fract(stc.y*12345))/shadow_res.x; // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + float cs = shadow2D(shadowMap, stc.xyz).x; - float cs = shadow2DRect(shadowMap, stc.xyz).x; float shadow = cs; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; return shadow*0.2; } @@ -120,8 +121,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos)*w; @@ -132,8 +132,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -144,8 +143,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -156,8 +154,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl index f7f1f649ce..5621e47ab7 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl @@ -154,7 +154,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm norm = normalize(norm); float l_dist = -dot(lv, proj_n); diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index a137bea30f..9df9d75905 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -279,7 +279,7 @@ void main() float depth = texture2DRect(depthMap, tc.xy).r; vec3 pos = getPosition_d(tc, depth).xyz; vec3 norm = texture2DRect(normalMap, tc).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm float da = max(dot(norm.xyz, sun_dir.xyz), 0.0); diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl index ab077d9e02..6d6ad6d565 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl @@ -154,7 +154,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm norm = normalize(norm); float l_dist = -dot(lv, proj_n); diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index db3d760359..890486c4b1 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -35,10 +35,10 @@ out vec4 frag_color; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DShadow shadowMap4; uniform sampler2DShadow shadowMap5; @@ -55,10 +55,10 @@ VARYING vec2 vary_fragcoord; uniform mat4 inv_proj; uniform vec2 screen_res; -uniform vec2 shadow_res; uniform vec2 proj_shadow_res; uniform vec3 sun_dir; +uniform vec2 shadow_res; uniform float shadow_bias; uniform float shadow_offset; @@ -78,30 +78,31 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; - stc.z += shadow_bias*scl; + stc.z += shadow_bias; - stc.x = floor(stc.x + fract(pos_screen.y*0.666666666)); // add some jitter to X sample pos according to Y to disguise the snapping going on here + stc.x = floor(stc.x*shadow_res.x + fract(pos_screen.y*0.666666666))/shadow_res.x; // add some jitter to X sample pos according to Y to disguise the snapping going on here + float cs = shadow2D(shadowMap, stc.xyz).x; - float cs = shadow2DRect(shadowMap, stc.xyz).x; float shadow = cs; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, -1.5, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + - return shadow*0.2; + return shadow*0.2; } -float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfSpotShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; stc.z += spot_shadow_bias*scl; stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap - + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; @@ -125,7 +126,7 @@ void main() vec4 pos = getPosition(pos_screen); vec4 nmap4 = texture2DRect(normalMap, pos_screen); - nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm + nmap4 = vec4((nmap4.xyz-0.5)*2.0,nmap4.w); // unpack norm float displace = nmap4.w; vec3 norm = nmap4.xyz; @@ -162,8 +163,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w; @@ -174,8 +174,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -186,7 +185,6 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; @@ -198,7 +196,6 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; @@ -237,11 +234,11 @@ void main() //spotlight shadow 1 vec4 lpos = shadow_matrix[4]*spos; - frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen); + frag_color[2] = pcfSpotShadow(shadowMap4, lpos, 0.8, pos_screen); //spotlight shadow 2 lpos = shadow_matrix[5]*spos; - frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen); + frag_color[3] = pcfSpotShadow(shadowMap5, lpos, 0.8, pos_screen); //frag_color.rgb = pos.xyz; //frag_color.b = shadow; diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl index dfe108eb01..2dcd3d656f 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl @@ -34,10 +34,10 @@ out vec4 frag_color; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DShadow shadowMap4; uniform sampler2DShadow shadowMap5; uniform sampler2D noiseMap; @@ -55,10 +55,11 @@ VARYING vec2 vary_fragcoord; uniform mat4 inv_proj; uniform vec2 screen_res; -uniform vec2 shadow_res; uniform vec2 proj_shadow_res; uniform vec3 sun_dir; +uniform vec2 shadow_res; + uniform float shadow_bias; uniform float shadow_offset; @@ -139,30 +140,30 @@ float calcAmbientOcclusion(vec4 pos, vec3 norm) return min(ret, 1.0); } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; - stc.z += shadow_bias*scl; + stc.z += shadow_bias; - stc.x = floor(stc.x + fract(pos_screen.y*0.666666666)); + stc.x = floor(stc.x*shadow_res.x + fract(pos_screen.y*0.666666666))/shadow_res.x; + float cs = shadow2D(shadowMap, stc.xyz).x; - float cs = shadow2DRect(shadowMap, stc.xyz).x; float shadow = cs; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; - + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + return shadow*0.2; } -float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfSpotShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; stc.z += spot_shadow_bias*scl; stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap - + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; @@ -186,7 +187,7 @@ void main() vec4 pos = getPosition(pos_screen); vec4 nmap4 = texture2DRect(normalMap, pos_screen); - nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm + nmap4 = vec4((nmap4.xyz-0.5)*2.0,nmap4.w); // unpack norm float displace = nmap4.w; vec3 norm = nmap4.xyz; @@ -223,8 +224,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w; @@ -235,8 +235,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -247,8 +246,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -259,8 +257,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; @@ -298,11 +295,11 @@ void main() //spotlight shadow 1 vec4 lpos = shadow_matrix[4]*spos; - frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen); + frag_color[2] = pcfSpotShadow(shadowMap4, lpos, 0.8, pos_screen); //spotlight shadow 2 lpos = shadow_matrix[5]*spos; - frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen); + frag_color[3] = pcfSpotShadow(shadowMap5, lpos, 0.8, pos_screen); //frag_color.rgb = pos.xyz; //frag_color.b = shadow; diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index eeb632acaf..4030324ecb 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 32 +version 33 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -97,10 +97,10 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 @@ -129,16 +129,16 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// Mid Graphics Settings +// Medium Low Graphics Settings // -list Mid +list LowMid RenderAnisotropic 1 0 RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 0.5 @@ -159,14 +159,74 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// High Graphics Settings (purty) +// Medium Graphics Settings (standard) +// +list Mid +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// Medium High Graphics Settings (deferred enabled) +// +list MidHigh +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// High Graphics Settings (deferred + SSAO) // list High RenderAnisotropic 1 1 @@ -189,12 +249,43 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 -WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 2 +// +// High Ultra Graphics Settings (deferred + SSAO + shadows) +// +list HighUltra +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +RenderShadowDetail 1 2 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + + // // Ultra graphics (REALLY PURTY!) // @@ -230,6 +321,9 @@ RenderFSAASamples 1 2 // list Unknown RenderVBOEnable 1 0 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 0 Hardware (just old) @@ -244,17 +338,29 @@ list Class1 RenderVBOEnable 1 1 // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 // -// Class 3 Hardware (make it purty) +// Class 3 Hardware // list Class3 RenderVBOEnable 1 1 +// +// Class 4 Hardware +// +list Class4 +RenderVBOEnable 1 1 + +// +// Class 5 Hardware +// +list Class5 +RenderVBOEnable 1 1 + // // VRAM > 512MB // diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index 3a0e7e3697..6d5284c602 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -1,4 +1,4 @@ -version 27 +version 28 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -94,10 +94,10 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // @@ -125,16 +125,16 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// Mid Graphics Settings +// Medium Low Graphics Settings // -list Mid +list LowMid RenderAnisotropic 1 0 RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 0.5 @@ -143,9 +143,9 @@ RenderAvatarVP 1 1 RenderFarClip 1 96 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 8 -RenderLocalLights 1 1 RenderMaxPartCount 1 2048 RenderObjectBump 1 1 +RenderLocalLights 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 @@ -155,14 +155,74 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// High Graphics Settings (purty) +// Medium Graphics Settings (standard) +// +list Mid +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// Medium High Graphics Settings (deferred enabled) +// +list MidHigh +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// High Graphics Settings (deferred + SSAO) // list High RenderAnisotropic 1 1 @@ -173,9 +233,9 @@ RenderAvatarVP 1 1 RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 -RenderLocalLights 1 1 RenderMaxPartCount 1 4096 RenderObjectBump 1 1 +RenderLocalLights 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -185,10 +245,40 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 -WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// High Ultra Graphics Settings (deferred + SSAO + shadows) +// +list HighUltra +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +RenderShadowDetail 1 2 +WLSkyDetail 1 48 RenderFSAASamples 1 2 // @@ -226,6 +316,9 @@ RenderFSAASamples 1 2 // list Unknown RenderVBOEnable 1 0 +RenderShadowDetail 1 0 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 // // Class 0 Hardware (just old) @@ -240,17 +333,29 @@ list Class1 RenderVBOEnable 1 1 // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 // -// Class 3 Hardware (make it purty) +// Class 3 Hardware // list Class3 RenderVBOEnable 1 1 +// +// Class 4 Hardware +// +list Class4 +RenderVBOEnable 1 1 + +// +// Class 5 Hardware +// +list Class5 +RenderVBOEnable 1 1 + // // VRAM > 512MB // diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 96362ff4bb..1c0d45c11b 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 32 +version 35 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -96,10 +96,10 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // @@ -127,16 +127,16 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// Mid Graphics Settings +// Medium Low Graphics Settings // -list Mid +list LowMid RenderAnisotropic 1 0 RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 0.5 @@ -145,9 +145,9 @@ RenderAvatarVP 1 1 RenderFarClip 1 96 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 8 -RenderLocalLights 1 1 RenderMaxPartCount 1 2048 RenderObjectBump 1 1 +RenderLocalLights 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 @@ -157,14 +157,74 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// High Graphics Settings (purty) +// Medium Graphics Settings (standard) +// +list Mid +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// Medium High Graphics Settings (deferred enabled) +// +list MidHigh +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// High Graphics Settings (deferred + SSAO) // list High RenderAnisotropic 1 1 @@ -175,9 +235,9 @@ RenderAvatarVP 1 1 RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 -RenderLocalLights 1 1 RenderMaxPartCount 1 4096 RenderObjectBump 1 1 +RenderLocalLights 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -187,12 +247,43 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +RenderShadowDetail 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 2 RenderFSAASamples 1 2 +// +// High Ultra Graphics Settings (deferred + SSAO + shadows) +// +list HighUltra +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +RenderShadowDetail 1 2 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + + // // Ultra graphics (REALLY PURTY!) // @@ -242,16 +333,27 @@ list Class1 RenderVBOEnable 1 1 // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 // -// Class 3 Hardware (make it purty) +// Class 3 Hardware // list Class3 RenderVBOEnable 1 1 +// +// Class 4 Hardware +// +list Class4 +RenderVBOEnable 1 1 + +// +// Class 5 Hardware +// +list Class5 +RenderVBOEnable 1 1 // // No Pixel Shaders available diff --git a/indra/newview/featuretable_xp.txt b/indra/newview/featuretable_xp.txt index a945f7a693..68e09d010e 100644 --- a/indra/newview/featuretable_xp.txt +++ b/indra/newview/featuretable_xp.txt @@ -1,4 +1,4 @@ -version 31 +version 32 // The version number above should be implemented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -63,9 +63,9 @@ Disregard96DefaultDrawDistance 1 1 RenderTextureMemoryMultiple 1 1.0 RenderCompressTextures 1 1 RenderShaderLightingMaxLevel 1 3 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 -RenderShadowDetail 1 0 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +RenderShadowDetail 1 2 WatchdogDisabled 1 1 RenderUseStreamVBO 1 1 RenderFSAASamples 1 16 @@ -96,10 +96,10 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 0 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // @@ -127,16 +127,16 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// Mid Graphics Settings +// Medium Low Graphics Settings // -list Mid +list LowMid RenderAnisotropic 1 0 RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 0.5 @@ -145,9 +145,9 @@ RenderAvatarVP 1 1 RenderFarClip 1 96 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 8 -RenderLocalLights 1 1 RenderMaxPartCount 1 2048 RenderObjectBump 1 1 +RenderLocalLights 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 @@ -157,14 +157,74 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 0 -WLSkyDetail 1 48 RenderDeferred 1 0 RenderDeferredSSAO 1 0 RenderShadowDetail 1 0 +WLSkyDetail 1 48 RenderFSAASamples 1 0 // -// High Graphics Settings (purty) +// Medium Graphics Settings (standard) +// +list Mid +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 0 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// Medium High Graphics Settings (deferred enabled) +// +list MidHigh +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 0 +RenderShadowDetail 1 0 +WLSkyDetail 1 48 +RenderFSAASamples 1 2 + +// +// High Graphics Settings (deferred + SSAO) // list High RenderAnisotropic 1 1 @@ -175,9 +235,9 @@ RenderAvatarVP 1 1 RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 -RenderLocalLights 1 1 RenderMaxPartCount 1 4096 RenderObjectBump 1 1 +RenderLocalLights 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -187,10 +247,40 @@ RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 VertexShaderEnable 1 1 WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 +RenderShadowDetail 1 0 WLSkyDetail 1 48 -RenderDeferred 1 0 -RenderDeferredSSAO 1 0 +RenderFSAASamples 1 2 + +// +// High Ultra Graphics Settings (deferred + SSAO + shadows) +// +list HighUltra +RenderAnisotropic 1 1 +RenderAvatarCloth 1 0 +RenderAvatarLODFactor 1 1.0 +RenderAvatarPhysicsLODFactor 1 1.0 +RenderAvatarVP 1 1 +RenderFarClip 1 128 +RenderFlexTimeFactor 1 1.0 +RenderGlowResolutionPow 1 9 +RenderMaxPartCount 1 4096 +RenderObjectBump 1 1 +RenderLocalLights 1 1 +RenderReflectionDetail 1 0 +RenderTerrainDetail 1 1 +RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 +RenderTreeLODFactor 1 0.5 +RenderUseImpostors 1 1 +RenderVolumeLODFactor 1 1.125 +VertexShaderEnable 1 1 +WindLightUseAtmosShaders 1 1 +RenderDeferred 1 1 +RenderDeferredSSAO 1 1 RenderShadowDetail 1 2 +WLSkyDetail 1 48 RenderFSAASamples 1 2 // @@ -242,17 +332,29 @@ list Class1 RenderVBOEnable 1 1 // -// Class 2 Hardware (make it purty) +// Class 2 Hardware // list Class2 RenderVBOEnable 1 1 // -// Class 3 Hardware (make it purty) +// Class 3 Hardware // list Class3 RenderVBOEnable 1 1 +// +// Class 4 Hardware (deferred + SSAO) +// +list Class4 +RenderVBOEnable 1 1 + +// +// Class 5 Hardware +// +list Class5 +RenderVBOEnable 1 1 + // // VRAM > 512MB // diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 777d54a5c3..4c39014c8b 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -17,520 +17,582 @@ // // Format: // Fields are separated by one or more tab (not space) characters -// +// // // Class Numbers: // 0 - Defaults to low graphics settings. No shaders on by default // 1 - Defaults to mid graphics settings. Basic shaders on by default // 2 - Defaults to high graphics settings. Atmospherics on by default. -// 3 - Same as class 2 for now. +// 3 - Same as 2, but with lighting and shadows enabled. +// 4 - Same as 3, but with ambient occlusion enabled. +// 5 - Same as 4, but with shadows set to "Sun/Moon+Projectors." // // Supported Number: // 0 - We claim to not support this card. // 1 - We claim to support this card. // -3Dfx .*3Dfx.* 0 0 -3Dlabs .*3Dlabs.* 0 0 -ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 -ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 -ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 -ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 -ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 -ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 -ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 -ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 2 1 -ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 -ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 -ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 -ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 -ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 -ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 -ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 -ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 3 1 -ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 -ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 3 1 -ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 3 1 -ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 -ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 -ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 -ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 -ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 -ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 -ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 -ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 3 1 -ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 3 1 -ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 1 1 -ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 1 1 -ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 -ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 -ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 3 1 -ATI ASUS EAH6xxx .*ATI.*ASUS.*EAH6.* 3 1 -ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 3 1 -ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 -ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 3 1 -ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 -ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 2 1 -ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 2 1 -ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 2 1 -ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 -ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 1 1 -ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 -ATI Display Adapter .*ATI.*display adapter.* 0 1 -ATI FireGL 5200 .*ATI.*FireGL V52.* 0 1 -ATI FireGL 5xxx .*ATI.*FireGL V5.* 1 1 -ATI FireGL .*ATI.*Fire.*GL.* 0 1 -ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 -ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 -ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 -ATI FirePro M7820 .*ATI.*FirePro.*M78.* 3 1 -ATI FireMV .*ATI.*FireMV.* 0 1 -ATI Geforce 9500 GT .*ATI.*Geforce 9500 *GT.* 2 1 -ATI Geforce 9600 GT .*ATI.*Geforce 9600 *GT.* 2 1 -ATI Geforce 9800 GT .*ATI.*Geforce 9800 *GT.* 2 1 -ATI Generic .*ATI.*Generic.* 0 0 -ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 -ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 -ATI M52 .*ATI.*M52.* 1 1 -ATI M54 .*ATI.*M54.* 1 1 -ATI M56 .*ATI.*M56.* 1 1 -ATI M71 .*ATI.*M71.* 1 1 -ATI M72 .*ATI.*M72.* 1 1 -ATI M76 .*ATI.*M76.* 3 1 -ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 3 1 -ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 3 1 -ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 -ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 -ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 -ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 -ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 -ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 1 1 -ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 0 1 -ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 -ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 2 1 -ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 -ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 2 1 -ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 2 1 -ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 2 1 -ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 2 1 -ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 2 1 -ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 3 1 -ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 -ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 -ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 -ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 2 1 -ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 3 1 -ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 -ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 2 1 -ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 2 1 -ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 3 1 -ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 3 1 -ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 -ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 3 1 -ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 -ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 3 1 -ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 -ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 -ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 3 1 -ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 -ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 -ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 -ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 3 1 -ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 3 1 -ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 3 1 -ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 3 1 -ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 3 1 -ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 2 1 -ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 2 1 -ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 -ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 -ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 -ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 -ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 -ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 2 1 -ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 2 1 -ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 -ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 -ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 -ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 -ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 -ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 -ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 -ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 -ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 3 1 -ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 -ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 -ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 -ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 -ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 3 1 -ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 -ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 -ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 3 1 -ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 3 1 -ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 3 1 -ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 3 1 -ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 3 1 -ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 3 1 -ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 -ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 -ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 3 1 -ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 3 1 -ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 -ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 -ATI Radeon 3000 .*ATI.*Radeon 30.. 0 1 -ATI Radeon 3100 .*ATI.*Radeon 31.. 1 1 -ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 -ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 -ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 -ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 -ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 -ATI Radeon 9200 .*ATI.*Radeon 92.. 0 1 -ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 -ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 -ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 -ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 -ATI Radeon RV250 .*ATI.*RV250.* 0 1 -ATI Radeon RV600 .*ATI.*RV6.* 1 1 -ATI Radeon RX700 .*ATI.*RX70.* 1 1 -ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 -ATI RS880M .*ATI.*RS880M 1 1 -ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 -ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 -ATI Radeon X300 .*ATI.*Radeon *X3.* 0 1 -ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 -ATI Radeon X500 .*ATI.*Radeon ?X5.* 0 1 -ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 -ATI Radeon X700 .*ATI.*Radeon ?X7.* 1 1 -ATI Radeon X800 .*ATI.*Radeon ?X8.* 2 1 -ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 -ATI Radeon Xpress .*ATI.*Radeon Xpress.* 0 1 -ATI Rage 128 .*ATI.*Rage 128.* 0 1 -ATI R300 (9700) .*R300.* 1 1 -ATI R350 (9800) .*R350.* 1 1 -ATI R580 (X1900) .*R580.* 3 1 -ATI RC410 (Xpress 200) .*RC410.* 0 0 -ATI RS48x (Xpress 200x) .*RS48.* 0 0 -ATI RS600 (Xpress 3200) .*RS600.* 0 0 -ATI RV350 (9600) .*RV350.* 0 1 -ATI RV370 (X300) .*RV370.* 0 1 -ATI RV410 (X700) .*RV410.* 1 1 -ATI RV515 .*RV515.* 1 1 -ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 -ATI RV380 .*RV380.* 0 1 -ATI RV530 .*RV530.* 1 1 -ATI RX480 (Xpress 200P) .*RX480.* 0 1 -ATI RX700 .*RX700.* 1 1 -AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 -AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 -AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 -AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 -AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 -AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 -AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 -AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 -AMD PARK .*(AMD|ATI).*Park.* 3 1 -AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 -AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 -AMD RS780 (HD 3200) .*RS780.* 0 1 -AMD RS880 (HD 4200) .*RS880.* 1 1 -AMD RV610 (HD 2400) .*RV610.* 1 1 -AMD RV620 (HD 3400) .*RV620.* 1 1 -AMD RV630 (HD 2600) .*RV630.* 2 1 -AMD RV635 (HD 3600) .*RV635.* 3 1 -AMD RV670 (HD 3800) .*RV670.* 3 1 -AMD R680 (HD 3870 X2) .*R680.* 3 1 -AMD R700 (HD 4800 X2) .*R700.* 3 1 -AMD RV710 (HD 4300) .*RV710.* 1 1 -AMD RV730 (HD 4600) .*RV730.* 3 1 -AMD RV740 (HD 4700) .*RV740.* 3 1 -AMD RV770 (HD 4800) .*RV770.* 3 1 -AMD RV790 (HD 4800) .*RV790.* 3 1 -ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 -ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 -ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 -ATI FirePro 2000 .*ATI.*FirePro 2.* 1 1 -ATI FirePro 3000 .*ATI.*FirePro V3.* 1 1 -ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 -ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 -ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 -ATI FirePro M .*ATI.*FirePro M.* 3 1 -ATI Technologies .*ATI *Technologies.* 0 1 -// This entry is last to work around the "R300" driver problem. -ATI R300 (9700) .*R300.* 1 1 -ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 -Intel X3100 .*Intel.*X3100.* 0 1 -Intel 830M .*Intel.*830M 0 0 -Intel 845G .*Intel.*845G 0 0 -Intel 855GM .*Intel.*855GM 0 0 -Intel 865G .*Intel.*865G 0 0 -Intel 900 .*Intel.*900.*900 0 0 -Intel 915GM .*Intel.*915GM 0 0 -Intel 915G .*Intel.*915G 0 0 -Intel 945GM .*Intel.*945GM.* 0 1 -Intel 945G .*Intel.*945G.* 0 1 -Intel 950 .*Intel.*950.* 0 1 -Intel 965 .*Intel.*965.* 0 1 -Intel G33 .*Intel.*G33.* 0 0 -Intel G41 .*Intel.*G41.* 0 1 -Intel G45 .*Intel.*G45.* 0 1 -Intel Bear Lake .*Intel.*Bear Lake.* 0 0 -Intel Broadwater .*Intel.*Broadwater.* 0 0 -Intel Brookdale .*Intel.*Brookdale.* 0 0 -Intel Cantiga .*Intel.*Cantiga.* 0 0 -Intel Eaglelake .*Intel.*Eaglelake.* 0 1 -Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 0 1 -Intel HD Graphics .*Intel.*HD Graphics.* 2 1 -Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 -Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 -Intel Montara .*Intel.*Montara.* 0 0 -Intel Pineview .*Intel.*Pineview.* 0 1 -Intel Springdale .*Intel.*Springdale.* 0 0 -Intel HD Graphics 2000 .*Intel.*HD2000.* 1 1 -Intel HD Graphics 3000 .*Intel.*HD3000.* 2 1 -Matrox .*Matrox.* 0 0 -Mesa .*Mesa.* 0 0 -NVIDIA 205 .*NVIDIA .*GeForce 205.* 2 1 -NVIDIA 210 .*NVIDIA .*GeForce 210.* 2 1 -NVIDIA 310M .*NVIDIA .*GeForce 310M.* 1 1 -NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 -NVIDIA 315M .*NVIDIA .*GeForce 315M.* 2 1 -NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 -NVIDIA 320M .*NVIDIA .*GeForce 320M.* 2 1 -NVIDIA G100M .*NVIDIA .*100M.* 0 1 -NVIDIA G100 .*NVIDIA .*100.* 0 1 -NVIDIA G102M .*NVIDIA .*102M.* 0 1 -NVIDIA G103M .*NVIDIA .*103M.* 0 1 -NVIDIA G105M .*NVIDIA .*105M.* 0 1 -NVIDIA G 110M .*NVIDIA .*110M.* 0 1 -NVIDIA G 120M .*NVIDIA .*120M.* 1 1 -NVIDIA G 200 .*NVIDIA .*200(M)?.* 0 1 -NVIDIA G 205M .*NVIDIA .*205(M)?.* 0 1 -NVIDIA G 210 .*NVIDIA .*210(M)?.* 1 1 -NVIDIA 305M .*NVIDIA .*305(M)?.* 1 1 -NVIDIA G 310M .*NVIDIA .*310(M)?.* 2 1 -NVIDIA G 315 .*NVIDIA .*315(M)?.* 2 1 -NVIDIA G 320M .*NVIDIA .*320(M)?.* 2 1 -NVIDIA G 405 .*NVIDIA .*405(M)?.* 1 1 -NVIDIA G 410M .*NVIDIA .*410(M)?.* 1 1 -NVIDIA GT 120M .*NVIDIA .*GT *120(M)?.* 2 1 -NVIDIA GT 120 .*NVIDIA .*GT.*120 2 1 -NVIDIA GT 130M .*NVIDIA .*GT *130(M)?.* 2 1 -NVIDIA GT 140M .*NVIDIA .*GT *140(M)?.* 2 1 -NVIDIA GT 150M .*NVIDIA .*GT(S)? *150(M)?.* 2 1 -NVIDIA GT 160M .*NVIDIA .*GT *160(M)?.* 2 1 -NVIDIA GT 220M .*NVIDIA .*GT *220(M)?.* 2 1 -NVIDIA GT 230M .*NVIDIA .*GT *230(M)?.* 2 1 -NVIDIA GT 240M .*NVIDIA .*GT *240(M)?.* 2 1 -NVIDIA GT 250M .*NVIDIA .*GT *250(M)?.* 2 1 -NVIDIA GT 260M .*NVIDIA .*GT *260(M)?.* 2 1 -NVIDIA GT 320M .*NVIDIA .*GT *320(M)?.* 2 1 -NVIDIA GT 325M .*NVIDIA .*GT *325(M)?.* 0 1 -NVIDIA GT 330M .*NVIDIA .*GT *330(M)?.* 3 1 -NVIDIA GT 335M .*NVIDIA .*GT *335(M)?.* 1 1 -NVIDIA GT 340M .*NVIDIA .*GT *340(M)?.* 2 1 -NVIDIA GT 415M .*NVIDIA .*GT *415(M)?.* 2 1 -NVIDIA GT 420M .*NVIDIA .*GT *420(M)?.* 2 1 -NVIDIA GT 425M .*NVIDIA .*GT *425(M)?.* 3 1 -NVIDIA GT 430M .*NVIDIA .*GT *430(M)?.* 3 1 -NVIDIA GT 435M .*NVIDIA .*GT *435(M)?.* 3 1 -NVIDIA GT 440M .*NVIDIA .*GT *440(M)?.* 3 1 -NVIDIA GT 445M .*NVIDIA .*GT *445(M)?.* 3 1 -NVIDIA GT 450M .*NVIDIA .*GT *450(M)?.* 3 1 -NVIDIA GT 520M .*NVIDIA .*GT *52.(M)?.* 3 1 -NVIDIA GT 530M .*NVIDIA .*GT *530(M)?.* 3 1 -NVIDIA GT 540M .*NVIDIA .*GT *54.(M)?.* 3 1 -NVIDIA GT 550M .*NVIDIA .*GT *550(M)?.* 3 1 -NVIDIA GT 555M .*NVIDIA .*GT *555(M)?.* 3 1 -NVIDIA GTS 160M .*NVIDIA .*GT(S)? *160(M)?.* 2 1 -NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 3 1 -NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 3 1 -NVIDIA GTS 350M .*NVIDIA .*GTS *350M.* 3 1 -NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 3 1 -NVIDIA GTS 360 .*NVIDIA .*GTS *360.* 3 1 -NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 3 1 -NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 3 1 -NVIDIA GTX 275 .*NVIDIA .*GTX *275.* 3 1 -NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 3 1 -NVIDIA GTX 285 .*NVIDIA .*GTX *285.* 3 1 -NVIDIA GTX 280 .*NVIDIA .*GTX *280.* 3 1 -NVIDIA GTX 290 .*NVIDIA .*GTX *290.* 3 1 -NVIDIA GTX 295 .*NVIDIA .*GTX *295.* 3 1 -NVIDIA GTX 460M .*NVIDIA .*GTX *460M.* 3 1 -NVIDIA GTX 465 .*NVIDIA .*GTX *465.* 3 1 -NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 3 1 -NVIDIA GTX 470M .*NVIDIA .*GTX *470M.* 3 1 -NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 3 1 -NVIDIA GTX 480M .*NVIDIA .*GTX *480M.* 3 1 -NVIDIA GTX 485M .*NVIDIA .*GTX *485M.* 3 1 -NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 3 1 -NVIDIA GTX 530 .*NVIDIA .*GTX *53.* 3 1 -NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 3 1 -NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 3 1 -NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 3 1 -NVIDIA GTX 580M .*NVIDIA .*GTX *580M.* 3 1 -NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 3 1 -NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 3 1 -NVIDIA C51 .*NVIDIA .*C51.* 0 1 -NVIDIA G72 .*NVIDIA .*G72.* 1 1 -NVIDIA G73 .*NVIDIA .*G73.* 1 1 -NVIDIA G84 .*NVIDIA .*G84.* 2 1 -NVIDIA G86 .*NVIDIA .*G86.* 3 1 -NVIDIA G92 .*NVIDIA .*G92.* 3 1 -NVIDIA GeForce .*GeForce 256.* 0 0 -NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 -NVIDIA GeForce 3 .*GeForce ?3 ?.* 0 1 -NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 -NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 0 1 -NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 -NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 -NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 -NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 -NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 0 1 -NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 -NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 0 1 -NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 1 1 -NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 -NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 2 1 -NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 0 1 -NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 0 1 -NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 -NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 -NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 1 1 -NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 -NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 -NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 2 1 -NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 -NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 -NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 -NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 2 1 -NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 2 1 -NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 -NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 3 1 -NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 -NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 -NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 3 1 -NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 -NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 3 1 -NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 -NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 -NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 -NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 -NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 -NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 2 1 -NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 2 1 -NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 -NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 2 1 -NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 2 1 -NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 2 1 -NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 3 1 -NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 2 1 -NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 2 1 -NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 3 1 -NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 -NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 -NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 -NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 -NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 -NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 0 1 -NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 1 1 -NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 -NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 -NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 -NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 -NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 -NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 -NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 -NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 -NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 -NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 -NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 -NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 -NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 -NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 -NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 -NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 1 1 -NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 -NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 1 1 -NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 -NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 0 1 -NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 -NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 -NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 2 1 -NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 2 1 -NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 -NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 2 1 -NVIDIA D9M .*NVIDIA .*D9M.* 1 1 -NVIDIA G94 .*NVIDIA .*G94.* 3 1 -NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 -NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 -NVIDIA ION .*NVIDIA .*ION.* 2 1 -NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 -NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 -NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 -NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 -NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 -NVIDIA N10 .*NVIDIA .*N10.* 1 1 -NVIDIA GeForce PCX .*GeForce PCX.* 0 1 -NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 -NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 -NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 -NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 -NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 -NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 -NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 -NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 -NVIDIA nForce .*NVIDIA .*nForce.* 0 0 -NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 -NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 -NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 -NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 -NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 -NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 -NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 -NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 -NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 -NVIDIA Quadro2 .*Quadro2.* 0 1 -NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 -NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 -NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 -NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 -NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 -NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 -NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 -NVIDIA Quadro 400 .*Quadro.*400.* 2 1 -NVIDIA Quadro 600 .*Quadro.*600.* 2 1 -NVIDIA Quadro4 .*Quadro4.* 0 1 -NVIDIA Quadro DCC .*Quadro DCC.* 0 1 -NVIDIA Quadro CX .*Quadro.*CX.* 3 1 -NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 -NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 -NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 -NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 -NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 -NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 -NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 -NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 -NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 -NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 -NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 -NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 -NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 -NVIDIA Quadro FX .*Quadro FX.* 1 1 -NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 0 1 -NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 -NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 -NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 -NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 -NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 -NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 -NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 -NVIDIA RIVA TNT .*RIVA TNT.* 0 0 -S3 .*S3 Graphics.* 0 0 -SiS SiS.* 0 0 -Trident Trident.* 0 0 -Tungsten Graphics Tungsten.* 0 0 -XGI XGI.* 0 0 -VIA VIA.* 0 0 -Apple Generic Apple.*Generic.* 0 0 -Apple Software Renderer Apple.*Software Renderer.* 0 0 -Humper Humper.* 0 1 +3Dfx .*3Dfx.* 0 0 0 0 +3Dlabs .*3Dlabs.* 0 0 0 0 +ATI 3D-Analyze .*ATI.*3D-Analyze.* 0 0 0 0 +ATI All-in-Wonder 7500 .*ATI.*All-in-Wonder 75.* 0 1 0 0 +ATI All-in-Wonder 8500 .*ATI.*All-in-Wonder 85.* 0 1 0 0 +ATI All-in-Wonder 9200 .*ATI.*All-in-Wonder 92.* 0 1 0 0 +ATI All-in-Wonder 9xxx .*ATI.*All-in-Wonder 9.* 1 1 0 0 +ATI All-in-Wonder HD .*ATI.*All-in-Wonder HD.* 1 1 1 3.3 +ATI All-in-Wonder X600 .*ATI.*All-in-Wonder X6.* 1 1 0 0 +ATI All-in-Wonder X800 .*ATI.*All-in-Wonder X8.* 1 1 1 2.1 +ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3 1 0 0 +ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3 1 0 0 +ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1 1 0 0 +ATI All-in-Wonder Radeon .*ATI.*All-in-Wonder Radeon.* 0 1 0 0 +ATI ASUS ARES .*ATI.*ASUS.*ARES.* 3 1 0 0 +ATI ASUS A9xxx .*ATI.*ASUS.*A9.* 1 1 0 0 +ATI ASUS AH24xx .*ATI.*ASUS.*AH24.* 1 1 1 3.3 +ATI ASUS AH26xx .*ATI.*ASUS.*AH26.* 1 1 1 3.3 +ATI ASUS AH34xx .*ATI.*ASUS.*AH34.* 1 1 1 3.3 +ATI ASUS AH36xx .*ATI.*ASUS.*AH36.* 1 1 1 3.3 +ATI ASUS AH46xx .*ATI.*ASUS.*AH46.* 2 1 1 3.3 +ATI ASUS AX3xx .*ATI.*ASUS.*AX3.* 1 1 0 0 +ATI ASUS AX5xx .*ATI.*ASUS.*AX5.* 1 1 0 0 +ATI ASUS AX8xx .*ATI.*ASUS.*AX8.* 2 1 0 0 +ATI ASUS EAH24xx .*ATI.*ASUS.*EAH24.* 2 1 0 0 +ATI ASUS EAH26xx .*ATI.*ASUS.*EAH26.* 3 1 0 0 +ATI ASUS EAH29xx .*ATI.*ASUS.*EAH29.* 3 1 0 0 +ATI ASUS EAH34xx .*ATI.*ASUS.*EAH34.* 1 1 0 0 +ATI ASUS EAH36xx .*ATI.*ASUS.*EAH36.* 2 1 0 0 +ATI ASUS EAH38xx .*ATI.*ASUS.*EAH38.* 2 1 1 3.3 +ATI ASUS EAH43xx .*ATI.*ASUS.*EAH43.* 2 1 1 3.3 +ATI ASUS EAH45xx .*ATI.*ASUS.*EAH45.* 2 1 0 0 +ATI ASUS EAH48xx .*ATI.*ASUS.*EAH48.* 3 1 1 3.3 +ATI ASUS EAH57xx .*ATI.*ASUS.*EAH57.* 3 1 1 4.1 +ATI ASUS EAH58xx .*ATI.*ASUS.*EAH58.* 5 1 1 4.1 +ATI ASUS EAH62xx .*ATI.*ASUS.*EAH62.* 2 1 0 0 +ATI ASUS EAH63xx .*ATI.*ASUS.*EAH63.* 2 1 0 0 +ATI ASUS EAH64xx .*ATI.*ASUS.*EAH64.* 2 1 0 0 +ATI ASUS EAH65xx .*ATI.*ASUS.*EAH65.* 2 1 0 4.1 +ATI ASUS EAH66xx .*ATI.*ASUS.*EAH66.* 3 1 0 4.1 +ATI ASUS EAH67xx .*ATI.*ASUS.*EAH67.* 3 1 0 0 +ATI ASUS EAH68xx .*ATI.*ASUS.*EAH68.* 5 1 0 4 +ATI ASUS EAH69xx .*ATI.*ASUS.*EAH69.* 5 1 0 4.1 +ATI ASUS Radeon X1xxx .*ATI.*ASUS.*X1.* 2 1 1 2.1 +ATI Radeon X7xx .*ATI.*ASUS.*X7.* 1 1 0 0 +ATI Radeon X19xx .*ATI.*(Radeon|Diamond) X19.* ?.* 2 1 1 2.1 +ATI Radeon X18xx .*ATI.*(Radeon|Diamond) X18.* ?.* 3 1 1 2.1 +ATI Radeon X17xx .*ATI.*(Radeon|Diamond) X17.* ?.* 1 1 1 2.1 +ATI Radeon X16xx .*ATI.*(Radeon|Diamond) X16.* ?.* 1 1 1 2.1 +ATI Radeon X15xx .*ATI.*(Radeon|Diamond) X15.* ?.* 1 1 1 2.1 +ATI Radeon X13xx .*ATI.*(Radeon|Diamond) X13.* ?.* 1 1 1 2.1 +ATI Radeon X1xxx .*ATI.*(Radeon|Diamond) X1.. ?.* 0 1 1 2.1 +ATI Radeon X2xxx .*ATI.*(Radeon|Diamond) X2.. ?.* 1 1 1 2.1 +ATI Display Adapter .*ATI.*display adapter.* 1 1 1 4.1 +ATI FireGL 5200 .*ATI.*FireGL V52.* 1 1 1 2.1 +ATI FireGL 5xxx .*ATI.*FireGL V5.* 2 1 1 3.3 +ATI FireGL .*ATI.*Fire.*GL.* 4 1 1 4.2 +ATI FirePro M3900 .*ATI.*FirePro.*M39.* 2 1 0 0 +ATI FirePro M5800 .*ATI.*FirePro.*M58.* 3 1 0 0 +ATI FirePro M7740 .*ATI.*FirePro.*M77.* 3 1 0 0 +ATI FirePro M7820 .*ATI.*FirePro.*M78.* 5 1 1 4.2 +ATI FireMV .*ATI.*FireMV.* 0 1 1 1.3 +ATI Generic .*ATI.*Generic.* 0 0 0 0 +ATI Hercules 9800 .*ATI.*Hercules.*9800.* 1 1 0 0 +ATI IGP 340M .*ATI.*IGP.*340M.* 0 0 0 0 +ATI M52 .*ATI.*M52.* 1 1 0 0 +ATI M54 .*ATI.*M54.* 1 1 0 0 +ATI M56 .*ATI.*M56.* 1 1 0 0 +ATI M71 .*ATI.*M71.* 1 1 0 0 +ATI M72 .*ATI.*M72.* 1 1 0 0 +ATI M76 .*ATI.*M76.* 3 1 0 0 +ATI Radeon HD 64xx .*ATI.*AMD Radeon.* HD [67]4..[MG] 2 1 1 4.2 +ATI Radeon HD 65xx .*ATI.*AMD Radeon.* HD [67]5..[MG] 2 1 1 4.2 +ATI Radeon HD 66xx .*ATI.*AMD Radeon.* HD [67]6..[MG] 3 1 1 4.2 +ATI Radeon HD 7100 .*ATI.*AMD Radeon.* HD 71.* 2 1 0 0 +ATI Radeon HD 7200 .*ATI.*AMD Radeon.* HD 72.* 2 1 0 0 +ATI Radeon HD 7300 .*ATI.*AMD Radeon.* HD 73.* 2 1 0 4.2 +ATI Radeon HD 7400 .*ATI.*AMD Radeon.* HD 74.* 2 1 0 4.2 +ATI Radeon HD 7500 .*ATI.*AMD Radeon.* HD 75.* 3 1 1 4.2 +ATI Radeon HD 7600 .*ATI.*AMD Radeon.* HD 76.* 3 1 0 4.2 +ATI Radeon HD 7700 .*ATI.*AMD Radeon.* HD 77.* 4 1 1 4.2 +ATI Radeon HD 7800 .*ATI.*AMD Radeon.* HD 78.* 5 1 1 4.2 +ATI Radeon HD 7900 .*ATI.*AMD Radeon.* HD 79.* 5 1 1 4.2 +ATI ASUS HD7100 .*ATI.*ASUS.* HD71.* 2 1 0 0 +ATI ASUS HD7200 .*ATI.*ASUS.* HD72.* 2 1 0 0 +ATI ASUS HD7300 .*ATI.*ASUS.* HD73.* 2 1 0 0 +ATI ASUS HD7400 .*ATI.*ASUS.* HD74.* 2 1 0 0 +ATI ASUS HD7500 .*ATI.*ASUS.* HD75.* 3 1 1 4.2 +ATI ASUS HD7600 .*ATI.*ASUS.* HD76.* 3 1 0 0 +ATI ASUS HD7700 .*ATI.*ASUS.* HD77.* 4 1 1 4.2 +ATI ASUS HD7800 .*ATI.*ASUS.* HD78.* 5 1 1 4.2 +ATI ASUS HD7900 .*ATI.*ASUS.* HD79.* 5 1 1 4.2 +ATI Mobility Radeon 4100 .*ATI.*Mobility.*41.. 1 1 1 3.3 +ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 1 1.3 +ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 0 0 +ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 0 0 +ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 0 1 1 2.1 +ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 1 1 1 2.1 +ATI Mobility Radeon HD 530v .*ATI.*Mobility.*HD *530v.* 1 1 1 3.3 +ATI Mobility Radeon HD 540v .*ATI.*Mobility.*HD *540v.* 1 1 1 3.3 +ATI Mobility Radeon HD 545v .*ATI.*Mobility.*HD *545v.* 2 1 1 4 +ATI Mobility Radeon HD 550v .*ATI.*Mobility.*HD *550v.* 3 1 1 4 +ATI Mobility Radeon HD 560v .*ATI.*Mobility.*HD *560v.* 3 1 1 3.2 +ATI Mobility Radeon HD 565v .*ATI.*Mobility.*HD *565v.* 3 1 1 3.3 +ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD *23.* 0 1 1 2.1 +ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD *24.* 1 1 1 3.3 +ATI Mobility Radeon HD 2600 .*ATI.*Mobility.*HD *26.* 1 1 1 3.3 +ATI Mobility Radeon HD 2700 .*ATI.*Mobility.*HD *27.* 3 1 0 0 +ATI Mobility Radeon HD 3100 .*ATI.*Mobility.*HD *31.* 0 1 0 0 +ATI Mobility Radeon HD 3200 .*ATI.*Mobility.*HD *32.* 0 1 0 0 +ATI Mobility Radeon HD 3400 .*ATI.*Mobility.*HD *34.* 1 1 1 3.3 +ATI Mobility Radeon HD 3600 .*ATI.*Mobility.*HD *36.* 1 1 1 4 +ATI Mobility Radeon HD 3800 .*ATI.*Mobility.*HD *38.* 3 1 1 3.3 +ATI Mobility Radeon HD 4200 .*ATI.*Mobility.*HD *42.* 1 1 1 4 +ATI Mobility Radeon HD 4300 .*ATI.*Mobility.*HD *43.* 1 1 1 4 +ATI Mobility Radeon HD 4500 .*ATI.*Mobility.*HD *45.* 1 1 1 4 +ATI Mobility Radeon HD 4600 .*ATI.*Mobility.*HD *46.* 2 1 1 3.3 +ATI Mobility Radeon HD 4800 .*ATI.*Mobility.*HD *48.* 3 1 1 3.3 +ATI Mobility Radeon HD 5100 .*ATI.*Mobility.*HD *51.* 3 1 1 3.2 +ATI Mobility Radeon HD 5300 .*ATI.*Mobility.*HD *53.* 3 1 0 0 +ATI Mobility Radeon HD 5400 .*ATI.*Mobility.*HD *54.* 2 1 1 4.2 +ATI Mobility Radeon HD 5500 .*ATI.*Mobility.*HD *55.* 3 1 0 0 +ATI Mobility Radeon HD 5600 .*ATI.*Mobility.*HD *56.* 3 1 1 4.2 +ATI Mobility Radeon HD 5700 .*ATI.*Mobility.*HD *57.* 3 1 1 4.1 +ATI Mobility Radeon HD 6200 .*ATI.*Mobility.*HD *62.* 3 1 0 0 +ATI Mobility Radeon HD 6300 .*ATI.*Mobility.*HD *63.* 3 1 1 4.2 +ATI Mobility Radeon HD 6400M .*ATI.*Mobility.*HD *64.* 3 1 0 0 +ATI Mobility Radeon HD 6500M .*ATI.*Mobility.*HD *65.* 5 1 1 4.2 +ATI Mobility Radeon HD 6600M .*ATI.*Mobility.*HD *66.* 5 1 0 0 +ATI Mobility Radeon HD 6700M .*ATI.*Mobility.*HD *67.* 5 1 0 0 +ATI Mobility Radeon HD 6800M .*ATI.*Mobility.*HD *68.* 5 1 0 0 +ATI Mobility Radeon HD 6900M .*ATI.*Mobility.*HD *69.* 5 1 0 0 +ATI Radeon HD 2300 .*ATI.*Radeon HD *23.. 0 1 1 3.3 +ATI Radeon HD 2400 .*ATI.*Radeon HD *24.. 1 1 1 4 +ATI Radeon HD 2600 .*ATI.*Radeon HD *26.. 2 1 1 3.3 +ATI Radeon HD 2900 .*ATI.*Radeon HD *29.. 3 1 1 3.3 +ATI Radeon HD 3000 .*ATI.*Radeon HD *30.. 0 1 0 0 +ATI Radeon HD 3100 .*ATI.*Radeon HD *31.. 1 1 0 0 +ATI Radeon HD 3200 .*ATI.*Radeon HD *32.. 1 1 1 4 +ATI Radeon HD 3300 .*ATI.*Radeon HD *33.. 1 1 1 3.3 +ATI Radeon HD 3400 .*ATI.*Radeon HD *34.. 1 1 1 4 +ATI Radeon HD 3500 .*ATI.*Radeon HD *35.. 2 1 0 0 +ATI Radeon HD 3600 .*ATI.*Radeon HD *36.. 3 1 1 3.3 +ATI Radeon HD 3700 .*ATI.*Radeon HD *37.. 3 1 0 0 +ATI HD3700 .*ATI.* HD37.. 3 1 0 3.3 +ATI Radeon HD 3800 .*ATI.*Radeon HD *38.. 3 1 1 4 +ATI Radeon HD 4100 .*ATI.*Radeon HD *41.. 1 1 0 0 +ATI Radeon HD 4200 .*ATI.*Radeon HD *42.. 1 1 1 4 +ATI Radeon HD 4300 .*ATI.*Radeon HD *43.. 2 1 1 4 +ATI Radeon HD 4400 .*ATI.*Radeon HD *44.. 2 1 0 0 +ATI Radeon HD 4500 .*ATI.*Radeon HD *45.. 2 1 1 3.3 +ATI Radeon HD 4600 .*ATI.*Radeon HD *46.. 3 1 1 4 +ATI Radeon HD 4700 .*ATI.*Radeon HD *47.. 3 1 1 3.3 +ATI Radeon HD 4800 .*ATI.*Radeon HD *48.. 3 1 1 4 +ATI ASUS EAH5400 .*ATI.*ASUS EAH54.. 3 1 1 4.2 +ATI Radeon HD 5400 .*ATI.*Radeon HD *54.. 3 1 1 4.2 +ATI Radeon HD 5500 .*ATI.*Radeon HD *55.. 3 1 1 4.2 +ATI ASUS EAH5500 .*ATI.*ASUS EAH55.. 3 1 1 4.2 +ATI Radeon HD 5600 .*ATI.*Radeon HD *56.. 3 1 1 4.2 +ATI Radeon HD 5700 .*ATI.*Radeon HD *57.. 3 1 1 4.2 +ATI Radeon HD 5800 .*ATI.*Radeon HD *58.. 4 1 1 4.2 +ATI Radeon HD 5900 .*ATI.*Radeon HD *59.. 4 1 1 4.2 +ATI Radeon HD 6200 .*ATI.*Radeon HD *62.. 0 1 1 4.2 +ATI Radeon HD 6300 .*ATI.*Radeon HD *63.. 1 1 1 4.2 +ATI Radeon HD 6400 .*ATI.*Radeon HD *64.. 3 1 1 4.2 +ATI Radeon HD 6500 .*ATI.*Radeon HD *65.. 3 1 1 4.2 +ATI Radeon HD 6600 .*ATI.*Radeon HD *66.. 3 1 1 4.2 +ATI Radeon HD 6700 .*ATI.*Radeon HD *67.. 3 1 1 4.2 +ATI Radeon HD 6800 .*ATI.*Radeon HD *68.. 4 1 1 4.2 +ATI Radeon HD 6900 .*ATI.*Radeon HD *69.. 5 1 1 4.2 +ATI Radeon OpenGL .*ATI.*Radeon OpenGL.* 0 0 0 0 +ATI Radeon 2100 .*ATI.*Radeon 21.. 0 1 1 2.1 +ATI Radeon 3000 .*ATI.*Radeon 30.. 1 1 1 4 +ATI Radeon 3100 .*ATI.*Radeon 31.. 0 1 1 3.3 +ATI Radeon 5xxx .*ATI.*Radeon 5... 3 1 0 0 +ATI Radeon 7xxx .*ATI.*Radeon 7... 0 1 1 2 +ATI Radeon 8xxx .*ATI.*Radeon 8... 0 1 0 0 +ATI Radeon 9000 .*ATI.*Radeon 90.. 0 1 1 1.3 +ATI Radeon 9100 .*ATI.*Radeon 91.. 0 1 0 0 +ATI Radeon 9200 .*ATI.*Radeon 92.. 0 1 1 1.3 +ATI Radeon 9500 .*ATI.*Radeon 95.. 0 1 1 2.1 +ATI Radeon 9600 .*ATI.*Radeon 96.. 0 1 1 2.1 +ATI Radeon 9700 .*ATI.*Radeon 97.. 1 1 0 0 +ATI Radeon 9800 .*ATI.*Radeon 98.. 1 1 1 2.1 +ATI Radeon RV250 .*ATI.*RV250.* 0 1 0 0 +ATI Radeon RV600 .*ATI.*RV6.* 1 1 0 0 +ATI Radeon RX700 .*ATI.*RX70.* 1 1 0 0 +ATI Radeon RX800 .*ATI.*Radeon *RX80.* 2 1 0 0 +ATI RS880M .*ATI.*RS880M 1 1 0 0 +ATI Radeon RX9550 .*ATI.*RX9550.* 1 1 0 0 +ATI Radeon VE .*ATI.*Radeon.*VE.* 0 0 0 0 +ATI Radeon X300 .*ATI.*Radeon *X3.* 1 1 1 2.1 +ATI Radeon X400 .*ATI.*Radeon ?X4.* 0 1 0 0 +ATI Radeon X500 .*ATI.*Radeon ?X5.* 1 1 1 2.1 +ATI Radeon X600 .*ATI.*Radeon ?X6.* 1 1 1 2.1 +ATI Radeon X700 .*ATI.*Radeon ?X7.* 2 1 1 2.1 +ATI Radeon X800 .*ATI.*Radeon ?X8.* 1 1 1 2.1 +ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1 0 0 +ATI Radeon Xpress .*ATI.*Radeon Xpress.* 0 1 1 2.1 +ATI Rage 128 .*ATI.*Rage 128.* 0 1 0 0 +ATI R300 (9700) .*R300.* 0 1 1 2.1 +ATI R350 (9800) .*R350.* 1 1 0 0 +ATI R580 (X1900) .*R580.* 3 1 0 0 +ATI RC410 (Xpress 200) .*RC410.* 0 0 0 0 +ATI RS48x (Xpress 200x) .*RS48.* 0 0 0 0 +ATI RS600 (Xpress 3200) .*RS600.* 0 0 0 0 +ATI RV350 (9600) .*RV350.* 0 1 0 0 +ATI RV370 (X300) .*RV370.* 0 1 0 0 +ATI RV410 (X700) .*RV410.* 1 1 0 0 +ATI RV515 .*RV515.* 1 1 0 0 +ATI RV570 (X1900 GT/PRO) .*RV570.* 3 1 0 0 +ATI RV380 .*RV380.* 0 1 0 0 +ATI RV530 .*RV530.* 1 1 0 0 +ATI RX480 (Xpress 200P) .*RX480.* 0 1 0 0 +ATI RX700 .*RX700.* 1 1 0 0 +AMD ANTILLES (HD 6990) .*(AMD|ATI).*Antilles.* 3 1 0 0 +AMD BARTS (HD 6800) .*(AMD|ATI).*Barts.* 3 1 1 2.1 +AMD CAICOS (HD 6400) .*(AMD|ATI).*Caicos.* 3 1 0 0 +AMD CAYMAN (HD 6900) .*(AMD|ATI).*(Cayman|CAYMAM).* 3 1 0 0 +AMD CEDAR (HD 5450) .*(AMD|ATI).*Cedar.* 2 1 0 0 +AMD CYPRESS (HD 5800) .*(AMD|ATI).*Cypress.* 3 1 0 0 +AMD HEMLOCK (HD 5970) .*(AMD|ATI).*Hemlock.* 3 1 0 0 +AMD JUNIPER (HD 5700) .*(AMD|ATI).*Juniper.* 3 1 0 0 +AMD PARK .*(AMD|ATI).*Park.* 3 1 0 0 +AMD REDWOOD (HD 5500/5600) .*(AMD|ATI).*Redwood.* 3 1 0 0 +AMD TURKS (HD 6500/6600) .*(AMD|ATI).*Turks.* 3 1 0 0 +AMD RS780 (HD 3200) .*RS780.* 0 1 1 2.1 +AMD RS880 (HD 4200) .*RS880.* 0 1 1 3.2 +AMD RV610 (HD 2400) .*RV610.* 1 1 0 0 +AMD RV620 (HD 3400) .*RV620.* 1 1 0 0 +AMD RV630 (HD 2600) .*RV630.* 2 1 0 0 +AMD RV635 (HD 3600) .*RV635.* 3 1 0 0 +AMD RV670 (HD 3800) .*RV670.* 3 1 0 0 +AMD R680 (HD 3870 X2) .*R680.* 3 1 0 0 +AMD R700 (HD 4800 X2) .*R700.* 3 1 0 0 +AMD RV710 (HD 4300) .*RV710.* 0 1 1 1.4 +AMD RV730 (HD 4600) .*RV730.* 3 1 0 0 +AMD RV740 (HD 4700) .*RV740.* 3 1 0 0 +AMD RV770 (HD 4800) .*RV770.* 3 1 0 0 +AMD RV790 (HD 4800) .*RV790.* 3 1 0 0 +ATI 760G/Radeon 3000 .*ATI.*AMD 760G.* 1 1 1 3.3 +ATI 780L/Radeon 3000 .*ATI.*AMD 780L.* 1 1 0 0 +ATI Radeon DDR .*ATI.*Radeon ?DDR.* 0 1 0 0 +ATI FirePro 2000 .*ATI.*FirePro 2.* 2 1 1 4.1 +ATI FirePro 3000 .*ATI.*FirePro V3.* 2 1 0 0 +ATI FirePro 4000 .*ATI.*FirePro V4.* 2 1 0 0 +ATI FirePro 5000 .*ATI.*FirePro V5.* 3 1 0 0 +ATI FirePro 7000 .*ATI.*FirePro V7.* 3 1 0 0 +ATI FirePro M .*ATI.*FirePro M.* 3 1 1 4.2 +ATI R300 (9700) .*R300.* 0 1 1 2.1 +ATI Radeon .*ATI.*(Diamond|Radeon).* 0 1 0 4.2 +Intel X3100 .*Intel.*X3100.* 1 1 1 2.1 +Intel GMA 3600 .*Intel.* 3600.* 0 1 1 3 +Intel 830M .*Intel.*830M 0 0 0 0 +Intel 845G .*Intel.*845G 0 0 1 1.4 +Intel 855GM .*Intel.*855GM 0 0 1 1.4 +Intel 865G .*Intel.*865G 0 0 1 1.4 +Intel 900 .*Intel.*900.*900 0 0 0 0 +Intel 915GM .*Intel.*915GM 0 0 1 1.4 +Intel 915G .*Intel.*915G 0 0 1 1.4 +Intel 945GM .*Intel.*945GM.* 0 1 1 1.4 +Intel 945G .*Intel.*945G.* 0 1 1 1.4 +Intel 950 .*Intel.*950.* 0 1 1 1.4 +Intel 965 .*Intel.*965.* 0 1 1 2.1 +Intel G33 .*Intel.*G33.* 1 0 1 1.4 +Intel G41 .*Intel.*G41.* 1 1 1 2.1 +Intel G45 .*Intel.*G45.* 1 1 1 2.1 +Intel Bear Lake .*Intel.*Bear Lake.* 1 0 1 1.4 +Intel Broadwater .*Intel.*Broadwater.* 0 0 1 1.4 +Intel Brookdale .*Intel.*Brookdale.* 0 0 1 1.3 +Intel Cantiga .*Intel.*Cantiga.* 0 0 1 2 +Intel Eaglelake .*Intel.*Eaglelake.* 1 1 1 2 +Intel Graphics Media HD .*Intel.*Graphics Media.*HD.* 1 1 1 2.1 +Intel HD Graphics 2000 .*Intel.*HD Graphics 2.* 2 1 0 4 +Intel HD Graphics 3000 .*Intel.*HD Graphics 3.* 3 1 1 3.1 +Intel HD Graphics 4000 .*Intel.*HD Graphics 4.* 3 1 1 4 +Intel HD2000 .*Intel.*HD2000.* 2 1 0 0 +Intel HD3000 .*Intel.*HD3000.* 3 1 0 0 +Intel HD Graphics .*Intel.*HD Graphics.* 2 1 1 4 +Intel Mobile 4 Series .*Intel.*Mobile.* 4 Series.* 0 1 1 2.1 +Intel 4 Series Internal .*Intel.* 4 Series Internal.* 1 1 1 2.1 +Intel Media Graphics HD .*Intel.*Media Graphics HD.* 0 1 0 0 +Intel Montara .*Intel.*Montara.* 0 0 1 1.3 +Intel Pineview .*Intel.*Pineview.* 0 1 1 1.4 +Intel Springdale .*Intel.*Springdale.* 0 0 1 1.3 +Intel Grantsdale .*Intel.*Grantsdale.* 1 1 0 0 +Intel Q45/Q43 .*Intel.*Q4.* 1 1 1 2.1 +Intel B45/B43 .*Intel.*B4.* 1 1 1 2.1 +Intel 3D-Analyze .*Intel.*3D-Analyze.* 2 1 0 0 +Matrox .*Matrox.* 0 0 0 0 +Mesa .*Mesa.* 1 0 1 2.1 +Gallium .*Gallium.* 1 1 1 2.1 +NVIDIA G100M .*NVIDIA .*100M.* 4 1 1 3.3 +NVIDIA G102M .*NVIDIA .*102M.* 1 1 1 3.3 +NVIDIA G103M .*NVIDIA .*103M.* 2 1 1 3.3 +NVIDIA G105M .*NVIDIA .*105M.* 2 1 1 3.3 +NVIDIA G 110M .*NVIDIA .*110M.* 1 1 1 3.3 +NVIDIA G 120M .*NVIDIA .*120M.* 1 1 1 3.3 +NVIDIA G 205M .*NVIDIA .*205M.* 1 1 0 0 +NVIDIA G 410M .*NVIDIA .*410M.* 3 1 1 4.2 +NVIDIA GT 120M .*NVIDIA .*GT *12*M.* 3 1 1 3.3 +NVIDIA GT 130M .*NVIDIA .*GT *13*M.* 3 1 1 3.3 +NVIDIA GT 140M .*NVIDIA .*GT *14*M.* 3 1 1 3.3 +NVIDIA GT 150M .*NVIDIA .*GTS *15*M.* 2 1 0 0 +NVIDIA GTS 160M .*NVIDIA .*GTS *16*M.* 2 1 0 0 +NVIDIA G210M .*NVIDIA .*G21*M.* 3 1 0 0 +NVIDIA GT 220M .*NVIDIA .*GT *22*M.* 3 1 1 3.3 +NVIDIA GT 230M .*NVIDIA .*GT *23*M.* 3 1 1 3.3 +NVIDIA GT 240M .*NVIDIA .*GT *24*M.* 3 1 1 3.3 +NVIDIA GTS 250M .*NVIDIA .*GTS *25*M.* 3 1 0 0 +NVIDIA GTS 260M .*NVIDIA .*GTS *26*M.* 3 1 0 0 +NVIDIA GTX 260M .*NVIDIA .*GTX *26*M.* 3 1 0 0 +NVIDIA GTX 270M .*NVIDIA .*GTX *27*M.* 3 1 0 0 +NVIDIA GTX 280M .*NVIDIA .*GTX *28*M.* 3 1 0 0 +NVIDIA 300M .*NVIDIA .*30*M.* 3 1 1 4.2 +NVIDIA G 310M .*NVIDIA .*31*M.* 2 1 0 0 +NVIDIA GT 320M .*NVIDIA .*GT *32*M.* 3 1 0 0 +NVIDIA GT 325M .*NVIDIA .*GT *32*M.* 3 1 1 3.3 +NVIDIA GT 330M .*NVIDIA .*GT *33*M.* 3 1 1 3.3 +NVIDIA GT 340M .*NVIDIA .*GT *34*M.* 4 1 1 3.3 +NVIDIA GTS 350M .*NVIDIA .*GTS *35*M.* 4 1 1 3.3 +NVIDIA GTS 360M .*NVIDIA .*GTS *360M.* 5 1 1 3.3 +NVIDIA 405M .*NVIDIA .* 40*M.* 2 1 0 4.2 +NVIDIA 410M .*NVIDIA .* 41*M.* 3 1 0 0 +NVIDIA GT 415M .*NVIDIA .*GT *41*M.* 3 1 1 4.2 +NVIDIA GT 420M .*NVIDIA .*GT *42*M.* 3 1 1 4.2 +NVIDIA GT 430M .*NVIDIA .*GT *43*M.* 3 1 1 4.2 +NVIDIA GT 440M .*NVIDIA .*GT *44*M.* 3 1 1 4.2 +NVIDIA GT 450M .*NVIDIA .*GT *45*M.* 3 1 0 0 +NVIDIA GTX 460M .*NVIDIA .*GTX *46*M.* 4 1 1 4.2 +NVIDIA GTX 470M .*NVIDIA .*GTX *47*M.* 3 1 0 0 +NVIDIA GTX 480M .*NVIDIA .*GTX *48*M.* 3 1 1 4.2 +NVIDIA GT 520M .*NVIDIA .*GT *52*M.* 3 1 1 4.2 +NVIDIA GT 530M .*NVIDIA .*GT *53*M.* 3 1 1 4.2 +NVIDIA GT 540M .*NVIDIA .*GT *54*M.* 3 1 1 4.2 +NVIDIA GT 550M .*NVIDIA .*GT *55*M.* 3 1 1 4.2 +NVIDIA GTX 560M .*NVIDIA .*GTX *56*M.* 3 1 0 0 +NVIDIA GTX 570M .*NVIDIA .*GTX *57*M.* 5 1 0 0 +NVIDIA GTX 580M .*NVIDIA .*GTX *58*M.* 5 1 1 4.2 +NVIDIA 610M .*NVIDIA.* 61*M.* 3 1 1 4.2 +NVIDIA GT 620M .*NVIDIA .*GT *62*M.* 3 1 0 0 +NVIDIA GT 630M .*NVIDIA .*GT *63*M.* 3 1 0 0 +NVIDIA GT 640M .*NVIDIA .*GT *64*M.* 3 1 0 0 +NVIDIA GT 650M .*NVIDIA .*GT *65*M.* 3 1 0 0 +NVIDIA GTX 660M .*NVIDIA .*GTX *66*M.* 5 1 0 0 +NVIDIA GTX 670M .*NVIDIA .*GTX *67*M.* 5 1 1 4.2 +NVIDIA GTX 680M .*NVIDIA .*GTX *68*M.* 5 1 0 0 +NVIDIA GTX 690M .*NVIDIA .*GTX *69*M.* 5 1 0 0 +NVIDIA G100 .*NVIDIA .*G10.* 3 1 1 4.2 +NVIDIA GT 120 .*NVIDIA .*GT *12.* 2 1 0 3 +NVIDIA GT 130 .*NVIDIA .*GT *13.* 2 1 0 3.3 +NVIDIA GTS 150 .*NVIDIA .*GTS *15.* 2 1 0 0 +NVIDIA 200 .*NVIDIA .*GeForce 20.* 2 1 1 3.3 +NVIDIA G200 .*NVIDIA .*GeForce G20.* 2 1 1 3.3 +NVIDIA G210 .*NVIDIA .*GeForce G210.* 3 1 1 3.3 +NVIDIA 210 .*NVIDIA .*GeForce 210.* 3 1 1 3.3 +NVIDIA GT 220 .*NVIDIA .*GT *22.* 2 1 1 3.3 +NVIDIA GT 230 .*NVIDIA .*GT *23.* 2 1 1 3.3 +NVIDIA GT 240 .*NVIDIA .*GT *24.* 4 1 1 3.3 +NVIDIA GTS 240 .*NVIDIA .*GTS *24.* 4 1 1 3.3 +NVIDIA GTS 250 .*NVIDIA .*GTS *25.* 4 1 1 3.3 +NVIDIA GTX 260 .*NVIDIA .*GTX *26.* 4 1 1 3.3 +NVIDIA GTX 270 .*NVIDIA .*GTX *27.* 4 1 0 3.3 +NVIDIA GTX 280 .*NVIDIA .*GTX *28.* 4 1 1 3.3 +NVIDIA GTX 290 .*NVIDIA .*GTX *29.* 5 1 0 3.3 +NVIDIA 310 .*NVIDIA .*GeForce 310.* 3 1 1 3.3 +NVIDIA 315 .*NVIDIA .*GeForce 315.* 3 1 1 3.3 +NVIDIA GT 320 .*NVIDIA .*GT *32.* 3 1 0 3.3 +NVIDIA GT 330 .*NVIDIA .*GT *33.* 3 1 0 3.3 +NVIDIA GT 340 .*NVIDIA .*GT *34.* 3 1 0 0 +NVIDIA 405 .*NVIDIA .* 405.* 3 1 0 3.3 +NVIDIA GT 420 .*NVIDIA .*GT *42.* 3 1 1 4.2 +NVIDIA GT 430 .*NVIDIA .*GT *43.* 3 1 1 4.2 +NVIDIA GT 440 .*NVIDIA .*GT *44.* 4 1 0 4.2 +NVIDIA GTS 450 .*NVIDIA .*GTS *45.* 4 1 1 4.2 +NVIDIA GTX 460 .*NVIDIA .*GTX *46.* 5 1 1 4.3 +NVIDIA GTX 470 .*NVIDIA .*GTX *47.* 5 1 1 4.2 +NVIDIA GTX 480 .*NVIDIA .*GTX *48.* 5 1 1 4.2 +NVIDIA 510 .*NVIDIA .* 510.* 3 1 0 0 +NVIDIA GT 520 .*NVIDIA .*GT *52.* 3 1 1 4.2 +NVIDIA GT 530 .*NVIDIA .*GT *53.* 3 1 1 4.2 +NVIDIA GT 540 .*NVIDIA .*GT *54.* 3 1 1 4.2 +NVIDIA GTX 550 .*NVIDIA .*GTX *55.* 5 1 1 4.3 +NVIDIA GTX 560 .*NVIDIA .*GTX *56.* 5 1 1 4.2 +NVIDIA GTX 570 .*NVIDIA .*GTX *57.* 5 1 1 4.2 +NVIDIA GTX 580 .*NVIDIA .*GTX *58.* 5 1 1 4.3 +NVIDIA GTX 590 .*NVIDIA .*GTX *59.* 5 1 1 4.2 +NVIDIA GT 610 .*NVIDIA .*GT *61.* 3 1 1 4.2 +NVIDIA GT 620 .*NVIDIA .*GT *62.* 3 1 0 4.2 +NVIDIA GT 630 .*NVIDIA .*GT *63.* 3 1 0 4.2 +NVIDIA GT 640 .*NVIDIA .*GT *64.* 3 1 0 4.3 +NVIDIA GT 650 .*NVIDIA .*GT *65.* 3 1 1 4.2 +NVIDIA GTX 650 .*NVIDIA .*GTX *65.* 3 1 1 4.2 +NVIDIA GTX 660 .*NVIDIA .*GTX *66.* 5 1 0 4.3 +NVIDIA GTX 670 .*NVIDIA .*GTX *67.* 5 1 1 4.2 +NVIDIA GTX 680 .*NVIDIA .*GTX *68.* 5 1 1 4.2 +NVIDIA GTX 690 .*NVIDIA .*GTX *69.* 5 1 1 4.2 +NVIDIA C51 .*NVIDIA .*C51.* 0 1 1 2 +NVIDIA G72 .*NVIDIA .*G72.* 1 1 0 0 +NVIDIA G73 .*NVIDIA .*G73.* 1 1 0 0 +NVIDIA G84 .*NVIDIA .*G84.* 2 1 0 0 +NVIDIA G86 .*NVIDIA .*G86.* 3 1 0 0 +NVIDIA G92 .*NVIDIA .*G92.* 3 1 0 0 +NVIDIA GeForce .*GeForce 256.* 0 0 0 0 +NVIDIA GeForce 2 .*GeForce ?2 ?.* 0 1 1 1.5 +NVIDIA GeForce 3 .*GeForce ?3 ?.* 2 1 1 2.1 +NVIDIA GeForce 3 Ti .*GeForce ?3 Ti.* 0 1 0 0 +NVIDIA GeForce 4 .*NVIDIA .*GeForce ?4.* 0 1 1 1.5 +NVIDIA GeForce 4 Go .*NVIDIA .*GeForce ?4.*Go.* 0 1 0 0 +NVIDIA GeForce 4 MX .*NVIDIA .*GeForce ?4 MX.* 0 1 0 0 +NVIDIA GeForce 4 PCX .*NVIDIA .*GeForce ?4 PCX.* 0 1 0 0 +NVIDIA GeForce 4 Ti .*NVIDIA .*GeForce ?4 Ti.* 0 1 0 0 +NVIDIA GeForce 6100 .*NVIDIA .*GeForce 61.* 3 1 1 4.2 +NVIDIA GeForce 6200 .*NVIDIA .*GeForce 62.* 0 1 1 2.1 +NVIDIA GeForce 6500 .*NVIDIA .*GeForce 65.* 1 1 1 2.1 +NVIDIA GeForce 6600 .*NVIDIA .*GeForce 66.* 2 1 1 2.1 +NVIDIA GeForce 6700 .*NVIDIA .*GeForce 67.* 2 1 1 2.1 +NVIDIA GeForce 6800 .*NVIDIA .*GeForce 68.* 1 1 1 2.1 +NVIDIA GeForce 7000 .*NVIDIA .*GeForce 70.* 1 1 1 2.1 +NVIDIA GeForce 7100 .*NVIDIA .*GeForce 71.* 1 1 1 2.1 +NVIDIA GeForce 7200 .*NVIDIA .*GeForce 72.* 1 1 0 0 +NVIDIA GeForce 7300 .*NVIDIA .*GeForce 73.* 1 1 1 2.1 +NVIDIA GeForce 7500 .*NVIDIA .*GeForce 75.* 2 1 1 2.1 +NVIDIA GeForce 7600 .*NVIDIA .*GeForce 76.* 2 1 1 2.1 +NVIDIA GeForce 7800 .*NVIDIA .*GeForce 78.* 2 1 1 2.1 +NVIDIA GeForce 7900 .*NVIDIA .*GeForce 79.* 3 1 1 2.1 +NVIDIA GeForce 8100 .*NVIDIA .*GeForce 81.* 1 1 0 0 +NVIDIA GeForce 8200M .*NVIDIA .*GeForce 8200M.* 1 1 0 3.3 +NVIDIA GeForce 8200 .*NVIDIA .*GeForce 82.* 1 1 0 2.1 +NVIDIA GeForce 8300 .*NVIDIA .*GeForce 83.* 3 1 1 3.3 +NVIDIA GeForce 8400M .*NVIDIA .*GeForce 8400M.* 1 1 1 3.3 +NVIDIA GeForce 8400 .*NVIDIA .*GeForce 84.* 2 1 1 3.3 +NVIDIA GeForce 8500 .*NVIDIA .*GeForce 85.* 2 1 1 3.3 +NVIDIA GeForce 8600M .*NVIDIA .*GeForce 8600M.* 2 1 1 3.3 +NVIDIA GeForce 8600 .*NVIDIA .*GeForce 86.* 3 1 1 3.3 +NVIDIA GeForce 8700M .*NVIDIA .*GeForce 8700M.* 2 1 1 3.3 +NVIDIA GeForce 8700 .*NVIDIA .*GeForce 87.* 3 1 0 0 +NVIDIA GeForce 8800M .*NVIDIA .*GeForce 8800M.* 2 1 1 3.3 +NVIDIA GeForce 8800 .*NVIDIA .*GeForce 88.* 3 1 1 3.3 +NVIDIA GeForce 9100M .*NVIDIA .*GeForce 9100M.* 0 1 0 0 +NVIDIA GeForce 9100 .*NVIDIA .*GeForce 91.* 0 1 0 3.3 +NVIDIA GeForce 9200M .*NVIDIA .*GeForce 9200M.* 1 1 0 3.1 +NVIDIA GeForce 9200 .*NVIDIA .*GeForce 92.* 1 1 0 3.3 +NVIDIA GeForce 9300M .*NVIDIA .*GeForce 9300M.* 1 1 1 3.3 +NVIDIA GeForce 9300 .*NVIDIA .*GeForce 93.* 1 1 1 3.3 +NVIDIA GeForce 9400M .*NVIDIA .*GeForce 9400M.* 2 1 1 3.3 +NVIDIA GeForce 9400 .*NVIDIA .*GeForce 94.* 3 1 1 3.3 +NVIDIA GeForce 9500M .*NVIDIA .*GeForce 9500M.* 1 1 1 3.3 +NVIDIA GeForce 9500 .*NVIDIA .*GeForce 95.* 3 1 1 3.3 +NVIDIA GeForce 9600M .*NVIDIA .*GeForce 9600M.* 2 1 1 3.3 +NVIDIA GeForce 9600 .*NVIDIA .*GeForce 96.* 3 1 1 3.3 +NVIDIA GeForce 9700M .*NVIDIA .*GeForce 9700M.* 0 1 1 3.3 +NVIDIA GeForce 9800M .*NVIDIA .*GeForce 9800M.* 2 1 1 3.3 +NVIDIA GeForce 9800 .*NVIDIA .*GeForce 98.* 3 1 1 3.3 +NVIDIA GeForce FX 5100 .*NVIDIA .*GeForce FX 51.* 0 1 0 0 +NVIDIA GeForce FX 5200 .*NVIDIA .*GeForce FX 52.* 0 1 0 2.1 +NVIDIA GeForce FX 5300 .*NVIDIA .*GeForce FX 53.* 0 1 0 0 +NVIDIA GeForce FX 5500 .*NVIDIA .*GeForce FX 55.* 0 1 1 2.1 +NVIDIA GeForce FX 5600 .*NVIDIA .*GeForce FX 56.* 1 1 1 2.1 +NVIDIA GeForce FX 5700 .*NVIDIA .*GeForce FX 57.* 0 1 1 2.1 +NVIDIA GeForce FX 5800 .*NVIDIA .*GeForce FX 58.* 1 1 0 0 +NVIDIA GeForce FX 5900 .*NVIDIA .*GeForce FX 59.* 1 1 1 2.1 +NVIDIA GeForce FX Go5100 .*NVIDIA .*GeForce FX Go51.* 0 1 0 0 +NVIDIA GeForce FX Go5200 .*NVIDIA .*GeForce FX Go52.* 0 1 0 0 +NVIDIA GeForce FX Go5300 .*NVIDIA .*GeForce FX Go53.* 0 1 0 0 +NVIDIA GeForce FX Go5500 .*NVIDIA .*GeForce FX Go55.* 0 1 0 0 +NVIDIA GeForce FX Go5600 .*NVIDIA .*GeForce FX Go56.* 0 1 1 2.1 +NVIDIA GeForce FX Go5700 .*NVIDIA .*GeForce FX Go57.* 1 1 1 1.5 +NVIDIA GeForce FX Go5800 .*NVIDIA .*GeForce FX Go58.* 1 1 0 0 +NVIDIA GeForce FX Go5900 .*NVIDIA .*GeForce FX Go59.* 1 1 0 0 +NVIDIA GeForce FX Go5xxx .*NVIDIA .*GeForce FX Go.* 0 1 0 0 +NVIDIA GeForce Go 6100 .*NVIDIA .*GeForce Go 61.* 0 1 1 2.1 +NVIDIA GeForce Go 6200 .*NVIDIA .*GeForce Go 62.* 0 1 0 0 +NVIDIA GeForce Go 6400 .*NVIDIA .*GeForce Go 64.* 1 1 1 2 +NVIDIA GeForce Go 6500 .*NVIDIA .*GeForce Go 65.* 1 1 0 0 +NVIDIA GeForce Go 6600 .*NVIDIA .*GeForce Go 66.* 0 1 1 2.1 +NVIDIA GeForce Go 6700 .*NVIDIA .*GeForce Go 67.* 1 1 0 0 +NVIDIA GeForce Go 6800 .*NVIDIA .*GeForce Go 68.* 0 1 1 2.1 +NVIDIA GeForce Go 7200 .*NVIDIA .*GeForce Go 72.* 1 1 0 0 +NVIDIA GeForce Go 7300 LE .*NVIDIA .*GeForce Go 73.*LE.* 1 1 0 0 +NVIDIA GeForce Go 7300 .*NVIDIA .*GeForce Go 73.* 1 1 1 2.1 +NVIDIA GeForce Go 7400 .*NVIDIA .*GeForce Go 74.* 1 1 1 2.1 +NVIDIA GeForce Go 7600 .*NVIDIA .*GeForce Go 76.* 1 1 1 2.1 +NVIDIA GeForce Go 7700 .*NVIDIA .*GeForce Go 77.* 0 1 1 2.1 +NVIDIA GeForce Go 7800 .*NVIDIA .*GeForce Go 78.* 2 1 0 0 +NVIDIA GeForce Go 7900 .*NVIDIA .*GeForce Go 79.* 1 1 1 2.1 +NVIDIA D9M .*NVIDIA .*D9M.* 1 1 0 0 +NVIDIA G94 .*NVIDIA .*G94.* 3 1 0 0 +NVIDIA GeForce Go 6 .*GeForce Go 6.* 1 1 0 0 +NVIDIA ION 2 .*NVIDIA .*ION 2.* 2 1 0 0 +NVIDIA ION .*NVIDIA Corporation.*ION.* 2 1 1 3.3 +NVIDIA NB8M .*NVIDIA .*NB8M.* 1 1 0 0 +NVIDIA NB8P .*NVIDIA .*NB8P.* 2 1 0 0 +NVIDIA NB9E .*NVIDIA .*NB9E.* 3 1 0 0 +NVIDIA NB9M .*NVIDIA .*NB9M.* 1 1 0 0 +NVIDIA NB9P .*NVIDIA .*NB9P.* 2 1 0 0 +NVIDIA N10 .*NVIDIA .*N10.* 1 1 0 0 +NVIDIA GeForce PCX .*GeForce PCX.* 0 1 0 0 +NVIDIA Generic .*NVIDIA .*Unknown.* 0 0 0 3 +NVIDIA NV17 .*NVIDIA .*NV17.* 0 1 0 0 +NVIDIA NV34 .*NVIDIA .*NV34.* 0 1 0 0 +NVIDIA NV35 .*NVIDIA .*NV35.* 0 1 0 0 +NVIDIA NV36 .*NVIDIA .*NV36.* 1 1 0 0 +NVIDIA NV41 .*NVIDIA .*NV41.* 1 1 0 0 +NVIDIA NV43 .*NVIDIA .*NV43.* 1 1 0 0 +NVIDIA NV44 .*NVIDIA .*NV44.* 1 1 0 0 +NVIDIA nForce .*NVIDIA .*nForce.* 0 0 0 3.3 +NVIDIA MCP51 .*NVIDIA .*MCP51.* 1 1 0 0 +NVIDIA MCP61 .*NVIDIA .*MCP61.* 1 1 0 0 +NVIDIA MCP67 .*NVIDIA .*MCP67.* 1 1 0 0 +NVIDIA MCP68 .*NVIDIA .*MCP68.* 1 1 0 0 +NVIDIA MCP73 .*NVIDIA .*MCP73.* 1 1 0 0 +NVIDIA MCP77 .*NVIDIA .*MCP77.* 1 1 0 0 +NVIDIA MCP78 .*NVIDIA .*MCP78.* 1 1 0 0 +NVIDIA MCP79 .*NVIDIA .*MCP79.* 1 1 0 0 +NVIDIA MCP7A .*NVIDIA .*MCP7A.* 1 1 0 0 +NVIDIA Quadro2 .*Quadro2.* 0 1 0 0 +NVIDIA Quadro 1000M .*Quadro.*1000M.* 2 1 0 4.2 +NVIDIA Quadro 2000 M/D .*Quadro.*2000.* 3 1 0 4.2 +NVIDIA Quadro 3000M .*Quadro.*3000M.* 3 1 0 0 +NVIDIA Quadro 4000M .*Quadro.*4000M.* 3 1 0 0 +NVIDIA Quadro 4000 .*Quadro *4000.* 3 1 0 4.2 +NVIDIA Quadro 50x0 M .*Quadro.*50.0.* 3 1 0 0 +NVIDIA Quadro 6000 .*Quadro.*6000.* 3 1 0 0 +NVIDIA Quadro 400 .*Quadro.*400.* 2 1 0 3.3 +NVIDIA Quadro 600 .*Quadro.*600.* 2 1 0 3.3 +NVIDIA Quadro4 .*Quadro4.* 0 1 0 0 +NVIDIA Quadro DCC .*Quadro DCC.* 0 1 0 0 +NVIDIA Quadro CX .*Quadro.*CX.* 3 1 0 0 +NVIDIA Quadro FX 770M .*Quadro.*FX *770M.* 2 1 0 0 +NVIDIA Quadro FX 1500M .*Quadro.*FX *1500M.* 1 1 0 2.1 +NVIDIA Quadro FX 1600M .*Quadro.*FX *1600M.* 2 1 0 0 +NVIDIA Quadro FX 2500M .*Quadro.*FX *2500M.* 2 1 0 0 +NVIDIA Quadro FX 2700M .*Quadro.*FX *2700M.* 3 1 0 0 +NVIDIA Quadro FX 2800M .*Quadro.*FX *2800M.* 3 1 0 3.3 +NVIDIA Quadro FX 3500 .*Quadro.*FX *3500.* 2 1 0 2.1 +NVIDIA Quadro FX 3600 .*Quadro.*FX *3600.* 3 1 0 0 +NVIDIA Quadro FX 3700 .*Quadro.*FX *3700.* 3 1 0 3.3 +NVIDIA Quadro FX 3800 .*Quadro.*FX *3800.* 3 1 0 3.2 +NVIDIA Quadro FX 4500 .*Quadro.*FX *45.* 3 1 0 0 +NVIDIA Quadro FX 880M .*Quadro.*FX *880M.* 3 1 0 3.3 +NVIDIA Quadro FX 4800 .*NVIDIA .*Quadro *FX *4800.* 3 1 0 0 +NVIDIA Quadro FX .*Quadro FX.* 1 1 0 3.3 +NVIDIA Quadro NVS 1xxM .*Quadro NVS *1.[05]M.* 0 1 1 3.3 +NVIDIA Quadro NVS 300M .*NVIDIA .*NVS *300M.* 2 1 0 0 +NVIDIA Quadro NVS 320M .*NVIDIA .*NVS *320M.* 2 1 0 0 +NVIDIA Quadro NVS 2100M .*NVIDIA .*NVS *2100M.* 2 1 0 0 +NVIDIA Quadro NVS 3100M .*NVIDIA .*NVS *3100M.* 2 1 0 0 +NVIDIA Quadro NVS 4200M .*NVIDIA .*NVS *4200M.* 2 1 0 4.1 +NVIDIA Quadro NVS 5100M .*NVIDIA .*NVS *5100M.* 2 1 0 0 +NVIDIA Quadro NVS .*NVIDIA .*NVS 0 1 0 3.2 +NVIDIA Corporation N12P .*NVIDIA .*N12P.* 1 1 1 4.1 +NVIDIA Corporation N11M .*NVIDIA .*N11M.* 2 1 0 0 +NVIDIA RIVA TNT .*RIVA TNT.* 0 0 0 0 +S3 .*S3 Graphics.* 0 0 1 1.4 +SiS SiS.* 0 0 1 1.5 +Trident Trident.* 0 0 0 0 +Tungsten Graphics Tungsten.* 0 0 0 0 +XGI XGI.* 0 0 0 0 +VIA VIA.* 0 0 0 0 +Apple Generic Apple.*Generic.* 0 0 0 0 +Apple Software Renderer Apple.*Software Renderer.* 0 0 0 0 +Humper Humper.* 0 1 1 2.1 +PowerVR SGX545 .*PowerVR SGX.* 1 1 1 3 diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 447836910d..b6fd7bc9c2 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -56,7 +56,6 @@ #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state #include "llnearbychatbar.h" #include "llnotificationsutil.h" -#include "llpanelpathfindingrebakenavmesh.h" #include "llpaneltopinfobar.h" #include "llparcel.h" #include "llrendersphere.h" @@ -2002,7 +2001,6 @@ void LLAgent::endAnimationUpdateUI() LLChicletBar::getInstance()->setVisible(TRUE); LLPanelStandStopFlying::getInstance()->setVisible(TRUE); - LLPanelPathfindingRebakeNavmesh::getInstance()->setVisible(TRUE); LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); @@ -2112,7 +2110,6 @@ void LLAgent::endAnimationUpdateUI() LLChicletBar::getInstance()->setVisible(FALSE); LLPanelStandStopFlying::getInstance()->setVisible(FALSE); - LLPanelPathfindingRebakeNavmesh::getInstance()->setVisible(FALSE); // clear out camera lag effect gAgentCamera.clearCameraLag(); @@ -4045,6 +4042,12 @@ void LLAgent::teleportViaLocation(const LLVector3d& pos_global) void LLAgent::doTeleportViaLocation(const LLVector3d& pos_global) { LLViewerRegion* regionp = getRegion(); + + if (!regionp) + { + return; + } + U64 handle = to_region_handle(pos_global); LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); if(regionp && info) diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp new file mode 100644 index 0000000000..0d7d41304d --- /dev/null +++ b/indra/newview/llappcorehttp.cpp @@ -0,0 +1,192 @@ +/** + * @file llappcorehttp.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llviewerprecompiledheaders.h" + +#include "llappcorehttp.h" + +#include "llviewercontrol.h" + + +const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); + +LLAppCoreHttp::LLAppCoreHttp() + : mRequest(NULL), + mStopHandle(LLCORE_HTTP_HANDLE_INVALID), + mStopRequested(0.0), + mStopped(false), + mPolicyDefault(-1) +{} + + +LLAppCoreHttp::~LLAppCoreHttp() +{ + delete mRequest; + mRequest = NULL; +} + + +void LLAppCoreHttp::init() +{ + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); + if (! status) + { + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Point to our certs or SSH/https: will fail on connect + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); + if (! status) + { + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Establish HTTP Proxy. "LLProxy" is a special string which directs + // the code to use LLProxy::applyProxySettings() to establish any + // HTTP or SOCKS proxy for http operations. + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); + if (! status) + { + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): + // 0 - None + // 1 - Basic start, stop simple transitions + // 2 - libcurl CURLOPT_VERBOSE mode with brief lines + // 3 - with partial data content + static const std::string http_trace("QAModeHttpTrace"); + if (gSavedSettings.controlExists(http_trace)) + { + long trace_level(0L); + trace_level = long(gSavedSettings.getU32(http_trace)); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); + } + + // Setup default policy and constrain if directed to + mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; + static const std::string texture_concur("TextureFetchConcurrency"); + if (gSavedSettings.controlExists(texture_concur)) + { + U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12))); + + if (concur > 0) + { + LLCore::HttpStatus status; + status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault, + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + concur); + if (! status) + { + LL_WARNS("Init") << "Unable to set texture fetch concurrency. Reason: " + << status.toString() + << LL_ENDL; + } + else + { + LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency. New value: " + << concur + << LL_ENDL; + } + } + } + + // Kick the thread + status = LLCore::HttpRequest::startThread(); + if (! status) + { + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " + << status.toString() + << LL_ENDL; + } + + mRequest = new LLCore::HttpRequest; +} + + +void LLAppCoreHttp::requestStop() +{ + llassert_always(mRequest); + + mStopHandle = mRequest->requestStopThread(this); + if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) + { + mStopRequested = LLTimer::getTotalSeconds(); + } +} + + +void LLAppCoreHttp::cleanup() +{ + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + // Should have been started already... + requestStop(); + } + + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" + << LL_ENDL; + } + else + { + while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) + { + mRequest->update(200000); + ms_sleep(50); + } + if (! mStopped) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" + << LL_ENDL; + } + } + + delete mRequest; + mRequest = NULL; + + LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); + if (! status) + { + LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " + << status.toString() + << LL_ENDL; + } +} + + +void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) +{ + mStopped = true; +} diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h new file mode 100644 index 0000000000..241d73ad52 --- /dev/null +++ b/indra/newview/llappcorehttp.h @@ -0,0 +1,86 @@ +/** + * @file llappcorehttp.h + * @brief Singleton initialization/shutdown class for llcorehttp library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_APP_COREHTTP_H_ +#define _LL_APP_COREHTTP_H_ + + +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" + + +// This class manages the lifecyle of the core http library. +// Slightly different style than traditional code but reflects +// the use of handler classes and light-weight interface +// object instances of the new libraries. To be used +// as a singleton and static construction is fine. +class LLAppCoreHttp : public LLCore::HttpHandler +{ +public: + LLAppCoreHttp(); + ~LLAppCoreHttp(); + + // Initialize the LLCore::HTTP library creating service classes + // and starting the servicing thread. Caller is expected to do + // other initializations (SSL mutex, thread hash function) appropriate + // for the application. + void init(); + + // Request that the servicing thread stop servicing requests, + // release resource references and stop. Request is asynchronous + // and @see cleanup() will perform a limited wait loop for this + // request to stop the thread. + void requestStop(); + + // Terminate LLCore::HTTP library services. Caller is expected + // to have made a best-effort to shutdown the servicing thread + // by issuing a requestThreadStop() and waiting for completion + // notification that the stop has completed. + void cleanup(); + + // Notification when the stop request is complete. + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + // Retrieve the policy class for default operations. + int getPolicyDefault() const + { + return mPolicyDefault; + } + +private: + static const F64 MAX_THREAD_WAIT_TIME; + +private: + LLCore::HttpRequest * mRequest; + LLCore::HttpHandle mStopHandle; + F64 mStopRequested; + bool mStopped; + int mPolicyDefault; +}; + + +#endif // _LL_APP_COREHTTP_H_ diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 6d67e098a6..06a9892c7e 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -52,7 +52,8 @@ std::string self_av_string() { - return gAgentAvatarp->avString(); + // On logout gAgentAvatarp can already be invalid + return isAgentAvatarValid() ? gAgentAvatarp->avString() : ""; } // RAII thingy to guarantee that a variable gets reset when the Setter diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 4de80037ed..30f82926f1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -116,6 +116,7 @@ #include "llnotificationsutil.h" #include "llleap.h" +#include "stringize.h" // Third party library includes #include @@ -123,7 +124,6 @@ #include - #if LL_WINDOWS # include // For _SH_DENYWR in initMarkerFile #else @@ -219,6 +219,7 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -379,6 +380,9 @@ void init_default_trans_args() default_trans_args.insert("CAPITALIZED_APP_NAME"); default_trans_args.insert("SECOND_LIFE_GRID"); default_trans_args.insert("SUPPORT_SITE"); + // This URL shows up in a surprising number of places in various skin + // files. We really only want to have to maintain a single copy of it. + default_trans_args.insert("create_account_url"); } //---------------------------------------------------------------------------- @@ -688,7 +692,7 @@ bool LLAppViewer::init() gDirUtilp->initAppDirs("SecondLife"); // set skin search path to default, will be overridden later // this allows simple skinned file lookups to work - gDirUtilp->setSkinFolder("default"); + gDirUtilp->setSkinFolder("default", "en"); initLogging(); @@ -706,7 +710,7 @@ bool LLAppViewer::init() //set the max heap size. initMaxHeapSize() ; - LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")) ; + LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")*1024*1024) ; // write Google Breakpad minidump files to our log directory std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); @@ -731,6 +735,10 @@ bool LLAppViewer::init() LLViewerStatsRecorder::initClass(); #endif + // Initialize the non-LLCurl libcurl library. Should be called + // before consumers (LLTextureFetch). + mAppCoreHttp.init(); + // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), @@ -772,12 +780,15 @@ bool LLAppViewer::init() &LLUI::sGLScaleFactor); LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ; - // Setup paths and LLTrans after LLUI::initClass has been called. - LLUI::setupPaths(); - LLTransUtil::parseStrings("strings.xml", default_trans_args); - LLTransUtil::parseLanguageStrings("language_settings.xml"); + // NOW LLUI::getLanguage() should work. gDirUtilp must know the language + // for this session ASAP so all the file-loading commands that follow, + // that use findSkinnedFilenames(), will include the localized files. + gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), LLUI::getLanguage()); - // Setup notifications after LLUI::setupPaths() has been called. + // Setup LLTrans after LLUI::initClass has been called. + initStrings(); + + // Setup notifications after LLUI::initClass() has been called. LLNotifications::instance(); LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ; @@ -1881,6 +1892,7 @@ bool LLAppViewer::cleanup() // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies + mAppCoreHttp.requestStop(); sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); @@ -1896,6 +1908,9 @@ bool LLAppViewer::cleanup() LLCurl::cleanupClass(); LL_CHECK_MEMORY + // Non-LLCurl libcurl library + mAppCoreHttp.cleanup(); + LLFilePickerThread::cleanupClass(); //MUST happen AFTER LLCurl::cleanupClass @@ -2249,10 +2264,8 @@ bool LLAppViewer::initConfiguration() OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); return false; } - - LLUI::setupPaths(); // setup paths for LLTrans based on settings files only - LLTransUtil::parseStrings("strings.xml", default_trans_args); - LLTransUtil::parseLanguageStrings("language_settings.xml"); + + initStrings(); // setup paths for LLTrans based on settings files only // - set procedural settings // Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet gSavedSettings.setString("ClientSettingsFile", @@ -2566,13 +2579,15 @@ bool LLAppViewer::initConfiguration() LLStartUp::setStartSLURL(start_slurl); } - const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); - if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) - { - // hack to force the skin to default. - gDirUtilp->setSkinFolder(skinfolder->getValue().asString()); - //gDirUtilp->setSkinFolder("default"); - } + const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); + if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) + { + // Examining "Language" may not suffice -- see LLUI::getLanguage() + // logic. Unfortunately LLUI::getLanguage() doesn't yet do us much + // good because we haven't yet called LLUI::initClass(). + gDirUtilp->setSkinFolder(skinfolder->getValue().asString(), + gSavedSettings.getString("Language")); + } if (gSavedSettings.getBOOL("SpellCheck")) { @@ -2744,6 +2759,52 @@ bool LLAppViewer::initConfiguration() return true; // Config was successful. } +// The following logic is replicated in initConfiguration() (to be able to get +// some initial strings before we've finished initializing enough to know the +// current language) and also in init() (to initialize for real). Somehow it +// keeps growing, necessitating a method all its own. +void LLAppViewer::initStrings() +{ + LLTransUtil::parseStrings("strings.xml", default_trans_args); + LLTransUtil::parseLanguageStrings("language_settings.xml"); + + // parseStrings() sets up the LLTrans substitution table. Add this one item. + LLTrans::setDefaultArg("[sourceid]", gSavedSettings.getString("sourceid")); + + // Now that we've set "[sourceid]", have to go back through + // default_trans_args and reinitialize all those other keys because some + // of them, in turn, reference "[sourceid]". + BOOST_FOREACH(std::string key, default_trans_args) + { + std::string brackets(key), nobrackets(key); + // Invalid to inspect key[0] if key is empty(). But then, the entire + // body of this loop is pointless if key is empty(). + if (key.empty()) + continue; + + if (key[0] != '[') + { + // key was passed without brackets. That means that 'nobrackets' + // is correct but 'brackets' is not. + brackets = STRINGIZE('[' << brackets << ']'); + } + else + { + // key was passed with brackets. That means that 'brackets' is + // correct but 'nobrackets' is not. Erase the left bracket. + nobrackets.erase(0, 1); + std::string::size_type length(nobrackets.length()); + if (length && nobrackets[length - 1] == ']') + { + nobrackets.erase(length - 1); + } + } + // Calling LLTrans::getString() is what embeds the other default + // translation strings into this one. + LLTrans::setDefaultArg(brackets, LLTrans::getString(nobrackets)); + } +} + namespace { // *TODO - decide if there's a better place for these functions. // do we need a file llupdaterui.cpp or something? -brad @@ -3596,8 +3657,7 @@ void LLAppViewer::migrateCacheDirectory() { gSavedSettings.setBOOL("MigrateCacheDirectory", FALSE); - std::string delimiter = gDirUtilp->getDirDelimiter(); - std::string old_cache_dir = gDirUtilp->getOSUserAppDir() + delimiter + "cache"; + std::string old_cache_dir = gDirUtilp->add(gDirUtilp->getOSUserAppDir(), "cache"); std::string new_cache_dir = gDirUtilp->getCacheDir(true); if (gDirUtilp->fileExists(old_cache_dir)) @@ -3613,8 +3673,8 @@ void LLAppViewer::migrateCacheDirectory() while (iter.next(file_name)) { if (file_name == "." || file_name == "..") continue; - std::string source_path = old_cache_dir + delimiter + file_name; - std::string dest_path = new_cache_dir + delimiter + file_name; + std::string source_path = gDirUtilp->add(old_cache_dir, file_name); + std::string dest_path = gDirUtilp->add(new_cache_dir, file_name); if (!LLFile::rename(source_path, dest_path)) { file_count++; @@ -3845,7 +3905,7 @@ bool LLAppViewer::initCache() LLDirIterator iter(dir, mask); if (iter.next(found_file)) { - old_vfs_data_file = dir + gDirUtilp->getDirDelimiter() + found_file; + old_vfs_data_file = gDirUtilp->add(dir, found_file); S32 start_pos = found_file.find_last_of('.'); if (start_pos > 0) @@ -4356,6 +4416,10 @@ void LLAppViewer::idle() { return; } + if (gTeleportDisplay) + { + return; + } gViewerWindow->updateUI(); @@ -5156,20 +5220,20 @@ void LLAppViewer::launchUpdater() // we tell the updater where to find the xml containing string // translations which it can use for its own UI std::string xml_strings_file = "strings.xml"; - std::vector xui_path_vec = LLUI::getXUIPaths(); + std::vector xui_path_vec = + gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_strings_file); std::string xml_search_paths; - std::vector::const_iterator iter; + const char* delim = ""; // build comma-delimited list of xml paths to pass to updater - for (iter = xui_path_vec.begin(); iter != xui_path_vec.end(); ) + BOOST_FOREACH(std::string this_skin_path, xui_path_vec) { - std::string this_skin_dir = gDirUtilp->getDefaultSkinDir() - + gDirUtilp->getDirDelimiter() - + (*iter); - llinfos << "Got a XUI path: " << this_skin_dir << llendl; - xml_search_paths.append(this_skin_dir); - ++iter; - if (iter != xui_path_vec.end()) - xml_search_paths.append(","); // comma-delimit + // Although we already have the full set of paths with the filename + // appended, the linux-updater.bin command-line switches require us to + // snip the filename OFF and pass it as a separate switch argument. :-P + llinfos << "Got a XUI path: " << this_skin_path << llendl; + xml_search_paths.append(delim); + xml_search_paths.append(gDirUtilp->getDirName(this_skin_path)); + delim = ","; } // build the overall command-line to run the updater correctly LLAppViewer::sUpdaterInfo->mUpdateExePath = diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 543ae6653a..6aab237040 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -46,6 +46,7 @@ #include "llcontrol.h" #include "llsys.h" // for LLOSInfo #include "lltimer.h" +#include "llappcorehttp.h" class LLCommandLineParser; class LLFrameTimer; @@ -188,6 +189,9 @@ public: // Metrics policy helper statics. static void metricsUpdateRegion(U64 region_handle); static void metricsSend(bool enable_reporting); + + // llcorehttp init/shutdown/config information. + LLAppCoreHttp & getAppCoreHttp() { return mAppCoreHttp; } protected: virtual bool initWindow(); // Initialize the viewer's window. @@ -209,6 +213,7 @@ private: void initMaxHeapSize(); bool initThreads(); // Initialize viewer threads, return false on failure. bool initConfiguration(); // Initialize settings from the command line/config file. + void initStrings(); // Initialize LLTrans machinery void initUpdater(); // Initialize the updater service. bool initCache(); // Initialize local client cache. void checkMemory() ; @@ -286,6 +291,9 @@ private: boost::scoped_ptr mUpdater; + // llcorehttp library init/shutdown helper + LLAppCoreHttp mAppCoreHttp; + //--------------------------------------------- //*NOTE: Mani - legacy updater stuff // Still useable? diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index e0ca1232b0..11790d562f 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -125,7 +125,7 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, #if WINDOWS_CRT_MEM_CHECKS && !INCLUDE_VLD _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); // dump memory leaks on exit -#elif 1 +#elif 0 // Experimental - enable the low fragmentation heap // This results in a 2-3x improvement in opening a new Inventory window (which uses a large numebr of allocations) // Note: This won't work when running from the debugger unless the _NO_DEBUG_HEAP environment variable is set to 1 diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index c9543988a6..ea0b8f00a4 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -62,6 +62,12 @@ void LLAttachmentsMgr::onIdle(void *) void LLAttachmentsMgr::onIdle() { + // Make sure we got a region before trying anything else + if( !gAgent.getRegion() ) + { + return; + } + S32 obj_count = mPendingAttachments.size(); if (obj_count == 0) { diff --git a/indra/newview/llautoreplace.cpp b/indra/newview/llautoreplace.cpp index 0f1ce2bcd0..d71cf290d6 100644 --- a/indra/newview/llautoreplace.cpp +++ b/indra/newview/llautoreplace.cpp @@ -536,6 +536,46 @@ LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::addList(const LLSD& return result; } +LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::replaceList(const LLSD& newList) +{ + AddListResult result = AddListInvalidList; + if ( listIsValid( newList ) ) + { + std::string listName = newList[AUTOREPLACE_LIST_NAME].asString(); + bool listFound = false; + S32 search_index; + LLSD targetList; + // The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30 + for ( search_index = 0, targetList = mLists[0]; + !listFound && search_index < mLists.size(); + search_index += 1, targetList = mLists[search_index] + ) + { + if ( targetList.isMap() ) + { + if ( listNameMatches( targetList, listName) ) + { + LL_DEBUGS("AutoReplace")<<"list to replace found at "<add(dir, file)); } } diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h index 2188b7edd0..682f9d6476 100644 --- a/indra/newview/lldirpicker.h +++ b/indra/newview/lldirpicker.h @@ -37,8 +37,8 @@ #include // AssertMacros.h does bad things. +#include "fix_macros.h" #undef verify -#undef check #undef require #include diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index a0289cd2af..4894d63e13 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -99,7 +99,6 @@ void LLDrawable::init() mPositionGroup.clear(); mExtents[0].clear(); mExtents[1].clear(); - mQuietCount = 0; mState = 0; mVObjp = NULL; @@ -407,6 +406,8 @@ void LLDrawable::makeActive() if (!isRoot() && !mParent->isActive()) { mParent->makeActive(); + //NOTE: linked set will now NEVER become static + mParent->setState(LLDrawable::ACTIVE_CHILD); } //all child objects must also be active @@ -424,14 +425,6 @@ void LLDrawable::makeActive() } } - if (mVObjp->getPCode() == LL_PCODE_VOLUME) - { - if (mVObjp->isFlexible()) - { - return; - } - } - if (mVObjp->getPCode() == LL_PCODE_VOLUME) { gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE); @@ -439,28 +432,22 @@ void LLDrawable::makeActive() updatePartition(); } - if (isRoot()) - { - mQuietCount = 0; - } - else - { - getParent()->mQuietCount = 0; - } + llassert(isAvatar() || isRoot() || mParent->isActive()); } void LLDrawable::makeStatic(BOOL warning_enabled) { - if (isState(ACTIVE)) + if (isState(ACTIVE) && + !isState(ACTIVE_CHILD) && + !mVObjp->isAttachment() && + !mVObjp->isFlexible()) { clearState(ACTIVE | ANIMATED_CHILD); - if (mParent.notNull() && mParent->isActive() && warning_enabled) - { - LL_WARNS_ONCE("Drawable") << "Drawable becomes static with active parent!" << LL_ENDL; - } - + //drawable became static with active parent, not acceptable + llassert(mParent.isNull() || !mParent->isActive() || !warning_enabled); + LLViewerObject::const_child_list_t& child_list = mVObjp->getChildren(); for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); iter != child_list.end(); iter++) @@ -487,8 +474,8 @@ void LLDrawable::makeStatic(BOOL warning_enabled) mSpatialBridge->markDead(); setSpatialBridge(NULL); } + updatePartition(); } - updatePartition(); } // Returns "distance" between target destination and resulting xfrom @@ -637,9 +624,9 @@ BOOL LLDrawable::updateMove() { return FALSE; } - + makeActive(); - + BOOL done; if (isState(MOVE_UNDAMPED)) diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 960c64fa9e..b1e32bdb5b 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -292,6 +292,7 @@ public: RIGGED = 0x08000000, PARTITION_MOVE = 0x10000000, ANIMATED_CHILD = 0x20000000, + ACTIVE_CHILD = 0x40000000, } EDrawableFlags; private: //aligned members @@ -305,8 +306,6 @@ public: LLPointer mParent; F32 mDistanceWRTCamera; - - S32 mQuietCount; static S32 getCurrentFrame() { return sCurVisible; } static S32 getMinVisFrameRange(); diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 013c698445..94dd927d26 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -419,6 +419,7 @@ void LLRenderPass::applyModelMatrix(LLDrawInfo& params) gGL.loadMatrix(gGLModelView); if (params.mModelMatrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGL.multMatrix((GLfloat*) params.mModelMatrix->mMatrix); } gPipeline.mMatrixOpCount++; diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 730ad1a364..0e15f474c9 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -1630,6 +1630,7 @@ void LLDrawPoolAvatar::renderRiggedAlpha(LLVOAvatar* avatar) LLRender::BF_ONE_MINUS_SOURCE_ALPHA); renderRigged(avatar, RIGGED_ALPHA); + gGL.setColorMask(true, false); } } @@ -1646,6 +1647,7 @@ void LLDrawPoolAvatar::renderRiggedFullbrightAlpha(LLVOAvatar* avatar) LLRender::BF_ONE_MINUS_SOURCE_ALPHA); renderRigged(avatar, RIGGED_FULLBRIGHT_ALPHA); + gGL.setColorMask(true, false); } } diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 7fc78fb382..9bc32fddbd 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -308,6 +308,7 @@ void LLDrawPoolTerrain::drawLoop() if (model_matrix != gGLLastMatrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGLLastMatrix = model_matrix; gGL.loadMatrix(gGLModelView); if (model_matrix) @@ -594,7 +595,8 @@ void LLDrawPoolTerrain::renderFull4TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-1.f, 0.f, 0.f); - + gGL.matrixMode(LLRender::MM_MODELVIEW); + // Set alpha texture and do lighting modulation gGL.getTexUnit(3)->setTextureColorBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_PREV_COLOR, LLTexUnit::TBS_VERT_COLOR); gGL.getTexUnit(3)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA); @@ -742,6 +744,7 @@ void LLDrawPoolTerrain::renderFull2TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-1.f, 0.f, 0.f); + gGL.matrixMode(LLRender::MM_MODELVIEW); // Care about alpha only gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); @@ -781,6 +784,7 @@ void LLDrawPoolTerrain::renderFull2TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-2.f, 0.f, 0.f); + gGL.matrixMode(LLRender::MM_MODELVIEW); // Care about alpha only gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 83f04e45a8..fedbd782dc 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -116,6 +116,7 @@ void LLDrawPoolTree::render(S32 pass) gGL.loadMatrix(gGLModelView); if (model_matrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGL.multMatrix((GLfloat*) model_matrix->mMatrix); } gPipeline.mMatrixOpCount++; diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index caf15fe1cb..b5faff7968 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -192,14 +192,18 @@ void LLDrawPoolWLSky::renderStars(void) const bool error; LLColor4 star_alpha(LLColor4::black); star_alpha.mV[3] = LLWLParamManager::getInstance()->mCurParams.getFloat("star_brightness", error) / 2.f; - llassert_always(!error); + + // If start_brightness is not set, exit + if( error ) + { + llwarns << "star_brightness missing in mCurParams" << llendl; + return; + } gGL.getTexUnit(0)->bind(gSky.mVOSkyp->getBloomTex()); gGL.pushMatrix(); gGL.rotatef(gFrameTimeSeconds*0.01f, 0.f, 0.f, 1.f); - // gl_FragColor.rgb = gl_Color.rgb; - // gl_FragColor.a = gl_Color.a * star_alpha.a; if (LLGLSLShader::sNoFixedFunction) { gCustomAlphaProgram.bind(); diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index bf8338e5f2..fa42b157a7 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -129,7 +129,7 @@ void LLViewerDynamicTexture::preRender(BOOL clear_depth) llassert(mFullHeight <= 512); llassert(mFullWidth <= 512); - if (gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete()) + if (gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI) { //using offscreen render target, just use the bottom left corner mOrigin.set(0, 0); } @@ -216,14 +216,12 @@ BOOL LLViewerDynamicTexture::updateAllInstances() return TRUE; } -#if 0 //THIS CAUSES MAINT-1092 - bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete(); + bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI; if (use_fbo) { gPipeline.mWaterDis.bindTarget(); } -#endif LLGLSLShader::bindNoShader(); LLVertexBuffer::unbind(); @@ -258,12 +256,10 @@ BOOL LLViewerDynamicTexture::updateAllInstances() } } -#if 0 if (use_fbo) { gPipeline.mWaterDis.flush(); } -#endif return ret; } diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 582f06eeb0..605cb81c10 100755 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1062,7 +1062,11 @@ bool LLFace::canRenderAsMask() } const LLTextureEntry* te = getTextureEntry(); - + if( !te || !getViewerObject() || !getTexture() ) + { + return false; + } + if ((te->getColor().mV[3] == 1.0f) && // can't treat as mask if we have face alpha (te->getGlow() == 0.f) && // glowing masks are hard to implement - don't mask getTexture()->getIsAlphaMask()) // texture actually qualifies for masking (lazily recalculated but expensive) @@ -2168,6 +2172,12 @@ BOOL LLFace::hasMedia() const const F32 LEAST_IMPORTANCE = 0.05f ; const F32 LEAST_IMPORTANCE_FOR_LARGE_IMAGE = 0.3f ; +void LLFace::resetVirtualSize() +{ + setVirtualSize(0.f); + mImportanceToCamera = 0.f; +} + F32 LLFace::getTextureVirtualSize() { F32 radius; @@ -2233,8 +2243,17 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) LLVector4a t; t.load3(camera->getOrigin().mV); lookAt.setSub(center, t); + F32 dist = lookAt.getLength3().getF32(); - dist = llmax(dist-size.getLength3().getF32(), 0.f); + dist = llmax(dist-size.getLength3().getF32(), 0.001f); + //ramp down distance for nearby objects + if (dist < 16.f) + { + dist /= 16.f; + dist *= dist; + dist *= 16.f; + } + lookAt.normalize3fast() ; //get area of circle around node diff --git a/indra/newview/llface.h b/indra/newview/llface.h index efc3424858..de4d03351c 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -218,6 +218,7 @@ public: F32 getTextureVirtualSize() ; F32 getImportanceToCamera()const {return mImportanceToCamera ;} + void resetVirtualSize(); void setHasMedia(bool has_media) { mHasMedia = has_media ;} BOOL hasMedia() const ; diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 393f8b9d46..2b39b771e7 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -57,6 +57,7 @@ #include "lldxhardware.h" #endif +#define LL_EXPORT_GPU_TABLE 0 #if LL_DARWIN const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt"; @@ -386,6 +387,13 @@ void LLFeatureManager::parseGPUTable(std::string filename) *i = tolower(*i); } +#if LL_EXPORT_GPU_TABLE + llofstream json; + json.open("gpu_table.json"); + + json << "var gpu_table = [" << std::endl; +#endif + bool gpuFound; U32 lineNumber; for (gpuFound = false, lineNumber = 0; !gpuFound && !file.eof(); lineNumber++) @@ -411,7 +419,7 @@ void LLFeatureManager::parseGPUTable(std::string filename) // setup the tokenizer std::string buf(buffer); - std::string cls, label, expr, supported; + std::string cls, label, expr, supported, stats_based, expected_gl_version; boost_tokenizer tokens(buf, boost::char_separator("\t\n")); boost_tokenizer::iterator token_iter = tokens.begin(); @@ -432,13 +440,29 @@ void LLFeatureManager::parseGPUTable(std::string filename) { supported = *token_iter++; } + if (token_iter != tokens.end()) + { + stats_based = *token_iter++; + } + if (token_iter != tokens.end()) + { + expected_gl_version = *token_iter++; + } if (label.empty() || expr.empty() || cls.empty() || supported.empty()) { LL_WARNS("RenderInit") << "invald gpu_table.txt:" << lineNumber << ": '" << buffer << "'" << LL_ENDL; continue; } - +#if LL_EXPORT_GPU_TABLE + json << "{'label' : '" << label << "',\n" << + "'regexp' : '" << expr << "',\n" << + "'class' : '" << cls << "',\n" << + "'supported' : '" << supported << "',\n" << + "'stats_based' : " << stats_based << ",\n" << + "'gl_version' : " << expected_gl_version << "\n},\n"; +#endif + for (U32 i = 0; i < expr.length(); i++) /*Flawfinder: ignore*/ { expr[i] = tolower(expr[i]); @@ -449,12 +473,18 @@ void LLFeatureManager::parseGPUTable(std::string filename) if(boost::regex_search(renderer, re)) { // if we found it, stop! +#if !LL_EXPORT_GPU_TABLE gpuFound = true; +#endif mGPUString = label; mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10); mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10); } } +#if LL_EXPORT_GPU_TABLE + json << "];\n\n"; + json.close(); +#endif file.close(); if ( gpuFound ) @@ -469,6 +499,10 @@ void LLFeatureManager::parseGPUTable(std::string filename) { LL_WARNS("RenderInit") << "GPU '" << rawRenderer << "' not recognized" << LL_ENDL; } + +#if LL_DARWIN // never go over "Mid" settings by default on OS X + mGPUClass = llmin(mGPUClass, GPU_CLASS_2); +#endif } // responder saves table into file @@ -585,7 +619,7 @@ void LLFeatureManager::applyRecommendedSettings() { // apply saved settings // cap the level at 2 (high) - S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2)); + S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_5)); llinfos << "Applying Recommended Features" << llendl; @@ -680,12 +714,21 @@ void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures) } break; case 1: - maskFeatures("Mid"); + maskFeatures("LowMid"); break; case 2: - maskFeatures("High"); + maskFeatures("Mid"); break; case 3: + maskFeatures("MidHigh"); + break; + case 4: + maskFeatures("High"); + break; + case 5: + maskFeatures("HighUltra"); + break; + case 6: maskFeatures("Ultra"); break; default: @@ -715,14 +758,16 @@ void LLFeatureManager::applyBaseMasks() mFeatures = maskp->getFeatures(); // mask class - if (mGPUClass >= 0 && mGPUClass < 4) + if (mGPUClass >= 0 && mGPUClass < 6) { const char* class_table[] = { "Class0", "Class1", "Class2", - "Class3" + "Class3", + "Class4", + "Class5", }; LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL; diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h index c9cb397fcc..6f9d2e49c6 100644 --- a/indra/newview/llfeaturemanager.h +++ b/indra/newview/llfeaturemanager.h @@ -39,7 +39,9 @@ typedef enum EGPUClass GPU_CLASS_0 = 0, GPU_CLASS_1 = 1, GPU_CLASS_2 = 2, - GPU_CLASS_3 = 3 + GPU_CLASS_3 = 3, + GPU_CLASS_4 = 4, + GPU_CLASS_5 = 5 } EGPUClass; diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 55c665b9c7..4f602f63f1 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -39,8 +39,8 @@ #include // AssertMacros.h does bad things. +#include "fix_macros.h" #undef verify -#undef check #undef require #include diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index 9745bb6d64..28d195d5e9 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -44,6 +44,8 @@ #include "llvoavatar.h" /*static*/ F32 LLVolumeImplFlexible::sUpdateFactor = 1.0f; +std::vector LLVolumeImplFlexible::sInstanceList; +std::vector LLVolumeImplFlexible::sUpdateDelay; static LLFastTimer::DeclareTimer FTM_FLEXIBLE_REBUILD("Rebuild"); static LLFastTimer::DeclareTimer FTM_DO_FLEXIBLE_UPDATE("Update"); @@ -70,8 +72,45 @@ LLVolumeImplFlexible::LLVolumeImplFlexible(LLViewerObject* vo, LLFlexibleObjectD { mVO->mDrawable->makeActive() ; } + + mInstanceIndex = sInstanceList.size(); + sInstanceList.push_back(this); + sUpdateDelay.push_back(0); }//----------------------------------------------- +LLVolumeImplFlexible::~LLVolumeImplFlexible() +{ + S32 end_idx = sInstanceList.size()-1; + + if (end_idx != mInstanceIndex) + { + sInstanceList[mInstanceIndex] = sInstanceList[end_idx]; + sInstanceList[mInstanceIndex]->mInstanceIndex = mInstanceIndex; + sUpdateDelay[mInstanceIndex] = sUpdateDelay[end_idx]; + } + + sInstanceList.pop_back(); + sUpdateDelay.pop_back(); +} + +//static +void LLVolumeImplFlexible::updateClass() +{ + std::vector::iterator delay_iter = sUpdateDelay.begin(); + + for (std::vector::iterator iter = sInstanceList.begin(); + iter != sInstanceList.end(); + ++iter) + { + --(*delay_iter); + if (*delay_iter <= 0) + { + (*iter)->doIdleUpdate(); + } + ++delay_iter; + } +} + LLVector3 LLVolumeImplFlexible::getFramePosition() const { return mVO->getRenderPosition(); @@ -296,22 +335,17 @@ void LLVolumeImplFlexible::updateRenderRes() // optimization similar to what Havok does for objects that are stationary. //--------------------------------------------------------------------------------- static LLFastTimer::DeclareTimer FTM_FLEXIBLE_UPDATE("Update Flexies"); -void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVolumeImplFlexible::doIdleUpdate() { LLDrawable* drawablep = mVO->mDrawable; if (drawablep) { //LLFastTimer ftm(FTM_FLEXIBLE_UPDATE); - - //flexible objects never go static - drawablep->mQuietCount = 0; - if (!drawablep->isRoot()) - { - LLViewerObject* parent = (LLViewerObject*) mVO->getParent(); - parent->mDrawable->mQuietCount = 0; - } - + + //ensure drawable is active + drawablep->makeActive(); + if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE)) { bool visible = drawablep->isVisible(); @@ -321,31 +355,47 @@ void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6 updateRenderRes(); gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); } - else if (visible && - !drawablep->isState(LLDrawable::IN_REBUILD_Q1) && - mVO->getPixelArea() > 256.f) + else { - U32 id; F32 pixel_area = mVO->getPixelArea(); - if (mVO->isRootEdit()) + U32 update_period = (U32) (LLViewerCamera::getInstance()->getScreenPixelArea()*0.01f/(pixel_area*(sUpdateFactor+1.f)))+1; + // MAINT-1890 Clamp the update period to ensure that the update_period is no greater than 32 frames + update_period = llclamp(update_period, 0U, 32U); + + if (visible) { - id = mID; + if (!drawablep->isState(LLDrawable::IN_REBUILD_Q1) && + mVO->getPixelArea() > 256.f) + { + U32 id; + + if (mVO->isRootEdit()) + { + id = mID; + } + else + { + LLVOVolume* parent = (LLVOVolume*) mVO->getParent(); + id = parent->getVolumeInterfaceID(); + } + + if ((LLDrawable::getCurrentFrame()+id)%update_period == 0) + { + sUpdateDelay[mInstanceIndex] = (S32) update_period-1; + + updateRenderRes(); + + gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); + } + } } else { - LLVOVolume* parent = (LLVOVolume*) mVO->getParent(); - id = parent->getVolumeInterfaceID(); - } - - U32 update_period = (U32) (LLViewerCamera::getInstance()->getScreenPixelArea()*0.01f/(pixel_area*(sUpdateFactor+1.f)))+1; - - if ((LLDrawable::getCurrentFrame()+id)%update_period == 0) - { - updateRenderRes(); - gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); + sUpdateDelay[mInstanceIndex] = (S32) update_period; } } + } } } @@ -370,7 +420,7 @@ void LLVolumeImplFlexible::doFlexibleUpdate() { BOOL force_update = mSimulateRes == 0 ? TRUE : FALSE; - doIdleUpdate(gAgent, *LLWorld::getInstance(), 0.0); + doIdleUpdate(); if (!force_update || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE)) { @@ -383,6 +433,15 @@ void LLVolumeImplFlexible::doFlexibleUpdate() //the object is not visible return ; } + + // stinson 11/12/2012: Need to check with davep on the following. + // Skipping the flexible update if render res is negative. If we were to continue with a negative value, + // the subsequent S32 num_render_sections = 1< sInstanceList; + static std::vector sUpdateDelay; + S32 mInstanceIndex; + public: + static void updateClass(); + LLVolumeImplFlexible(LLViewerObject* volume, LLFlexibleObjectData* attributes); + ~LLVolumeImplFlexible(); // Implements LLVolumeInterface U32 getID() const { return mID; } @@ -79,7 +87,7 @@ class LLVolumeImplFlexible : public LLVolumeInterface LLQuaternion getFrameRotation() const; LLVolumeInterfaceType getInterfaceType() const { return INTERFACE_FLEXIBLE; } void updateRenderRes(); - void doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + void doIdleUpdate(); BOOL doUpdateGeometry(LLDrawable *drawable); LLVector3 getPivotPosition() const; void onSetVolume(const LLVolumeParams &volume_params, const S32 detail); diff --git a/indra/newview/llfloaterautoreplacesettings.cpp b/indra/newview/llfloaterautoreplacesettings.cpp index 7d1bcba978..6e56e929df 100644 --- a/indra/newview/llfloaterautoreplacesettings.cpp +++ b/indra/newview/llfloaterautoreplacesettings.cpp @@ -478,14 +478,25 @@ bool LLFloaterAutoReplaceSettings::callbackNewListName(const LLSD& notification, bool LLFloaterAutoReplaceSettings::callbackListNameConflict(const LLSD& notification, const LLSD& response) { LLSD newList = notification["payload"]["list"]; - + std::string listName = LLAutoReplaceSettings::getListName(newList); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); switch ( option ) { case 0: // Replace current list - LL_INFOS("AutoReplace")<<"option 'replace current list' selected"<getFirstSelected()->getColumn(0)->getValue().asString(); - mSettings.removeReplacementList(listName); // remove from the copy of settings - mReplacementsList->deleteSelectedItems(); // remove from the scrolling list - - mSelectedListName.clear(); - updateListNames(); - updateListNamesControls(); - updateReplacementsList(); + std::string listName = mListNames->getSelectedValue().asString(); + if ( ! listName.empty() ) + { + if ( mSettings.removeReplacementList(listName) ) + { + LL_INFOS("AutoReplace")<<"deleted list '"<deleteSelectedItems(); // remove from the scrolling list + mSelectedListName.clear(); + updateListNames(); + updateListNamesControls(); + updateReplacementsList(); + } + else + { + LL_WARNS("AutoReplace")<<"failed to delete list '"<getDummyAvatar(); BOOL paused = avatarp->areAnimationsPaused(); - // *TODO: Fix awful casting hack - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - - // Set emotion - std::string emote = getChild("emote_combo")->getValue().asString(); - motionp->setEmote(mIDList[emote]); - + LLKeyframeMotion* motionp = dynamic_cast(avatarp->findMotion(mMotionID)); + if( motionp ) + { + // Set emotion + std::string emote = getChild("emote_combo")->getValue().asString(); + motionp->setEmote(mIDList[emote]); + } + LLUUID base_id = mIDList[getChild("preview_base_anim")->getValue().asString()]; avatarp->deactivateAllMotions(); avatarp->startMotion(mMotionID, 0.0f); @@ -438,8 +439,12 @@ void LLFloaterBvhPreview::resetMotion() // Set pose std::string handpose = getChild("hand_pose_combo")->getValue().asString(); avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); - motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + if( motionp ) + { + motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + } + if (paused) { mPauseRequest = avatarp->requestPause(); diff --git a/indra/newview/llfloaterhelpbrowser.cpp b/indra/newview/llfloaterhelpbrowser.cpp index fd9c37ae73..4cb632bd6a 100644 --- a/indra/newview/llfloaterhelpbrowser.cpp +++ b/indra/newview/llfloaterhelpbrowser.cpp @@ -77,15 +77,7 @@ void LLFloaterHelpBrowser::onOpen(const LLSD& key) gSavedSettings.setBOOL("HelpFloaterOpen", TRUE); std::string topic = key.asString(); - - if (topic == "__local") - { - mBrowser->navigateToLocalPage( "help-offline" , "index.html" ); - } - else - { - mBrowser->navigateTo(LLViewerHelp::instance().getURL(topic)); - } + mBrowser->navigateTo(LLViewerHelp::instance().getURL(topic)); } //virtual diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index df8ecb6fd9..55f3d548ec 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2362,12 +2362,6 @@ LLPanelLandAccess::~LLPanelLandAccess() void LLPanelLandAccess::refresh() { LLFloater* parent_floater = gFloaterView->getParentFloater(this); - - if (mListAccess) - mListAccess->deleteAllItems(); - if (mListBanned) - mListBanned->deleteAllItems(); - LLParcel *parcel = mParcel->getParcel(); // Display options @@ -2385,7 +2379,11 @@ void LLPanelLandAccess::refresh() getChild("GroupCheck")->setLabelArg("[GROUP]", group_name ); // Allow list + if (mListAccess) { + // Clear the sort order so we don't re-sort on every add. + mListAccess->clearSortOrder(); + mListAccess->deleteAllItems(); S32 count = parcel->mAccessList.size(); getChild("AccessList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); getChild("AccessList")->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST)); @@ -2420,13 +2418,17 @@ void LLPanelLandAccess::refresh() } suffix.append(" " + parent_floater->getString("Remaining") + ")"); } - if (mListAccess) - mListAccess->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); + mListAccess->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); } + mListAccess->sortByName(TRUE); } // Ban List + if(mListBanned) { + // Clear the sort order so we don't re-sort on every add. + mListBanned->clearSortOrder(); + mListBanned->deleteAllItems(); S32 count = parcel->mBanList.size(); getChild("BannedList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); @@ -2464,6 +2466,7 @@ void LLPanelLandAccess::refresh() } mListBanned->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); } + mListBanned->sortByName(TRUE); } if(parcel->getRegionDenyAnonymousOverride()) @@ -2599,13 +2602,13 @@ void LLPanelLandAccess::refresh_ui() getChildView("AccessList")->setEnabled(can_manage_allowed); S32 allowed_list_count = parcel->mAccessList.size(); getChildView("add_allowed")->setEnabled(can_manage_allowed && allowed_list_count < PARCEL_MAX_ACCESS_LIST); - BOOL has_selected = mListAccess->getSelectionInterface()->getFirstSelectedIndex() >= 0; + BOOL has_selected = (mListAccess && mListAccess->getSelectionInterface()->getFirstSelectedIndex() >= 0); getChildView("remove_allowed")->setEnabled(can_manage_allowed && has_selected); getChildView("BannedList")->setEnabled(can_manage_banned); S32 banned_list_count = parcel->mBanList.size(); getChildView("add_banned")->setEnabled(can_manage_banned && banned_list_count < PARCEL_MAX_ACCESS_LIST); - has_selected = mListBanned->getSelectionInterface()->getFirstSelectedIndex() >= 0; + has_selected = (mListBanned && mListBanned->getSelectionInterface()->getFirstSelectedIndex() >= 0); getChildView("remove_banned")->setEnabled(can_manage_banned && has_selected); } } diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 5752f839ce..542e96cf16 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -750,7 +750,10 @@ void LLFloaterPreference::onClose(bool app_quitting) { gSavedSettings.setS32("LastPrefTab", getChild("pref core")->getCurrentPanelIndex()); LLPanelLogin::setAlwaysRefresh(false); - cancel(); + if (!app_quitting) + { + cancel(); + } } void LLFloaterPreference::onOpenHardwareSettings() diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 17850ff35d..fe29bb38c7 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -287,8 +287,7 @@ void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**) //dispatch the message dispatch.dispatch(request, invoice, strings); - LLViewerRegion* region = gAgent.getRegion(); - panel->updateControls(region); + panel->updateControls(gAgent.getRegion()); } @@ -1924,10 +1923,18 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) BOOL manager = (region && region->isEstateManager()); setCtrlsEnabled(god || owner || manager); + BOOL has_allowed_avatar = getChild("allowed_avatar_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_allowed_group = getChild("allowed_group_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_banned_agent = getChild("banned_avatar_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_estate_manager = getChild("estate_manager_name_list")->getFirstSelected() ? TRUE : FALSE; + getChildView("add_allowed_avatar_btn")->setEnabled(god || owner || manager); - getChildView("remove_allowed_avatar_btn")->setEnabled(god || owner || manager); + getChildView("remove_allowed_avatar_btn")->setEnabled(has_allowed_avatar && (god || owner || manager)); + getChildView("allowed_avatar_name_list")->setEnabled(god || owner || manager); + getChildView("add_allowed_group_btn")->setEnabled(god || owner || manager); - getChildView("remove_allowed_group_btn")->setEnabled(god || owner || manager); + getChildView("remove_allowed_group_btn")->setEnabled(has_allowed_group && (god || owner || manager) ); + getChildView("allowed_group_name_list")->setEnabled(god || owner || manager); // Can't ban people from mainland, orientation islands, etc. because this // creates much network traffic and server load. @@ -1935,14 +1942,15 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) bool linden_estate = isLindenEstate(); bool enable_ban = (god || owner || manager) && !linden_estate; getChildView("add_banned_avatar_btn")->setEnabled(enable_ban); - getChildView("remove_banned_avatar_btn")->setEnabled(enable_ban); + getChildView("remove_banned_avatar_btn")->setEnabled(has_banned_agent && enable_ban); + getChildView("banned_avatar_name_list")->setEnabled(god || owner || manager); getChildView("message_estate_btn")->setEnabled(god || owner || manager); getChildView("kick_user_from_estate_btn")->setEnabled(god || owner || manager); // estate managers can't add estate managers getChildView("add_estate_manager_btn")->setEnabled(god || owner); - getChildView("remove_estate_manager_btn")->setEnabled(god || owner); + getChildView("remove_estate_manager_btn")->setEnabled(has_estate_manager && (god || owner)); getChildView("estate_manager_name_list")->setEnabled(god || owner); refresh(); @@ -1979,10 +1987,8 @@ bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region) void LLPanelEstateInfo::updateChild(LLUICtrl* child_ctrl) { - if (checkRemovalButton(child_ctrl->getName())) - { - // do nothing - } + // Ensure appropriate state of the management ui. + updateControls(gAgent.getRegion()); } bool LLPanelEstateInfo::estateUpdate(LLMessageSystem* msg) @@ -2080,23 +2086,8 @@ void LLPanelEstateInfo::refreshFromEstate() getChild("limit_payment")->setValue(estate_info.getDenyAnonymous()); getChild("limit_age_verified")->setValue(estate_info.getDenyAgeUnverified()); - // If visible from mainland, disable the access allowed - // UI, as anyone can teleport there. - // However, gods need to be able to edit the access list for - // linden estates, regardless of visibility, to allow object - // and L$ transfers. - { - bool visible_from_mainland = estate_info.getIsExternallyVisible(); - bool god = gAgent.isGodlike(); - bool linden_estate = isLindenEstate(); - - bool enable_agent = (!visible_from_mainland || (god && linden_estate)); - bool enable_group = enable_agent; - bool enable_ban = !linden_estate; - - setAccessAllowedEnabled(enable_agent, enable_group, enable_ban); - } - + // Ensure appriopriate state of the management UI + updateControls(gAgent.getRegion()); refresh(); } @@ -2225,47 +2216,6 @@ void LLPanelEstateInfo::setOwnerName(const std::string& name) getChild("estate_owner")->setValue(LLSD(name)); } -void LLPanelEstateInfo::setAccessAllowedEnabled(bool enable_agent, - bool enable_group, - bool enable_ban) -{ - getChildView("allow_resident_label")->setEnabled(enable_agent); - getChildView("allowed_avatar_name_list")->setEnabled(enable_agent); - getChildView("allowed_avatar_name_list")->setVisible( enable_agent); - getChildView("add_allowed_avatar_btn")->setEnabled(enable_agent); - getChildView("remove_allowed_avatar_btn")->setEnabled(enable_agent); - - // Groups - getChildView("allow_group_label")->setEnabled(enable_group); - getChildView("allowed_group_name_list")->setEnabled(enable_group); - getChildView("allowed_group_name_list")->setVisible( enable_group); - getChildView("add_allowed_group_btn")->setEnabled(enable_group); - getChildView("remove_allowed_group_btn")->setEnabled(enable_group); - - // Ban - getChildView("ban_resident_label")->setEnabled(enable_ban); - getChildView("banned_avatar_name_list")->setEnabled(enable_ban); - getChildView("banned_avatar_name_list")->setVisible( enable_ban); - getChildView("add_banned_avatar_btn")->setEnabled(enable_ban); - getChildView("remove_banned_avatar_btn")->setEnabled(enable_ban); - - // Update removal buttons if needed - if (enable_agent) - { - checkRemovalButton("allowed_avatar_name_list"); - } - - if (enable_group) - { - checkRemovalButton("allowed_group_name_list"); - } - - if (enable_ban) - { - checkRemovalButton("banned_avatar_name_list"); - } -} - void LLPanelEstateInfo::clearAccessLists() { LLNameListCtrl* name_list = getChild("allowed_avatar_name_list"); @@ -2279,39 +2229,7 @@ void LLPanelEstateInfo::clearAccessLists() { name_list->deleteAllItems(); } -} - -// enables/disables the "remove" button for the various allow/ban lists -BOOL LLPanelEstateInfo::checkRemovalButton(std::string name) -{ - std::string btn_name = ""; - if (name == "allowed_avatar_name_list") - { - btn_name = "remove_allowed_avatar_btn"; - } - else if (name == "allowed_group_name_list") - { - btn_name = "remove_allowed_group_btn"; - } - else if (name == "banned_avatar_name_list") - { - btn_name = "remove_banned_avatar_btn"; - } - else if (name == "estate_manager_name_list") - { - //ONLY OWNER CAN ADD /DELET ESTATE MANAGER - LLViewerRegion* region = gAgent.getRegion(); - if (region && (region->getOwner() == gAgent.getID())) - { - btn_name = "remove_estate_manager_btn"; - } - } - - // enable the remove button if something is selected - LLNameListCtrl* name_list = getChild(name); - getChildView(btn_name)->setEnabled(name_list && name_list->getFirstSelected() ? TRUE : FALSE); - - return (btn_name != ""); + updateControls(gAgent.getRegion()); } // static @@ -2792,15 +2710,15 @@ bool LLDispatchSetEstateAccess::operator()( if (allowed_agent_name_list) { - //allowed_agent_name_list->deleteAllItems(); + // Don't sort these as we add them, sort them when we are done. + allowed_agent_name_list->clearSortOrder(); for (S32 i = 0; i < num_allowed_agents && i < ESTATE_MAX_ACCESS_IDS; i++) { LLUUID id; memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ allowed_agent_name_list->addNameItem(id); } - panel->getChildView("remove_allowed_avatar_btn")->setEnabled(allowed_agent_name_list->getFirstSelected() ? TRUE : FALSE); - allowed_agent_name_list->sortByColumnIndex(0, TRUE); + allowed_agent_name_list->sortByName(TRUE); } } @@ -2817,6 +2735,8 @@ bool LLDispatchSetEstateAccess::operator()( if (allowed_group_name_list) { + // Don't sort these as we add them, sort them when we are done. + allowed_group_name_list->clearSortOrder(); allowed_group_name_list->deleteAllItems(); for (S32 i = 0; i < num_allowed_groups && i < ESTATE_MAX_GROUP_IDS; i++) { @@ -2824,8 +2744,7 @@ bool LLDispatchSetEstateAccess::operator()( memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ allowed_group_name_list->addGroupNameItem(id); } - panel->getChildView("remove_allowed_group_btn")->setEnabled(allowed_group_name_list->getFirstSelected() ? TRUE : FALSE); - allowed_group_name_list->sortByColumnIndex(0, TRUE); + allowed_group_name_list->sortByName(TRUE); } } @@ -2849,15 +2768,16 @@ bool LLDispatchSetEstateAccess::operator()( if (banned_agent_name_list) { - //banned_agent_name_list->deleteAllItems(); + // Don't sort these as we add them, sort them when we are done. + banned_agent_name_list->clearSortOrder(); + for (S32 i = 0; i < num_banned_agents && i < ESTATE_MAX_ACCESS_IDS; i++) { LLUUID id; memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ banned_agent_name_list->addNameItem(id); } - panel->getChildView("remove_banned_avatar_btn")->setEnabled(banned_agent_name_list->getFirstSelected() ? TRUE : FALSE); - banned_agent_name_list->sortByColumnIndex(0, TRUE); + banned_agent_name_list->sortByName(TRUE); } } @@ -2872,6 +2792,9 @@ bool LLDispatchSetEstateAccess::operator()( panel->getChild("estate_manager_name_list"); if (estate_manager_name_list) { + // Don't sort these as we add them, sort them when we are done. + estate_manager_name_list->clearSortOrder(); + estate_manager_name_list->deleteAllItems(); // Clear existing entries // There should be only ESTATE_MAX_MANAGERS people in the list, but if the database gets more (SL-46107) don't @@ -2883,11 +2806,13 @@ bool LLDispatchSetEstateAccess::operator()( memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ estate_manager_name_list->addNameItem(id); } - panel->getChildView("remove_estate_manager_btn")->setEnabled(estate_manager_name_list->getFirstSelected() ? TRUE : FALSE); - estate_manager_name_list->sortByColumnIndex(0, TRUE); + estate_manager_name_list->sortByName(TRUE); } } + // Update the buttons which may change based on the list contents but also needs to account for general access features. + panel->updateControls(gAgent.getRegion()); + return true; } diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index e36ef4604b..f0499f1903 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -312,9 +312,6 @@ public: const std::string getOwnerName() const; void setOwnerName(const std::string& name); - // If visible from mainland, allowed agent and allowed groups - // are ignored, so must disable UI. - void setAccessAllowedEnabled(bool enable_agent, bool enable_group, bool enable_ban); protected: virtual BOOL sendUpdate(); // confirmation dialog callback @@ -324,7 +321,6 @@ protected: void commitEstateManagers(); void clearAccessLists(); - BOOL checkRemovalButton(std::string name); BOOL checkSunHourSlider(LLUICtrl* child_ctrl); U32 mEstateID; diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp index 2b34b72055..9157389187 100644 --- a/indra/newview/llfloatertexturefetchdebugger.cpp +++ b/indra/newview/llfloatertexturefetchdebugger.cpp @@ -59,12 +59,15 @@ LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key) mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisCache", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisCache, this)); mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisHTTP", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP, this)); + mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchAllCache", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchAllCache, this)); + mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchAllHTTP", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchAllHTTP, this)); } //---------------------------------------------- BOOL LLFloaterTextureFetchDebugger::postBuild(void) { mDebugger = LLAppViewer::getTextureFetch()->getFetchDebugger(); + mStartStatus = (S32)LLTextureFetchDebugger::IDLE; //set states for buttons mButtonStateMap["start_btn"] = true; @@ -76,8 +79,10 @@ BOOL LLFloaterTextureFetchDebugger::postBuild(void) mButtonStateMap["decode_btn"] = false; mButtonStateMap["gl_btn"] = false; - mButtonStateMap["refetchviscache_btn"] = true; - mButtonStateMap["refetchvishttp_btn"] = true; + mButtonStateMap["refetchviscache_btn"] = false; + mButtonStateMap["refetchvishttp_btn"] = false; + mButtonStateMap["refetchallcache_btn"] = false; + mButtonStateMap["refetchallhttp_btn"] = false; updateButtons(); @@ -89,7 +94,7 @@ BOOL LLFloaterTextureFetchDebugger::postBuild(void) LLFloaterTextureFetchDebugger::~LLFloaterTextureFetchDebugger() { //stop everything - mDebugger->stopDebug(); + mDebugger->setStopDebug(); } void LLFloaterTextureFetchDebugger::updateButtons() @@ -118,47 +123,81 @@ void LLFloaterTextureFetchDebugger::disableButtons() childDisable("gl_btn"); childDisable("refetchviscache_btn"); childDisable("refetchvishttp_btn"); + childDisable("refetchallcache_btn"); + childDisable("refetchallhttp_btn"); +} +void LLFloaterTextureFetchDebugger::setStartStatus(S32 status) +{ + llassert_always(LLTextureFetchDebugger::IDLE == (LLTextureFetchDebugger::e_debug_state)mStartStatus) ; + mStartStatus = status; +} + +bool LLFloaterTextureFetchDebugger::idleStart() +{ + if(mStartStatus != (S32)LLTextureFetchDebugger::IDLE) + { + mDebugger->startWork((LLTextureFetchDebugger::e_debug_state)mStartStatus); + mStartStatus = (S32)LLTextureFetchDebugger::IDLE; + return true; + } + + return false; } void LLFloaterTextureFetchDebugger::idle() { - LLTextureFetchDebugger::e_debug_state state = mDebugger->getState(); - - if(mDebugger->update()) + if(idleStart()) + { + return; + } + + const F32 max_time = 0.005f; //5ms + LLTextureFetchDebugger::e_debug_state state = mDebugger->getState(); + if(mDebugger->update(max_time)) { switch(state) { case LLTextureFetchDebugger::IDLE: break; - case LLTextureFetchDebugger::READ_CACHE: - mButtonStateMap["cachewrite_btn"] = true; - mButtonStateMap["decode_btn"] = true; - updateButtons(); + case LLTextureFetchDebugger::START_DEBUG: + mButtonStateMap["cacheread_btn"] = true; + mButtonStateMap["http_btn"] = true; + mButtonStateMap["refetchviscache_btn"] = true; + mButtonStateMap["refetchvishttp_btn"] = true; + mButtonStateMap["refetchallcache_btn"] = true; + mButtonStateMap["refetchallhttp_btn"] = true; break; - case LLTextureFetchDebugger::WRITE_CACHE: - updateButtons(); + case LLTextureFetchDebugger::READ_CACHE: + mButtonStateMap["decode_btn"] = true; + break; + case LLTextureFetchDebugger::WRITE_CACHE: break; case LLTextureFetchDebugger::DECODING: - mButtonStateMap["gl_btn"] = true; - updateButtons(); + mButtonStateMap["gl_btn"] = true; break; case LLTextureFetchDebugger::HTTP_FETCHING: mButtonStateMap["cacheread_btn"] = true; mButtonStateMap["cachewrite_btn"] = true; - mButtonStateMap["decode_btn"] = true; - updateButtons(); + mButtonStateMap["decode_btn"] = true; break; - case LLTextureFetchDebugger::GL_TEX: - updateButtons(); + case LLTextureFetchDebugger::GL_TEX: break; - case LLTextureFetchDebugger::REFETCH_VIS_CACHE: - updateButtons(); - case LLTextureFetchDebugger::REFETCH_VIS_HTTP: - updateButtons(); + case LLTextureFetchDebugger::REFETCH_VIS_CACHE: + break; + case LLTextureFetchDebugger::REFETCH_VIS_HTTP: + break; + case LLTextureFetchDebugger::REFETCH_ALL_CACHE: + break; + case LLTextureFetchDebugger::REFETCH_ALL_HTTP: break; default: break; } + + if(state != LLTextureFetchDebugger::IDLE) + { + updateButtons(); + } } } @@ -172,11 +211,10 @@ void LLFloaterTextureFetchDebugger::onClickStart() { disableButtons(); - mDebugger->startDebug(); + setStartStatus((S32)LLTextureFetchDebugger::START_DEBUG); mButtonStateMap["start_btn"] = false; - mButtonStateMap["cacheread_btn"] = true; - mButtonStateMap["http_btn"] = true; + updateButtons(); } @@ -185,7 +223,9 @@ void LLFloaterTextureFetchDebugger::onClickClose() setVisible(FALSE); //stop everything - mDebugger->stopDebug(); + mDebugger->setStopDebug(); + + delete this; } void LLFloaterTextureFetchDebugger::onClickClear() @@ -203,7 +243,7 @@ void LLFloaterTextureFetchDebugger::onClickClear() updateButtons(); //stop everything - mDebugger->stopDebug(); + mDebugger->setStopDebug(); mDebugger->clearHistory(); } @@ -211,49 +251,63 @@ void LLFloaterTextureFetchDebugger::onClickCacheRead() { disableButtons(); - mDebugger->debugCacheRead(); + setStartStatus((S32)LLTextureFetchDebugger::READ_CACHE); } void LLFloaterTextureFetchDebugger::onClickCacheWrite() { disableButtons(); - mDebugger->debugCacheWrite(); + setStartStatus((S32)LLTextureFetchDebugger::WRITE_CACHE); } void LLFloaterTextureFetchDebugger::onClickHTTPLoad() { disableButtons(); - mDebugger->debugHTTP(); + setStartStatus((S32)LLTextureFetchDebugger::HTTP_FETCHING); } void LLFloaterTextureFetchDebugger::onClickDecode() { disableButtons(); - mDebugger->debugDecoder(); + setStartStatus((S32)LLTextureFetchDebugger::DECODING); } void LLFloaterTextureFetchDebugger::onClickGLTexture() { disableButtons(); - mDebugger->debugGLTextureCreation(); + setStartStatus((S32)LLTextureFetchDebugger::GL_TEX); } void LLFloaterTextureFetchDebugger::onClickRefetchVisCache() { disableButtons(); - mDebugger->debugRefetchVisibleFromCache(); + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_VIS_CACHE); } void LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP() { disableButtons(); - mDebugger->debugRefetchVisibleFromHTTP(); + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_VIS_HTTP); +} + +void LLFloaterTextureFetchDebugger::onClickRefetchAllCache() +{ + disableButtons(); + + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_ALL_CACHE); +} + +void LLFloaterTextureFetchDebugger::onClickRefetchAllHTTP() +{ + disableButtons(); + + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_ALL_HTTP); } void LLFloaterTextureFetchDebugger::draw() @@ -368,8 +422,22 @@ void LLFloaterTextureFetchDebugger::draw() else { getChild("total_time_refetch_vis_cache_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisCacheTime())); - getChild("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10)); - getChild("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f)); + getChild("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedVisData() >> 10)); + getChild("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedVisPixels() / 1000000.f)); + } + + //total time on refetching all textures from cache + if(mDebugger->getRefetchAllCacheTime() < 0.f) + { + getChild("total_time_refetch_all_cache_label")->setTextArg("[TIME]", std::string("----")); + getChild("total_time_refetch_all_cache_label")->setTextArg("[SIZE]", std::string("----")); + getChild("total_time_refetch_all_cache_label")->setTextArg("[PIXEL]", std::string("----")); + } + else + { + getChild("total_time_refetch_all_cache_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchAllCacheTime())); + getChild("total_time_refetch_all_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedAllData() >> 10)); + getChild("total_time_refetch_all_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedAllPixels() / 1000000.f)); } //total time on refetching visible textures from http @@ -382,8 +450,22 @@ void LLFloaterTextureFetchDebugger::draw() else { getChild("total_time_refetch_vis_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisHTTPTime())); - getChild("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10)); - getChild("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f)); + getChild("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedVisData() >> 10)); + getChild("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedVisPixels() / 1000000.f)); + } + + //total time on refetching all textures from http + if(mDebugger->getRefetchAllHTTPTime() < 0.f) + { + getChild("total_time_refetch_all_http_label")->setTextArg("[TIME]", std::string("----")); + getChild("total_time_refetch_all_http_label")->setTextArg("[SIZE]", std::string("----")); + getChild("total_time_refetch_all_http_label")->setTextArg("[PIXEL]", std::string("----")); + } + else + { + getChild("total_time_refetch_all_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchAllHTTPTime())); + getChild("total_time_refetch_all_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedAllData() >> 10)); + getChild("total_time_refetch_all_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedAllPixels() / 1000000.f)); } LLFloater::draw(); diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h index 33012c6a3d..096ad88e07 100644 --- a/indra/newview/llfloatertexturefetchdebugger.h +++ b/indra/newview/llfloatertexturefetchdebugger.h @@ -53,6 +53,8 @@ public: void onClickRefetchVisCache(); void onClickRefetchVisHTTP(); + void onClickRefetchAllCache(); + void onClickRefetchAllHTTP(); public: void idle() ; @@ -63,9 +65,12 @@ private: void updateButtons(); void disableButtons(); + void setStartStatus(S32 status); + bool idleStart(); private: LLTextureFetchDebugger* mDebugger; std::map mButtonStateMap; + S32 mStartStatus; }; #endif // LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index 87d048c15b..2d91a61b54 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -82,6 +82,7 @@ LLFloaterTopObjects::LLFloaterTopObjects(const LLSD& key) mCommitCallbackRegistrar.add("TopObjects.Refresh", boost::bind(&LLFloaterTopObjects::onRefresh, this)); mCommitCallbackRegistrar.add("TopObjects.GetByObjectName", boost::bind(&LLFloaterTopObjects::onGetByObjectName, this)); mCommitCallbackRegistrar.add("TopObjects.GetByOwnerName", boost::bind(&LLFloaterTopObjects::onGetByOwnerName, this)); + mCommitCallbackRegistrar.add("TopObjects.GetByParcelName", boost::bind(&LLFloaterTopObjects::onGetByParcelName, this)); mCommitCallbackRegistrar.add("TopObjects.CommitObjectsList",boost::bind(&LLFloaterTopObjects::onCommitObjectsList, this)); } @@ -99,21 +100,6 @@ BOOL LLFloaterTopObjects::postBuild() setDefaultBtn("show_beacon_btn"); - /* - LLLineEditor* line_editor = getChild("owner_name_editor"); - if (line_editor) - { - line_editor->setCommitOnFocusLost(FALSE); - line_editor->setCommitCallback(onGetByOwnerName, this); - } - - line_editor = getChild("object_name_editor"); - if (line_editor) - { - line_editor->setCommitOnFocusLost(FALSE); - line_editor->setCommitCallback(onGetByObjectName, this); - }*/ - mCurrentMode = STAT_REPORT_TOP_SCRIPTS; mFlags = 0; mFilter.clear(); @@ -168,9 +154,11 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) F32 score; std::string name_buf; std::string owner_buf; + std::string parcel_buf("unknown"); F32 mono_score = 0.f; bool have_extended_data = false; S32 public_urls = 0; + F32 script_memory = 0.f; msg->getU32Fast(_PREHASH_ReportData, _PREHASH_TaskLocalID, task_local_id, block); msg->getUUIDFast(_PREHASH_ReportData, _PREHASH_TaskID, task_id, block); @@ -180,12 +168,18 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) msg->getF32Fast(_PREHASH_ReportData, _PREHASH_Score, score, block); msg->getStringFast(_PREHASH_ReportData, _PREHASH_TaskName, name_buf, block); msg->getStringFast(_PREHASH_ReportData, _PREHASH_OwnerName, owner_buf, block); + if(msg->has("DataExtended")) { have_extended_data = true; msg->getU32("DataExtended", "TimeStamp", time_stamp, block); msg->getF32("DataExtended", "MonoScore", mono_score, block); msg->getS32("DataExtended", "PublicURLs", public_urls, block); + if (msg->getSize("DataExtended", "ParcelName") > 0) + { + msg->getString("DataExtended", "ParcelName", parcel_buf, block); + msg->getF32("DataExtended", "Size", script_memory, block); + } } LLSD element; @@ -193,13 +187,14 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) element["id"] = task_id; LLSD columns; - columns[0]["column"] = "score"; - columns[0]["value"] = llformat("%0.3f", score); - columns[0]["font"] = "SANSSERIF"; + S32 column_num = 0; + columns[column_num]["column"] = "score"; + columns[column_num]["value"] = llformat("%0.3f", score); + columns[column_num++]["font"] = "SANSSERIF"; - columns[1]["column"] = "name"; - columns[1]["value"] = name_buf; - columns[1]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "name"; + columns[column_num]["value"] = name_buf; + columns[column_num++]["font"] = "SANSSERIF"; // Owner names can have trailing spaces sent from server LLStringUtil::trim(owner_buf); @@ -215,28 +210,33 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) // ...just strip out legacy "Resident" name owner_buf = LLCacheName::cleanFullName(owner_buf); } - columns[2]["column"] = "owner"; - columns[2]["value"] = owner_buf; - columns[2]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "owner"; + columns[column_num]["value"] = owner_buf; + columns[column_num++]["font"] = "SANSSERIF"; - columns[3]["column"] = "location"; - columns[3]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z); - columns[3]["font"] = "SANSSERIF"; - columns[4]["column"] = "time"; - columns[4]["type"] = "date"; - columns[4]["value"] = LLDate((time_t)time_stamp); - columns[4]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "location"; + columns[column_num]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z); + columns[column_num++]["font"] = "SANSSERIF"; + + columns[column_num]["column"] = "parcel"; + columns[column_num]["value"] = parcel_buf; + columns[column_num++]["font"] = "SANSSERIF"; + + columns[column_num]["column"] = "time"; + columns[column_num]["type"] = "date"; + columns[column_num]["value"] = LLDate((time_t)time_stamp); + columns[column_num++]["font"] = "SANSSERIF"; if (mCurrentMode == STAT_REPORT_TOP_SCRIPTS && have_extended_data) { - columns[5]["column"] = "mono_time"; - columns[5]["value"] = llformat("%0.3f", mono_score); - columns[5]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "memory"; + columns[column_num]["value"] = llformat("%0.0f", (script_memory / 1000.f)); + columns[column_num++]["font"] = "SANSSERIF"; - columns[6]["column"] = "URLs"; - columns[6]["value"] = llformat("%d", public_urls); - columns[6]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "URLs"; + columns[column_num]["value"] = llformat("%d", public_urls); + columns[column_num++]["font"] = "SANSSERIF"; } element["columns"] = columns; list->addElement(element); @@ -260,18 +260,18 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) { setTitle(getString("top_scripts_title")); list->setColumnLabel("score", getString("scripts_score_label")); - list->setColumnLabel("mono_time", getString("scripts_mono_time_label")); LLUIString format = getString("top_scripts_text"); format.setArg("[COUNT]", llformat("%d", total_count)); - format.setArg("[TIME]", llformat("%0.1f", mtotalScore)); + format.setArg("[TIME]", llformat("%0.3f", mtotalScore)); getChild("title_text")->setValue(LLSD(format)); } else { setTitle(getString("top_colliders_title")); list->setColumnLabel("score", getString("colliders_score_label")); - list->setColumnLabel("mono_time", ""); + list->setColumnLabel("URLs", ""); + list->setColumnLabel("memory", ""); LLUIString format = getString("top_colliders_text"); format.setArg("[COUNT]", llformat("%d", total_count)); getChild("title_text")->setValue(LLSD(format)); @@ -301,6 +301,7 @@ void LLFloaterTopObjects::updateSelectionInfo() { getChild("object_name_editor")->setValue(sli->getColumn(1)->getValue().asString()); getChild("owner_name_editor")->setValue(sli->getColumn(2)->getValue().asString()); + getChild("parcel_name_editor")->setValue(sli->getColumn(4)->getValue().asString()); } } @@ -480,6 +481,15 @@ void LLFloaterTopObjects::onGetByOwnerName() onRefresh(); } + +void LLFloaterTopObjects::onGetByParcelName() +{ + mFlags = STAT_FILTER_BY_PARCEL_NAME; + mFilter = getChild("parcel_name_editor")->getValue().asString(); + onRefresh(); +} + + void LLFloaterTopObjects::showBeacon() { LLScrollListCtrl* list = getChild("objects_list"); diff --git a/indra/newview/llfloatertopobjects.h b/indra/newview/llfloatertopobjects.h index a608ca20f1..6edc46cf79 100644 --- a/indra/newview/llfloatertopobjects.h +++ b/indra/newview/llfloatertopobjects.h @@ -73,9 +73,7 @@ private: void onGetByOwnerName(); void onGetByObjectName(); - -// static void onGetByOwnerNameClicked(void* data) { onGetByOwnerName(NULL, data); }; -// static void onGetByObjectNameClicked(void* data) { onGetByObjectName(NULL, data); }; + void onGetByParcelName(); void showBeacon(); diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index d741b5b133..15e0b89f6c 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -137,7 +137,7 @@ public: virtual ~LLFloaterUIPreview(); std::string getLocStr(S32 ID); // fetches the localization string based on what is selected in the drop-down menu - void displayFloater(BOOL click, S32 ID, bool save = false); // needs to be public so live file can call it when it finds an update + void displayFloater(BOOL click, S32 ID); // needs to be public so live file can call it when it finds an update /*virtual*/ BOOL postBuild(); /*virtual*/ void onClose(bool app_quitting); @@ -291,7 +291,8 @@ LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater { mSavedLocalization = LLUI::sSettingGroups["config"]->getString("Language"); // save current localization setting LLUI::sSettingGroups["config"]->setString("Language", floater->getLocStr(ID));// hack language to be the one we want to preview floaters in - LLUI::setupPaths(); // forcibly reset XUI paths with this new language + // forcibly reset XUI paths with this new language + gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), floater->getLocStr(ID)); } // Actually reset in destructor @@ -299,7 +300,8 @@ LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater LLLocalizationResetForcer::~LLLocalizationResetForcer() { LLUI::sSettingGroups["config"]->setString("Language", mSavedLocalization); // reset language to what it was before we changed it - LLUI::setupPaths(); // forcibly reset XUI paths with this new language + // forcibly reset XUI paths with this new language + gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), mSavedLocalization); } // Live file constructor @@ -488,7 +490,7 @@ BOOL LLFloaterUIPreview::postBuild() { if((found = iter.next(language_directory))) // get next directory { - std::string full_path = xui_dir + language_directory; + std::string full_path = gDirUtilp->add(xui_dir, language_directory); if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it { continue; @@ -773,7 +775,8 @@ void LLFloaterUIPreview::onClickDisplayFloater(S32 caller_id) // Saves the current floater/panel void LLFloaterUIPreview::onClickSaveFloater(S32 caller_id) { - displayFloater(TRUE, caller_id, true); + displayFloater(TRUE, caller_id); + popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files"); } // Saves all floater/panels @@ -784,25 +787,15 @@ void LLFloaterUIPreview::onClickSaveAll(S32 caller_id) for (int index = 0; index < listSize; index++) { mFileList->selectNthItem(index); - displayFloater(TRUE, caller_id, true); + displayFloater(TRUE, caller_id); } -} - -// Given path to floater or panel XML file "filename.xml", -// returns "filename_new.xml" -static std::string append_new_to_xml_filename(const std::string& path) -{ - std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), path); - std::string::size_type extension_pos = full_filename.rfind(".xml"); - full_filename.resize(extension_pos); - full_filename += "_new.xml"; - return full_filename; + popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files"); } // Actually display the floater // Only set up a new live file if this came from a click (at which point there should be no existing live file), rather than from the live file's update itself; // otherwise, we get an infinite loop as the live file keeps recreating itself. That means this function is generally called twice. -void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) +void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID) { // Convince UI that we're in a different language (the one selected on the drop-down menu) LLLocalizationResetForcer reset_forcer(this, ID); // save old language in reset forcer object (to be reset upon destruction when it falls out of scope) @@ -843,48 +836,13 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) if(!strncmp(path.c_str(),"floater_",8) || !strncmp(path.c_str(), "inspect_", 8)) // if it's a floater { - if (save) - { - LLXMLNodePtr floater_write = new LLXMLNode(); - (*floaterp)->buildFromFile(path, floater_write); // just build it - - if (!floater_write->isNull()) - { - std::string full_filename = append_new_to_xml_filename(path); - LLFILE* floater_temp = LLFile::fopen(full_filename.c_str(), "w"); - LLXMLNode::writeHeaderToFile(floater_temp); - const bool use_type_decorations = false; - floater_write->writeToFile(floater_temp, std::string(), use_type_decorations); - fclose(floater_temp); - } - } - else - { - (*floaterp)->buildFromFile(path); // just build it - (*floaterp)->openFloater((*floaterp)->getKey()); - (*floaterp)->setCanResize((*floaterp)->isResizable()); - } - + (*floaterp)->buildFromFile(path); // just build it + (*floaterp)->openFloater((*floaterp)->getKey()); + (*floaterp)->setCanResize((*floaterp)->isResizable()); } else if (!strncmp(path.c_str(),"menu_",5)) // if it's a menu { - if (save) - { - LLXMLNodePtr menu_write = new LLXMLNode(); - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile(path, gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance(), menu_write); - - if (!menu_write->isNull()) - { - std::string full_filename = append_new_to_xml_filename(path); - LLFILE* menu_temp = LLFile::fopen(full_filename.c_str(), "w"); - LLXMLNode::writeHeaderToFile(menu_temp); - const bool use_type_decorations = false; - menu_write->writeToFile(menu_temp, std::string(), use_type_decorations); - fclose(menu_temp); - } - - delete menu; - } + // former 'save' processing excised } else // if it is a panel... { @@ -896,39 +854,21 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) LLPanel::Params panel_params; LLPanel* panel = LLUICtrlFactory::create(panel_params); // create a new panel - if (save) - { - LLXMLNodePtr panel_write = new LLXMLNode(); - panel->buildFromFile(path, panel_write); // build it - - if (!panel_write->isNull()) - { - std::string full_filename = append_new_to_xml_filename(path); - LLFILE* panel_temp = LLFile::fopen(full_filename.c_str(), "w"); - LLXMLNode::writeHeaderToFile(panel_temp); - const bool use_type_decorations = false; - panel_write->writeToFile(panel_temp, std::string(), use_type_decorations); - fclose(panel_temp); - } - } - else - { - panel->buildFromFile(path); // build it - LLRect new_size = panel->getRect(); // get its rectangle - panel->setOrigin(2,2); // reset its origin point so it's not offset by -left or other XUI attributes - (*floaterp)->setTitle(path); // use the file name as its title, since panels have no guaranteed meaningful name attribute - panel->setUseBoundingRect(TRUE); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements) - panel->updateBoundingRect(); // update bounding rect - LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect - LLRect new_rect = panel->getRect(); // get the panel's rect - new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible - LLRect floater_rect = new_rect; - floater_rect.stretch(4, 4); - (*floaterp)->reshape(floater_rect.getWidth(), floater_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions - panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed) - (*floaterp)->addChild(panel); // add panel as child - (*floaterp)->openFloater(); // open floater (needed?) - } + panel->buildFromFile(path); // build it + LLRect new_size = panel->getRect(); // get its rectangle + panel->setOrigin(2,2); // reset its origin point so it's not offset by -left or other XUI attributes + (*floaterp)->setTitle(path); // use the file name as its title, since panels have no guaranteed meaningful name attribute + panel->setUseBoundingRect(TRUE); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements) + panel->updateBoundingRect(); // update bounding rect + LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect + LLRect new_rect = panel->getRect(); // get the panel's rect + new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible + LLRect floater_rect = new_rect; + floater_rect.stretch(4, 4); + (*floaterp)->reshape(floater_rect.getWidth(), floater_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions + panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed) + (*floaterp)->addChild(panel); // add panel as child + (*floaterp)->openFloater(); // open floater (needed?) } if(ID == 1) @@ -964,7 +904,7 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) (*floaterp)->center(); addDependentFloater(*floaterp); - if(click && ID == 1 && !save) + if(click && ID == 1) { // set up live file to track it if(mLiveFile) diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index efffd0f98e..81eb1d397e 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -36,6 +36,7 @@ #include #include +#include "llappviewer.h" #include "llagent.h" #include "llui.h" #include "message.h" @@ -63,7 +64,7 @@ #pragma warning(pop) // Restore all warnings to the previous state #endif -const U32 MAX_CACHED_GROUPS = 10; +const U32 MAX_CACHED_GROUPS = 20; // // LLRoleActionSet @@ -234,8 +235,15 @@ LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) : mRoleDataComplete(FALSE), mRoleMemberDataComplete(FALSE), mGroupPropertiesDataComplete(FALSE), - mPendingRoleMemberRequest(FALSE) + mPendingRoleMemberRequest(FALSE), + mAccessTime(0.0f) { + mMemberVersion.generate(); +} + +void LLGroupMgrGroupData::setAccessed() +{ + mAccessTime = (F32)LLFrameTimer::getTotalSeconds(); } BOOL LLGroupMgrGroupData::getRoleData(const LLUUID& role_id, LLRoleData& role_data) @@ -311,14 +319,14 @@ void LLGroupMgrGroupData::setRoleData(const LLUUID& role_id, LLRoleData role_dat role_data.mChangeType = RC_UPDATE_DATA; } else - { + { role_data.mChangeType = RC_UPDATE_POWERS; } mRoleChanges[role_id] = role_data; } else - { + { llwarns << "Change being made to non-existant role " << role_id << llendl; } } @@ -417,6 +425,7 @@ void LLGroupMgrGroupData::removeMemberData() } mMembers.clear(); mMemberDataComplete = FALSE; + mMemberVersion.generate(); } void LLGroupMgrGroupData::removeRoleData() @@ -739,6 +748,7 @@ void LLGroupMgrGroupData::cancelRoleChanges() LLGroupMgr::LLGroupMgr() { + mLastGroupMembersRequestFrame = 0; } LLGroupMgr::~LLGroupMgr() @@ -937,6 +947,8 @@ void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data) } } + group_datap->mMemberVersion.generate(); + if (group_datap->mMembers.size() == (U32)group_datap->mMemberCount) { group_datap->mMemberDataComplete = TRUE; @@ -1360,7 +1372,7 @@ void LLGroupMgr::processCreateGroupReply(LLMessageSystem* msg, void ** data) LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) { - LLGroupMgrGroupData* group_datap; + LLGroupMgrGroupData* group_datap = NULL; group_map_t::iterator existing_group = LLGroupMgr::getInstance()->mGroups.find(id); if (existing_group == LLGroupMgr::getInstance()->mGroups.end()) @@ -1373,6 +1385,11 @@ LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) group_datap = existing_group->second; } + if (group_datap) + { + group_datap->setAccessed(); + } + return group_datap; } @@ -1413,25 +1430,41 @@ void LLGroupMgr::notifyObservers(LLGroupChange gc) void LLGroupMgr::addGroup(LLGroupMgrGroupData* group_datap) { - if (mGroups.size() > MAX_CACHED_GROUPS) + while (mGroups.size() >= MAX_CACHED_GROUPS) { - // get rid of groups that aren't observed - for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end() && mGroups.size() > MAX_CACHED_GROUPS / 2; ) + // LRU: Remove the oldest un-observed group from cache until group size is small enough + + F32 oldest_access = LLFrameTimer::getTotalSeconds(); + group_map_t::iterator oldest_gi = mGroups.end(); + + for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end(); ++gi ) { observer_multimap_t::iterator oi = mObservers.find(gi->first); if (oi == mObservers.end()) { - // not observed - LLGroupMgrGroupData* unobserved_groupp = gi->second; - delete unobserved_groupp; - mGroups.erase(gi++); - } - else - { - ++gi; + if (gi->second + && (gi->second->getAccessTime() < oldest_access)) + { + oldest_access = gi->second->getAccessTime(); + oldest_gi = gi; + } } } + + if (oldest_gi != mGroups.end()) + { + delete oldest_gi->second; + mGroups.erase(oldest_gi); + } + else + { + // All groups must be currently open, none to remove. + // Just add the new group anyway, but get out of this loop as it + // will never drop below max_cached_groups. + break; + } } + mGroups[group_datap->getID()] = group_datap; } @@ -1473,6 +1506,7 @@ void LLGroupMgr::sendGroupMembersRequest(const LLUUID& group_id) } } + void LLGroupMgr::sendGroupRoleDataRequest(const LLUUID& group_id) { lldebugs << "LLGroupMgr::sendGroupRoleDataRequest" << llendl; @@ -1741,8 +1775,6 @@ void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id, bool start_message = true; LLMessageSystem* msg = gMessageSystem; - - LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id); if (!group_datap) return; @@ -1803,8 +1835,193 @@ void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id, { gAgent.sendReliableMessage(); } + + group_datap->mMemberVersion.generate(); } + +// Responder class for capability group management +class GroupMemberDataResponder : public LLHTTPClient::Responder +{ +public: + GroupMemberDataResponder() {} + virtual ~GroupMemberDataResponder() {} + virtual void result(const LLSD& pContent); + virtual void error(U32 pStatus, const std::string& pReason); +private: + LLSD mMemberData; +}; + +void GroupMemberDataResponder::error(U32 pStatus, const std::string& pReason) +{ + LL_WARNS("GrpMgr") << "Error receiving group member data." << LL_ENDL; +} + +void GroupMemberDataResponder::result(const LLSD& content) +{ + LLGroupMgr::processCapGroupMembersRequest(content); +} + + +// static +void LLGroupMgr::sendCapGroupMembersRequest(const LLUUID& group_id) +{ + // Have we requested the information already this frame? + if(mLastGroupMembersRequestFrame == gFrameCount) + return; + + LLViewerRegion* currentRegion = gAgent.getRegion(); + // Thank you FS:Ansariel! + if(!currentRegion) + { + LL_WARNS("GrpMgr") << "Agent does not have a current region. Uh-oh!" << LL_ENDL; + return; + } + + // Check to make sure we have our capabilities + if(!currentRegion->capabilitiesReceived()) + { + LL_WARNS("GrpMgr") << " Capabilities not received!" << LL_ENDL; + return; + } + + // Get our capability + std::string cap_url = currentRegion->getCapability("GroupMemberData"); + + // Thank you FS:Ansariel! + if(cap_url.empty()) + { + LL_INFOS("GrpMgr") << "Region has no GroupMemberData capability. Falling back to UDP fetch." << LL_ENDL; + sendGroupMembersRequest(group_id); + return; + } + + // Post to our service. Add a body containing the group_id. + LLSD body = LLSD::emptyMap(); + body["group_id"] = group_id; + + LLHTTPClient::ResponderPtr grp_data_responder = new GroupMemberDataResponder(); + + // This could take a while to finish, timeout after 5 minutes. + LLHTTPClient::post(cap_url, body, grp_data_responder, LLSD(), 300); + + mLastGroupMembersRequestFrame = gFrameCount; +} + + +// static +void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) +{ + // Did we get anything in content? + if(!content.size()) + { + LL_DEBUGS("GrpMgr") << "No group member data received." << LL_ENDL; + return; + } + + // If we have no members, there's no reason to do anything else + S32 num_members = content["member_count"]; + if(num_members < 1) + return; + + LLUUID group_id = content["group_id"].asUUID(); + + LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id); + if(!group_datap) + { + LL_WARNS("GrpMgr") << "Received incorrect, possibly stale, group or request id" << LL_ENDL; + return; + } + + group_datap->mMemberCount = num_members; + + LLSD member_list = content["members"]; + LLSD titles = content["titles"]; + LLSD defaults = content["defaults"]; + + std::string online_status; + std::string title; + S32 contribution; + U64 member_powers; + // If this is changed to a bool, make sure to change the LLGroupMemberData constructor + BOOL is_owner; + + // Compute this once, rather than every time. + U64 default_powers = llstrtou64(defaults["default_powers"].asString().c_str(), NULL, 16); + + LLSD::map_const_iterator member_iter_start = member_list.beginMap(); + LLSD::map_const_iterator member_iter_end = member_list.endMap(); + for( ; member_iter_start != member_iter_end; ++member_iter_start) + { + // Reset defaults + online_status = "unknown"; + title = titles[0].asString(); + contribution = 0; + member_powers = default_powers; + is_owner = false; + + const LLUUID member_id(member_iter_start->first); + LLSD member_info = member_iter_start->second; + + if(member_info.has("last_login")) + { + online_status = member_info["last_login"].asString(); + if(online_status == "Online") + online_status = LLTrans::getString("group_member_status_online"); + else + formatDateString(online_status); + } + + if(member_info.has("title")) + title = titles[member_info["title"].asInteger()].asString(); + + if(member_info.has("powers")) + member_powers = llstrtou64(member_info["powers"].asString().c_str(), NULL, 16); + + if(member_info.has("donated_square_meters")) + contribution = member_info["donated_square_meters"]; + + if(member_info.has("owner")) + is_owner = true; + + LLGroupMemberData* data = new LLGroupMemberData(member_id, + contribution, + member_powers, + title, + online_status, + is_owner); + + group_datap->mMembers[member_id] = data; + } + + group_datap->mMemberVersion.generate(); + + // Technically, we have this data, but to prevent completely overhauling + // this entire system (it would be nice, but I don't have the time), + // I'm going to be dumb and just call services I most likely don't need + // with the thought being that the system might need it to be done. + // + // TODO: + // Refactor to reduce multiple calls for data we already have. + if(group_datap->mTitles.size() < 1) + LLGroupMgr::getInstance()->sendGroupTitlesRequest(group_id); + + + group_datap->mMemberDataComplete = TRUE; + group_datap->mMemberRequestID.setNull(); + // Make the role-member data request + if (group_datap->mPendingRoleMemberRequest) + { + group_datap->mPendingRoleMemberRequest = FALSE; + LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_id); + } + + group_datap->mChanged = TRUE; + LLGroupMgr::getInstance()->notifyObservers(GC_MEMBER_DATA); + +} + + void LLGroupMgr::sendGroupRoleChanges(const LLUUID& group_id) { lldebugs << "LLGroupMgr::sendGroupRoleChanges" << llendl; diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index faf0531c10..d8c1ab7ef5 100644 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -86,7 +86,7 @@ public: BOOL isInRole(const LLUUID& role_id) { return (mRolesList.find(role_id) != mRolesList.end()); } -protected: +private: LLUUID mID; S32 mContribution; U64 mAgentPowers; @@ -233,6 +233,11 @@ public: BOOL isRoleMemberDataComplete() { return mRoleMemberDataComplete; } BOOL isGroupPropertiesDataComplete() { return mGroupPropertiesDataComplete; } + F32 getAccessTime() const { return mAccessTime; } + void setAccessed(); + + const LLUUID& getMemberVersion() const { return mMemberVersion; } + public: typedef std::map member_list_t; typedef std::map role_list_t; @@ -280,6 +285,10 @@ private: BOOL mGroupPropertiesDataComplete; BOOL mPendingRoleMemberRequest; + F32 mAccessTime; + + // Generate a new ID every time mMembers + LLUUID mMemberVersion; }; struct LLRoleAction @@ -336,6 +345,10 @@ public: static void sendGroupMemberEjects(const LLUUID& group_id, uuid_vec_t& member_ids); + // BAKER + void sendCapGroupMembersRequest(const LLUUID& group_id); + static void processCapGroupMembersRequest(const LLSD& content); + void cancelGroupRoleChanges(const LLUUID& group_id); static void processGroupPropertiesReply(LLMessageSystem* msg, void** data); @@ -371,6 +384,8 @@ private: typedef std::set observer_set_t; typedef std::map observer_map_t; observer_map_t mParticularObservers; + + S32 mLastGroupMembersRequestFrame; }; diff --git a/indra/newview/llhints.cpp b/indra/newview/llhints.cpp index e15862e2a4..197408b40e 100644 --- a/indra/newview/llhints.cpp +++ b/indra/newview/llhints.cpp @@ -171,12 +171,12 @@ LLHintPopup::LLHintPopup(const LLHintPopup::Params& p) } if (p.hint_image.isProvided()) { - buildFromFile("panel_hint_image.xml", NULL, p); + buildFromFile("panel_hint_image.xml", p); getChild("hint_image")->setImage(p.hint_image()); } else { - buildFromFile( "panel_hint.xml", NULL, p); + buildFromFile( "panel_hint.xml", p); } } diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b819100b9b..14a228df1c 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -71,6 +71,9 @@ #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llwearablelist.h" +#include "lllandmarkactions.h" + +void copy_slurl_to_clipboard_callback_inv(const std::string& slurl); // Marketplace outbox current disabled #define ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU 1 @@ -1399,6 +1402,29 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); } + else if ("copy_slurl" == action) + { + LLViewerInventoryItem* item = static_cast(getItem()); + if(item) + { + LLUUID asset_id = item->getAssetUUID(); + LLLandmark* landmark = gLandmarkList.getAsset(asset_id); + if (landmark) + { + LLVector3d global_pos; + landmark->getGlobalPos(global_pos); + LLLandmarkActions::getSLURLfromPosGlobal(global_pos, ©_slurl_to_clipboard_callback_inv, true); + } + } + } +} + +void copy_slurl_to_clipboard_callback_inv(const std::string& slurl) +{ + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); } void LLItemBridge::selectItem() @@ -2551,14 +2577,23 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer) LLInventoryModel::item_array_t* item_array; gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array); - S32 item_count = item_array->count(); - S32 cat_count = cat_array->count(); - + S32 item_count(0); + if( item_array ) + { + item_count = item_array->count(); + } + + S32 cat_count(0); + if( cat_array ) + { + cat_count = cat_array->count(); + } + // Move to next if current folder empty if ((item_count == 0) && (cat_count == 0)) - { + { continue; - } + } uuid_vec_t ids; LLRightClickInventoryFetchObserver* outfit = NULL; @@ -4403,6 +4438,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } items.push_back(std::string("Landmark Separator")); + items.push_back(std::string("url_copy")); items.push_back(std::string("About Landmark")); } @@ -4411,6 +4447,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) // info panel can be shown at a time. if ((flags & FIRST_SELECTED_ITEM) == 0) { + disabled_items.push_back(std::string("url_copy")); disabled_items.push_back(std::string("About Landmark")); } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index dc9e88d54d..118430efe1 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -197,6 +197,8 @@ public: const LLUUID& uuid) : LLInvFVBridge(inventory, root, uuid) {} + typedef boost::function slurl_callback_t; + virtual void performAction(LLInventoryModel* model, std::string action); virtual void selectItem(); virtual void restoreItem(); @@ -214,7 +216,6 @@ public: virtual BOOL isItemCopyable() const; virtual BOOL hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } LLViewerInventoryItem* getItem() const; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index ab5b082915..e98d3f88a6 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -440,7 +440,7 @@ void show_item_original(const LLUUID& item_uuid) //sidetray inventory panel LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); - bool reset_inventory_filter = !floater_inventory->isInVisibleChain(); + bool do_reset_inventory_filter = !floater_inventory->isInVisibleChain(); LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(); if (!active_panel) @@ -460,37 +460,49 @@ void show_item_original(const LLUUID& item_uuid) } active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO); - if(reset_inventory_filter) + if(do_reset_inventory_filter) { - //inventory floater - bool floater_inventory_visible = false; - - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) - { - LLFloaterInventory* floater_inventory = dynamic_cast(*iter); - if (floater_inventory) - { - LLPanelMainInventory* main_inventory = floater_inventory->getMainInventoryPanel(); - - main_inventory->onFilterEdit(""); - - if(floater_inventory->getVisible()) - { - floater_inventory_visible = true; - } - } - } - if(sidepanel_inventory && !floater_inventory_visible) - { - LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); - - main_inventory->onFilterEdit(""); - } + reset_inventory_filter(); } } +void reset_inventory_filter() +{ + //inventory floater + bool floater_inventory_visible = false; + + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloaterInventory* floater_inventory = dynamic_cast(*iter); + if (floater_inventory) + { + LLPanelMainInventory* main_inventory = floater_inventory->getMainInventoryPanel(); + + main_inventory->onFilterEdit(""); + + if(floater_inventory->getVisible()) + { + floater_inventory_visible = true; + } + } + } + + if(!floater_inventory_visible) + { + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory) + { + main_inventory->onFilterEdit(""); + } + } + } +} + void open_outbox() { LLFloaterReg::showInstance("outbox"); diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 5cf9c528b0..909f7fd10b 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -56,6 +56,7 @@ void show_item_profile(const LLUUID& item_uuid); void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id); void show_item_original(const LLUUID& item_uuid); +void reset_inventory_filter(); void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 71dd963f28..05c81957c6 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -966,6 +966,7 @@ void LLInventoryPanel::doToSelected(const LLSD& userdata) void LLInventoryPanel::doCreate(const LLSD& userdata) { + reset_inventory_filter(); menu_create_inventory_item(mFolderRoot, LLFolderBridge::sSelf.get(), userdata); } diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index 459e52c4f4..97ba5b634a 100644 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -32,9 +32,7 @@ #include "lllocalbitmaps.h" /* boost: will not compile unless equivalent is undef'd, beware. */ -#ifdef equivalent -#undef equivalent -#endif +#include "fix_macros.h" #include /* image compression headers. */ diff --git a/indra/newview/llmachineid.cpp b/indra/newview/llmachineid.cpp index 778693876e..cd6473921d 100644 --- a/indra/newview/llmachineid.cpp +++ b/indra/newview/llmachineid.cpp @@ -252,12 +252,20 @@ S32 LLMachineID::getUniqueID(unsigned char *unique_id, size_t len) if (has_static_unique_id) { memcpy ( unique_id, &static_unique_id, len); - LL_DEBUGS("AppInit") << "UniqueID: " << unique_id[0] << unique_id[1]<< unique_id[2] << unique_id[3] << unique_id[4] << unique_id [5] << LL_ENDL; + LL_DEBUGS("AppInit") << "UniqueID: 0x"; + // Code between here and LL_ENDL is not executed unless the LL_DEBUGS + // actually produces output + for (size_t i = 0; i < len; ++i) + { + // Copy each char to unsigned int to hexify. Sending an unsigned + // char to a std::ostream tries to represent it as a char, not + // what we want here. + unsigned byte = unique_id[i]; + LL_CONT << std::hex << std::setw(2) << std::setfill('0') << byte; + } + // Reset default output formatting to avoid nasty surprises! + LL_CONT << std::dec << std::setw(0) << std::setfill(' ') << LL_ENDL; return 1; } return 0; } - - - - diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index 10608463b4..362308c176 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -1755,6 +1755,11 @@ void LLManipTranslate::highlightIntersection(LLVector3 normal, shader->bind(); } + if (shader) + { + shader->bind(); + } + //draw volume/plane intersections { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 7650fe9229..99b4707158 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -564,32 +564,13 @@ void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type) // void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in ) { - std::string language = LLUI::getLanguage(); - std::string delim = gDirUtilp->getDirDelimiter(); - std::string filename; + std::string filename(gDirUtilp->add(subdir, filename_in)); + std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", filename); - filename += subdir; - filename += delim; - filename += filename_in; - - std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename); - - if (! gDirUtilp->fileExists(expanded_filename)) + if (expanded_filename.empty()) { - if (language != "en") - { - expanded_filename = gDirUtilp->findSkinnedFilename("html", "en", filename); - if (! gDirUtilp->fileExists(expanded_filename)) - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } - } - else - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } + llwarns << "File " << filename << "not found" << llendl; + return; } if (ensureMediaSourceExists()) { @@ -597,7 +578,6 @@ void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::str mMediaSource->setSize(mTextureWidth, mTextureHeight); mMediaSource->navigateTo(expanded_filename, "text/html", false); } - } //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp new file mode 100644 index 0000000000..dd5f1ea689 --- /dev/null +++ b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp @@ -0,0 +1,238 @@ +/** +* @file llmenuoptionpathfindingrebakenavmesh.cpp +* @brief Implementation of llmenuoptionpathfindingrebakenavmesh +* @author Prep@lindenlab.com +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, 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 "llviewerprecompiledheaders.h" + +#include "llmenuoptionpathfindingrebakenavmesh.h" + +#include +#include + +#include "llagent.h" +#include "llenvmanager.h" +#include "llnotificationsutil.h" +#include "llpathfindingmanager.h" +#include "llpathfindingnavmesh.h" +#include "llpathfindingnavmeshstatus.h" +#include "llviewerregion.h" + +LLMenuOptionPathfindingRebakeNavmesh::LLMenuOptionPathfindingRebakeNavmesh() + : LLSingleton(), + mIsInitialized(false), + mCanRebakeRegion(false), + mRebakeNavMeshMode(kRebakeNavMesh_Default), + mNavMeshSlot(), + mRegionCrossingSlot(), + mAgentStateSlot() +{ +} + +LLMenuOptionPathfindingRebakeNavmesh::~LLMenuOptionPathfindingRebakeNavmesh() +{ + if (mRebakeNavMeshMode == kRebakeNavMesh_RequestSent) + { + LL_WARNS("navmeshRebaking") << "During destruction of the LLMenuOptionPathfindingRebakeNavmesh " + << "singleton, the mode indicates that a request has been sent for which a response has yet " + << "to be received. This could contribute to a crash on exit." << LL_ENDL; + } + + llassert(!mIsInitialized); + if (mIsInitialized) + { + quit(); + } +} + +void LLMenuOptionPathfindingRebakeNavmesh::initialize() +{ + llassert(!mIsInitialized); + if (!mIsInitialized) + { + mIsInitialized = true; + + setMode(kRebakeNavMesh_Default); + + createNavMeshStatusListenerForCurrentRegion(); + + if ( !mRegionCrossingSlot.connected() ) + { + mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this)); + } + + if (!mAgentStateSlot.connected()) + { + mAgentStateSlot = LLPathfindingManager::getInstance()->registerAgentStateListener(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleAgentState, this, _1)); + } + LLPathfindingManager::getInstance()->requestGetAgentState(); + } +} + +void LLMenuOptionPathfindingRebakeNavmesh::quit() +{ + llassert(mIsInitialized); + if (mIsInitialized) + { + if (mNavMeshSlot.connected()) + { + mNavMeshSlot.disconnect(); + } + + if (mRegionCrossingSlot.connected()) + { + mRegionCrossingSlot.disconnect(); + } + + if (mAgentStateSlot.connected()) + { + mAgentStateSlot.disconnect(); + } + + mIsInitialized = false; + } +} + +bool LLMenuOptionPathfindingRebakeNavmesh::canRebakeRegion() const +{ + if (!mIsInitialized) + { + LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized " + << "when the ability to rebake navmesh is being requested." << LL_ENDL; + } + return mCanRebakeRegion; +} + +LLMenuOptionPathfindingRebakeNavmesh::ERebakeNavMeshMode LLMenuOptionPathfindingRebakeNavmesh::getMode() const +{ + if (!mIsInitialized) + { + LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized " + << "when the mode is being requested." << LL_ENDL; + } + return mRebakeNavMeshMode; +} + +void LLMenuOptionPathfindingRebakeNavmesh::sendRequestRebakeNavmesh() +{ + if (!mIsInitialized) + { + LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized " + << "when the request is being made to rebake the navmesh." << LL_ENDL; + } + else + { + if (!canRebakeRegion()) + { + LL_WARNS("navmeshRebaking") << "attempting to rebake navmesh when user does not have permissions " + << "on this region" << LL_ENDL; + } + if (getMode() != kRebakeNavMesh_Available) + { + LL_WARNS("navmeshRebaking") << "attempting to rebake navmesh when mode is not available" + << LL_ENDL; + } + + setMode(kRebakeNavMesh_RequestSent); + LLPathfindingManager::getInstance()->requestRebakeNavMesh(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRebakeNavMeshResponse, this, _1)); + } +} + +void LLMenuOptionPathfindingRebakeNavmesh::setMode(ERebakeNavMeshMode pRebakeNavMeshMode) +{ + mRebakeNavMeshMode = pRebakeNavMeshMode; +} + +void LLMenuOptionPathfindingRebakeNavmesh::handleAgentState(BOOL pCanRebakeRegion) +{ + llassert(mIsInitialized); + mCanRebakeRegion = pCanRebakeRegion; +} + +void LLMenuOptionPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponseStatus) +{ + llassert(mIsInitialized); + if (getMode() == kRebakeNavMesh_RequestSent) + { + setMode(pResponseStatus ? kRebakeNavMesh_InProgress : kRebakeNavMesh_Default); + } + + if (!pResponseStatus) + { + LLNotificationsUtil::add("PathfindingCannotRebakeNavmesh"); + } +} + +void LLMenuOptionPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus) +{ + llassert(mIsInitialized); + ERebakeNavMeshMode rebakeNavMeshMode = kRebakeNavMesh_Default; + if (pNavMeshStatus.isValid()) + { + switch (pNavMeshStatus.getStatus()) + { + case LLPathfindingNavMeshStatus::kPending : + case LLPathfindingNavMeshStatus::kRepending : + rebakeNavMeshMode = kRebakeNavMesh_Available; + break; + case LLPathfindingNavMeshStatus::kBuilding : + rebakeNavMeshMode = kRebakeNavMesh_InProgress; + break; + case LLPathfindingNavMeshStatus::kComplete : + rebakeNavMeshMode = kRebakeNavMesh_NotAvailable; + break; + default : + rebakeNavMeshMode = kRebakeNavMesh_Default; + llassert(0); + break; + } + } + + setMode(rebakeNavMeshMode); +} + +void LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed() +{ + llassert(mIsInitialized); + createNavMeshStatusListenerForCurrentRegion(); + mCanRebakeRegion = FALSE; + LLPathfindingManager::getInstance()->requestGetAgentState(); +} + +void LLMenuOptionPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegion() +{ + if (mNavMeshSlot.connected()) + { + mNavMeshSlot.disconnect(); + } + + LLViewerRegion *currentRegion = gAgent.getRegion(); + if (currentRegion != NULL) + { + mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleNavMeshStatus, this, _2)); + LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true); + } +} diff --git a/indra/newview/llpanelpathfindingrebakenavmesh.h b/indra/newview/llmenuoptionpathfindingrebakenavmesh.h similarity index 63% rename from indra/newview/llpanelpathfindingrebakenavmesh.h rename to indra/newview/llmenuoptionpathfindingrebakenavmesh.h index 48764f2aa7..7b1d2873ba 100644 --- a/indra/newview/llpanelpathfindingrebakenavmesh.h +++ b/indra/newview/llmenuoptionpathfindingrebakenavmesh.h @@ -1,6 +1,6 @@ /** -* @file llpanelpathfindingrebakenavmesh.h -* @brief Header file for llpanelpathfindingrebakenavmesh +* @file llmenuoptionpathfindingrebakenavmesh.h +* @brief Header file for llmenuoptionpathfindingrebakenavmesh * @author Prep@lindenlab.com * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ @@ -24,34 +24,22 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -#ifndef LL_LLPANELPATHFINDINGREBAKENAVMESH_H -#define LL_LLPANELPATHFINDINGREBAKENAVMESH_H +#ifndef LL_LLMENUOPTIONPATHFINDINGREBAKENAVMESH_H +#define LL_LLMENUOPTIONPATHFINDINGREBAKENAVMESH_H #include -#include "llpanel.h" #include "llpathfindingmanager.h" #include "llpathfindingnavmesh.h" +#include "llsingleton.h" -class LLButton; class LLPathfindingNavMeshStatus; -class LLPanelPathfindingRebakeNavmesh : public LLPanel +class LLMenuOptionPathfindingRebakeNavmesh : public LLSingleton { - - LOG_CLASS(LLPanelPathfindingRebakeNavmesh); + LOG_CLASS(LLMenuOptionPathfindingRebakeNavmesh); public: - static LLPanelPathfindingRebakeNavmesh* getInstance(); - - virtual BOOL postBuild(); - - virtual void draw(); - virtual BOOL handleToolTip( S32 x, S32 y, MASK mask ); - -protected: - -private: typedef enum { kRebakeNavMesh_Available, @@ -61,15 +49,21 @@ private: kRebakeNavMesh_Default = kRebakeNavMesh_NotAvailable } ERebakeNavMeshMode; - LLPanelPathfindingRebakeNavmesh(); - virtual ~LLPanelPathfindingRebakeNavmesh(); + LLMenuOptionPathfindingRebakeNavmesh(); + virtual ~LLMenuOptionPathfindingRebakeNavmesh(); - static LLPanelPathfindingRebakeNavmesh* getPanel(); + void initialize(); + void quit(); - void setMode(ERebakeNavMeshMode pRebakeNavMeshMode); + bool canRebakeRegion() const; ERebakeNavMeshMode getMode() const; - void onNavMeshRebakeClick(); + void sendRequestRebakeNavmesh(); + +protected: + +private: + void setMode(ERebakeNavMeshMode pRebakeNavMeshMode); void handleAgentState(BOOL pCanRebakeRegion); void handleRebakeNavMeshResponse(bool pResponseStatus); @@ -78,19 +72,14 @@ private: void createNavMeshStatusListenerForCurrentRegion(); - bool doDraw() const; - void updatePosition(); + bool mIsInitialized; - BOOL mCanRebakeRegion; + bool mCanRebakeRegion; ERebakeNavMeshMode mRebakeNavMeshMode; - LLButton* mNavMeshRebakeButton; - LLButton* mNavMeshSendingButton; - LLButton* mNavMeshBakingButton; - LLPathfindingNavMesh::navmesh_slot_t mNavMeshSlot; boost::signals2::connection mRegionCrossingSlot; LLPathfindingManager::agent_state_slot_t mAgentStateSlot; }; -#endif // LL_LLPANELPATHFINDINGREBAKENAVMESH_H +#endif // LL_LLMENUOPTIONPATHFINDINGREBAKENAVMESH_H diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index bc7f522848..92ac435f08 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -361,7 +361,20 @@ public: mModelData(model_data), mObserverHandle(observer_handle) { + if (mThread) + { + mThread->startRequest(); + } } + + ~LLWholeModelFeeResponder() + { + if (mThread) + { + mThread->stopRequest(); + } + } + virtual void completed(U32 status, const std::string& reason, const LLSD& content) @@ -372,7 +385,6 @@ public: cc = llsd_from_file("fake_upload_error.xml"); } - mThread->mPendingUploads--; dump_llsd_to_file(cc,make_dump_name("whole_model_fee_response_",dump_num)); LLWholeModelFeeObserver* observer = mObserverHandle.get(); @@ -415,7 +427,20 @@ public: mModelData(model_data), mObserverHandle(observer_handle) { + if (mThread) + { + mThread->startRequest(); + } } + + ~LLWholeModelUploadResponder() + { + if (mThread) + { + mThread->stopRequest(); + } + } + virtual void completed(U32 status, const std::string& reason, const LLSD& content) @@ -426,7 +451,6 @@ public: cc = llsd_from_file("fake_upload_error.xml"); } - mThread->mPendingUploads--; dump_llsd_to_file(cc,make_dump_name("whole_model_upload_response_",dump_num)); LLWholeModelUploadObserver* observer = mObserverHandle.get(); @@ -1097,11 +1121,13 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat mMeshHeader[mesh_id] = header; } + + LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. + //check for pending requests pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); if (iter != mPendingLOD.end()) { - LLMutexLock lock(mMutex); for (U32 i = 0; i < iter->second.size(); ++i) { LODRequest req(mesh_params, iter->second[i]); @@ -1620,7 +1646,7 @@ void LLMeshUploadThread::doWholeModelUpload() mCurlRequest->process(); //sleep for 10ms to prevent eating a whole core apr_sleep(10000); - } while (!LLAppViewer::isQuitting() && mCurlRequest->getQueued() > 0); + } while (!LLAppViewer::isQuitting() && mPendingUploads > 0); } delete mCurlRequest; @@ -1642,7 +1668,6 @@ void LLMeshUploadThread::requestWholeModelFee() wholeModelToLLSD(model_data,false); dump_llsd_to_file(model_data,make_dump_name("whole_model_fee_request_",dump_num)); - mPendingUploads++; LLCurlRequest::headers_t headers; { @@ -1659,7 +1684,7 @@ void LLMeshUploadThread::requestWholeModelFee() mCurlRequest->process(); //sleep for 10ms to prevent eating a whole core apr_sleep(10000); - } while (!LLApp::isQuitting() && mCurlRequest->getQueued() > 0); + } while (!LLApp::isQuitting() && mPendingUploads > 0); delete mCurlRequest; mCurlRequest = NULL; @@ -1796,7 +1821,12 @@ void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { - + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1851,6 +1881,12 @@ void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1905,6 +1941,11 @@ void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& r const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1959,6 +2000,12 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -2013,6 +2060,12 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + if (status < 200 || status > 400) { //llwarns diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index da81bb057b..6e301c26a2 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -405,6 +405,9 @@ public: LLHandle fee_observer= (LLHandle()), LLHandle upload_observer = (LLHandle())); ~LLMeshUploadThread(); + void startRequest() { ++mPendingUploads; } + void stopRequest() { --mPendingUploads; } + bool finished() { return mFinished; } virtual void run(); void preStart(); diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index a7059eb519..54522bb7f6 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -44,6 +44,8 @@ #include "llmutelist.h" +#include "pipeline.h" + #include #include "lldispatcher.h" @@ -192,6 +194,23 @@ BOOL LLMuteList::isLinden(const std::string& name) const return last_name == "Linden"; } +static LLVOAvatar* find_avatar(const LLUUID& id) +{ + LLViewerObject *obj = gObjectList.findObject(id); + while (obj && obj->isAttachment()) + { + obj = (LLViewerObject *)obj->getParent(); + } + + if (obj && obj->isAvatar()) + { + return (LLVOAvatar*)obj; + } + else + { + return NULL; + } +} BOOL LLMuteList::add(const LLMute& mute, U32 flags) { @@ -288,6 +307,12 @@ BOOL LLMuteList::add(const LLMute& mute, U32 flags) LLViewerPartSim::getInstance()->clearParticlesByOwnerID(localmute.mID); } } + //mute local lights that are attached to the avatar + LLVOAvatar *avatarp = find_avatar(localmute.mID); + if (avatarp) + { + LLPipeline::removeMutedAVsLights(avatarp); + } return TRUE; } } diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 4e28d1f526..b0fbad33b0 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -68,7 +68,7 @@ LLNameListCtrl::LLNameListCtrl(const LLNameListCtrl::Params& p) {} // public -void LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos, +LLScrollListItem* LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos, BOOL enabled, const std::string& suffix) { //llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl; @@ -78,7 +78,7 @@ void LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos, item.enabled = enabled; item.target = INDIVIDUAL; - addNameItemRow(item, pos, suffix); + return addNameItemRow(item, pos, suffix); } // virtual, public @@ -204,7 +204,7 @@ BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; S32 column_index = getColumnIndexFromOffset(x); - LLScrollListItem* hit_item = hitItem(x, y); + LLNameListItem* hit_item = dynamic_cast(hitItem(x, y)); if (hit_item && column_index == mNameColumnIndex) { @@ -228,7 +228,7 @@ BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask) LLCoordGL pos( sticky_rect.mRight - info_icon_size, sticky_rect.mTop - (sticky_rect.getHeight() - icon->getHeight())/2 ); // Should we show a group or an avatar inspector? - bool is_group = hit_item->getValue()["is_group"].asBoolean(); + bool is_group = hit_item->isGroup(); LLToolTip::Params params; params.background_visible( false ); @@ -271,10 +271,10 @@ void LLNameListCtrl::addGroupNameItem(LLNameListCtrl::NameItem& item, EAddPositi addNameItemRow(item, pos); } -void LLNameListCtrl::addNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos) +LLScrollListItem* LLNameListCtrl::addNameItem(LLNameListCtrl::NameItem& item, EAddPosition pos) { item.target = INDIVIDUAL; - addNameItemRow(item, pos); + return addNameItemRow(item, pos); } LLScrollListItem* LLNameListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata) @@ -293,19 +293,12 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( const std::string& suffix) { LLUUID id = name_item.value().asUUID(); - LLNameListItem* item = NULL; - - // Store item type so that we can invoke the proper inspector. - // *TODO Vadim: Is there a more proper way of storing additional item data? - { - LLNameListCtrl::NameItem item_p(name_item); - item_p.value = LLSD().with("uuid", id).with("is_group", name_item.target() == GROUP); - item = new LLNameListItem(item_p); - LLScrollListCtrl::addRow(item, item_p, pos); - } + LLNameListItem* item = new LLNameListItem(name_item,name_item.target() == GROUP); if (!item) return NULL; + LLScrollListCtrl::addRow(item, name_item, pos); + // use supplied name by default std::string fullname = name_item.name; switch(name_item.target) @@ -336,7 +329,7 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( // ...schedule a callback LLAvatarNameCache::get(id, boost::bind(&LLNameListCtrl::onAvatarNameCache, - this, _1, _2)); + this, _1, _2, item->getHandle())); } break; } @@ -392,7 +385,8 @@ void LLNameListCtrl::removeNameItem(const LLUUID& agent_id) } void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, - const LLAvatarName& av_name) + const LLAvatarName& av_name, + LLHandle item) { std::string name; if (mShortNames) @@ -400,20 +394,17 @@ void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, else name = av_name.getCompleteName(); - item_list::iterator iter; - for (iter = getItemList().begin(); iter != getItemList().end(); iter++) + LLNameListItem* list_item = item.get(); + if (list_item && list_item->getUUID() == agent_id) { - LLScrollListItem* item = *iter; - if (item->getUUID() == agent_id) + LLScrollListCell* cell = list_item->getColumn(mNameColumnIndex); + if (cell) { - LLScrollListCell* cell = item->getColumn(mNameColumnIndex); - if (cell) - { - cell->setValue(name); - } + cell->setValue(name); + setNeedsSort(); } } - + dirtyColumns(); } @@ -431,3 +422,8 @@ void LLNameListCtrl::updateColumns() } } } + +void LLNameListCtrl::sortByName(BOOL ascending) +{ + sortByColumnIndex(mNameColumnIndex,ascending); +} diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index ca9956dc53..3ac0565761 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -33,6 +33,36 @@ class LLAvatarName; +/** + * LLNameListCtrl item + * + * We don't use LLScrollListItem to be able to override getUUID(), which is needed + * because the name list item value is not simply an UUID but a map (uuid, is_group). + */ +class LLNameListItem : public LLScrollListItem, public LLHandleProvider +{ +public: + bool isGroup() const { return mIsGroup; } + void setIsGroup(bool is_group) { mIsGroup = is_group; } + +protected: + friend class LLNameListCtrl; + + LLNameListItem( const LLScrollListItem::Params& p ) + : LLScrollListItem(p), mIsGroup(false) + { + } + + LLNameListItem( const LLScrollListItem::Params& p, bool is_group ) + : LLScrollListItem(p), mIsGroup(is_group) + { + } + +private: + bool mIsGroup; +}; + + class LLNameListCtrl : public LLScrollListCtrl, public LLInstanceTracker { @@ -85,9 +115,9 @@ protected: public: // Add a user to the list by name. It will be added, the name // requested from the cache, and updated as necessary. - void addNameItem(const LLUUID& agent_id, EAddPosition pos = ADD_BOTTOM, + LLScrollListItem* addNameItem(const LLUUID& agent_id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE, const std::string& suffix = LLStringUtil::null); - void addNameItem(NameItem& item, EAddPosition pos = ADD_BOTTOM); + LLScrollListItem* addNameItem(NameItem& item, EAddPosition pos = ADD_BOTTOM); /*virtual*/ LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); LLScrollListItem* addNameItemRow(const NameItem& value, EAddPosition pos = ADD_BOTTOM, const std::string& suffix = LLStringUtil::null); @@ -110,12 +140,14 @@ public: void setAllowCallingCardDrop(BOOL b) { mAllowCallingCardDrop = b; } + void sortByName(BOOL ascending); + /*virtual*/ void updateColumns(); /*virtual*/ void mouseOverHighlightNthItem( S32 index ); private: void showInspector(const LLUUID& avatar_id, bool is_group); - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, LLHandle item); private: S32 mNameColumnIndex; @@ -124,24 +156,5 @@ private: bool mShortNames; // display name only, no SLID }; -/** - * LLNameListCtrl item - * - * We don't use LLScrollListItem to be able to override getUUID(), which is needed - * because the name list item value is not simply an UUID but a map (uuid, is_group). - */ -class LLNameListItem : public LLScrollListItem -{ -public: - LLUUID getUUID() const { return getValue()["uuid"].asUUID(); } - -protected: - friend class LLNameListCtrl; - - LLNameListItem( const LLScrollListItem::Params& p ) - : LLScrollListItem(p) - { - } -}; #endif diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index f792f53ac5..3d8150eed3 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -97,6 +97,13 @@ bool LLNotificationManager::onNotification(const LLSD& notify) { LLSysHandler* handle = NULL; + // Don't bother if we're going down. + // Otherwise we might crash when trying to use handlers that are already dead. + if( LLApp::isExiting() ) + { + return false; + } + if (LLNotifications::destroyed()) return false; diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index d58d6d536c..d42056ef9d 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -835,11 +835,11 @@ BOOL LLPanelEditWearable::isDirty() const BOOL isDirty = FALSE; if (mWearablePtr) { - if (mWearablePtr->isDirty() || - mWearableItem->getName().compare(mNameEditor->getText()) != 0) - { - isDirty = TRUE; - } + if (mWearablePtr->isDirty() || + ( mWearableItem && mNameEditor && mWearableItem->getName().compare(mNameEditor->getText()) != 0 )) + { + isDirty = TRUE; + } } return isDirty; } diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index b494c7d403..0e6f5b8924 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -33,7 +33,7 @@ class LLOfferInfo; -const S32 UPDATE_MEMBERS_PER_FRAME = 500; +const F32 UPDATE_MEMBERS_SECONDS_PER_FRAME = 0.005; // 5ms // Forward declares class LLPanelGroupTab; diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index bc594b5517..993ffb7825 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -28,8 +28,10 @@ #include "llpanelgroupgeneral.h" -#include "lluictrlfactory.h" +#include "llavatarnamecache.h" #include "llagent.h" +#include "llsdparam.h" +#include "lluictrlfactory.h" #include "roles_constants.h" // UI elements @@ -313,11 +315,10 @@ void LLPanelGroupGeneral::activate() { LLGroupMgr::getInstance()->sendGroupTitlesRequest(mGroupID); LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mGroupID); - if (!gdatap || !gdatap->isMemberDataComplete() ) { - LLGroupMgr::getInstance()->sendGroupMembersRequest(mGroupID); + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); } mFirstUse = FALSE; @@ -697,76 +698,45 @@ void LLPanelGroupGeneral::updateMembers() LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); - if (!mListVisibleMembers || !gdatap + if (!mListVisibleMembers + || !gdatap || !gdatap->isMemberDataComplete() || gdatap->mMembers.empty()) { return; } - static LLTimer all_timer; - static LLTimer sd_timer; - static LLTimer element_timer; + LLTimer update_time; + update_time.setTimerExpirySec(UPDATE_MEMBERS_SECONDS_PER_FRAME); - all_timer.reset(); - S32 i = 0; + LLAvatarName av_name; - for( ; mMemberProgress != gdatap->mMembers.end() && imMembers.end() && !update_time.hasExpired(); + ++mMemberProgress) { - //llinfos << "Adding " << iter->first << ", " << iter->second->getTitle() << llendl; LLGroupMemberData* member = mMemberProgress->second; if (!member) { continue; } - // Owners show up in bold. - std::string style = "NORMAL"; - sd_timer.reset(); - LLSD row; - row["id"] = member->getID(); - row["columns"][0]["column"] = "name"; - row["columns"][0]["font"]["name"] = "SANSSERIF_SMALL"; - row["columns"][0]["font"]["style"] = style; - // value is filled in by name list control - - row["columns"][1]["column"] = "title"; - row["columns"][1]["value"] = member->getTitle(); - row["columns"][1]["font"]["name"] = "SANSSERIF_SMALL"; - row["columns"][1]["font"]["style"] = style; - - std::string status = member->getOnlineStatus(); - - row["columns"][2]["column"] = "status"; - row["columns"][2]["value"] = status; - row["columns"][2]["font"]["name"] = "SANSSERIF_SMALL"; - row["columns"][2]["font"]["style"] = style; - - sSDTime += sd_timer.getElapsedTimeF32(); - - element_timer.reset(); - LLScrollListItem* member_row = mListVisibleMembers->addElement(row); - - if ( member->isOwner() ) + if (LLAvatarNameCache::get(mMemberProgress->first, &av_name)) { - LLScrollListText* name_textp = dynamic_cast(member_row->getColumn(0)); - if (name_textp) - name_textp->setFontStyle(LLFontGL::BOLD); + addMember(mMemberProgress->second); + } + else + { + // If name is not cached, onNameCache() should be called when it is cached and add this member to list. + LLAvatarNameCache::get(mMemberProgress->first, + boost::bind(&LLPanelGroupGeneral::onNameCache, + this, gdatap->getMemberVersion(), member, _2)); } - sElementTime += element_timer.getElapsedTimeF32(); } - sAllTime += all_timer.getElapsedTimeF32(); - llinfos << "Updated " << i << " of " << UPDATE_MEMBERS_PER_FRAME << "members in the list." << llendl; if (mMemberProgress == gdatap->mMembers.end()) { - llinfos << " member list completed." << llendl; + lldebugs << " member list completed." << llendl; mListVisibleMembers->setEnabled(TRUE); - - llinfos << "All Time: " << sAllTime << llendl; - llinfos << "SD Time: " << sSDTime << llendl; - llinfos << "Element Time: " << sElementTime << llendl; } else { @@ -775,6 +745,43 @@ void LLPanelGroupGeneral::updateMembers() } } +void LLPanelGroupGeneral::addMember(LLGroupMemberData* member) +{ + LLNameListCtrl::NameItem item_params; + item_params.value = member->getID(); + + LLScrollListCell::Params column; + item_params.columns.add().column("name").font.name("SANSSERIF_SMALL"); + + item_params.columns.add().column("title").value(member->getTitle()).font.name("SANSSERIF_SMALL"); + + item_params.columns.add().column("status").value(member->getOnlineStatus()).font.name("SANSSERIF_SMALL"); + + LLScrollListItem* member_row = mListVisibleMembers->addNameItemRow(item_params); + + if ( member->isOwner() ) + { + LLScrollListText* name_textp = dynamic_cast(member_row->getColumn(0)); + if (name_textp) + name_textp->setFontStyle(LLFontGL::BOLD); + } +} + +void LLPanelGroupGeneral::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name) +{ + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); + + if (!gdatap + || !gdatap->isMemberDataComplete() + || gdatap->getMemberVersion() != update_id) + { + // Stale data + return; + } + + addMember(member); +} + void LLPanelGroupGeneral::updateChanged() { // List all the controls we want to check for changes... diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index 88c092c461..1b4e8e2645 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -38,6 +38,7 @@ class LLNameListCtrl; class LLCheckBoxCtrl; class LLComboBox; class LLSpinCtrl; +class LLAvatarName; class LLPanelGroupGeneral : public LLPanelGroupTab { @@ -62,6 +63,7 @@ public: virtual void setupCtrls (LLPanel* parent); + void onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name); private: void reset(); @@ -75,7 +77,8 @@ private: static void onReceiveNotices(LLUICtrl* ctrl, void* data); static void openProfile(void* data); - S32 sortMembersList(S32,const LLScrollListItem*,const LLScrollListItem*); + S32 sortMembersList(S32,const LLScrollListItem*,const LLScrollListItem*); + void addMember(LLGroupMemberData* member); static bool joinDlgCB(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index 7a15d93181..b9b347d4be 100644 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -83,6 +83,7 @@ public: LLTextBox *mGroupName; std::string mOwnerWarning; std::string mAlreadyInGroup; + std::string mTooManySelected; bool mConfirmedOwnerInvite; void (*mCloseCallback)(void* data); @@ -185,6 +186,17 @@ void LLPanelGroupInvite::impl::submitInvitations() role_member_pairs[item->getUUID()] = role_id; } + const S32 MAX_GROUP_INVITES = 100; // Max invites per request. 100 to match server cap. + if (role_member_pairs.size() > MAX_GROUP_INVITES) + { + // Fail! + LLSD msg; + msg["MESSAGE"] = mTooManySelected; + LLNotificationsUtil::add("GenericAlert", msg); + (*mCloseCallback)(mCloseCallbackUserData); + return; + } + LLGroupMgr::getInstance()->sendGroupMemberInvites(mGroupID, role_member_pairs); if(already_in_group) @@ -455,10 +467,11 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) //so we need to do this additional search in avatar tracker, see EXT-4732 if (LLAvatarTracker::instance().isBuddy(agent_id)) { - if (!gCacheName->getFullName(agent_id, fullname)) + LLAvatarName av_name; + if (!LLAvatarNameCache::get(agent_id, &av_name)) { // actually it should happen, just in case - gCacheName->get(LLUUID(agent_id), false, boost::bind( + LLAvatarNameCache::get(LLUUID(agent_id), boost::bind( &LLPanelGroupInvite::addUserCallback, this, _1, _2)); // for this special case! //when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence @@ -467,7 +480,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) } else { - names.push_back(fullname); + names.push_back(av_name.getLegacyName()); } } } @@ -475,12 +488,12 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) mImplementation->addUsers(names, agent_ids); } -void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const std::string& full_name) +void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const LLAvatarName& av_name) { std::vector names; uuid_vec_t agent_ids; agent_ids.push_back(id); - names.push_back(full_name); + names.push_back(av_name.getLegacyName()); mImplementation->addUsers(names, agent_ids); } @@ -558,8 +571,8 @@ void LLPanelGroupInvite::updateLists() if (!mPendingUpdate) { LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID); - LLGroupMgr::getInstance()->sendGroupMembersRequest(mImplementation->mGroupID); LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID); + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID); } mPendingUpdate = TRUE; } @@ -621,6 +634,7 @@ BOOL LLPanelGroupInvite::postBuild() mImplementation->mOwnerWarning = getString("confirm_invite_owner_str"); mImplementation->mAlreadyInGroup = getString("already_in_group"); + mImplementation->mTooManySelected = getString("invite_selection_too_large"); update(); diff --git a/indra/newview/llpanelgroupinvite.h b/indra/newview/llpanelgroupinvite.h index a7bfd2226e..9f7b5ae9be 100644 --- a/indra/newview/llpanelgroupinvite.h +++ b/indra/newview/llpanelgroupinvite.h @@ -29,6 +29,8 @@ #include "llpanel.h" #include "lluuid.h" +class LLAvatarName; + class LLPanelGroupInvite : public LLPanel { @@ -40,7 +42,7 @@ public: /** * this callback is being used to add a user whose fullname isn't been loaded before invoking of addUsers(). */ - void addUserCallback(const LLUUID& id, const std::string& full_name); + void addUserCallback(const LLUUID& id, const LLAvatarName& av_name); void clear(); void update(); diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp index 363443646d..37755fb851 100644 --- a/indra/newview/llpanelgrouplandmoney.cpp +++ b/indra/newview/llpanelgrouplandmoney.cpp @@ -517,7 +517,7 @@ void LLPanelGroupLandMoney::impl::processGroupLand(LLMessageSystem* msg) row["columns"][3]["column"] = "type"; row["columns"][3]["value"] = land_type; - row["columns"][3]["font"] = "SANSSERIFSMALL"; + row["columns"][3]["font"] = "SANSSERIF_SMALL"; // hidden is always last column row["columns"][4]["column"] = "hidden"; diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index f825ee3215..ff106882f4 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -29,6 +29,7 @@ #include "llcheckboxctrl.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llbutton.h" #include "llfiltereditor.h" #include "llfloatergroupinvite.h" @@ -356,7 +357,7 @@ void LLPanelGroupRoles::activate() if (!gdatap || !gdatap->isMemberDataComplete() ) { - LLGroupMgr::getInstance()->sendGroupMembersRequest(mGroupID); + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); } // Check role data. @@ -744,7 +745,6 @@ LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab() mHasMatch(FALSE), mNumOwnerAdditions(0) { - mUdpateSessionID = LLUUID::null; } LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab() @@ -1426,13 +1426,20 @@ U64 LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges(const LLUUID& ag return GP_NO_POWERS; } - LLGroupMemberData* member_data = gdatap->mMembers[agent_id]; - if ( !member_data ) + LLGroupMgrGroupData::member_list_t::iterator iter = gdatap->mMembers.find(agent_id); + if ( iter == gdatap->mMembers.end() ) { llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No member data for member with UUID " << agent_id << llendl; return GP_NO_POWERS; } + LLGroupMemberData* member_data = (*iter).second; + if (!member_data) + { + llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- Null member data for member with UUID " << agent_id << llendl; + return GP_NO_POWERS; + } + //see if there are unsaved role changes for this agent role_change_data_map_t* role_change_datap = NULL; member_role_changes_map_t::iterator member = mMemberRoleChangeData.find(agent_id); @@ -1547,10 +1554,6 @@ void LLPanelGroupMembersSubTab::update(LLGroupChange gc) mMemberProgress = gdatap->mMembers.begin(); mPendingMemberUpdate = TRUE; mHasMatch = FALSE; - // Generate unique ID for current updateMembers()- see onNameCache for details. - // Using unique UUID is perhaps an overkill but this way we are perfectly safe - // from coincidences. - mUdpateSessionID.generate(); } else { @@ -1578,55 +1581,41 @@ void LLPanelGroupMembersSubTab::update(LLGroupChange gc) } } -void LLPanelGroupMembersSubTab::addMemberToList(LLUUID id, LLGroupMemberData* data) +void LLPanelGroupMembersSubTab::addMemberToList(LLGroupMemberData* data) { if (!data) return; LLUIString donated = getString("donation_area"); donated.setArg("[AREA]", llformat("%d", data->getContribution())); - LLSD row; - row["id"] = id; + LLNameListCtrl::NameItem item_params; + item_params.value = data->getID(); - row["columns"][0]["column"] = "name"; - // value is filled in by name list control + item_params.columns.add().column("name").font.name("SANSSERIF_SMALL").style("NORMAL"); - row["columns"][1]["column"] = "donated"; - row["columns"][1]["value"] = donated.getString(); + item_params.columns.add().column("donated").value(donated.getString()) + .font.name("SANSSERIF_SMALL").style("NORMAL"); - row["columns"][2]["column"] = "online"; - row["columns"][2]["value"] = data->getOnlineStatus(); - row["columns"][2]["font"] = "SANSSERIF_SMALL"; - - mMembersList->addElement(row); + item_params.columns.add().column("online").value(data->getOnlineStatus()) + .font.name("SANSSERIF_SMALL").style("NORMAL"); + mMembersList->addNameItemRow(item_params); mHasMatch = TRUE; } -void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, const LLUUID& id) +void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name) { - // Update ID is used to determine whether member whose id is passed - // into onNameCache() was passed after current or previous user-initiated update. - // This is needed to avoid probable duplication of members in list after changing filter - // or adding of members of another group if gets for their names were called on - // previous update. If this id is from get() called from older update, - // we do nothing. - if (mUdpateSessionID != update_id) return; - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); - if (!gdatap) + if (!gdatap + || gdatap->getMemberVersion() != update_id + || !member) { - llwarns << "LLPanelGroupMembersSubTab::updateMembers() -- No group data!" << llendl; return; } - std::string fullname; - gCacheName->getFullName(id, fullname); - - LLGroupMemberData* data; // trying to avoid unnecessary hash lookups - if (matchesSearchFilter(fullname) && ((data = gdatap->mMembers[id]) != NULL)) + if (matchesSearchFilter(av_name.getLegacyName())) { - addMemberToList(id, data); + addMemberToList(member); if(!mMembersList->getEnabled()) { mMembersList->setEnabled(TRUE); @@ -1665,27 +1654,29 @@ void LLPanelGroupMembersSubTab::updateMembers() LLGroupMgrGroupData::member_list_t::iterator end = gdatap->mMembers.end(); - - S32 i = 0; - for( ; mMemberProgress != end && isecond) continue; + // Do filtering on name if it is already in the cache. - std::string fullname; - if (gCacheName->getFullName(mMemberProgress->first, fullname)) + LLAvatarName av_name; + if (LLAvatarNameCache::get(mMemberProgress->first, &av_name)) { - if (matchesSearchFilter(fullname)) + if (matchesSearchFilter(av_name.getLegacyName())) { - addMemberToList(mMemberProgress->first, mMemberProgress->second); + addMemberToList(mMemberProgress->second); } } else { // If name is not cached, onNameCache() should be called when it is cached and add this member to list. - gCacheName->get(mMemberProgress->first, FALSE, boost::bind(&LLPanelGroupMembersSubTab::onNameCache, - this, mUdpateSessionID, _1)); + LLAvatarNameCache::get(mMemberProgress->first, boost::bind(&LLPanelGroupMembersSubTab::onNameCache, + this, gdatap->getMemberVersion(), mMemberProgress->second, _2)); } } @@ -1987,7 +1978,7 @@ void LLPanelGroupRolesSubTab::update(LLGroupChange gc) if (!gdatap || !gdatap->isMemberDataComplete()) { - LLGroupMgr::getInstance()->sendGroupMembersRequest(mGroupID); + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); } if (!gdatap || !gdatap->isRoleMemberDataComplete()) @@ -2580,7 +2571,7 @@ void LLPanelGroupActionsSubTab::handleActionSelect() } else { - LLGroupMgr::getInstance()->sendGroupMembersRequest(mGroupID); + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); } if (gdatap->isRoleDataComplete()) @@ -2604,6 +2595,7 @@ void LLPanelGroupActionsSubTab::handleActionSelect() LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mGroupID); } } + void LLPanelGroupRoles::setGroupID(const LLUUID& id) { LLPanelGroupTab::setGroupID(id); diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index a55e264150..bead8bd85b 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -187,8 +187,8 @@ public: virtual void setGroupID(const LLUUID& id); - void addMemberToList(LLUUID id, LLGroupMemberData* data); - void onNameCache(const LLUUID& update_id, const LLUUID& id); + void addMemberToList(LLGroupMemberData* data); + void onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name); protected: typedef std::map role_change_data_map_t; @@ -210,9 +210,6 @@ protected: BOOL mPendingMemberUpdate; BOOL mHasMatch; - // This id is generated after each user initiated member list update(opening Roles or changing filter) - LLUUID mUdpateSessionID; - member_role_changes_map_t mMemberRoleChangeData; U32 mNumOwnerAdditions; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 44ff62e290..bcb90bcb56 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llpanellogin.h" +#include "lllayoutstack.h" #include "indra_constants.h" // for key and mask constants #include "llfloaterreg.h" @@ -138,14 +139,17 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, mLogoImage = LLUI::getUIImage("startup_logo"); buildFromFile( "panel_login.xml"); - + reshape(rect.getWidth(), rect.getHeight()); - getChild("password_edit")->setKeystrokeCallback(onPassKey, this); + LLLineEditor* password_edit(getChild("password_edit")); + password_edit->setKeystrokeCallback(onPassKey, this); + // STEAM-14: When user presses Enter with this field in focus, initiate login + password_edit->setCommitCallback(boost::bind(&LLPanelLogin::onClickConnect, this)); // change z sort of clickable text to be behind buttons sendChildToBack(getChildView("forgot_password_text")); - + LLComboBox* location_combo = getChild("start_location_combo"); updateLocationSelectorsVisibility(); // separate so that it can be called from preferences location_combo->setFocusLostCallback(boost::bind(&LLPanelLogin::onLocationSLURL, this)); @@ -201,7 +205,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, childSetAction("connect_btn", onClickConnect, this); - getChild("login")->setDefaultBtn("connect_btn"); + getChild("links_login_panel")->setDefaultBtn("connect_btn"); std::string channel = LLVersionInfo::getChannel(); std::string version = llformat("%s (%d)", @@ -211,8 +215,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLTextBox* forgot_password_text = getChild("forgot_password_text"); forgot_password_text->setClickedCallback(onClickForgotPassword, NULL); - LLTextBox* create_new_account_text = getChild("create_new_account_text"); - create_new_account_text->setClickedCallback(onClickNewAccount, NULL); + childSetAction("create_new_account_btn", onClickNewAccount, NULL); LLTextBox* need_help_text = getChild("login_help"); need_help_text->setClickedCallback(onClickHelp, NULL); @@ -220,14 +223,17 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, // get the web browser control LLMediaCtrl* web_browser = getChild("login_html"); web_browser->addObserver(this); - + reshapeBrowser(); loadLoginPage(); - + // Show last logged in user favorites in "Start at" combo. addUsersWithFavoritesToUsername(); - getChild("username_combo")->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); + LLComboBox* username_combo(getChild("username_combo")); + username_combo->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); + // STEAM-14: When user presses Enter with this field in focus, initiate login + username_combo->setCommitCallback(boost::bind(&LLPanelLogin::onClickConnect, this)); } void LLPanelLogin::addUsersWithFavoritesToUsername() @@ -636,12 +642,10 @@ void LLPanelLogin::updateLocationSelectorsVisibility() if (sInstance) { BOOL show_start = gSavedSettings.getBOOL("ShowStartLocation"); - sInstance->getChildView("start_location_combo")->setVisible(show_start); - sInstance->getChildView("start_location_text")->setVisible(show_start); + sInstance->getChild("start_location_panel")->setVisible(show_start); BOOL show_server = gSavedSettings.getBOOL("ForceShowGrid"); - LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); - server_choice_combo->setVisible( show_server ); + sInstance->getChild("grid_panel")->setVisible(show_server); } } @@ -746,63 +750,48 @@ void LLPanelLogin::setAlwaysRefresh(bool refresh) void LLPanelLogin::loadLoginPage() { if (!sInstance) return; - - std::ostringstream oStr; - std::string login_page = LLGridManager::getInstance()->getLoginPage(); + LLURI login_page = LLURI(LLGridManager::getInstance()->getLoginPage()); + LLSD params(login_page.queryMap()); - oStr << login_page; - - // Use the right delimeter depending on how LLURI parses the URL - LLURI login_page_uri = LLURI(login_page); - - std::string first_query_delimiter = "&"; - if (login_page_uri.queryMap().size() == 0) - { - first_query_delimiter = "?"; - } + LL_DEBUGS("AppInit") << "login_page: " << login_page << LL_ENDL; // Language - std::string language = LLUI::getLanguage(); - oStr << first_query_delimiter<<"lang=" << language; - + params["lang"] = LLUI::getLanguage(); + // First Login? if (gSavedSettings.getBOOL("FirstLoginThisInstall")) { - oStr << "&firstlogin=TRUE"; + params["firstlogin"] = "TRUE"; // not bool: server expects string TRUE } // Channel and Version - std::string version = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); - - char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0); - char* curl_version = curl_escape(version.c_str(), 0); - - oStr << "&channel=" << curl_channel; - oStr << "&version=" << curl_version; - - curl_free(curl_channel); - curl_free(curl_version); + params["version"] = llformat("%s (%d)", + LLVersionInfo::getShortVersion().c_str(), + LLVersionInfo::getBuild()); + params["channel"] = LLVersionInfo::getChannel(); // Grid - char* curl_grid = curl_escape(LLGridManager::getInstance()->getGridId().c_str(), 0); - oStr << "&grid=" << curl_grid; - curl_free(curl_grid); - + params["grid"] = LLGridManager::getInstance()->getGridId(); + // add OS info - char * os_info = curl_escape(LLAppViewer::instance()->getOSInfo().getOSStringSimple().c_str(), 0); - oStr << "&os=" << os_info; - curl_free(os_info); - + params["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); + + // sourceid + params["sourceid"] = gSavedSettings.getString("sourceid"); + + // Make an LLURI with this augmented info + LLURI login_uri(LLURI::buildHTTP(login_page.authority(), + login_page.path(), + params)); + gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); - + LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - if (web_browser->getCurrentNavUrl() != oStr.str()) + if (web_browser->getCurrentNavUrl() != login_uri.asString()) { - LL_DEBUGS("AppInit")<navigateTo( oStr.str(), "text/html" ); + LL_DEBUGS("AppInit") << "loading: " << login_uri << LL_ENDL; + web_browser->navigateTo( login_uri.asString(), "text/html" ); } } @@ -886,7 +875,7 @@ void LLPanelLogin::onClickNewAccount(void*) { if (sInstance) { - LLWeb::loadURLExternal(sInstance->getString("create_account_url")); + LLWeb::loadURLExternal(LLTrans::getString("create_account_url")); } } @@ -946,9 +935,11 @@ void LLPanelLogin::updateServer() // update the login panel links bool system_grid = LLGridManager::getInstance()->isSystemGrid(); - - sInstance->getChildView("create_new_account_text")->setVisible( system_grid); - sInstance->getChildView("forgot_password_text")->setVisible( system_grid); + + // Want to vanish not only create_new_account_btn, but also the + // title text over it, so turn on/off the whole layout_panel element. + sInstance->getChild("links")->setVisible(system_grid); + sInstance->getChildView("forgot_password_text")->setVisible(system_grid); // grid changed so show new splash screen (possibly) loadLoginPage(); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index c11597f532..9f3273da2d 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -306,6 +306,7 @@ void LLPanelMainInventory::newWindow() void LLPanelMainInventory::doCreate(const LLSD& userdata) { + reset_inventory_filter(); menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata); } diff --git a/indra/newview/llpanelpathfindingrebakenavmesh.cpp b/indra/newview/llpanelpathfindingrebakenavmesh.cpp deleted file mode 100644 index 7efb1a9227..0000000000 --- a/indra/newview/llpanelpathfindingrebakenavmesh.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/** -* @file llpanelpathfindingrebakenavmesh.cpp -* @brief Implementation of llpanelpathfindingrebakenavmesh -* @author Prep@lindenlab.com -* -* $LicenseInfo:firstyear=2012&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2012, 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 "llviewerprecompiledheaders.h" - -#include "llpanelpathfindingrebakenavmesh.h" - -#include -#include - -#include "llagent.h" -#include "llbutton.h" -#include "llenvmanager.h" -#include "llhints.h" -#include "llnotificationsutil.h" -#include "llpanel.h" -#include "llpathfindingmanager.h" -#include "llpathfindingnavmesh.h" -#include "llpathfindingnavmeshstatus.h" -#include "lltoolbar.h" -#include "lltoolbarview.h" -#include "lltooltip.h" -#include "llviewerregion.h" - -LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getInstance() -{ - static LLPanelPathfindingRebakeNavmesh* panel = getPanel(); - return panel; -} - -BOOL LLPanelPathfindingRebakeNavmesh::postBuild() -{ - //Rebake button - mNavMeshRebakeButton = findChild("navmesh_btn"); - llassert(mNavMeshRebakeButton != NULL); - mNavMeshRebakeButton->setCommitCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick, this)); - LLHints::registerHintTarget("navmesh_btn", mNavMeshRebakeButton->getHandle()); - - //Sending rebake request - mNavMeshSendingButton = findChild("navmesh_btn_sending"); - llassert(mNavMeshSendingButton != NULL); - LLHints::registerHintTarget("navmesh_btn_sending", mNavMeshSendingButton->getHandle()); - - //rebaking... - mNavMeshBakingButton = findChild("navmesh_btn_baking"); - llassert(mNavMeshBakingButton != NULL); - LLHints::registerHintTarget("navmesh_btn_baking", mNavMeshBakingButton->getHandle()); - - setMode(kRebakeNavMesh_Default); - - createNavMeshStatusListenerForCurrentRegion(); - - if ( !mRegionCrossingSlot.connected() ) - { - mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this)); - } - - if (!mAgentStateSlot.connected()) - { - mAgentStateSlot = LLPathfindingManager::getInstance()->registerAgentStateListener(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleAgentState, this, _1)); - } - LLPathfindingManager::getInstance()->requestGetAgentState(); - - return LLPanel::postBuild(); -} - -void LLPanelPathfindingRebakeNavmesh::draw() -{ - if (doDraw()) - { - updatePosition(); - LLPanel::draw(); - } -} - -BOOL LLPanelPathfindingRebakeNavmesh::handleToolTip( S32 x, S32 y, MASK mask ) -{ - LLToolTipMgr::instance().unblockToolTips(); - - if (mNavMeshRebakeButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshRebakeButton->getToolTip()); - } - else if (mNavMeshSendingButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshSendingButton->getToolTip()); - } - else if (mNavMeshBakingButton->getVisible()) - { - LLToolTipMgr::instance().show(mNavMeshBakingButton->getToolTip()); - } - - return LLPanel::handleToolTip(x, y, mask); -} - -LLPanelPathfindingRebakeNavmesh::LLPanelPathfindingRebakeNavmesh() - : LLPanel(), - mCanRebakeRegion(FALSE), - mRebakeNavMeshMode(kRebakeNavMesh_Default), - mNavMeshRebakeButton(NULL), - mNavMeshSendingButton(NULL), - mNavMeshBakingButton(NULL), - mNavMeshSlot(), - mRegionCrossingSlot(), - mAgentStateSlot() -{ - // make sure we have the only instance of this class - static bool b = true; - llassert_always(b); - b=false; -} - -LLPanelPathfindingRebakeNavmesh::~LLPanelPathfindingRebakeNavmesh() -{ -} - -LLPanelPathfindingRebakeNavmesh* LLPanelPathfindingRebakeNavmesh::getPanel() -{ - LLPanelPathfindingRebakeNavmesh* panel = new LLPanelPathfindingRebakeNavmesh(); - panel->buildFromFile("panel_navmesh_rebake.xml"); - return panel; -} - -void LLPanelPathfindingRebakeNavmesh::setMode(ERebakeNavMeshMode pRebakeNavMeshMode) -{ - if (pRebakeNavMeshMode == kRebakeNavMesh_Available) - { - LLNotificationsUtil::add("PathfindingRebakeNavmesh"); - } - mNavMeshRebakeButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_Available); - mNavMeshSendingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_RequestSent); - mNavMeshBakingButton->setVisible(pRebakeNavMeshMode == kRebakeNavMesh_InProgress); - mRebakeNavMeshMode = pRebakeNavMeshMode; -} - -LLPanelPathfindingRebakeNavmesh::ERebakeNavMeshMode LLPanelPathfindingRebakeNavmesh::getMode() const -{ - return mRebakeNavMeshMode; -} - -void LLPanelPathfindingRebakeNavmesh::onNavMeshRebakeClick() -{ - setMode(kRebakeNavMesh_RequestSent); - LLPathfindingManager::getInstance()->requestRebakeNavMesh(boost::bind(&LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse, this, _1)); -} - -void LLPanelPathfindingRebakeNavmesh::handleAgentState(BOOL pCanRebakeRegion) -{ - mCanRebakeRegion = pCanRebakeRegion; -} - -void LLPanelPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponseStatus) -{ - if (getMode() == kRebakeNavMesh_RequestSent) - { - setMode(pResponseStatus ? kRebakeNavMesh_InProgress : kRebakeNavMesh_Default); - } - - if (!pResponseStatus) - { - LLNotificationsUtil::add("PathfindingCannotRebakeNavmesh"); - } -} - -void LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus) -{ - ERebakeNavMeshMode rebakeNavMeshMode = kRebakeNavMesh_Default; - if (pNavMeshStatus.isValid()) - { - switch (pNavMeshStatus.getStatus()) - { - case LLPathfindingNavMeshStatus::kPending : - case LLPathfindingNavMeshStatus::kRepending : - rebakeNavMeshMode = kRebakeNavMesh_Available; - break; - case LLPathfindingNavMeshStatus::kBuilding : - rebakeNavMeshMode = kRebakeNavMesh_InProgress; - break; - case LLPathfindingNavMeshStatus::kComplete : - rebakeNavMeshMode = kRebakeNavMesh_NotAvailable; - break; - default : - rebakeNavMeshMode = kRebakeNavMesh_Default; - llassert(0); - break; - } - } - - setMode(rebakeNavMeshMode); -} - -void LLPanelPathfindingRebakeNavmesh::handleRegionBoundaryCrossed() -{ - createNavMeshStatusListenerForCurrentRegion(); - mCanRebakeRegion = FALSE; - LLPathfindingManager::getInstance()->requestGetAgentState(); -} - -void LLPanelPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegion() -{ - if (mNavMeshSlot.connected()) - { - mNavMeshSlot.disconnect(); - } - - LLViewerRegion *currentRegion = gAgent.getRegion(); - if (currentRegion != NULL) - { - mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLPanelPathfindingRebakeNavmesh::handleNavMeshStatus, this, _2)); - LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true); - } -} - -bool LLPanelPathfindingRebakeNavmesh::doDraw() const -{ - return (mCanRebakeRegion && (mRebakeNavMeshMode != kRebakeNavMesh_NotAvailable)); -} - -void LLPanelPathfindingRebakeNavmesh::updatePosition() -{ - S32 y_pos = 0; - S32 bottom_tb_center = 0; - - if (LLToolBar* toolbar_bottom = gToolBarView->getChild("toolbar_bottom")) - { - y_pos = toolbar_bottom->getRect().getHeight(); - bottom_tb_center = toolbar_bottom->getRect().getCenterX(); - } - - S32 left_tb_width = 0; - if (LLToolBar* toolbar_left = gToolBarView->getChild("toolbar_left")) - { - left_tb_width = toolbar_left->getRect().getWidth(); - } - - if(LLPanel* panel_ssf_container = getRootView()->getChild("state_management_buttons_container")) - { - panel_ssf_container->setOrigin(0, y_pos); - } - - S32 x_pos = bottom_tb_center-getRect().getWidth()/2 - left_tb_width + 113 /* width of stand/fly button */ + 10 /* margin */; - - setOrigin( x_pos, 0); -} diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp index c6b9aaae4b..9902d047e4 100644 --- a/indra/newview/llpolymesh.cpp +++ b/indra/newview/llpolymesh.cpp @@ -240,6 +240,7 @@ BOOL LLPolyMeshSharedData::allocateVertexData( U32 numVertices ) mBaseCoords[i].clear(); mBaseNormals[i].clear(); mBaseBinormals[i].clear(); + mTexCoords[i].clear(); mWeights[i] = 0.f; } mNumVertices = numVertices; diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index 18626e3273..04934b13f1 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -464,7 +464,9 @@ LLMultiPreview::LLMultiPreview() void LLMultiPreview::onOpen(const LLSD& key) { - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* frontmost_preview = dynamic_cast(mTabContainer->getCurrentPanel()); + if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { frontmost_preview->loadAsset(); @@ -477,8 +479,13 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) { if(new_rect.getWidth() != getRect().getWidth() || new_rect.getHeight() != getRect().getHeight()) { - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); - if (frontmost_preview) frontmost_preview->userResized(); + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* frontmost_preview = dynamic_cast(mTabContainer->getCurrentPanel()); + + if (frontmost_preview) + { + frontmost_preview->userResized(); + } } LLFloater::handleReshape(new_rect, by_user); } @@ -486,7 +493,9 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) void LLMultiPreview::tabOpen(LLFloater* opened_floater, bool from_click) { - LLPreview* opened_preview = (LLPreview*)opened_floater; + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* opened_preview = dynamic_cast(opened_floater); + if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { opened_preview->loadAsset(); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 88727bf59b..9c25e69db0 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -815,7 +815,7 @@ void LLScriptEdCore::onBtnDynamicHelp() if (!live_help_floater) { live_help_floater = new LLFloater(LLSD()); - live_help_floater->buildFromFile("floater_lsl_guide.xml", NULL); + live_help_floater->buildFromFile("floater_lsl_guide.xml"); LLFloater* parent = dynamic_cast(getParent()); llassert(parent); if (parent) diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index d340b304ca..d2280ea089 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -571,9 +571,13 @@ void LLScreenChannel::showToastsBottom() LLDockableFloater* floater = dynamic_cast(LLDockableFloater::getInstanceHandle().get()); - for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) + // Use a local variable instead of mToastList. + // mToastList can be modified during recursive calls and then all iteratos will be invalidated. + std::vector vToastList( mToastList ); + + for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) { - if(it != mToastList.rbegin()) + if(it != vToastList.rbegin()) { LLToast* toast = (it-1)->getToast(); if (!toast) @@ -601,7 +605,7 @@ void LLScreenChannel::showToastsBottom() if(floater && floater->overlapsScreenChannel()) { - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { // move first toast above docked floater S32 shift = floater->getRect().getHeight(); @@ -624,7 +628,7 @@ void LLScreenChannel::showToastsBottom() if(!stop_showing_toasts) { - if( it != mToastList.rend()-1) + if( it != vToastList.rend()-1) { S32 toast_top = toast->getRect().mTop + gSavedSettings.getS32("ToastGap"); stop_showing_toasts = toast_top > getRect().mTop; @@ -632,7 +636,8 @@ void LLScreenChannel::showToastsBottom() } // at least one toast should be visible - if(it == mToastList.rbegin()) + + if(it == vToastList.rbegin()) { stop_showing_toasts = false; } @@ -655,10 +660,11 @@ void LLScreenChannel::showToastsBottom() } // Dismiss toasts we don't have space for (STORM-391). - if(it != mToastList.rend()) + if(it != vToastList.rend()) { mHiddenToastsNum = 0; - for(; it != mToastList.rend(); it++) + + for(; it != vToastList.rend(); it++) { LLToast* toast = it->getToast(); if (toast) @@ -714,9 +720,13 @@ void LLScreenChannel::showToastsTop() LLDockableFloater* floater = dynamic_cast(LLDockableFloater::getInstanceHandle().get()); - for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) + // Use a local variable instead of mToastList. + // mToastList can be modified during recursive calls and then all iteratos will be invalidated. + std::vector vToastList( mToastList ); + + for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) { - if(it != mToastList.rbegin()) + if(it != vToastList.rbegin()) { LLToast* toast = (it-1)->getToast(); if (!toast) @@ -744,7 +754,7 @@ void LLScreenChannel::showToastsTop() if(floater && floater->overlapsScreenChannel()) { - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { // move first toast above docked floater S32 shift = -floater->getRect().getHeight(); @@ -767,7 +777,7 @@ void LLScreenChannel::showToastsTop() if(!stop_showing_toasts) { - if( it != mToastList.rend()-1) + if( it != vToastList.rend()-1) { S32 toast_bottom = toast->getRect().mBottom - gSavedSettings.getS32("ToastGap"); stop_showing_toasts = toast_bottom < channel_rect.mBottom; @@ -775,7 +785,7 @@ void LLScreenChannel::showToastsTop() } // at least one toast should be visible - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { stop_showing_toasts = false; } @@ -799,10 +809,12 @@ void LLScreenChannel::showToastsTop() // Dismiss toasts we don't have space for (STORM-391). std::vector toasts_to_hide; - if(it != mToastList.rend()) + + if(it != vToastList.rend()) { mHiddenToastsNum = 0; - for(; it != mToastList.rend(); it++) + + for(; it != vToastList.rend(); it++) { LLToast* toast = it->getToast(); if (toast) diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 3b52dd552f..c3c37141ed 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -390,7 +390,7 @@ LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, // don't include an avatar. LLViewerObject* root = obj; - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + while(!root->isAvatar() && root->getParent()) { LLViewerObject* parent = (LLViewerObject*)root->getParent(); if (parent->isAvatar()) @@ -684,7 +684,7 @@ void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_s // don't include an avatar. LLViewerObject* root = object; - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + while(!root->isAvatar() && root->getParent()) { LLViewerObject* parent = (LLViewerObject*)root->getParent(); if (parent->isAvatar()) @@ -1397,7 +1397,7 @@ void LLSelectMgr::promoteSelectionToRoot() } LLViewerObject* parentp = object; - while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild())) + while(parentp->getParent() && !(parentp->isRootEdit())) { parentp = (LLViewerObject*)parentp->getParent(); } @@ -4474,8 +4474,7 @@ struct LLSelectMgrApplyFlags : public LLSelectedObjectFunctor virtual bool apply(LLViewerObject* object) { if ( object->permModify() && // preemptive permissions check - object->isRoot() && // don't send for child objects - !object->isJointChild()) + object->isRoot()) // don't send for child objects { object->setFlags( mFlags, mState); } @@ -6330,8 +6329,6 @@ void LLSelectMgr::updateSelectionCenter() // matches the root prim's (affecting the orientation of the manipulators). bbox.addBBoxAgent( (mSelectedObjects->getFirstRootObject(TRUE))->getBoundingBoxAgent() ); - std::vector < LLViewerObject *> jointed_objects; - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); iter != mSelectedObjects->end(); iter++) { @@ -6349,11 +6346,6 @@ void LLSelectMgr::updateSelectionCenter() } bbox.addBBoxAgent( object->getBoundingBoxAgent() ); - - if (object->isJointChild()) - { - jointed_objects.push_back(object); - } } LLVector3 bbox_center_agent = bbox.getCenterAgent(); @@ -6643,19 +6635,19 @@ void LLSelectMgr::setAgentHUDZoom(F32 target_zoom, F32 current_zoom) bool LLObjectSelection::is_root::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit()); } bool LLObjectSelection::is_valid_root::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit()); } bool LLObjectSelection::is_root_object::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && (object->isRootEdit()); } LLObjectSelection::LLObjectSelection() : diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 3bb8e60787..2083afdcf5 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -3500,6 +3500,8 @@ void renderPhysicsShapes(LLSpatialGroup* group) LLViewerObject* object = drawable->getVObj(); if (object && object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) { + gGL.pushMatrix(); + gGL.multMatrix((F32*) object->getRegion()->mRenderMatrix.mMatrix); //push face vertices for terrain for (S32 i = 0; i < drawable->getNumFaces(); ++i) { @@ -3521,6 +3523,7 @@ void renderPhysicsShapes(LLSpatialGroup* group) } } } + gGL.popMatrix(); } } } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp old mode 100644 new mode 100755 index 218c35029e..3d1fd74ba6 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -362,6 +362,15 @@ bool idle_startup() if ( STATE_FIRST == LLStartUp::getStartupState() ) { + static bool first_call = true; + if (first_call) + { + // Other phases get handled when startup state changes, + // need to capture the initial state as well. + LLStartUp::getPhases().startPhase(LLStartUp::getStartupStateString()); + first_call = false; + } + gViewerWindow->showCursor(); gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); @@ -721,12 +730,14 @@ bool idle_startup() if (STATE_LOGIN_SHOW == LLStartUp::getStartupState()) { - LL_DEBUGS("AppInit") << "Initializing Window" << LL_ENDL; + LL_DEBUGS("AppInit") << "Initializing Window, show_connect_box = " + << show_connect_box << LL_ENDL; // if we've gone backwards in the login state machine, to this state where we show the UI // AND the debug setting to exit in this case is true, then go ahead and bail quickly if ( mLoginStatePastUI && gSavedSettings.getBOOL("QuitOnLoginActivated") ) { + LL_DEBUGS("AppInit") << "taking QuitOnLoginActivated exit" << LL_ENDL; // no requirement for notification here - just exit LLAppViewer::instance()->earlyExitNoNotify(); } @@ -739,6 +750,7 @@ bool idle_startup() // this startup phase more than once. if (gLoginMenuBarView == NULL) { + LL_DEBUGS("AppInit") << "initializing menu bar" << LL_ENDL; display_startup(); initialize_edit_menu(); initialize_spellcheck_menu(); @@ -749,11 +761,13 @@ bool idle_startup() if (show_connect_box) { + LL_DEBUGS("AppInit") << "show_connect_box on" << LL_ENDL; // Load all the name information out of the login view // NOTE: Hits "Attempted getFields with no login view shown" warning, since we don't // show the login view until login_show() is called below. if (gUserCredential.isNull()) { + LL_DEBUGS("AppInit") << "loading credentials from gLoginHandler" << LL_ENDL; display_startup(); gUserCredential = gLoginHandler.initializeLoginInfo(); display_startup(); @@ -770,17 +784,28 @@ bool idle_startup() login_show(); display_startup(); // connect dialog is already shown, so fill in the names - if (gUserCredential.notNull()) - { - LLPanelLogin::setFields( gUserCredential, gRememberPassword); - } + if (gUserCredential.notNull()) + { + LLPanelLogin::setFields( gUserCredential, gRememberPassword); + } display_startup(); LLPanelLogin::giveFocus(); + if (gSavedSettings.getBOOL("FirstLoginThisInstall")) + { + LL_INFOS("AppInit") << "FirstLoginThisInstall, calling show_first_run_dialog()" << LL_ENDL; + show_first_run_dialog(); + } + else + { + LL_DEBUGS("AppInit") << "FirstLoginThisInstall off" << LL_ENDL; + } + LLStartUp::setStartupState( STATE_LOGIN_WAIT ); // Wait for user input } else { + LL_DEBUGS("AppInit") << "show_connect_box off, skipping to STATE_LOGIN_CLEANUP" << LL_ENDL; // skip directly to message template verification LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); } @@ -2704,9 +2729,10 @@ void LLStartUp::setStartupState( EStartupState state ) getStartupStateString() << " to " << startupStateToString(state) << LL_ENDL; - sPhases->stopPhase(getStartupStateString()); + getPhases().stopPhase(getStartupStateString()); gStartupState = state; - sPhases->startPhase(getStartupStateString()); + getPhases().startPhase(getStartupStateString()); + postStartupState(); } @@ -3281,11 +3307,8 @@ bool process_login_success_response() { // replace the default help URL format gSavedSettings.setString("HelpURLFormat",text); - - // don't fall back to Standalone's pre-connection static help - gSavedSettings.setBOOL("HelpUseLocal", false); } - + std::string home_location = response["home"]; if(!home_location.empty()) { diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h old mode 100644 new mode 100755 diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 89240c982f..ff69c6e9fd 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -469,7 +469,7 @@ void LLStatusBar::onMouseEnterVolume() LLRect vol_btn_rect = volbtn->getRect(); LLRect volume_pulldown_rect = mPanelVolumePulldown->getRect(); volume_pulldown_rect.setLeftTopAndSize(vol_btn_rect.mLeft - - (volume_pulldown_rect.getWidth() - vol_btn_rect.getWidth())/2, + (volume_pulldown_rect.getWidth() - vol_btn_rect.getWidth()), vol_btn_rect.mBottom, volume_pulldown_rect.getWidth(), volume_pulldown_rect.getHeight()); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 0cb6c85012..2002647fef 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -242,7 +242,7 @@ LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) : LLPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent) { - buildFromFile( "panel_activeim_row.xml", NULL); + buildFromFile( "panel_activeim_row.xml"); // Choose which of the pre-created chiclets (IM/group) to use. // The other one gets hidden. @@ -356,7 +356,7 @@ LLIMWellWindow::ObjectRowPanel::ObjectRowPanel(const LLUUID& notification_id, bo : LLPanel() , mChiclet(NULL) { - buildFromFile( "panel_active_object_row.xml", NULL); + buildFromFile( "panel_active_object_row.xml"); initChiclet(notification_id); diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 8632890bbb..305f6fca0f 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -50,6 +50,8 @@ const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE;//1024; const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate) +const S32 TEXTURE_FAST_CACHE_ENTRY_OVERHEAD = sizeof(S32) * 4; //w, h, c, level +const S32 TEXTURE_FAST_CACHE_ENTRY_SIZE = 16 * 16 * 4 + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD; class LLTextureCacheWorker : public LLWorkerClass { @@ -283,9 +285,12 @@ public: LLTextureCacheRemoteWorker(LLTextureCache* cache, U32 priority, const LLUUID& id, U8* data, S32 datasize, S32 offset, S32 imagesize, // for writes + LLPointer raw, S32 discardlevel, LLTextureCache::Responder* responder) : LLTextureCacheWorker(cache, priority, id, data, datasize, offset, imagesize, responder), - mState(INIT) + mState(INIT), + mRawImage(raw), + mRawDiscardLevel(discardlevel) { } @@ -303,6 +308,8 @@ private: }; e_state mState; + LLPointer mRawImage; + S32 mRawDiscardLevel; }; @@ -559,6 +566,11 @@ bool LLTextureCacheRemoteWorker::doWrite() if(idx < 0) { idx = mCache->setHeaderCacheEntry(mID, entry, mImageSize, mDataSize); // create the new entry. + if(idx >= 0) + { + //write to the fast cache. + llassert_always(mCache->writeToFastCache(idx, mRawImage, mRawDiscardLevel)); + } } else { @@ -658,6 +670,7 @@ bool LLTextureCacheRemoteWorker::doWrite() // Nothing else to do at that point... done = true; } + mRawImage = NULL; // Clean up and exit return done; @@ -744,10 +757,14 @@ LLTextureCache::LLTextureCache(bool threaded) mWorkersMutex(NULL), mHeaderMutex(NULL), mListMutex(NULL), + mFastCacheMutex(NULL), mHeaderAPRFile(NULL), mReadOnly(TRUE), //do not allow to change the texture cache until setReadOnly() is called. mTexturesSizeTotal(0), - mDoPurge(FALSE) + mDoPurge(FALSE), + mFastCachep(NULL), + mFastCachePoolp(NULL), + mFastCachePadBuffer(NULL) { } @@ -755,6 +772,9 @@ LLTextureCache::~LLTextureCache() { clearDeleteList() ; writeUpdatedEntries() ; + delete mFastCachep; + delete mFastCachePoolp; + FREE_MEM(LLImageBase::getPrivatePool(), mFastCachePadBuffer); } ////////////////////////////////////////////////////////////////////////////// @@ -879,15 +899,15 @@ BOOL LLTextureCache::isInLocal(const LLUUID& id) ////////////////////////////////////////////////////////////////////////////// //static -const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB -F32 LLTextureCache::sHeaderCacheVersion = 1.4f; -U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE; +F32 LLTextureCache::sHeaderCacheVersion = 1.7f; +U32 LLTextureCache::sCacheMaxEntries = 1024 * 1024; //~1 million textures. S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit const char* entries_filename = "texture.entries"; const char* cache_filename = "texture.cache"; const char* old_textures_dirname = "textures"; //change the location of the texture cache to prevent from being deleted by old version viewers. const char* textures_dirname = "texturecache"; +const char* fast_cache_filename = "FastCache.cache"; void LLTextureCache::setDirNames(ELLPath location) { @@ -896,6 +916,7 @@ void LLTextureCache::setDirNames(ELLPath location) mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, entries_filename); mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, cache_filename); mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); + mFastCacheFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, fast_cache_filename); } void LLTextureCache::purgeCache(ELLPath location) @@ -938,8 +959,8 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL texture_cache { llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized. - S64 header_size = (max_size * 2) / 10; - S64 max_entries = header_size / TEXTURE_CACHE_ENTRY_SIZE; + S64 header_size = (max_size / 100) * 36; //0.36 * max_size + S64 max_entries = header_size / (TEXTURE_CACHE_ENTRY_SIZE + TEXTURE_FAST_CACHE_ENTRY_SIZE); sCacheMaxEntries = (S32)(llmin((S64)sCacheMaxEntries, max_entries)); header_size = sCacheMaxEntries * TEXTURE_CACHE_ENTRY_SIZE; max_size -= header_size; @@ -981,6 +1002,7 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL texture_cache purgeTextures(true); // calc mTexturesSize and make some room in the texture cache if we need it llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized. + openFastCache(true); return max_size; // unused cache space } @@ -1751,7 +1773,7 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 pri LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, NULL, size, offset, - 0, responder); + 0, NULL, 0, responder); handle_t handle = worker->read(); mReaders[handle] = worker; return handle; @@ -1789,6 +1811,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort) LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 priority, U8* data, S32 datasize, S32 imagesize, + LLPointer rawimage, S32 discardlevel, WriteResponder* responder) { if (mReadOnly) @@ -1807,12 +1830,174 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, data, datasize, 0, - imagesize, responder); + imagesize, rawimage, discardlevel, responder); handle_t handle = worker->write(); mWriters[handle] = worker; return handle; } +//called in the main thread +LLPointer LLTextureCache::readFromFastCache(const LLUUID& id, S32& discardlevel) +{ + U32 offset; + { + LLMutexLock lock(&mHeaderMutex); + id_map_t::const_iterator iter = mHeaderIDMap.find(id); + if(iter == mHeaderIDMap.end()) + { + return NULL; //not in the cache + } + + offset = iter->second; + } + offset *= TEXTURE_FAST_CACHE_ENTRY_SIZE; + + U8* data; + S32 head[4]; + { + LLMutexLock lock(&mFastCacheMutex); + + openFastCache(); + + mFastCachep->seek(APR_SET, offset); + + if(mFastCachep->read(head, TEXTURE_FAST_CACHE_ENTRY_OVERHEAD) != TEXTURE_FAST_CACHE_ENTRY_OVERHEAD) + { + //cache corrupted or under thread race condition + closeFastCache(); + return NULL; + } + + S32 image_size = head[0] * head[1] * head[2]; + if(!image_size) //invalid + { + closeFastCache(); + return NULL; + } + discardlevel = head[3]; + + data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), image_size); + if(mFastCachep->read(data, image_size) != image_size) + { + FREE_MEM(LLImageBase::getPrivatePool(), data); + closeFastCache(); + return NULL; + } + + closeFastCache(); + } + LLPointer raw = new LLImageRaw(data, head[0], head[1], head[2], true); + + return raw; +} + +//return the fast cache location +bool LLTextureCache::writeToFastCache(S32 id, LLPointer raw, S32 discardlevel) +{ + //rescale image if needed + S32 w, h, c; + w = raw->getWidth(); + h = raw->getHeight(); + c = raw->getComponents(); + S32 i = 0 ; + + while(((w >> i) * (h >> i) * c) > TEXTURE_FAST_CACHE_ENTRY_SIZE - TEXTURE_FAST_CACHE_ENTRY_OVERHEAD) + { + ++i ; + } + + if(i) + { + w >>= i; + h >>= i; + if(w * h *c > 0) //valid + { + LLPointer newraw = new LLImageRaw(raw->getData(), raw->getWidth(), raw->getHeight(), raw->getComponents()); + newraw->scale(w, h) ; + raw = newraw; + + discardlevel += i ; + } + } + + //copy data + memcpy(mFastCachePadBuffer, &w, sizeof(S32)); + memcpy(mFastCachePadBuffer + sizeof(S32), &h, sizeof(S32)); + memcpy(mFastCachePadBuffer + sizeof(S32) * 2, &c, sizeof(S32)); + memcpy(mFastCachePadBuffer + sizeof(S32) * 3, &discardlevel, sizeof(S32)); + if(w * h * c > 0) //valid + { + memcpy(mFastCachePadBuffer + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD, raw->getData(), w * h * c); + } + S32 offset = id * TEXTURE_FAST_CACHE_ENTRY_SIZE; + + { + LLMutexLock lock(&mFastCacheMutex); + + openFastCache(); + + mFastCachep->seek(APR_SET, offset); + + //no need to do this assertion check. When it fails, let it fail quietly. + //this failure could happen because other viewer removes the fast cache file when clearing cache. + //--> llassert_always(mFastCachep->write(mFastCachePadBuffer, TEXTURE_FAST_CACHE_ENTRY_SIZE) == TEXTURE_FAST_CACHE_ENTRY_SIZE); + mFastCachep->write(mFastCachePadBuffer, TEXTURE_FAST_CACHE_ENTRY_SIZE); + + closeFastCache(true); + } + + return true; +} + +void LLTextureCache::openFastCache(bool first_time) +{ + if(!mFastCachep) + { + if(first_time) + { + if(!mFastCachePadBuffer) + { + mFastCachePadBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), TEXTURE_FAST_CACHE_ENTRY_SIZE); + } + mFastCachePoolp = new LLVolatileAPRPool(); + if (LLAPRFile::isExist(mFastCacheFileName, mFastCachePoolp)) + { + mFastCachep = new LLAPRFile(mFastCacheFileName, APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; + } + else + { + mFastCachep = new LLAPRFile(mFastCacheFileName, APR_CREATE|APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; + } + } + else + { + mFastCachep = new LLAPRFile(mFastCacheFileName, APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; + } + + mFastCacheTimer.reset(); + } + return; +} + +void LLTextureCache::closeFastCache(bool forced) +{ + static const F32 timeout = 10.f ; //seconds + + if(!mFastCachep) + { + return ; + } + + if(!forced && mFastCacheTimer.getElapsedTimeF32() < timeout) + { + return ; + } + + delete mFastCachep; + mFastCachep = NULL; + return; +} + bool LLTextureCache::writeComplete(handle_t handle, bool abort) { lockWorkers(); diff --git a/indra/newview/lltexturecache.h b/indra/newview/lltexturecache.h index dd0cc9b4bd..e3fc957fd2 100644 --- a/indra/newview/lltexturecache.h +++ b/indra/newview/lltexturecache.h @@ -36,6 +36,7 @@ class LLImageFormatted; class LLTextureCacheWorker; +class LLImageRaw; class LLTextureCache : public LLWorkerThread { @@ -113,8 +114,9 @@ public: handle_t readFromCache(const LLUUID& id, U32 priority, S32 offset, S32 size, ReadResponder* responder); bool readComplete(handle_t handle, bool abort); - handle_t writeToCache(const LLUUID& id, U32 priority, U8* data, S32 datasize, S32 imagesize, + handle_t writeToCache(const LLUUID& id, U32 priority, U8* data, S32 datasize, S32 imagesize, LLPointer rawimage, S32 discardlevel, WriteResponder* responder); + LLPointer readFromFastCache(const LLUUID& id, S32& discardlevel); bool writeComplete(handle_t handle, bool abort = false); void prioritizeWrite(handle_t handle); @@ -171,12 +173,18 @@ private: void lockHeaders() { mHeaderMutex.lock(); } void unlockHeaders() { mHeaderMutex.unlock(); } + void openFastCache(bool first_time = false); + void closeFastCache(bool forced = false); + bool writeToFastCache(S32 id, LLPointer raw, S32 discardlevel); + private: // Internal LLMutex mWorkersMutex; LLMutex mHeaderMutex; LLMutex mListMutex; + LLMutex mFastCacheMutex; LLAPRFile* mHeaderAPRFile; + LLVolatileAPRPool* mFastCachePoolp; typedef std::map handle_map_t; handle_map_t mReaders; @@ -193,12 +201,17 @@ private: // HEADERS (Include first mip) std::string mHeaderEntriesFileName; std::string mHeaderDataFileName; + std::string mFastCacheFileName; EntriesInfo mHeaderEntriesInfo; std::set mFreeList; // deleted entries std::set mLRU; - typedef std::map id_map_t; + typedef std::map id_map_t; id_map_t mHeaderIDMap; + LLAPRFile* mFastCachep; + LLFrameTimer mFastCacheTimer; + U8* mFastCachePadBuffer; + // BODIES (TEXTURES minus headers) std::string mTexturesDirName; typedef std::map size_map_t; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index e665403110..95adf879ca 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -28,12 +28,12 @@ #include #include +#include #include "llstl.h" #include "lltexturefetch.h" -#include "llcurl.h" #include "lldir.h" #include "llhttpclient.h" #include "llhttpstatuscodes.h" @@ -54,28 +54,214 @@ #include "llworld.h" #include "llsdutil.h" #include "llstartup.h" -#include "llviewerstats.h" +#include "llsdserialize.h" + +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "bufferstream.h" bool LLTextureFetchDebugger::sDebuggerEnabled = false ; LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128); LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128); + ////////////////////////////////////////////////////////////////////////////// -class LLTextureFetchWorker : public LLWorkerClass +// +// Introduction +// +// This is an attempt to document what's going on in here after-the-fact. +// It's a sincere attempt to be accurate but there will be mistakes. +// +// +// Purpose +// +// What is this module trying to do? It accepts requests to load textures +// at a given priority and discard level and notifies the caller when done +// (successfully or not). Additional constraints are: +// +// * Support a local texture cache. Don't hit network when possible +// to avoid it. +// * Use UDP or HTTP as directed or as fallback. HTTP is tried when +// not disabled and a URL is available. UDP when a URL isn't +// available or HTTP attempts fail. +// * Asynchronous (using threads). Main thread is not to be blocked or +// burdened. +// * High concurrency. Many requests need to be in-flight and at various +// stages of completion. +// * Tolerate frequent re-prioritizations of requests. Priority is +// a reflection of a camera's viewpoint and as that viewpoint changes, +// objects and textures become more and less relevant and that is +// expressed at this level by priority changes and request cancelations. +// +// The caller interfaces that fall out of the above and shape the +// implementation are: +// * createRequest - Load j2c image via UDP or HTTP at given discard level and priority +// * deleteRequest - Request removal of prior request +// * getRequestFinished - Test if request is finished returning data to caller +// * updateRequestPriority - Change priority of existing request +// * getFetchState - Retrieve progress on existing request +// +// Everything else in here is mostly plumbing, metrics and debug. +// +// +// The Work Queue +// +// The two central classes are LLTextureFetch and LLTextureFetchWorker. +// LLTextureFetch combines threading with a priority queue of work +// requests. The priority queue is sorted by a U32 priority derived +// from the F32 priority in the APIs. The *only* work request that +// receives service time by this thread is the highest priority +// request. All others wait until it is complete or a dynamic priority +// change has re-ordered work. +// +// LLTextureFetchWorker implements the work request and is 1:1 with +// texture fetch requests. Embedded in each is a state machine that +// walks it through the cache, HTTP, UDP, image decode and retry +// steps of texture acquisition. +// +// +// Threads +// +// Several threads are actively invoking code in this module. They +// include: +// +// 1. Tmain Main thread of execution +// 2. Ttf LLTextureFetch's worker thread provided by LLQueuedThread +// 3. Tcurl LLCurl's worker thread (should disappear over time) +// 4. Ttc LLTextureCache's worker thread +// 5. Tid Image decoder's worker thread +// 6. Thl HTTP library's worker thread +// +// +// Mutexes/Condition Variables +// +// 1. Mt Mutex defined for LLThread's condition variable (base class of +// LLTextureFetch) +// 2. Ct Condition variable for LLThread and used by lock/unlockData(). +// 3. Mwtd Special LLWorkerThread mutex used for request deletion +// operations (base class of LLTextureFetch) +// 4. Mfq LLTextureFetch's mutex covering request and command queue +// data. +// 5. Mfnq LLTextureFetch's mutex covering udp and http request +// queue data. +// 6. Mwc Mutex covering LLWorkerClass's members (base class of +// LLTextureFetchWorker). One per request. +// 7. Mw LLTextureFetchWorker's mutex. One per request. +// +// +// Lock Ordering Rules +// +// Not an exhaustive list but shows the order of lock acquisition +// needed to prevent deadlocks. 'A < B' means acquire 'A' before +// acquiring 'B'. +// +// 1. Mw < Mfnq +// (there are many more...) +// +// +// Method and Member Definitions +// +// With the above, we'll try to document what threads can call what +// methods (using T* for any), what locks must be held on entry and +// are taken out during execution and what data is covered by which +// lock (if any). This latter category will be especially prone to +// error so be skeptical. +// +// A line like: "// Locks: M" indicates a method that must +// be invoked by a caller holding the 'M' lock. Similarly, +// "// Threads: T" means that a caller should be running in +// the indicated thread. +// +// For data members, a trailing comment like "// M" means that +// the data member is covered by the specified lock. Absence of a +// comment can mean the member is unlocked or that I didn't bother +// to do the archaeology. In the case of LLTextureFetchWorker, +// most data members added by the leaf class are actually covered +// by the Mw lock. You may also see "// T" which means that +// the member's usage is restricted to one thread (except for +// perhaps construction and destruction) and so explicit locking +// isn't used. +// +// In code, a trailing comment like "// [-+]M" indicates a +// lock acquision or release point. +// +// +// Worker Lifecycle +// +// The threading and responder model makes it very likely that +// other components are holding on to a pointer to a worker request. +// So, uncoordinated deletions of requests is a guarantee of memory +// corruption in a short time. So destroying a request involves +// invocations's of LLQueuedThread/LLWorkerThread's abort/stop +// logic that removes workers and puts them ona delete queue for +// 2-phase destruction. That second phase is deferrable by calls +// to deleteOK() which only allow final destruction (via dtor) +// once deleteOK has determined that the request is in a safe +// state. +// +// +// Worker State Machine +// +// (ASCII art needed) +// +// +// Priority Scheme +// +// [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state +// and other wait states +// [PRIORITY_HIGH, PRIORITY_URGENT) - External event delivered, +// rapidly transitioning through states, +// no waiting allowed +// +// By itself, the above work queue model would fail the concurrency +// and liveness requirements of the interface. A high priority +// request could find itself on the head and stalled for external +// reasons (see VWR-28996). So a few additional constraints are +// required to keep things running: +// * Anything that can make forward progress must be kept at a +// higher priority than anything that can't. +// * On completion of external events, the associated request +// needs to be elevated beyond the normal range to handle +// any data delivery and release any external resource. +// +// This effort is made to keep higher-priority entities moving +// forward in their state machines at every possible step of +// processing. It's not entirely proven that this produces the +// experiencial benefits promised. +// + + +////////////////////////////////////////////////////////////////////////////// + +// Tuning/Parameterization Constants + +static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; // Maximum requests to have active in HTTP +static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; // Active level at which to refill + + +////////////////////////////////////////////////////////////////////////////// + +class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler + { friend class LLTextureFetch; - friend class HTTPGetResponder; friend class LLTextureFetchDebugger; private: class CacheReadResponder : public LLTextureCache::ReadResponder { public: + + // Threads: Ttf CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) : mFetcher(fetcher), mID(id) { setImage(image); } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -92,10 +278,14 @@ private: class CacheWriteResponder : public LLTextureCache::WriteResponder { public: + + // Threads: Ttf CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) : mFetcher(fetcher), mID(id) { } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -112,10 +302,14 @@ private: class DecodeResponder : public LLImageDecodeThread::Responder { public: + + // Threads: Ttf DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) : mFetcher(fetcher), mID(id), mWorker(worker) { } + + // Threads: Tid virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -148,22 +342,35 @@ private: }; public: + + // Threads: Ttf /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() + + // Threads: Ttf /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) - /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) + + // Threads: Tmain + /*virtual*/ bool deleteOK(); // called from update() ~LLTextureFetchWorker(); - // void relese() { --mActiveCount; } - S32 callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); + // Threads: Ttf + // Locks: Mw + S32 callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success); + + // Threads: Ttc void callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); + + // Threads: Ttc void callbackCacheWrite(bool success); + + // Threads: Tid void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); - void setGetStatus(U32 status, const std::string& reason) + // Threads: T* + void setGetStatus(LLCore::HttpStatus status, const std::string& reason) { LLMutexLock lock(&mWorkMutex); @@ -175,33 +382,93 @@ public: bool getCanUseHTTP() const { return mCanUseHTTP; } LLTextureFetch & getFetcher() { return *mFetcher; } + + // Inherited from LLCore::HttpHandler + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 discard, S32 size); private: + + // Threads: Tmain /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) + + // Threads: Tmain /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) + // Locks: Mw void resetFormattedData(); + // Locks: Mw void setImagePriority(F32 priority); + + // Locks: Mw (ctor invokes without lock) void setDesiredDiscard(S32 discard, S32 size); + + // Threads: T* + // Locks: Mw bool insertPacket(S32 index, U8* data, S32 size); + + // Locks: Mw void clearPackets(); + + // Locks: Mw void setupPacketData(); + + // Locks: Mw (ctor invokes without lock) U32 calcWorkPriority(); + + // Locks: Mw void removeFromCache(); + + // Threads: Ttf + // Locks: Mw bool processSimulatorPackets(); + + // Threads: Ttf bool writeToCacheComplete(); + // Threads: Ttf + void recordTextureStart(bool is_http); + + // Threads: Ttf + void recordTextureDone(bool is_http); + void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + // Threads: Ttf + // Locks: Mw + bool acquireHttpSemaphore() + { + llassert(! mHttpHasResource); + if (mFetcher->mHttpSemaphore <= 0) + { + return false; + } + mHttpHasResource = true; + mFetcher->mHttpSemaphore--; + return true; + } + + // Threads: Ttf + // Locks: Mw + void releaseHttpSemaphore() + { + llassert(mHttpHasResource); + mHttpHasResource = false; + mFetcher->mHttpSemaphore++; + } + private: enum e_state // mState { + // *NOTE: Do not change the order/value of state variables, some code + // depends upon specific ordering/adjacency. + // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) INVALID = 0, INIT, @@ -209,8 +476,10 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, - WAIT_HTTP_REQ, + WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP + WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, WRITE_TO_CACHE, @@ -254,9 +523,8 @@ private: F32 mCacheReadTime; LLTextureCache::handle_t mCacheReadHandle; LLTextureCache::handle_t mCacheWriteHandle; - U8* mBuffer; - S32 mBufferSize; S32 mRequestedSize; + S32 mRequestedOffset; S32 mDesiredSize; S32 mFileSize; S32 mCachedSize; @@ -271,10 +539,9 @@ private: BOOL mInCache; bool mCanUseHTTP ; bool mCanUseNET ; //can get from asset server. - S32 mHTTPFailCount; S32 mRetryAttempt; S32 mActiveCount; - U32 mGetStatus; + LLCore::HttpStatus mGetStatus; std::string mGetReason; // Work Data @@ -294,105 +561,19 @@ private: U8 mImageCodec; LLViewerAssetStats::duration_t mMetricsStartTime; -}; -////////////////////////////////////////////////////////////////////////////// + LLCore::HttpHandle mHttpHandle; // Handle of any active request + LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data + int mHttpPolicyClass; + bool mHttpActive; // Active request to http library + unsigned int mHttpReplySize; // Actual received data size + unsigned int mHttpReplyOffset; // Actual received data offset + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore -class HTTPGetResponder : public LLCurl::Responder -{ - LOG_CLASS(HTTPGetResponder); -public: - HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir) - : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir) - { - } - ~HTTPGetResponder() - { - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - static LLCachedControl log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); - static LLCachedControl log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); - static LLCachedControl log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; - - if (log_to_viewer_log || log_to_sim) - { - mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); - U64 timeNow = LLTimer::getTotalTime(); - mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); - mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); - mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); - mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); - } - - lldebugs << "HTTP COMPLETE: " << mID << llendl; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - - if (!success) - { - worker->setGetStatus(status, reason); -// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; - } - - S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success); - - if(log_texture_traffic && data_size > 0) - { - LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ; - if(tex) - { - gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; - } - } - - mFetcher->removeFromHTTPQueue(mID, data_size); - - if (worker->mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType, - LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime); - worker->mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType); - } - else - { - mFetcher->removeFromHTTPQueue(mID); - llwarns << "Worker not found: " << mID << llendl; - } - } - - virtual bool followRedir() - { - return mFollowRedir; - } - -private: - LLTextureFetch* mFetcher; - LLUUID mID; - U64 mStartTime; - S32 mRequestedSize; - U32 mOffset; - bool mFollowRedir; + // State history + U32 mCacheReadCount; + U32 mCacheWriteCount; + U32 mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 }; ////////////////////////////////////////////////////////////////////////////// @@ -629,13 +810,15 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", + "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", "SEND_HTTP_REQ", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", "WRITE_TO_CACHE", "WAIT_ON_WRITE", - "DONE", + "DONE" }; // static @@ -651,6 +834,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, S32 discard, // Desired discard S32 size) // Desired size : LLWorkerClass(fetcher, "TextureFetch"), + LLCore::HttpHandler(), mState(INIT), mWriteToCacheState(NOT_WRITE), mFetcher(fetcher), @@ -668,9 +852,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mCacheReadTime(0.f), mCacheReadHandle(LLTextureCache::nullHandle()), mCacheWriteHandle(LLTextureCache::nullHandle()), - mBuffer(NULL), - mBufferSize(0), mRequestedSize(0), + mRequestedOffset(0), mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), mFileSize(0), mCachedSize(0), @@ -684,19 +867,27 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mInLocalCache(FALSE), mInCache(FALSE), mCanUseHTTP(true), - mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), - mGetStatus(0), mWorkMutex(NULL), mFirstPacket(0), mLastPacket(-1), mTotalPackets(0), mImageCodec(IMG_CODEC_INVALID), - mMetricsStartTime(0) + mMetricsStartTime(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mHttpBufferArray(NULL), + mHttpPolicyClass(mFetcher->mHttpPolicyClass), + mHttpActive(false), + mHttpReplySize(0U), + mHttpReplyOffset(0U), + mHttpHasResource(false), + mCacheReadCount(0U), + mCacheWriteCount(0U), + mResourceWaitCount(0U) { mCanUseNET = mUrl.empty() ; - + calcWorkPriority(); mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; // llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl; @@ -715,7 +906,20 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); - lockWorkMutex(); + + lockWorkMutex(); // +Mw (should be useless) + if (mHttpHasResource) + { + // Last-chance catchall to recover the resource. Using an + // atomic datatype solely because this can be running in + // another thread. + releaseHttpSemaphore(); + } + if (mHttpActive) + { + // Issue a cancel on a live request... + mFetcher->getHttpRequest().requestCancel(mHttpHandle, NULL); + } if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); @@ -726,10 +930,18 @@ LLTextureFetchWorker::~LLTextureFetchWorker() } mFormattedImage = NULL; clearPackets(); - unlockWorkMutex(); - mFetcher->removeFromHTTPQueue(mID); + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + unlockWorkMutex(); // -Mw + mFetcher->removeFromHTTPQueue(mID, 0); + mFetcher->removeHttpWaiter(mID); + mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); } +// Locks: Mw void LLTextureFetchWorker::clearPackets() { for_each(mPackets.begin(), mPackets.end(), DeletePointer()); @@ -739,6 +951,7 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } +// Locks: Mw void LLTextureFetchWorker::setupPacketData() { S32 data_size = 0; @@ -771,6 +984,7 @@ void LLTextureFetchWorker::setupPacketData() } } +// Locks: Mw (ctor invokes without lock) U32 LLTextureFetchWorker::calcWorkPriority() { //llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority()); @@ -780,7 +994,7 @@ U32 LLTextureFetchWorker::calcWorkPriority() return mWorkPriority; } -// mWorkMutex is locked +// Locks: Mw (ctor invokes without lock) void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { bool prioritize = false; @@ -816,6 +1030,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) } } +// Locks: Mw void LLTextureFetchWorker::setImagePriority(F32 priority) { // llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority()); @@ -829,32 +1044,37 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) } } +// Locks: Mw void LLTextureFetchWorker::resetFormattedData() { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } if (mFormattedImage.notNull()) { mFormattedImage->deleteData(); } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; } -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::startWork(S32 param) { llassert(mFormattedImage.isNull()); } -#include "llviewertexturelist.h" // debug - -// Called from LLWorkerThread::processRequest() +// Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - static const F32 FETCHING_TIMEOUT = 120.f;//seconds - - LLMutexLock lock(&mWorkMutex); + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 + static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; + + LLMutexLock lock(&mWorkMutex); // +Mw if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) { @@ -898,15 +1118,20 @@ bool LLTextureFetchWorker::doWork(S32 param) mLoadedDiscard = -1; mDecodedDiscard = -1; mRequestedSize = 0; + mRequestedOffset = 0; mFileSize = 0; mCachedSize = 0; mLoaded = FALSE; mSentRequest = UNSENT; mDecoded = FALSE; mWritten = FALSE; - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); @@ -939,22 +1164,24 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it // read file from local disk + ++mCacheReadCount; std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, offset, size, responder); mCacheReadTimer.reset(); } - else if (mUrl.empty()) + else if (mUrl.empty() && mFetcher->canLoadFromCache()) { setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + ++mCacheReadCount; CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); mCacheReadTimer.reset(); } - else if(mCanUseHTTP) + else if(!mUrl.empty() && mCanUseHTTP) { if (!(mUrl.compare(0, 7, "http://") == 0)) { @@ -962,7 +1189,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llwarns << "Unknown URL Type: " << mUrl << llendl; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; } else { @@ -982,6 +1209,9 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { + // + //This should never happen + // return false; } } @@ -1021,6 +1251,7 @@ bool LLTextureFetchWorker::doWork(S32 param) LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; mState = LOAD_FROM_NETWORK; } + // fall through LLTextureFetch::sCacheHitRate.addValue(0.f); } @@ -1061,7 +1292,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = LLTextureFetchWorker::SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1078,13 +1309,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureStart(false); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; @@ -1095,12 +1320,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); - //if (! mMetricsStartTime) - //{ - // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - //} - //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false, - // LLImageBase::TYPE_AVATAR_BAKE == mType); + //recordTextureStart(false); //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1125,196 +1345,250 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; mWriteToCacheState = SHOULD_WRITE; - - if (mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType, - LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); - mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureDone(false); } else { mFetcher->addToNetworkQueue(this); // failsafe - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + recordTextureStart(false); } return false; } + if (mState == WAIT_HTTP_RESOURCE) + { + // NOTE: + // control the number of the http requests issued for: + // 1, not openning too many file descriptors at the same time; + // 2, control the traffic of http so udp gets bandwidth. + // + // If it looks like we're busy, keep this request here. + // Otherwise, advance into the HTTP states. + if (mFetcher->getHttpWaitersCount() || ! acquireHttpSemaphore()) + { + mState = WAIT_HTTP_RESOURCE2; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + ++mResourceWaitCount; + return false; + } + + mState = SEND_HTTP_REQ; + // *NOTE: You must invoke releaseHttpSemaphore() if you transition + // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort + // the request. + } + + if (mState == WAIT_HTTP_RESOURCE2) + { + // Just idle it if we make it to the head... + return false; + } + if (mState == SEND_HTTP_REQ) { - if(mCanUseHTTP) + if (! mCanUseHTTP) { - //NOTE: - //control the number of the http requests issued for: - //1, not openning too many file descriptors at the same time; - //2, control the traffic of http so udp gets bandwidth. - // - static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8 ; - if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) - { - return false ; //wait. - } + releaseHttpSemaphore(); + return true; // abort + } - mFetcher->removeFromNetworkQueue(this, false); + mFetcher->removeFromNetworkQueue(this, false); - S32 cur_size = 0; - if (mFormattedImage.notNull()) + S32 cur_size = 0; + if (mFormattedImage.notNull()) + { + cur_size = mFormattedImage->getDataSize(); // amount of data we already have + if (mFormattedImage->getDiscardLevel() == 0) { - cur_size = mFormattedImage->getDataSize(); // amount of data we already have - if (mFormattedImage->getDiscardLevel() == 0) + if (cur_size > 0) { - if(cur_size > 0) - { - // We already have all the data, just decode it - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - mState = DECODE_IMAGE; - return false; - } - else - { - return true ; //abort. - } + // We already have all the data, just decode it + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = DECODE_IMAGE; + releaseHttpSemaphore(); + return false; + } + else + { + releaseHttpSemaphore(); + return true; // abort. } } - mRequestedSize = mDesiredSize; - mRequestedDiscard = mDesiredDiscard; - mRequestedSize -= cur_size; - S32 offset = cur_size; - mBufferSize = cur_size; // This will get modified by callbackHttpGet() - - bool res = false; - if (!mUrl.empty()) - { - mRequestedTimer.reset(); - - mLoaded = FALSE; - mGetStatus = 0; - mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - mFetcher->addToHTTPQueue(mID); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == mType); - - // Will call callbackHttpGet when curl request completes - std::vector headers; - headers.push_back("Accept: image/x-j2c"); - res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, - new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); - } - if (!res) - { - llwarns << "HTTP GET request failed for " << mID << llendl; - resetFormattedData(); - ++mHTTPFailCount; - return true; // failed - } - // fall through } - else //can not use http fetch. + mRequestedSize = mDesiredSize; + mRequestedDiscard = mDesiredDiscard; + mRequestedSize -= cur_size; + mRequestedOffset = cur_size; + if (mRequestedOffset) { - return true ; //abort + // Texture fetching often issues 'speculative' loads that + // start beyond the end of the actual asset. Some cache/web + // systems, e.g. Varnish, will respond to this not with a + // 416 but with a 200 and the entire asset in the response + // body. By ensuring that we always have a partially + // satisfiable Range request, we avoid that hit to the network. + // We just have to deal with the overlapping data which is made + // somewhat harder by the fact that grid services don't necessarily + // return the Content-Range header on 206 responses. *Sigh* + mRequestedOffset -= 1; + mRequestedSize += 1; } + + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; + if (!mUrl.empty()) + { + mRequestedTimer.reset(); + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); + } + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + { + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + releaseHttpSemaphore(); + return true; // failed + } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mState = WAIT_HTTP_REQ; + + // fall through } if (mState == WAIT_HTTP_REQ) { + // *NOTE: As stated above, all transitions out of this state should + // call releaseHttpSemaphore(). if (mLoaded) { S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { - S32 max_attempts; - if (mGetStatus == HTTP_NOT_FOUND) + if (http_not_found == mGetStatus) { - mHTTPFailCount = max_attempts = 1; // Don't retry + if(mWriteToCacheState == NOT_WRITE) //map tiles + { + mState = DONE; + releaseHttpSemaphore(); + return true; // failed, means no map tile on the empty region. + } + llwarns << "Texture missing from server (404): " << mUrl << llendl; - //roll back to try UDP - if(mCanUseNET) + // roll back to try UDP + if (mCanUseNET) { - mState = INIT ; - mCanUseHTTP = false ; + mState = INIT; + mCanUseHTTP = false; + mUrl.clear(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - return false ; + releaseHttpSemaphore(); + return false; } } - else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE) + else if (http_service_unavail == mGetStatus) { - // *TODO: Should probably introduce a timer here to delay future HTTP requsts - // for a short time (~1s) to ease server load? Ideally the server would queue - // requests instead of returning 503... we already limit the number pending. - ++mHTTPFailCount; - max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } + else if (http_not_sat == mGetStatus) + { + // Allowed, we'll accept whatever data we have as complete. + mHaveAllData = TRUE; + } else { - const S32 HTTP_MAX_RETRY_COUNT = 3; - max_attempts = HTTP_MAX_RETRY_COUNT + 1; - ++mHTTPFailCount; llinfos << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" - << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; + << " Status: " << mGetStatus.toHex() + << " Reason: '" << mGetReason << "'" + << llendl; } - if (mHTTPFailCount >= max_attempts) + mUrl.clear(); + if (cur_size > 0) { - if (cur_size > 0) - { - // Use available data - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - mState = DECODE_IMAGE; - return false; - } - else - { - resetFormattedData(); - mState = DONE; - return true; // failed - } - } - else - { - mState = SEND_HTTP_REQ; - return false; // retry + // Use available data + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = DECODE_IMAGE; + releaseHttpSemaphore(); + return false; } + + // Fail harder + resetFormattedData(); + mState = DONE; + releaseHttpSemaphore(); + return true; // failed } - llassert_always(mBufferSize == cur_size + mRequestedSize); - if(!mBufferSize)//no data received. + // Clear the url since we're done with the fetch + // Note: mUrl is used to check is fetching is required so failure to clear it will force an http fetch + // next time the texture is requested, even if the data have already been fetched. + if(mWriteToCacheState != NOT_WRITE) { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; + mUrl.clear(); + } + + if (! mHttpBufferArray || ! mHttpBufferArray->size()) + { + // no data received. + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } - //abort. + // abort. mState = DONE; + releaseHttpSemaphore(); return true; } + S32 append_size(mHttpBufferArray->size()); + S32 total_size(cur_size + append_size); + S32 src_offset(0); + llassert_always(append_size == mRequestedSize); + if (mHttpReplyOffset && mHttpReplyOffset != cur_size) + { + // In case of a partial response, our offset may + // not be trivially contiguous with the data we have. + // Get back into alignment. + if (mHttpReplyOffset > cur_size) + { + LL_WARNS("Texture") << "Partial HTTP response produces break in image data for texture " + << mID << ". Aborting load." << LL_ENDL; + mState = DONE; + releaseHttpSemaphore(); + return true; + } + src_offset = cur_size - mHttpReplyOffset; + append_size -= src_offset; + total_size -= src_offset; + mRequestedSize -= src_offset; // Make requested values reflect useful part + mRequestedOffset += src_offset; + } + if (mFormattedImage.isNull()) { // For now, create formatted image based on extension @@ -1326,44 +1600,49 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. + if (mHaveAllData) //the image file is fully loaded. { - mFileSize = mBufferSize; + mFileSize = total_size; } else //the file size is unknown. { - mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded. + mFileSize = total_size + 1 ; //flag the file is not fully loaded. } - U8* buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mBufferSize); + U8 * buffer = (U8 *) ALLOCATE_MEM(LLImageBase::getPrivatePool(), total_size); if (cur_size > 0) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append + mHttpBufferArray->read(src_offset, (char *) buffer + cur_size, append_size); + // NOTE: setData releases current data and owns new data (buffer) - mFormattedImage->setData(buffer, mBufferSize); - // delete temp data - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); // Note: not 'buffer' (assigned in setData()) - mBuffer = NULL; - mBufferSize = 0; + mFormattedImage->setData(buffer, total_size); + + // Done with buffer array + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + mHttpReplySize = 0; + mHttpReplyOffset = 0; + mLoadedDiscard = mRequestedDiscard; mState = DECODE_IMAGE; - if(mWriteToCacheState != NOT_WRITE) + if (mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = SHOULD_WRITE ; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } else { - if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) - { - //timeout, abort. - mState = DONE; - return true; - } + // *HISTORY: There was a texture timeout test here originally that + // would cancel a request that was over 120 seconds old. That's + // probably not a good idea. Particularly rich regions can take + // an enormous amount of time to load textures. We'll revisit the + // various possible timeout components (total request time, connection + // time, I/O time, with and without retries, etc.) in the future. setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; @@ -1373,11 +1652,12 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DECODE_IMAGE) { static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - if(textures_decode_disabled) + + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + if (textures_decode_disabled) { // for debug use, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } @@ -1385,7 +1665,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We aborted, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } @@ -1395,7 +1674,6 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } if (mLoadedDiscard < 0) @@ -1404,10 +1682,9 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + mRawImage = NULL; mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); @@ -1493,10 +1770,11 @@ bool LLTextureFetchWorker::doWork(S32 param) U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; + ++mCacheWriteCount; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, - mFileSize, responder); + mFileSize, mRawImage, mDecodedDiscard, responder); // fall through } @@ -1537,9 +1815,84 @@ bool LLTextureFetchWorker::doWork(S32 param) } return false; -} +} // -Mw -// Called from MAIN thread +// Threads: Ttf +// virtual +void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + static LLCachedControl log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog"); + static LLCachedControl log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); + static LLCachedControl log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + + LLMutexLock lock(&mWorkMutex); // +Mw + + mHttpActive = false; + + if (log_to_viewer_log || log_to_sim) + { + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + + bool success = true; + bool partial = false; + LLCore::HttpStatus status(response->getStatus()); + + lldebugs << "HTTP COMPLETE: " << mID + << " status: " << status.toHex() + << " '" << status.toString() << "'" + << llendl; +// unsigned int offset(0), length(0), full_length(0); +// response->getRange(&offset, &length, &full_length); +// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle +// << " status: " << status.toULong() << " '" << status.toString() << "'" +// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize +// << " offset: " << offset << " length: " << length +// << llendl; + + if (! status) + { + success = false; + std::string reason(status.toString()); + setGetStatus(status, reason); + llwarns << "CURL GET FAILED, status: " << status.toHex() + << " reason: " << reason << llendl; + } + else + { + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + partial = (par_status == status); + } + + S32 data_size = callbackHttpGet(response, partial, success); + + if (log_texture_traffic && data_size > 0) + { + LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID); + if (tex) + { + gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; + } + } + + mFetcher->removeFromHTTPQueue(mID, data_size); + + recordTextureDone(true); +} // -Mw + + +// Threads: Tmain void LLTextureFetchWorker::endWork(S32 param, bool aborted) { if (mDecodeHandle != 0) @@ -1552,6 +1905,8 @@ void LLTextureFetchWorker::endWork(S32 param, bool aborted) ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf + // virtual void LLTextureFetchWorker::finishWork(S32 param, bool completed) { @@ -1568,10 +1923,37 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed) } } +// LLQueuedThread's update() method is asking if it's okay to +// delete this worker. You'll notice we're not locking in here +// which is a slight concern. Caller is expected to have made +// this request 'quiet' by whatever means... +// +// Threads: Tmain + // virtual bool LLTextureFetchWorker::deleteOK() { bool delete_ok = true; + + if (mHttpActive) + { + // HTTP library has a pointer to this worker + // and will dereference it to do notification. + delete_ok = false; + } + + if (WAIT_HTTP_RESOURCE2 == mState) + { + if (mFetcher->isHttpWaiter(mID)) + { + // Don't delete the worker out from under the releaseHttpWaiters() + // method. Keep the pointers valid, clean up after that method + // has recognized the cancelation and removed the UUID from the + // waiter list. + delete_ok = false; + } + } + // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) { @@ -1606,6 +1988,7 @@ bool LLTextureFetchWorker::deleteOK() return delete_ok; } +// Threads: Ttf void LLTextureFetchWorker::removeFromCache() { if (!mInLocalCache) @@ -1617,6 +2000,8 @@ void LLTextureFetchWorker::removeFromCache() ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf +// Locks: Mw bool LLTextureFetchWorker::processSimulatorPackets() { if (mFormattedImage.isNull() || mRequestedSize < 0) @@ -1677,14 +2062,13 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// -S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +// Threads: Ttf +// Locks: Mw +S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success) { S32 data_size = 0 ; - LLMutexLock lock(&mWorkMutex); - if (mState != WAIT_HTTP_REQ) { llwarns << "callbackHttpGet for unrequested fetch worker: " << mID @@ -1699,27 +2083,68 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, if (success) { // get length of stream: - data_size = buffer->countAfter(channels.in(), NULL); - + LLCore::BufferArray * body(response->getBody()); + data_size = body ? body->size() : 0; + LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; if (data_size > 0) { // *TODO: set the formatted image data here directly to avoid the copy - mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, mBuffer, data_size); - mBufferSize += data_size; - if (data_size < mRequestedSize && mRequestedDiscard == 0) + + // Hold on to body for later copy + llassert_always(NULL == mHttpBufferArray); + body->addRef(); + mHttpBufferArray = body; + + if (partial) + { + unsigned int offset(0), length(0), full_length(0); + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for... + mHttpReplySize = data_size; + mHttpReplyOffset = mRequestedOffset; + } + else + { + mHttpReplySize = length; + mHttpReplyOffset = offset; + } + } + + if (! partial) + { + // Response indicates this is the entire asset regardless + // of our asking for a byte range. Mark it so and drop + // any partial data we might have so that the current + // response body becomes the entire dataset. + if (data_size <= mRequestedOffset) + { + LL_WARNS("Texture") << "Fetched entire texture " << mID + << " when it was expected to be marked complete. mImageSize: " + << mFileSize << " datasize: " << mFormattedImage->getDataSize() + << LL_ENDL; + } + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + else if (data_size < mRequestedSize) { mHaveAllData = TRUE; } else if (data_size > mRequestedSize) { - // *TODO: This shouldn't be happening any more + // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; mHaveAllData = TRUE; llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had - mBufferSize = data_size; } } else @@ -1734,6 +2159,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, { mRequestedSize = -1; // error } + mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); @@ -1742,10 +2168,11 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttc void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != LOAD_FROM_TEXTURE_CACHE) { // llwarns << "Read callback for " << mID << " with state = " << mState << llendl; @@ -1765,11 +2192,12 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw +// Threads: Ttc void LLTextureFetchWorker::callbackCacheWrite(bool success) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != WAIT_ON_WRITE) { // llwarns << "Write callback for " << mID << " with state = " << mState << llendl; @@ -1777,13 +2205,14 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) } mWritten = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Tid void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mDecodeHandle == 0) { return; // aborted, ignore @@ -1816,10 +2245,11 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag // llinfos << mID << " : DECODE COMPLETE " << llendl; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mCacheReadTime = mCacheReadTimer.getElapsedTimeF32(); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf bool LLTextureFetchWorker::writeToCacheComplete() { // Complete write to cache @@ -1842,6 +2272,36 @@ bool LLTextureFetchWorker::writeToCacheComplete() } +// Threads: Ttf +void LLTextureFetchWorker::recordTextureStart(bool is_http) +{ + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + +// Threads: Ttf +void LLTextureFetchWorker::recordTextureDone(bool is_http) +{ + if (mMetricsStartTime) + { + LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType, + LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); + mMetricsStartTime = 0; + } + LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // public @@ -1859,12 +2319,21 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTextureBandwidth(0), mHTTPTextureBits(0), mTotalHTTPRequests(0), - mCurlGetRequest(NULL), mQAMode(qa_mode), + mHttpRequest(NULL), + mHttpOptions(NULL), + mHttpHeaders(NULL), + mHttpMetricsHeaders(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), + mTotalCacheReadCount(0U), + mTotalCacheWriteCount(0U), + mTotalResourceWaitCount(0U), mFetchDebugger(NULL), + mFetchSource(LLTextureFetch::FROM_ALL), + mOriginFetchSource(LLTextureFetch::FROM_ALL), mFetcherLocked(FALSE) { - mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -1872,12 +2341,27 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image if(LLTextureFetchDebugger::isEnabled()) { mFetchDebugger = new LLTextureFetchDebugger(this, cache, imagedecodethread) ; + mFetchSource = (e_tex_source)gSavedSettings.getS32("TextureFetchSource"); + if(mFetchSource < 0 && mFetchSource >= INVALID_SOURCE) + { + mFetchSource = LLTextureFetch::FROM_ALL; + gSavedSettings.setS32("TextureFetchSource", 0); + } + mOriginFetchSource = mFetchSource; } + + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); } LLTextureFetch::~LLTextureFetch() { - clearDeleteList() ; + clearDeleteList(); while (! mCommands.empty()) { @@ -1885,10 +2369,34 @@ LLTextureFetch::~LLTextureFetch() mCommands.erase(mCommands.begin()); delete req; } + + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + + if (mHttpMetricsHeaders) + { + mHttpMetricsHeaders->release(); + mHttpMetricsHeaders = NULL; + } + + mHttpWaitResource.clear(); - // ~LLQueuedThread() called here + delete mHttpRequest; + mHttpRequest = NULL; delete mFetchDebugger; + mFetchDebugger = NULL; + + // ~LLQueuedThread() called here } bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, @@ -1941,6 +2449,8 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con } else { + // If the requester knows nothing about the file, we fetch the smallest + // amount of data at the lowest resolution (highest discard level) possible. desired_size = TEXTURE_CACHE_ENTRY_SIZE; desired_discard = MAX_DISCARD_LEVEL; } @@ -1952,7 +2462,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con { return false; // need to wait for previous aborted request to complete } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setImagePriority(priority); @@ -1961,41 +2471,44 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con if (!worker->haveWork()) { worker->mState = LLTextureFetchWorker::INIT; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); } else { - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else { worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); - lockQueue() ; + lockQueue(); // +Mfq mRequestMap[id] = worker; - unlockQueue() ; + unlockQueue(); // -Mfq - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setCanUseHTTP(can_use_http) ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } // llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; return true; } + +// Threads: T* (but Ttf in practice) + // protected void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { - lockQueue() ; + lockQueue(); // +Mfq bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ; - unlockQueue() ; + unlockQueue(); // -Mfq - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq if (in_request_map) { // only add to the queue if in the request map @@ -2007,44 +2520,52 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { iter1->second.erase(worker->mID); } -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq size_t erased = mNetworkQueue.erase(worker->mID); if (cancel && erased > 0) { mCancelQueue[worker->mHost].insert(worker->mID); } -} +} // -Mfnq +// Threads: T* +// // protected void LLTextureFetch::addToHTTPQueue(const LLUUID& id) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq mHTTPTextureQueue.insert(id); mTotalHTTPRequests++; -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq mHTTPTextureQueue.erase(id); mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits -} +} // -Mfnq +// NB: If you change deleteRequest() you should probably make +// parallel changes in removeRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { - lockQueue() ; + lockQueue(); // +Mfq LLTextureFetchWorker* worker = getWorkerAfterLock(id); if (worker) { size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; - removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; @@ -2052,15 +2573,25 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) } else { - unlockQueue() ; + unlockQueue(); // -Mfq } } +// NB: If you change removeRequest() you should probably make +// parallel changes in deleteRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { - lockQueue() ; + if(!worker) + { + return; + } + + lockQueue(); // +Mfq size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; removeFromNetworkQueue(worker, cancel); @@ -2069,34 +2600,57 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) worker->scheduleDelete(); } +void LLTextureFetch::deleteAllRequests() +{ + while(1) + { + lockQueue(); + if(mRequestMap.empty()) + { + unlockQueue() ; + break; + } + + LLTextureFetchWorker* worker = mRequestMap.begin()->second; + unlockQueue() ; + + removeRequest(worker, true); + } +} + +// Threads: T* S32 LLTextureFetch::getNumRequests() { - lockQueue() ; + lockQueue(); // +Mfq S32 size = (S32)mRequestMap.size(); - unlockQueue() ; + unlockQueue(); // -Mfq - return size ; + return size; } +// Threads: T* S32 LLTextureFetch::getNumHTTPRequests() { - mNetworkQueueMutex.lock() ; + mNetworkQueueMutex.lock(); // +Mfq S32 size = (S32)mHTTPTextureQueue.size(); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfq - return size ; + return size; } +// Threads: T* U32 LLTextureFetch::getTotalNumHTTPRequests() { - mNetworkQueueMutex.lock() ; - U32 size = mTotalHTTPRequests ; - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.lock(); // +Mfq + U32 size = mTotalHTTPRequests; + mNetworkQueueMutex.unlock(); // -Mfq - return size ; + return size; } // call lockQueue() first! +// Threads: T* +// Locks: Mfq LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) { LLTextureFetchWorker* res = NULL; @@ -2108,14 +2662,16 @@ LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) return res; } +// Threads: T* LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) { - LLMutexLock lock(&mQueueMutex) ; + LLMutexLock lock(&mQueueMutex); // +Mfq - return getWorkerAfterLock(id) ; -} + return getWorkerAfterLock(id); +} // -Mfq +// Threads: T* bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer& raw, LLPointer& aux) { @@ -2138,7 +2694,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else if (worker->checkWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw discard_level = worker->mDecodedDiscard; raw = worker->mRawImage; aux = worker->mAuxImage; @@ -2149,11 +2705,11 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } res = true; LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } else { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw if ((worker->mDecodedDiscard >= 0) && (worker->mDecodedDiscard < discard_level || discard_level < 0) && (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) @@ -2163,7 +2719,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, raw = worker->mRawImage; aux = worker->mAuxImage; } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else @@ -2173,15 +2729,16 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, return res; } +// Threads: T* bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { bool res = false; LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->setImagePriority(priority); - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw res = true; } return res; @@ -2196,24 +2753,24 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) // in step, at least until this can be refactored and // the redundancy eliminated. // -// May be called from any thread +// Threads: T* //virtual S32 LLTextureFetch::getPending() { S32 res; - lockData(); + lockData(); // +Ct { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq res = mRequestQueue.size(); - res += mCurlPOSTRequestCount; res += mCommands.size(); - } - unlockData(); + } // -Mfq + unlockData(); // -Ct return res; } +// Locks: Ct // virtual bool LLTextureFetch::runCondition() { @@ -2228,49 +2785,52 @@ bool LLTextureFetch::runCondition() bool have_no_commands(false); { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq have_no_commands = mCommands.empty(); - } - - bool have_no_curl_requests(0 == mCurlPOSTRequestCount); + } // -Mfq return ! (have_no_commands - && have_no_curl_requests && (mRequestQueue.empty() && mIdleThread)); // From base class } ////////////////////////////////////////////////////////////////////////////// -// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs) +// Threads: Ttf void LLTextureFetch::commonUpdate() { + // Release waiters + releaseHttpWaiters(); + // Run a cross-thread command, if any. cmdDoWork(); - // Update Curl on same thread as mCurlGetRequest was constructed - S32 processed = mCurlGetRequest->process(); - if (processed > 0) + // Deliver all completion notifications + LLCore::HttpStatus status = mHttpRequest->update(0); + if (! status) { - lldebugs << "processed: " << processed << " messages." << llendl; + LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: " + << status.toString() + << LL_ENDL; } } -// MAIN THREAD +// Threads: Tmain + //virtual S32 LLTextureFetch::update(F32 max_time_ms) { static LLCachedControl band_width(gSavedSettings,"ThrottleBandwidthKBPS"); { - mNetworkQueueMutex.lock() ; - mMaxBandwidth = band_width ; + mNetworkQueueMutex.lock(); // +Mfnq + mMaxBandwidth = band_width; - gTextureList.sTextureBits += mHTTPTextureBits ; - mHTTPTextureBits = 0 ; + gTextureList.sTextureBits += mHTTPTextureBits; + mHTTPTextureBits = 0; - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq } S32 res = LLWorkerThread::update(max_time_ms); @@ -2282,7 +2842,7 @@ S32 LLTextureFetch::update(F32 max_time_ms) // won't work so don't bother trying if (LLStartUp::getStartupState() > STATE_AGENT_SEND) { - sendRequestListToSimulators(); + sendRequestListToSimulators(); } } @@ -2291,10 +2851,17 @@ S32 LLTextureFetch::update(F32 max_time_ms) commonUpdate(); } + if (mFetchDebugger) + { + mFetchDebugger->tryToStopDebug(); //check if need to stop debugger. + } + return res; } -//called in the MAIN thread after the TextureCacheThread shuts down. +// called in the MAIN thread after the TextureCacheThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownTextureCacheThread() { if(mTextureCache) @@ -2304,7 +2871,9 @@ void LLTextureFetch::shutDownTextureCacheThread() } } -//called in the MAIN thread after the ImageDecodeThread shuts down. +// called in the MAIN thread after the ImageDecodeThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownImageDecodeThread() { if(mImageDecodeThread) @@ -2314,35 +2883,27 @@ void LLTextureFetch::shutDownImageDecodeThread() } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::startThread() { - // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlRequest(); - - if(mFetchDebugger) - { - mFetchDebugger->setCurlGetRequest(mCurlGetRequest); - } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::endThread() { - // Destroy mCurlGetRequest from Worker Thread - delete mCurlGetRequest; - mCurlGetRequest = NULL; - if(mFetchDebugger) - { - mFetchDebugger->setCurlGetRequest(NULL); - } + LL_INFOS("Texture") << "CacheReads: " << mTotalCacheReadCount + << ", CacheWrites: " << mTotalCacheWriteCount + << ", ResWaits: " << mTotalResourceWaitCount + << ", TotalHTTPReq: " << getTotalNumHTTPRequests() + << LL_ENDL; } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::threadedUpdate() { - llassert_always(mCurlGetRequest); - + llassert_always(mHttpRequest); + +#if 0 // Limit update frequency const F32 PROCESS_TIME = 0.05f; static LLFrameTimer process_timer; @@ -2351,9 +2912,10 @@ void LLTextureFetch::threadedUpdate() return; } process_timer.reset(); +#endif commonUpdate(); - + #if 0 const F32 INFO_TIME = 1.0f; static LLFrameTimer info_timer; @@ -2367,11 +2929,11 @@ void LLTextureFetch::threadedUpdate() } } #endif - } ////////////////////////////////////////////////////////////////////////////// +// Threads: Tmain void LLTextureFetch::sendRequestListToSimulators() { // All requests @@ -2397,48 +2959,48 @@ void LLTextureFetch::sendRequestListToSimulators() typedef std::map< LLHost, request_list_t > work_request_map_t; work_request_map_t requests; { - LLMutexLock lock2(&mNetworkQueueMutex); - for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) - { - queue_t::iterator curiter = iter++; - LLTextureFetchWorker* req = getWorker(*curiter); - if (!req) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) { - mNetworkQueue.erase(curiter); - continue; // paranoia - } - if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && - (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) - { - // We already received our URL, remove from the queue - llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; - mNetworkQueue.erase(curiter); - continue; - } - if (req->mID == mDebugID) - { - mDebugCount++; // for setting breakpoints - } - if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && - req->mTotalPackets > 0 && - req->mLastPacket >= req->mTotalPackets-1) - { - // We have all the packets... make sure this is high priority -// req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); - continue; - } - F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); - { - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + queue_t::iterator curiter = iter++; + LLTextureFetchWorker* req = getWorker(*curiter); + if (!req) { - requests[req->mHost].insert(req); + mNetworkQueue.erase(curiter); + continue; // paranoia + } + if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && + (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + { + // We already received our URL, remove from the queue + llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; + mNetworkQueue.erase(curiter); + continue; + } + if (req->mID == mDebugID) + { + mDebugCount++; // for setting breakpoints + } + if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && + req->mTotalPackets > 0 && + req->mLastPacket >= req->mTotalPackets-1) + { + // We have all the packets... make sure this is high priority +// req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); + continue; + } + F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); + { + F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); + if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || + (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || + (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + { + requests[req->mHost].insert(req); + } } } - } - } + } // -Mfnq for (work_request_map_t::iterator iter1 = requests.begin(); iter1 != requests.end(); ++iter1) @@ -2461,9 +3023,9 @@ void LLTextureFetch::sendRequestListToSimulators() if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) { // Initialize packet data based on data read from cache - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->setupPacketData(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw } if (0 == sim_request_count) { @@ -2492,12 +3054,12 @@ void LLTextureFetch::sendRequestListToSimulators() mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); } - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->mSentRequest = LLTextureFetchWorker::SENT_SIM; req->mSimRequestedDiscard = req->mDesiredDiscard; req->mRequestedPriority = req->mImagePriority; req->mRequestedTimer.reset(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw sim_request_count++; if (sim_request_count >= IMAGES_PER_REQUEST) { @@ -2518,55 +3080,57 @@ void LLTextureFetch::sendRequestListToSimulators() // Send cancelations { - LLMutexLock lock2(&mNetworkQueueMutex); - if (gMessageSystem && !mCancelQueue.empty()) - { - for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); - iter1 != mCancelQueue.end(); ++iter1) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + if (gMessageSystem && !mCancelQueue.empty()) { - LLHost host = iter1->first; - if (host == LLHost::invalid) + for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); + iter1 != mCancelQueue.end(); ++iter1) { - host = gAgent.getRegionHost(); - } - S32 request_count = 0; - for (queue_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) - { - if (0 == request_count) + LLHost host = iter1->first; + if (host == LLHost::invalid) { - gMessageSystem->newMessageFast(_PREHASH_RequestImage); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + host = gAgent.getRegionHost(); } - gMessageSystem->nextBlockFast(_PREHASH_RequestImage); - gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); - gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); - gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); - gMessageSystem->addU32Fast(_PREHASH_Packet, 0); - gMessageSystem->addU8Fast(_PREHASH_Type, 0); + S32 request_count = 0; + for (queue_t::iterator iter2 = iter1->second.begin(); + iter2 != iter1->second.end(); ++iter2) + { + if (0 == request_count) + { + gMessageSystem->newMessageFast(_PREHASH_RequestImage); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + gMessageSystem->nextBlockFast(_PREHASH_RequestImage); + gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); + gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); + gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); + gMessageSystem->addU32Fast(_PREHASH_Packet, 0); + gMessageSystem->addU8Fast(_PREHASH_Type, 0); // llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl; - request_count++; - if (request_count >= IMAGES_PER_REQUEST) + request_count++; + if (request_count >= IMAGES_PER_REQUEST) + { + gMessageSystem->sendSemiReliable(host, NULL, NULL); + request_count = 0; + } + } + if (request_count > 0 && request_count < IMAGES_PER_REQUEST) { gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; } } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { - gMessageSystem->sendSemiReliable(host, NULL, NULL); - } + mCancelQueue.clear(); } - mCancelQueue.clear(); - } - } + } // -Mfnq } ////////////////////////////////////////////////////////////////////////////// +// Threads: T* +// Locks: Mw bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) { mRequestedTimer.reset(); @@ -2599,6 +3163,7 @@ bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) return true; } +// Threads: T* bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data) { @@ -2633,14 +3198,14 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw // Copy header data into image object worker->mImageCodec = codec; @@ -2651,10 +3216,12 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 res = worker->insertPacket(0, data, data_size); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } + +// Threads: T* bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) { LLTextureFetchWorker* worker = getWorker(id); @@ -2679,14 +3246,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw res = worker->insertPacket(packet_num, data, data_size); @@ -2703,7 +3270,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 removeFromNetworkQueue(worker, true); // failsafe } - if(packet_num >= (worker->mTotalPackets - 1)) + if (packet_num >= (worker->mTotalPackets - 1)) { static LLCachedControl log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); static LLCachedControl log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); @@ -2715,12 +3282,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); } } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } ////////////////////////////////////////////////////////////////////////////// + +// Threads: T* BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) { BOOL from_cache = FALSE ; @@ -2728,14 +3297,15 @@ BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex() ; - from_cache = worker->mInLocalCache ; - worker->unlockWorkMutex() ; + worker->lockWorkMutex(); // +Mw + from_cache = worker->mInLocalCache; + worker->unlockWorkMutex(); // -Mw } return from_cache ; } +// Threads: T* S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) { @@ -2749,7 +3319,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r LLTextureFetchWorker* worker = getWorker(id); if (worker && worker->haveWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw state = worker->mState; fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); @@ -2776,7 +3346,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r } fetch_priority = worker->getPriority(); can_use_http = worker->getCanUseHTTP() ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } data_progress_p = data_progress; requested_priority_p = requested_priority; @@ -2800,12 +3370,219 @@ void LLTextureFetch::dump() << " STATE: " << worker->sStateDescs[worker->mState] << llendl; } + + llinfos << "LLTextureFetch ACTIVE_HTTP:" << llendl; + for (queue_t::const_iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } + + llinfos << "LLTextureFetch WAIT_HTTP_RESOURCE:" << llendl; + for (wait_http_res_queue_t::const_iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } +} + +////////////////////////////////////////////////////////////////////////////// + +// HTTP Resource Waiting Methods + +// Threads: Ttf +void LLTextureFetch::addHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.insert(tid); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: Ttf +void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + if (mHttpWaitResource.end() != iter) + { + mHttpWaitResource.erase(iter); + } + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +bool LLTextureFetch::isHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + const bool ret(mHttpWaitResource.end() != iter); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + +// Release as many requests as permitted from the WAIT_HTTP_RESOURCE2 +// state to the SEND_HTTP_REQ state based on their current priority. +// +// This data structures and code associated with this looks a bit +// indirect and naive but it's done in the name of safety. An +// ordered container may become invalid from time to time due to +// priority changes caused by actions in other threads. State itself +// could also suffer the same fate with canceled operations. Even +// done this way, I'm not fully trusting we're truly safe. This +// module is due for a major refactoring and we'll deal with it then. +// +// Threads: Ttf +// Locks: -Mw (must not hold any worker when called) +void LLTextureFetch::releaseHttpWaiters() +{ + // Use mHttpSemaphore rather than mHTTPTextureQueue.size() + // to avoid a lock. + if (mHttpSemaphore < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - HTTP_REQUESTS_IN_QUEUE_LOW_WATER)) + return; + + // Quickly make a copy of all the LLUIDs. Get off the + // mutex as early as possible. + typedef std::vector uuid_vec_t; + uuid_vec_t tids; + + { + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + + if (mHttpWaitResource.empty()) + return; + tids.reserve(mHttpWaitResource.size()); + tids.assign(mHttpWaitResource.begin(), mHttpWaitResource.end()); + } // -Mfnq + + // Now lookup the UUUIDs to find valid requests and sort + // them in priority order, highest to lowest. We're going + // to modify priority later as a side-effect of releasing + // these objects. That, in turn, would violate the partial + // ordering assumption of std::set, std::map, etc. so we + // don't use those containers. We use a vector and an explicit + // sort to keep the containers valid later. + typedef std::vector worker_list_t; + worker_list_t tids2; + + tids2.reserve(tids.size()); + for (uuid_vec_t::iterator iter(tids.begin()); + tids.end() != iter; + ++iter) + { + LLTextureFetchWorker * worker(getWorker(* iter)); + if (worker) + { + tids2.push_back(worker); + } + else + { + // If worker isn't found, this should be due to a request + // for deletion. We signal our recognition that this + // uuid shouldn't be used for resource waiting anymore by + // erasing it from the resource waiter list. That allows + // deleteOK to do final deletion on the worker. + removeHttpWaiter(* iter); + } + } + tids.clear(); + + // Sort into priority order, if necessary and only as much as needed + if (tids2.size() > mHttpSemaphore) + { + LLTextureFetchWorker::Compare compare; + std::partial_sort(tids2.begin(), tids2.begin() + mHttpSemaphore, tids2.end(), compare); + } + + // Release workers up to the high water mark. Since we aren't + // holding any locks at this point, we can be in competition + // with other callers. Do defensive things like getting + // refreshed counts of requests and checking if someone else + // has moved any worker state around.... + for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2; ++iter2) + { + LLTextureFetchWorker * worker(* iter2); + + worker->lockWorkMutex(); // +Mw + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) + { + // Not in expected state, remove it, try the next one + worker->unlockWorkMutex(); // -Mw + LL_WARNS("Texture") << "Resource-waited texture " << worker->mID + << " in unexpected state: " << worker->mState + << ". Removing from wait list." + << LL_ENDL; + removeHttpWaiter(worker->mID); + continue; + } + + if (! worker->acquireHttpSemaphore()) + { + // Out of active slots, quit + worker->unlockWorkMutex(); // -Mw + break; + } + + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->unlockWorkMutex(); // -Mw + + removeHttpWaiter(worker->mID); + } +} + +// Threads: T* +void LLTextureFetch::cancelHttpWaiters() +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.clear(); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +int LLTextureFetch::getHttpWaitersCount() +{ + mNetworkQueueMutex.lock(); // +Mfnq + int ret(mHttpWaitResource.size()); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + + +// Threads: T* +void LLTextureFetch::updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait) +{ + LLMutexLock lock(&mQueueMutex); // +Mfq + + mTotalCacheReadCount += cache_read; + mTotalCacheWriteCount += cache_write; + mTotalResourceWaitCount += res_wait; +} // -Mfq + + +// Threads: T* +void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait) +{ + U32 ret1(0U), ret2(0U), ret3(0U); + + { + LLMutexLock lock(&mQueueMutex); // +Mfq + ret1 = mTotalCacheReadCount; + ret2 = mTotalCacheWriteCount; + ret3 = mTotalResourceWaitCount; + } // -Mfq + + *cache_read = ret1; + *cache_write = ret2; + *res_wait = ret3; } ////////////////////////////////////////////////////////////////////////////// // cross-thread command methods +// Threads: T* void LLTextureFetch::commandSetRegion(U64 region_handle) { TFReqSetRegion * req = new TFReqSetRegion(region_handle); @@ -2813,6 +3590,7 @@ void LLTextureFetch::commandSetRegion(U64 region_handle) cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, @@ -2823,6 +3601,7 @@ void LLTextureFetch::commandSendMetrics(const std::string & caps_url, cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandDataBreak() { // The pedantically correct way to implement this is to create a command @@ -2833,30 +3612,33 @@ void LLTextureFetch::commandDataBreak() LLTextureFetch::svMetricsDataBreak = true; } +// Threads: T* void LLTextureFetch::cmdEnqueue(TFRequest * req) { - lockQueue(); + lockQueue(); // +Mfq mCommands.push_back(req); - unlockQueue(); + unlockQueue(); // -Mfq unpause(); } +// Threads: T* LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() { TFRequest * ret = 0; - lockQueue(); + lockQueue(); // +Mfq if (! mCommands.empty()) { ret = mCommands.front(); mCommands.erase(mCommands.begin()); } - unlockQueue(); + unlockQueue(); // -Mfq return ret; } +// Threads: Ttf void LLTextureFetch::cmdDoWork() { if (mDebugPause) @@ -2880,6 +3662,37 @@ void LLTextureFetch::cmdDoWork() namespace { + +// Example of a simple notification handler for metrics +// delivery notification. Earlier versions of the code used +// a Responder that tried harder to detect delivery breaks +// but it really isn't that important. If someone wants to +// revisit that effort, here is a place to start. +class AssetReportHandler : public LLCore::HttpHandler +{ +public: + + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) + { + LLCore::HttpStatus status(response->getStatus()); + + if (status) + { + LL_WARNS("Texture") << "Successfully delivered asset metrics to grid." + << LL_ENDL; + } + else + { + LL_WARNS("Texture") << "Error delivering asset metrics to grid. Reason: " + << status.toString() << LL_ENDL; + } + } +}; // end class AssetReportHandler + +AssetReportHandler stats_handler; + + /** * Implements the 'Set Region' command. * @@ -2910,75 +3723,8 @@ TFReqSendMetrics::~TFReqSendMetrics() bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { - /* - * HTTP POST responder. Doesn't do much but tries to - * detect simple breaks in recording the metrics stream. - * - * The 'volatile' modifiers don't indicate signals, - * mmap'd memory or threads, really. They indicate that - * the referenced data is part of a pseudo-closure for - * this responder rather than being required for correct - * operation. - * - * We don't try very hard with the POST request. We give - * it one shot and that's more-or-less it. With a proper - * refactoring of the LLQueuedThread usage, these POSTs - * could be put in a request object and made more reliable. - */ - class lcl_responder : public LLCurl::Responder - { - public: - lcl_responder(LLTextureFetch * fetcher, - S32 expected_sequence, - volatile const S32 & live_sequence, - volatile bool & reporting_break, - volatile bool & reporting_started) - : LLCurl::Responder(), - mFetcher(fetcher), - mExpectedSequence(expected_sequence), - mLiveSequence(live_sequence), - mReportingBreak(reporting_break), - mReportingStarted(reporting_started) - { - mFetcher->incrCurlPOSTCount(); - } - - ~lcl_responder() - { - LL_CHECK_MEMORY - mFetcher->decrCurlPOSTCount(); - LL_CHECK_MEMORY - } - - // virtual - void error(U32 status_num, const std::string & reason) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = true; - } - LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: " - << reason << LL_ENDL; - } - - // virtual - void result(const LLSD & content) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = false; - mReportingStarted = true; - } - } - - private: - LLTextureFetch * mFetcher; - S32 mExpectedSequence; - volatile const S32 & mLiveSequence; - volatile bool & mReportingBreak; - volatile bool & mReportingStarted; - - }; // class lcl_responder + static const U32 report_priority(1); + static LLCore::HttpHandler * const handler(fetcher->isQAMode() || true ? &stats_handler : NULL); if (! gViewerAssetStatsThread1) return true; @@ -3006,21 +3752,26 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // Update sequence number if (S32_MAX == ++report_sequence) report_sequence = 0; - + reporting_started = true; + // Limit the size of the stats report if necessary. merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd); if (! mCapsURL.empty()) { - LLCurlRequest::headers_t headers; - fetcher->getCurlRequest().post(mCapsURL, - headers, - merged_llsd, - new lcl_responder(fetcher, - report_sequence, - report_sequence, - LLTextureFetch::svMetricsDataBreak, - reporting_started)); + LLCore::BufferArray * ba = new LLCore::BufferArray; + LLCore::BufferArrayStream bas(ba); + LLSDSerialize::toXML(merged_llsd, bas); + + fetcher->getHttpRequest().requestPost(fetcher->getPolicyClass(), + report_priority, + mCapsURL, + ba, + NULL, + fetcher->getMetricsHeaders(), + handler); + ba->release(); + LLTextureFetch::svMetricsDataBreak = false; } else { @@ -3078,6 +3829,7 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics) } // end of anonymous namespace + /////////////////////////////////////////////////////////////////////////////////////////// //Start LLTextureFetchDebugger /////////////////////////////////////////////////////////////////////////////////////////// @@ -3131,47 +3883,13 @@ private: S32 mID; }; -class LLDebuggerHTTPResponder : public LLCurl::Responder -{ -public: - LLDebuggerHTTPResponder(LLTextureFetchDebugger* debugger, S32 index) - : mDebugger(debugger), mIndex(index) - { - } - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - if (!success) - { - llinfos << "Fetch Debugger : CURL GET FAILED, index = " << mIndex << ", status:" << status << " reason:" << reason << llendl; - } - mDebugger->callbackHTTP(mIndex, channels, buffer, partial, success); - } - virtual bool followRedir() - { - return true; - } -private: - LLTextureFetchDebugger* mDebugger; - S32 mIndex; -}; LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) : mFetcher(fetcher), mTextureCache(cache), mImageDecodeThread(imagedecodethread), - mCurlGetRequest(NULL) + mHttpHeaders(NULL), + mHttpPolicyClass(fetcher->getPolicyClass()) { init(); } @@ -3179,7 +3897,13 @@ LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextur LLTextureFetchDebugger::~LLTextureFetchDebugger() { mFetchingHistory.clear(); - stopDebug(); + mStopDebug = TRUE; + tryToStopDebug(); + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } } void LLTextureFetchDebugger::init() @@ -3194,6 +3918,8 @@ void LLTextureFetchDebugger::init() mTotalFetchingTime = 0.f; mRefetchVisCacheTime = -1.f; mRefetchVisHTTPTime = -1.f; + mRefetchAllCacheTime = -1.f; + mRefetchAllHTTPTime = -1.f; mNumFetchedTextures = 0; mNumCacheHits = 0; @@ -3207,10 +3933,62 @@ void LLTextureFetchDebugger::init() mRenderedDecodedData = 0; mFetchedPixels = 0; mRenderedPixels = 0; - mRefetchedData = 0; - mRefetchedPixels = 0; + mRefetchedVisData = 0; + mRefetchedVisPixels = 0; + mRefetchedAllData = 0; + mRefetchedAllPixels = 0; mFreezeHistory = FALSE; + mStopDebug = FALSE; + mClearHistory = FALSE; + + if (! mHttpHeaders) + { + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + } +} + +void LLTextureFetchDebugger::startWork(e_debug_state state) +{ + switch(state) + { + case IDLE: + break; + case START_DEBUG: + startDebug(); + break; + case READ_CACHE: + debugCacheRead(); + break; + case WRITE_CACHE: + debugCacheWrite(); + break; + case DECODING: + debugDecoder(); + break; + case HTTP_FETCHING: + debugHTTP(); + break; + case GL_TEX: + debugGLTextureCreation(); + break; + case REFETCH_VIS_CACHE: + debugRefetchVisibleFromCache(); + break; + case REFETCH_VIS_HTTP: + debugRefetchVisibleFromHTTP(); + break; + case REFETCH_ALL_CACHE: + debugRefetchAllFromCache(); + break; + case REFETCH_ALL_HTTP: + debugRefetchAllFromHTTP(); + break; + default: + break; + } + return; } void LLTextureFetchDebugger::startDebug() @@ -3218,10 +3996,18 @@ void LLTextureFetchDebugger::startDebug() //lock the fetcher mFetcher->lockFetcher(true); mFreezeHistory = TRUE; + mFetcher->resetLoadSource(); //clear the current fetching queue gTextureList.clearFetchingRequests(); + mState = START_DEBUG; +} + +bool LLTextureFetchDebugger::processStartDebug(F32 max_time) +{ + mTimer.reset(); + //wait for all works to be done while(1) { @@ -3233,6 +4019,11 @@ void LLTextureFetchDebugger::startDebug() { break; } + + if(mTimer.getElapsedTimeF32() > max_time) + { + return false; + } } //collect statistics @@ -3271,10 +4062,17 @@ void LLTextureFetchDebugger::startDebug() } mNumFetchedTextures = fetched_textures.size(); + + return true; } -void LLTextureFetchDebugger::stopDebug() +void LLTextureFetchDebugger::tryToStopDebug() { + if(!mStopDebug) + { + return; + } + //clear the current debug work S32 size = mFetchingHistory.size(); switch(mState) @@ -3286,7 +4084,7 @@ void LLTextureFetchDebugger::stopDebug() { mTextureCache->readComplete(mFetchingHistory[i].mCacheHandle, true); } - } + } break; case WRITE_CACHE: for(S32 i = 0 ; i < size; i++) @@ -3303,37 +4101,72 @@ void LLTextureFetchDebugger::stopDebug() break; case GL_TEX: break; + case REFETCH_VIS_CACHE: + break; + case REFETCH_VIS_HTTP: + break; + case REFETCH_ALL_CACHE: + mRefetchList.clear(); + break; + case REFETCH_ALL_HTTP: + mRefetchList.clear(); + break; default: break; } - while(1) + if(update(0.005f)) { - if(update()) + //unlock the fetcher + mFetcher->lockFetcher(false); + mFetcher->resetLoadSource(); + mFreezeHistory = FALSE; + mStopDebug = FALSE; + + if(mClearHistory) { - break; + mFetchingHistory.clear(); + mHandleToFetchIndex.clear(); + init(); + mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset } } - - //unlock the fetcher - mFetcher->lockFetcher(false); - mFreezeHistory = FALSE; - mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset } //called in the main thread and when the fetching queue is empty void LLTextureFetchDebugger::clearHistory() { - mFetchingHistory.clear(); - init(); + mClearHistory = TRUE; } void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker) { + if(worker->mRawImage.isNull() || worker->mFormattedImage.isNull()) + { + return; + } + if(mFreezeHistory) { - mRefetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); - mRefetchedData += worker->mFormattedImage->getDataSize(); + if(mState == REFETCH_VIS_CACHE || mState == REFETCH_VIS_HTTP) + { + mRefetchedVisPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); + mRefetchedVisData += worker->mFormattedImage->getDataSize(); + } + else + { + mRefetchedAllPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); + mRefetchedAllData += worker->mFormattedImage->getDataSize(); + + LLViewerFetchedTexture* tex = LLViewerTextureManager::findFetchedTexture(worker->mID); + if(tex && mRefetchList[tex].begin() != mRefetchList[tex].end()) + { + if(worker->mDecodedDiscard == mFetchingHistory[mRefetchList[tex][0]].mDecodedLevel) + { + mRefetchList[tex].erase(mRefetchList[tex].begin()); + } + } + } return; } @@ -3345,9 +4178,8 @@ void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker) mDecodedData += worker->mRawImage->getDataSize(); mFetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); - mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize())); - //mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mHaveAllData ? 0 : worker->mLoadedDiscard, worker->mFormattedImage->getComponents(), - //worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize())); + mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mDecodedDiscard, + worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize())); } void LLTextureFetchDebugger::lockCache() @@ -3364,6 +4196,7 @@ void LLTextureFetchDebugger::debugCacheRead() llassert_always(mState == IDLE); mTimer.reset(); mState = READ_CACHE; + mCacheReadTime = -1.f; S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3399,6 +4232,7 @@ void LLTextureFetchDebugger::debugCacheWrite() llassert_always(mState == IDLE); mTimer.reset(); mState = WRITE_CACHE; + mCacheWriteTime = -1.f; S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3408,7 +4242,7 @@ void LLTextureFetchDebugger::debugCacheWrite() mFetchingHistory[i].mCacheHandle = mTextureCache->writeToCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, mFetchingHistory[i].mFormattedImage->getData(), mFetchingHistory[i].mFetchedSize, mFetchingHistory[i].mDecodedLevel == 0 ? mFetchingHistory[i].mFetchedSize : mFetchingHistory[i].mFetchedSize + 1, - new LLDebuggerCacheWriteResponder(this, i)); + NULL, 0, new LLDebuggerCacheWriteResponder(this, i)); } } } @@ -3427,6 +4261,7 @@ void LLTextureFetchDebugger::debugDecoder() llassert_always(mState == IDLE); mTimer.reset(); mState = DECODING; + mDecodingTime = -1.f; S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3462,6 +4297,7 @@ void LLTextureFetchDebugger::debugHTTP() mTimer.reset(); mState = HTTP_FETCHING; + mHTTPTime = -1.f; S32 size = mFetchingHistory.size(); for (S32 i = 0 ; i < size ; i++) @@ -3469,6 +4305,7 @@ void LLTextureFetchDebugger::debugHTTP() mFetchingHistory[i].mCurlState = FetchEntry::CURL_NOT_DONE; mFetchingHistory[i].mCurlReceivedSize = 0; mFetchingHistory[i].mHTTPFailCount = 0; + mFetchingHistory[i].mFormattedImage = NULL; } mNbCurlRequests = 0; mNbCurlCompleted = 0; @@ -3478,30 +4315,51 @@ void LLTextureFetchDebugger::debugHTTP() S32 LLTextureFetchDebugger::fillCurlQueue() { - if (mNbCurlRequests == 24) + if(mStopDebug) //stop + { + mNbCurlCompleted = mFetchingHistory.size(); + return 0; + } + if (mNbCurlRequests > HTTP_REQUESTS_IN_QUEUE_LOW_WATER) + { return mNbCurlRequests; + } S32 size = mFetchingHistory.size(); for (S32 i = 0 ; i < size ; i++) { + mNbCurlRequests++; + if (mFetchingHistory[i].mCurlState != FetchEntry::CURL_NOT_DONE) + { continue; + } std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[i].mID.asString().c_str(); S32 requestedSize = mFetchingHistory[i].mRequestedSize; // We request the whole file if the size was not set. requestedSize = llmax(0,requestedSize); // We request the whole file if the size was set to an absurdly high value (meaning all file) requestedSize = (requestedSize == 33554432 ? 0 : requestedSize); - std::vector headers; - headers.push_back("Accept: image/x-j2c"); - bool res = mCurlGetRequest->getByteRange(texture_url, headers, 0, requestedSize, new LLDebuggerHTTPResponder(this, i)); - if (res) + + LLCore::HttpHandle handle = mFetcher->getHttpRequest().requestGetByteRange(mHttpPolicyClass, + LLWorkerThread::PRIORITY_LOWBITS, + texture_url, + 0, + requestedSize, + NULL, + mHttpHeaders, + this); + if (LLCORE_HTTP_HANDLE_INVALID != handle) { + mHandleToFetchIndex[handle] = i; + mFetchingHistory[i].mHttpHandle = handle; mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; mNbCurlRequests++; // Hack - if (mNbCurlRequests == 24) + if (mNbCurlRequests == HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) // emulate normal pipeline + { break; + } } else { @@ -3516,7 +4374,7 @@ void LLTextureFetchDebugger::debugGLTextureCreation() { llassert_always(mState == IDLE); mState = GL_TEX; - std::vector tex_list; + mTempTexList.clear(); S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3527,30 +4385,56 @@ void LLTextureFetchDebugger::debugGLTextureCreation() if(tex && !tex->isForSculptOnly()) { tex->destroyGLTexture() ; - tex_list.push_back(tex); + mTempTexList.push_back(tex); } } } - - mTimer.reset(); - S32 j = 0 ; - S32 size1 = tex_list.size(); - for(S32 i = 0 ; i < size && j < size1; i++) - { - if(mFetchingHistory[i].mRawImage.notNull()) - { - if(mFetchingHistory[i].mID == tex_list[j]->getID()) - { - tex_list[j]->createGLTexture(mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mRawImage, 0, TRUE, tex_list[j]->getBoostLevel()); - j++; - } - } - } - - mGLCreationTime = mTimer.getElapsedTimeF32() ; + + mGLCreationTime = -1.f; + mTempIndex = 0; + mHistoryListIndex = 0; + return; } +bool LLTextureFetchDebugger::processGLCreation(F32 max_time) +{ + mTimer.reset(); + + bool done = true; + S32 size = mFetchingHistory.size(); + S32 size1 = mTempTexList.size(); + for(; mHistoryListIndex < size && mTempIndex < size1; mHistoryListIndex++) + { + if(mFetchingHistory[mHistoryListIndex].mRawImage.notNull()) + { + if(mFetchingHistory[mHistoryListIndex].mID == mTempTexList[mTempIndex]->getID()) + { + mTempTexList[mTempIndex]->createGLTexture(mFetchingHistory[mHistoryListIndex].mDecodedLevel, + mFetchingHistory[mHistoryListIndex].mRawImage, 0, TRUE, mTempTexList[mTempIndex]->getBoostLevel()); + mTempIndex++; + } + } + + if(mTimer.getElapsedTimeF32() > max_time) + { + done = false; + break; + } + } + + if(mGLCreationTime < 0.f) + { + mGLCreationTime = mTimer.getElapsedTimeF32() ; + } + else + { + mGLCreationTime += mTimer.getElapsedTimeF32() ; + } + + return done; +} + //clear fetching results of all textures. void LLTextureFetchDebugger::clearTextures() { @@ -3565,15 +4449,62 @@ void LLTextureFetchDebugger::clearTextures() } } +void LLTextureFetchDebugger::makeRefetchList() +{ + mRefetchList.clear(); + S32 size = mFetchingHistory.size(); + for(S32 i = 0 ; i < size; i++) + { + LLViewerFetchedTexture* tex = LLViewerTextureManager::getFetchedTexture(mFetchingHistory[i].mID); + if(tex && tex->isJustBound()) //visible + { + continue; //the texture fetch pipeline will take care of visible textures. + } + + mRefetchList[tex].push_back(i); + } +} + +void LLTextureFetchDebugger::scanRefetchList() +{ + if(mStopDebug) + { + return; + } + if(!mRefetchNonVis) + { + return; + } + + for(std::map< LLPointer, std::vector >::iterator iter = mRefetchList.begin(); + iter != mRefetchList.end(); ) + { + if(iter->second.empty()) + { + gTextureList.setDebugFetching(iter->first, -1); + mRefetchList.erase(iter++); // This is the correct method to "erase and move on" in an std::map + } + else + { + gTextureList.setDebugFetching(iter->first, mFetchingHistory[iter->second[0]].mDecodedLevel); + ++iter; + } + } +} + void LLTextureFetchDebugger::debugRefetchVisibleFromCache() { llassert_always(mState == IDLE); mState = REFETCH_VIS_CACHE; clearTextures(); - + mFetcher->setLoadSource(LLTextureFetch::FROM_ALL); + mTimer.reset(); mFetcher->lockFetcher(false); + mRefetchVisCacheTime = -1.f; + mRefetchedVisData = 0; + mRefetchedVisPixels = 0; } void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP() @@ -3581,17 +4512,60 @@ void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP() llassert_always(mState == IDLE); mState = REFETCH_VIS_HTTP; - clearCache(); clearTextures(); + mFetcher->setLoadSource(LLTextureFetch::FROM_HTTP_ONLY); mTimer.reset(); mFetcher->lockFetcher(false); + mRefetchVisHTTPTime = -1.f; + mRefetchedVisData = 0; + mRefetchedVisPixels = 0; } -bool LLTextureFetchDebugger::update() +void LLTextureFetchDebugger::debugRefetchAllFromCache() +{ + llassert_always(mState == IDLE); + mState = REFETCH_ALL_CACHE; + + clearTextures(); + makeRefetchList(); + mFetcher->setLoadSource(LLTextureFetch::FROM_ALL); + + mTimer.reset(); + mFetcher->lockFetcher(false); + mRefetchAllCacheTime = -1.f; + mRefetchedAllData = 0; + mRefetchedAllPixels = 0; + mRefetchNonVis = FALSE; +} + +void LLTextureFetchDebugger::debugRefetchAllFromHTTP() +{ + llassert_always(mState == IDLE); + mState = REFETCH_ALL_HTTP; + + clearTextures(); + makeRefetchList(); + mFetcher->setLoadSource(LLTextureFetch::FROM_HTTP_ONLY); + + mTimer.reset(); + mFetcher->lockFetcher(false); + mRefetchAllHTTPTime = -1.f; + mRefetchedAllData = 0; + mRefetchedAllPixels = 0; + mRefetchNonVis = TRUE; +} + +bool LLTextureFetchDebugger::update(F32 max_time) { switch(mState) { + case START_DEBUG: + if(processStartDebug(max_time)) + { + mState = IDLE; + } + break; case READ_CACHE: if(!mTextureCache->update(1)) { @@ -3617,8 +4591,8 @@ bool LLTextureFetchDebugger::update() } break; case HTTP_FETCHING: - mCurlGetRequest->process(); - LLCurl::getCurlThread()->update(1); + // Do some notifications... + mFetcher->getHttpRequest().update(10); if (!fillCurlQueue() && mNbCurlCompleted == mFetchingHistory.size()) { mHTTPTime = mTimer.getElapsedTimeF32() ; @@ -3626,22 +4600,59 @@ bool LLTextureFetchDebugger::update() } break; case GL_TEX: - mState = IDLE; + if(processGLCreation(max_time)) + { + mState = IDLE; + mTempTexList.clear(); + } break; case REFETCH_VIS_CACHE: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisCacheTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime; + mRefetchVisCacheTime = mTimer.getElapsedTimeF32() ; mState = IDLE; mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); } break; case REFETCH_VIS_HTTP: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime; + mRefetchVisHTTPTime = mTimer.getElapsedTimeF32() ; mState = IDLE; mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); + } + break; + case REFETCH_ALL_CACHE: + scanRefetchList(); + if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) + { + if(!mRefetchNonVis) + { + mRefetchNonVis = TRUE; //start to fetch non-vis + scanRefetchList(); + break; + } + + mRefetchAllCacheTime = mTimer.getElapsedTimeF32() ; + mState = IDLE; + mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); + mRefetchList.clear(); + mRefetchNonVis = FALSE; + } + break; + case REFETCH_ALL_HTTP: + scanRefetchList(); + if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) + { + mRefetchAllHTTPTime = mTimer.getElapsedTimeF32() ; + mState = IDLE; + mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); + mRefetchList.clear(); + mRefetchNonVis = FALSE; } break; default: @@ -3652,6 +4663,28 @@ bool LLTextureFetchDebugger::update() return mState == IDLE; } +void LLTextureFetchDebugger::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + handle_fetch_map_t::iterator iter(mHandleToFetchIndex.find(handle)); + if (mHandleToFetchIndex.end() == iter) + { + llinfos << "Fetch Debugger : Couldn't find handle " << handle << " in fetch list." << llendl; + return; + } + + S32 fetch_ind(iter->second); + mHandleToFetchIndex.erase(iter); + if (fetch_ind >= mFetchingHistory.size() || mFetchingHistory[fetch_ind].mHttpHandle != handle) + { + llinfos << "Fetch Debugger : Handle and fetch object in disagreement. Punting." << llendl; + } + else + { + callbackHTTP(mFetchingHistory[fetch_ind], response); + mFetchingHistory[fetch_ind].mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; // Not valid after notification + } +} + void LLTextureFetchDebugger::callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { @@ -3678,50 +4711,60 @@ void LLTextureFetchDebugger::callbackDecoded(S32 id, bool success, LLImageRaw* r } } -void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpResponse * response) { + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + LLCore::HttpStatus status(response->getStatus()); mNbCurlRequests--; - if (success) + if (status) { - mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; + const bool partial(par_status == status); + LLCore::BufferArray * ba(response->getBody()); // *Not* holding reference to body + + fetch.mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; - S32 data_size = buffer->countAfter(channels.in(), NULL); - mFetchingHistory[id].mCurlReceivedSize += data_size; - //llinfos << "Fetch Debugger : got results for " << id << ", data_size = " << data_size << ", received = " << mFetchingHistory[id].mCurlReceivedSize << ", requested = " << mFetchingHistory[id].mRequestedSize << ", partial = " << partial << llendl; - if ((mFetchingHistory[id].mCurlReceivedSize >= mFetchingHistory[id].mRequestedSize) || !partial || (mFetchingHistory[id].mRequestedSize == 600)) + S32 data_size = ba ? ba->size() : 0; + fetch.mCurlReceivedSize += data_size; + //llinfos << "Fetch Debugger : got results for " << fetch.mID << ", data_size = " << data_size << ", received = " << fetch.mCurlReceivedSize << ", requested = " << fetch.mRequestedSize << ", partial = " << partial << llendl; + if ((fetch.mCurlReceivedSize >= fetch.mRequestedSize) || !partial || (fetch.mRequestedSize == 600)) { U8* d_buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, d_buffer, data_size); + if (ba) + { + ba->read(0, d_buffer, data_size); + } - llassert_always(mFetchingHistory[id].mFormattedImage.isNull()); + llassert_always(fetch.mFormattedImage.isNull()); { // For now, create formatted image based on extension - std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[id].mID.asString().c_str(); + std::string texture_url = mHTTPUrl + "/?texture_id=" + fetch.mID.asString().c_str(); std::string extension = gDirUtilp->getExtension(texture_url); - mFetchingHistory[id].mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); - if (mFetchingHistory[id].mFormattedImage.isNull()) + fetch.mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); + if (fetch.mFormattedImage.isNull()) { - mFetchingHistory[id].mFormattedImage = new LLImageJ2C; // default + fetch.mFormattedImage = new LLImageJ2C; // default } } - mFetchingHistory[id].mFormattedImage->setData(d_buffer, data_size); + fetch.mFormattedImage->setData(d_buffer, data_size); } } else //failed { - mFetchingHistory[id].mHTTPFailCount++; - if(mFetchingHistory[id].mHTTPFailCount < 5) + llinfos << "Fetch Debugger : CURL GET FAILED, ID = " << fetch.mID + << ", status: " << status.toHex() + << " reason: " << status.toString() << llendl; + fetch.mHTTPFailCount++; + if(fetch.mHTTPFailCount < 5) { // Fetch will have to be redone - mFetchingHistory[id].mCurlState = FetchEntry::CURL_NOT_DONE; + fetch.mCurlState = FetchEntry::CURL_NOT_DONE; } else //skip { - mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; + fetch.mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; } } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 107e1623b0..3a99432b48 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -27,19 +27,24 @@ #ifndef LL_LLTEXTUREFETCH_H #define LL_LLTEXTUREFETCH_H +#include +#include + #include "lldir.h" #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" -#include "llcurl.h" #include "lltextureinfo.h" #include "llapr.h" #include "llimageworker.h" -//#include "lltexturecache.h" +#include "llcurl.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "httphandler.h" class LLViewerTexture; class LLTextureFetchWorker; -class HTTPGetResponder; class LLImageDecodeThread; class LLHost; class LLViewerAssetStats; @@ -47,10 +52,10 @@ class LLTextureFetchDebugger; class LLTextureCache; // Interface class + class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; - friend class HTTPGetResponder; public: LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode); @@ -58,72 +63,201 @@ public: class TFRequest; - /*virtual*/ S32 update(F32 max_time_ms); - void shutDownTextureCacheThread() ; //called in the main thread after the TextureCacheThread shuts down. - void shutDownImageDecodeThread() ; //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + /*virtual*/ S32 update(F32 max_time_ms); + + // called in the main thread after the TextureCacheThread shuts down. + // Threads: Tmain + void shutDownTextureCacheThread(); + //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + void shutDownImageDecodeThread(); + + // Threads: T* (but Tmain mostly) bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); + + // Requests that a fetch operation be deleted from the queue. + // If @cancel is true, also stops any I/O operations pending. + // Actual delete will be scheduled and performed later. + // + // Note: This *looks* like an override/variant of the + // base class's deleteRequest() but is functionally quite + // different. + // + // Threads: T* void deleteRequest(const LLUUID& id, bool cancel); + + void deleteAllRequests(); + + // Threads: T* bool getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer& raw, LLPointer& aux); + + // Threads: T* bool updateRequestPriority(const LLUUID& id, F32 priority); + // Threads: T* bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); + + // Threads: T* bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); + // Threads: T* (but not safe) void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } + + // Threads: T* (but not safe) F32 getTextureBandwidth() { return mTextureBandwidth; } - // Debug + // Threads: T* BOOL isFromLocalCache(const LLUUID& id); + + // @return Magic number giving the internal state of the + // request. We should make these codes public if we're + // going to return them as a status value. + // + // Threads: T* S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); + + // Debug utility - generally not safe void dump(); - S32 getNumRequests() ; - S32 getNumHTTPRequests() ; - U32 getTotalNumHTTPRequests() ; + + // Threads: T* + S32 getNumRequests(); + + // Threads: T* + S32 getNumHTTPRequests(); + + // Threads: T* + U32 getTotalNumHTTPRequests(); - // Public for access by callbacks + // Threads: T* S32 getPending(); + + // Threads: T* void lockQueue() { mQueueMutex.lock(); } + + // Threads: T* void unlockQueue() { mQueueMutex.unlock(); } + + // Threads: T* LLTextureFetchWorker* getWorker(const LLUUID& id); + + // Threads: T* + // Locks: Mfq LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id); - LLTextureInfo* getTextureInfo() { return &mTextureInfo; } - // Commands available to other threads to control metrics gathering operations. + + // Threads: T* void commandSetRegion(U64 region_handle); + + // Threads: T* void commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, LLViewerAssetStats * main_stats); + + // Threads: T* void commandDataBreak(); - LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } + // Threads: T* + LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } + + // Threads: T* + LLCore::HttpRequest::policy_t getPolicyClass() const { return mHttpPolicyClass; } + + // Return a pointer to the shared metrics headers definition. + // Does not increment the reference count, caller is required + // to do that to hold a reference for any length of time. + // + // Threads: T* + LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } bool isQAMode() const { return mQAMode; } - // Curl POST counter maintenance - inline void incrCurlPOSTCount() { mCurlPOSTRequestCount++; } - inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } + // ---------------------------------- + // HTTP resource waiting methods + // Threads: T* + void addHttpWaiter(const LLUUID & tid); + + // Threads: T* + void removeHttpWaiter(const LLUUID & tid); + + // Threads: T* + bool isHttpWaiter(const LLUUID & tid); + + // If there are slots, release one or more LLTextureFetchWorker + // requests from resource wait state (WAIT_HTTP_RESOURCE) to + // active (SEND_HTTP_REQ). + // + // Because this will modify state of many workers, you may not + // hold any Mw lock while calling. This makes it a little + // inconvenient to use but that's the rule. + // + // Threads: T* + // Locks: -Mw (must not hold any worker when called) + void releaseHttpWaiters(); + + // Threads: T* + void cancelHttpWaiters(); + + // Threads: T* + int getHttpWaitersCount(); + // ---------------------------------- + // Stats management + + // Add given counts to the global totals for the states/requests + // Threads: T* + void updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait); + + // Return the global counts + // Threads: T* + void getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait); + + // ---------------------------------- + protected: + // Threads: T* (but Ttf in practice) void addToNetworkQueue(LLTextureFetchWorker* worker); - void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); - void addToHTTPQueue(const LLUUID& id); - void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); - void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // Threads: T* + void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); + + // Threads: T* + void addToHTTPQueue(const LLUUID& id); + + // XXX possible delete + // Threads: T* + void removeFromHTTPQueue(const LLUUID& id, S32 received_size); + + // Identical to @deleteRequest but with different arguments + // (caller already has the worker pointer). + // + // Threads: T* + void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // Overrides from the LLThread tree + // Locks: Ct bool runCondition(); private: + // Threads: Tmain void sendRequestListToSimulators(); + + // Threads: Ttf /*virtual*/ void startThread(void); + + // Threads: Ttf /*virtual*/ void endThread(void); + + // Threads: Ttf /*virtual*/ void threadedUpdate(void); + + // Threads: Ttf void commonUpdate(); // Metrics command helpers @@ -134,6 +268,8 @@ private: * Takes ownership of the TFRequest object. * * Method locks the command queue. + * + * Threads: T* */ void cmdEnqueue(TFRequest *); @@ -144,6 +280,8 @@ private: * Caller acquires ownership of the object and must dispose of it. * * Method locks the command queue. + * + * Threads: T* */ TFRequest * cmdDequeue(); @@ -153,6 +291,8 @@ private: * additional commands. * * Method locks the command queue. + * + * Threads: Ttf */ void cmdDoWork(); @@ -172,43 +312,64 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; // Map of all requests by UUID typedef std::map map_t; - map_t mRequestMap; + map_t mRequestMap; // Mfq // Set of requests that require network data typedef std::set queue_t; - queue_t mNetworkQueue; - queue_t mHTTPTextureQueue; + queue_t mNetworkQueue; // Mfnq + queue_t mHTTPTextureQueue; // Mfnq typedef std::map > cancel_queue_t; - cancel_queue_t mCancelQueue; - F32 mTextureBandwidth; - F32 mMaxBandwidth; + cancel_queue_t mCancelQueue; // Mfnq + F32 mTextureBandwidth; // + F32 mMaxBandwidth; // Mfnq LLTextureInfo mTextureInfo; - U32 mHTTPTextureBits; + // XXX possible delete + U32 mHTTPTextureBits; // Mfnq + // XXX possible delete //debug use - U32 mTotalHTTPRequests ; + U32 mTotalHTTPRequests; // Out-of-band cross-thread command queue. This command queue // is logically tied to LLQueuedThread's list of // QueuedRequest instances and so must be covered by the // same locks. typedef std::vector command_queue_t; - command_queue_t mCommands; + command_queue_t mCommands; // Mfq // If true, modifies some behaviors that help with QA tasks. const bool mQAMode; - // Count of POST requests outstanding. We maintain the count - // indirectly in the CURL request responder's ctor and dtor and - // use it when determining whether or not to sleep the thread. Can't - // use the LLCurl module's request counter as it isn't thread compatible. - // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. - LLAtomic32 mCurlPOSTRequestCount; + // Interfaces and objects into the core http library used + // to make our HTTP requests. These replace the various + // LLCurl interfaces used in the past. + LLCore::HttpRequest * mHttpRequest; // Ttf + LLCore::HttpOptions * mHttpOptions; // Ttf + LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf + LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* + + // We use a resource semaphore to keep HTTP requests in + // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the + // transport. This keeps them near where they can be cheaply + // reprioritized rather than dumping them all across a thread + // where it's more expensive to get at them. Requests in either + // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore + // and tracking state transitions is critical to liveness. + LLAtomicS32 mHttpSemaphore; // Ttf + Tmain + + typedef std::set wait_http_res_queue_t; + wait_http_res_queue_t mHttpWaitResource; // Mfnq + + // Cumulative stats on the states/requests issued by + // textures running through here. + U32 mTotalCacheReadCount; // Mfq + U32 mTotalCacheWriteCount; // Mfq + U32 mTotalResourceWaitCount; // Mfq public: // A probabilistically-correct indicator that the current @@ -216,19 +377,35 @@ public: // reporting due to either startup or a problem POSTing data. static volatile bool svMetricsDataBreak; +public: + //debug use + enum e_tex_source + { + FROM_ALL = 0, + FROM_HTTP_ONLY, + INVALID_SOURCE + }; private: //debug use LLTextureFetchDebugger* mFetchDebugger; bool mFetcherLocked; + + e_tex_source mFetchSource; + e_tex_source mOriginFetchSource; public: //debug use LLTextureFetchDebugger* getFetchDebugger() { return mFetchDebugger;} void lockFetcher(bool lock) { mFetcherLocked = lock;} + + void setLoadSource(e_tex_source source) {mFetchSource = source;} + void resetLoadSource() {mFetchSource = mOriginFetchSource;} + bool canLoadFromCache() { return mFetchSource != FROM_HTTP_ONLY;} }; //debug use -class LLTextureFetchDebugger +class LLViewerFetchedTexture; +class LLTextureFetchDebugger : public LLCore::HttpHandler { friend class LLTextureFetch; public: @@ -239,6 +416,7 @@ public: enum e_debug_state { IDLE = 0, + START_DEBUG, READ_CACHE, WRITE_CACHE, DECODING, @@ -272,11 +450,13 @@ private: e_curl_state mCurlState; S32 mCurlReceivedSize; S32 mHTTPFailCount; + LLCore::HttpHandle mHttpHandle; FetchEntry() : mDecodedLevel(-1), mFetchedSize(0), - mDecodedSize(0) + mDecodedSize(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) {} FetchEntry(LLUUID& id, S32 r_size, /*S32 f_discard, S32 c,*/ S32 level, S32 f_size, S32 d_size) : mID(id), @@ -285,10 +465,15 @@ private: mFetchedSize(f_size), mDecodedSize(d_size), mNeedsAux(false), - mHTTPFailCount(0) + mHTTPFailCount(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) {} }; - std::vector mFetchingHistory; + typedef std::vector fetch_list_t; + fetch_list_t mFetchingHistory; + + typedef std::map handle_fetch_map_t; + handle_fetch_map_t mHandleToFetchIndex; e_debug_state mState; @@ -301,13 +486,16 @@ private: F32 mTotalFetchingTime; F32 mRefetchVisCacheTime; F32 mRefetchVisHTTPTime; + F32 mRefetchAllCacheTime; + F32 mRefetchAllHTTPTime; LLTimer mTimer; LLTextureFetch* mFetcher; LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; + LLCore::HttpHeaders* mHttpHeaders; + LLCore::HttpRequest::policy_t mHttpPolicyClass; S32 mNumFetchedTextures; S32 mNumCacheHits; @@ -321,42 +509,44 @@ private: U32 mRenderedDecodedData; U32 mFetchedPixels; U32 mRenderedPixels; - U32 mRefetchedData; - U32 mRefetchedPixels; + U32 mRefetchedVisData; + U32 mRefetchedVisPixels; + U32 mRefetchedAllData; + U32 mRefetchedAllPixels; BOOL mFreezeHistory; + BOOL mStopDebug; + BOOL mClearHistory; + BOOL mRefetchNonVis; std::string mHTTPUrl; S32 mNbCurlRequests; S32 mNbCurlCompleted; + std::map< LLPointer, std::vector > mRefetchList; + std::vector< LLPointer > mTempTexList; + S32 mTempIndex; + S32 mHistoryListIndex; + public: - bool update(); //called in the main thread once per frame + bool update(F32 max_time); //called in the main thread once per frame //fetching history void clearHistory(); void addHistoryEntry(LLTextureFetchWorker* worker); - void setCurlGetRequest(LLCurlRequest* request) { mCurlGetRequest = request;} - - void startDebug(); - void stopDebug(); //stop everything - void debugCacheRead(); - void debugCacheWrite(); - void debugHTTP(); - void debugDecoder(); - void debugGLTextureCreation(); - void debugRefetchVisibleFromCache(); - void debugRefetchVisibleFromHTTP(); + // Inherited from LLCore::HttpHandler + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + void startWork(e_debug_state state); + void setStopDebug() {mStopDebug = TRUE;} + void tryToStopDebug(); //stop everything void callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); void callbackCacheWrite(S32 id, bool success); void callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux); - void callbackHTTP(S32 id, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); - + void callbackHTTP(FetchEntry & fetch, LLCore::HttpResponse * response); e_debug_state getState() {return mState;} S32 getNumFetchedTextures() {return mNumFetchedTextures;} @@ -372,8 +562,10 @@ public: U32 getRenderedDecodedData() {return mRenderedDecodedData;} U32 getFetchedPixels() {return mFetchedPixels;} U32 getRenderedPixels() {return mRenderedPixels;} - U32 getRefetchedData() {return mRefetchedData;} - U32 getRefetchedPixels() {return mRefetchedPixels;} + U32 getRefetchedVisData() {return mRefetchedVisData;} + U32 getRefetchedVisPixels() {return mRefetchedVisPixels;} + U32 getRefetchedAllData() {return mRefetchedAllData;} + U32 getRefetchedAllPixels() {return mRefetchedAllPixels;} F32 getCacheReadTime() {return mCacheReadTime;} F32 getCacheWriteTime() {return mCacheWriteTime;} @@ -383,11 +575,15 @@ public: F32 getTotalFetchingTime() {return mTotalFetchingTime;} F32 getRefetchVisCacheTime() {return mRefetchVisCacheTime;} F32 getRefetchVisHTTPTime() {return mRefetchVisHTTPTime;} + F32 getRefetchAllCacheTime() {return mRefetchAllCacheTime;} + F32 getRefetchAllHTTPTime() {return mRefetchAllHTTPTime;} private: void init(); void clearTextures();//clear fetching results of all textures. void clearCache(); + void makeRefetchList(); + void scanRefetchList(); void lockFetcher(); void unlockFetcher(); @@ -400,6 +596,20 @@ private: S32 fillCurlQueue(); + void startDebug(); + void debugCacheRead(); + void debugCacheWrite(); + void debugHTTP(); + void debugDecoder(); + void debugGLTextureCreation(); + void debugRefetchVisibleFromCache(); + void debugRefetchVisibleFromHTTP(); + void debugRefetchAllFromCache(); + void debugRefetchAllFromHTTP(); + + bool processStartDebug(F32 max_time); + bool processGLCreation(F32 max_time); + private: static bool sDebuggerEnabled; public: diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp old mode 100644 new mode 100755 index 52d085dd2c..16c42dbd43 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -74,7 +74,7 @@ static std::string title_string4(" W x H (Dis) Mem"); static S32 title_x1 = 0; static S32 title_x2 = 460; static S32 title_x3 = title_x2 + 40; -static S32 title_x4 = title_x3 + 50; +static S32 title_x4 = title_x3 + 46; static S32 texture_bar_height = 8; //////////////////////////////////////////////////////////////////////////// @@ -232,6 +232,8 @@ void LLTextureBar::draw() { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE2 { "REQ", LLColor4::yellow },// SEND_HTTP_REQ { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE @@ -239,7 +241,7 @@ void LLTextureBar::draw() { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 12 +#define LAST_STATE 14 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 @@ -345,7 +347,7 @@ void LLTextureBar::draw() // draw the image size at the end { - std::string num_str = llformat("%3dx%3d (%d) %7d", mImagep->getWidth(), mImagep->getHeight(), + std::string num_str = llformat("%3dx%3d (%2d) %7d", mImagep->getWidth(), mImagep->getHeight(), mImagep->getDiscardLevel(), mImagep->hasGLTexture() ? mImagep->getTextureMemory() : 0); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, title_x4, getRect().getHeight(), color, LLFontGL::LEFT, LLFontGL::TOP); @@ -514,14 +516,18 @@ void LLGLTexMemBar::draw() S32 v_offset = 0;//(S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f); F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024); F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024); - U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests() ; + U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests(); //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); LLColor4 color; - - std::string text = ""; + // Gray background using completely magic numbers + gGL.color4f(0.f, 0.f, 0.f, 0.25f); + // const LLRect & rect(getRect()); + // gl_rect_2d(-4, v_offset, rect.mRight - rect.mLeft + 2, v_offset + line_height*4); + + std::string text = ""; LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -531,14 +537,26 @@ void LLGLTexMemBar::draw() bound_mem, max_bound_mem, LLRenderTarget::sBytesAllocated/(1024*1024), - LLImageRaw::sGlobalRawMemory >> 20, discard_bias, - cache_usage, cache_max_usage); + LLImageRaw::sGlobalRawMemory >> 20, + discard_bias, + cache_usage, + cache_max_usage); + //, cache_entries, cache_max_entries + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, text_color, LLFontGL::LEFT, LLFontGL::TOP); - text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d", - total_texture_downloaded, total_object_downloaded, total_http_requests); - //, cache_entries, cache_max_entries + U32 cache_read(0U), cache_write(0U), res_wait(0U); + LLAppViewer::getTextureFetch()->getStateStats(&cache_read, &cache_write, &res_wait); + + text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d Cread: %u Cwrite: %u Rwait: %u", + total_texture_downloaded, + total_object_downloaded, + total_http_requests, + cache_read, + cache_write, + res_wait); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, text_color, LLFontGL::LEFT, LLFontGL::TOP); diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index 0eec7f0afd..9dfb29b905 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -118,7 +118,7 @@ LLToast::LLToast(const LLToast::Params& p) { mTimer.reset(new LLToastLifeTimer(this, p.lifetime_secs)); - buildFromFile("panel_toast.xml", NULL); + buildFromFile("panel_toast.xml"); setCanDrag(FALSE); diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index d629f3abac..602b924398 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -334,32 +334,24 @@ void LLToastNotifyPanel::updateButtonsLayout(const std::vectorgetRect()); - S32 buttons_per_row = max_width / BUTTON_WIDTH; //assume that h_pad far less than BUTTON_WIDTH - S32 ignore_btn_left = buttons_per_row * BUTTON_WIDTH + (buttons_per_row - 1) * h_pad - ignore_btn_rect.getWidth(); - if (ignore_btn_left + ignore_btn_rect.getWidth() > max_width)// make sure that the ignore button is in panel - { - ignore_btn_left = max_width - ignore_btn_rect.getWidth() - 2 * HPAD; - } + S32 ignore_btn_left = max_width - ignore_btn_rect.getWidth(); ignore_btn_rect.setOriginAndSize(ignore_btn_left, BOTTOM_PAD,// always move ignore button at the bottom ignore_btn_rect.getWidth(), ignore_btn_rect.getHeight()); ignore_btn->setRect(ignore_btn_rect); ignore_btn_width = ignore_btn_rect.getWidth(); mControlPanel->addChild(ignore_btn, -1); + mute_btn_pad = 4 * HPAD; //only use a 4 * HPAD padding if an ignore button exists } if (mIsScriptDialog && mute_btn != NULL) { LLRect mute_btn_rect(mute_btn->getRect()); - S32 buttons_per_row = max_width / BUTTON_WIDTH; //assume that h_pad far less than BUTTON_WIDTH // Place mute (Block) button to the left of the ignore button. - S32 mute_btn_left = buttons_per_row * BUTTON_WIDTH + (buttons_per_row - 1) * h_pad - mute_btn_rect.getWidth() - ignore_btn_width - (h_pad / 2); - if (mute_btn_left + mute_btn_rect.getWidth() > max_width) // make sure that the mute button is in panel - { - mute_btn_left = max_width - mute_btn_rect.getWidth() - 2 * HPAD; - } + S32 mute_btn_left = max_width - mute_btn_rect.getWidth() - ignore_btn_width - mute_btn_pad; mute_btn_rect.setOriginAndSize(mute_btn_left, BOTTOM_PAD,// always move mute button at the bottom mute_btn_rect.getWidth(), mute_btn_rect.getHeight()); mute_btn->setRect(mute_btn_rect); diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index a8a918f259..04c2e27c9d 100644 --- a/indra/newview/llviewerhelp.cpp +++ b/indra/newview/llviewerhelp.cpp @@ -71,12 +71,6 @@ LLHelpHandler gHelpHandler; std::string LLViewerHelp::getURL(const std::string &topic) { - // allow overriding the help server with a local help file - if( gSavedSettings.getBOOL("HelpUseLocal") ) - { - return "__local"; - } - // if the help topic is empty, use the default topic std::string help_topic = topic; if (help_topic.empty()) diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 1eb4bedfaf..47059b0b8c 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1184,12 +1184,9 @@ void LLViewerMedia::clearAllCookies() LLDirIterator dir_iter(base_dir, "*_*"); while (dir_iter.next(filename)) { - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += "browser_profile"; - target += gDirUtilp->getDirDelimiter(); - target += "cookies"; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, "browser_profile"); + gDirUtilp->append(target, "cookies"); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { @@ -1197,10 +1194,8 @@ void LLViewerMedia::clearAllCookies() } // Other accounts may have new-style cookie files too -- delete them as well - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += PLUGIN_COOKIE_FILE_NAME; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, PLUGIN_COOKIE_FILE_NAME); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index d11e7e32c7..5a8ef00b1f 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -83,6 +83,7 @@ #include "llinventoryfunctions.h" #include "llpanellogin.h" #include "llpanelblockedlist.h" +#include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmoveview.h" #include "llparcel.h" #include "llrootview.h" @@ -4876,6 +4877,37 @@ class LLToolsEnablePathfindingView : public view_listener_t } }; +class LLToolsDoPathfindingRebakeRegion : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool hasPathfinding = (LLPathfindingManager::getInstance() != NULL); + + if (hasPathfinding) + { + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->sendRequestRebakeNavmesh(); + } + + return hasPathfinding; + } +}; + +class LLToolsEnablePathfindingRebakeRegion : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool returnValue = false; + + if (LLPathfindingManager::getInstance() != NULL) + { + LLMenuOptionPathfindingRebakeNavmesh *rebakeInstance = LLMenuOptionPathfindingRebakeNavmesh::getInstance(); + returnValue = (rebakeInstance->canRebakeRegion() && + (rebakeInstance->getMode() == LLMenuOptionPathfindingRebakeNavmesh::kRebakeNavMesh_Available)); + } + return returnValue; + } +}; + // Round the position of all root objects to the grid class LLToolsSnapObjectXY : public view_listener_t { @@ -8359,6 +8391,8 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsEnablePathfinding(), "Tools.EnablePathfinding"); view_listener_t::addMenu(new LLToolsEnablePathfindingView(), "Tools.EnablePathfindingView"); + view_listener_t::addMenu(new LLToolsDoPathfindingRebakeRegion(), "Tools.DoPathfindingRebakeRegion"); + view_listener_t::addMenu(new LLToolsEnablePathfindingRebakeRegion(), "Tools.EnablePathfindingRebakeRegion"); // Help menu // most items use the ShowFloater method diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 1447f133e6..b2bd547811 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -238,7 +238,6 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mRotTime(0.f), mAngularVelocityRot(), mPreviousRotation(), - mJointInfo(NULL), mState(0), mMedia(NULL), mClickAction(0), @@ -284,12 +283,6 @@ LLViewerObject::~LLViewerObject() mInventory = NULL; } - if (mJointInfo) - { - delete mJointInfo; - mJointInfo = NULL; - } - if (mPartSourcep) { mPartSourcep->setDead(); @@ -340,9 +333,6 @@ void LLViewerObject::markDead() if (getParent()) { ((LLViewerObject *)getParent())->removeChild(this); - // go ahead and delete any jointinfo's that we find - delete mJointInfo; - mJointInfo = NULL; } // Mark itself as dead @@ -745,7 +735,7 @@ void LLViewerObject::addThisAndNonJointChildren(std::vector& ob iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; - if ( (!child->isAvatar()) && (!child->isJointChild())) + if ( (!child->isAvatar())) { child->addThisAndNonJointChildren(objects); } @@ -796,6 +786,12 @@ BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) LLDrawable* old_parent = mDrawable->mParent; mDrawable->mParent = parentp; + if (parentp && mDrawable->isActive()) + { + parentp->makeActive(); + parentp->setState(LLDrawable::ACTIVE_CHILD); + } + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); if( (old_parent != parentp && old_parent) || (parentp && parentp->isActive())) @@ -1236,12 +1232,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, coloru.mV[3] = 255 - coloru.mV[3]; mText->setColor(LLColor4(coloru)); mText->setString(temp_string); - - if (mDrawable.notNull()) - { - setChanged(MOVED | SILHOUETTE); - gPipeline.markMoved(mDrawable, FALSE); // undamped - } + + setChanged(MOVED | SILHOUETTE); } else if (mText.notNull()) { @@ -1298,26 +1290,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } } - U8 joint_type = 0; - mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_JointType, joint_type, block_num); - if (joint_type) - { - // create new joint info - if (!mJointInfo) - { - mJointInfo = new LLVOJointInfo; - } - mJointInfo->mJointType = (EHavokJointType) joint_type; - mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointPivot, mJointInfo->mPivot, block_num); - mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointAxisOrAnchor, mJointInfo->mAxisOrAnchor, block_num); - } - else if (mJointInfo) - { - // this joint info is no longer needed - delete mJointInfo; - mJointInfo = NULL; - } - break; } @@ -1436,8 +1408,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, val = (U16 *) &data[count]; #endif new_angv.set(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); setAngularVelocity(new_angv); break; @@ -1463,8 +1435,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f); new_angv.set(U8_to_F32(data[13], -size, size), - U8_to_F32(data[14], -size, size), - U8_to_F32(data[15], -size, size)); + U8_to_F32(data[14], -size, size), + U8_to_F32(data[15], -size, size) ); setAngularVelocity(new_angv); break; } @@ -1538,8 +1510,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, dp->unpackU16(val[VY], "AccY"); dp->unpackU16(val[VZ], "AccZ"); new_angv.set(U16_to_F32(val[VX], -64.f, 64.f), - U16_to_F32(val[VY], -64.f, 64.f), - U16_to_F32(val[VZ], -64.f, 64.f)); + U16_to_F32(val[VY], -64.f, 64.f), + U16_to_F32(val[VZ], -64.f, 64.f)); setAngularVelocity(new_angv); } break; @@ -1968,14 +1940,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, cur_parentp->removeChild(this); - if (mJointInfo && !parent_id) - { - // since this object is no longer parent-relative - // we make sure we delete any joint info - delete mJointInfo; - mJointInfo = NULL; - } - setChanged(MOVED | SILHOUETTE); if (mDrawable.notNull()) @@ -2083,7 +2047,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } else if (new_angv != old_angv) { - if (flagUsePhysics() || new_angv.isExactlyZero()) + if (flagUsePhysics()) { resetRot(); } @@ -2101,7 +2065,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, setChanged(ROTATED | SILHOUETTE); } - if ( gShowObjectUpdates ) { LLColor4 color; @@ -2116,9 +2079,15 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, gPipeline.addDebugBlip(getPositionAgent(), color); } - if ((0.0f == vel_mag_sq) && - (0.0f == accel_mag_sq) && - (0.0f == getAngularVelocity().magVecSquared())) + const F32 MAG_CUTOFF = F_APPROXIMATELY_ZERO; + + llassert(vel_mag_sq >= 0.f); + llassert(accel_mag_sq >= 0.f); + llassert(getAngularVelocity().magVecSquared() >= 0.f); + + if ((MAG_CUTOFF >= vel_mag_sq) && + (MAG_CUTOFF >= accel_mag_sq) && + (MAG_CUTOFF >= getAngularVelocity().magVecSquared())) { mStatic = TRUE; // This object doesn't move! } @@ -2189,17 +2158,13 @@ BOOL LLViewerObject::isActive() const -BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { //static LLFastTimer::DeclareTimer ftm("Viewer Object"); //LLFastTimer t(ftm); - if (mDead) + if (!mDead) { - // It's dead. Don't update it. - return TRUE; - } - // CRO - don't velocity interp linked objects! // Leviathan - but DO velocity interp joints if (!mStatic && sVelocityInterpolate && !isSelected()) @@ -2208,88 +2173,12 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) F32 dt_raw = (F32)(time - mLastInterpUpdateSecs); F32 dt = mTimeDilation * dt_raw; - if (!mJointInfo) - { applyAngularVelocity(dt); - } - LLViewerObject *parentp = (LLViewerObject *) getParent(); - if (mJointInfo) - { - if (parentp) - { - // do parent-relative stuff - LLVector3 ang_vel = getAngularVelocity(); - F32 omega = ang_vel.magVecSquared(); - F32 angle = 0.0f; - LLQuaternion dQ; - if (omega > 0.00001f) + if (isAttachment()) { - omega = sqrt(omega); - angle = omega * dt; - dQ.setQuat(angle, ang_vel); - } - LLVector3 pos = getPosition(); - - if (HJT_HINGE == mJointInfo->mJointType) - { - // hinge = uniform circular motion - LLVector3 parent_pivot = getVelocity(); - LLVector3 parent_axis = getAcceleration(); - - angle = dt * (ang_vel * mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis - dQ.setQuat(angle, mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis - LLVector3 pivot_offset = pos - mJointInfo->mPivot; // pos in pivot-frame - pivot_offset = pivot_offset * dQ; // new rotated pivot-frame pos - pos = mJointInfo->mPivot + pivot_offset; // parent-frame - LLViewerObject::setPosition(pos); - LLQuaternion Q_PC = getRotation(); - setRotation(Q_PC * dQ); mLastInterpUpdateSecs = time; - } - else if (HJT_POINT == mJointInfo->mJointType) - // || HJT_LPOINT == mJointInfo->mJointType) - { - // point-to-point = spin about axis and uniform circular motion - // of axis about the pivot point - // - // NOTE: this interpolation scheme is not quite good enough to - // reduce the bandwidth -- needs a gravitational correction. - // Similarly for hinges with axes that deviate from vertical. - - LLQuaternion Q_PC = getRotation(); - Q_PC = Q_PC * dQ; - setRotation(Q_PC); - - LLVector3 pivot_to_child = - mJointInfo->mAxisOrAnchor; // AxisOrAnchor = anchor - pos = mJointInfo->mPivot + pivot_to_child * Q_PC; - LLViewerObject::setPosition(pos); - mLastInterpUpdateSecs = time; - } - /* else if (HJT_WHEEL == mJointInfo->mJointInfo) - { - // wheel = uniform rotation about axis, with linear - // velocity interpolation (if any) - LLVector3 parent_axis = getAcceleration(); // HACK -- accel stores the parent-axis (parent-frame) - - LLQuaternion Q_PC = getRotation(); - - angle = dt * (parent_axis * ang_vel); - dQ.setQuat(angle, parent_axis); - - Q_PC = Q_PC * dQ; - setRotation(Q_PC); - - pos = getPosition() + dt * getVelocity(); - LLViewerObject::setPosition(pos); - mLastInterpUpdateSecs = time; - }*/ - } - } - else if (isAttachment()) - { - mLastInterpUpdateSecs = time; - return TRUE; + return; } else { // Move object based on it's velocity and rotation @@ -2298,8 +2187,7 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } updateDrawable(FALSE); - - return TRUE; + } } @@ -3889,15 +3777,6 @@ void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped) ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset); updateDrawable(damped); } - else if (isJointChild()) - { - // compute new parent-relative position - LLViewerObject *parent = (LLViewerObject *) getParent(); - LLQuaternion inv_parent_rot = parent->getRotation(); - inv_parent_rot.transQuat(); - LLVector3 pos_parent = (pos_edit - parent->getPositionRegion()) * inv_parent_rot; - LLViewerObject::setPosition(pos_parent, damped); - } else { LLViewerObject::setPosition(pos_edit, damped); @@ -3911,8 +3790,7 @@ LLViewerObject* LLViewerObject::getRootEdit() const { const LLViewerObject* root = this; while (root->mParent - && !(root->mJointInfo - || ((LLViewerObject*)root->mParent)->isAvatar()) ) + && !((LLViewerObject*)root->mParent)->isAvatar()) { root = (LLViewerObject*)root->mParent; } @@ -4621,19 +4499,11 @@ void LLViewerObject::clearIcon() LLViewerObject* LLViewerObject::getSubParent() { - if (isJointChild()) - { - return this; - } return (LLViewerObject*) getParent(); } const LLViewerObject* LLViewerObject::getSubParent() const { - if (isJointChild()) - { - return this; - } return (const LLViewerObject*) getParent(); } @@ -5503,9 +5373,12 @@ BOOL LLViewerObject::setFlagsWithoutUpdate(U32 flags, BOOL state) void LLViewerObject::setPhysicsShapeType(U8 type) { mPhysicsShapeUnknown = false; + if (type != mPhysicsShapeType) + { mPhysicsShapeType = type; mCostStale = true; } +} void LLViewerObject::setPhysicsGravity(F32 gravity) { @@ -5531,7 +5404,6 @@ U8 LLViewerObject::getPhysicsShapeType() const { if (mPhysicsShapeUnknown) { - mPhysicsShapeUnknown = false; gObjectList.updatePhysicsFlags(this); } diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 6f5f7aae42..1fb30db8f2 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -88,18 +88,6 @@ typedef void (*inventory_callback)(LLViewerObject*, S32 serial_num, void*); -// a small struct for keeping track of joints -struct LLVOJointInfo -{ - EHavokJointType mJointType; - LLVector3 mPivot; // parent-frame - // whether the below an axis or anchor (and thus its frame) - // depends on the joint type: - // HINGE ==> axis=parent-frame - // P2P ==> anchor=child-frame - LLVector3 mAxisOrAnchor; -}; - // for exporting textured materials from SL struct LLMaterialExportInfo { @@ -157,7 +145,7 @@ public: LLNameValue* getNVPair(const std::string& name) const; // null if no name value pair by that name // Object create and update functions - virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Types of media we can associate enum { MEDIA_NONE = 0, MEDIA_SET = 1 }; @@ -188,8 +176,6 @@ public: virtual void updateRadius() {}; virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius() - BOOL isJointChild() const { return mJointInfo ? TRUE : FALSE; } - EHavokJointType getJointType() const { return mJointInfo ? mJointInfo->mJointType : HJT_INVALID; } // for jointed and other parent-relative hacks LLViewerObject* getSubParent(); const LLViewerObject* getSubParent() const; @@ -741,7 +727,6 @@ protected: LLQuaternion mAngularVelocityRot; // accumulated rotation from the angular velocity computations LLQuaternion mPreviousRotation; - LLVOJointInfo* mJointInfo; U8 mState; // legacy LLViewerObjectMedia* mMedia; // NULL if no media associated U8 mClickAction; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index b433484783..3eeab0bf3e 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -49,6 +49,8 @@ #include "llstring.h" #include "llhudnametag.h" #include "lldrawable.h" +#include "llflexibleobject.h" +#include "llviewertextureanim.h" #include "xform.h" #include "llsky.h" #include "llviewercamera.h" @@ -78,11 +80,9 @@ extern F32 gMinObjectDistance; extern BOOL gAnimateTextures; -void dialog_refresh_all(); +#define MAX_CONCURRENT_PHYSICS_REQUESTS 256 -#define CULL_VIS -//#define ORPHAN_SPAM -//#define IGNORE_DEAD +void dialog_refresh_all(); // Global lists of objects - should go away soon. LLViewerObjectList gObjectList; @@ -909,8 +909,6 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) const F64 frame_time = LLFrameTimer::getElapsedSeconds(); - std::vector kill_list; - S32 num_active_objects = 0; LLViewerObject *objectp = NULL; // Make a copy of the list in case something in idleUpdate() messes with it @@ -950,7 +948,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) std::vector::iterator idle_end = idle_list.begin()+idle_count; if (gSavedSettings.getBOOL("FreezeTime")) - { + { for (std::vector::iterator iter = idle_list.begin(); iter != idle_end; iter++) @@ -968,24 +966,20 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) idle_iter != idle_end; idle_iter++) { objectp = *idle_iter; - if (objectp->idleUpdate(agent, world, frame_time)) - { - num_active_objects++; - } - else - { - // If Idle Update returns false, kill object! - kill_list.push_back(objectp); - } - } - for (std::vector::iterator kill_iter = kill_list.begin(); - kill_iter != kill_list.end(); kill_iter++) - { - objectp = *kill_iter; - killObject(objectp); + llassert(objectp->isActive()); + objectp->idleUpdate(agent, world, frame_time); + } + + //update flexible objects + LLVolumeImplFlexible::updateClass(); + + //update animated textures + LLViewerTextureAnim::updateClass(); } + + fetchObjectCosts(); fetchPhysicsFlags(); @@ -1052,7 +1046,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) */ LLViewerStats::getInstance()->mNumObjectsStat.addValue((S32) mObjects.size()); - LLViewerStats::getInstance()->mNumActiveObjectsStat.addValue(num_active_objects); + LLViewerStats::getInstance()->mNumActiveObjectsStat.addValue(idle_count); LLViewerStats::getInstance()->mNumSizeCulledStat.addValue(mNumSizeCulled); LLViewerStats::getInstance()->mNumVisCulledStat.addValue(mNumVisCulled); } @@ -1073,8 +1067,6 @@ void LLViewerObjectList::fetchObjectCosts() LLSD id_list; U32 object_index = 0; - U32 count = 0; - for ( std::set::iterator iter = mStaleObjectCost.begin(); iter != mStaleObjectCost.end(); @@ -1091,7 +1083,7 @@ void LLViewerObjectList::fetchObjectCosts() mStaleObjectCost.erase(iter++); - if (count++ >= 450) + if (object_index >= MAX_CONCURRENT_PHYSICS_REQUESTS) { break; } @@ -1136,7 +1128,7 @@ void LLViewerObjectList::fetchPhysicsFlags() for ( std::set::iterator iter = mStalePhysicsFlags.begin(); iter != mStalePhysicsFlags.end(); - ++iter) + ) { // Check to see if a request for this object // has already been made. @@ -1146,12 +1138,14 @@ void LLViewerObjectList::fetchPhysicsFlags() mPendingPhysicsFlags.insert(*iter); id_list[object_index++] = *iter; } - } - // id_list should now contain all - // requests in mStalePhysicsFlags before, so clear - // it now - mStalePhysicsFlags.clear(); + mStalePhysicsFlags.erase(iter++); + + if (object_index >= MAX_CONCURRENT_PHYSICS_REQUESTS) + { + break; + } + } if ( id_list.size() > 0 ) { diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 2bb2e92279..77e382b8c7 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1658,9 +1658,6 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use // Request access list information for this land parcel_mgr.sendParcelAccessListRequest(AL_ACCESS | AL_BAN); - // Request the media url filter list for this land - parcel_mgr.requestParcelMediaURLFilter(); - // Request dwell for this land, if it's not public land. parcel_mgr.mSelectedDwell = DWELL_NAN; if (0 != local_id) @@ -1989,67 +1986,6 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) } } -class LLParcelMediaURLFilterResponder : public LLHTTPClient::Responder -{ - virtual void result(const LLSD& content) - { - LLViewerParcelMgr::getInstance()->receiveParcelMediaURLFilter(content); - } -}; - -void LLViewerParcelMgr::requestParcelMediaURLFilter() -{ - if (!mSelected) - { - return; - } - - LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth ); - if (!region) - { - return; - } - - LLParcel* parcel = mCurrentParcel; - if (!parcel) - { - llwarns << "no parcel" << llendl; - return; - } - - LLSD body; - body["local-id"] = parcel->getLocalID(); - body["list"] = parcel->getMediaURLFilterList(); - - std::string url = region->getCapability("ParcelMediaURLFilterList"); - if (!url.empty()) - { - LLHTTPClient::post(url, body, new LLParcelMediaURLFilterResponder); - } - else - { - llwarns << "can't get ParcelMediaURLFilterList cap" << llendl; - } -} - - -void LLViewerParcelMgr::receiveParcelMediaURLFilter(const LLSD &content) -{ - if (content.has("list")) - { - LLParcel* parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel; - if (!parcel) return; - - if (content["local-id"].asInteger() == parcel->getLocalID()) - { - parcel->setMediaURLFilterList(content["list"]); - - LLViewerParcelMgr::getInstance()->notifyObservers(); - } - } -} - - void LLViewerParcelMgr::deedLandToGroup() { std::string group_name; diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 2a11549426..6183b7e90e 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -223,11 +223,6 @@ public: // Takes an Access List flag, like AL_ACCESS or AL_BAN void sendParcelAccessListRequest(U32 flags); - // asks for the parcel's media url filter list - void requestParcelMediaURLFilter(); - // receive the response - void receiveParcelMediaURLFilter(const LLSD &content); - // Dwell is not part of the usual parcel update information because the // simulator doesn't actually know the per-parcel dwell. Ack! We have // to get it out of the database. diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 47a99d7ea4..b607afbd9d 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1523,11 +1523,9 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("CopyInventoryFromNotecard"); capabilityNames.append("CreateInventoryCategory"); capabilityNames.append("DispatchRegionInfo"); + capabilityNames.append("EnvironmentSettings"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); - capabilityNames.append("EnvironmentSettings"); - capabilityNames.append("ObjectMedia"); - capabilityNames.append("ObjectMediaNavigate"); if (gSavedSettings.getBOOL("UseHTTPInventory")) { @@ -1538,10 +1536,11 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) } capabilityNames.append("GetDisplayNames"); - capabilityNames.append("GetTexture"); capabilityNames.append("GetMesh"); capabilityNames.append("GetObjectCost"); capabilityNames.append("GetObjectPhysicsData"); + capabilityNames.append("GetTexture"); + capabilityNames.append("GroupMemberData"); capabilityNames.append("GroupProposalBallot"); capabilityNames.append("HomeLocation"); capabilityNames.append("LandResources"); @@ -1550,10 +1549,10 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("MeshUploadFlag"); capabilityNames.append("NavMeshGenerationStatus"); capabilityNames.append("NewFileAgentInventory"); + capabilityNames.append("ObjectMedia"); + capabilityNames.append("ObjectMediaNavigate"); capabilityNames.append("ObjectNavMeshProperties"); capabilityNames.append("ParcelPropertiesUpdate"); - capabilityNames.append("ParcelMediaURLFilterList"); - capabilityNames.append("ParcelNavigateMedia"); capabilityNames.append("ParcelVoiceInfoRequest"); capabilityNames.append("ProductInfoRequest"); capabilityNames.append("ProvisionVoiceAccountRequest"); @@ -1567,10 +1566,9 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("SendUserReport"); capabilityNames.append("SendUserReportWithScreenshot"); capabilityNames.append("ServerReleaseNotes"); - capabilityNames.append("SimConsole"); - capabilityNames.append("SimulatorFeatures"); capabilityNames.append("SetDisplayName"); capabilityNames.append("SimConsoleAsync"); + capabilityNames.append("SimulatorFeatures"); capabilityNames.append("StartGroupProposal"); capabilityNames.append("TerrainNavMeshProperties"); capabilityNames.append("TextureStats"); @@ -1578,10 +1576,10 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("UpdateAgentInformation"); capabilityNames.append("UpdateAgentLanguage"); capabilityNames.append("UpdateGestureAgentInventory"); - capabilityNames.append("UpdateNotecardAgentInventory"); - capabilityNames.append("UpdateScriptAgent"); capabilityNames.append("UpdateGestureTaskInventory"); + capabilityNames.append("UpdateNotecardAgentInventory"); capabilityNames.append("UpdateNotecardTaskInventory"); + capabilityNames.append("UpdateScriptAgent"); capabilityNames.append("UpdateScriptTask"); capabilityNames.append("UploadBakedTexture"); capabilityNames.append("ViewerMetrics"); diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index d9918e633b..4b0e0598f6 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -529,9 +529,10 @@ void LLViewerShaderMgr::setShaders() { loaded = loadShadersInterface(); } - + if (loaded) - { + + { loaded = loadTransformShaders(); } diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp old mode 100644 new mode 100755 index 8fef13a6bc..603634e5f3 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -787,7 +787,7 @@ void send_stats() "%-6s Class %d ", gGLManager.mGLVendorShort.substr(0,6).c_str(), (S32)LLFeatureManager::getInstance()->getGPUClass()) - + LLFeatureManager::getInstance()->getGPUString(); + + gGLManager.getRawGLString(); system["gpu"] = gpu_desc; system["gpu_class"] = (S32)LLFeatureManager::getInstance()->getGPUClass(); @@ -798,7 +798,18 @@ void send_stats() S32 shader_level = 0; if (LLPipeline::sRenderDeferred) { - shader_level = 3; + if (LLPipeline::RenderShadowDetail > 0) + { + shader_level = 5; + } + else if (LLPipeline::RenderDeferredSSAO) + { + shader_level = 4; + } + else + { + shader_level = 3; + } } else if (gPipeline.canUseWindLightShadersOnObjects()) { @@ -949,15 +960,8 @@ LLSD LLViewerStats::PhaseMap::dumpPhases() for (phase_map_t::iterator iter = mPhaseMap.begin(); iter != mPhaseMap.end(); ++iter) { const std::string& phase_name = iter->first; - result[phase_name]["completed"] = !(iter->second.getStarted()); + result[phase_name]["completed"] = LLSD::Integer(!(iter->second.getStarted())); result[phase_name]["elapsed"] = iter->second.getElapsedTimeF32(); -#if 0 // global stats for each phase seem like overkill here - phase_stats_t::iterator stats_iter = sPhaseStats.find(phase_name); - if (stats_iter != sPhaseStats.end()) - { - result[phase_name]["stats"] = stats_iter->second.getData(); - } -#endif } return result; } diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 7f638a24bf..96d7890a9e 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -62,6 +62,7 @@ #include "llmediaentry.h" #include "llvovolume.h" #include "llviewermedia.h" +#include "lltexturecache.h" /////////////////////////////////////////////////////////////////////////////// // statics @@ -409,7 +410,11 @@ void LLViewerTextureManager::cleanup() void LLViewerTexture::initClass() { LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ; - sTexelPixelRatio = gSavedSettings.getF32("TexelPixelRatio"); + + if(gSavedSettings.getBOOL("TextureFetchDebuggerEnabled")) + { + sTexelPixelRatio = gSavedSettings.getF32("TexelPixelRatio"); + } } // static @@ -619,6 +624,7 @@ LLViewerTexture::~LLViewerTexture() void LLViewerTexture::init(bool firstinit) { mBoostLevel = LLViewerTexture::BOOST_NONE; + mSelectedTime = 0.f; mFullWidth = 0; mFullHeight = 0; @@ -674,11 +680,18 @@ void LLViewerTexture::setBoostLevel(S32 level) if(mBoostLevel != level) { mBoostLevel = level ; - if(mBoostLevel != LLViewerTexture::BOOST_NONE) + if(mBoostLevel != LLViewerTexture::BOOST_NONE && + mBoostLevel != LLViewerTexture::BOOST_SELECTED) { setNoDelete() ; } } + + if (mBoostLevel == LLViewerTexture::BOOST_SELECTED) + { + mSelectedTime = gFrameTimeSeconds; + } + } @@ -1236,7 +1249,7 @@ void LLViewerFetchedTexture::init(bool firstinit) mIsMissingAsset = FALSE; mLoadedCallbackDesiredDiscardLevel = S8_MAX; - mPauseLoadedCallBacks = TRUE ; + mPauseLoadedCallBacks = FALSE ; mNeedsCreateTexture = FALSE; @@ -1253,6 +1266,7 @@ void LLViewerFetchedTexture::init(bool firstinit) mRequestDeltaTime = 0.f; mForSculpt = FALSE ; mIsFetched = FALSE ; + mInFastCacheList = FALSE; mCachedRawImage = NULL ; mCachedRawDiscardLevel = -1 ; @@ -1266,6 +1280,8 @@ void LLViewerFetchedTexture::init(bool firstinit) mLastReferencedSavedRawImageTime = 0.0f ; mKeptSavedRawImageTime = 0.f ; mLastCallBackActiveTime = 0.f; + + mInDebug = FALSE; } LLViewerFetchedTexture::~LLViewerFetchedTexture() @@ -1310,14 +1326,47 @@ void LLViewerFetchedTexture::cleanup() mSavedRawDiscardLevel = -1; } +//access the fast cache +void LLViewerFetchedTexture::loadFromFastCache() +{ + if(!mInFastCacheList) + { + return; //no need to access the fast cache. + } + mInFastCacheList = FALSE; + + mRawImage = LLAppViewer::getTextureCache()->readFromFastCache(getID(), mRawDiscardLevel) ; + if(mRawImage.notNull()) + { + mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; + mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; + setTexelsPerImage(); + + if(mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE) + { + //discard all oversized textures. + destroyRawImage(); + setIsMissingAsset(); + mRawDiscardLevel = INVALID_DISCARD_LEVEL ; + } + else + { + mRequestedDiscardLevel = mDesiredDiscardLevel + 1; + mIsRawImageValid = TRUE; + addToCreateTexture() ; + } + } +} + void LLViewerFetchedTexture::setForSculpt() { static const S32 MAX_INTERVAL = 8 ; //frames mForSculpt = TRUE ; - if(isForSculptOnly() && !getBoundRecently()) + if(isForSculptOnly() && hasGLTexture() && !getBoundRecently()) { destroyGLTexture() ; //sculpt image does not need gl texture. + mTextureState = ACTIVE; } checkCachedRawSculptImage() ; setMaxVirtualSizeResetInterval(MAX_INTERVAL) ; @@ -1735,7 +1784,7 @@ F32 LLViewerFetchedTexture::calcDecodePriority() S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired; ddiscard = llclamp(ddiscard, 0, MAX_DELTA_DISCARD_LEVEL_FOR_PRIORITY); priority = (ddiscard + 1) * PRIORITY_DELTA_DISCARD_LEVEL_FACTOR; - setAdditionalDecodePriority(1.0f) ;//boost the textures without any data so far. + setAdditionalDecodePriority(0.1f) ;//boost the textures without any data so far. } else if ((mMinDiscardLevel > 0) && (cur_discard <= mMinDiscardLevel)) { @@ -1836,8 +1885,6 @@ F32 LLViewerFetchedTexture::maxDecodePriority() void LLViewerFetchedTexture::setDecodePriority(F32 priority) { - llassert(!mInImageList); - mDecodePriority = priority; if(mDecodePriority < F_ALMOST_ZERO) @@ -1865,13 +1912,34 @@ void LLViewerFetchedTexture::updateVirtualSize() for(U32 i = 0 ; i < mNumFaces ; i++) { LLFace* facep = mFaceList[i] ; - if(facep->getDrawable()->isRecentlyVisible()) + if( facep ) { - addTextureStats(facep->getVirtualSize()) ; - setAdditionalDecodePriority(facep->getImportanceToCamera()) ; + LLDrawable* drawable = facep->getDrawable(); + if (drawable) + { + if(drawable->isRecentlyVisible()) + { + if (getBoostLevel() == LLViewerTexture::BOOST_NONE && + drawable->getVObj() && drawable->getVObj()->isSelected()) + { + setBoostLevel(LLViewerTexture::BOOST_SELECTED); + } + addTextureStats(facep->getVirtualSize()) ; + setAdditionalDecodePriority(facep->getImportanceToCamera()) ; + } + } } } + //reset whether or not a face was selected after 10 seconds + const F32 SELECTION_RESET_TIME = 10.f; + + if (getBoostLevel() == LLViewerTexture::BOOST_SELECTED && + gFrameTimeSeconds - mSelectedTime > SELECTION_RESET_TIME) + { + setBoostLevel(LLViewerTexture::BOOST_NONE); + } + if(mMaxVirtualSizeResetCounter > 0) { mMaxVirtualSizeResetCounter--; @@ -1898,6 +1966,20 @@ S32 LLViewerFetchedTexture::getCurrentDiscardLevelForFetching() return current_discard ; } +bool LLViewerFetchedTexture::setDebugFetching(S32 debug_level) +{ + if(debug_level < 0) + { + mInDebug = FALSE; + return false; + } + mInDebug = TRUE; + + mDesiredDiscardLevel = debug_level; + + return true; +} + bool LLViewerFetchedTexture::updateFetch() { static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); @@ -1935,6 +2017,10 @@ bool LLViewerFetchedTexture::updateFetch() { return false; // process any raw image data in callbacks before replacing } + if(mInFastCacheList) + { + return false; + } S32 current_discard = getCurrentDiscardLevelForFetching() ; S32 desired_discard = getDesiredDiscardLevel(); @@ -2052,6 +2138,10 @@ bool LLViewerFetchedTexture::updateFetch() { make_request = false; } + else if(mDesiredDiscardLevel > getMaxDiscardLevel()) + { + make_request = false; + } else if (mNeedsCreateTexture || mIsMissingAsset) { make_request = false; @@ -2060,6 +2150,11 @@ bool LLViewerFetchedTexture::updateFetch() { make_request = false; } + else if(mCachedRawImage.notNull() && (current_discard < 0 || current_discard > mCachedRawDiscardLevel)) + { + make_request = false; + switchToCachedImage() ; //use the cached raw data first + } //else if (!isJustBound() && mCachedRawImageReady) //{ // make_request = false; @@ -2152,7 +2247,10 @@ bool LLViewerFetchedTexture::updateFetch() void LLViewerFetchedTexture::clearFetchedResults() { - llassert_always(!mNeedsCreateTexture && !mIsFetching); + if(mNeedsCreateTexture || mIsFetching) + { + return ; + } cleanup(); destroyGLTexture(); @@ -2167,11 +2265,13 @@ void LLViewerFetchedTexture::forceToDeleteRequest() { if (mHasFetcher) { - LLAppViewer::getTextureFetch()->deleteRequest(getID(), true); mHasFetcher = FALSE; mIsFetching = FALSE ; - resetTextureStats(); } + + resetTextureStats(); + + mDesiredDiscardLevel = getMaxDiscardLevel() + 1; } void LLViewerFetchedTexture::setIsMissingAsset() @@ -2182,7 +2282,8 @@ void LLViewerFetchedTexture::setIsMissingAsset() } else { - llwarns << mUrl << ": Marking image as missing" << llendl; + //it is normal no map tile on an empty region. + //llwarns << mUrl << ": Marking image as missing" << llendl; } if (mHasFetcher) { @@ -2214,10 +2315,18 @@ void LLViewerFetchedTexture::setLoadedCallback( loaded_callback_func loaded_call mLoadedCallbackDesiredDiscardLevel = llmin(mLoadedCallbackDesiredDiscardLevel, (S8)discard_level) ; } - if(mPauseLoadedCallBacks && !pause) + if(mPauseLoadedCallBacks) { - unpauseLoadedCallbacks(src_callback_list) ; + if(!pause) + { + unpauseLoadedCallbacks(src_callback_list) ; + } } + else if(pause) + { + pauseLoadedCallbacks(src_callback_list) ; + } + LLLoadedCallbackEntry* entryp = new LLLoadedCallbackEntry(loaded_callback, discard_level, keep_imageraw, userdata, src_callback_list, this, pause); mLoadedCallbackList.push_back(entryp); diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h old mode 100644 new mode 100755 index f1105c3705..2e7949e9a3 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -275,6 +275,7 @@ private: protected: LLUUID mID; S32 mBoostLevel; // enum describing priority level + F32 mSelectedTime; // time texture was last selected S32 mFullWidth; S32 mFullHeight; BOOL mUseMipMaps ; @@ -436,6 +437,8 @@ public: void setMinDiscardLevel(S32 discard) { mMinDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel,(S8)discard); } bool updateFetch(); + bool setDebugFetching(S32 debug_level); + bool isInDebug() {return mInDebug;} void clearFetchedResults(); //clear all fetched results, for debug use. @@ -498,6 +501,9 @@ public: void setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;} void forceToDeleteRequest(); + void loadFromFastCache(); + void setInFastCacheList(bool in_list) { mInFastCacheList = in_list; } + bool isInFastCacheList() { return mInFastCacheList; } protected: /*virtual*/ void switchToCachedImage(); S32 getCurrentDiscardLevelForFetching() ; @@ -516,6 +522,8 @@ private: private: BOOL mFullyLoaded; + BOOL mInDebug; + BOOL mInFastCacheList; protected: std::string mLocalFileName; diff --git a/indra/newview/llviewertextureanim.cpp b/indra/newview/llviewertextureanim.cpp index 9f1ac7c49c..2b364851a7 100644 --- a/indra/newview/llviewertextureanim.cpp +++ b/indra/newview/llviewertextureanim.cpp @@ -27,21 +27,37 @@ #include "llviewerprecompiledheaders.h" #include "llviewertextureanim.h" +#include "llvovolume.h" #include "llmath.h" #include "llerror.h" -LLViewerTextureAnim::LLViewerTextureAnim() : LLTextureAnim() +std::vector LLViewerTextureAnim::sInstanceList; + +LLViewerTextureAnim::LLViewerTextureAnim(LLVOVolume* vobj) : LLTextureAnim() { + mVObj = vobj; mLastFrame = -1.f; // Force an update initially mLastTime = 0.f; mOffS = mOffT = 0; mScaleS = mScaleT = 1; mRot = 0; + + mInstanceIndex = sInstanceList.size(); + sInstanceList.push_back(this); } LLViewerTextureAnim::~LLViewerTextureAnim() { + S32 end_idx = sInstanceList.size()-1; + + if (end_idx != mInstanceIndex) + { + sInstanceList[mInstanceIndex] = sInstanceList[end_idx]; + sInstanceList[mInstanceIndex]->mInstanceIndex = mInstanceIndex; + } + + sInstanceList.pop_back(); } void LLViewerTextureAnim::reset() @@ -50,6 +66,14 @@ void LLViewerTextureAnim::reset() mTimer.reset(); } +//static +void LLViewerTextureAnim::updateClass() +{ + for (std::vector::iterator iter = sInstanceList.begin(); iter != sInstanceList.end(); ++iter) + { + (*iter)->mVObj->animateTextures(); + } +} S32 LLViewerTextureAnim::animateTextures(F32 &off_s, F32 &off_t, F32 &scale_s, F32 &scale_t, diff --git a/indra/newview/llviewertextureanim.h b/indra/newview/llviewertextureanim.h index dd7bd0cb90..abbfabceb9 100644 --- a/indra/newview/llviewertextureanim.h +++ b/indra/newview/llviewertextureanim.h @@ -30,10 +30,18 @@ #include "lltextureanim.h" #include "llframetimer.h" +class LLVOVolume; + class LLViewerTextureAnim : public LLTextureAnim { +private: + static std::vector sInstanceList; + S32 mInstanceIndex; + public: - LLViewerTextureAnim(); + static void updateClass(); + + LLViewerTextureAnim(LLVOVolume* vobj); virtual ~LLViewerTextureAnim(); /*virtual*/ void reset(); @@ -51,6 +59,7 @@ public: F32 mRot; protected: + LLVOVolume* mVObj; LLFrameTimer mTimer; F64 mLastTime; F32 mLastFrame; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 9a6c0569a9..49d7e8b842 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -58,7 +58,7 @@ #include "pipeline.h" #include "llappviewer.h" #include "llxuiparser.h" -#include "llagent.h" +#include "llviewerdisplay.h" //////////////////////////////////////////////////////////////////////////// @@ -276,6 +276,7 @@ void LLViewerTextureList::shutdown() // Flush all of the references mLoadingStreamList.clear(); mCreateTextureList.clear(); + mFastCacheList.clear(); mUUIDMap.clear(); @@ -453,6 +454,8 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, LLGLenum primary_format, LLHost request_from_host) { + static LLCachedControl fast_cache_fetching_enabled(gSavedSettings, "FastCacheFetchEnabled"); + LLPointer imagep ; switch(texture_type) { @@ -490,6 +493,11 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, imagep->forceActive() ; } + if(fast_cache_fetching_enabled) + { + mFastCacheList.insert(imagep); + imagep->setInFastCacheList(true); + } return imagep ; } @@ -503,6 +511,7 @@ LLViewerFetchedTexture *LLViewerTextureList::findImage(const LLUUID &image_id) void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image) { + assert_main_thread(); llassert_always(mInitialized) ; llassert(image); if (image->isInImageList()) @@ -519,6 +528,7 @@ void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image) void LLViewerTextureList::removeImageFromList(LLViewerFetchedTexture *image) { + assert_main_thread(); llassert_always(mInitialized) ; llassert(image); if (!image->isInImageList()) @@ -593,16 +603,24 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images"); static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_PRIORITIES("Prioritize"); static LLFastTimer::DeclareTimer FTM_IMAGE_CALLBACKS("Callbacks"); static LLFastTimer::DeclareTimer FTM_IMAGE_FETCH("Fetch"); +static LLFastTimer::DeclareTimer FTM_FAST_CACHE_IMAGE_FETCH("Fast Cache Fetch"); static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create"); static LLFastTimer::DeclareTimer FTM_IMAGE_STATS("Stats"); void LLViewerTextureList::updateImages(F32 max_time) { - if(gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) + static BOOL cleared = FALSE; + if(gTeleportDisplay) { - clearFetchingRequests(); + if(!cleared) + { + clearFetchingRequests(); + gPipeline.clearRebuildGroups(); + cleared = TRUE; + } return; } + cleared = FALSE; LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec()); @@ -613,6 +631,11 @@ void LLViewerTextureList::updateImages(F32 max_time) LLViewerStats::getInstance()->mRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageRaw::sGlobalRawMemory)); LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory)); + { + //loading from fast cache + LLFastTimer t(FTM_FAST_CACHE_IMAGE_FETCH); + max_time -= updateImagesLoadingFastCache(max_time); + } { LLFastTimer t(FTM_IMAGE_UPDATE_PRIORITIES); @@ -673,14 +696,13 @@ void LLViewerTextureList::clearFetchingRequests() return; } + LLAppViewer::getTextureFetch()->deleteAllRequests(); + for (image_priority_list_t::iterator iter = mImageList.begin(); iter != mImageList.end(); ++iter) { - LLViewerFetchedTexture* image = *iter; - if(image->hasFetcher()) - { - image->forceToDeleteRequest() ; - } + LLViewerFetchedTexture* imagep = *iter; + imagep->forceToDeleteRequest() ; } } @@ -688,10 +710,11 @@ void LLViewerTextureList::updateImagesDecodePriorities() { // Update the decode priority for N images each frame { - const size_t max_update_count = llmin((S32) (1024*gFrameIntervalSeconds) + 1, 32); //target 1024 textures per second - S32 update_counter = llmin(max_update_count, mUUIDMap.size()/10); + static const S32 MAX_PRIO_UPDATES = gSavedSettings.getS32("TextureFetchUpdatePriorities"); // default: 32 + const size_t max_update_count = llmin((S32) (MAX_PRIO_UPDATES*MAX_PRIO_UPDATES*gFrameIntervalSeconds) + 1, MAX_PRIO_UPDATES); + S32 update_counter = llmin(max_update_count, mUUIDMap.size()); uuid_map_t::iterator iter = mUUIDMap.upper_bound(mLastUpdateUUID); - while(update_counter > 0 && !mUUIDMap.empty()) + while ((update_counter-- > 0) && !mUUIDMap.empty()) { if (iter == mUUIDMap.end()) { @@ -699,7 +722,13 @@ void LLViewerTextureList::updateImagesDecodePriorities() } mLastUpdateUUID = iter->first; LLPointer imagep = iter->second; - ++iter; // safe to incrament now + ++iter; // safe to increment now + + if(imagep->isInDebug()) + { + update_counter--; + continue; //is in debug, ignore. + } // // Flush formatted images using a lazy flush @@ -754,7 +783,16 @@ void LLViewerTextureList::updateImagesDecodePriorities() imagep->setInactive() ; } } - + + if (!imagep->isInImageList()) + { + continue; + } + if(imagep->isInFastCacheList()) + { + continue; //wait for loading from the fast cache. + } + imagep->processTextureStats(); F32 old_priority = imagep->getDecodePriority(); F32 old_priority_test = llmax(old_priority, 0.0f); @@ -764,15 +802,35 @@ void LLViewerTextureList::updateImagesDecodePriorities() if ((decode_priority_test < old_priority_test * .8f) || (decode_priority_test > old_priority_test * 1.25f)) { - removeImageFromList(imagep); + mImageList.erase(imagep) ; imagep->setDecodePriority(decode_priority); - addImageToList(imagep); + mImageList.insert(imagep); } - update_counter--; } } } +void LLViewerTextureList::setDebugFetching(LLViewerFetchedTexture* tex, S32 debug_level) +{ + if(!tex->setDebugFetching(debug_level)) + { + return; + } + + const F32 DEBUG_PRIORITY = 100000.f; + F32 old_priority_test = llmax(tex->getDecodePriority(), 0.0f); + F32 decode_priority_test = DEBUG_PRIORITY; + + // Ignore < 20% difference + if ((decode_priority_test < old_priority_test * .8f) || + (decode_priority_test > old_priority_test * 1.25f)) + { + removeImageFromList(tex); + tex->setDecodePriority(decode_priority_test); + addImageToList(tex); + } +} + /* static U8 get_image_type(LLViewerFetchedTexture* imagep, LLHost target_host) { @@ -827,6 +885,36 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) return create_timer.getElapsedTimeF32(); } +F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time) +{ + if (gGLManager.mIsDisabled) return 0.0f; + if(mFastCacheList.empty()) + { + return 0.f; + } + + // + // loading texture raw data from the fast cache directly. + // + + LLTimer timer; + image_list_t::iterator enditer = mFastCacheList.begin(); + for (image_list_t::iterator iter = mFastCacheList.begin(); + iter != mFastCacheList.end();) + { + image_list_t::iterator curiter = iter++; + enditer = iter; + LLViewerFetchedTexture *imagep = *curiter; + imagep->loadFromFastCache(); + if (timer.getElapsedTimeF32() > max_time) + { + break; + } + } + mFastCacheList.erase(mFastCacheList.begin(), enditer); + return timer.getElapsedTimeF32(); +} + void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep) { if(!imagep) @@ -850,15 +938,24 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) { LLTimer image_op_timer; - // Update the decode priority for N images each frame - // Make a list with 32 high priority entries + 256 cycled entries - const size_t max_priority_count = llmin((S32) (256*10.f*gFrameIntervalSeconds)+1, 32); - const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256); + // Update fetch for N images each frame + static const S32 MAX_HIGH_PRIO_COUNT = gSavedSettings.getS32("TextureFetchUpdateHighPriority"); // default: 32 + static const S32 MAX_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMaxMediumPriority"); // default: 256 + static const S32 MIN_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMinMediumPriority"); // default: 32 + static const F32 MIN_PRIORITY_THRESHOLD = gSavedSettings.getF32("TextureFetchUpdatePriorityThreshold"); // default: 0.0 + static const bool SKIP_LOW_PRIO = gSavedSettings.getBOOL("TextureFetchUpdateSkipLowPriority"); // default: false + + size_t max_priority_count = llmin((S32) (MAX_HIGH_PRIO_COUNT*MAX_HIGH_PRIO_COUNT*gFrameIntervalSeconds)+1, MAX_HIGH_PRIO_COUNT); + max_priority_count = llmin(max_priority_count, mImageList.size()); - // 32 high priority entries + size_t total_update_count = mUUIDMap.size(); + size_t max_update_count = llmin((S32) (MAX_UPDATE_COUNT*MAX_UPDATE_COUNT*gFrameIntervalSeconds)+1, MAX_UPDATE_COUNT); + max_update_count = llmin(max_update_count, total_update_count); + + // MAX_HIGH_PRIO_COUNT high priority entries typedef std::vector entries_list_t; entries_list_t entries; - size_t update_counter = llmin(max_priority_count, mImageList.size()); + size_t update_counter = max_priority_count; image_priority_list_t::iterator iter1 = mImageList.begin(); while(update_counter > 0) { @@ -868,43 +965,46 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) update_counter--; } - // 256 cycled entries - update_counter = llmin(max_update_count, mUUIDMap.size()); + // MAX_UPDATE_COUNT cycled entries + update_counter = max_update_count; if(update_counter > 0) { uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); - uuid_map_t::iterator iter2p = iter2; - while(update_counter > 0) + while ((update_counter > 0) && (total_update_count > 0)) { if (iter2 == mUUIDMap.end()) { iter2 = mUUIDMap.begin(); } - entries.push_back(iter2->second); - iter2p = iter2++; - update_counter--; - } + LLViewerFetchedTexture* imagep = iter2->second; + // Skip the textures where there's really nothing to do so to give some times to others. Also skip the texture if it's already in the high prio set. + if (!SKIP_LOW_PRIO || (SKIP_LOW_PRIO && ((imagep->getDecodePriority() > MIN_PRIORITY_THRESHOLD) || imagep->hasFetcher()))) + { + entries.push_back(imagep); + update_counter--; + } - mLastFetchUUID = iter2p->first; + iter2++; + total_update_count--; + } } S32 fetch_count = 0; - S32 min_count = max_priority_count + max_update_count/4; + size_t min_update_count = llmin(MIN_UPDATE_COUNT,(S32)(entries.size()-max_priority_count)); + S32 min_count = max_priority_count + min_update_count; for (entries_list_t::iterator iter3 = entries.begin(); iter3 != entries.end(); ) { LLViewerFetchedTexture* imagep = *iter3++; - - bool fetching = imagep->updateFetch(); - if (fetching) + fetch_count += (imagep->updateFetch() ? 1 : 0); + if (min_count <= min_update_count) { - fetch_count++; + mLastFetchUUID = imagep->getID(); } - if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time) + if ((min_count-- <= 0) && (image_op_timer.getElapsedTimeF32() > max_time)) { break; } - min_count--; } //if (fetch_count == 0) //{ @@ -936,6 +1036,9 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) { LLTimer timer; + //loading from fast cache + updateImagesLoadingFastCache(max_time); + // Update texture stats and priorities std::vector > image_list; for (image_priority_list_t::iterator iter = mImageList.begin(); @@ -1583,49 +1686,43 @@ struct UIImageDeclarations : public LLInitParam::Block bool LLUIImageList::initFromFile() { - // construct path to canonical textures.xml in default skin dir - std::string base_file_path = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "default", "textures", "textures.xml"); - - LLXMLNodePtr root; - - if (!LLXMLNode::parseFile(base_file_path, root, NULL)) + // Look for textures.xml in all the right places. Pass + // constraint=LLDir::ALL_SKINS because we want to overlay textures.xml + // from all the skins directories. + std::vector textures_paths = + gDirUtilp->findSkinnedFilenames(LLDir::TEXTURES, "textures.xml", LLDir::ALL_SKINS); + std::vector::const_iterator pi(textures_paths.begin()), pend(textures_paths.end()); + if (pi == pend) { - llwarns << "Unable to parse UI image list file " << base_file_path << llendl; + llwarns << "No textures.xml found in skins directories" << llendl; + return false; + } + + // The first (most generic) file gets special validations + LLXMLNodePtr root; + if (!LLXMLNode::parseFile(*pi, root, NULL)) + { + llwarns << "Unable to parse UI image list file " << *pi << llendl; return false; } if (!root->hasAttribute("version")) { - llwarns << "No valid version number in UI image list file " << base_file_path << llendl; + llwarns << "No valid version number in UI image list file " << *pi << llendl; return false; } UIImageDeclarations images; LLXUIParser parser; - parser.readXUI(root, images, base_file_path); + parser.readXUI(root, images, *pi); - // add components defined in current skin - std::string skin_update_path = gDirUtilp->getSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"; - LLXMLNodePtr update_root; - if (skin_update_path != base_file_path - && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) + // add components defined in the rest of the skin paths + while (++pi != pend) { - parser.readXUI(update_root, images, skin_update_path); - } - - // add components defined in user override of current skin - skin_update_path = gDirUtilp->getUserSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"; - if (skin_update_path != base_file_path - && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) - { - parser.readXUI(update_root, images, skin_update_path); + LLXMLNodePtr update_root; + if (LLXMLNode::parseFile(*pi, update_root, NULL)) + { + parser.readXUI(update_root, images, *pi); + } } if (!images.validateBlock()) return false; diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index e89997fe28..3dda973d3f 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -111,6 +111,7 @@ public: void doPrefetchImages(); void clearFetchingRequests(); + void setDebugFetching(LLViewerFetchedTexture* tex, S32 debug_level); static S32 getMinVideoRamSetting(); static S32 getMaxVideoRamSetting(bool get_recommended = false); @@ -120,6 +121,7 @@ private: F32 updateImagesCreateTextures(F32 max_time); F32 updateImagesFetchTextures(F32 max_time); void updateImagesUpdateStats(); + F32 updateImagesLoadingFastCache(F32 max_time); void addImage(LLViewerFetchedTexture *image); void deleteImage(LLViewerFetchedTexture *image); @@ -173,6 +175,7 @@ public: image_list_t mLoadingStreamList; image_list_t mCreateTextureList; image_list_t mCallbackList; + image_list_t mFastCacheList; // Note: just raw pointers because they are never referenced, just compared against std::set mDirtyTextureList; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp old mode 100644 new mode 100755 index 98ea923272..1def2db829 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -122,11 +122,11 @@ #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" +#include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmodaldialog.h" #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" -#include "llpanelpathfindingrebakenavmesh.h" #include "llpaneltopinfobar.h" #include "llpopupview.h" #include "llpreviewtexture.h" @@ -1558,8 +1558,12 @@ LLViewerWindow::LLViewerWindow(const Params& p) LLNotifications::instance().getChannel("VW_alerts")->connectChanged(&LLViewerWindow::onAlert); LLNotifications::instance().getChannel("VW_alertmodal")->connectChanged(&LLViewerWindow::onAlert); - LLNotifications::instance().setIgnoreAllNotifications(gSavedSettings.getBOOL("IgnoreAllNotifications")); - llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl; + bool ignore = gSavedSettings.getBOOL("IgnoreAllNotifications"); + LLNotifications::instance().setIgnoreAllNotifications(ignore); + if (ignore) + { + llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl; + } // Default to application directory. LLViewerWindow::sSnapshotBaseName = "Snapshot"; @@ -1688,8 +1692,7 @@ LLViewerWindow::LLViewerWindow(const Params& p) LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), mDisplayScale.mV[VX], mDisplayScale.mV[VY], - gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths()); + gDirUtilp->getAppRODataDir()); // Create container for all sub-views LLView::Params rvp; @@ -1930,11 +1933,10 @@ void LLViewerWindow::initWorldUI() LLPanelStandStopFlying* panel_stand_stop_flying = LLPanelStandStopFlying::getInstance(); panel_ssf_container->addChild(panel_stand_stop_flying); - LLPanelPathfindingRebakeNavmesh *panel_rebake_navmesh = LLPanelPathfindingRebakeNavmesh::getInstance(); - panel_ssf_container->addChild(panel_rebake_navmesh); - panel_ssf_container->setVisible(TRUE); + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->initialize(); + // Load and make the toolbars visible // Note: we need to load the toolbars only *after* the user is logged in and IW if (gToolBarView) @@ -2004,6 +2006,8 @@ void LLViewerWindow::shutdownViews() mRootView = NULL; llinfos << "RootView deleted." << llendl ; + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->quit(); + // Automatically deleted as children of mRootView. Fix the globals. gStatusBar = NULL; gIMMgr = NULL; @@ -4233,14 +4237,48 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei image_height = llmin(image_height, window_height); } + S32 original_width = 0; + S32 original_height = 0; + bool reset_deferred = false; + + LLRenderTarget scratch_space; + F32 scale_factor = 1.0f ; if (!keep_window_aspect || (image_width > window_width) || (image_height > window_height)) { - // if image cropping or need to enlarge the scene, compute a scale_factor - F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ; - snapshot_width = (S32)(ratio * image_width) ; - snapshot_height = (S32)(ratio * image_height) ; - scale_factor = llmax(1.0f, 1.0f / ratio) ; + if ((image_width > window_width || image_height > window_height) && LLPipeline::sRenderDeferred && !show_ui) + { + if (scratch_space.allocate(image_width, image_height, GL_RGBA, true, true)) + { + original_width = gPipeline.mDeferredScreen.getWidth(); + original_height = gPipeline.mDeferredScreen.getHeight(); + + if (gPipeline.allocateScreenBuffer(image_width, image_height)) + { + window_width = image_width; + window_height = image_height; + snapshot_width = image_width; + snapshot_height = image_height; + reset_deferred = true; + mWorldViewRectRaw.set(0, image_height, image_width, 0); + scratch_space.bindTarget(); + } + else + { + scratch_space.release(); + gPipeline.allocateScreenBuffer(original_width, original_height); + } + } + } + + if (!reset_deferred) + { + // if image cropping or need to enlarge the scene, compute a scale_factor + F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ; + snapshot_width = (S32)(ratio * image_width) ; + snapshot_height = (S32)(ratio * image_height) ; + scale_factor = llmax(1.0f, 1.0f / ratio) ; + } } if (show_ui && scale_factor > 1.f) @@ -4429,11 +4467,20 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei gPipeline.resetDrawOrders(); } + if (reset_deferred) + { + mWorldViewRectRaw = window_rect; + scratch_space.flush(); + scratch_space.release(); + gPipeline.allocateScreenBuffer(original_width, original_height); + + } + if (high_res) { send_agent_resume(); } - + return ret; } @@ -4760,8 +4807,7 @@ void LLViewerWindow::initFonts(F32 zoom_factor) LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), mDisplayScale.mV[VX] * zoom_factor, mDisplayScale.mV[VY] * zoom_factor, - gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths()); + gDirUtilp->getAppRODataDir()); // Force font reloads, which can be very slow LLFontGL::loadDefaultFonts(); } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp old mode 100644 new mode 100755 index 05febdf93b..627238b0f5 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2423,7 +2423,7 @@ void LLVOAvatar::dumpAnimationState() //------------------------------------------------------------------------ // idleUpdate() //------------------------------------------------------------------------ -BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { LLMemType mt(LLMemType::MTYPE_AVATAR); LLFastTimer t(FTM_AVATAR_UPDATE); @@ -2431,12 +2431,12 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) if (isDead()) { llinfos << "Warning! Idle on dead avatar" << llendl; - return TRUE; + return; } if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR))) { - return TRUE; + return; } checkTextureLoading() ; @@ -2519,8 +2519,6 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) idleUpdateNameTag( root_pos_last ); idleUpdateRenderCost(); - - return TRUE; } void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) @@ -4423,7 +4421,9 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass) } // Can't test for baked hair being defined, since that won't always be the case (not all viewers send baked hair) // TODO: 1.25 will be able to switch this logic back to calling isTextureVisible(); - if (getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha) + + if ( getImage(TEX_HAIR_BAKED, 0) + && getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha) { num_indices += mMeshLOD[MESH_ID_HAIR]->render(mAdjustedPixelArea, first_pass, mIsDummy); first_pass = FALSE; @@ -4567,7 +4567,20 @@ void LLVOAvatar::updateTextures() LLWearableType::EType wearable_type = LLVOAvatarDictionary::getTEWearableType((ETextureIndex)texture_index); U32 num_wearables = gAgentWearables.getWearableCount(wearable_type); const LLTextureEntry *te = getTE(texture_index); - const F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT); + + // getTE can return 0. + // Not sure yet why it does, but of course it crashes when te->mScale? gets used. + // Put safeguard in place so this corner case get better handling and does not result in a crash. + F32 texel_area_ratio = 1.0f; + if( te ) + { + texel_area_ratio = fabs(te->mScaleS * te->mScaleT); + } + else + { + llwarns << "getTE( " << texture_index << " ) returned 0" < is_layer_baked; @@ -7223,7 +7236,7 @@ void LLVOAvatar::onFirstTEMessageReceived() if(!isSelf()) { src_callback_list = &mCallbackTextureList ; - paused = mLoadedCallbacksPaused ; + paused = !isVisible(); } for (U32 i = 0; i < mBakedTextureDatas.size(); i++) @@ -8707,6 +8720,12 @@ BOOL LLVOAvatar::isTextureDefined(LLVOAvatarDefines::ETextureIndex te, U32 index return FALSE; } + if( !getImage( te, index ) ) + { + llwarns << "getImage( " << te << ", " << index << " ) returned 0" << llendl; + return FALSE; + } + return (getImage(te, index)->getID() != IMG_DEFAULT_AVATAR && getImage(te, index)->getID() != IMG_DEFAULT); } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index f5692bb52f..1adb680962 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -134,7 +134,7 @@ public: U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp); - virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); virtual BOOL updateLOD(); BOOL updateJointLODs(); void updateLODRiggedAttachments( void ); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp old mode 100644 new mode 100755 index 6d672acc32..7a81063f83 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -668,15 +668,13 @@ BOOL LLVOAvatarSelf::updateCharacter(LLAgent &agent) } // virtual -BOOL LLVOAvatarSelf::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOAvatarSelf::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - if (!isAgentAvatarValid()) + if (isAgentAvatarValid()) { - return TRUE; + LLVOAvatar::idleUpdate(agent, world, time); + idleUpdateTractorBeam(); } - LLVOAvatar::idleUpdate(agent, world, time); - idleUpdateTractorBeam(); - return TRUE; } // virtual @@ -2132,9 +2130,7 @@ LLSD LLVOAvatarSelf::metricsData() { // runway - add region info LLSD result; - result["id"] = getID(); result["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus()); - result["is_self"] = isSelf(); std::vector rez_counts; LLVOAvatar::getNearbyRezzedStats(rez_counts); result["nearby"] = LLSD::emptyMap(); @@ -2148,7 +2144,6 @@ LLSD LLVOAvatarSelf::metricsData() result["timers"]["ruth"] = mRuthTimer.getElapsedTimeF32(); result["timers"]["invisible"] = mInvisibleTimer.getElapsedTimeF32(); result["timers"]["fully_loaded"] = mFullyLoadedTimer.getElapsedTimeF32(); - result["phases"] = getPhases().dumpPhases(); result["startup"] = LLStartUp::getPhases().dumpPhases(); return result; @@ -2157,7 +2152,12 @@ LLSD LLVOAvatarSelf::metricsData() class ViewerAppearanceChangeMetricsResponder: public LLCurl::Responder { public: - ViewerAppearanceChangeMetricsResponder() + ViewerAppearanceChangeMetricsResponder( S32 expected_sequence, + volatile const S32 & live_sequence, + volatile bool & reporting_started): + mExpectedSequence(expected_sequence), + mLiveSequence(live_sequence), + mReportingStarted(reporting_started) { } @@ -2176,14 +2176,44 @@ public: error(status,reason); } } + + // virtual + void error(U32 status_num, const std::string & reason) + { + } + + // virtual + void result(const LLSD & content) + { + if (mLiveSequence == mExpectedSequence) + { + mReportingStarted = true; + } + } + +private: + S32 mExpectedSequence; + volatile const S32 & mLiveSequence; + volatile bool & mReportingStarted; }; void LLVOAvatarSelf::sendAppearanceChangeMetrics() { // gAgentAvatarp->stopAllPhases(); + static volatile bool reporting_started(false); + static volatile S32 report_sequence(0); LLSD msg = metricsData(); msg["message"] = "ViewerAppearanceChangeMetrics"; + msg["session_id"] = gAgentSessionID; + msg["agent_id"] = gAgentID; + msg["sequence"] = report_sequence; + msg["initial"] = !reporting_started; + msg["break"] = false; + + // Update sequence number + if (S32_MAX == ++report_sequence) + report_sequence = 0; LL_DEBUGS("Avatar") << avString() << "message: " << ll_pretty_print_sd(msg) << LL_ENDL; std::string caps_url; @@ -2196,8 +2226,10 @@ void LLVOAvatarSelf::sendAppearanceChangeMetrics() { LLCurlRequest::headers_t headers; LLHTTPClient::post(caps_url, - msg, - new ViewerAppearanceChangeMetricsResponder); + msg, + new ViewerAppearanceChangeMetricsResponder(report_sequence, + report_sequence, + reporting_started)); } } diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 2b273e616c..7bd0c0bf93 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -85,7 +85,7 @@ protected: //-------------------------------------------------------------------- public: /*virtual*/ void updateRegion(LLViewerRegion *regionp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); //-------------------------------------------------------------------- // LLCharacter interface and related diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index 5ad9ccc9af..566c33c0af 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -277,17 +277,17 @@ BOOL LLVOGrass::isActive() const return TRUE; } -BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GRASS))) { - return TRUE; + return; } if (!mDrawable) { // So drones work. - return TRUE; + return; } if(LLVOTree::isTreeRenderingStopped()) //stop rendering grass @@ -297,14 +297,14 @@ BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) mNumBlades = 0 ; gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); } - return TRUE ; + return; } else if(!mNumBlades)//restart grass rendering { mNumBlades = GRASS_MAX_BLADES ; gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); - return TRUE ; + return; } if (mPatch && (mLastPatchUpdateTime != mPatch->getLastUpdateTime())) @@ -312,7 +312,7 @@ BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); } - return TRUE; + return; } diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h index 00a59facf7..b9835b8802 100644 --- a/indra/newview/llvograss.h +++ b/indra/newview/llvograss.h @@ -73,7 +73,7 @@ public: void plantBlades(); /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. - BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face = -1, // which face to check, -1 = ALL_SIDES diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp index 6da54435e3..97b7418b40 100644 --- a/indra/newview/llvoground.cpp +++ b/indra/newview/llvoground.cpp @@ -49,18 +49,8 @@ LLVOGround::~LLVOGround() { } -BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GROUND))) - { - return TRUE; - } - - /*if (mDrawable) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); - }*/ - return TRUE; } diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h index 73b097327e..290579b4da 100644 --- a/indra/newview/llvoground.h +++ b/indra/newview/llvoground.h @@ -41,7 +41,7 @@ protected: public: LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Graphical stuff for objects - maybe broken out into render class // later? diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 9cce68fff6..e4f9915e93 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -196,9 +196,8 @@ void LLVOPartGroup::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) mDrawable->setPositionGroup(pos); } -BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; } void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent) diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h index 43b2844f07..42c1252d01 100644 --- a/indra/newview/llvopartgroup.h +++ b/indra/newview/llvopartgroup.h @@ -63,7 +63,7 @@ public: LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. - BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); virtual F32 getBinRadius(); virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax); diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp index 312034022e..31358df85f 100644 --- a/indra/newview/llvosky.cpp +++ b/indra/newview/llvosky.cpp @@ -1052,9 +1052,8 @@ void LLVOSky::calcAtmospherics(void) mFadeColor.setAlpha(0); } -BOOL LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; } BOOL LLVOSky::updateSky() diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h index 6e6898d80a..2a150eccb9 100644 --- a/indra/newview/llvosky.h +++ b/indra/newview/llvosky.h @@ -461,7 +461,7 @@ public: void cleanupGL(); void restoreGL(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); BOOL updateSky(); // Graphical stuff for objects - maybe broken out into render class diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index 94a3111f4c..cb905d02da 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -80,9 +80,9 @@ public: glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL])); } if (data_mask & MAP_TEXCOORD3) - { //substitute tex coord 0 for tex coord 3 + { //substitute tex coord 1 for tex coord 3 glClientActiveTextureARB(GL_TEXTURE3_ARB); - glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0])); + glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1])); glClientActiveTextureARB(GL_TEXTURE0_ARB); } if (data_mask & MAP_TEXCOORD2) diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index 337ddfb24d..6687ce432f 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -339,11 +339,11 @@ U32 LLVOTree::processUpdateMessage(LLMessageSystem *mesgsys, return retval; } -BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TREE))) { - return TRUE; + return; } S32 trunk_LOD = sMAX_NUM_TREE_LOD_LEVELS ; @@ -393,8 +393,6 @@ BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } mTrunkLOD = trunk_LOD; - - return TRUE; } const F32 TREE_BLEND_MIN = 1.f; diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h index 0554935539..52debc85ab 100644 --- a/indra/newview/llvotree.h +++ b/indra/newview/llvotree.h @@ -59,7 +59,7 @@ public: void **user_data, U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Graphical stuff for objects - maybe broken out into render class later? /*virtual*/ void render(LLAgent &agent); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 0a5c8b5139..538911e9b8 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -329,7 +329,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, { if (!mTextureAnimp) { - mTextureAnimp = new LLViewerTextureAnim(); + mTextureAnimp = new LLViewerTextureAnim(this); } else { @@ -431,7 +431,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, { if (!mTextureAnimp) { - mTextureAnimp = new LLViewerTextureAnim(); + mTextureAnimp = new LLViewerTextureAnim(this); } else { @@ -499,184 +499,145 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, void LLVOVolume::animateTextures() { - F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f; - S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot); - - if (result) + if (!mDead) { - if (!mTexAnimMode) + F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f; + S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot); + + if (result) { - mFaceMappingChanged = TRUE; - gPipeline.markTextured(mDrawable); - } - mTexAnimMode = result | mTextureAnimp->mMode; + if (!mTexAnimMode) + { + mFaceMappingChanged = TRUE; + gPipeline.markTextured(mDrawable); + } + mTexAnimMode = result | mTextureAnimp->mMode; - S32 start=0, end=mDrawable->getNumFaces()-1; - if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end) - { - start = end = mTextureAnimp->mFace; - } + S32 start=0, end=mDrawable->getNumFaces()-1; + if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end) + { + start = end = mTextureAnimp->mFace; + } - for (S32 i = start; i <= end; i++) - { - LLFace* facep = mDrawable->getFace(i); - if (!facep) continue; - if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue; + for (S32 i = start; i <= end; i++) + { + LLFace* facep = mDrawable->getFace(i); + if (!facep) continue; + if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue; - const LLTextureEntry* te = facep->getTextureEntry(); + const LLTextureEntry* te = facep->getTextureEntry(); - if (!te) - { - continue; - } + if (!te) + { + continue; + } - if (!(result & LLViewerTextureAnim::ROTATE)) - { - te->getRotation(&rot); - } - if (!(result & LLViewerTextureAnim::TRANSLATE)) - { - te->getOffset(&off_s,&off_t); - } - if (!(result & LLViewerTextureAnim::SCALE)) - { - te->getScale(&scale_s, &scale_t); - } + if (!(result & LLViewerTextureAnim::ROTATE)) + { + te->getRotation(&rot); + } + if (!(result & LLViewerTextureAnim::TRANSLATE)) + { + te->getOffset(&off_s,&off_t); + } + if (!(result & LLViewerTextureAnim::SCALE)) + { + te->getScale(&scale_s, &scale_t); + } - if (!facep->mTextureMatrix) - { - facep->mTextureMatrix = new LLMatrix4(); - } + if (!facep->mTextureMatrix) + { + facep->mTextureMatrix = new LLMatrix4(); + } - LLMatrix4& tex_mat = *facep->mTextureMatrix; - tex_mat.setIdentity(); - LLVector3 trans ; + LLMatrix4& tex_mat = *facep->mTextureMatrix; + tex_mat.setIdentity(); + LLVector3 trans ; - if(facep->isAtlasInUse()) - { - // - //if use atlas for animated texture - //apply the following transform to the animation matrix. - // - - F32 tcoord_xoffset = 0.f ; - F32 tcoord_yoffset = 0.f ; - F32 tcoord_xscale = 1.f ; - F32 tcoord_yscale = 1.f ; if(facep->isAtlasInUse()) { - const LLVector2* tmp = facep->getTexCoordOffset() ; - tcoord_xoffset = tmp->mV[0] ; - tcoord_yoffset = tmp->mV[1] ; + // + //if use atlas for animated texture + //apply the following transform to the animation matrix. + // - tmp = facep->getTexCoordScale() ; - tcoord_xscale = tmp->mV[0] ; - tcoord_yscale = tmp->mV[1] ; + F32 tcoord_xoffset = 0.f ; + F32 tcoord_yoffset = 0.f ; + F32 tcoord_xscale = 1.f ; + F32 tcoord_yscale = 1.f ; + if(facep->isAtlasInUse()) + { + const LLVector2* tmp = facep->getTexCoordOffset() ; + tcoord_xoffset = tmp->mV[0] ; + tcoord_yoffset = tmp->mV[1] ; + + tmp = facep->getTexCoordScale() ; + tcoord_xscale = tmp->mV[0] ; + tcoord_yscale = tmp->mV[1] ; + } + trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f)); + + tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f)); + } + else //non atlas + { + trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); + tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); } - trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f)); - tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f)); - } - else //non atlas - { - trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); - tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); - } - - LLVector3 scale(scale_s, scale_t, 1.f); - LLQuaternion quat; - quat.setQuat(rot, 0, 0, -1.f); + LLVector3 scale(scale_s, scale_t, 1.f); + LLQuaternion quat; + quat.setQuat(rot, 0, 0, -1.f); - tex_mat.rotate(quat); + tex_mat.rotate(quat); - LLMatrix4 mat; - mat.initAll(scale, LLQuaternion(), LLVector3()); - tex_mat *= mat; + LLMatrix4 mat; + mat.initAll(scale, LLQuaternion(), LLVector3()); + tex_mat *= mat; - tex_mat.translate(trans); + tex_mat.translate(trans); + } } - } - else - { - if (mTexAnimMode && mTextureAnimp->mRate == 0) + else { - U8 start, count; - - if (mTextureAnimp->mFace == -1) + if (mTexAnimMode && mTextureAnimp->mRate == 0) { - start = 0; - count = getNumTEs(); - } - else - { - start = (U8) mTextureAnimp->mFace; - count = 1; - } + U8 start, count; - for (S32 i = start; i < start + count; i++) - { - if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE) + if (mTextureAnimp->mFace == -1) { - setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT); + start = 0; + count = getNumTEs(); } - if (mTexAnimMode & LLViewerTextureAnim::SCALE) + else { - setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT); + start = (U8) mTextureAnimp->mFace; + count = 1; } - if (mTexAnimMode & LLViewerTextureAnim::ROTATE) - { - setTERotation(i, mTextureAnimp->mRot); - } - } - gPipeline.markTextured(mDrawable); - mFaceMappingChanged = TRUE; - mTexAnimMode = 0; + for (S32 i = start; i < start + count; i++) + { + if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE) + { + setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT); + } + if (mTexAnimMode & LLViewerTextureAnim::SCALE) + { + setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT); + } + if (mTexAnimMode & LLViewerTextureAnim::ROTATE) + { + setTERotation(i, mTextureAnimp->mRot); + } + } + + gPipeline.markTextured(mDrawable); + mFaceMappingChanged = TRUE; + mTexAnimMode = 0; + } } } } -BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) -{ - LLViewerObject::idleUpdate(agent, world, time); - - //static LLFastTimer::DeclareTimer ftm("Volume Idle"); - //LLFastTimer t(ftm); - - if (mDead || mDrawable.isNull()) - { - return TRUE; - } - - /////////////////////// - // - // Do texture animation stuff - // - - if (mTextureAnimp && gAnimateTextures) - { - animateTextures(); - } - - // Dispatch to implementation - if (mVolumeImpl) - { - mVolumeImpl->doIdleUpdate(agent, world, time); - } - - const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40; - - if (mDrawable->isActive()) - { - if (mDrawable->isRoot() && - mDrawable->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES && - (!mDrawable->getParent() || !mDrawable->getParent()->isActive())) - { - mDrawable->makeStatic(); - } - } - - return TRUE; -} void LLVOVolume::updateTextures() { @@ -698,7 +659,8 @@ void LLVOVolume::updateTextures() } } - } + + } } BOOL LLVOVolume::isVisible() const @@ -860,7 +822,7 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) } } - S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture + S32 texture_discard = mSculptTexture->getCachedRawImageLevel(); //try to match the texture S32 current_discard = getVolume() ? getVolume()->getSculptLevel() : -2 ; if (texture_discard >= 0 && //texture has some data available @@ -916,8 +878,7 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) BOOL LLVOVolume::isActive() const { - return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()) || - (mDrawable.notNull() && mDrawable->isActive()); + return !mStatic; } BOOL LLVOVolume::setMaterial(const U8 material) @@ -1167,7 +1128,7 @@ void LLVOVolume::sculpt() S8 sculpt_components = 0; const U8* sculpt_data = NULL; - S32 discard_level = mSculptTexture->getDiscardLevel() ; + S32 discard_level = mSculptTexture->getCachedRawImageLevel() ; LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ; S32 max_discard = mSculptTexture->getMaxDiscardLevel(); @@ -1266,6 +1227,13 @@ BOOL LLVOVolume::calcLOD() if (mDrawable->isState(LLDrawable::RIGGED)) { LLVOAvatar* avatar = getAvatar(); + + // Not sure how this can really happen, but alas it does. Better exit here than crashing. + if( !avatar || !avatar->mDrawable ) + { + return FALSE; + } + distance = avatar->mDrawable->mDistanceWRTCamera; radius = avatar->getBinRadius(); } @@ -1374,7 +1342,8 @@ BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp) void LLVOVolume::updateFaceFlags() { - for (S32 i = 0; i < getVolume()->getNumFaces(); i++) + // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces() + for (S32 i = 0; i < getVolume()->getNumFaces() && i < mDrawable->getNumFaces(); i++) { LLFace *face = mDrawable->getFace(i); if (face) @@ -1475,7 +1444,10 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global) volume = getVolume(); } - for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++) + // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces() + for (S32 i = 0; + i < getVolume()->getNumVolumeFaces() && i < mDrawable->getNumFaces() && i < getNumTEs(); + i++) { LLFace *face = mDrawable->getFace(i); if (!face) @@ -1511,7 +1483,7 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global) updateRadius(); mDrawable->movePartition(); - + return res; } @@ -1776,6 +1748,11 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) void LLVOVolume::updateFaceSize(S32 idx) { + if( mDrawable->getNumFaces() <= idx ) + { + return; + } + LLFace* facep = mDrawable->getFace(idx); if (facep) { @@ -2466,7 +2443,12 @@ void LLVOVolume::addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) //add the face to show the media if it is in playing if(mDrawable) { - LLFace* facep = mDrawable->getFace(texture_index) ; + LLFace* facep(NULL); + if( texture_index < mDrawable->getNumFaces() ) + { + facep = mDrawable->getFace(texture_index) ; + } + if(facep) { LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ; @@ -3601,7 +3583,6 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e if (LLFloater::isVisible(gFloaterTools) && getAvatar()->isSelf()) { updateRiggedVolume(); - genBBoxes(FALSE); volume = mRiggedVolume; transform = false; } @@ -3865,6 +3846,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons LLVector4a* pos = dst_face.mPositions; + if( pos && weight && dst_face.mExtents ) { LLFastTimer t(FTM_SKIN_RIGGED); diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 798927a45f..5482c80f2b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -68,7 +68,7 @@ class LLVolumeInterface public: virtual ~LLVolumeInterface() { } virtual LLVolumeInterfaceType getInterfaceType() const = 0; - virtual void doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) = 0; + virtual void doIdleUpdate() = 0; virtual BOOL doUpdateGeometry(LLDrawable *drawable) = 0; virtual LLVector3 getPivotPosition() const = 0; virtual void onSetVolume(const LLVolumeParams &volume_params, const S32 detail) = 0; @@ -114,8 +114,7 @@ public: void deleteFaces(); void animateTextures(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); - + BOOL isVisible() const ; /*virtual*/ BOOL isActive() const; /*virtual*/ BOOL isAttachment() const; diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp index 942eff6171..e8a1c3d1d6 100644 --- a/indra/newview/llvowater.cpp +++ b/indra/newview/llvowater.cpp @@ -100,17 +100,8 @@ void LLVOWater::updateTextures() } // Never gets called -BOOL LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - /*if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER))) - { - return TRUE; - } - if (mDrawable) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); - }*/ - return TRUE; } LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline) diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h index ed709dd840..cf9323ef2e 100644 --- a/indra/newview/llvowater.h +++ b/indra/newview/llvowater.h @@ -58,7 +58,7 @@ public: static void initClass(); static void cleanupClass(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax); diff --git a/indra/newview/llvowlsky.cpp b/indra/newview/llvowlsky.cpp index afd902201b..a33f42cf84 100644 --- a/indra/newview/llvowlsky.cpp +++ b/indra/newview/llvowlsky.cpp @@ -92,9 +92,9 @@ void LLVOWLSky::initSunDirection(LLVector3 const & sun_direction, { } -BOOL LLVOWLSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOWLSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; + } BOOL LLVOWLSky::isActive(void) const diff --git a/indra/newview/llvowlsky.h b/indra/newview/llvowlsky.h index 825e13a203..729dced15e 100644 --- a/indra/newview/llvowlsky.h +++ b/indra/newview/llvowlsky.h @@ -53,7 +53,7 @@ public: void initSunDirection(LLVector3 const & sun_direction, LLVector3 const & sun_angular_velocity); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ BOOL isActive(void) const; /*virtual*/ LLDrawable * createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index e386112334..4f52ff9778 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -100,7 +100,7 @@ void LLWaterParamManager::loadPresetsFromDir(const std::string& dir) break; // no more files } - std::string path = dir + file; + std::string path = gDirUtilp->add(dir, file); if (!loadPreset(path)) { llwarns << "Error loading water preset from " << path << llendl; diff --git a/indra/newview/llwlhandlers.cpp b/indra/newview/llwlhandlers.cpp index 2425b96678..be3e3ff30e 100644 --- a/indra/newview/llwlhandlers.cpp +++ b/indra/newview/llwlhandlers.cpp @@ -105,10 +105,16 @@ LLEnvironmentRequestResponder::LLEnvironmentRequestResponder() return; } - if (unvalidated_content[0]["regionID"].asUUID() != gAgent.getRegion()->getRegionID()) + LLUUID regionId; + if( gAgent.getRegion() ) + { + regionId = gAgent.getRegion()->getRegionID(); + } + + if (unvalidated_content[0]["regionID"].asUUID() != regionId ) { LL_WARNS("WindlightCaps") << "Not in the region from where this data was received (wanting " - << gAgent.getRegion()->getRegionID() << " but got " << unvalidated_content[0]["regionID"].asUUID() + << regionId << " but got " << unvalidated_content[0]["regionID"].asUUID() << ") - ignoring..." << LL_ENDL; return; } diff --git a/indra/newview/llwlparammanager.cpp b/indra/newview/llwlparammanager.cpp index 49d9d44d74..6077208799 100644 --- a/indra/newview/llwlparammanager.cpp +++ b/indra/newview/llwlparammanager.cpp @@ -283,7 +283,7 @@ void LLWLParamManager::loadPresetsFromDir(const std::string& dir) break; // no more files } - std::string path = dir + file; + std::string path = gDirUtilp->add(dir, file); if (!loadPreset(path)) { llwarns << "Error loading sky preset from " << path << llendl; diff --git a/indra/newview/macutil_Prefix.h b/indra/newview/macutil_Prefix.h index fd8e927a08..b54a764a62 100644 --- a/indra/newview/macutil_Prefix.h +++ b/indra/newview/macutil_Prefix.h @@ -33,7 +33,7 @@ */ #include +#include "fix_macros.h" -#undef check #undef verify #undef require diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index b42a84da47..ab45bd3d3b 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -449,6 +449,19 @@ LLPipeline::LLPipeline() : mLightFunc = 0; } +void LLPipeline::connectRefreshCachedSettingsSafe(const std::string name) +{ + LLPointer cntrl_ptr = gSavedSettings.getControl(name); + if ( cntrl_ptr.isNull() ) + { + llwarns << "Global setting name not found:" << name << llendl; + } + else + { + cntrl_ptr->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + } +} + void LLPipeline::init() { LLMemType mt(LLMemType::MTYPE_PIPELINE_INIT); @@ -533,88 +546,86 @@ void LLPipeline::init() // // Update all settings to trigger a cached settings refresh // - - gSavedSettings.getControl("RenderAutoMaskAlphaDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAutoMaskAlphaNonDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderUseFarClip")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAvatarMaxVisible")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDelayVBUpdate")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - - gSavedSettings.getControl("UseOcclusion")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - - gSavedSettings.getControl("VertexShaderEnable")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAvatarVP")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("WindLightUseAtmosShaders")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredSunWash")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderFSAASamples")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderResolutionDivisor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderUIBuffer")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowDetail")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredSSAO")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowResolutionScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderLocalLights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDelayCreation")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAnimateRes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("FreezeTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("DebugBeaconLineWidth")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightBrightness")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightColor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightThickness")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotLightsInNondeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewAmbientColor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowMinLuminance")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowMaxExtractAlpha")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWarmthAmount")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowLumWeights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWarmthWeights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowResolutionPow")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowIterations")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWidth")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowStrength")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDepthOfField")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFocusTransitionTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFNumber")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFocalLength")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFieldOfView")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowNoise")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBlurSize")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOMaxScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOFactor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOEffect")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOffsetError")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBiasError")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBias")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotShadowOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotShadowBias")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderEdgeDepthCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderEdgeNormCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowGaussian")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBlurDistFactor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredAtmospheric")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderReflectionDetail")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightFadeTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowClipPlanes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOrthoClipPlanes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowNearDist")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderFarClip")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowSplitExponent")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowErrorCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowFOVCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraMaxCoF")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraDoFResScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + connectRefreshCachedSettingsSafe("RenderAutoMaskAlphaDeferred"); + connectRefreshCachedSettingsSafe("RenderAutoMaskAlphaNonDeferred"); + connectRefreshCachedSettingsSafe("RenderUseFarClip"); + connectRefreshCachedSettingsSafe("RenderAvatarMaxVisible"); + connectRefreshCachedSettingsSafe("RenderDelayVBUpdate"); + connectRefreshCachedSettingsSafe("UseOcclusion"); + connectRefreshCachedSettingsSafe("VertexShaderEnable"); + connectRefreshCachedSettingsSafe("RenderAvatarVP"); + connectRefreshCachedSettingsSafe("WindLightUseAtmosShaders"); + connectRefreshCachedSettingsSafe("RenderDeferred"); + connectRefreshCachedSettingsSafe("RenderDeferredSunWash"); + connectRefreshCachedSettingsSafe("RenderFSAASamples"); + connectRefreshCachedSettingsSafe("RenderResolutionDivisor"); + connectRefreshCachedSettingsSafe("RenderUIBuffer"); + connectRefreshCachedSettingsSafe("RenderShadowDetail"); + connectRefreshCachedSettingsSafe("RenderDeferredSSAO"); + connectRefreshCachedSettingsSafe("RenderShadowResolutionScale"); + connectRefreshCachedSettingsSafe("RenderLocalLights"); + connectRefreshCachedSettingsSafe("RenderDelayCreation"); + connectRefreshCachedSettingsSafe("RenderAnimateRes"); + connectRefreshCachedSettingsSafe("FreezeTime"); + connectRefreshCachedSettingsSafe("DebugBeaconLineWidth"); + connectRefreshCachedSettingsSafe("RenderHighlightBrightness"); + connectRefreshCachedSettingsSafe("RenderHighlightColor"); + connectRefreshCachedSettingsSafe("RenderHighlightThickness"); + connectRefreshCachedSettingsSafe("RenderSpotLightsInNondeferred"); + connectRefreshCachedSettingsSafe("PreviewAmbientColor"); + connectRefreshCachedSettingsSafe("PreviewDiffuse0"); + connectRefreshCachedSettingsSafe("PreviewSpecular0"); + connectRefreshCachedSettingsSafe("PreviewDiffuse1"); + connectRefreshCachedSettingsSafe("PreviewSpecular1"); + connectRefreshCachedSettingsSafe("PreviewDiffuse2"); + connectRefreshCachedSettingsSafe("PreviewSpecular2"); + connectRefreshCachedSettingsSafe("PreviewDirection0"); + connectRefreshCachedSettingsSafe("PreviewDirection1"); + connectRefreshCachedSettingsSafe("PreviewDirection2"); + connectRefreshCachedSettingsSafe("RenderGlowMinLuminance"); + connectRefreshCachedSettingsSafe("RenderGlowMaxExtractAlpha"); + connectRefreshCachedSettingsSafe("RenderGlowWarmthAmount"); + connectRefreshCachedSettingsSafe("RenderGlowLumWeights"); + connectRefreshCachedSettingsSafe("RenderGlowWarmthWeights"); + connectRefreshCachedSettingsSafe("RenderGlowResolutionPow"); + connectRefreshCachedSettingsSafe("RenderGlowIterations"); + connectRefreshCachedSettingsSafe("RenderGlowWidth"); + connectRefreshCachedSettingsSafe("RenderGlowStrength"); + connectRefreshCachedSettingsSafe("RenderDepthOfField"); + connectRefreshCachedSettingsSafe("CameraFocusTransitionTime"); + connectRefreshCachedSettingsSafe("CameraFNumber"); + connectRefreshCachedSettingsSafe("CameraFocalLength"); + connectRefreshCachedSettingsSafe("CameraFieldOfView"); + connectRefreshCachedSettingsSafe("RenderShadowNoise"); + connectRefreshCachedSettingsSafe("RenderShadowBlurSize"); + connectRefreshCachedSettingsSafe("RenderSSAOScale"); + connectRefreshCachedSettingsSafe("RenderSSAOMaxScale"); + connectRefreshCachedSettingsSafe("RenderSSAOFactor"); + connectRefreshCachedSettingsSafe("RenderSSAOEffect"); + connectRefreshCachedSettingsSafe("RenderShadowOffsetError"); + connectRefreshCachedSettingsSafe("RenderShadowBiasError"); + connectRefreshCachedSettingsSafe("RenderShadowOffset"); + connectRefreshCachedSettingsSafe("RenderShadowBias"); + connectRefreshCachedSettingsSafe("RenderSpotShadowOffset"); + connectRefreshCachedSettingsSafe("RenderSpotShadowBias"); + connectRefreshCachedSettingsSafe("RenderEdgeDepthCutoff"); + connectRefreshCachedSettingsSafe("RenderEdgeNormCutoff"); + connectRefreshCachedSettingsSafe("RenderShadowGaussian"); + connectRefreshCachedSettingsSafe("RenderShadowBlurDistFactor"); + connectRefreshCachedSettingsSafe("RenderDeferredAtmospheric"); + connectRefreshCachedSettingsSafe("RenderReflectionDetail"); + connectRefreshCachedSettingsSafe("RenderHighlightFadeTime"); + connectRefreshCachedSettingsSafe("RenderShadowClipPlanes"); + connectRefreshCachedSettingsSafe("RenderShadowOrthoClipPlanes"); + connectRefreshCachedSettingsSafe("RenderShadowNearDist"); + connectRefreshCachedSettingsSafe("RenderFarClip"); + connectRefreshCachedSettingsSafe("RenderShadowSplitExponent"); + connectRefreshCachedSettingsSafe("RenderShadowErrorCutoff"); + connectRefreshCachedSettingsSafe("RenderShadowFOVCutoff"); + connectRefreshCachedSettingsSafe("CameraOffset"); + connectRefreshCachedSettingsSafe("CameraMaxCoF"); + connectRefreshCachedSettingsSafe("CameraDoFResScale"); + connectRefreshCachedSettingsSafe("RenderAutoHideSurfaceAreaLimit"); gSavedSettings.getControl("RenderAutoHideSurfaceAreaLimit")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); } @@ -722,7 +733,7 @@ void LLPipeline::destroyGL() { glDeleteQueriesARB(1, &mMeshDirtyQueryObject); mMeshDirtyQueryObject = 0; -} + } } static LLFastTimer::DeclareTimer FTM_RESIZE_SCREEN_TEXTURE("Resize Screen Texture"); @@ -768,18 +779,57 @@ void LLPipeline::allocatePhysicsBuffer() } } -void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) +bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) { refreshCachedSettings(); - U32 samples = RenderFSAASamples; + + bool save_settings = sRenderDeferred; + if (save_settings) + { + // Set this flag in case we crash while resizing window or allocating space for deferred rendering targets + gSavedSettings.setBOOL("RenderInitError", TRUE); + gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); + } - //try to allocate screen buffers at requested resolution and samples + eFBOStatus ret = doAllocateScreenBuffer(resX, resY); + + if (save_settings) + { + // don't disable shaders on next session + gSavedSettings.setBOOL("RenderInitError", FALSE); + gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); + } + + if (ret == FBO_FAILURE) + { //FAILSAFE: screen buffer allocation failed, disable deferred rendering if it's enabled + //NOTE: if the session closes successfully after this call, deferred rendering will be + // disabled on future sessions + if (LLPipeline::sRenderDeferred) + { + gSavedSettings.setBOOL("RenderDeferred", FALSE); + LLPipeline::refreshCachedSettings(); + } + } + + return ret == FBO_SUCCESS_FULLRES; +} + + +LLPipeline::eFBOStatus LLPipeline::doAllocateScreenBuffer(U32 resX, U32 resY) +{ + // try to allocate screen buffers at requested resolution and samples // - on failure, shrink number of samples and try again // - if not multisampled, shrink resolution and try again (favor X resolution over Y) // Make sure to call "releaseScreenBuffers" after each failure to cleanup the partially loaded state + U32 samples = RenderFSAASamples; + + eFBOStatus ret = FBO_SUCCESS_FULLRES; if (!allocateScreenBuffer(resX, resY, samples)) { + //failed to allocate at requested specification, return false + ret = FBO_FAILURE; + releaseScreenBuffers(); //reduce number of samples while (samples > 0) @@ -787,7 +837,7 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) samples /= 2; if (allocateScreenBuffer(resX, resY, samples)) { //success - return; + return FBO_SUCCESS_LOWRES; } releaseScreenBuffers(); } @@ -800,22 +850,23 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) resY /= 2; if (allocateScreenBuffer(resX, resY, samples)) { - return; + return FBO_SUCCESS_LOWRES; } releaseScreenBuffers(); resX /= 2; if (allocateScreenBuffer(resX, resY, samples)) { - return; + return FBO_SUCCESS_LOWRES; } releaseScreenBuffers(); } llwarns << "Unable to allocate screen buffer at any resolution!" << llendl; } -} + return ret; +} bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) { @@ -843,10 +894,6 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) if (LLPipeline::sRenderDeferred) { - // Set this flag in case we crash while resizing window or allocating space for deferred rendering targets - gSavedSettings.setBOOL("RenderInitError", TRUE); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); - S32 shadow_detail = RenderShadowDetail; BOOL ssao = RenderDeferredSSAO; @@ -858,7 +905,7 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) if (!mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE, samples)) return false; if (samples > 0) { - if (!mFXAABuffer.allocate(nhpo2(resX), nhpo2(resY), GL_RGBA, FALSE, FALSE, LLTexUnit::TT_TEXTURE, FALSE, samples)) return false; + if (!mFXAABuffer.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_TEXTURE, FALSE, samples)) return false; } else { @@ -881,7 +928,7 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) U32 sun_shadow_map_width = ((U32(resX*scale)+1)&~1); // must be even to avoid a stripe in the horizontal shadow blur for (U32 i = 0; i < 4; i++) { - if (!mShadow[i].allocate(sun_shadow_map_width,U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE)) return false; + if (!mShadow[i].allocate(sun_shadow_map_width,U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE)) return false; } } else @@ -892,7 +939,7 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) } } - U32 width = nhpo2(U32(resX*scale))/2; + U32 width = (U32) (resX*scale); U32 height = width; if (shadow_detail > 1) @@ -911,9 +958,11 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) } } - // don't disable shaders on next session - gSavedSettings.setBOOL("RenderInitError", FALSE); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); + //HACK make screenbuffer allocations start failing after 30 seconds + if (gSavedSettings.getBOOL("SimulateFBOFailure")) + { + return false; + } } else { @@ -1671,6 +1720,21 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable) } +//static +void LLPipeline::removeMutedAVsLights(LLVOAvatar* muted_avatar) +{ + LLFastTimer t(FTM_REMOVE_FROM_LIGHT_SET); + for (light_set_t::iterator iter = gPipeline.mNearbyLights.begin(); + iter != gPipeline.mNearbyLights.end(); iter++) + { + if (iter->drawable->getVObj()->isAttachment() && iter->drawable->getVObj()->getAvatar() == muted_avatar) + { + gPipeline.mLights.erase(iter->drawable); + gPipeline.mNearbyLights.erase(iter); + } + } +} + U32 LLPipeline::addObject(LLViewerObject *vobj) { LLMemType mt_ao(LLMemType::MTYPE_PIPELINE_ADD_OBJECT); @@ -1850,6 +1914,10 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED); if (done) { + if (drawablep->isRoot()) + { + drawablep->makeStatic(); + } drawablep->clearState(LLDrawable::ON_MOVE_LIST); if (drawablep->isState(LLDrawable::ANIMATED_CHILD)) { //will likely not receive any future world matrix updates @@ -1981,6 +2049,7 @@ void LLPipeline::grabReferences(LLCullResult& result) void LLPipeline::clearReferences() { sCull = NULL; + mGroupSaveQ1.clear(); } void check_references(LLSpatialGroup* group, LLDrawable* drawable) @@ -2545,6 +2614,64 @@ void LLPipeline::updateGL() static LLFastTimer::DeclareTimer FTM_REBUILD_PRIORITY_GROUPS("Rebuild Priority Groups"); +void LLPipeline::clearRebuildGroups() +{ + LLSpatialGroup::sg_vector_t hudGroups; + + mGroupQ1Locked = true; + // Iterate through all drawables on the priority build queue, + for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ1.begin(); + iter != mGroupQ1.end(); ++iter) + { + LLSpatialGroup* group = *iter; + + // If the group contains HUD objects, save the group + if (group->isHUDGroup()) + { + hudGroups.push_back(group); + } + // Else, no HUD objects so clear the build state + else + { + group->clearState(LLSpatialGroup::IN_BUILD_Q1); + } + } + + // Clear the group + mGroupQ1.clear(); + + // Copy the saved HUD groups back in + mGroupQ1.assign(hudGroups.begin(), hudGroups.end()); + mGroupQ1Locked = false; + + // Clear the HUD groups + hudGroups.clear(); + + mGroupQ2Locked = true; + for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ2.begin(); + iter != mGroupQ2.end(); ++iter) + { + LLSpatialGroup* group = *iter; + + // If the group contains HUD objects, save the group + if (group->isHUDGroup()) + { + hudGroups.push_back(group); + } + // Else, no HUD objects so clear the build state + else + { + group->clearState(LLSpatialGroup::IN_BUILD_Q2); + } + } + // Clear the group + mGroupQ2.clear(); + + // Copy the saved HUD groups back in + mGroupQ2.assign(hudGroups.begin(), hudGroups.end()); + mGroupQ2Locked = false; +} + void LLPipeline::rebuildPriorityGroups() { LLFastTimer t(FTM_REBUILD_PRIORITY_GROUPS); @@ -2564,6 +2691,7 @@ void LLPipeline::rebuildPriorityGroups() group->clearState(LLSpatialGroup::IN_BUILD_Q1); } + mGroupSaveQ1 = mGroupQ1; mGroupQ1.clear(); mGroupQ1Locked = false; @@ -3469,7 +3597,7 @@ void LLPipeline::postSort(LLCamera& camera) rebuildPriorityGroups(); llpushcallstacks ; - + //build render map for (LLCullResult::sg_iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) { @@ -3541,7 +3669,7 @@ void LLPipeline::postSort(LLCamera& camera) } } } - + //flush particle VB LLVOPartGroup::sVB->flush(); @@ -6720,10 +6848,10 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable) LLFace* facep = drawable->getFace(i); if (facep) { - facep->clearVertexBuffer(); + facep->clearVertexBuffer(); + } } } -} void LLPipeline::resetVertexBuffers() { @@ -6762,6 +6890,8 @@ void LLPipeline::doResetVertexBuffers() gSky.resetVertexBuffers(); + LLVOPartGroup::destroyGL(); + if ( LLPathingLib::getInstance() ) { LLPathingLib::getInstance()->cleanupVBOManager(); @@ -7025,11 +7155,11 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) gGlowProgram.unbind(); - if (LLRenderTarget::sUseFBO) + /*if (LLRenderTarget::sUseFBO) { LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); glBindFramebuffer(GL_FRAMEBUFFER, 0); - } + }*/ gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; @@ -7096,15 +7226,8 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) } else { - LLViewerObject* obj = gAgentCamera.getFocusObject(); - if (obj) - { //focus on alt-zoom target - focus_point = LLVector3(gAgentCamera.getFocusGlobal()-gAgent.getRegion()->getOriginGlobal()); - } - else - { //focus on your avatar - focus_point = gAgent.getPositionAgent(); - } + //focus on alt-zoom target + focus_point = LLVector3(gAgentCamera.getFocusGlobal()-gAgent.getRegion()->getOriginGlobal()); } } @@ -7605,7 +7728,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n for (U32 i = 0; i < 4; i++) { - channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_TEXTURE); stop_glerror(); if (channel > -1) { @@ -7615,8 +7738,8 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); stop_glerror(); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); stop_glerror(); } } @@ -7696,13 +7819,13 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n matrix_nondiag, matrix_nondiag, matrix_diag}; shader.uniformMatrix3fv(LLShaderMgr::DEFERRED_SSAO_EFFECT_MAT, 1, GL_FALSE, ssao_effect_mat); - F32 shadow_offset_error = 1.f + RenderShadowOffsetError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); - F32 shadow_bias_error = 1.f + RenderShadowBiasError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + //F32 shadow_offset_error = 1.f + RenderShadowOffsetError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + F32 shadow_bias_error = RenderShadowBiasError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2])/3000.f; shader.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); shader.uniform1f(LLShaderMgr::DEFERRED_NEAR_CLIP, LLViewerCamera::getInstance()->getNear()*2.f); - shader.uniform1f (LLShaderMgr::DEFERRED_SHADOW_OFFSET, RenderShadowOffset*shadow_offset_error); - shader.uniform1f(LLShaderMgr::DEFERRED_SHADOW_BIAS, RenderShadowBias*shadow_bias_error); + shader.uniform1f (LLShaderMgr::DEFERRED_SHADOW_OFFSET, RenderShadowOffset); //*shadow_offset_error); + shader.uniform1f(LLShaderMgr::DEFERRED_SHADOW_BIAS, RenderShadowBias+shadow_bias_error); shader.uniform1f(LLShaderMgr::DEFERRED_SPOT_SHADOW_OFFSET, RenderSpotShadowOffset); shader.uniform1f(LLShaderMgr::DEFERRED_SPOT_SHADOW_BIAS, RenderSpotShadowBias); @@ -7912,10 +8035,6 @@ void LLPipeline::renderDeferredLighting() gGL.popMatrix(); stop_glerror(); - //copy depth and stencil from deferred screen - //mScreen.copyContents(mDeferredScreen, 0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight(), - // 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); - mScreen.bindTarget(); // clear color buffer here - zeroing alpha (glow) is important or it will accumulate against sky glClearColor(0,0,0,0); @@ -7993,7 +8112,7 @@ void LLPipeline::renderDeferredLighting() } mCubeVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - + LLGLDepthTest depth(GL_TRUE, GL_FALSE); for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); iter != mLights.end(); ++iter) { @@ -8039,7 +8158,7 @@ void LLPipeline::renderDeferredLighting() } sVisibleLightCount++; - + if (camera->getOrigin().mV[0] > c[0] + s + 0.2f || camera->getOrigin().mV[0] < c[0] - s - 0.2f || camera->getOrigin().mV[1] > c[1] + s + 0.2f || @@ -8426,9 +8545,9 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader) for (U32 i = 0; i < 4; i++) { - if (shader.disableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE) > -1) + if (shader.disableTexture(LLShaderMgr::DEFERRED_SHADOW0+i) > -1) { - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); } } @@ -8687,8 +8806,6 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) } last_update = LLDrawPoolWater::sNeedsReflectionUpdate && LLDrawPoolWater::sNeedsDistortionUpdate; - LLRenderTarget::unbindTarget(); - LLPipeline::sReflectionRender = FALSE; if (!LLRenderTarget::sUseFBO) @@ -8845,6 +8962,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera gGL.setColorMask(false, false); LLFastTimer ftm(FTM_SHADOW_SIMPLE); + gGL.getTexUnit(0)->disable(); for (U32 i = 0; i < sizeof(types)/sizeof(U32); ++i) { diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 368be1c14d..36abeca295 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -119,8 +119,25 @@ public: void createGLBuffers(); void createLUTBuffers(); - void allocateScreenBuffer(U32 resX, U32 resY); + //allocate the largest screen buffer possible up to resX, resY + //returns true if full size buffer allocated, false if some other size is allocated + bool allocateScreenBuffer(U32 resX, U32 resY); + + typedef enum { + FBO_SUCCESS_FULLRES = 0, + FBO_SUCCESS_LOWRES, + FBO_FAILURE + } eFBOStatus; + +private: + //implementation of above, wrapped for easy error handling + eFBOStatus doAllocateScreenBuffer(U32 resX, U32 resY); +public: + + //attempt to allocate screen buffers at resX, resY + //returns true if allocation successful, false otherwise bool allocateScreenBuffer(U32 resX, U32 resY, U32 samples); + void allocatePhysicsBuffer(); void resetVertexBuffers(LLDrawable* drawable); @@ -151,6 +168,8 @@ public: void unlinkDrawable(LLDrawable*); + static void removeMutedAVsLights(LLVOAvatar*); + // Object related methods void markVisible(LLDrawable *drawablep, LLCamera& camera); void markOccluder(LLSpatialGroup* group); @@ -223,6 +242,7 @@ public: void updateGL(); void rebuildPriorityGroups(); void rebuildGroups(); + void clearRebuildGroups(); //calculate pixel area of given box from vantage point of given camera static F32 calcPixelArea(LLVector3 center, LLVector3 size, LLCamera& camera); @@ -382,6 +402,7 @@ private: BOOL updateDrawableGeom(LLDrawable* drawable, BOOL priority); void assertInitializedDoError(); bool assertInitialized() { const bool is_init = isInit(); if (!is_init) assertInitializedDoError(); return is_init; }; + void connectRefreshCachedSettingsSafe(const std::string name); void hideDrawable( LLDrawable *pDrawable ); void unhideDrawable( LLDrawable *pDrawable ); public: @@ -664,6 +685,8 @@ protected: LLSpatialGroup::sg_vector_t mGroupQ1; //priority LLSpatialGroup::sg_vector_t mGroupQ2; // non-priority + LLSpatialGroup::sg_vector_t mGroupSaveQ1; // a place to save mGroupQ1 until it is safe to unref + LLSpatialGroup::sg_vector_t mMeshDirtyGroup; //groups that need rebuildMesh called U32 mMeshDirtyQueryObject; diff --git a/indra/newview/skins/default/xui/da/notifications.xml b/indra/newview/skins/default/xui/da/notifications.xml index cf6f1ccdd9..33b876bdb9 100644 --- a/indra/newview/skins/default/xui/da/notifications.xml +++ b/indra/newview/skins/default/xui/da/notifications.xml @@ -481,7 +481,6 @@ Du kan bruge [SECOND_LIFE] normalt og andre personer vil se dig korrekt. [APP_NAME] installationen er færdig. Hvis det er første gang du bruger [SECOND_LIFE], skal du først oprette en konto for at logge på. -Vend tilbage til [http://join.secondlife.com secondlife.com] for at oprette en ny konto? Der er problemer med at koble på. Der kan være et problem med din Internet forbindelse eller [SECOND_LIFE_GRID]. diff --git a/indra/newview/skins/default/xui/da/panel_login.xml b/indra/newview/skins/default/xui/da/panel_login.xml index 2e0f726e1a..b7cb76d4cb 100644 --- a/indra/newview/skins/default/xui/da/panel_login.xml +++ b/indra/newview/skins/default/xui/da/panel_login.xml @@ -1,13 +1,13 @@ - - http://join.secondlife.com/ - http://secondlife.com/account/request.php + + LOG PÅ + Brugernavn: @@ -15,15 +15,8 @@ Password: - -