# Conflicts:
#	.gitignore
#	indra/cmake/Copy3rdPartyLibs.cmake
#	indra/llcommon/llcommon.cpp
#	indra/llcommon/llerror.cpp
#	indra/llcommon/llprofiler.h
#	indra/llcommon/llthread.cpp
#	indra/llimage/llimageworker.cpp
#	indra/llprimitive/llmodel.h
#	indra/llrender/llfontgl.cpp
#	indra/llrender/llfontgl.h
#	indra/llrender/llgl.cpp
#	indra/llrender/llvertexbuffer.cpp
#	indra/llrender/llvertexbuffer.h
#	indra/llwindow/llwindowmacosx.h
#	indra/llwindow/llwindowwin32.cpp
#	indra/llwindow/llwindowwin32.h
#	indra/newview/app_settings/settings.xml
#	indra/newview/llappviewer.cpp
#	indra/newview/llappviewer.h
#	indra/newview/lldrawpoolavatar.cpp
#	indra/newview/lldrawpoolavatar.h
#	indra/newview/lldynamictexture.cpp
#	indra/newview/llfloatermodelpreview.cpp
#	indra/newview/llimview.cpp
#	indra/newview/llmeshrepository.h
#	indra/newview/llmodelpreview.cpp
#	indra/newview/llnetmap.cpp
#	indra/newview/llskinningutil.cpp
#	indra/newview/llskinningutil.h
#	indra/newview/llspatialpartition.cpp
#	indra/newview/llteleporthistory.cpp
#	indra/newview/llviewerdisplay.cpp
#	indra/newview/llviewerobject.cpp
#	indra/newview/llviewerobjectlist.cpp
#	indra/newview/llviewertexture.cpp
#	indra/newview/llviewertexturelist.cpp
#	indra/newview/llviewerwindow.cpp
#	indra/newview/llvoicevivox.cpp
#	indra/newview/llvosky.cpp
#	indra/newview/llvovolume.cpp
master
Ansariel 2021-11-12 15:21:52 +01:00
commit 7427b46ea5
168 changed files with 6600 additions and 4054 deletions

1
.gitignore vendored
View File

@ -24,6 +24,7 @@ build-vc100/
build-vc120/
build-vc[0-9]*-32*/
build-vc[0-9]*-64*/
build-vc160-64/
indra/CMakeFiles
indra/build-vc[0-9]*
indra/lib/mono/1.0/*.dll

View File

@ -273,6 +273,7 @@ Beq Janus
SL-14766
SL-14927
SL-15709
SL-16021
Beth Walcher
Bezilon Kasei
Biancaluce Robbiani

View File

@ -7,7 +7,7 @@ if (USE_TRACY)
set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy)
# See: indra/llcommon/llprofiler.h
add_definitions(-DLL_PROFILER_CONFIGURATION=2)
add_definitions(-DLL_PROFILER_CONFIGURATION=3)
use_prebuilt_binary(tracy)
# if (WINDOWS)

View File

@ -1,3 +1,4 @@
euclid 5/29/2020
euclid 7/23/2020
euclid 4/29/2021
euclid 4/29/2021
euclid 10/5/2021 DRTVWR-546

View File

@ -42,9 +42,11 @@
//-----------------------------------------------------------------------------
// class LLEditingMotion
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLEditingMotion :
public LLMotion
{
LL_ALIGN_NEW
public:
// Constructor
LLEditingMotion(const LLUUID &id);
@ -108,6 +110,13 @@ public:
//-------------------------------------------------------------------------
// joint states to be animated
//-------------------------------------------------------------------------
LL_ALIGN_16(LLJoint mParentJoint);
LL_ALIGN_16(LLJoint mShoulderJoint);
LL_ALIGN_16(LLJoint mElbowJoint);
LL_ALIGN_16(LLJoint mWristJoint);
LL_ALIGN_16(LLJoint mTarget);
LLJointSolverRP3 mIKSolver;
LLCharacter *mCharacter;
LLVector3 mWristOffset;
@ -117,17 +126,10 @@ public:
LLPointer<LLJointState> mWristState;
LLPointer<LLJointState> mTorsoState;
LLJoint mParentJoint;
LLJoint mShoulderJoint;
LLJoint mElbowJoint;
LLJoint mWristJoint;
LLJoint mTarget;
LLJointSolverRP3 mIKSolver;
static S32 sHandPose;
static S32 sHandPosePriority;
LLVector3 mLastSelectPt;
};
} LL_ALIGN_POSTFIX(16);
#endif // LL_LLKEYFRAMEMOTION_H

View File

@ -944,6 +944,13 @@ const LLMatrix4 &LLJoint::getWorldMatrix()
return mXform.getWorldMatrix();
}
const LLMatrix4a& LLJoint::getWorldMatrix4a()
{
updateWorldMatrixParent();
return mWorldMatrix;
}
//--------------------------------------------------------------------
// setWorldMatrix()
@ -1025,6 +1032,7 @@ void LLJoint::updateWorldMatrix()
{
sNumUpdates++;
mXform.updateMatrix(FALSE);
mWorldMatrix.loadu(mXform.getWorldMatrix());
mDirtyFlags = 0x0;
}
}

View File

@ -38,6 +38,7 @@
#include "m4math.h"
#include "llquaternion.h"
#include "xform.h"
#include "llmatrix4a.h"
//<FS:ND> Query by JointKey rather than just a string, the key can be a U32 index for faster lookup
struct JointKey
@ -111,8 +112,10 @@ inline bool operator!=(const LLVector3OverrideMap& a, const LLVector3OverrideMap
//-----------------------------------------------------------------------------
// class LLJoint
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLJoint
{
LL_ALIGN_NEW
public:
// priority levels, from highest to lowest
enum JointPriority
@ -140,16 +143,17 @@ public:
SUPPORT_EXTENDED
};
protected:
std::string mName;
// explicit transformation members
LL_ALIGN_16(LLMatrix4a mWorldMatrix);
LLXformMatrix mXform;
std::string mName;
SupportCategory mSupport;
// parent joint
LLJoint *mParent;
// explicit transformation members
LLXformMatrix mXform;
LLVector3 mDefaultPosition;
LLVector3 mDefaultScale;
@ -285,6 +289,8 @@ public:
const LLMatrix4 &getWorldMatrix();
void setWorldMatrix( const LLMatrix4& mat );
const LLMatrix4a& getWorldMatrix4a();
void updateWorldMatrixChildren();
void updateWorldMatrixParent();
@ -322,6 +328,6 @@ public:
// These are used in checks of whether a pos/scale override is considered significant.
bool aboveJointPosThreshold(const LLVector3& pos) const;
bool aboveJointScaleThreshold(const LLVector3& scale) const;
};
} LL_ALIGN_POSTFIX(16);
#endif // LL_LLJOINT_H

View File

@ -37,9 +37,11 @@
//-----------------------------------------------------------------------------
// class LLKeyframeStandMotion
//-----------------------------------------------------------------------------
LL_ALIGN_PREFIX(16)
class LLKeyframeStandMotion :
public LLKeyframeMotion
{
LL_ALIGN_NEW
public:
// Constructor
LLKeyframeStandMotion(const LLUUID &id);
@ -69,6 +71,18 @@ public:
//-------------------------------------------------------------------------
// Member Data
//-------------------------------------------------------------------------
LLJoint mPelvisJoint;
LLJoint mHipLeftJoint;
LLJoint mKneeLeftJoint;
LLJoint mAnkleLeftJoint;
LLJoint mTargetLeft;
LLJoint mHipRightJoint;
LLJoint mKneeRightJoint;
LLJoint mAnkleRightJoint;
LLJoint mTargetRight;
LLCharacter *mCharacter;
BOOL mFlipFeet;
@ -83,18 +97,6 @@ public:
LLPointer<LLJointState> mKneeRightState;
LLPointer<LLJointState> mAnkleRightState;
LLJoint mPelvisJoint;
LLJoint mHipLeftJoint;
LLJoint mKneeLeftJoint;
LLJoint mAnkleLeftJoint;
LLJoint mTargetLeft;
LLJoint mHipRightJoint;
LLJoint mKneeRightJoint;
LLJoint mAnkleRightJoint;
LLJoint mTargetRight;
LLJointSolverRP3 mIKLeft;
LLJointSolverRP3 mIKRight;
@ -110,7 +112,7 @@ public:
BOOL mTrackAnkles;
S32 mFrameNum;
};
} LL_ALIGN_POSTFIX(16);
#endif // LL_LLKEYFRAMESTANDMOTION_H

View File

@ -80,8 +80,10 @@ public:
const S32 JSB_NUM_JOINT_STATES = 6;
LL_ALIGN_PREFIX(16)
class LLJointStateBlender
{
LL_ALIGN_NEW
protected:
LLPointer<LLJointState> mJointStates[JSB_NUM_JOINT_STATES];
S32 mPriorities[JSB_NUM_JOINT_STATES];
@ -96,8 +98,8 @@ public:
void resetCachedJoint();
public:
LLJoint mJointCache;
};
LL_ALIGN_16(LLJoint mJointCache);
} LL_ALIGN_POSTFIX(16);
class LLMotion;

View File

@ -121,12 +121,14 @@ set(llcommon_SOURCE_FILES
llworkerthread.cpp
timing.cpp
u64.cpp
workqueue.cpp
StackWalker.cpp
)
set(llcommon_HEADER_FILES
CMakeLists.txt
chrono.h
ctype_workaround.h
fix_macros.h
indra_constants.h
@ -254,8 +256,11 @@ set(llcommon_HEADER_FILES
lockstatic.h
stdtypes.h
stringize.h
threadsafeschedule.h
timer.h
tuple.h
u64.h
workqueue.h
StackWalker.h
)
@ -398,6 +403,9 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(threadsafeschedule "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(tuple "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(workqueue "" "${test_libs}")
## llexception_test.cpp isn't a regression test, and doesn't need to be run
## every build. It's to help a developer make implementation choices about

65
indra/llcommon/chrono.h Normal file
View File

@ -0,0 +1,65 @@
/**
* @file chrono.h
* @author Nat Goodspeed
* @date 2021-10-05
* @brief supplement <chrono> with utility functions
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_CHRONO_H)
#define LL_CHRONO_H
#include <chrono>
#include <type_traits> // std::enable_if
namespace LL
{
// time_point_cast() is derived from https://stackoverflow.com/a/35293183
// without the iteration: we think errors in the ~1 microsecond range are
// probably acceptable.
// This variant is for the optimal case when the source and dest use the same
// clock: that case is handled by std::chrono.
template <typename DestTimePoint, typename SrcTimePoint,
typename std::enable_if<std::is_same<typename DestTimePoint::clock,
typename SrcTimePoint::clock>::value,
bool>::type = true>
DestTimePoint time_point_cast(const SrcTimePoint& time)
{
return std::chrono::time_point_cast<typename DestTimePoint::duration>(time);
}
// This variant is for when the source and dest use different clocks -- see
// the linked StackOverflow answer, also Howard Hinnant's, for more context.
template <typename DestTimePoint, typename SrcTimePoint,
typename std::enable_if<! std::is_same<typename DestTimePoint::clock,
typename SrcTimePoint::clock>::value,
bool>::type = true>
DestTimePoint time_point_cast(const SrcTimePoint& time)
{
// The basic idea is that we must adjust the passed time_point by the
// difference between the clocks' epochs. But since time_point doesn't
// expose its epoch, we fall back on what each of them thinks is now().
// However, since we necessarily make sequential calls to those now()
// functions, the answers differ not only by the cycles spent executing
// those calls, but by potential OS interruptions between them. Try to
// reduce that error by capturing the source clock time both before and
// after the dest clock, and splitting the difference. Of course an
// interruption between two of these now() calls without a comparable
// interruption between the other two will skew the result, but better is
// more expensive.
const auto src_before = typename SrcTimePoint::clock::now();
const auto dest_now = typename DestTimePoint::clock::now();
const auto src_after = typename SrcTimePoint::clock::now();
const auto src_diff = src_after - src_before;
const auto src_now = src_before + src_diff / 2;
return dest_now + (time - src_now);
}
} // namespace LL
#endif /* ! defined(LL_CHRONO_H) */

View File

@ -33,12 +33,23 @@
#include "lltracethreadrecorder.h"
#include "llcleanup.h"
thread_local bool gProfilerEnabled = false;
// <FS:Beq/> #if (TRACY_ENABLE)
#if (TRACY_ENABLE) && LL_PROFILER_ENABLE_TRACY_MEMORY
// Override new/delete for tracy memory profiling
void *operator new(size_t size)
{
auto ptr = (malloc) (size);
void* ptr;
if (gProfilerEnabled)
{
LL_PROFILE_ZONE_SCOPED;
ptr = (malloc)(size);
}
else
{
ptr = (malloc)(size);
}
if (!ptr)
{
throw std::bad_alloc();
@ -50,7 +61,15 @@ void *operator new(size_t size)
void operator delete(void *ptr) noexcept
{
TracyFree(ptr);
(free)(ptr);
if (gProfilerEnabled)
{
LL_PROFILE_ZONE_SCOPED;
(free)(ptr);
}
else
{
(free)(ptr);
}
}
// C-style malloc/free can't be so easily overridden, so we define tracy versions and use

View File

@ -53,6 +53,8 @@ private:
LLCoros::Mutex mMutex;
// Use LLCoros::ConditionVariable for the same reason.
LLCoros::ConditionVariable mCond;
using LockType = LLCoros::LockType;
using cv_status = LLCoros::cv_status;
public:
/// LLCond can be explicitly initialized with a specific value for mData if
@ -65,10 +67,14 @@ public:
LLCond(const LLCond&) = delete;
LLCond& operator=(const LLCond&) = delete;
/// get() returns a const reference to the stored DATA. The only way to
/// get a non-const reference -- to modify the stored DATA -- is via
/// update_one() or update_all().
const value_type& get() const { return mData; }
/// get() returns the stored DATA by value -- so to use get(), DATA must
/// be copyable. The only way to get a non-const reference -- to modify
/// the stored DATA -- is via update_one() or update_all().
value_type get()
{
LockType lk(mMutex);
return mData;
}
/**
* Pass update_one() an invocable accepting non-const (DATA&). The
@ -83,7 +89,7 @@ public:
void update_one(MODIFY modify)
{
{ // scope of lock can/should end before notify_one()
LLCoros::LockType lk(mMutex);
LockType lk(mMutex);
modify(mData);
}
mCond.notify_one();
@ -102,7 +108,7 @@ public:
void update_all(MODIFY modify)
{
{ // scope of lock can/should end before notify_all()
LLCoros::LockType lk(mMutex);
LockType lk(mMutex);
modify(mData);
}
mCond.notify_all();
@ -118,7 +124,7 @@ public:
template <typename Pred>
void wait(Pred pred)
{
LLCoros::LockType lk(mMutex);
LockType lk(mMutex);
// We must iterate explicitly since the predicate accepted by
// condition_variable::wait() requires a different signature:
// condition_variable::wait() calls its predicate with no arguments.
@ -205,14 +211,14 @@ private:
template <typename Clock, typename Duration, typename Pred>
bool wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time, Pred pred)
{
LLCoros::LockType lk(mMutex);
LockType lk(mMutex);
// We advise the caller to pass a predicate accepting (const DATA&).
// But what if they instead pass a predicate accepting non-const
// (DATA&)? Such a predicate could modify mData, which would be Bad.
// Forbid that.
while (! pred(const_cast<const value_type&>(mData)))
{
if (LLCoros::cv_status::timeout == mCond.wait_until(lk, timeout_time))
if (cv_status::timeout == mCond.wait_until(lk, timeout_time))
{
// It's possible that wait_until() timed out AND the predicate
// became true more or less simultaneously. Even though

View File

@ -109,6 +109,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED
int syslogPriority = LOG_CRIT;
switch (level) {
case LLError::LEVEL_DEBUG: syslogPriority = LOG_DEBUG; break;
@ -168,6 +169,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED
if (LLError::getAlwaysFlush())
{
mFile << message << std::endl;
@ -234,6 +236,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED
// The default colors for error, warn and debug are now a bit more pastel
// and easier to read on the default (black) terminal background but you
// now have the option to set the color of each via an environment variables:
@ -263,7 +266,8 @@ namespace {
}
else
{
fprintf(stderr, "%s\n", message.c_str());
LL_PROFILE_ZONE_NAMED("fprintf");
fprintf(stderr, "%s\n", message.c_str());
}
#if LL_WINDOWS
fflush(stderr); //Now using a buffer. flush is required.
@ -275,6 +279,7 @@ namespace {
LL_FORCE_INLINE void writeANSI(const std::string& ansi_code, const std::string& message)
{
LL_PROFILE_ZONE_SCOPED
static std::string s_ansi_bold = createBoldANSI(); // bold text
static std::string s_ansi_reset = createResetANSI(); // reset
// ANSI color code escape sequence, message, and reset in one fprintf call
@ -311,6 +316,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED
mBuffer->addLine(message);
}
@ -337,6 +343,7 @@ namespace {
virtual void recordMessage(LLError::ELevel level,
const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED
debugger_print(message);
}
};
@ -1230,6 +1237,7 @@ namespace
void writeToRecorders(const LLError::CallSite& site, const std::string& message)
{
LL_PROFILE_ZONE_SCOPED
LLError::ELevel level = site.mLevel;
LLError::SettingsConfigPtr s = LLError::Settings::getInstance()->getSettingsConfig();
@ -1370,6 +1378,7 @@ namespace LLError
bool Log::shouldLog(CallSite& site)
{
LL_PROFILE_ZONE_SCOPED
LLMutexTrylock lock(getMutex<LOG_MUTEX>(), 5);
if (!lock.isLocked())
{
@ -1413,6 +1422,7 @@ namespace LLError
void Log::flush(const std::ostringstream& out, const CallSite& site)
{
LL_PROFILE_ZONE_SCOPED
LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5);
if (!lock.isLocked())
{

View File

@ -35,7 +35,9 @@
#include "stdtypes.h"
#include "llprofiler.h"
#include "llpreprocessor.h"
#include <boost/static_assert.hpp>
// <FS:ND> Supress some false positives of PVS Studio.
@ -354,7 +356,8 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL;
#define lllog(level, once, ...) \
do { \
do { \
LL_PROFILE_ZONE_NAMED("lllog"); \
const char* tags[] = {"", ##__VA_ARGS__}; \
static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \
lllog_test_()

View File

@ -190,6 +190,7 @@ namespace LLError
{}
void recordMessage(LLError::ELevel level, const std::string& message) override
{
LL_PROFILE_ZONE_SCOPED
mCallable(level, message);
}
private:

View File

@ -83,13 +83,34 @@ class LLInstanceTracker
typedef llthread::LockStatic<StaticData> LockStatic;
public:
using ptr_t = std::shared_ptr<T>;
using weak_t = std::weak_ptr<T>;
/**
* Storing a dumb T* somewhere external is a bad idea, since
* LLInstanceTracker subclasses are explicitly destroyed rather than
* managed by smart pointers. It's legal to declare stack instances of an
* LLInstanceTracker subclass. But it's reasonable to store a
* std::weak_ptr<T>, which will become invalid when the T instance is
* destroyed.
*/
weak_t getWeak()
{
return mSelf;
}
static S32 instanceCount()
{
return LockStatic()->mMap.size();
}
// snapshot of std::pair<const KEY, std::shared_ptr<T>> pairs
class snapshot
{
// It's very important that what we store in this snapshot are
// weak_ptrs, NOT shared_ptrs. That's how we discover whether any
// instance has been deleted during the lifespan of a snapshot.
typedef std::vector<std::pair<const KEY, std::weak_ptr<T>>> VectorType;
typedef std::vector<std::pair<const KEY, weak_t>> VectorType;
// Dereferencing our iterator produces a std::shared_ptr for each
// instance that still exists. Since we store weak_ptrs, that involves
// two chained transformations:
@ -98,7 +119,7 @@ public:
// It is very important that we filter lazily, that is, during
// traversal. Any one of our stored weak_ptrs might expire during
// traversal.
typedef std::pair<const KEY, std::shared_ptr<T>> strong_pair;
typedef std::pair<const KEY, ptr_t> strong_pair;
// Note for future reference: nat has not yet had any luck (up to
// Boost 1.67) trying to use boost::transform_iterator with a hand-
// coded functor, only with actual functions. In my experience, an
@ -202,17 +223,12 @@ public:
iterator end() { return iterator(snapshot::end(), key_getter); }
};
static T* getInstance(const KEY& k)
static ptr_t getInstance(const KEY& k)
{
LockStatic lock;
const InstanceMap& map(lock->mMap);
typename InstanceMap::const_iterator found = map.find(k);
return (found == map.end()) ? NULL : found->second.get();
}
static S32 instanceCount()
{
return LockStatic()->mMap.size();
return (found == map.end()) ? NULL : found->second;
}
protected:
@ -222,7 +238,9 @@ protected:
// shared_ptr, so give it a no-op deleter. We store shared_ptrs in our
// InstanceMap specifically so snapshot can store weak_ptrs so we can
// detect deletions during traversals.
std::shared_ptr<T> ptr(static_cast<T*>(this), [](T*){});
ptr_t ptr(static_cast<T*>(this), [](T*){});
// save corresponding weak_ptr for future reference
mSelf = ptr;
LockStatic lock;
add_(lock, key, ptr);
}
@ -257,7 +275,7 @@ private:
static std::string report(const char* key) { return report(std::string(key)); }
// caller must instantiate LockStatic
void add_(LockStatic& lock, const KEY& key, const std::shared_ptr<T>& ptr)
void add_(LockStatic& lock, const KEY& key, const ptr_t& ptr)
{
mInstanceKey = key;
InstanceMap& map = lock->mMap;
@ -281,7 +299,7 @@ private:
break;
}
}
std::shared_ptr<T> remove_(LockStatic& lock)
ptr_t remove_(LockStatic& lock)
{
InstanceMap& map = lock->mMap;
typename InstanceMap::iterator iter = map.find(mInstanceKey);
@ -295,6 +313,9 @@ private:
}
private:
// Storing a weak_ptr to self is a bit like deriving from
// std::enable_shared_from_this(), except more explicit.
weak_t mSelf;
KEY mInstanceKey;
};
@ -326,6 +347,9 @@ class LLInstanceTracker<T, void, KEY_COLLISION_BEHAVIOR>
typedef llthread::LockStatic<StaticData> LockStatic;
public:
using ptr_t = std::shared_ptr<T>;
using weak_t = std::weak_ptr<T>;
/**
* Storing a dumb T* somewhere external is a bad idea, since
* LLInstanceTracker subclasses are explicitly destroyed rather than
@ -334,12 +358,15 @@ public:
* std::weak_ptr<T>, which will become invalid when the T instance is
* destroyed.
*/
std::weak_ptr<T> getWeak()
weak_t getWeak()
{
return mSelf;
}
static S32 instanceCount() { return LockStatic()->mSet.size(); }
static S32 instanceCount()
{
return LockStatic()->mSet.size();
}
// snapshot of std::shared_ptr<T> pointers
class snapshot
@ -347,7 +374,7 @@ public:
// It's very important that what we store in this snapshot are
// weak_ptrs, NOT shared_ptrs. That's how we discover whether any
// instance has been deleted during the lifespan of a snapshot.
typedef std::vector<std::weak_ptr<T>> VectorType;
typedef std::vector<weak_t> VectorType;
// Dereferencing our iterator produces a std::shared_ptr for each
// instance that still exists. Since we store weak_ptrs, that involves
// two chained transformations:
@ -453,7 +480,7 @@ protected:
private:
// Storing a weak_ptr to self is a bit like deriving from
// std::enable_shared_from_this(), except more explicit.
std::weak_ptr<T> mSelf;
weak_t mSelf;
};
#endif

View File

@ -220,7 +220,7 @@ void LLLeapListener::getAPI(const LLSD& request) const
{
Response reply(LLSD(), request);
LLEventAPI* found = LLEventAPI::getInstance(request["api"]);
auto found = LLEventAPI::getInstance(request["api"]);
if (found)
{
reply["name"] = found->getName();

View File

@ -82,6 +82,7 @@ void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size)
//static
void LLMemory::updateMemoryInfo()
{
LL_PROFILE_ZONE_SCOPED
#if LL_WINDOWS
PROCESS_MEMORY_COUNTERS counters;
@ -145,6 +146,7 @@ void* LLMemory::tryToAlloc(void* address, U32 size)
//static
void LLMemory::logMemoryInfo(BOOL update)
{
LL_PROFILE_ZONE_SCOPED
if(update)
{
updateMemoryInfo() ;

View File

@ -101,6 +101,19 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16)
#define LL_ALIGN_NEW \
public: \
void* operator new(size_t size) \
{ \
return ll_aligned_malloc_16(size); \
} \
\
void operator delete(void* ptr) \
{ \
ll_aligned_free_16(ptr); \
}
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
// for enable buffer overrun detection predefine LL_DEBUG_BUFFER_OVERRUN in current library

View File

@ -44,6 +44,7 @@ LLMutex::~LLMutex()
void LLMutex::lock()
{
LL_PROFILE_ZONE_SCOPED
if(isSelfLocked())
{ //redundant lock
mCount++;
@ -65,6 +66,7 @@ void LLMutex::lock()
void LLMutex::unlock()
{
LL_PROFILE_ZONE_SCOPED
if (mCount > 0)
{ //not the root unlock
mCount--;
@ -85,6 +87,7 @@ void LLMutex::unlock()
bool LLMutex::isLocked()
{
LL_PROFILE_ZONE_SCOPED
if (!mMutex.try_lock())
{
return true;
@ -108,6 +111,7 @@ LLThread::id_t LLMutex::lockingThread() const
bool LLMutex::trylock()
{
LL_PROFILE_ZONE_SCOPED
if(isSelfLocked())
{ //redundant lock
mCount++;
@ -146,17 +150,20 @@ LLCondition::~LLCondition()
void LLCondition::wait()
{
LL_PROFILE_ZONE_SCOPED
std::unique_lock< std::mutex > lock(mMutex);
mCond.wait(lock);
}
void LLCondition::signal()
{
LL_PROFILE_ZONE_SCOPED
mCond.notify_one();
}
void LLCondition::broadcast()
{
LL_PROFILE_ZONE_SCOPED
mCond.notify_all();
}
@ -166,6 +173,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex)
: mMutex(mutex),
mLocked(false)
{
LL_PROFILE_ZONE_SCOPED
if (mMutex)
mLocked = mMutex->trylock();
}
@ -174,6 +182,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms)
: mMutex(mutex),
mLocked(false)
{
LL_PROFILE_ZONE_SCOPED
if (!mMutex)
return;
@ -188,6 +197,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms)
LLMutexTrylock::~LLMutexTrylock()
{
LL_PROFILE_ZONE_SCOPED
if (mMutex && mLocked)
mMutex->unlock();
}
@ -199,6 +209,7 @@ LLMutexTrylock::~LLMutexTrylock()
//
LLScopedLock::LLScopedLock(std::mutex* mutex) : mMutex(mutex)
{
LL_PROFILE_ZONE_SCOPED
if(mutex)
{
mutex->lock();
@ -217,6 +228,7 @@ LLScopedLock::~LLScopedLock()
void LLScopedLock::unlock()
{
LL_PROFILE_ZONE_SCOPED
if(mLocked)
{
mMutex->unlock();

View File

@ -36,12 +36,8 @@
#define LL_PROFILER_CONFIGURATION LL_PROFILER_CONFIG_FAST_TIMER
#endif
// <FS:Beq> active switch for deferred profiling
namespace LLProfiler
{
extern bool active;
}
// </FS:Beq>
extern thread_local bool gProfilerEnabled;
#if defined(LL_PROFILER_CONFIGURATION) && (LL_PROFILER_CONFIGURATION > LL_PROFILER_CONFIG_NONE)
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#define TRACY_ENABLE 1
@ -60,7 +56,7 @@ namespace LLProfiler
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY
#define LL_PROFILER_FRAME_END FrameMark
#define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name )
#define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name ); gProfilerEnabled = true;
#define LL_PROFILER_THREAD_BEGIN(name) FrameMarkStart( name ) // C string
#define LL_PROFILER_THREAD_END(name) FrameMarkEnd( name ) // C string
// <FS:Beq> revert change that obscures custom FTM zones. We may want to may FTM Zones unique in future.
@ -120,7 +116,7 @@ namespace LLProfiler
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#define LL_PROFILER_FRAME_END FrameMark
#define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name )
#define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name ); gProfilerEnabled = true;
#define LL_PROFILER_THREAD_BEGIN(name) FrameMarkStart( name ) // C string
#define LL_PROFILER_THREAD_END(name) FrameMarkEnd( name ) // C string

View File

@ -400,6 +400,7 @@ namespace
ImplMap& ImplMap::makeMap(LLSD::Impl*& var)
{
LL_PROFILE_ZONE_SCOPED;
if (shared())
{
ImplMap* i = new ImplMap(mData);
@ -414,18 +415,21 @@ namespace
bool ImplMap::has(const LLSD::String& k) const
{
LL_PROFILE_ZONE_SCOPED;
DataMap::const_iterator i = mData.find(k);
return i != mData.end();
}
LLSD ImplMap::get(const LLSD::String& k) const
{
LL_PROFILE_ZONE_SCOPED;
DataMap::const_iterator i = mData.find(k);
return (i != mData.end()) ? i->second : LLSD();
}
LLSD ImplMap::getKeys() const
{
LL_PROFILE_ZONE_SCOPED;
LLSD keys = LLSD::emptyArray();
DataMap::const_iterator iter = mData.begin();
while (iter != mData.end())
@ -438,11 +442,13 @@ namespace
void ImplMap::insert(const LLSD::String& k, const LLSD& v)
{
LL_PROFILE_ZONE_SCOPED;
mData.insert(DataMap::value_type(k, v));
}
void ImplMap::erase(const LLSD::String& k)
{
LL_PROFILE_ZONE_SCOPED;
mData.erase(k);
}
@ -684,6 +690,7 @@ const LLSD::Impl& LLSD::Impl::safe(const Impl* impl)
ImplMap& LLSD::Impl::makeMap(Impl*& var)
{
LL_PROFILE_ZONE_SCOPED;
ImplMap* im = new ImplMap;
reset(var, im);
return *im;
@ -887,11 +894,16 @@ LLSD& LLSD::with(const String& k, const LLSD& v)
}
void LLSD::erase(const String& k) { makeMap(impl).erase(k); }
LLSD& LLSD::operator[](const String& k)
{ return makeMap(impl).ref(k); }
LLSD& LLSD::operator[](const String& k)
{
LL_PROFILE_ZONE_SCOPED;
return makeMap(impl).ref(k);
}
const LLSD& LLSD::operator[](const String& k) const
{ return safe(impl).ref(k); }
{
LL_PROFILE_ZONE_SCOPED;
return safe(impl).ref(k);
}
LLSD LLSD::emptyArray()
{
@ -914,10 +926,16 @@ LLSD& LLSD::with(Integer i, const LLSD& v)
LLSD& LLSD::append(const LLSD& v) { return makeArray(impl).append(v); }
void LLSD::erase(Integer i) { makeArray(impl).erase(i); }
LLSD& LLSD::operator[](Integer i)
{ return makeArray(impl).ref(i); }
LLSD& LLSD::operator[](Integer i)
{
LL_PROFILE_ZONE_SCOPED;
return makeArray(impl).ref(i);
}
const LLSD& LLSD::operator[](Integer i) const
{ return safe(impl).ref(i); }
{
LL_PROFILE_ZONE_SCOPED;
return safe(impl).ref(i);
}
static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
{

View File

@ -290,9 +290,17 @@ public:
LLSD& with(const String&, const LLSD&);
LLSD& operator[](const String&);
LLSD& operator[](const char* c) { return (*this)[String(c)]; }
LLSD& operator[](const char* c)
{
LL_PROFILE_ZONE_SCOPED;
return (*this)[String(c)];
}
const LLSD& operator[](const String&) const;
const LLSD& operator[](const char* c) const { return (*this)[String(c)]; }
const LLSD& operator[](const char* c) const
{
LL_PROFILE_ZONE_SCOPED;
return (*this)[String(c)];
}
//@}
/** @name Array Values */

View File

@ -496,6 +496,7 @@ public:
static DERIVED_TYPE* getInstance()
{
LL_PROFILE_ZONE_SCOPED;
// We know the viewer has LLSingleton dependency circularities. If you
// feel strongly motivated to eliminate them, cheers and good luck.
// (At that point we could consider a much simpler locking mechanism.)

View File

@ -869,6 +869,7 @@ LLSD LLMemoryInfo::getStatsMap() const
LLMemoryInfo& LLMemoryInfo::refresh()
{
LL_PROFILE_ZONE_SCOPED
mStatsMap = loadStatsMap();
LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n";

View File

@ -374,12 +374,14 @@ void LLThread::setQuitting()
// <FS:Beq> give this a better chance to inline
// LLThread::id_t LLThread::currentID()
// {
// LL_PROFILE_ZONE_SCOPED
// return std::this_thread::get_id();
// }
// static
void LLThread::yield()
{
LL_PROFILE_ZONE_SCOPED
std::this_thread::yield();
}
@ -399,6 +401,7 @@ void LLThread::wake()
void LLThread::wakeLocked()
{
LL_PROFILE_ZONE_SCOPED
if(!shouldSleep())
{
mRunCondition->signal();
@ -407,11 +410,13 @@ void LLThread::wakeLocked()
void LLThread::lockData()
{
LL_PROFILE_ZONE_SCOPED
mDataLock->lock();
}
void LLThread::unlockData()
{
LL_PROFILE_ZONE_SCOPED
mDataLock->unlock();
}

View File

@ -1,6 +1,6 @@
/**
* @file llthreadsafequeue.h
* @brief Base classes for thread, mutex and condition handling.
* @brief Queue protected with mutexes for cross-thread use
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
@ -27,16 +27,19 @@
#ifndef LL_LLTHREADSAFEQUEUE_H
#define LL_LLTHREADSAFEQUEUE_H
#include "llexception.h"
#include <deque>
#include <string>
#include <chrono>
#include "mutex.h"
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
#include <boost/fiber/timed_mutex.hpp>
#include LLCOROS_CONDVAR_HEADER
#include "llexception.h"
#include "mutex.h"
#include <chrono>
#include <queue>
#include <string>
/*****************************************************************************
* LLThreadSafeQueue
*****************************************************************************/
//
// A general queue exception.
//
@ -66,70 +69,111 @@ public:
}
};
//
// Implements a thread safe FIFO.
//
template<typename ElementT>
/**
* Implements a thread safe FIFO.
*/
// Let the default std::queue default to underlying std::deque. Override if
// desired.
template<typename ElementT, typename QueueT=std::queue<ElementT>>
class LLThreadSafeQueue
{
public:
typedef ElementT value_type;
// If the pool is set to NULL one will be allocated and managed by this
// queue.
// Limiting the number of pending items prevents unbounded growth of the
// underlying queue.
LLThreadSafeQueue(U32 capacity = 1024);
// Add an element to the front of queue (will block if the queue has
virtual ~LLThreadSafeQueue() {}
// Add an element to the queue (will block if the queue has
// reached capacity).
//
// This call will raise an interrupt error if the queue is closed while
// the caller is blocked.
void pushFront(ElementT const & element);
// Try to add an element to the front of queue without blocking. Returns
// true only if the element was actually added.
bool tryPushFront(ElementT const & element);
template <typename T>
void push(T&& element);
// legacy name
void pushFront(ElementT const & element) { return push(element); }
// Try to add an element to the front of queue, blocking if full but with
// timeout. Returns true if the element was added.
// Try to add an element to the queue without blocking. Returns
// true only if the element was actually added.
template <typename T>
bool tryPush(T&& element);
// legacy name
bool tryPushFront(ElementT const & element) { return tryPush(element); }
// Try to add an element to the queue, blocking if full but with timeout
// after specified duration. Returns true if the element was added.
// There are potentially two different timeouts involved: how long to try
// to lock the mutex, versus how long to wait for the queue to stop being
// full. Careful settings for each timeout might be orders of magnitude
// apart. However, this method conflates them.
template <typename Rep, typename Period, typename T>
bool tryPushFor(const std::chrono::duration<Rep, Period>& timeout,
T&& element);
// legacy name
template <typename Rep, typename Period>
bool tryPushFrontFor(const std::chrono::duration<Rep, Period>& timeout,
ElementT const & element);
ElementT const & element) { return tryPushFor(timeout, element); }
// Pop the element at the end of the queue (will block if the queue is
// Try to add an element to the queue, blocking if full but with
// timeout at specified time_point. Returns true if the element was added.
template <typename Clock, typename Duration, typename T>
bool tryPushUntil(const std::chrono::time_point<Clock, Duration>& until,
T&& element);
// no legacy name because this is a newer method
// Pop the element at the head of the queue (will block if the queue is
// empty).
//
// This call will raise an interrupt error if the queue is closed while
// the caller is blocked.
ElementT popBack(void);
// Pop an element from the end of the queue if there is one available.
ElementT pop(void);
// legacy name
ElementT popBack(void) { return pop(); }
// Pop an element from the head of the queue if there is one available.
// Returns true only if an element was popped.
bool tryPopBack(ElementT & element);
bool tryPop(ElementT & element);
// legacy name
bool tryPopBack(ElementT & element) { return tryPop(element); }
// Pop the element at the head of the queue, blocking if empty, with
// timeout after specified duration. Returns true if an element was popped.
template <typename Rep, typename Period>
bool tryPopFor(const std::chrono::duration<Rep, Period>& timeout, ElementT& element);
// no legacy name because this is a newer method
// Pop the element at the head of the queue, blocking if empty, with
// timeout at specified time_point. Returns true if an element was popped.
template <typename Clock, typename Duration>
bool tryPopUntil(const std::chrono::time_point<Clock, Duration>& until,
ElementT& element);
// no legacy name because this is a newer method
// Returns the size of the queue.
size_t size();
//Returns the capacity of the queue.
U32 capacity() { return mCapacity; }
// closes the queue:
// - every subsequent pushFront() call will throw LLThreadSafeQueueInterrupt
// - every subsequent tryPushFront() call will return false
// - popBack() calls will return normally until the queue is drained, then
// every subsequent popBack() will throw LLThreadSafeQueueInterrupt
// - tryPopBack() calls will return normally until the queue is drained,
// then every subsequent tryPopBack() call will return false
// - every subsequent push() call will throw LLThreadSafeQueueInterrupt
// - every subsequent tryPush() call will return false
// - pop() calls will return normally until the queue is drained, then
// every subsequent pop() will throw LLThreadSafeQueueInterrupt
// - tryPop() calls will return normally until the queue is drained,
// then every subsequent tryPop() call will return false
void close();
// detect closed state
// producer end: are we prevented from pushing any additional items?
bool isClosed();
// inverse of isClosed()
explicit operator bool();
// consumer end: are we done, is the queue entirely drained?
bool done();
private:
std::deque< ElementT > mStorage;
protected:
typedef QueueT queue_type;
QueueT mStorage;
U32 mCapacity;
bool mClosed;
@ -137,37 +181,152 @@ private:
typedef std::unique_lock<decltype(mLock)> lock_t;
boost::fibers::condition_variable_any mCapacityCond;
boost::fibers::condition_variable_any mEmptyCond;
enum pop_result { EMPTY, DONE, WAITING, POPPED };
// implementation logic, suitable for passing to tryLockUntil()
template <typename Clock, typename Duration>
pop_result tryPopUntil_(lock_t& lock,
const std::chrono::time_point<Clock, Duration>& until,
ElementT& element);
// if we're able to lock immediately, do so and run the passed callable,
// which must accept lock_t& and return bool
template <typename CALLABLE>
bool tryLock(CALLABLE&& callable);
// if we're able to lock before the passed time_point, do so and run the
// passed callable, which must accept lock_t& and return bool
template <typename Clock, typename Duration, typename CALLABLE>
bool tryLockUntil(const std::chrono::time_point<Clock, Duration>& until,
CALLABLE&& callable);
// while lock is locked, really push the passed element, if we can
template <typename T>
bool push_(lock_t& lock, T&& element);
// while lock is locked, really pop the head element, if we can
pop_result pop_(lock_t& lock, ElementT& element);
// Is the current head element ready to pop? We say yes; subclass can
// override as needed.
virtual bool canPop(const ElementT& head) const { return true; }
};
// LLThreadSafeQueue
//-----------------------------------------------------------------------------
/*****************************************************************************
* PriorityQueueAdapter
*****************************************************************************/
namespace LL
{
/**
* std::priority_queue's API is almost like std::queue, intentionally of
* course, but you must access the element about to pop() as top() rather
* than as front(). Make an adapter for use with LLThreadSafeQueue.
*/
template <typename T, typename Container=std::vector<T>,
typename Compare=std::less<typename Container::value_type>>
class PriorityQueueAdapter
{
public:
// publish all the same types
typedef std::priority_queue<T, Container, Compare> queue_type;
typedef typename queue_type::container_type container_type;
typedef typename queue_type::value_compare value_compare;
typedef typename queue_type::value_type value_type;
typedef typename queue_type::size_type size_type;
typedef typename queue_type::reference reference;
typedef typename queue_type::const_reference const_reference;
template<typename ElementT>
LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(U32 capacity) :
// Although std::queue defines both const and non-const front()
// methods, std::priority_queue defines only const top().
const_reference front() const { return mQ.top(); }
// std::priority_queue has no equivalent to back(), so it's good that
// LLThreadSafeQueue doesn't use it.
// All the rest of these merely forward to the corresponding
// queue_type methods.
bool empty() const { return mQ.empty(); }
size_type size() const { return mQ.size(); }
void push(const value_type& value) { mQ.push(value); }
void push(value_type&& value) { mQ.push(std::move(value)); }
template <typename... Args>
void emplace(Args&&... args) { mQ.emplace(std::forward<Args>(args)...); }
void pop() { mQ.pop(); }
private:
queue_type mQ;
};
} // namespace LL
/*****************************************************************************
* LLThreadSafeQueue implementation
*****************************************************************************/
template<typename ElementT, typename QueueT>
LLThreadSafeQueue<ElementT, QueueT>::LLThreadSafeQueue(U32 capacity) :
mCapacity(capacity),
mClosed(false)
{
}
template<typename ElementT>
void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element)
// if we're able to lock immediately, do so and run the passed callable, which
// must accept lock_t& and return bool
template <typename ElementT, typename QueueT>
template <typename CALLABLE>
bool LLThreadSafeQueue<ElementT, QueueT>::tryLock(CALLABLE&& callable)
{
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock())
return false;
return std::forward<CALLABLE>(callable)(lock1);
}
// if we're able to lock before the passed time_point, do so and run the
// passed callable, which must accept lock_t& and return bool
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration, typename CALLABLE>
bool LLThreadSafeQueue<ElementT, QueueT>::tryLockUntil(
const std::chrono::time_point<Clock, Duration>& until,
CALLABLE&& callable)
{
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock_until(until))
return false;
return std::forward<CALLABLE>(callable)(lock1);
}
// while lock is locked, really push the passed element, if we can
template <typename ElementT, typename QueueT>
template <typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::push_(lock_t& lock, T&& element)
{
if (mStorage.size() >= mCapacity)
return false;
mStorage.push(std::forward<T>(element));
lock.unlock();
// now that we've pushed, if somebody's been waiting to pop, signal them
mEmptyCond.notify_one();
return true;
}
template <typename ElementT, typename QueueT>
template<typename T>
void LLThreadSafeQueue<ElementT, QueueT>::push(T&& element)
{
lock_t lock1(mLock);
while (true)
{
// On the producer side, it doesn't matter whether the queue has been
// drained or not: the moment either end calls close(), further push()
// operations will fail.
if (mClosed)
{
LLTHROW(LLThreadSafeQueueInterrupt());
}
if (mStorage.size() < mCapacity)
{
mStorage.push_front(element);
lock1.unlock();
mEmptyCond.notify_one();
if (push_(lock1, std::forward<T>(element)))
return;
}
// Storage Full. Wait for signal.
mCapacityCond.wait(lock1);
@ -175,39 +334,184 @@ void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element)
}
template <typename ElementT>
template <typename Rep, typename Period>
bool LLThreadSafeQueue<ElementT>::tryPushFrontFor(const std::chrono::duration<Rep, Period>& timeout,
ElementT const & element)
template<typename ElementT, typename QueueT>
template<typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPush(T&& element)
{
return tryLock(
[this, element=std::move(element)](lock_t& lock)
{
if (mClosed)
return false;
return push_(lock, std::move(element));
});
}
template <typename ElementT, typename QueueT>
template <typename Rep, typename Period, typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPushFor(
const std::chrono::duration<Rep, Period>& timeout,
T&& element)
{
// Convert duration to time_point: passing the same timeout duration to
// each of multiple calls is wrong.
auto endpoint = std::chrono::steady_clock::now() + timeout;
return tryPushUntil(std::chrono::steady_clock::now() + timeout,
std::forward<T>(element));
}
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock_until(endpoint))
return false;
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration, typename T>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPushUntil(
const std::chrono::time_point<Clock, Duration>& until,
T&& element)
{
return tryLockUntil(
until,
[this, until, element=std::move(element)](lock_t& lock)
{
while (true)
{
if (mClosed)
{
return false;
}
if (push_(lock, std::move(element)))
return true;
// Storage Full. Wait for signal.
if (LLCoros::cv_status::timeout == mCapacityCond.wait_until(lock, until))
{
// timed out -- formally we might recheck both conditions above
return false;
}
// If we didn't time out, we were notified for some reason. Loop back
// to check.
}
});
}
// while lock is locked, really pop the head element, if we can
template <typename ElementT, typename QueueT>
typename LLThreadSafeQueue<ElementT, QueueT>::pop_result
LLThreadSafeQueue<ElementT, QueueT>::pop_(lock_t& lock, ElementT& element)
{
// If mStorage is empty, there's no head element.
if (mStorage.empty())
return mClosed? DONE : EMPTY;
// If there's a head element, pass it to canPop() to see if it's ready to pop.
if (! canPop(mStorage.front()))
return WAITING;
// std::queue::front() is the element about to pop()
element = mStorage.front();
mStorage.pop();
lock.unlock();
// now that we've popped, if somebody's been waiting to push, signal them
mCapacityCond.notify_one();
return POPPED;
}
template<typename ElementT, typename QueueT>
ElementT LLThreadSafeQueue<ElementT, QueueT>::pop(void)
{
lock_t lock1(mLock);
ElementT value;
while (true)
{
if (mClosed)
// On the consumer side, we always try to pop before checking mClosed
// so we can finish draining the queue.
pop_result popped = pop_(lock1, value);
if (popped == POPPED)
return std::move(value);
// Once the queue is DONE, there will never be any more coming.
if (popped == DONE)
{
return false;
LLTHROW(LLThreadSafeQueueInterrupt());
}
if (mStorage.size() < mCapacity)
// If we didn't pop because WAITING, i.e. canPop() returned false,
// then even if the producer end has been closed, there's still at
// least one item to drain: wait for it. Or we might be EMPTY, with
// the queue still open. Either way, wait for signal.
mEmptyCond.wait(lock1);
}
}
template<typename ElementT, typename QueueT>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPop(ElementT & element)
{
return tryLock(
[this, &element](lock_t& lock)
{
mStorage.push_front(element);
lock1.unlock();
mEmptyCond.notify_one();
return true;
// conflate EMPTY, DONE, WAITING: tryPop() behavior when the queue
// is closed is implemented by simple inability to push any new
// elements
return pop_(lock, element) == POPPED;
});
}
template <typename ElementT, typename QueueT>
template <typename Rep, typename Period>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPopFor(
const std::chrono::duration<Rep, Period>& timeout,
ElementT& element)
{
// Convert duration to time_point: passing the same timeout duration to
// each of multiple calls is wrong.
return tryPopUntil(std::chrono::steady_clock::now() + timeout, element);
}
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration>
bool LLThreadSafeQueue<ElementT, QueueT>::tryPopUntil(
const std::chrono::time_point<Clock, Duration>& until,
ElementT& element)
{
return tryLockUntil(
until,
[this, until, &element](lock_t& lock)
{
// conflate EMPTY, DONE, WAITING
return tryPopUntil_(lock, until, element) == POPPED;
});
}
// body of tryPopUntil(), called once we have the lock
template <typename ElementT, typename QueueT>
template <typename Clock, typename Duration>
typename LLThreadSafeQueue<ElementT, QueueT>::pop_result
LLThreadSafeQueue<ElementT, QueueT>::tryPopUntil_(
lock_t& lock,
const std::chrono::time_point<Clock, Duration>& until,
ElementT& element)
{
while (true)
{
pop_result popped = pop_(lock, element);
if (popped == POPPED || popped == DONE)
{
// If we succeeded, great! If we've drained the last item, so be
// it. Either way, break the loop and tell caller.
return popped;
}
// Storage Full. Wait for signal.
if (LLCoros::cv_status::timeout == mCapacityCond.wait_until(lock1, endpoint))
// EMPTY or WAITING: wait for signal.
if (LLCoros::cv_status::timeout == mEmptyCond.wait_until(lock, until))
{
// timed out -- formally we might recheck both conditions above
return false;
// timed out -- formally we might recheck
// as it is, break loop
return popped;
}
// If we didn't time out, we were notified for some reason. Loop back
// to check.
@ -215,102 +519,40 @@ bool LLThreadSafeQueue<ElementT>::tryPushFrontFor(const std::chrono::duration<Re
}
template<typename ElementT>
bool LLThreadSafeQueue<ElementT>::tryPushFront(ElementT const & element)
{
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock())
return false;
if (mClosed)
return false;
if (mStorage.size() >= mCapacity)
return false;
mStorage.push_front(element);
lock1.unlock();
mEmptyCond.notify_one();
return true;
}
template<typename ElementT>
ElementT LLThreadSafeQueue<ElementT>::popBack(void)
{
lock_t lock1(mLock);
while (true)
{
if (!mStorage.empty())
{
ElementT value = mStorage.back();
mStorage.pop_back();
lock1.unlock();
mCapacityCond.notify_one();
return value;
}
if (mClosed)
{
LLTHROW(LLThreadSafeQueueInterrupt());
}
// Storage empty. Wait for signal.
mEmptyCond.wait(lock1);
}
}
template<typename ElementT>
bool LLThreadSafeQueue<ElementT>::tryPopBack(ElementT & element)
{
lock_t lock1(mLock, std::defer_lock);
if (!lock1.try_lock())
return false;
// no need to check mClosed: tryPopBack() behavior when the queue is
// closed is implemented by simple inability to push any new elements
if (mStorage.empty())
return false;
element = mStorage.back();
mStorage.pop_back();
lock1.unlock();
mCapacityCond.notify_one();
return true;
}
template<typename ElementT>
size_t LLThreadSafeQueue<ElementT>::size(void)
template<typename ElementT, typename QueueT>
size_t LLThreadSafeQueue<ElementT, QueueT>::size(void)
{
lock_t lock(mLock);
return mStorage.size();
}
template<typename ElementT>
void LLThreadSafeQueue<ElementT>::close()
template<typename ElementT, typename QueueT>
void LLThreadSafeQueue<ElementT, QueueT>::close()
{
lock_t lock(mLock);
mClosed = true;
lock.unlock();
// wake up any blocked popBack() calls
// wake up any blocked pop() calls
mEmptyCond.notify_all();
// wake up any blocked pushFront() calls
// wake up any blocked push() calls
mCapacityCond.notify_all();
}
template<typename ElementT>
bool LLThreadSafeQueue<ElementT>::isClosed()
template<typename ElementT, typename QueueT>
bool LLThreadSafeQueue<ElementT, QueueT>::isClosed()
{
lock_t lock(mLock);
return mClosed && mStorage.size() == 0;
return mClosed;
}
template<typename ElementT>
LLThreadSafeQueue<ElementT>::operator bool()
template<typename ElementT, typename QueueT>
bool LLThreadSafeQueue<ElementT, QueueT>::done()
{
return ! isClosed();
lock_t lock(mLock);
return mClosed && mStorage.empty();
}
#endif

View File

@ -201,6 +201,17 @@ struct FSUUIDHash
};
// </FS:Ansariel> UUID hash calculation
// Adapt boost hash to std hash
namespace std
{
template<> struct hash<LLUUID>
{
std::size_t operator()(LLUUID const& s) const noexcept
{
return boost::hash<LLUUID>()(s);
}
};
}
#endif

View File

@ -90,19 +90,19 @@ namespace tut
{
Keyed one("one");
ensure_equals(Keyed::instanceCount(), 1);
Keyed* found = Keyed::getInstance("one");
ensure("couldn't find stack Keyed", found);
ensure_equals("found wrong Keyed instance", found, &one);
auto found = Keyed::getInstance("one");
ensure("couldn't find stack Keyed", bool(found));
ensure_equals("found wrong Keyed instance", found.get(), &one);
{
boost::scoped_ptr<Keyed> two(new Keyed("two"));
ensure_equals(Keyed::instanceCount(), 2);
Keyed* found = Keyed::getInstance("two");
ensure("couldn't find heap Keyed", found);
ensure_equals("found wrong Keyed instance", found, two.get());
auto found = Keyed::getInstance("two");
ensure("couldn't find heap Keyed", bool(found));
ensure_equals("found wrong Keyed instance", found.get(), two.get());
}
ensure_equals(Keyed::instanceCount(), 1);
}
Keyed* found = Keyed::getInstance("one");
auto found = Keyed::getInstance("one");
ensure("Keyed key lives too long", ! found);
ensure_equals(Keyed::instanceCount(), 0);
}

View File

@ -356,14 +356,15 @@ namespace tut
// Create a script file in a temporary place.
NamedTempFile script("py",
"from __future__ import print_function" EOL
"import sys" EOL
"import time" EOL
EOL
"time.sleep(2)" EOL
"print >>sys.stdout, 'stdout after wait'" EOL
"print('stdout after wait',file=sys.stdout)" EOL
"sys.stdout.flush()" EOL
"time.sleep(2)" EOL
"print >>sys.stderr, 'stderr after wait'" EOL
"print('stderr after wait',file=sys.stderr)" EOL
"sys.stderr.flush()" EOL
);
@ -568,12 +569,12 @@ namespace tut
{
set_test_name("arguments");
PythonProcessLauncher py(get_test_name(),
"from __future__ import with_statement\n"
"from __future__ import with_statement, print_function\n"
"import sys\n"
// note nonstandard output-file arg!
"with open(sys.argv[3], 'w') as f:\n"
" for arg in sys.argv[1:]:\n"
" print >>f, arg\n");
" print(arg,file=f)\n");
// We expect that PythonProcessLauncher has already appended
// its own NamedTempFile to mParams.args (sys.argv[0]).
py.mParams.args.add("first arg"); // sys.argv[1]
@ -857,7 +858,8 @@ namespace tut
set_test_name("'bogus' test");
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
"print 'Hello world'\n");
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam("bogus"));
py.mPy = LLProcess::create(py.mParams);
ensure("should have rejected 'bogus'", ! py.mPy);
@ -872,7 +874,8 @@ namespace tut
// Replace this test with one or more real 'file' tests when we
// implement 'file' support
PythonProcessLauncher py(get_test_name(),
"print 'Hello world'\n");
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("file"));
py.mPy = LLProcess::create(py.mParams);
@ -887,7 +890,8 @@ namespace tut
// implement 'tpipe' support
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
"print 'Hello world'\n");
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("tpipe"));
py.mPy = LLProcess::create(py.mParams);
@ -904,7 +908,8 @@ namespace tut
// implement 'npipe' support
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
"print 'Hello world'\n");
"from __future__ import print_function\n"
"print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("npipe"));
@ -980,7 +985,8 @@ namespace tut
{
set_test_name("get*Pipe() validation");
PythonProcessLauncher py(get_test_name(),
"print 'this output is expected'\n");
"from __future__ import print_function\n"
"print('this output is expected')\n");
py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stdin
py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr
@ -1000,14 +1006,15 @@ namespace tut
{
set_test_name("talk to stdin/stdout");
PythonProcessLauncher py(get_test_name(),
"from __future__ import print_function\n"
"import sys, time\n"
"print 'ok'\n"
"print('ok')\n"
"sys.stdout.flush()\n"
"# wait for 'go' from test program\n"
"go = sys.stdin.readline()\n"
"if go != 'go\\n':\n"
" sys.exit('expected \"go\", saw %r' % go)\n"
"print 'ack'\n");
"print('ack')\n");
py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
py.launch();
@ -1118,7 +1125,8 @@ namespace tut
{
set_test_name("ReadPipe \"eof\" event");
PythonProcessLauncher py(get_test_name(),
"print 'Hello from Python!'\n");
"from __future__ import print_function\n"
"print('Hello from Python!')\n");
py.mParams.files.add(LLProcess::FileParam()); // stdin
py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
py.launch();

View File

@ -0,0 +1,69 @@
/**
* @file threadsafeschedule_test.cpp
* @author Nat Goodspeed
* @date 2021-10-04
* @brief Test for threadsafeschedule.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "threadsafeschedule.h"
// STL headers
// std headers
#include <chrono>
// external library headers
// other Linden headers
#include "../test/lltut.h"
using namespace std::literals::chrono_literals; // ms suffix
using namespace std::literals::string_literals; // s suffix
using Queue = LL::ThreadSafeSchedule<std::string>;
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct threadsafeschedule_data
{
Queue queue;
};
typedef test_group<threadsafeschedule_data> threadsafeschedule_group;
typedef threadsafeschedule_group::object object;
threadsafeschedule_group threadsafeschedulegrp("threadsafeschedule");
template<> template<>
void object::test<1>()
{
set_test_name("push");
// Simply calling push() a few times might result in indeterminate
// delivery order if the resolution of steady_clock is coarser than
// the real time required for each push() call. Explicitly increment
// the timestamp for each one -- but since we're passing explicit
// timestamps, make the queue reorder them.
queue.push(Queue::TimeTuple(Queue::Clock::now() + 20ms, "ghi"));
// Given the various push() overloads, you have to match the type
// exactly: conversions are ambiguous.
queue.push("abc"s);
queue.push(Queue::Clock::now() + 10ms, "def");
queue.close();
auto entry = queue.pop();
ensure_equals("failed to pop first", std::get<0>(entry), "abc"s);
entry = queue.pop();
ensure_equals("failed to pop second", std::get<0>(entry), "def"s);
ensure("queue not closed", queue.isClosed());
ensure("queue prematurely done", ! queue.done());
std::string s;
bool popped = queue.tryPopFor(1s, s);
ensure("failed to pop third", popped);
ensure_equals("third is wrong", s, "ghi"s);
popped = queue.tryPop(s);
ensure("queue not empty", ! popped);
ensure("queue not done", queue.done());
}
} // namespace tut

View File

@ -0,0 +1,47 @@
/**
* @file tuple_test.cpp
* @author Nat Goodspeed
* @date 2021-10-04
* @brief Test for tuple.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "tuple.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct tuple_data
{
};
typedef test_group<tuple_data> tuple_group;
typedef tuple_group::object object;
tuple_group tuplegrp("tuple");
template<> template<>
void object::test<1>()
{
set_test_name("tuple");
std::tuple<std::string, int> tup{ "abc", 17 };
std::tuple<int, std::string, int> ptup{ tuple_cons(34, tup) };
std::tuple<std::string, int> tup2;
int i;
std::tie(i, tup2) = tuple_split(ptup);
ensure_equals("tuple_car() fail", i, 34);
ensure_equals("tuple_cdr() (0) fail", std::get<0>(tup2), "abc");
ensure_equals("tuple_cdr() (1) fail", std::get<1>(tup2), 17);
}
} // namespace tut

View File

@ -0,0 +1,159 @@
/**
* @file workqueue_test.cpp
* @author Nat Goodspeed
* @date 2021-10-07
* @brief Test for workqueue.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "workqueue.h"
// STL headers
// std headers
#include <chrono>
#include <deque>
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "llcond.h"
#include "llstring.h"
#include "stringize.h"
using namespace LL;
using namespace std::literals::chrono_literals; // ms suffix
using namespace std::literals::string_literals; // s suffix
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct workqueue_data
{
WorkQueue queue{"queue"};
};
typedef test_group<workqueue_data> workqueue_group;
typedef workqueue_group::object object;
workqueue_group workqueuegrp("workqueue");
template<> template<>
void object::test<1>()
{
set_test_name("name");
ensure_equals("didn't capture name", queue.getKey(), "queue");
ensure("not findable", WorkQueue::getInstance("queue") == queue.getWeak().lock());
WorkQueue q2;
ensure("has no name", LLStringUtil::startsWith(q2.getKey(), "WorkQueue"));
}
template<> template<>
void object::test<2>()
{
set_test_name("post");
bool wasRun{ false };
// We only get away with binding a simple bool because we're running
// the work on the same thread.
queue.post([&wasRun](){ wasRun = true; });
queue.close();
ensure("ran too soon", ! wasRun);
queue.runUntilClose();
ensure("didn't run", wasRun);
}
template<> template<>
void object::test<3>()
{
set_test_name("postEvery");
// record of runs
using Shared = std::deque<WorkQueue::TimePoint>;
// This is an example of how to share data between the originator of
// postEvery(work) and the work item itself, since usually a WorkQueue
// is used to dispatch work to a different thread. Neither of them
// should call any of LLCond's wait methods: you don't want to stall
// either the worker thread or the originating thread (conventionally
// main). Use LLCond or a subclass even if all you want to do is
// signal the work item that it can quit; consider LLOneShotCond.
LLCond<Shared> data;
auto start = WorkQueue::TimePoint::clock::now();
auto interval = 100ms;
queue.postEvery(
interval,
[&data, count = 0]
() mutable
{
// record the timestamp at which this instance is running
data.update_one(
[](Shared& data)
{
data.push_back(WorkQueue::TimePoint::clock::now());
});
// by the 3rd call, return false to stop
return (++count < 3);
});
// no convenient way to close() our queue while we've got a
// postEvery() running, so run until we think we should have exhausted
// the iterations
queue.runFor(10*interval);
// Take a copy of the captured deque.
Shared result = data.get();
ensure_equals("called wrong number of times", result.size(), 3);
// postEvery() assumes you want the first call to happen right away.
// Pretend our start time was (interval) earlier than that, to make
// our too early/too late tests uniform for all entries.
start -= interval;
for (size_t i = 0; i < result.size(); ++i)
{
auto diff = result[i] - start;
start += interval;
try
{
ensure(STRINGIZE("call " << i << " too soon"), diff >= interval);
ensure(STRINGIZE("call " << i << " too late"), diff < interval*1.5);
}
catch (const tut::failure&)
{
auto interval_ms = interval / 1ms;
auto diff_ms = diff / 1ms;
std::cerr << "interval " << interval_ms
<< "ms; diff " << diff_ms << "ms" << std::endl;
throw;
}
}
}
template<> template<>
void object::test<4>()
{
set_test_name("postTo");
WorkQueue main("main");
auto qptr = WorkQueue::getInstance("queue");
int result = 0;
main.postTo(
qptr,
[](){ return 17; },
// Note that a postTo() *callback* can safely bind a reference to
// a variable on the invoking thread, because the callback is run
// on the invoking thread.
[&result](int i){ result = i; });
// this should post the callback to main
qptr->runOne();
// this should run the callback
main.runOne();
ensure_equals("failed to run int callback", result, 17);
std::string alpha;
// postTo() handles arbitrary return types
main.postTo(
qptr,
[](){ return "abc"s; },
[&alpha](const std::string& s){ alpha = s; });
qptr->runPending();
main.runPending();
ensure_equals("failed to run string callback", alpha, "abc");
}
} // namespace tut

View File

@ -0,0 +1,373 @@
/**
* @file threadsafeschedule.h
* @author Nat Goodspeed
* @date 2021-10-02
* @brief ThreadSafeSchedule is an ordered queue in which every item has an
* associated timestamp.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_THREADSAFESCHEDULE_H)
#define LL_THREADSAFESCHEDULE_H
#include "chrono.h"
#include "llexception.h"
#include "llthreadsafequeue.h"
#include "tuple.h"
#include <chrono>
#include <tuple>
namespace LL
{
namespace ThreadSafeSchedulePrivate
{
using TimePoint = std::chrono::steady_clock::time_point;
// Bundle consumer's data with a TimePoint to order items by timestamp.
template <typename... Args>
using TimestampedTuple = std::tuple<TimePoint, Args...>;
// comparison functor for TimedTuples -- see TimedQueue comments
struct ReverseTupleOrder
{
template <typename Tuple>
bool operator()(const Tuple& left, const Tuple& right) const
{
return std::get<0>(left) > std::get<0>(right);
}
};
template <typename... Args>
using TimedQueue = PriorityQueueAdapter<
TimestampedTuple<Args...>,
// std::vector is the default storage for std::priority_queue,
// have to restate to specify comparison template parameter
std::vector<TimestampedTuple<Args...>>,
// std::priority_queue uses a counterintuitive comparison
// behavior: the default std::less comparator is used to present
// the *highest* value as top(). So to sort by earliest timestamp,
// we must invert by using >.
ReverseTupleOrder>;
} // namespace ThreadSafeSchedulePrivate
/**
* ThreadSafeSchedule is an ordered LLThreadSafeQueue in which every item
* is given an associated timestamp. That is, TimePoint is implicitly
* prepended to the std::tuple with the specified types.
*
* Items are popped in increasing chronological order. Moreover, any item
* with a timestamp in the future is held back until
* std::chrono::steady_clock reaches that timestamp.
*/
template <typename... Args>
class ThreadSafeSchedule:
public LLThreadSafeQueue<ThreadSafeSchedulePrivate::TimestampedTuple<Args...>,
ThreadSafeSchedulePrivate::TimedQueue<Args...>>
{
public:
using DataTuple = std::tuple<Args...>;
using TimeTuple = ThreadSafeSchedulePrivate::TimestampedTuple<Args...>;
private:
using super = LLThreadSafeQueue<TimeTuple, ThreadSafeSchedulePrivate::TimedQueue<Args...>>;
using lock_t = typename super::lock_t;
// VS 2017 needs this due to a bug:
// https://developercommunity.visualstudio.com/t/cannot-access-protected-enumerator-of-enclosing-cl/203430
enum pop_result { EMPTY=super::EMPTY, DONE=super::DONE, WAITING=super::WAITING, POPPED=super::POPPED };
public:
using Closed = LLThreadSafeQueueInterrupt;
using TimePoint = ThreadSafeSchedulePrivate::TimePoint;
using Clock = TimePoint::clock;
ThreadSafeSchedule(U32 capacity=1024):
super(capacity)
{}
/*----------------------------- push() -----------------------------*/
/// explicitly pass TimeTuple
using super::push;
/// pass DataTuple with implicit now
// This could be ambiguous for Args with a single type. Unfortunately
// we can't enable_if an individual method with a condition based on
// the *class* template arguments, only on that method's template
// arguments. We could specialize this class for the single-Args case;
// we could minimize redundancy by breaking out a common base class...
void push(const DataTuple& tuple)
{
push(tuple_cons(Clock::now(), tuple));
}
/// individually pass each component of the TimeTuple
void push(const TimePoint& time, Args&&... args)
{
push(TimeTuple(time, std::forward<Args>(args)...));
}
/// individually pass every component except the TimePoint (implies now)
// This could be ambiguous if the first specified template parameter
// type is also TimePoint. We could try to disambiguate, but a simpler
// approach would be for the caller to explicitly construct DataTuple
// and call that overload.
void push(Args&&... args)
{
push(Clock::now(), std::forward<Args>(args)...);
}
/*--------------------------- tryPush() ----------------------------*/
/// explicit TimeTuple
using super::tryPush;
/// DataTuple with implicit now
bool tryPush(const DataTuple& tuple)
{
return tryPush(tuple_cons(Clock::now(), tuple));
}
/// individually pass components
bool tryPush(const TimePoint& time, Args&&... args)
{
return tryPush(TimeTuple(time, std::forward<Args>(args)...));
}
/// individually pass components with implicit now
bool tryPush(Args&&... args)
{
return tryPush(Clock::now(), std::forward<Args>(args)...);
}
/*-------------------------- tryPushFor() --------------------------*/
/// explicit TimeTuple
using super::tryPushFor;
/// DataTuple with implicit now
template <typename Rep, typename Period>
bool tryPushFor(const std::chrono::duration<Rep, Period>& timeout,
const DataTuple& tuple)
{
return tryPushFor(timeout, tuple_cons(Clock::now(), tuple));
}
/// individually pass components
template <typename Rep, typename Period>
bool tryPushFor(const std::chrono::duration<Rep, Period>& timeout,
const TimePoint& time, Args&&... args)
{
return tryPushFor(TimeTuple(time, std::forward<Args>(args)...));
}
/// individually pass components with implicit now
template <typename Rep, typename Period>
bool tryPushFor(const std::chrono::duration<Rep, Period>& timeout,
Args&&... args)
{
return tryPushFor(Clock::now(), std::forward<Args>(args)...);
}
/*------------------------- tryPushUntil() -------------------------*/
/// explicit TimeTuple
using super::tryPushUntil;
/// DataTuple with implicit now
template <typename Clock, typename Duration>
bool tryPushUntil(const std::chrono::time_point<Clock, Duration>& until,
const DataTuple& tuple)
{
return tryPushUntil(until, tuple_cons(Clock::now(), tuple));
}
/// individually pass components
template <typename Clock, typename Duration>
bool tryPushUntil(const std::chrono::time_point<Clock, Duration>& until,
const TimePoint& time, Args&&... args)
{
return tryPushUntil(until, TimeTuple(time, std::forward<Args>(args)...));
}
/// individually pass components with implicit now
template <typename Clock, typename Duration>
bool tryPushUntil(const std::chrono::time_point<Clock, Duration>& until,
Args&&... args)
{
return tryPushUntil(until, Clock::now(), std::forward<Args>(args)...);
}
/*----------------------------- pop() ------------------------------*/
// Our consumer may or may not care about the timestamp associated
// with each popped item, so we allow retrieving either DataTuple or
// TimeTuple. One potential use would be to observe, and possibly
// adjust for, the time lag between the item time and the actual
// current time.
/// pop DataTuple by value
// It would be great to notice when sizeof...(Args) == 1 and directly
// return the first (only) value, instead of making pop()'s caller
// call std::get<0>(value). See push(DataTuple) remarks for why we
// haven't yet jumped through those hoops.
DataTuple pop()
{
return tuple_cdr(popWithTime());
}
/// pop TimeTuple by value
TimeTuple popWithTime()
{
lock_t lock(super::mLock);
// We can't just sit around waiting forever, given that there may
// be items in the queue that are not yet ready but will *become*
// ready in the near future. So in fact, with this class, every
// pop() becomes a tryPopUntil(), constrained to the timestamp of
// the head item. It almost doesn't matter what we specify for the
// caller's time constraint -- all we really care about is the
// head item's timestamp. Since pop() and popWithTime() are
// defined to wait until either an item becomes available or the
// queue is closed, loop until one of those things happens. The
// constraint we pass just determines how often we'll loop while
// waiting.
TimeTuple tt;
while (true)
{
// Pick a point suitably far into the future.
TimePoint until = TimePoint::clock::now() + std::chrono::hours(24);
pop_result popped = tryPopUntil_(lock, until, tt);
if (popped == POPPED)
return std::move(tt);
// DONE: throw, just as super::pop() does
if (popped == DONE)
{
LLTHROW(LLThreadSafeQueueInterrupt());
}
// WAITING: we've still got items to drain.
// EMPTY: not closed, so it's worth waiting for more items.
// Either way, loop back to wait.
}
}
// We can use tryPop(TimeTuple&) just as it stands; the only behavior
// difference is in our canPop() override method.
using super::tryPop;
/// tryPop(DataTuple&)
bool tryPop(DataTuple& tuple)
{
TimeTuple tt;
if (! super::tryPop(tt))
return false;
tuple = tuple_cdr(std::move(tt));
return true;
}
/// for when Args has exactly one type
bool tryPop(typename std::tuple_element<1, TimeTuple>::type& value)
{
TimeTuple tt;
if (! super::tryPop(tt))
return false;
value = std::get<1>(std::move(tt));
return true;
}
/// tryPopFor()
template <typename Rep, typename Period, typename Tuple>
bool tryPopFor(const std::chrono::duration<Rep, Period>& timeout, Tuple& tuple)
{
// It's important to use OUR tryPopUntil() implementation, rather
// than delegating immediately to our base class.
return tryPopUntil(Clock::now() + timeout, tuple);
}
/// tryPopUntil(TimeTuple&)
template <typename Clock, typename Duration>
bool tryPopUntil(const std::chrono::time_point<Clock, Duration>& until,
TimeTuple& tuple)
{
// super::tryPopUntil() wakes up when an item becomes available or
// we hit 'until', whichever comes first. Thing is, the current
// head of the queue could become ready sooner than either of
// those events, and we need to deliver it as soon as it does.
// Don't wait past the TimePoint of the head item.
// Naturally, lock the queue before peeking at mStorage.
return super::tryLockUntil(
until,
[this, until, &tuple](lock_t& lock)
{
// Use our time_point_cast to allow for 'until' that's a
// time_point type other than TimePoint.
return POPPED ==
tryPopUntil_(lock, LL::time_point_cast<TimePoint>(until), tuple);
});
}
pop_result tryPopUntil_(lock_t& lock, const TimePoint& until, TimeTuple& tuple)
{
TimePoint adjusted = until;
if (! super::mStorage.empty())
{
// use whichever is earlier: the head item's timestamp, or
// the caller's limit
adjusted = min(std::get<0>(super::mStorage.front()), adjusted);
}
// now delegate to base-class tryPopUntil_()
pop_result popped;
while ((popped = pop_result(super::tryPopUntil_(lock, adjusted, tuple))) == WAITING)
{
// If super::tryPopUntil_() returns WAITING, it means there's
// a head item, but it's not yet time. But it's worth looping
// back to recheck.
}
return popped;
}
/// tryPopUntil(DataTuple&)
template <typename Clock, typename Duration>
bool tryPopUntil(const std::chrono::time_point<Clock, Duration>& until,
DataTuple& tuple)
{
TimeTuple tt;
if (! tryPopUntil(until, tt))
return false;
tuple = tuple_cdr(std::move(tt));
return true;
}
/// for when Args has exactly one type
template <typename Clock, typename Duration>
bool tryPopUntil(const std::chrono::time_point<Clock, Duration>& until,
typename std::tuple_element<1, TimeTuple>::type& value)
{
TimeTuple tt;
if (! tryPopUntil(until, tt))
return false;
value = std::get<1>(std::move(tt));
return true;
}
/*------------------------------ etc. ------------------------------*/
// We can't hide items that aren't yet ready because we can't traverse
// the underlying priority_queue: it has no iterators, only top(). So
// a consumer could observe size() > 0 and yet tryPop() returns false.
// Shrug, in a multi-consumer scenario that would be expected behavior.
using super::size;
// open/closed state
using super::close;
using super::isClosed;
using super::done;
private:
// this method is called by base class pop_() every time we're
// considering whether to deliver the current head element
bool canPop(const TimeTuple& head) const override
{
// an item with a future timestamp isn't yet ready to pop
// (should we add some slop for overhead?)
return std::get<0>(head) <= Clock::now();
}
};
} // namespace LL
#endif /* ! defined(LL_THREADSAFESCHEDULE_H) */

84
indra/llcommon/tuple.h Normal file
View File

@ -0,0 +1,84 @@
/**
* @file tuple.h
* @author Nat Goodspeed
* @date 2021-10-04
* @brief A couple tuple utilities
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_TUPLE_H)
#define LL_TUPLE_H
#include <tuple>
#include <type_traits> // std::remove_reference
#include <utility> // std::pair
/**
* tuple_cons() behaves like LISP cons: it uses std::tuple_cat() to prepend a
* new item of arbitrary type to an existing std::tuple.
*/
template <typename First, typename... Rest, typename Tuple_=std::tuple<Rest...>>
auto tuple_cons(First&& first, Tuple_&& rest)
{
// All we need to do is make a tuple containing 'first', and let
// tuple_cat() do the hard part.
return std::tuple_cat(std::tuple<First>(std::forward<First>(first)),
std::forward<Tuple_>(rest));
}
/**
* tuple_car() behaves like LISP car: it extracts the first item from a
* std::tuple.
*/
template <typename... Args, typename Tuple_=std::tuple<Args...>>
auto tuple_car(Tuple_&& tuple)
{
return std::get<0>(std::forward<Tuple_>(tuple));
}
/**
* tuple_cdr() behaves like LISP cdr: it returns a new tuple containing
* everything BUT the first item.
*/
// derived from https://stackoverflow.com/a/24046437
template <typename Tuple, std::size_t... Indices>
auto tuple_cdr_(Tuple&& tuple, const std::index_sequence<Indices...>)
{
// Given an index sequence from [0..N-1), extract tuple items [1..N)
return std::make_tuple(std::get<Indices+1u>(std::forward<Tuple>(tuple))...);
}
template <typename Tuple>
auto tuple_cdr(Tuple&& tuple)
{
return tuple_cdr_(
std::forward<Tuple>(tuple),
// Pass helper function an index sequence one item shorter than tuple
std::make_index_sequence<
std::tuple_size<
// tuple_size doesn't like reference types
typename std::remove_reference<Tuple>::type
>::value - 1u>
());
}
/**
* tuple_split(), the opposite of tuple_cons(), has no direct analog in LISP.
* It returns a std::pair of tuple_car(), tuple_cdr(). We could call this
* function tuple_car_cdr(), or tuple_slice() or some such. But tuple_split()
* feels more descriptive.
*/
template <typename... Args, typename Tuple_=std::tuple<Args...>>
auto tuple_split(Tuple_&& tuple)
{
// We're not really worried about forwarding multiple times a tuple that
// might contain move-only items, because the implementation above only
// applies std::get() exactly once to each item.
return std::make_pair(tuple_car(std::forward<Tuple_>(tuple)),
tuple_cdr(std::forward<Tuple_>(tuple)));
}
#endif /* ! defined(LL_TUPLE_H) */

View File

@ -0,0 +1,130 @@
/**
* @file workqueue.cpp
* @author Nat Goodspeed
* @date 2021-10-06
* @brief Implementation for WorkQueue.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "workqueue.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
#include "llerror.h"
#include "llexception.h"
#include "stringize.h"
using Mutex = LLCoros::Mutex;
using Lock = LLCoros::LockType;
LL::WorkQueue::WorkQueue(const std::string& name):
super(makeName(name))
{
// TODO: register for "LLApp" events so we can implicitly close() on
// viewer shutdown.
}
void LL::WorkQueue::close()
{
mQueue.close();
}
void LL::WorkQueue::runUntilClose()
{
try
{
for (;;)
{
callWork(mQueue.pop());
}
}
catch (const Queue::Closed&)
{
}
}
bool LL::WorkQueue::runPending()
{
LL_PROFILE_ZONE_SCOPED;
for (Work work; mQueue.tryPop(work); )
{
callWork(work);
}
return ! mQueue.done();
}
bool LL::WorkQueue::runOne()
{
Work work;
if (mQueue.tryPop(work))
{
callWork(work);
}
return ! mQueue.done();
}
bool LL::WorkQueue::runUntil(const TimePoint& until)
{
// Should we subtract some slop to allow for typical Work execution time?
// How much slop?
Work work;
while (TimePoint::clock::now() < until && mQueue.tryPopUntil(until, work))
{
callWork(work);
}
return ! mQueue.done();
}
std::string LL::WorkQueue::makeName(const std::string& name)
{
if (! name.empty())
return name;
static U32 discriminator = 0;
static Mutex mutex;
U32 num;
{
// Protect discriminator from concurrent access by different threads.
// It can't be thread_local, else two racing threads will come up with
// the same name.
Lock lk(mutex);
num = discriminator++;
}
return STRINGIZE("WorkQueue" << num);
}
void LL::WorkQueue::callWork(const Queue::DataTuple& work)
{
// ThreadSafeSchedule::pop() always delivers a tuple, even when
// there's only one data field per item, as for us.
callWork(std::get<0>(work));
}
void LL::WorkQueue::callWork(const Work& work)
{
LL_PROFILE_ZONE_SCOPED;
try
{
work();
}
catch (...)
{
// No matter what goes wrong with any individual work item, the worker
// thread must go on! Log our own instance name with the exception.
LOG_UNHANDLED_EXCEPTION(getKey());
}
}
void LL::WorkQueue::error(const std::string& msg)
{
LL_ERRS("WorkQueue") << msg << LL_ENDL;
}

329
indra/llcommon/workqueue.h Normal file
View File

@ -0,0 +1,329 @@
/**
* @file workqueue.h
* @author Nat Goodspeed
* @date 2021-09-30
* @brief Queue used for inter-thread work passing.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_WORKQUEUE_H)
#define LL_WORKQUEUE_H
#include "llinstancetracker.h"
#include "threadsafeschedule.h"
#include <chrono>
#include <functional> // std::function
#include <queue>
#include <string>
#include <utility> // std::pair
#include <vector>
namespace LL
{
/**
* A typical WorkQueue has a string name that can be used to find it.
*/
class WorkQueue: public LLInstanceTracker<WorkQueue, std::string>
{
private:
using super = LLInstanceTracker<WorkQueue, std::string>;
public:
using Work = std::function<void()>;
private:
using Queue = ThreadSafeSchedule<Work>;
// helper for postEvery()
template <typename Rep, typename Period, typename CALLABLE>
class BackJack;
public:
using TimePoint = Queue::TimePoint;
using TimedWork = Queue::TimeTuple;
using Closed = Queue::Closed;
/**
* You may omit the WorkQueue name, in which case a unique name is
* synthesized; for practical purposes that makes it anonymous.
*/
WorkQueue(const std::string& name = std::string());
/**
* Since the point of WorkQueue is to pass work to some other worker
* thread(s) asynchronously, it's important that the WorkQueue continue
* to exist until the worker thread(s) have drained it. To communicate
* that it's time for them to quit, close() the queue.
*/
void close();
/*---------------------- fire and forget API -----------------------*/
/// fire-and-forget, but at a particular (future?) time
template <typename CALLABLE>
void post(const TimePoint& time, CALLABLE&& callable)
{
// Defer reifying an arbitrary CALLABLE until we hit this method.
// All other methods should accept CALLABLEs of arbitrary type to
// avoid multiple levels of std::function indirection.
mQueue.push(TimedWork(time, std::move(callable)));
}
/// fire-and-forget
template <typename CALLABLE>
void post(CALLABLE&& callable)
{
// We use TimePoint::clock::now() instead of TimePoint's
// representation of the epoch because this WorkQueue may contain
// a mix of past-due TimedWork items and TimedWork items scheduled
// for the future. Sift this new item into the correct place.
post(TimePoint::clock::now(), std::move(callable));
}
/**
* Launch a callable returning bool that will trigger repeatedly at
* specified interval, until the callable returns false.
*
* If you need to signal that callable from outside, DO NOT bind a
* reference to a simple bool! That's not thread-safe. Instead, bind
* an LLCond variant, e.g. LLOneShotCond or LLBoolCond.
*/
template <typename Rep, typename Period, typename CALLABLE>
void postEvery(const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable);
/*------------------------- handshake API --------------------------*/
/**
* Post work to another WorkQueue to be run at a specified time,
* requesting a specific callback to be run on this WorkQueue on
* completion.
*
* Returns true if able to post, false if the other WorkQueue is
* inaccessible.
*/
// Apparently some Microsoft header file defines a macro CALLBACK? The
// natural template argument name CALLBACK produces very weird Visual
// Studio compile errors that seem utterly unrelated to this source
// code.
template <typename CALLABLE, typename FOLLOWUP>
bool postTo(WorkQueue::weak_t target,
const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback)
{
// We're being asked to post to the WorkQueue at target.
// target is a weak_ptr: have to lock it to check it.
auto tptr = target.lock();
if (! tptr)
// can't post() if the target WorkQueue has been destroyed
return false;
// Here we believe target WorkQueue still exists. Post to it a
// lambda that packages our callable, our callback and a weak_ptr
// to this originating WorkQueue.
tptr->post(
time,
[reply = super::getWeak(),
callable = std::move(callable),
callback = std::move(callback)]
()
{
// Call the callable in any case -- but to minimize
// copying the result, immediately bind it into a reply
// lambda. The reply lambda also binds the original
// callback, so that when we, the originating WorkQueue,
// finally receive and process the reply lambda, we'll
// call the bound callback with the bound result -- on the
// same thread that originally called postTo().
auto rlambda =
[result = callable(),
callback = std::move(callback)]
()
{ callback(std::move(result)); };
// Check if this originating WorkQueue still exists.
// Remember, the outer lambda is now running on a thread
// servicing the target WorkQueue, and real time has
// elapsed since postTo()'s tptr->post() call.
// reply is a weak_ptr: have to lock it to check it.
auto rptr = reply.lock();
if (rptr)
{
// Only post reply lambda if the originating WorkQueue
// still exists. If not -- who would we tell? Log it?
try
{
rptr->post(std::move(rlambda));
}
catch (const Closed&)
{
// Originating WorkQueue might still exist, but
// might be Closed. Same thing: just discard the
// callback.
}
}
});
// looks like we were able to post()
return true;
}
/**
* Post work to another WorkQueue, requesting a specific callback to
* be run on this WorkQueue on completion.
*
* Returns true if able to post, false if the other WorkQueue is
* inaccessible.
*/
template <typename CALLABLE, typename FOLLOWUP>
bool postTo(WorkQueue::weak_t target,
CALLABLE&& callable, FOLLOWUP&& callback)
{
return postTo(target, TimePoint::clock::now(), std::move(callable), std::move(callback));
}
/*--------------------------- worker API ---------------------------*/
/**
* runUntilClose() pulls TimedWork items off this WorkQueue until the
* queue is closed, at which point it returns. This would be the
* typical entry point for a simple worker thread.
*/
void runUntilClose();
/**
* runPending() runs all TimedWork items that are ready to run. It
* returns true if the queue remains open, false if the queue has been
* closed. This could be used by a thread whose primary purpose is to
* serve the queue, but also wants to do other things with its idle time.
*/
bool runPending();
/**
* runOne() runs at most one ready TimedWork item -- zero if none are
* ready. It returns true if the queue remains open, false if the
* queue has been closed.
*/
bool runOne();
/**
* runFor() runs a subset of ready TimedWork items, until the
* timeslice has been exceeded. It returns true if the queue remains
* open, false if the queue has been closed. This could be used by a
* busy main thread to lend a bounded few CPU cycles to this WorkQueue
* without risking the WorkQueue blowing out the length of any one
* frame.
*/
template <typename Rep, typename Period>
bool runFor(const std::chrono::duration<Rep, Period>& timeslice)
{
return runUntil(TimePoint::clock::now() + timeslice);
}
/**
* runUntil() is just like runFor(), only with a specific end time
* instead of a timeslice duration.
*/
bool runUntil(const TimePoint& until);
private:
static void error(const std::string& msg);
static std::string makeName(const std::string& name);
void callWork(const Queue::DataTuple& work);
void callWork(const Work& work);
Queue mQueue;
};
/**
* BackJack is, in effect, a hand-rolled lambda, binding a WorkQueue, a
* CALLABLE that returns bool, a TimePoint and an interval at which to
* relaunch it. As long as the callable continues returning true, BackJack
* keeps resubmitting it to the target WorkQueue.
*/
// Why is BackJack a class and not a lambda? Because, unlike a lambda, a
// class method gets its own 'this' pointer -- which we need to resubmit
// the whole BackJack callable.
template <typename Rep, typename Period, typename CALLABLE>
class WorkQueue::BackJack
{
public:
// bind the desired data
BackJack(WorkQueue::weak_t target,
const WorkQueue::TimePoint& start,
const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable):
mTarget(target),
mStart(start),
mInterval(interval),
mCallable(std::move(callable))
{}
// Call by target WorkQueue -- note that although WE require a
// callable returning bool, WorkQueue wants a void callable. We
// consume the bool.
void operator()()
{
// If mCallable() throws an exception, don't catch it here: if it
// throws once, it's likely to throw every time, so it's a waste
// of time to arrange to call it again.
if (mCallable())
{
// Modify mStart to the new start time we desire. If we simply
// added mInterval to now, we'd get actual timings of
// (mInterval + slop), where 'slop' is the latency between the
// previous mStart and the WorkQueue actually calling us.
// Instead, add mInterval to mStart so that at least we
// register our intent to fire at exact mIntervals.
mStart += mInterval;
// We're being called at this moment by the target WorkQueue.
// Assume it still exists, rather than checking the result of
// lock().
// Resubmit the whole *this callable: that's why we're a class
// rather than a lambda. Allow moving *this so we can carry a
// move-only callable; but naturally this statement must be
// the last time we reference this instance, which may become
// moved-from.
try
{
mTarget.lock()->post(mStart, std::move(*this));
}
catch (const Closed&)
{
// Once this queue is closed, oh well, just stop
}
}
}
private:
WorkQueue::weak_t mTarget;
WorkQueue::TimePoint mStart;
std::chrono::duration<Rep, Period> mInterval;
CALLABLE mCallable;
};
template <typename Rep, typename Period, typename CALLABLE>
void WorkQueue::postEvery(const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable)
{
if (interval.count() <= 0)
{
// It's essential that postEvery() be called with a positive
// interval, since each call to BackJack posts another instance of
// itself at (start + interval) and we order by target time. A
// zero or negative interval would result in that BackJack
// instance going to the head of the queue every time, immediately
// ready to run. Effectively that would produce an infinite loop,
// a denial of service on this WorkQueue.
error("postEvery(interval) may not be 0");
}
// Instantiate and post a suitable BackJack, binding a weak_ptr to
// self, the current time, the desired interval and the desired
// callable.
post(
BackJack<Rep, Period, CALLABLE>(
getWeak(), TimePoint::clock::now(), interval, std::move(callable)));
}
} // namespace LL
#endif /* ! defined(LL_WORKQUEUE_H) */

View File

@ -25,7 +25,6 @@
*/
#include "linden_common.h"
#include "fstelemetry.h" // <FS:Beq> add telemetry support.
#include "llimageworker.h"
#include "llimagedxt.h"
@ -135,7 +134,7 @@ LLImageDecodeThread::~LLImageDecodeThread()
// virtual
S32 LLImageDecodeThread::update(F32 max_time_ms)
{
LL_PROFILE_ZONE_COLOR(tracy::Color::Blue); // <FS:Beq/> instrument image decodes
LL_PROFILE_ZONE_SCOPED;
LLMutexLock lock(mCreationMutex);
// <FS:Beq> instrument image decodes
{
@ -174,7 +173,7 @@ S32 LLImageDecodeThread::update(F32 max_time_ms)
LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux, Responder* responder)
{
LL_PROFILE_ZONE_COLOR(tracy::Color::Orange); // <FS:Beq> instrument the image decode pipeline
LL_PROFILE_ZONE_SCOPED;
// <FS:Beq> De-couple texture threading from mainloop
// LLMutexLock lock(mCreationMutex);
// handle_t handle = generateHandle();
@ -270,7 +269,7 @@ bool LLImageDecodeThread::ImageRequest::processRequestIntern()
{
// <FS:Beq> allow longer timeout for async and add instrumentation
// const F32 decode_time_slice = .1f;
LL_PROFILE_ZONE_COLOR(tracy::Color::DarkOrange);
LL_PROFILE_ZONE_SCOPED;
F32 decode_time_slice = .1f;
if(mFlags & FLAG_ASYNC)
{
@ -350,6 +349,7 @@ bool LLImageDecodeThread::ImageRequest::processRequestIntern()
void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)
{
LL_PROFILE_ZONE_SCOPED;
if (mResponder.notNull())
{
bool success = completed && mDecodedRaw && (!mNeedsAux || mDecodedAux);

View File

@ -683,6 +683,7 @@ bool LLSettingsBase::Validator::verifyStringLength(LLSD &value, S32 length)
//=========================================================================
void LLSettingsBlender::update(const LLSettingsBase::BlendFactor& blendf)
{
LL_PROFILE_ZONE_SCOPED;
F64 res = setBlendFactor(blendf);
llassert(res >= 0.0 && res <= 1.0);
(void)res;
@ -713,6 +714,7 @@ F64 LLSettingsBlender::setBlendFactor(const LLSettingsBase::BlendFactor& blendf_
void LLSettingsBlender::triggerComplete()
{
LL_PROFILE_ZONE_SCOPED;
if (mTarget)
mTarget->replaceSettings(mFinal->getSettings());
LLSettingsBlender::ptr_t hold = shared_from_this(); // prevents this from deleting too soon
@ -725,11 +727,13 @@ const LLSettingsBase::BlendFactor LLSettingsBlenderTimeDelta::MIN_BLEND_DELTA(FL
LLSettingsBase::BlendFactor LLSettingsBlenderTimeDelta::calculateBlend(const LLSettingsBase::TrackPosition& spanpos, const LLSettingsBase::TrackPosition& spanlen) const
{
LL_PROFILE_ZONE_SCOPED;
return LLSettingsBase::BlendFactor(fmod((F64)spanpos, (F64)spanlen) / (F64)spanlen);
}
bool LLSettingsBlenderTimeDelta::applyTimeDelta(const LLSettingsBase::Seconds& timedelta)
{
LL_PROFILE_ZONE_SCOPED;
mTimeSpent += timedelta;
if (mTimeSpent > mBlendSpan)

View File

@ -448,6 +448,7 @@ void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother)
void LLSettingsSky::blend(const LLSettingsBase::ptr_t &end, F64 blendf)
{
LL_PROFILE_ZONE_SCOPED;
llassert(getSettingsType() == end->getSettingsType());
LLSettingsSky::ptr_t other = PTR_NAMESPACE::dynamic_pointer_cast<LLSettingsSky>(end);
@ -1026,6 +1027,7 @@ LLColor3 LLSettingsSky::getLightDiffuse() const
LLColor3 LLSettingsSky::getColor(const std::string& key, const LLColor3& default_value) const
{
LL_PROFILE_ZONE_SCOPED;
if (mSettings.has(SETTING_LEGACY_HAZE) && mSettings[SETTING_LEGACY_HAZE].has(key))
{
return LLColor3(mSettings[SETTING_LEGACY_HAZE][key]);
@ -1039,6 +1041,7 @@ LLColor3 LLSettingsSky::getColor(const std::string& key, const LLColor3& default
F32 LLSettingsSky::getFloat(const std::string& key, F32 default_value) const
{
LL_PROFILE_ZONE_SCOPED;
if (mSettings.has(SETTING_LEGACY_HAZE) && mSettings[SETTING_LEGACY_HAZE].has(key))
{
return mSettings[SETTING_LEGACY_HAZE][key].asReal();
@ -1210,6 +1213,14 @@ LLColor3 LLSettingsSky::getLightTransmittance(F32 distance) const
return transmittance;
}
// SL-16127: getTotalDensity() and getDensityMultiplier() call LLSettingsSky::getColor() and LLSettingsSky::getFloat() respectively which are S-L-O-W
LLColor3 LLSettingsSky::getLightTransmittanceFast( const LLColor3& total_density, const F32 density_multiplier, const F32 distance ) const
{
// Transparency (-> density) from Beer's law
LLColor3 transmittance = componentExp(total_density * -(density_multiplier * distance));
return transmittance;
}
// performs soft scale clip and gamma correction ala the shader implementation
// scales colors down to 0 - 1 range preserving relative ratios
LLColor3 LLSettingsSky::gammaCorrect(const LLColor3& in) const

View File

@ -252,6 +252,7 @@ public:
LLColor3 getLightAttenuation(F32 distance) const;
LLColor3 getLightTransmittance(F32 distance) const;
LLColor3 getLightTransmittanceFast(const LLColor3& total_density, const F32 density_multiplier, const F32 distance) const;
LLColor3 getTotalDensity() const;
LLColor3 gammaCorrect(const LLColor3& in) const;

View File

@ -36,6 +36,26 @@ class LLMatrix4a
public:
LL_ALIGN_16(LLVector4a mMatrix[4]);
LLMatrix4a()
{
}
explicit LLMatrix4a(const LLMatrix4& val)
{
loadu(val);
}
inline F32* getF32ptr()
{
return (F32*) &mMatrix;
}
inline const F32* getF32ptr() const
{
return (F32*)&mMatrix;
}
inline void clear()
{
mMatrix[0].clear();
@ -44,6 +64,14 @@ public:
mMatrix[3].clear();
}
inline void setIdentity()
{
mMatrix[0].set(1.f, 0.f, 0.f, 0.f);
mMatrix[1].set(0.f, 1.f, 0.f, 0.f);
mMatrix[2].set(0.f, 0.f, 1.f, 0.f);
mMatrix[3].set(0.f, 0.f, 0.f, 1.f);
}
inline void loadu(const LLMatrix4& src)
{
mMatrix[0] = _mm_loadu_ps(src.mMatrix[0]);
@ -105,7 +133,7 @@ public:
mMatrix[3].setAdd(a.mMatrix[3],d3);
}
inline void rotate(const LLVector4a& v, LLVector4a& res)
inline void rotate(const LLVector4a& v, LLVector4a& res) const
{
LLVector4a y,z;
@ -151,6 +179,8 @@ public:
{
affineTransformSSE(v,res);
}
const LLVector4a& getTranslation() const { return mMatrix[3]; }
};
inline LLVector4a rowMul(const LLVector4a &row, const LLMatrix4a &mat)
@ -176,6 +206,15 @@ inline void matMul(const LLMatrix4a &a, const LLMatrix4a &b, LLMatrix4a &res)
res.mMatrix[3] = row3;
}
//Faster version of matMul wehere res must not be a or b
inline void matMulUnsafe(const LLMatrix4a &a, const LLMatrix4a &b, LLMatrix4a &res)
{
res.mMatrix[0] = rowMul(a.mMatrix[0], b);
res.mMatrix[1] = rowMul(a.mMatrix[1], b);
res.mMatrix[2] = rowMul(a.mMatrix[2], b);
res.mMatrix[3] = rowMul(a.mMatrix[3], b);
}
inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m)
{
s << "[" << m.mMatrix[0] << ", " << m.mMatrix[1] << ", " << m.mMatrix[2] << ", " << m.mMatrix[3] << "]";

View File

@ -46,10 +46,9 @@ class LLRotation;
// of this writing, July 08, 2010) about getting it implemented before you resort to
// LLVector3/LLVector4.
/////////////////////////////////
struct LLVector4a;
LL_ALIGN_PREFIX(16)
struct LLVector4a
class LLVector4a
{
public:
@ -138,10 +137,10 @@ public:
// BASIC GET/SET
////////////////////////////////////
// Return a "this" as an F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
// Return a "this" as an F32 pointer.
inline F32* getF32ptr();
// Return a "this" as a const F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
// Return a "this" as a const F32 pointer.
inline const F32* const getF32ptr() const;
// Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates

View File

@ -58,13 +58,13 @@ inline void LLVector4a::store4a(F32* dst) const
// BASIC GET/SET
////////////////////////////////////
// Return a "this" as an F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
// Return a "this" as an F32 pointer.
F32* LLVector4a::getF32ptr()
{
return (F32*) &mQ;
}
// Return a "this" as a const F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon)
// Return a "this" as a const F32 pointer.
const F32* const LLVector4a::getF32ptr() const
{
return (const F32* const) &mQ;

View File

@ -32,8 +32,7 @@
#include "m4math.h"
#include "m3math.h"
#include "llquaternion.h"
#include "llmatrix4a.h"
// LLMatrix4
@ -115,6 +114,12 @@ LLMatrix4::LLMatrix4(const LLQuaternion &q)
*this = initRotation(q);
}
LLMatrix4::LLMatrix4(const LLMatrix4a& mat)
: LLMatrix4(mat.getF32ptr())
{
}
LLMatrix4::LLMatrix4(const LLQuaternion &q, const LLVector4 &pos)
{
*this = initRotTrans(q, pos);

View File

@ -32,6 +32,7 @@
class LLVector4;
class LLMatrix3;
class LLQuaternion;
class LLMatrix4a;
// NOTA BENE: Currently assuming a right-handed, x-forward, y-left, z-up universe
@ -104,6 +105,7 @@ public:
explicit LLMatrix4(const F32 *mat); // Initializes Matrix to values in mat
explicit LLMatrix4(const LLMatrix3 &mat); // Initializes Matrix to values in mat and sets position to (0,0,0)
explicit LLMatrix4(const LLQuaternion &q); // Initializes Matrix with rotation q and sets position to (0,0,0)
explicit LLMatrix4(const LLMatrix4a& mat);
LLMatrix4(const LLMatrix3 &mat, const LLVector4 &pos); // Initializes Matrix to values in mat and pos

View File

@ -316,6 +316,12 @@ LLVector3::LLVector3(const LLVector4 &vec)
mV[VZ] = (F32)vec.mV[VZ];
}
LLVector3::LLVector3(const LLVector4a& vec)
: LLVector3(vec.getF32ptr())
{
}
LLVector3::LLVector3(const LLSD& sd)
{
setValue(sd);

View File

@ -33,6 +33,7 @@
#include "llsd.h"
class LLVector2;
class LLVector4;
class LLVector4a;
class LLMatrix3;
class LLMatrix4;
class LLVector3d;
@ -62,7 +63,9 @@ class LLVector3
explicit LLVector3(const LLVector2 &vec); // Initializes LLVector3 to (vec[0]. vec[1], 0)
explicit LLVector3(const LLVector3d &vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2])
explicit LLVector3(const LLVector4 &vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2])
explicit LLVector3(const LLSD& sd);
explicit LLVector3(const LLVector4a& vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2])
explicit LLVector3(const LLSD& sd);
LLSD getValue() const;

View File

@ -1220,17 +1220,19 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
LLMeshSkinInfo& skin_info = model->mSkinInfo;
LLMatrix4 mat;
for (int i = 0; i < 4; i++)
{
for(int j = 0; j < 4; j++)
{
skin_info.mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4];
mat.mMatrix[i][j] = dom_value[i + j*4];
}
}
LLMatrix4 trans = normalized_transformation;
trans *= skin_info.mBindShapeMatrix;
skin_info.mBindShapeMatrix = trans;
skin_info.mBindShapeMatrix.loadu(mat);
LLMatrix4a trans(normalized_transformation);
matMul(trans, skin_info.mBindShapeMatrix, skin_info.mBindShapeMatrix);
}
@ -1454,7 +1456,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
mat.mMatrix[i][j] = transform[k*16 + i + j*4];
}
}
model->mSkinInfo.mInvBindMatrix.push_back(mat);
model->mSkinInfo.mInvBindMatrix.push_back(LLMatrix4a(mat));
}
}
}
@ -1538,9 +1540,9 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
if (mJointMap.find(lookingForJoint) != mJointMap.end()
&& model->mSkinInfo.mInvBindMatrix.size() > i)
{
LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i];
LLMatrix4 newInverse = LLMatrix4(model->mSkinInfo.mInvBindMatrix[i].getF32ptr());
newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() );
model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse );
model->mSkinInfo.mAlternateBindMatrix.push_back( LLMatrix4a(newInverse) );
}
else
{

View File

@ -1431,7 +1431,7 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
}
}
mInvBindMatrix.push_back(mat);
mInvBindMatrix.push_back(LLMatrix4a(mat));
}
if (mJointNames.size() != mInvBindMatrix.size())
@ -1445,13 +1445,15 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
if (skin.has("bind_shape_matrix"))
{
LLMatrix4 mat;
for (U32 j = 0; j < 4; j++)
{
for (U32 k = 0; k < 4; k++)
{
mBindShapeMatrix.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal();
mat.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal();
}
}
mBindShapeMatrix.loadu(mat);
}
if (skin.has("alt_inverse_bind_matrix"))
@ -1467,7 +1469,7 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin)
}
}
mAlternateBindMatrix.push_back(mat);
mAlternateBindMatrix.push_back(LLMatrix4a(mat));
}
}

View File

@ -33,6 +33,8 @@
#include "m4math.h"
#include <queue>
#include <boost/align/aligned_allocator.hpp>
#include "lljoint.h"
class daeElement;
@ -40,8 +42,10 @@ class domMesh;
#define MAX_MODEL_FACES 8
LL_ALIGN_PREFIX(16)
class LLMeshSkinInfo
{
LL_ALIGN_NEW
public:
LLMeshSkinInfo();
LLMeshSkinInfo(LLSD& data);
@ -54,19 +58,21 @@ public:
std::vector< JointKey > mJointNames;
// </FS:ND>
mutable std::vector<S32> mJointNums;
typedef std::vector<LLMatrix4a, boost::alignment::aligned_allocator<LLMatrix4a, 16>> matrix_list_t;
matrix_list_t mInvBindMatrix;
matrix_list_t mAlternateBindMatrix;
std::vector<LLMatrix4> mInvBindMatrix;
std::vector<LLMatrix4> mAlternateBindMatrix;
LLMatrix4 mBindShapeMatrix;
LL_ALIGN_16(LLMatrix4a mBindShapeMatrix);
float mPelvisOffset;
bool mLockScaleIfJointPosition;
bool mInvalidJointsScrubbed;
bool mJointNumsInitialized;
};
} LL_ALIGN_POSTFIX(16);
LL_ALIGN_PREFIX(16)
class LLModel : public LLVolume
{
LL_ALIGN_NEW
public:
enum
@ -288,7 +294,7 @@ public:
EModelStatus mStatus ;
int mSubmodelID;
};
} LL_ALIGN_POSTFIX(16);
typedef std::vector<LLPointer<LLModel> > model_list;
typedef std::queue<LLPointer<LLModel> > model_queue;

View File

@ -150,6 +150,7 @@ void LLCubeMap::initRawData(const std::vector<LLPointer<LLImageRaw> >& rawimages
void LLCubeMap::initGLData()
{
LL_PROFILE_ZONE_SCOPED;
for (int i = 0; i < 6; i++)
{
mImages[i]->setSubImage(mRawImages[i], 0, 0, RESOLUTION, RESOLUTION);
@ -453,6 +454,7 @@ BOOL LLCubeMap::project(F32& v_min, F32& v_max, F32& h_min, F32& h_max,
void LLCubeMap::paintIn(LLVector3 dir[4], const LLColor4U& col)
{
LL_PROFILE_ZONE_SCOPED;
F32 v_min, v_max, h_min, h_max;
LLVector3 center = dir[0] + dir[1] + dir[2] + dir[3];
center.normVec();

View File

@ -557,6 +557,7 @@ LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const
LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const
{
LL_PROFILE_ZONE_SCOPED;
if (mFTFace == NULL)
return NULL;

View File

@ -573,9 +573,19 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars
return cur_x / sScaleX;
}
void LLFontGL::generateASCIIglyphs()
{
LL_PROFILE_ZONE_SCOPED
for (U32 i = 32; (i < 127); i++)
{
mFontFreetype->getGlyphInfo(i);
}
}
// Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels
S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, EWordWrapStyle end_on_word_boundary) const
{
LL_PROFILE_ZONE_SCOPED
if (!wchars || !wchars[0] || max_chars == 0)
{
return 0;
@ -885,6 +895,8 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st
{
sFontRegistry->reset();
}
LLFontGL::loadDefaultFonts();
}
// Force standard fonts to get generated up front.
@ -894,6 +906,7 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st
// static
bool LLFontGL::loadDefaultFonts()
{
LL_PROFILE_ZONE_SCOPED
bool succ = true;
succ &= (NULL != getFontSansSerifSmall());
succ &= (NULL != getFontSansSerif());
@ -901,7 +914,6 @@ bool LLFontGL::loadDefaultFonts()
succ &= (NULL != getFontSansSerifHuge());
succ &= (NULL != getFontSansSerifBold());
succ &= (NULL != getFontMonospace());
succ &= (NULL != getFontExtChar());
// <FS:CR> Advanced script editor
succ &= (NULL != getFontScripting());
succ &= (NULL != getFontOCRA());
@ -909,6 +921,15 @@ bool LLFontGL::loadDefaultFonts()
return succ;
}
void LLFontGL::loadCommonFonts()
{
LL_PROFILE_ZONE_SCOPED
getFont(LLFontDescriptor("SansSerif", "Small", BOLD));
getFont(LLFontDescriptor("SansSerif", "Large", BOLD));
getFont(LLFontDescriptor("SansSerif", "Huge", BOLD));
getFont(LLFontDescriptor("Monospace", "Medium", 0));
}
// static
void LLFontGL::destroyDefaultFonts()
{
@ -1109,12 +1130,6 @@ LLFontGL* LLFontGL::getFontCascadia()
}
// </FS:CR>
//static
LLFontGL* LLFontGL::getFontExtChar()
{
return getFontSansSerif();
}
//static
LLFontGL* LLFontGL::getFont(const LLFontDescriptor& desc)
{

View File

@ -160,12 +160,15 @@ public:
const LLFontDescriptor& getFontDesc() const;
void generateASCIIglyphs();
static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::string& fonts_file, F32 size_mod = 0, bool create_gl_textures = true);
// Load sans-serif, sans-serif-small, etc.
// Slow, requires multiple seconds to load fonts.
static bool loadDefaultFonts();
static void loadCommonFonts();
static void destroyDefaultFonts();
static void destroyAllGL();
@ -195,7 +198,6 @@ public:
static LLFontGL* getFontOCRA();
static LLFontGL* getFontCascadia();
// </FS:CR>
static LLFontGL* getFontExtChar();
static LLFontGL* getFont(const LLFontDescriptor& desc);
// Use with legacy names like "SANSSERIF_SMALL" or "OCRA"
static LLFontGL* getFontByName(const std::string& name);

View File

@ -638,6 +638,11 @@ LLFontGL *LLFontRegistry::getFont(const LLFontDescriptor& desc)
<<" style=[" << ((S32) desc.getStyle()) << "]"
<< " size=[" << desc.getSize() << "]" << LL_ENDL;
}
else
{
//generate glyphs for ASCII chars to avoid stalls later
fontp->generateASCIIglyphs();
}
return fontp;
}
}

View File

@ -62,6 +62,7 @@
BOOL gDebugSession = FALSE;
BOOL gClothRipple = FALSE;
BOOL gHeadlessClient = FALSE;
BOOL gNonInteractive = FALSE;
BOOL gGLActive = FALSE;
BOOL gGLDebugLoggingEnabled = TRUE;
@ -434,9 +435,6 @@ LLGLManager::LLGLManager() :
mHasMapBufferRange(FALSE),
mHasFlushBufferRange(FALSE),
mHasPBuffer(FALSE),
mHasShaderObjects(FALSE),
mHasVertexShader(FALSE),
mHasFragmentShader(FALSE),
mNumTextureImageUnits(0),
mHasOcclusionQuery(FALSE),
mHasTimerQuery(FALSE),
@ -778,14 +776,9 @@ bool LLGLManager::initGL()
stop_glerror();
stop_glerror();
if (mHasFragmentShader)
{
GLint num_tex_image_units;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &num_tex_image_units);
mNumTextureImageUnits = llmin(num_tex_image_units, 32);
}
GLint num_tex_image_units;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &num_tex_image_units);
mNumTextureImageUnits = llmin(num_tex_image_units, 32);
if (LLRender::sGLCoreProfile)
{
@ -980,9 +973,9 @@ void LLGLManager::asLLSD(LLSD& info)
info["has_map_buffer_range"] = mHasMapBufferRange;
info["has_flush_buffer_range"] = mHasFlushBufferRange;
info["has_pbuffer"] = mHasPBuffer;
info["has_shader_objects"] = mHasShaderObjects;
info["has_vertex_shader"] = mHasVertexShader;
info["has_fragment_shader"] = mHasFragmentShader;
info["has_shader_objects"] = std::string("Assumed TRUE"); // was mHasShaderObjects;
info["has_vertex_shader"] = std::string("Assumed TRUE"); // was mHasVertexShader;
info["has_fragment_shader"] = std::string("Assumed TRUE"); // was mHasFragmentShader;
info["num_texture_image_units"] = mNumTextureImageUnits;
info["has_occlusion_query"] = mHasOcclusionQuery;
info["has_timer_query"] = mHasTimerQuery;
@ -1088,9 +1081,6 @@ void LLGLManager::initExtensions()
mHasCubeMap = FALSE;
mHasOcclusionQuery = FALSE;
mHasPointParameters = FALSE;
mHasShaderObjects = FALSE;
mHasVertexShader = FALSE;
mHasFragmentShader = FALSE;
mHasTextureRectangle = FALSE;
#else // LL_MESA_HEADLESS //important, gGLHExts.mSysExts is uninitialized until after glh_init_extensions is called
mHasMultitexture = glh_init_extensions("GL_ARB_multitexture");
@ -1148,10 +1138,6 @@ void LLGLManager::initExtensions()
#if !LL_DARWIN
mHasPointParameters = !mIsATI && ExtensionExists("GL_ARB_point_parameters", gGLHExts.mSysExts);
#endif
mHasShaderObjects = ExtensionExists("GL_ARB_shader_objects", gGLHExts.mSysExts) && (LLRender::sGLCoreProfile || ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts));
mHasVertexShader = ExtensionExists("GL_ARB_vertex_program", gGLHExts.mSysExts) && ExtensionExists("GL_ARB_vertex_shader", gGLHExts.mSysExts)
&& (LLRender::sGLCoreProfile || ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts));
mHasFragmentShader = ExtensionExists("GL_ARB_fragment_shader", gGLHExts.mSysExts) && (LLRender::sGLCoreProfile || ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts));
#endif
#if LL_LINUX
@ -1174,9 +1160,6 @@ void LLGLManager::initExtensions()
mHasCubeMap = FALSE;
mHasOcclusionQuery = FALSE;
mHasPointParameters = FALSE;
mHasShaderObjects = FALSE;
mHasVertexShader = FALSE;
mHasFragmentShader = FALSE;
LL_WARNS("RenderInit") << "GL extension support DISABLED via LL_GL_NOEXT" << LL_ENDL;
}
else if (getenv("LL_GL_BASICEXT")) /* Flawfinder: ignore */
@ -1189,9 +1172,6 @@ void LLGLManager::initExtensions()
mHasAnisotropic = FALSE;
//mHasCubeMap = FALSE; // apparently fatal on Intel 915 & similar
//mHasOcclusionQuery = FALSE; // source of many ATI system hangs
mHasShaderObjects = FALSE;
mHasVertexShader = FALSE;
mHasFragmentShader = FALSE;
mHasBlendFuncSeparate = FALSE;
LL_WARNS("RenderInit") << "GL extension support forced to SIMPLE level via LL_GL_BASICEXT" << LL_ENDL;
}
@ -1213,9 +1193,6 @@ void LLGLManager::initExtensions()
if (strchr(blacklist,'j')) mHasCubeMap = FALSE;//S
// if (strchr(blacklist,'k')) mHasATIVAO = FALSE;//S
if (strchr(blacklist,'l')) mHasOcclusionQuery = FALSE;
if (strchr(blacklist,'m')) mHasShaderObjects = FALSE;//S
if (strchr(blacklist,'n')) mHasVertexShader = FALSE;//S
if (strchr(blacklist,'o')) mHasFragmentShader = FALSE;//S
if (strchr(blacklist,'p')) mHasPointParameters = FALSE;//S
if (strchr(blacklist,'q')) mHasFramebufferObject = FALSE;//S
if (strchr(blacklist,'r')) mHasDrawBuffers = FALSE;//S
@ -1262,18 +1239,6 @@ void LLGLManager::initExtensions()
{
LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_point_parameters" << LL_ENDL;
}
if (!mHasShaderObjects)
{
LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_shader_objects" << LL_ENDL;
}
if (!mHasVertexShader)
{
LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_vertex_shader" << LL_ENDL;
}
if (!mHasFragmentShader)
{
LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_fragment_shader" << LL_ENDL;
}
if (!mHasBlendFuncSeparate)
{
LL_INFOS("RenderInit") << "Couldn't initialize GL_EXT_blend_func_separate" << LL_ENDL;
@ -1301,7 +1266,7 @@ void LLGLManager::initExtensions()
glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange);
glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*) &mGLMaxTextureSize);
#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL;
if (mHasVertexBufferObject)
@ -1441,134 +1406,132 @@ void LLGLManager::initExtensions()
glPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterfARB");
glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterfvARB");
}
if (mHasShaderObjects)
{
glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteObjectARB");
glGetHandleARB = (PFNGLGETHANDLEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetHandleARB");
glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDetachObjectARB");
glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateShaderObjectARB");
glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glShaderSourceARB");
glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCompileShaderARB");
glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateProgramObjectARB");
glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glAttachObjectARB");
glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glLinkProgramARB");
glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUseProgramObjectARB");
glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glValidateProgramARB");
glUniform1fARB = (PFNGLUNIFORM1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fARB");
glUniform2fARB = (PFNGLUNIFORM2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fARB");
glUniform3fARB = (PFNGLUNIFORM3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fARB");
glUniform4fARB = (PFNGLUNIFORM4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fARB");
glUniform1iARB = (PFNGLUNIFORM1IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1iARB");
glUniform2iARB = (PFNGLUNIFORM2IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2iARB");
glUniform3iARB = (PFNGLUNIFORM3IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3iARB");
glUniform4iARB = (PFNGLUNIFORM4IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4iARB");
glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fvARB");
glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fvARB");
glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fvARB");
glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fvARB");
glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1ivARB");
glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2ivARB");
glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3ivARB");
glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4ivARB");
glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2fvARB");
glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3fvARB");
glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x4fv");
glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4fvARB");
glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterfvARB");
glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterivARB");
glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetInfoLogARB");
glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttachedObjectsARB");
glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformLocationARB");
glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformARB");
glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformfvARB");
glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformivARB");
glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetShaderSourceARB");
}
if (mHasVertexShader)
{
LL_INFOS() << "initExtensions() VertexShader-related procs..." << LL_ENDL;
// nSight doesn't support use of ARB funcs that have been normalized in the API
if (!LLRender::sNsightDebugSupport)
{
glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocationARB");
glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocationARB");
}
else
{
glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocation");
glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocation");
}
// Assume shader capabilities
glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteObjectARB");
glGetHandleARB = (PFNGLGETHANDLEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetHandleARB");
glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDetachObjectARB");
glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateShaderObjectARB");
glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glShaderSourceARB");
glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCompileShaderARB");
glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateProgramObjectARB");
glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glAttachObjectARB");
glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glLinkProgramARB");
glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUseProgramObjectARB");
glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glValidateProgramARB");
glUniform1fARB = (PFNGLUNIFORM1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fARB");
glUniform2fARB = (PFNGLUNIFORM2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fARB");
glUniform3fARB = (PFNGLUNIFORM3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fARB");
glUniform4fARB = (PFNGLUNIFORM4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fARB");
glUniform1iARB = (PFNGLUNIFORM1IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1iARB");
glUniform2iARB = (PFNGLUNIFORM2IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2iARB");
glUniform3iARB = (PFNGLUNIFORM3IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3iARB");
glUniform4iARB = (PFNGLUNIFORM4IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4iARB");
glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fvARB");
glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fvARB");
glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fvARB");
glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fvARB");
glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1ivARB");
glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2ivARB");
glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3ivARB");
glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4ivARB");
glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2fvARB");
glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3fvARB");
glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x4fv");
glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4fvARB");
glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterfvARB");
glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterivARB");
glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetInfoLogARB");
glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttachedObjectsARB");
glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformLocationARB");
glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformARB");
glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformfvARB");
glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformivARB");
glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetShaderSourceARB");
glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveAttribARB");
glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dARB");
glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dvARB");
glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fARB");
glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fvARB");
glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1sARB");
glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1svARB");
glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dARB");
glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dvARB");
glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fARB");
glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fvARB");
glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2sARB");
glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2svARB");
glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dARB");
glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dvARB");
glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fARB");
glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fvARB");
glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3sARB");
glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3svARB");
glVertexAttrib4nbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nbvARB");
glVertexAttrib4nivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nivARB");
glVertexAttrib4nsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nsvARB");
glVertexAttrib4nubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubARB");
glVertexAttrib4nubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubvARB");
glVertexAttrib4nuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nuivARB");
glVertexAttrib4nusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nusvARB");
glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4bvARB");
glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dARB");
glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dvARB");
glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fARB");
glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fvARB");
glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ivARB");
glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4sARB");
glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4svARB");
glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ubvARB");
glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4uivARB");
glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4usvARB");
glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttribPointerARB");
glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttribIPointer");
glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glEnableVertexAttribArrayARB");
glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDisableVertexAttribArrayARB");
glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramStringARB");
glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindProgramARB");
glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteProgramsARB");
glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGenProgramsARB");
glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dARB");
glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dvARB");
glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fARB");
glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fvARB");
glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dARB");
glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dvARB");
glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fARB");
glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fvARB");
glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterdvARB");
glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterfvARB");
glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterdvARB");
glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterfvARB");
glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramivARB");
glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramStringARB");
glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribdvARB");
glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribfvARB");
glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribivARB");
glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glgetVertexAttribPointervARB");
glIsProgramARB = (PFNGLISPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glIsProgramARB");
}
LL_DEBUGS("RenderInit") << "GL Probe: Got symbols" << LL_ENDL;
LL_INFOS() << "initExtensions() VertexShader-related procs..." << LL_ENDL;
// nSight doesn't support use of ARB funcs that have been normalized in the API
if (!LLRender::sNsightDebugSupport)
{
glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocationARB");
glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocationARB");
}
else
{
glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocation");
glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocation");
}
glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveAttribARB");
glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dARB");
glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dvARB");
glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fARB");
glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fvARB");
glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1sARB");
glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1svARB");
glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dARB");
glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dvARB");
glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fARB");
glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fvARB");
glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2sARB");
glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2svARB");
glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dARB");
glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dvARB");
glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fARB");
glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fvARB");
glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3sARB");
glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3svARB");
glVertexAttrib4nbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nbvARB");
glVertexAttrib4nivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nivARB");
glVertexAttrib4nsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nsvARB");
glVertexAttrib4nubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubARB");
glVertexAttrib4nubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubvARB");
glVertexAttrib4nuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nuivARB");
glVertexAttrib4nusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nusvARB");
glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4bvARB");
glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dARB");
glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dvARB");
glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fARB");
glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fvARB");
glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ivARB");
glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4sARB");
glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4svARB");
glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ubvARB");
glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4uivARB");
glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4usvARB");
glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttribPointerARB");
glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttribIPointer");
glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glEnableVertexAttribArrayARB");
glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDisableVertexAttribArrayARB");
glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramStringARB");
glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindProgramARB");
glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteProgramsARB");
glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGenProgramsARB");
glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dARB");
glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dvARB");
glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fARB");
glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fvARB");
glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dARB");
glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dvARB");
glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fARB");
glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fvARB");
glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterdvARB");
glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterfvARB");
glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterdvARB");
glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterfvARB");
glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramivARB");
glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramStringARB");
glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribdvARB");
glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribfvARB");
glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribivARB");
glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glgetVertexAttribPointervARB");
glIsProgramARB = (PFNGLISPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glIsProgramARB");
LL_DEBUGS("RenderInit") << "GL Probe: Got symbols" << LL_ENDL;
#endif
mInited = TRUE;
mInited = TRUE;
}
void rotate_quat(LLQuaternion& rotation)
@ -2121,7 +2084,7 @@ void LLGLState::checkClientArrays(const std::string& msg, U32 data_mask)
glClientActiveTextureARB(GL_TEXTURE0_ARB);
gGL.getTexUnit(0)->activate();
if (gGLManager.mHasVertexShader && LLGLSLShader::sNoFixedFunction)
if (LLGLSLShader::sNoFixedFunction)
{ //make sure vertex attribs are all disabled
GLint count;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &count);
@ -2159,6 +2122,7 @@ void LLGLState::checkClientArrays(const std::string& msg, U32 data_mask)
LLGLState::LLGLState(LLGLenum state, S32 enabled) :
mState(state), mWasEnabled(FALSE), mIsEnabled(FALSE)
{
LL_PROFILE_ZONE_SCOPED;
if (LLGLSLShader::sNoFixedFunction)
{ //always ignore state that's deprecated post GL 3.0
switch (state)
@ -2217,6 +2181,7 @@ void LLGLState::setEnabled(S32 enabled)
LLGLState::~LLGLState()
{
LL_PROFILE_ZONE_SCOPED;
stop_glerror();
if (mState)
{
@ -2769,3 +2734,4 @@ extern "C"
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif

View File

@ -104,9 +104,6 @@ public:
BOOL mHasMapBufferRange;
BOOL mHasFlushBufferRange;
BOOL mHasPBuffer;
BOOL mHasShaderObjects;
BOOL mHasVertexShader;
BOOL mHasFragmentShader;
S32 mNumTextureImageUnits;
BOOL mHasOcclusionQuery;
BOOL mHasTimerQuery;
@ -498,6 +495,7 @@ void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor
extern BOOL gClothRipple;
extern BOOL gHeadlessClient;
extern BOOL gNonInteractive;
extern BOOL gGLActive;
// Deal with changing glext.h definitions for newer SDK versions, specifically

View File

@ -208,6 +208,7 @@ void LLGLSLShader::dumpStats()
//static
void LLGLSLShader::startProfile()
{
LL_PROFILE_ZONE_SCOPED;
if (sProfileEnabled && sCurBoundShaderPtr)
{
sCurBoundShaderPtr->placeProfileQuery();
@ -218,6 +219,7 @@ void LLGLSLShader::startProfile()
//static
void LLGLSLShader::stopProfile(U32 count, U32 mode)
{
LL_PROFILE_ZONE_SCOPED;
if (sProfileEnabled && sCurBoundShaderPtr)
{
sCurBoundShaderPtr->readProfileQuery(count, mode);
@ -392,6 +394,8 @@ BOOL LLGLSLShader::createShader(std::vector<LLStaticHashedString> * attributes,
U32 varying_count,
const char** varyings)
{
LL_PROFILE_ZONE_SCOPED;
unloadInternal();
sInstances.insert(this);
@ -596,6 +600,8 @@ void LLGLSLShader::attachObjects(GLhandleARB* objects, S32 count)
BOOL LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString> * attributes)
{
LL_PROFILE_ZONE_SCOPED;
//before linking, make sure reserved attributes always have consistent locations
for (U32 i = 0; i < LLShaderMgr::instance()->mReservedAttribs.size(); i++)
{
@ -657,6 +663,8 @@ BOOL LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString> * attri
void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString> * uniforms)
{
LL_PROFILE_ZONE_SCOPED;
if (index == -1)
{
return;
@ -778,6 +786,8 @@ void LLGLSLShader::removePermutation(std::string name)
GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type)
{
LL_PROFILE_ZONE_SCOPED;
if ((type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB) ||
type == GL_SAMPLER_2D_MULTISAMPLE)
{ //this here is a texture
@ -790,7 +800,9 @@ GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type)
BOOL LLGLSLShader::mapUniforms(const vector<LLStaticHashedString> * uniforms)
{
BOOL res = TRUE;
LL_PROFILE_ZONE_SCOPED;
BOOL res = TRUE;
mTotalUniformSize = 0;
mActiveTextureChannels = 0;
@ -933,6 +945,8 @@ BOOL LLGLSLShader::mapUniforms(const vector<LLStaticHashedString> * uniforms)
BOOL LLGLSLShader::link(BOOL suppress_errors)
{
LL_PROFILE_ZONE_SCOPED;
BOOL success = LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors);
if (!success && !suppress_errors)
@ -945,56 +959,52 @@ BOOL LLGLSLShader::link(BOOL suppress_errors)
void LLGLSLShader::bind()
{
LL_PROFILE_ZONE_SCOPED;
gGL.flush();
if (gGLManager.mHasShaderObjects)
if (sCurBoundShader != mProgramObject) // Don't re-bind current shader
{
LLVertexBuffer::unbind();
glUseProgramObjectARB(mProgramObject);
sCurBoundShader = mProgramObject;
sCurBoundShaderPtr = this;
if (mUniformsDirty)
{
LLShaderMgr::instance()->updateShaderUniforms(this);
mUniformsDirty = FALSE;
}
}
if (mUniformsDirty)
{
LLShaderMgr::instance()->updateShaderUniforms(this);
mUniformsDirty = FALSE;
}
}
void LLGLSLShader::unbind()
{
LL_PROFILE_ZONE_SCOPED;
gGL.flush();
if (gGLManager.mHasShaderObjects)
{
stop_glerror();
if (gGLManager.mIsNVIDIA)
{
for (U32 i = 0; i < mAttribute.size(); ++i)
{
vertexAttrib4f(i, 0,0,0,1);
stop_glerror();
}
}
LLVertexBuffer::unbind();
glUseProgramObjectARB(0);
sCurBoundShader = 0;
sCurBoundShaderPtr = NULL;
stop_glerror();
}
stop_glerror();
LLVertexBuffer::unbind();
glUseProgramObjectARB(0);
sCurBoundShader = 0;
sCurBoundShaderPtr = NULL;
stop_glerror();
}
void LLGLSLShader::bindNoShader(void)
{
LL_PROFILE_ZONE_SCOPED;
LLVertexBuffer::unbind();
if (gGLManager.mHasShaderObjects)
{
glUseProgramObjectARB(0);
sCurBoundShader = 0;
sCurBoundShaderPtr = NULL;
}
glUseProgramObjectARB(0);
sCurBoundShader = 0;
sCurBoundShaderPtr = NULL;
}
S32 LLGLSLShader::bindTexture(const std::string &uniform, LLTexture *texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
{
LL_PROFILE_ZONE_SCOPED;
S32 channel = 0;
channel = getUniformLocation(uniform);
@ -1003,6 +1013,8 @@ S32 LLGLSLShader::bindTexture(const std::string &uniform, LLTexture *texture, LL
S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture *texture, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace colorspace)
{
LL_PROFILE_ZONE_SCOPED;
if (uniform < 0 || uniform >= (S32)mTexture.size())
{
LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
@ -1013,7 +1025,7 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture *texture, LLTexUnit::eTextu
if (uniform > -1)
{
gGL.getTexUnit(uniform)->bind(texture, mode);
gGL.getTexUnit(uniform)->bindFast(texture);
gGL.getTexUnit(uniform)->setTextureColorSpace(colorspace);
}
@ -1022,6 +1034,8 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture *texture, LLTexUnit::eTextu
S32 LLGLSLShader::unbindTexture(const std::string &uniform, LLTexUnit::eTextureType mode)
{
LL_PROFILE_ZONE_SCOPED;
S32 channel = 0;
channel = getUniformLocation(uniform);
@ -1030,6 +1044,8 @@ S32 LLGLSLShader::unbindTexture(const std::string &uniform, LLTexUnit::eTextureT
S32 LLGLSLShader::unbindTexture(S32 uniform, LLTexUnit::eTextureType mode)
{
LL_PROFILE_ZONE_SCOPED;
if (uniform < 0 || uniform >= (S32)mTexture.size())
{
LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
@ -1040,7 +1056,7 @@ S32 LLGLSLShader::unbindTexture(S32 uniform, LLTexUnit::eTextureType mode)
if (uniform > -1)
{
gGL.getTexUnit(uniform)->unbind(mode);
gGL.getTexUnit(uniform)->unbindFast(mode);
}
return uniform;
@ -1048,6 +1064,8 @@ S32 LLGLSLShader::unbindTexture(S32 uniform, LLTexUnit::eTextureType mode)
S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
{
LL_PROFILE_ZONE_SCOPED;
if (uniform < 0 || uniform >= (S32)mTexture.size())
{
LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
@ -1065,6 +1083,8 @@ S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTex
S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
{
LL_PROFILE_ZONE_SCOPED;
if (uniform < 0 || uniform >= (S32)mTexture.size())
{
LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
@ -1092,6 +1112,7 @@ S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTe
void LLGLSLShader::uniform1i(U32 index, GLint x)
{
LL_PROFILE_ZONE_SCOPED
if (mProgramObject)
{
if (mUniform.size() <= index)
@ -1102,7 +1123,7 @@ void LLGLSLShader::uniform1i(U32 index, GLint x)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
if (iter == mValue.end() || iter->second.mV[0] != x)
{
glUniform1iARB(mUniform[index], x);
@ -1114,6 +1135,7 @@ void LLGLSLShader::uniform1i(U32 index, GLint x)
void LLGLSLShader::uniform1f(U32 index, GLfloat x)
{
LL_PROFILE_ZONE_SCOPED
if (mProgramObject)
{
if (mUniform.size() <= index)
@ -1124,7 +1146,7 @@ void LLGLSLShader::uniform1f(U32 index, GLfloat x)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
if (iter == mValue.end() || iter->second.mV[0] != x)
{
glUniform1fARB(mUniform[index], x);
@ -1146,7 +1168,7 @@ void LLGLSLShader::uniform2f(U32 index, GLfloat x, GLfloat y)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(x,y,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1169,7 +1191,7 @@ void LLGLSLShader::uniform3f(U32 index, GLfloat x, GLfloat y, GLfloat z)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(x,y,z,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1192,7 +1214,7 @@ void LLGLSLShader::uniform4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(x,y,z,w);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1215,7 +1237,7 @@ void LLGLSLShader::uniform1iv(U32 index, U32 count, const GLint* v)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(v[0],0.f,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
@ -1238,7 +1260,7 @@ void LLGLSLShader::uniform1fv(U32 index, U32 count, const GLfloat* v)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(v[0],0.f,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
@ -1261,7 +1283,7 @@ void LLGLSLShader::uniform2fv(U32 index, U32 count, const GLfloat* v)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(v[0],v[1],0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
@ -1284,7 +1306,7 @@ void LLGLSLShader::uniform3fv(U32 index, U32 count, const GLfloat* v)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(v[0],v[1],v[2],0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
@ -1307,10 +1329,11 @@ void LLGLSLShader::uniform4fv(U32 index, U32 count, const GLfloat* v)
if (mUniform[index] >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]);
const auto& iter = mValue.find(mUniform[index]);
LLVector4 vec(v[0],v[1],v[2],v[3]);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
LL_PROFILE_ZONE_SCOPED;
glUniform4fvARB(mUniform[index], count, v);
mValue[mUniform[index]] = vec;
}
@ -1354,6 +1377,8 @@ void LLGLSLShader::uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, c
void LLGLSLShader::uniformMatrix3x4fv(U32 index, U32 count, GLboolean transpose, const GLfloat *v)
{
LL_PROFILE_ZONE_SCOPED;
if (mProgramObject)
{
if (mUniform.size() <= index)
@ -1388,6 +1413,8 @@ void LLGLSLShader::uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, c
GLint LLGLSLShader::getUniformLocation(const LLStaticHashedString& uniform)
{
LL_PROFILE_ZONE_SCOPED;
GLint ret = -1;
if (mProgramObject)
{
@ -1412,6 +1439,8 @@ GLint LLGLSLShader::getUniformLocation(const LLStaticHashedString& uniform)
GLint LLGLSLShader::getUniformLocation(U32 index)
{
LL_PROFILE_ZONE_SCOPED;
GLint ret = -1;
if (mProgramObject)
{
@ -1424,6 +1453,8 @@ GLint LLGLSLShader::getUniformLocation(U32 index)
GLint LLGLSLShader::getAttribLocation(U32 attrib)
{
LL_PROFILE_ZONE_SCOPED;
if (attrib < mAttribute.size())
{
return mAttribute[attrib];
@ -1440,7 +1471,7 @@ void LLGLSLShader::uniform1i(const LLStaticHashedString& uniform, GLint v)
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(v,0.f,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1456,7 +1487,7 @@ void LLGLSLShader::uniform2i(const LLStaticHashedString& uniform, GLint i, GLint
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(i,j,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1473,7 +1504,7 @@ void LLGLSLShader::uniform1f(const LLStaticHashedString& uniform, GLfloat v)
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(v,0.f,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1489,7 +1520,7 @@ void LLGLSLShader::uniform2f(const LLStaticHashedString& uniform, GLfloat x, GLf
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(x,y,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1506,7 +1537,7 @@ void LLGLSLShader::uniform3f(const LLStaticHashedString& uniform, GLfloat x, GLf
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(x,y,z,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec))
{
@ -1522,7 +1553,7 @@ void LLGLSLShader::uniform1fv(const LLStaticHashedString& uniform, U32 count, co
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(v[0],0.f,0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
@ -1538,7 +1569,7 @@ void LLGLSLShader::uniform2fv(const LLStaticHashedString& uniform, U32 count, co
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(v[0],v[1],0.f,0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
@ -1554,7 +1585,7 @@ void LLGLSLShader::uniform3fv(const LLStaticHashedString& uniform, U32 count, co
if (location >= 0)
{
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
LLVector4 vec(v[0],v[1],v[2],0.f);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
@ -1571,12 +1602,11 @@ void LLGLSLShader::uniform4fv(const LLStaticHashedString& uniform, U32 count, co
if (location >= 0)
{
LLVector4 vec(v);
std::map<GLint, LLVector4>::iterator iter = mValue.find(location);
const auto& iter = mValue.find(location);
if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1)
{
stop_glerror();
LL_PROFILE_ZONE_SCOPED;
glUniform4fvARB(location, count, v);
stop_glerror();
mValue[location] = vec;
}
}
@ -1616,3 +1646,27 @@ void LLGLSLShader::setMinimumAlpha(F32 minimum)
gGL.flush();
uniform1f(LLShaderMgr::MINIMUM_ALPHA, minimum);
}
void LLShaderUniforms::apply(LLGLSLShader* shader)
{
LL_PROFILE_ZONE_SCOPED;
for (auto& uniform : mIntegers)
{
shader->uniform1i(uniform.mUniform, uniform.mValue);
}
for (auto& uniform : mFloats)
{
shader->uniform1f(uniform.mUniform, uniform.mValue);
}
for (auto& uniform : mVectors)
{
shader->uniform4fv(uniform.mUniform, 1, uniform.mValue.mV);
}
for (auto& uniform : mVector3s)
{
shader->uniform3fv(uniform.mUniform, 1, uniform.mValue.mV);
}
}

View File

@ -30,6 +30,7 @@
#include "llgl.h"
#include "llrender.h"
#include "llstaticstringtable.h"
#include <unordered_map>
class LLShaderFeatures
{
@ -64,16 +65,79 @@ public:
LLShaderFeatures();
};
// ============= Structure for caching shader uniforms ===============
class LLGLSLShader;
class LLShaderUniforms
{
public:
template<typename T>
struct UniformSetting
{
S32 mUniform;
T mValue;
};
typedef UniformSetting<S32> IntSetting;
typedef UniformSetting<F32> FloatSetting;
typedef UniformSetting<LLVector4> VectorSetting;
typedef UniformSetting<LLVector3> Vector3Setting;
void clear()
{
mIntegers.resize(0);
mFloats.resize(0);
mVectors.resize(0);
mVector3s.resize(0);
}
void uniform1i(S32 index, S32 value)
{
mIntegers.push_back({ index, value });
}
void uniform1f(S32 index, F32 value)
{
mFloats.push_back({ index, value });
}
void uniform4fv(S32 index, const LLVector4& value)
{
mVectors.push_back({ index, value });
}
void uniform4fv(S32 index, const F32* value)
{
mVectors.push_back({ index, LLVector4(value) });
}
void uniform3fv(S32 index, const LLVector3& value)
{
mVector3s.push_back({ index, value });
}
void apply(LLGLSLShader* shader);
std::vector<IntSetting> mIntegers;
std::vector<FloatSetting> mFloats;
std::vector<VectorSetting> mVectors;
std::vector<Vector3Setting> mVector3s;
};
class LLGLSLShader
{
public:
enum
// enum primarily used to control application sky settings uniforms
typedef enum
{
SG_DEFAULT = 0,
SG_SKY,
SG_WATER
};
SG_DEFAULT = 0, // not sky or water specific
SG_SKY, //
SG_WATER,
SG_ANY,
SG_COUNT
} eGroup;
static std::set<LLGLSLShader*> sInstances;
static bool sProfileEnabled;
@ -190,13 +254,15 @@ public:
U32 mAttributeMask; //mask of which reserved attributes are set (lines up with LLVertexBuffer::getTypeMask())
std::vector<GLint> mUniform; //lookup table of uniform enum to uniform location
LLStaticStringTable<GLint> mUniformMap; //lookup map of uniform name to uniform location
std::map<GLint, std::string> mUniformNameMap; //lookup map of uniform location to uniform name
std::map<GLint, LLVector4> mValue; //lookup map of uniform location to last known value
typedef std::unordered_map<GLint, std::string> uniform_name_map_t;
typedef std::unordered_map<GLint, LLVector4> uniform_value_map_t;
uniform_name_map_t mUniformNameMap; //lookup map of uniform location to uniform name
uniform_value_map_t mValue; //lookup map of uniform location to last known value
std::vector<GLint> mTexture;
S32 mTotalUniformSize;
S32 mActiveTextureChannels;
S32 mShaderLevel;
S32 mShaderGroup;
S32 mShaderGroup; // see LLGLSLShader::eGroup
BOOL mUniformsDirty;
LLShaderFeatures mFeatures;
std::vector< std::pair< std::string, GLenum > > mShaderFiles;

View File

@ -262,6 +262,7 @@ LLTexUnit::eTextureType LLGLTexture::getTarget(void) const
BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height)
{
LL_PROFILE_ZONE_SCOPED;
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height) ;
@ -269,6 +270,7 @@ BOOL LLGLTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos,
BOOL LLGLTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height)
{
LL_PROFILE_ZONE_SCOPED;
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height) ;

View File

@ -179,7 +179,7 @@ private:
protected:
void setTexelsPerImage();
//note: do not make this function public.
public:
/*virtual*/ LLImageGL* getGLTexture() const ;
protected:

View File

@ -39,6 +39,7 @@
#include "llgl.h"
#include "llglslshader.h"
#include "llrender.h"
#include "llwindow.h"
//----------------------------------------------------------------------------
const F32 MIN_TEXTURE_LIFETIME = 10.f;
@ -175,15 +176,32 @@ BOOL is_little_endian()
return (*c == 0x78) ;
}
LLImageGLThread* LLImageGLThread::sInstance = nullptr;
//static
void LLImageGL::initClass(S32 num_catagories, BOOL skip_analyze_alpha /* = false */)
void LLImageGL::initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha /* = false */)
{
LL_PROFILE_ZONE_SCOPED;
sSkipAnalyzeAlpha = skip_analyze_alpha;
LLImageGLThread::sInstance = new LLImageGLThread(window);
LLImageGLThread::sInstance->start();
}
//static
void LLImageGL::updateClass()
{
LL_PROFILE_ZONE_SCOPED;
LLImageGLThread::sInstance->executeCallbacks();
}
//static
void LLImageGL::cleanupClass()
{
{
LL_PROFILE_ZONE_SCOPED;
LLImageGLThread::sInstance->mFunctionQueue.close();
delete LLImageGLThread::sInstance;
LLImageGLThread::sInstance = nullptr;
}
//static
@ -664,6 +682,7 @@ void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_for
void LLImageGL::setImage(const LLImageRaw* imageraw)
{
LL_PROFILE_ZONE_SCOPED;
llassert((imageraw->getWidth() == getWidth(mCurrentDiscardLevel)) &&
(imageraw->getHeight() == getHeight(mCurrentDiscardLevel)) &&
(imageraw->getComponents() == getComponents()));
@ -672,7 +691,7 @@ void LLImageGL::setImage(const LLImageRaw* imageraw)
}
static LLTrace::BlockTimerStatHandle FTM_SET_IMAGE("setImage");
BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips, S32 usename)
{
LL_RECORD_BLOCK_TIME(FTM_SET_IMAGE);
bool is_compressed = false;
@ -691,12 +710,11 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
break;
}
if (mUseMipMaps)
{
//set has mip maps to true before binding image so tex parameters get set properly
gGL.getTexUnit(0)->unbind(mBindTarget);
gGL.getTexUnit(0)->unbind(mBindTarget);
mHasMipMaps = true;
mTexOptionsDirty = true;
setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
@ -706,10 +724,10 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
mHasMipMaps = false;
}
llverify(gGL.getTexUnit(0)->bind(this));
if (mUseMipMaps)
gGL.getTexUnit(0)->bind(this, false, false, usename);
if (mUseMipMaps)
{
if (data_hasmips)
{
@ -789,7 +807,7 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
}
LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
w, h,
mFormatPrimary, mFormatType,
data_in, mAllowCompression);
@ -886,7 +904,7 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
stop_glerror();
}
LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
if (m == 0)
{
analyzeAlpha(data_in, w, h);
@ -1075,6 +1093,7 @@ void LLImageGL::postAddToAtlas()
BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update)
{
LL_PROFILE_ZONE_SCOPED;
if (!width || !height)
{
return TRUE;
@ -1171,6 +1190,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update)
{
LL_PROFILE_ZONE_SCOPED;
return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update);
}
@ -1199,7 +1219,7 @@ void LLImageGL::generateTextures(S32 numTextures, U32 *textures)
}
// static
void LLImageGL::deleteTextures(S32 numTextures, U32 *textures)
void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures)
{
if (gGLManager.mInited)
{
@ -1209,116 +1229,119 @@ void LLImageGL::deleteTextures(S32 numTextures, U32 *textures)
// static
static LLTrace::BlockTimerStatHandle FTM_SET_MANUAL_IMAGE("setManualImage");
void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression)
void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void* pixels, bool allow_compression)
{
LL_RECORD_BLOCK_TIME(FTM_SET_MANUAL_IMAGE);
bool use_scratch = false;
U32* scratch = NULL;
if (LLRender::sGLCoreProfile)
{
if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE)
{ //GL_ALPHA is deprecated, convert to RGBA
use_scratch = true;
scratch = new U32[width*height];
LL_RECORD_BLOCK_TIME(FTM_SET_MANUAL_IMAGE);
bool use_scratch = false;
U32* scratch = NULL;
if (LLRender::sGLCoreProfile)
{
if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE)
{ //GL_ALPHA is deprecated, convert to RGBA
use_scratch = true;
scratch = new U32[width * height];
U32 pixel_count = (U32) (width*height);
for (U32 i = 0; i < pixel_count; i++)
{
U8* pix = (U8*) &scratch[i];
pix[0] = pix[1] = pix[2] = 0;
pix[3] = ((U8*) pixels)[i];
}
pixformat = GL_RGBA;
intformat = GL_RGBA8;
}
U32 pixel_count = (U32)(width * height);
for (U32 i = 0; i < pixel_count; i++)
{
U8* pix = (U8*)&scratch[i];
pix[0] = pix[1] = pix[2] = 0;
pix[3] = ((U8*)pixels)[i];
}
if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE)
{ //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
use_scratch = true;
scratch = new U32[width*height];
pixformat = GL_RGBA;
intformat = GL_RGBA8;
}
U32 pixel_count = (U32) (width*height);
for (U32 i = 0; i < pixel_count; i++)
{
U8 lum = ((U8*) pixels)[i*2+0];
U8 alpha = ((U8*) pixels)[i*2+1];
if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE)
{ //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA
use_scratch = true;
scratch = new U32[width * height];
U8* pix = (U8*) &scratch[i];
pix[0] = pix[1] = pix[2] = lum;
pix[3] = alpha;
}
pixformat = GL_RGBA;
intformat = GL_RGBA8;
}
U32 pixel_count = (U32)(width * height);
for (U32 i = 0; i < pixel_count; i++)
{
U8 lum = ((U8*)pixels)[i * 2 + 0];
U8 alpha = ((U8*)pixels)[i * 2 + 1];
if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE)
{ //GL_LUMINANCE_ALPHA is deprecated, convert to RGB
use_scratch = true;
scratch = new U32[width*height];
U8* pix = (U8*)&scratch[i];
pix[0] = pix[1] = pix[2] = lum;
pix[3] = alpha;
}
U32 pixel_count = (U32) (width*height);
for (U32 i = 0; i < pixel_count; i++)
{
U8 lum = ((U8*) pixels)[i];
U8* pix = (U8*) &scratch[i];
pix[0] = pix[1] = pix[2] = lum;
pix[3] = 255;
}
pixformat = GL_RGBA;
intformat = GL_RGB8;
}
}
pixformat = GL_RGBA;
intformat = GL_RGBA8;
}
if (LLImageGL::sCompressTextures && allow_compression)
{
switch (intformat)
{
case GL_RGB:
case GL_RGB8:
intformat = GL_COMPRESSED_RGB;
break;
case GL_SRGB:
case GL_SRGB8:
intformat = GL_COMPRESSED_SRGB;
break;
case GL_RGBA:
case GL_RGBA8:
intformat = GL_COMPRESSED_RGBA;
break;
case GL_SRGB_ALPHA:
case GL_SRGB8_ALPHA8:
intformat = GL_COMPRESSED_SRGB_ALPHA;
break;
case GL_LUMINANCE:
case GL_LUMINANCE8:
intformat = GL_COMPRESSED_LUMINANCE;
break;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE8_ALPHA8:
intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
break;
case GL_ALPHA:
case GL_ALPHA8:
intformat = GL_COMPRESSED_ALPHA;
break;
default:
LL_WARNS() << "Could not compress format: " << std::hex << intformat << LL_ENDL;
break;
}
}
if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE)
{ //GL_LUMINANCE_ALPHA is deprecated, convert to RGB
use_scratch = true;
scratch = new U32[width * height];
stop_glerror();
glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels);
stop_glerror();
U32 pixel_count = (U32)(width * height);
for (U32 i = 0; i < pixel_count; i++)
{
U8 lum = ((U8*)pixels)[i];
if (use_scratch)
{
delete [] scratch;
}
U8* pix = (U8*)&scratch[i];
pix[0] = pix[1] = pix[2] = lum;
pix[3] = 255;
}
pixformat = GL_RGBA;
intformat = GL_RGB8;
}
}
if (LLImageGL::sCompressTextures && allow_compression)
{
switch (intformat)
{
case GL_RGB:
case GL_RGB8:
intformat = GL_COMPRESSED_RGB;
break;
case GL_SRGB:
case GL_SRGB8:
intformat = GL_COMPRESSED_SRGB;
break;
case GL_RGBA:
case GL_RGBA8:
intformat = GL_COMPRESSED_RGBA;
break;
case GL_SRGB_ALPHA:
case GL_SRGB8_ALPHA8:
intformat = GL_COMPRESSED_SRGB_ALPHA;
break;
case GL_LUMINANCE:
case GL_LUMINANCE8:
intformat = GL_COMPRESSED_LUMINANCE;
break;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE8_ALPHA8:
intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
break;
case GL_ALPHA:
case GL_ALPHA8:
intformat = GL_COMPRESSED_ALPHA;
break;
default:
LL_WARNS() << "Could not compress format: " << std::hex << intformat << LL_ENDL;
break;
}
}
stop_glerror();
{
LL_PROFILE_ZONE_NAMED("glTexImage2D");
glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels);
}
stop_glerror();
if (use_scratch)
{
delete[] scratch;
}
}
//create an empty GL texture: just create a texture name
@ -1341,6 +1364,7 @@ BOOL LLImageGL::createGLTexture()
if(mTexName)
{
LLImageGL::deleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ;
mTexName = 0;
}
@ -1365,13 +1389,13 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
return FALSE;
}
mGLTextureCreated = false ;
llassert(gGLManager.mInited);
stop_glerror();
if (!imageraw || imageraw->isBufferInvalid())
{
LL_WARNS() << "Trying to create a texture from invalid image data" << LL_ENDL;
mGLTextureCreated = false;
return FALSE;
}
@ -1391,6 +1415,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
if (!setSize(w, h, imageraw->getComponents(), discard_level))
{
LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL;
mGLTextureCreated = false;
return FALSE;
}
@ -1459,6 +1484,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
destroyGLTexture();
mCurrentDiscardLevel = discard_level;
mLastBindTime = sLastFrameTime;
mGLTextureCreated = false;
return TRUE ;
}
@ -1470,104 +1496,123 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
static LLTrace::BlockTimerStatHandle FTM_CREATE_GL_TEXTURE3("createGLTexture3(data)");
BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename)
{
LL_RECORD_BLOCK_TIME(FTM_CREATE_GL_TEXTURE3);
llassert(data_in);
stop_glerror();
LL_RECORD_BLOCK_TIME(FTM_CREATE_GL_TEXTURE3);
llassert(data_in);
stop_glerror();
if (discard_level < 0)
{
llassert(mCurrentDiscardLevel >= 0);
discard_level = mCurrentDiscardLevel;
}
discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
if (discard_level < 0)
{
llassert(mCurrentDiscardLevel >= 0);
discard_level = mCurrentDiscardLevel;
}
discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
if (mTexName != 0 && discard_level == mCurrentDiscardLevel)
{
// This will only be true if the size has not changed
return setImage(data_in, data_hasmips);
}
U32 old_name = mTexName;
// S32 old_discard = mCurrentDiscardLevel;
if (usename != 0)
{
mTexName = usename;
}
else
{
LLImageGL::generateTextures(1, &mTexName);
stop_glerror();
{
llverify(gGL.getTexUnit(0)->bind(this));
stop_glerror();
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0);
stop_glerror();
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel-discard_level);
stop_glerror();
}
}
if (!mTexName)
{
if (old_name)
{
sGlobalTextureMemory -= mTextureMemory;
LLImageGL::deleteTextures(1, &old_name);
disclaimMem(mTextureMemory);
stop_glerror();
}
if (mTexName != 0 && discard_level == mCurrentDiscardLevel)
{
// This will only be true if the size has not changed
return setImage(data_in, data_hasmips);
}
LL_WARNS() << "LLImageGL::createGLTexture failed to make texture" << LL_ENDL;
return FALSE;
}
if (mUseMipMaps)
{
mAutoGenMips = gGLManager.mHasMipMapGeneration;
GLuint old_texname = mTexName;
if (usename != 0)
{
mNewTexName = usename;
}
else
{
LLImageGL::generateTextures(1, &mNewTexName);
{
gGL.getTexUnit(0)->bind(this, false, false, mNewTexName);
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel - discard_level);
}
}
if (mUseMipMaps)
{
mAutoGenMips = gGLManager.mHasMipMapGeneration;
#if LL_DARWIN
// On the Mac GF2 and GF4MX drivers, auto mipmap generation doesn't work right with alpha-only textures.
if(gGLManager.mIsGF2or4MX && (mFormatInternal == GL_ALPHA8) && (mFormatPrimary == GL_ALPHA))
{
mAutoGenMips = FALSE;
}
// On the Mac GF2 and GF4MX drivers, auto mipmap generation doesn't work right with alpha-only textures.
if (gGLManager.mIsGF2or4MX && (mFormatInternal == GL_ALPHA8) && (mFormatPrimary == GL_ALPHA))
{
mAutoGenMips = FALSE;
}
#endif
}
}
mCurrentDiscardLevel = discard_level;
mCurrentDiscardLevel = discard_level;
if (!setImage(data_in, data_hasmips))
{
stop_glerror();
return FALSE;
}
if (!setImage(data_in, data_hasmips, mNewTexName))
{
return FALSE;
}
// Set texture options to our defaults.
gGL.getTexUnit(0)->setHasMipMaps(mHasMipMaps);
gGL.getTexUnit(0)->setTextureAddressMode(mAddressMode);
gGL.getTexUnit(0)->setTextureFilteringOption(mFilterOption);
// Set texture options to our defaults.
gGL.getTexUnit(0)->setHasMipMaps(mHasMipMaps);
gGL.getTexUnit(0)->setTextureAddressMode(mAddressMode);
gGL.getTexUnit(0)->setTextureFilteringOption(mFilterOption);
// things will break if we don't unbind after creation
gGL.getTexUnit(0)->unbind(mBindTarget);
stop_glerror();
// things will break if we don't unbind after creation
gGL.getTexUnit(0)->unbind(mBindTarget);
if (old_name != 0)
{
sGlobalTextureMemory -= mTextureMemory;
if (old_texname != 0)
{
sGlobalTextureMemory -= mTextureMemory;
}
LLImageGL::deleteTextures(1, &old_name);
//if we're on the image loading thread, be sure to delete old_texname and update mTexName on the main thread
if (LLImageGLThread::sInstance != nullptr &&
LLThread::currentID() == LLImageGLThread::sInstance->getID())
{
{
LL_PROFILE_ZONE_NAMED("cglt - sync");
if (gGLManager.mHasSync)
{
auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glClientWaitSync(sync, 0, 0);
glDeleteSync(sync);
}
else
{
glFinish();
}
}
stop_glerror();
}
ref();
LLImageGLThread::sInstance->postCallback([=]()
{
LL_PROFILE_ZONE_NAMED("cglt - delete callback");
if (old_texname != 0)
{
LLImageGL::deleteTextures(1, &old_texname);
}
mTexName = mNewTexName;
mNewTexName = 0;
unref();
});
}
else
{
//not on background thread, immediately set mTexName
if (old_texname != 0)
{
LLImageGL::deleteTextures(1, &old_texname);
}
mTexName = mNewTexName;
mNewTexName = 0;
}
disclaimMem(mTextureMemory);
mTextureMemory = (S32Bytes)getMipBytes(mCurrentDiscardLevel);
claimMem(mTextureMemory);
sGlobalTextureMemory += mTextureMemory;
mTexelsInGLTexture = getWidth() * getHeight();
disclaimMem(mTextureMemory);
mTextureMemory = (S32Bytes)getMipBytes(discard_level);
claimMem(mTextureMemory);
sGlobalTextureMemory += mTextureMemory;
mTexelsInGLTexture = getWidth() * getHeight() ;
// mark this as bound at this point, so we don't throw it out immediately
mLastBindTime = sLastFrameTime;
// mark this as bound at this point, so we don't throw it out immediately
mLastBindTime = sLastFrameTime;
return TRUE;
return TRUE;
}
BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const
@ -1702,7 +1747,7 @@ void LLImageGL::destroyGLTexture()
mTextureMemory = (S32Bytes)0;
}
LLImageGL::deleteTextures(1, &mTexName);
LLImageGL::deleteTextures(1, &mTexName);
mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
mTexName = 0;
mGLTextureCreated = FALSE ;
@ -2246,3 +2291,60 @@ void LLImageGL::resetCurTexSizebar()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, nummips);
*/
LLImageGLThread::LLImageGLThread(LLWindow* window)
: LLThread("LLImageGL"), mWindow(window)
{
mFinished = false;
mContext = mWindow->createSharedContext();
}
// post a function to be executed on the LLImageGL background thread
bool LLImageGLThread::post(const std::function<void()>& func)
{
try
{
mFunctionQueue.post(func);
}
catch (LLThreadSafeQueueInterrupt e)
{
return false;
}
return true;
}
//post a callback to be executed on the main thread
bool LLImageGLThread::postCallback(const std::function<void()>& callback)
{
try
{
mCallbackQueue.post(callback);
}
catch (LLThreadSafeQueueInterrupt e)
{
//thread is closing, drop request
return false;
}
return true;
}
void LLImageGLThread::executeCallbacks()
{
LL_PROFILE_ZONE_SCOPED;
//executed from main thread
mCallbackQueue.runPending();
}
void LLImageGLThread::run()
{
mWindow->makeContextCurrent(mContext);
gGL.init();
mFunctionQueue.runUntilClose();
gGL.shutdown();
mWindow->destroySharedContext(mContext);
}

View File

@ -35,9 +35,13 @@
#include "llrefcount.h"
#include "v2math.h"
#include "llunits.h"
#include "llthreadsafequeue.h"
#include "llrender.h"
#include "workqueue.h"
class LLTextureAtlas ;
class LLWindow;
#define BYTES_TO_MEGA_BYTES(x) ((x) >> 20)
#define MEGA_BYTES_TO_BYTES(x) ((x) << 20)
@ -48,7 +52,7 @@ class LLImageGL : public LLRefCount, public LLTrace::MemTrackable<LLImageGL>
public:
// These 2 functions replace glGenTextures() and glDeleteTextures()
static void generateTextures(S32 numTextures, U32 *textures);
static void deleteTextures(S32 numTextures, U32 *textures);
static void deleteTextures(S32 numTextures, const U32 *textures);
static void deleteDeadTextures();
// Size calculation
@ -105,13 +109,13 @@ public:
void setAllowCompression(bool allow) { mAllowCompression = allow; }
static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
BOOL createGLTexture() ;
BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE,
S32 category = sMaxCategories-1);
BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0);
void setImage(const LLImageRaw* imageraw);
BOOL setImage(const U8* data_in, BOOL data_hasmips = FALSE);
BOOL setImage(const U8* data_in, BOOL data_hasmips = FALSE, S32 usename = 0);
BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE);
BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE);
BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height);
@ -214,8 +218,9 @@ private:
bool mGLTextureCreated ;
LLGLuint mTexName;
LLGLuint mNewTexName = 0; // tex name set by background thread to be applied in main thread
U16 mWidth;
U16 mHeight;
U16 mHeight;
S8 mCurrentDiscardLevel;
S8 mDiscardLevelInAtlas;
@ -276,7 +281,8 @@ public:
#endif
public:
static void initClass(S32 num_catagories, BOOL skip_analyze_alpha = false);
static void initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha = false);
static void updateClass();
static void cleanupClass() ;
private:
@ -312,4 +318,33 @@ public:
};
class LLImageGLThread : public LLThread
{
public:
LLImageGLThread(LLWindow* window);
// post a function to be executed on the LLImageGL background thread
bool post(const std::function<void()>& func);
//post a callback to be executed on the main thread
bool postCallback(const std::function<void()>& callback);
void executeCallbacks();
void run() override;
// Work Queue for background thread
LL::WorkQueue mFunctionQueue;
// Work Queue for main thread (run from updateClass)
LL::WorkQueue mCallbackQueue;
LLWindow* mWindow;
void* mContext;
LLAtomicBool mFinished;
static LLImageGLThread* sInstance;
};
#endif // LL_LLIMAGEGL_H

View File

@ -36,7 +36,7 @@
#include "lltexture.h"
#include "llshadermgr.h"
LLRender gGL;
thread_local LLRender gGL;
// Handy copies of last good GL matrices
F32 gGLModelView[16];
@ -232,8 +232,24 @@ void LLTexUnit::disable(void)
}
}
void LLTexUnit::bindFast(LLTexture* texture)
{
LLImageGL* gl_tex = texture->getGLTexture();
glActiveTextureARB(GL_TEXTURE0_ARB + mIndex);
gGL.mCurrTextureUnitIndex = mIndex;
mCurrTexture = gl_tex->getTexName();
if (!mCurrTexture)
{
mCurrTexture = LLImageGL::sDefaultGLTexture->getTexName();
}
glBindTexture(sGLTextureType[gl_tex->getTarget()], mCurrTexture);
mHasMipMaps = gl_tex->mHasMipMaps;
}
bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
{
LL_PROFILE_ZONE_SCOPED;
stop_glerror();
if (mIndex >= 0)
{
@ -297,18 +313,20 @@ bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
return true;
}
bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind)
bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind, S32 usename)
{
stop_glerror();
if (mIndex < 0) return false;
U32 texname = usename ? usename : texture->getTexName();
if(!texture)
{
LL_DEBUGS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
return false;
}
if(!texture->getTexName())
if(!texname)
{
if(LLImageGL::sDefaultGLTexture && LLImageGL::sDefaultGLTexture->getTexName())
{
@ -318,7 +336,7 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind)
return false ;
}
if ((mCurrTexture != texture->getTexName()) || forceBind)
if ((mCurrTexture != texname) || forceBind)
{
gGL.flush();
stop_glerror();
@ -326,7 +344,7 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind)
stop_glerror();
enable(texture->getTarget());
stop_glerror();
mCurrTexture = texture->getTexName();
mCurrTexture = texname;
glBindTexture(sGLTextureType[texture->getTarget()], mCurrTexture);
stop_glerror();
texture->updateBindStats(texture->mTextureMemory);
@ -462,6 +480,28 @@ void LLTexUnit::unbind(eTextureType type)
}
}
void LLTexUnit::unbindFast(eTextureType type)
{
activate();
// Disabled caching of binding state.
if (mCurrTexType == type)
{
mCurrTexture = 0;
// Always make sure our texture color space is reset to linear. SRGB sampling should be opt-in in the vast majority of cases. Also prevents color space "popping".
mTexColorSpace = TCS_LINEAR;
if (type == LLTexUnit::TT_TEXTURE)
{
glBindTexture(sGLTextureType[type], sWhiteTexture);
}
else
{
glBindTexture(sGLTextureType[type], 0);
}
}
}
void LLTexUnit::setTextureAddressMode(eTextureAddressMode mode)
{
if (mIndex < 0 || mCurrTexture == 0) return;
@ -1288,8 +1328,6 @@ void LLRender::syncLightState()
void LLRender::syncMatrices()
{
stop_glerror();
static const U32 name[] =
{
LLShaderMgr::MODELVIEW_MATRIX,
@ -1460,8 +1498,6 @@ void LLRender::syncMatrices()
}
}
}
stop_glerror();
}
void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z)
@ -1893,6 +1929,7 @@ LLLightState* LLRender::getLight(U32 index)
void LLRender::setAmbientLightColor(const LLColor4& color)
{
LL_PROFILE_ZONE_SCOPED
if (color != mAmbientLightColor)
{
++mLightHash;
@ -1996,6 +2033,7 @@ void LLRender::flush()
{
if (mCount > 0)
{
LL_PROFILE_ZONE_SCOPED;
if (!mUIOffset.empty())
{
sUICalls++;

View File

@ -168,9 +168,20 @@ public:
// Binds the LLImageGL to this texture unit
// (automatically enables the unit for the LLImageGL's texture type)
bool bind(LLImageGL* texture, bool for_rendering = false, bool forceBind = false);
bool bind(LLImageGL* texture, bool for_rendering = false, bool forceBind = false, S32 usename = 0);
bool bind(LLTexture* texture, bool for_rendering = false, bool forceBind = false);
// bind implementation for inner loops
// makes the following assumptions:
// - No need for gGL.flush()
// - texture is not null
// - gl_tex->getTexName() is not zero
// - This texture is not being bound redundantly
// - USE_SRGB_DECODE is disabled
// - mTexOptionsDirty is false
// -
void bindFast(LLTexture* texture);
// Binds a cubemap to this texture unit
// (automatically enables the texture unit for cubemaps)
bool bind(LLCubeMap* cubeMap);
@ -187,6 +198,9 @@ public:
// (only if there's a texture of the given type currently bound)
void unbind(eTextureType type);
// Fast but unsafe version of unbind
void unbindFast(eTextureType type);
// Sets the addressing mode used to sample the texture
// Warning: this stays set for the bound texture forever,
// make sure you want to permanently change the address mode for the bound texture.
@ -534,7 +548,7 @@ extern F32 gGLLastProjection[16];
extern F32 gGLProjection[16];
extern S32 gGLViewport[4];
extern LLRender gGL;
extern thread_local LLRender gGL;
// This rotation matrix moves the default OpenGL reference frame
// (-Z at, Y up) to Cory's favorite reference frame (X at, Z up)

View File

@ -67,11 +67,9 @@ public:
virtual S32 getWidth(S32 discard_level = -1) const;
virtual S32 getHeight(S32 discard_level = -1) const;
virtual bool isActiveFetching();
virtual LLImageGL* getGLTexture() const;
private:
//note: do not make this function public.
virtual LLImageGL* getGLTexture() const;
virtual void updateBindStatsForTester();
};
#endif

View File

@ -91,6 +91,8 @@ LLVBOPool LLVertexBuffer::sDynamicIBOPool(GL_DYNAMIC_DRAW_ARB, GL_ELEMENT_ARRAY_
U32 LLVBOPool::sBytesPooled = 0;
U32 LLVBOPool::sIndexBytesPooled = 0;
U32 LLVBOPool::sNameIdx = 0;
U32 LLVBOPool::sNamePool[1024];
std::list<U32> LLVertexBuffer::sAvailableVAOName;
U32 LLVertexBuffer::sCurVAOName = 1;
@ -117,20 +119,23 @@ bool LLVertexBuffer::sMapped = false;
bool LLVertexBuffer::sUseStreamDraw = true;
bool LLVertexBuffer::sUseVAO = false;
bool LLVertexBuffer::sPreferStreamDraw = false;
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
LLVertexBuffer* LLVertexBuffer::sUtilityBuffer = NULL;
U32 LLVBOPool::genBuffer()
{
U32 ret = 0;
LL_PROFILE_ZONE_SCOPED
glGenBuffersARB(1, &ret);
return ret;
if (sNameIdx == 0)
{
glGenBuffersARB(1024, sNamePool);
sNameIdx = 1024;
}
return sNamePool[--sNameIdx];
}
void LLVBOPool::deleteBuffer(U32 name)
{
LL_PROFILE_ZONE_SCOPED
if (gGLManager.mInited)
{
LLVertexBuffer::unbind();
@ -153,6 +158,7 @@ LLVBOPool::LLVBOPool(U32 vboUsage, U32 vboType)
volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed)
{
LL_PROFILE_ZONE_SCOPED
llassert(vbo_block_size(size) == size);
volatile U8* ret = NULL;
@ -268,10 +274,12 @@ void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
void LLVBOPool::seedPool()
{
LL_PROFILE_ZONE_SCOPED
U32 dummy_name = 0;
if (mFreeList.size() < LL_VBO_POOL_SEED_COUNT)
{
LL_PROFILE_ZONE_NAMED("VBOPool Resize");
mFreeList.resize(LL_VBO_POOL_SEED_COUNT);
}
@ -412,6 +420,7 @@ void LLVertexBuffer::releaseVAOName(U32 name)
//static
void LLVertexBuffer::seedPools()
{
LL_PROFILE_ZONE_SCOPED
sStreamVBOPool.seedPool();
sDynamicVBOPool.seedPool();
sDynamicCopyVBOPool.seedPool();
@ -567,137 +576,22 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask)
}
//static
static LLTrace::BlockTimerStatHandle FTM_VB_DRAW_ARRAYS("drawArrays");
void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm)
void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos)
{
// <FS:ND/> Fast timers can have measurable impact in frequent places. A better all around solution would be to disable all fast timers until the fast timer view is open. But we're not there yet.
//LL_RECORD_BLOCK_TIME(FTM_VB_DRAW_ARRAYS);
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
gGL.syncMatrices();
U32 count = pos.size();
llassert(norm.size() >= pos.size());
llassert(count > 0);
if( count == 0 )
{
LL_WARNS() << "Called drawArrays with 0 vertices" << LL_ENDL;
return;
}
if( norm.size() < pos.size() )
{
LL_WARNS() << "Called drawArrays with #" << norm.size() << " normals and #" << pos.size() << " vertices" << LL_ENDL;
return;
}
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
//unbind();
//
//setupClientArrays(MAP_VERTEX | MAP_NORMAL);
//LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
//if (shader)
//{
// S32 loc = LLVertexBuffer::TYPE_VERTEX;
// if (loc > -1)
// {
// glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[0].mV);
// }
// loc = LLVertexBuffer::TYPE_NORMAL;
// if (loc > -1)
// {
// glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, norm[0].mV);
// }
//}
//else
//{
// glVertexPointer(3, GL_FLOAT, 0, pos[0].mV);
// glNormalPointer(GL_FLOAT, 0, norm[0].mV);
//}
//LLGLSLShader::startProfile();
//glDrawArrays(sGLMode[mode], 0, count);
//LLGLSLShader::stopProfile(count, mode);
U32 start_pos = 0;
U32 remaining = pos.size();
while (remaining > 0)
{
count = llmin(remaining, 65536u);
start_pos = pos.size() - remaining;
remaining -= count;
bool has_buffer = true;
if (!sUtilityBuffer)
{
sUtilityBuffer = new LLVertexBuffer(MAP_VERTEX | MAP_NORMAL | MAP_TEXCOORD0, GL_STREAM_DRAW);
has_buffer = sUtilityBuffer->allocateBuffer(count, count, true);
}
if (sUtilityBuffer->getNumVerts() < (S32)count)
{
has_buffer = sUtilityBuffer->resizeBuffer(count, count);
}
if (has_buffer)
{
LLStrider<LLVector3> vertex_strider;
LLStrider<LLVector3> normal_strider;
sUtilityBuffer->getVertexStrider(vertex_strider);
sUtilityBuffer->getNormalStrider(normal_strider);
for (U32 i = 0; i < count; ++i)
{
*(vertex_strider++) = pos[start_pos + i];
*(normal_strider++) = norm[start_pos + i];
}
sUtilityBuffer->setBuffer(MAP_VERTEX | MAP_NORMAL);
LLGLSLShader::startProfile();
sUtilityBuffer->drawArrays(mode, 0, count);
LLGLSLShader::stopProfile(count, mode);
}
else
{
unbind();
setupClientArrays(MAP_VERTEX | MAP_NORMAL);
LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
if (shader)
{
S32 loc = LLVertexBuffer::TYPE_VERTEX;
if (loc > -1)
{
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, pos[start_pos].mV);
}
loc = LLVertexBuffer::TYPE_NORMAL;
if (loc > -1)
{
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 0, norm[start_pos].mV);
}
}
else
{
glVertexPointer(3, GL_FLOAT, 0, pos[start_pos].mV);
glNormalPointer(GL_FLOAT, 0, norm[start_pos].mV);
}
LLGLSLShader::startProfile();
LL_PROFILER_GPU_ZONEC( "gl.DrawArrays", 0xFF0000 )
glDrawArrays(sGLMode[mode], 0, count);
LLGLSLShader::stopProfile(count, mode);
}
}
// </FS:Ansariel>
LL_PROFILE_ZONE_SCOPED;
gGL.begin(mode);
for (auto& v : pos)
{
gGL.vertex3fv(v.mV);
}
gGL.end();
gGL.flush();
}
//static
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
//void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp)
void LLVertexBuffer::drawElements(U32 mode, const S32 num_vertices, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp)
// </FS:Ansariel>
void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp)
{
LL_PROFILE_ZONE_SCOPED;
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
// <FS:Beq> FIRE-29679 trap empty calls that cause crashes when rezzing in OpenSim.
@ -709,12 +603,6 @@ void LLVertexBuffer::drawElements(U32 mode, const S32 num_vertices, const LLVect
// </FS:Beq>
// <FS:Ansariel> Crash fix due to invalid calls to drawElements by Drake Arconis
if (num_vertices <= 0)
{
LL_WARNS() << "Called drawElements with 0 vertices" << LL_ENDL;
return;
}
if (num_indices <= 0)
{
LL_WARNS() << "Called drawElements with 0 indices" << LL_ENDL;
@ -724,108 +612,35 @@ void LLVertexBuffer::drawElements(U32 mode, const S32 num_vertices, const LLVect
gGL.syncMatrices();
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
bool has_buffer = true;
if (!sUtilityBuffer)
{
sUtilityBuffer = new LLVertexBuffer(MAP_VERTEX | MAP_NORMAL | MAP_TEXCOORD0, GL_STREAM_DRAW);
has_buffer = sUtilityBuffer->allocateBuffer(num_vertices, num_indices, true);
}
if (sUtilityBuffer->getNumVerts() < num_vertices || sUtilityBuffer->getNumIndices() < num_indices)
{
has_buffer = sUtilityBuffer->resizeBuffer(llmax(sUtilityBuffer->getNumVerts(), num_vertices), llmax(sUtilityBuffer->getNumIndices(), num_indices));
}
// </FS:Ansariel>
U32 mask = LLVertexBuffer::MAP_VERTEX;
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
if (has_buffer)
{
LLStrider<U16> index_strider;
LLStrider<LLVector3> vertex_strider;
sUtilityBuffer->getIndexStrider(index_strider);
sUtilityBuffer->getVertexStrider(vertex_strider);
const S32 index_size = ((num_indices * sizeof(U16)) + 0xF) & ~0xF;
const S32 vertex_size = ((num_vertices * 4 * sizeof(F32)) + 0xF) & ~0xF;
LLVector4a::memcpyNonAliased16((F32*)index_strider.get(), (F32*)indicesp, index_size);
LLVector4a::memcpyNonAliased16((F32*)vertex_strider.get(), (F32*)pos, vertex_size);
}
// </FS:Ansariel>
if (tc)
{
mask = mask | LLVertexBuffer::MAP_TEXCOORD0;
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
if (has_buffer)
{
LLStrider<LLVector2> tc_strider;
sUtilityBuffer->getTexCoord0Strider(tc_strider);
const S32 tc_size = ((num_vertices * 2 * sizeof(F32)) + 0xF) & ~0xF;
LLVector4a::memcpyNonAliased16((F32*)tc_strider.get(), (F32*)tc, tc_size);
}
// </FS:Ansariel>
}
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
//unbind();
//
//setupClientArrays(mask);
unbind();
gGL.begin(mode);
//if (LLGLSLShader::sNoFixedFunction)
//{
// S32 loc = LLVertexBuffer::TYPE_VERTEX;
// glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);
// if (tc)
// {
// loc = LLVertexBuffer::TYPE_TEXCOORD0;
// glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, 0, tc);
// }
//}
//else
//{
// glTexCoordPointer(2, GL_FLOAT, 0, tc);
// glVertexPointer(3, GL_FLOAT, 16, pos);
//}
//LLGLSLShader::startProfile();
//glDrawElements(sGLMode[mode], num_indices, GL_UNSIGNED_SHORT, indicesp);
//LLGLSLShader::stopProfile(num_indices, mode);
if (has_buffer)
{
sUtilityBuffer->setBuffer(mask);
LLGLSLShader::startProfile();
sUtilityBuffer->draw(mode, num_indices, 0);
LLGLSLShader::stopProfile(num_indices, mode);
}
else
{
unbind();
setupClientArrays(mask);
if (LLGLSLShader::sNoFixedFunction)
{
S32 loc = LLVertexBuffer::TYPE_VERTEX;
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, 16, pos);
if (tc)
{
loc = LLVertexBuffer::TYPE_TEXCOORD0;
glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, 0, tc);
}
}
else
{
glTexCoordPointer(2, GL_FLOAT, 0, tc);
glVertexPointer(3, GL_FLOAT, 16, pos);
}
LLGLSLShader::startProfile();
LL_PROFILER_GPU_ZONEC( "gl.DrawElements", 0x80FF80 )
glDrawElements(sGLMode[mode], num_indices, GL_UNSIGNED_SHORT, indicesp);
LLGLSLShader::stopProfile(num_indices, mode);
}
// </FS:Ansariel>
if (tc != nullptr)
{
for (int i = 0; i < num_indices; ++i)
{
U16 idx = indicesp[i];
gGL.texCoord2fv(tc[idx].mV);
gGL.vertex3fv(pos[idx].getF32ptr());
}
}
else
{
for (int i = 0; i < num_indices; ++i)
{
U16 idx = indicesp[i];
gGL.vertex3fv(pos[idx].getF32ptr());
}
}
gGL.end();
gGL.flush();
}
void LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_offset) const
@ -944,6 +759,18 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi
placeFence();
}
void LLVertexBuffer::drawRangeFast(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
{
mMappable = false;
gGL.syncMatrices();
U16* idx = ((U16*)(U8*)mAlignedIndexOffset) + indices_offset;
LL_PROFILER_GPU_ZONEC("gl.DrawRangeElements", 0xFFFF00)
glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT,
idx);
}
void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
{
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
@ -996,52 +823,51 @@ void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
static LLTrace::BlockTimerStatHandle FTM_GL_DRAW_ARRAYS("GL draw arrays");
void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
{
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
mMappable = false;
gGL.syncMatrices();
llassert(mNumVerts >= 0);
if (first >= (U32) mNumVerts ||
first + count > (U32) mNumVerts)
{
LL_ERRS() << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << LL_ENDL;
}
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
mMappable = false;
gGL.syncMatrices();
if (mGLArray)
{
if (mGLArray != sGLRenderArray)
{
LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
}
}
else
{
if (mGLBuffer != sGLRenderBuffer || useVBOs() != sVBOActive)
{
LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
}
}
llassert(mNumVerts >= 0);
if (first >= (U32)mNumVerts ||
first + count > (U32)mNumVerts)
{
LL_ERRS() << "Bad vertex buffer draw range: [" << first << ", " << first + count << "]" << LL_ENDL;
}
if (mode >= LLRender::NUM_MODES)
{
LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
return;
}
if (mGLArray)
{
if (mGLArray != sGLRenderArray)
{
LL_ERRS() << "Wrong vertex array bound." << LL_ENDL;
}
}
else
{
if (mGLBuffer != sGLRenderBuffer || useVBOs() != sVBOActive)
{
LL_ERRS() << "Wrong vertex buffer bound." << LL_ENDL;
}
}
{
// <FS:ND/> Fast timers can have measurable impact in frequent places. A better all around solution would be to disable all fast timers until the fast timer view is open. But we're not there yet.
//LL_RECORD_BLOCK_TIME(FTM_GL_DRAW_ARRAYS);
stop_glerror();
LLGLSLShader::startProfile();
stop_glerror();
LL_PROFILER_GPU_ZONEC( "gl.DrawArrays", 0xFF4040 )
glDrawArrays(sGLMode[mode], first, count);
stop_glerror();
LLGLSLShader::stopProfile(count, mode);
}
if (mode >= LLRender::NUM_MODES)
{
LL_ERRS() << "Invalid draw mode: " << mode << LL_ENDL;
return;
}
stop_glerror();
placeFence();
{
LL_RECORD_BLOCK_TIME(FTM_GL_DRAW_ARRAYS);
stop_glerror();
LLGLSLShader::startProfile();
stop_glerror();
LL_PROFILER_GPU_ZONEC("gl.DrawArrays", 0xFF4040)
glDrawArrays(sGLMode[mode], first, count);
stop_glerror();
LLGLSLShader::stopProfile(count, mode);
}
stop_glerror();
placeFence();
}
//static
@ -1091,11 +917,6 @@ void LLVertexBuffer::cleanupClass()
sStreamVBOPool.cleanup();
sDynamicVBOPool.cleanup();
sDynamicCopyVBOPool.cleanup();
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
delete sUtilityBuffer;
sUtilityBuffer = NULL;
// </FS:Ansariel>
}
//----------------------------------------------------------------------------
@ -2457,6 +2278,21 @@ bool LLVertexBuffer::bindGLBuffer(bool force_bind)
return ret;
}
bool LLVertexBuffer::bindGLBufferFast()
{
if (mGLBuffer != sGLRenderBuffer || !sVBOActive)
{
glBindBufferARB(GL_ARRAY_BUFFER_ARB, mGLBuffer);
sGLRenderBuffer = mGLBuffer;
sBindCount++;
sVBOActive = true;
return true;
}
return false;
}
static LLTrace::BlockTimerStatHandle FTM_BIND_GL_INDICES("Bind Indices");
bool LLVertexBuffer::bindGLIndices(bool force_bind)
@ -2483,6 +2319,21 @@ bool LLVertexBuffer::bindGLIndices(bool force_bind)
return ret;
}
bool LLVertexBuffer::bindGLIndicesFast()
{
if (mGLIndices != sGLRenderIndices || !sIBOActive)
{
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mGLIndices);
sGLRenderIndices = mGLIndices;
sBindCount++;
sIBOActive = true;
return true;
}
return false;
}
void LLVertexBuffer::flush()
{
if (useVBOs())
@ -2675,6 +2526,26 @@ void LLVertexBuffer::setBuffer(U32 data_mask)
}
}
void LLVertexBuffer::setBufferFast(U32 data_mask)
{
//set up pointers if the data mask is different ...
bool setup = (sLastMask != data_mask);
const bool bindBuffer = bindGLBufferFast();
const bool bindIndices = bindGLIndicesFast();
setup = setup || bindBuffer || bindIndices;
setupClientArrays(data_mask);
if (data_mask && setup)
{
setupVertexBufferFast(data_mask);
sSetCount++;
}
}
// virtual (default)
void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
{
@ -2832,6 +2703,99 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
llglassertok();
}
void LLVertexBuffer::setupVertexBufferFast(U32 data_mask)
{
U8* base = (U8*)mAlignedOffset;
if (data_mask & MAP_NORMAL)
{
S32 loc = TYPE_NORMAL;
void* ptr = (void*)(base + mOffsets[TYPE_NORMAL]);
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_NORMAL], ptr);
}
if (data_mask & MAP_TEXCOORD3)
{
S32 loc = TYPE_TEXCOORD3;
void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD3]);
glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD3], ptr);
}
if (data_mask & MAP_TEXCOORD2)
{
S32 loc = TYPE_TEXCOORD2;
void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD2]);
glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD2], ptr);
}
if (data_mask & MAP_TEXCOORD1)
{
S32 loc = TYPE_TEXCOORD1;
void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD1]);
glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], ptr);
}
if (data_mask & MAP_TANGENT)
{
S32 loc = TYPE_TANGENT;
void* ptr = (void*)(base + mOffsets[TYPE_TANGENT]);
glVertexAttribPointerARB(loc, 4, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TANGENT], ptr);
}
if (data_mask & MAP_TEXCOORD0)
{
S32 loc = TYPE_TEXCOORD0;
void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD0]);
glVertexAttribPointerARB(loc, 2, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], ptr);
}
if (data_mask & MAP_COLOR)
{
S32 loc = TYPE_COLOR;
//bind emissive instead of color pointer if emissive is present
void* ptr = (data_mask & MAP_EMISSIVE) ? (void*)(base + mOffsets[TYPE_EMISSIVE]) : (void*)(base + mOffsets[TYPE_COLOR]);
glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_COLOR], ptr);
}
if (data_mask & MAP_EMISSIVE)
{
S32 loc = TYPE_EMISSIVE;
void* ptr = (void*)(base + mOffsets[TYPE_EMISSIVE]);
glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_EMISSIVE], ptr);
if (!(data_mask & MAP_COLOR))
{ //map emissive to color channel when color is not also being bound to avoid unnecessary shader swaps
loc = TYPE_COLOR;
glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_EMISSIVE], ptr);
}
}
if (data_mask & MAP_WEIGHT)
{
S32 loc = TYPE_WEIGHT;
void* ptr = (void*)(base + mOffsets[TYPE_WEIGHT]);
glVertexAttribPointerARB(loc, 1, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT], ptr);
}
if (data_mask & MAP_WEIGHT4)
{
S32 loc = TYPE_WEIGHT4;
void* ptr = (void*)(base + mOffsets[TYPE_WEIGHT4]);
glVertexAttribPointerARB(loc, 4, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], ptr);
}
if (data_mask & MAP_CLOTHWEIGHT)
{
S32 loc = TYPE_CLOTHWEIGHT;
void* ptr = (void*)(base + mOffsets[TYPE_CLOTHWEIGHT]);
glVertexAttribPointerARB(loc, 4, GL_FLOAT, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_CLOTHWEIGHT], ptr);
}
if (data_mask & MAP_TEXTURE_INDEX)
{
#if !LL_DARWIN
S32 loc = TYPE_TEXTURE_INDEX;
void* ptr = (void*)(base + mOffsets[TYPE_VERTEX] + 12);
glVertexAttribIPointer(loc, 1, GL_UNSIGNED_INT, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
#endif
}
if (data_mask & MAP_VERTEX)
{
S32 loc = TYPE_VERTEX;
void* ptr = (void*)(base + mOffsets[TYPE_VERTEX]);
glVertexAttribPointerARB(loc, 3, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
}
}
LLVertexBuffer::MappedRegion::MappedRegion(S32 type, S32 index, S32 count)
: mType(type), mIndex(index), mCount(count)
{

View File

@ -89,6 +89,9 @@ public:
std::vector<record_list_t> mFreeList;
std::vector<U32> mMissCount;
//used to avoid calling glGenBuffers for every VBO creation
static U32 sNamePool[1024];
static U32 sNameIdx;
};
@ -127,7 +130,7 @@ public:
static LLVBOPool sDynamicCopyVBOPool;
static LLVBOPool sStreamIBOPool;
static LLVBOPool sDynamicIBOPool;
static std::list<U32> sAvailableVAOName;
static U32 sCurVAOName;
@ -143,11 +146,8 @@ public:
static void initClass(bool use_vbo, bool no_vbo_mapping);
static void cleanupClass();
static void setupClientArrays(U32 data_mask);
static void pushPositions(U32 mode, const LLVector4a* pos, U32 count);
static void drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm);
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
//static void drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp);
static void drawElements(U32 mode, const S32 num_vertices, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp);
static void drawArrays(U32 mode, const std::vector<LLVector3>& pos);
static void drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp);
static void unbind(); //unbind any bound vertex buffer
@ -209,13 +209,17 @@ protected:
virtual ~LLVertexBuffer(); // use unref()
virtual void setupVertexBuffer(U32 data_mask); // pure virtual, called from mapBuffer()
virtual void setupVertexBuffer(U32 data_mask);
void setupVertexBufferFast(U32 data_mask);
void setupVertexArray();
void genBuffer(U32 size);
void genIndices(U32 size);
bool bindGLBuffer(bool force_bind = false);
bool bindGLBufferFast();
bool bindGLIndices(bool force_bind = false);
bool bindGLIndicesFast();
bool bindGLArray();
void releaseBuffer();
void releaseIndices();
@ -238,6 +242,8 @@ public:
// set for rendering
virtual void setBuffer(U32 data_mask); // calls setupVertexBuffer() if data_mask is not 0
void setBufferFast(U32 data_mask); // calls setupVertexBufferFast(), assumes data_mask is not 0 among other assumptions
void flush(); //flush pending data to GL memory
// allocate buffer
bool allocateBuffer(S32 nverts, S32 nindices, bool create);
@ -293,6 +299,9 @@ public:
void drawArrays(U32 mode, U32 offset, U32 count) const;
void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const;
//implementation for inner loops that does no safety checking
void drawRangeFast(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const;
//for debugging, validate data in given range is valid
void validateRange(U32 start, U32 end, U32 count, U32 offset) const;
@ -364,11 +373,6 @@ public:
static U32 sIndexCount;
static U32 sBindCount;
static U32 sSetCount;
// <FS:Ansariel> Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr
private:
static LLVertexBuffer* sUtilityBuffer;
// </FS:Ansariel>
};

View File

@ -358,6 +358,8 @@ public:
// handle refocusing.
static void closeFrontmostFloater();
static bool isQuitRequested() { return sQuitting; }
// LLNotification::Params contextualNotification(const std::string& name)
// {
// return LLNotification::Params(name).context(mNotificationContext);

View File

@ -1397,7 +1397,7 @@ bool LLNotifications::failedUniquenessTest(const LLSD& payload)
LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
{
return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName));
return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName).get());
}

View File

@ -632,29 +632,25 @@ void LLStatBar::draw()
void LLStatBar::setStat(const std::string& stat_name)
{
using namespace LLTrace;
const StatType<CountAccumulator>* count_stat;
const StatType<EventAccumulator>* event_stat;
const StatType<SampleAccumulator>* sample_stat;
const StatType<MemAccumulator>* mem_stat;
if ((count_stat = StatType<CountAccumulator>::getInstance(stat_name)))
if (auto count_stat = StatType<CountAccumulator>::getInstance(stat_name))
{
mStat.countStatp = count_stat;
mStat.countStatp = count_stat.get();
mStatType = STAT_COUNT;
}
else if ((event_stat = StatType<EventAccumulator>::getInstance(stat_name)))
else if (auto event_stat = StatType<EventAccumulator>::getInstance(stat_name))
{
mStat.eventStatp = event_stat;
mStat.eventStatp = event_stat.get();
mStatType = STAT_EVENT;
}
else if ((sample_stat = StatType<SampleAccumulator>::getInstance(stat_name)))
else if (auto sample_stat = StatType<SampleAccumulator>::getInstance(stat_name))
{
mStat.sampleStatp = sample_stat;
mStat.sampleStatp = sample_stat.get();
mStatType = STAT_SAMPLE;
}
else if ((mem_stat = StatType<MemAccumulator>::getInstance(stat_name)))
else if (auto mem_stat = StatType<MemAccumulator>::getInstance(stat_name))
{
mStat.memStatp = mem_stat;
mStat.memStatp = mem_stat.get();
mStatType = STAT_MEM;
}
}

View File

@ -295,7 +295,8 @@ attributedStringInfo getSegments(NSAttributedString *str)
if (vsync)
{
[glContext setValues:(const GLint*)1 forParameter:NSOpenGLCPSwapInterval];
GLint value = 1;
[glContext setValues:&value forParameter:NSOpenGLCPSwapInterval];
} else {
// supress this error after move to Xcode 7:
// error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull]

View File

@ -146,6 +146,12 @@ BOOL LLWindow::canDelete()
return TRUE;
}
//virtual
void LLWindow::setTitle(const std::string& title)
{
// the action happens in the platform specific impl
}
// virtual
void LLWindow::incBusyCount()
{

View File

@ -78,14 +78,35 @@ public:
BOOL setSize(LLCoordWindow size);
virtual void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true);
virtual BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) = 0;
virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
//create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread
// returns a pointer to be handed back to destroySharedConext/makeContextCurrent
virtual void* createSharedContext() = 0;
//make the given context current on the current thread
virtual void makeContextCurrent(void* context) = 0;
//destroy the given context that was retrieved by createSharedContext()
//Must be called on the same thread that called createSharedContext()
virtual void destroySharedContext(void* context) = 0;
virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
virtual BOOL getCursorPosition(LLCoordWindow *position) = 0;
#if LL_WINDOWS
virtual BOOL getCursorDelta(LLCoordCommon* delta) = 0;
#endif
virtual void showCursor() = 0;
virtual void hideCursor() = 0;
virtual BOOL isCursorHidden() = 0;
virtual void showCursorFromMouseMove() = 0;
virtual void hideCursorUntilMouseMove() = 0;
// Provide a way to set the Viewer window title after the
// windows has been created. The initial use case for this
// is described in SL-16102 (update window title with agent
// name, location etc. for non-interactive viewer) but it
// may also be useful in other cases.
virtual void setTitle(const std::string& title);
// These two functions create a way to make a busy cursor instead
// of an arrow when someone's busy doing something. Draw an
// arrow/hour if busycount > 0.
@ -170,12 +191,6 @@ public:
// Get system UI size based on DPI (for 96 DPI UI size should be 1.0)
virtual F32 getSystemUISize() { return 1.0f; }
// <FS:TT> Window Title Access
//this needs to be overridden for all platforms
virtual void setTitle(const std::string& win_title) {};
// </FS:TT>
static std::vector<std::string> getDisplaysResolutionList();
// windows only DirectInput8 for joysticks

View File

@ -49,8 +49,14 @@ public:
/*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;};
/*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;};
/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;};
void* createSharedContext() { return nullptr; }
void makeContextCurrent(void*) {}
void destroySharedContext(void*) {}
/*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;};
/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;};
#if LL_WINDOWS
/*virtual*/ BOOL getCursorDelta(LLCoordCommon* delta) { return FALSE; }
#endif
/*virtual*/ void showCursor() {};
/*virtual*/ void hideCursor() {};
/*virtual*/ void showCursorFromMouseMove() {};

View File

@ -1960,6 +1960,34 @@ void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)
allowDirectMarkedTextInput(b, mGLView); // mLanguageTextInputAllowed and mMarkedTextAllowed should be updated at once (by Pell Smit
}
class sharedContext
{
public:
CGLContextObj mContext;
};
void* LLWindowMacOSX::createSharedContext()
{
sharedContext* sc = new sharedContext();
CGLCreateContext(mPixelFormat, mContext, &(sc->mContext));
return (void *)sc;
}
void LLWindowMacOSX::makeContextCurrent(void* context)
{
CGLSetCurrentContext(((sharedContext*)context)->mContext);
}
void LLWindowMacOSX::destroySharedContext(void* context)
{
sharedContext* sc = (sharedContext*)context;
CGLDestroyContext(sc->mContext);
delete sc;
}
void LLWindowMacOSX::interruptLanguageTextInput()
{
commitCurrentPreedit(mGLView);

View File

@ -41,78 +41,77 @@
#undef verify
#undef require
class LLWindowMacOSX : public LLWindow
{
public:
/*virtual*/ void show();
/*virtual*/ void hide();
/*virtual*/ void close();
/*virtual*/ BOOL getVisible();
/*virtual*/ BOOL getMinimized();
/*virtual*/ BOOL getMaximized();
/*virtual*/ BOOL maximize();
/*virtual*/ void minimize();
/*virtual*/ void restore();
/*virtual*/ BOOL getFullscreen();
/*virtual*/ BOOL getPosition(LLCoordScreen *position);
/*virtual*/ BOOL getSize(LLCoordScreen *size);
/*virtual*/ BOOL getSize(LLCoordWindow *size);
/*virtual*/ BOOL setPosition(LLCoordScreen position);
/*virtual*/ BOOL setSizeImpl(LLCoordScreen size);
/*virtual*/ BOOL setSizeImpl(LLCoordWindow size);
/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL);
/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
/*virtual*/ void showCursor();
/*virtual*/ void hideCursor();
/*virtual*/ void showCursorFromMouseMove();
/*virtual*/ void hideCursorUntilMouseMove();
/*virtual*/ BOOL isCursorHidden();
/*virtual*/ void updateCursor();
/*virtual*/ ECursorType getCursor() const;
/*virtual*/ void captureMouse();
/*virtual*/ void releaseMouse();
/*virtual*/ void setMouseClipping( BOOL b );
/*virtual*/ BOOL isClipboardTextAvailable();
/*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst);
/*virtual*/ BOOL copyTextToClipboard(const LLWString & src);
/*virtual*/ void flashIcon(F32 seconds);
/*virtual*/ F32 getGamma();
/*virtual*/ BOOL setGamma(const F32 gamma); // Set the gamma
/*virtual*/ U32 getFSAASamples();
/*virtual*/ void setFSAASamples(const U32 fsaa_samples);
/*virtual*/ BOOL restoreGamma(); // Restore original gamma table (before updating gamma)
/*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
/*virtual*/ void gatherInput();
/*virtual*/ void delayInputProcessing() {};
/*virtual*/ void swapBuffers();
void show() override;
void hide() override;
void close() override;
BOOL getVisible() override;
BOOL getMinimized() override;
BOOL getMaximized() override;
BOOL maximize() override;
void minimize() override;
void restore() override;
BOOL getFullscreen();
BOOL getPosition(LLCoordScreen *position) override;
BOOL getSize(LLCoordScreen *size) override;
BOOL getSize(LLCoordWindow *size) override;
BOOL setPosition(LLCoordScreen position) override;
BOOL setSizeImpl(LLCoordScreen size) override;
BOOL setSizeImpl(LLCoordWindow size) override;
BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) override;
BOOL setCursorPosition(LLCoordWindow position) override;
BOOL getCursorPosition(LLCoordWindow *position) override;
void showCursor() override;
void hideCursor() override;
void showCursorFromMouseMove() override;
void hideCursorUntilMouseMove() override;
BOOL isCursorHidden() override;
void updateCursor() override;
ECursorType getCursor() const override;
void captureMouse() override;
void releaseMouse() override;
void setMouseClipping( BOOL b ) override;
BOOL isClipboardTextAvailable() override;
BOOL pasteTextFromClipboard(LLWString &dst) override;
BOOL copyTextToClipboard(const LLWString & src) override;
void flashIcon(F32 seconds) override;
F32 getGamma() override;
BOOL setGamma(const F32 gamma) override; // Set the gamma
U32 getFSAASamples() override;
void setFSAASamples(const U32 fsaa_samples) override;
BOOL restoreGamma() override; // Restore original gamma table (before updating gamma)
ESwapMethod getSwapMethod() override { return mSwapMethod; }
void gatherInput() override;
void delayInputProcessing() override {};
void swapBuffers() override;
// handy coordinate space conversion routines
/*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to);
/*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to);
/*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to);
/*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to);
/*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to);
/*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to);
BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to) override;
BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to) override;
BOOL convertCoords(LLCoordWindow from, LLCoordGL *to) override;
BOOL convertCoords(LLCoordGL from, LLCoordWindow *to) override;
BOOL convertCoords(LLCoordScreen from, LLCoordGL *to) override;
BOOL convertCoords(LLCoordGL from, LLCoordScreen *to) override;
/*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions);
/*virtual*/ F32 getNativeAspectRatio();
/*virtual*/ F32 getPixelAspectRatio();
/*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; }
LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override;
F32 getNativeAspectRatio() override;
F32 getPixelAspectRatio() override;
void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; }
/*virtual*/ void beforeDialog();
/*virtual*/ void afterDialog();
void beforeDialog() override;
void afterDialog() override;
/*virtual*/ BOOL dialogColorPicker(F32 *r, F32 *g, F32 *b);
BOOL dialogColorPicker(F32 *r, F32 *g, F32 *b) override;
/*virtual*/ void *getPlatformWindow();
/*virtual*/ void bringToFront() {};
void *getPlatformWindow() override;
void bringToFront() override {};
/*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b);
/*virtual*/ void interruptLanguageTextInput();
/*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async);
/*virtual*/ F32 getSystemUISize();
void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b) override;
void interruptLanguageTextInput() override;
void spawnWebBrowser(const std::string& escaped_url, bool async) override;
F32 getSystemUISize() override;
/*virtual*/ void openFile(const std::string& file_name);
/*virtual*/ void setTitle(const std::string& title);
@ -121,7 +120,7 @@ public:
static std::vector<std::string> getDynamicFallbackFontList();
// Provide native key event data
/*virtual*/ LLSD getNativeKeyData();
LLSD getNativeKeyData() override;
void* getWindow() { return mWindow; }
LLWindowCallbacks* getCallbacks() { return mCallbacks; }
@ -134,6 +133,15 @@ public:
bool allowsLanguageInput() { return mLanguageTextInputAllowed; }
//create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread
// returns a pointer to be handed back to destroySharedConext/makeContextCurrent
void* createSharedContext() override;
//make the given context current on the current thread
void makeContextCurrent(void* context) override;
//destroy the given context that was retrieved by createSharedContext()
//Must be called on the same thread that called createSharedContext()
void destroySharedContext(void* context) override;
protected:
LLWindowMacOSX(LLWindowCallbacks* callbacks,
const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags,
@ -145,7 +153,7 @@ protected:
//void initCursors();
void initCursors(BOOL useLegacyCursors); // <FS:LO> Legacy cursor setting from main program
BOOL isValid();
BOOL isValid() override;
void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
@ -161,7 +169,7 @@ protected:
BOOL shouldPostQuit() { return mPostQuit; }
//Satisfy MAINT-3135 and MAINT-3288 with a flag.
/*virtual */ void setOldResize(bool oldresize) {setResizeMode(oldresize, mGLView); }
/*virtual */ void setOldResize(bool oldresize) override {setResizeMode(oldresize, mGLView); }
private:
void restoreGLContext();
@ -237,9 +245,9 @@ public:
LLSplashScreenMacOSX();
virtual ~LLSplashScreenMacOSX();
/*virtual*/ void showImpl();
/*virtual*/ void updateImpl(const std::string& mesg);
/*virtual*/ void hideImpl();
void showImpl();
void updateImpl(const std::string& mesg);
void hideImpl();
private:
WindowRef mWindow;

File diff suppressed because it is too large Load Diff

View File

@ -33,11 +33,47 @@
#include "llwindow.h"
#include "llwindowcallbacks.h"
#include "lldragdropwin32.h"
#include "llthread.h"
#include "llthreadsafequeue.h"
#include "llmutex.h"
// Hack for async host by name
#define LL_WM_HOST_RESOLVED (WM_APP + 1)
typedef void (*LLW32MsgCallback)(const MSG &msg);
class LLWindowWin32;
// Thread that owns the Window Handle
class LLWindowWin32Thread : public LLThread
{
public:
class Message
{
public:
LRESULT mMsg;
};
static const int MAX_QUEUE_SIZE = 2048;
LLThreadSafeQueue<MSG> mMessageQueue;
LLThreadSafeQueue<std::function<void()>> mFunctionQueue;
bool mFinished = false;
LLWindowWin32Thread(LLWindowWin32* window);
void run() override;
void post(const std::function<void()>& func);
private:
// call PeekMessage and pull enqueue messages for later processing
void gatherInput();
LLWindowWin32* mWindow = nullptr;
};
class LLWindowWin32 : public LLWindow
{
public:
@ -58,8 +94,13 @@ public:
/*virtual*/ BOOL setSizeImpl(LLCoordScreen size);
/*virtual*/ BOOL setSizeImpl(LLCoordWindow size);
/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL);
/*virtual*/ void setTitle(const std::string& title);
void* createSharedContext() override;
void makeContextCurrent(void* context) override;
void destroySharedContext(void* context) override;
/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
/*virtual*/ BOOL getCursorDelta(LLCoordCommon* delta);
/*virtual*/ void showCursor();
/*virtual*/ void hideCursor();
/*virtual*/ void showCursorFromMouseMove();
@ -113,10 +154,6 @@ public:
/*virtual*/ F32 getSystemUISize();
// <FS:TT> Window Title Access
/*virtual*/ void setTitle(const std::string& win_title);
// </FS:TT>
LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url );
static std::vector<std::string> getDisplaysResolutionList();
@ -182,9 +219,9 @@ protected:
WCHAR *mWindowTitle;
WCHAR *mWindowClassName;
HWND mWindowHandle; // window handle
HGLRC mhRC; // OpenGL rendering context
HDC mhDC; // Windows Device context handle
HWND mWindowHandle = 0; // window handle
HGLRC mhRC = 0; // OpenGL rendering context
HDC mhDC = 0; // Windows Device context handle
HINSTANCE mhInstance; // handle to application instance
WNDPROC mWndProc; // user-installable window proc
RECT mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse()
@ -193,6 +230,14 @@ protected:
F32 mNativeAspectRatio;
HCURSOR mCursor[ UI_CURSOR_COUNT ]; // Array of all mouse cursors
LLCoordWindow mCursorPosition; // mouse cursor position, should only be mutated on main thread
LLMutex mRawMouseMutex;
RAWINPUTDEVICE mRawMouse;
LLCoordWindow mLastCursorPosition; // mouse cursor position from previous frame
LLCoordCommon mRawMouseDelta; // raw mouse delta according to window thread
LLCoordCommon mMouseFrameDelta; // how much the mouse moved between the last two calls to gatherInput
MASK mMouseMask;
static BOOL sIsClassRegistered; // has the window class been registered?
@ -203,7 +248,6 @@ protected:
BOOL mCustomGammaSet;
LPWSTR mIconResource;
BOOL mMousePositionModified;
BOOL mInputProcessingPaused;
// The following variables are for Language Text Input control.
@ -231,7 +275,14 @@ protected:
BOOL mMouseVanish;
LLWindowWin32Thread* mWindowThread = nullptr;
LLThreadSafeQueue<std::function<void()>> mFunctionQueue;
LLThreadSafeQueue<std::function<void()>> mMouseQueue;
void post(const std::function<void()>& func);
void postMouseButtonEvent(const std::function<void()>& func);
friend class LLWindowManager;
friend class LLWindowWin32Thread;
// <FS:ND> Allow to query for window chrome sizes.
public:
virtual void getWindowChrome( U32 &aChromeW, U32 &aChromeH );

View File

@ -452,8 +452,8 @@ public:
const T& default_value,
const std::string& comment = "Declared In Code")
{
mCachedControlPtr = LLControlCache<T>::getInstance(name);
if (mCachedControlPtr.isNull())
mCachedControlPtr = LLControlCache<T>::getInstance(name).get();
if (! mCachedControlPtr)
{
mCachedControlPtr = new LLControlCache<T>(group, name, default_value, comment);
}
@ -462,8 +462,8 @@ public:
LLCachedControl(LLControlGroup& group,
const std::string& name)
{
mCachedControlPtr = LLControlCache<T>::getInstance(name);
if (mCachedControlPtr.isNull())
mCachedControlPtr = LLControlCache<T>::getInstance(name).get();
if (! mCachedControlPtr)
{
mCachedControlPtr = new LLControlCache<T>(group, name);
}

View File

@ -114,9 +114,6 @@ if (DARWIN)
LINK_FLAGS "-stdlib=libc++ -exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp"
)
## turns on C++11 using Cmake
target_compile_features(media_plugin_cef PRIVATE cxx_range_for)
add_custom_command(TARGET media_plugin_cef
POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "@executable_path/Chromium Embedded Framework"
"@executable_path/../../../../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework"

View File

@ -248,6 +248,14 @@
<string>NoInventoryLibrary</string>
</map>
<key>noninteractive</key>
<map>
<key>desc</key>
<string>Run in semi-headless mode where only login and logout need to work.</string>
<key>map-to</key>
<string>NonInteractive</string>
</map>
<key>nonotifications</key>
<map>
<key>desc</key>

View File

@ -5451,13 +5451,13 @@
<key>DisableVerticalSync</key>
<map>
<key>Comment</key>
<string>Update frames as fast as possible (FALSE = update frames between display scans)</string>
<string>Update frames as fast as possible (FALSE = update frames between display scans). Requires restart.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
<integer>0</integer>
<key>Backup</key>
<integer>0</integer>
</map>
@ -9779,6 +9779,17 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>NonInteractive</key>
<map>
<key>Comment</key>
<string>Run in a semi-headless mode where only logging in and logging out needs to work.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>NonvisibleObjectsInMemoryTime</key>
<map>
<key>Comment</key>
@ -12835,7 +12846,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
<integer>1</integer>
<key>Backup</key>
<integer>0</integer>
</map>
@ -21863,6 +21874,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Backup</key>
<integer>0</integer>
</map>
<key>UpdateAppWindowTitleBar</key>
<map>
<key>Comment</key>
<string>Updates the application window title bar with brief information about user/location</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSShowServerVersionChangeNotice</key>
<map>
<key>Comment</key>

View File

@ -1686,7 +1686,7 @@ void LLAgent::resetControlFlags()
//-----------------------------------------------------------------------------
void LLAgent::setAFK()
{
if (!gAgent.getRegion())
if (gNonInteractive || !gAgent.getRegion())
{
// Don't set AFK if we're not talking to a region yet.
return;

View File

@ -766,7 +766,6 @@ LLAppViewer::LLAppViewer()
mFastTimerLogThread(NULL),
mSettingsLocationList(NULL),
mIsFirstRun(false),
//mMinMicroSecPerFrame(0.f), // <FS:Ansariel> FIRE-22297: FPS limiter not working properly on Mac/Linux
mSaveSettingsOnExit(true), // <FS:Zi> Backup Settings
mPurgeTextures(false) // <FS:Ansariel> FIRE-13066
{
@ -1051,8 +1050,6 @@ bool LLAppViewer::init()
LLNotifications::instance();
LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ;
writeSystemInfo();
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@ -1178,6 +1175,9 @@ bool LLAppViewer::init()
initWindow();
LL_INFOS("InitInfo") << "Window is initialized." << LL_ENDL ;
// writeSystemInfo can be called after window is initialized (gViewerWindow non-null)
writeSystemInfo();
// initWindow also initializes the Feature List, so now we can initialize this global.
LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap");
@ -1202,19 +1202,6 @@ bool LLAppViewer::init()
return 0;
}
// If we don't have the right shader requirements.
if (!gGLManager.mHasShaderObjects
|| !gGLManager.mHasVertexShader
|| !gGLManager.mHasFragmentShader)
{
LLUIString details = LLNotifications::instance().getGlobalString("UnsupportedShaderRequirements");
OSMessageBox(
details.getString(),
LLStringUtil::null,
OSMB_OK);
return 0;
}
// Without SSE2 support we will crash almost immediately, warn here.
if (!gSysCPU.hasSSE2())
{
@ -1530,12 +1517,6 @@ bool LLAppViewer::init()
joystick = LLViewerJoystick::getInstance();
joystick->setNeedsReset(true);
/*----------------------------------------------------------------------*/
// <FS:Ansariel> FIRE-22297: FPS limiter not working properly on Mac/Linux
//gSavedSettings.getControl("FramePerSecondLimit")->getSignal()->connect(boost::bind(&LLAppViewer::onChangeFrameLimit, this, _2));
//onChangeFrameLimit(gSavedSettings.getLLSD("FramePerSecondLimit"));
// </FS:Ansariel>
// Load User's bindings
loadKeyBindings();
@ -1825,25 +1806,6 @@ bool LLAppViewer::doFrame()
display();
// <FS:Ansariel> FIRE-22297: FPS limiter not working properly on Mac/Linux
//static U64 last_call = 0;
//if (!gTeleportDisplay)
//{
// // Frame/draw throttling, controlled by FramePerSecondLimit
// U64 elapsed_time = LLTimer::getTotalTime() - last_call;
// if (elapsed_time < mMinMicroSecPerFrame)
// {
// LL_PROFILE_ZONE_WARN("Sleep1")
// // llclamp for when time function gets funky
// U64 sleep_time = llclamp(mMinMicroSecPerFrame - elapsed_time, (U64)1, (U64)1e6);
// LL_PROFILE_ZONE_NAMED( "df Snapshot" )
// micro_sleep(sleep_time, 0);
// }
//}
//last_call = LLTimer::getTotalTime();
// </FS:Ansariel>
}
{
LL_PROFILE_ZONE_NAMED( "df Snapshot" )
pingMainloopTimeout("Main:Snapshot");
@ -1851,6 +1813,7 @@ bool LLAppViewer::doFrame()
LLFloaterOutfitSnapshot::update();
gGLActive = FALSE;
}
}
}
{
@ -1874,6 +1837,14 @@ bool LLAppViewer::doFrame()
ms_sleep(yield_time);
}
if (gNonInteractive)
{
S32 non_interactive_ms_sleep_time = 100;
LLAppViewer::getTextureCache()->pause();
LLAppViewer::getImageDecodeThread()->pause();
ms_sleep(non_interactive_ms_sleep_time);
}
// yield cooperatively when not running as foreground window
// and when not quiting (causes trouble at mac's cleanup stage)
if (!LLApp::isExiting()
@ -1881,8 +1852,8 @@ bool LLAppViewer::doFrame()
|| !gFocusMgr.getAppHasFocus()))
{
// Sleep if we're not rendering, or the window is minimized.
static LLCachedControl<S32> s_bacground_yeild_time(gSavedSettings, "BackgroundYieldTime", 40);
S32 milliseconds_to_sleep = llclamp((S32)s_bacground_yeild_time, 0, 1000);
static LLCachedControl<S32> s_background_yield_time(gSavedSettings, "BackgroundYieldTime", 40);
S32 milliseconds_to_sleep = llclamp((S32)s_background_yield_time, 0, 1000);
// don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
// of equal priority on Windows
if (milliseconds_to_sleep > 0)
@ -2793,7 +2764,7 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
LL_INFOS("Settings") << "Attempting to load settings for the group " << file.name()
<< " - from location " << location_key << LL_ENDL;
LLControlGroup* settings_group = LLControlGroup::getInstance(file.name);
auto settings_group = LLControlGroup::getInstance(file.name);
if(!settings_group)
{
LL_WARNS("Settings") << "No matching settings group for name " << file.name() << LL_ENDL;
@ -2888,6 +2859,38 @@ namespace
}
} // anonymous namespace
// Set a named control temporarily for this session, as when set via the command line --set option.
// Name can be specified as "<control_group>.<control_name>", with default group being Global.
bool tempSetControl(const std::string& name, const std::string& value)
{
std::string name_part;
std::string group_part;
LLControlVariable* control = NULL;
// Name can be further split into ControlGroup.Name, with the default control group being Global
size_t pos = name.find('.');
if (pos != std::string::npos)
{
group_part = name.substr(0, pos);
name_part = name.substr(pos+1);
LL_INFOS() << "Setting " << group_part << "." << name_part << " to " << value << LL_ENDL;
auto g = LLControlGroup::getInstance(group_part);
if (g) control = g->getControl(name_part);
}
else
{
LL_INFOS() << "Setting Global." << name << " to " << value << LL_ENDL;
control = gSavedSettings.getControl(name);
}
if (control)
{
control->setValue(value, false);
return true;
}
return false;
}
bool LLAppViewer::initConfiguration()
{
//Load settings files list
@ -2933,7 +2936,7 @@ bool LLAppViewer::initConfiguration()
// load defaults overide here. Can not use settings_files.xml as path is different then above loading of defaults.
std::string fsdata_defaults = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, llformat("fsdata_defaults.%s.xml", LLVersionInfo::getInstance()->getShortVersion().c_str()));
std::string fsdata_global = "Global";
LLControlGroup* settings_group = LLControlGroup::getInstance(fsdata_global);
std::shared_ptr<LLControlGroup> settings_group = LLControlGroup::getInstance(fsdata_global);
if(settings_group && settings_group->loadFromFile(fsdata_defaults, set_defaults))
{
LL_INFOS() << "Loaded settings file " << fsdata_defaults << LL_ENDL;
@ -2950,12 +2953,7 @@ bool LLAppViewer::initConfiguration()
#ifndef LL_RELEASE_FOR_DOWNLOAD
// provide developer build only overrides for these control variables that are not
// persisted to settings.xml
LLControlVariable* c = gSavedSettings.getControl("ShowConsoleWindow");
if (c)
{
c->setValue(true, false);
}
c = gSavedSettings.getControl("AllowMultipleViewers");
LLControlVariable* c = gSavedSettings.getControl("AllowMultipleViewers");
if (c)
{
c->setValue(true, false);
@ -3123,9 +3121,10 @@ bool LLAppViewer::initConfiguration()
disableCrashlogger();
}
gNonInteractive = gSavedSettings.getBOOL("NonInteractive");
// Handle initialization from settings.
// Start up the debugging console before handling other options.
if (gSavedSettings.getBOOL("ShowConsoleWindow"))
if (gSavedSettings.getBOOL("ShowConsoleWindow") && !gNonInteractive)
{
initConsole();
}
@ -3158,33 +3157,9 @@ bool LLAppViewer::initConfiguration()
{
const std::string& name = *itr;
const std::string& value = *(++itr);
std::string name_part;
std::string group_part;
LLControlVariable* control = NULL;
// Name can be further split into ControlGroup.Name, with the default control group being Global
size_t pos = name.find('.');
if (pos != std::string::npos)
{
group_part = name.substr(0, pos);
name_part = name.substr(pos+1);
LL_INFOS() << "Setting " << group_part << "." << name_part << " to " << value << LL_ENDL;
LLControlGroup* g = LLControlGroup::getInstance(group_part);
if (g) control = g->getControl(name_part);
}
else
{
LL_INFOS() << "Setting Global." << name << " to " << value << LL_ENDL;
control = gSavedSettings.getControl(name);
}
if (control)
if (!tempSetControl(name,value))
{
control->setValue(value, false);
}
else
{
LL_WARNS() << "Failed --set " << name << ": setting name unknown." << LL_ENDL;
LL_WARNS() << "Failed --set " << name << ": setting name unknown." << LL_ENDL;
}
}
}
@ -3291,6 +3266,19 @@ bool LLAppViewer::initConfiguration()
}
}
if (gNonInteractive)
{
tempSetControl("AllowMultipleViewers", "TRUE");
tempSetControl("SLURLPassToOtherInstance", "FALSE");
tempSetControl("RenderWater", "FALSE");
tempSetControl("FlyingAtExit", "FALSE");
tempSetControl("WindowWidth", "1024");
tempSetControl("WindowHeight", "200");
LLError::setEnabledLogTypesMask(0);
llassert_always(!gSavedSettings.getBOOL("SLURLPassToOtherInstance"));
}
// Handle slurl use. NOTE: Don't let SL-55321 reappear.
// This initial-SLURL logic, up through the call to
// sendURLToOtherInstance(), must precede LLSplashScreen::show() --
@ -4327,16 +4315,15 @@ void LLAppViewer::writeSystemInfo()
gDebugInfo["FirstLogin"] = LLSD::Boolean(gAgent.isFirstLogin());
gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
gDebugInfo["StartupState"] = LLStartUp::getStartupStateString();
// <FS:ND> FIRE-31153, do not use gViewerWindow->getWindow which equals nullptr at this point
//std::vector<std::string> resolutions = gViewerWindow->getWindow()->getDisplaysResolutionList();
std::vector<std::string> resolutions = LLWindow::getDisplaysResolutionList();
// </FS:ND>
for (auto res_iter : resolutions)
{
gDebugInfo["DisplayInfo"].append(res_iter);
}
if (gViewerWindow)
{
std::vector<std::string> resolutions = gViewerWindow->getWindow()->getDisplaysResolutionList();
for (auto res_iter : resolutions)
{
gDebugInfo["DisplayInfo"].append(res_iter);
}
}
writeDebugInfo(); // Save out debug_info.log early, in case of crash.
}
@ -5583,6 +5570,7 @@ void LLAppViewer::idle()
LLNotificationsUI::LLToast::updateClass();
LLSmoothInterpolation::updateInterpolants();
LLMortician::updateClass();
LLImageGL::updateClass();
LLFilePickerThread::clearDead(); //calls LLFilePickerThread::notify()
LLDirPickerThread::clearDead();
F32 dt_raw = idle_timer.getElapsedTimeAndResetF32();
@ -6427,21 +6415,6 @@ void LLAppViewer::disconnectViewer()
LLUrlEntryParcel::setDisconnected(gDisconnected);
}
// <FS:Ansariel> FIRE-22297: FPS limiter not working properly on Mac/Linux
//bool LLAppViewer::onChangeFrameLimit(LLSD const & evt)
//{
// if (evt.asInteger() > 0)
// {
// mMinMicroSecPerFrame = (U64)(1000000.0f / F32(evt.asInteger()));
// }
// else
// {
// mMinMicroSecPerFrame = 0;
// }
// return false;
//}
// </FS:Ansariel>
void LLAppViewer::forceErrorLLError()
{
LL_ERRS() << "This is a deliberate llerror" << LL_ENDL;

View File

@ -275,9 +275,6 @@ private:
void sendLogoutRequest();
void disconnectViewer();
// <FS:Ansariel> FIRE-22297: FPS limiter not working properly on Mac/Linux
//bool onChangeFrameLimit(LLSD const & evt);
// *FIX: the app viewer class should be some sort of singleton, no?
// Perhaps its child class is the singleton and this should be an abstract base.
static LLAppViewer* sInstance;
@ -344,11 +341,7 @@ private:
// llcorehttp library init/shutdown helper
LLAppCoreHttp mAppCoreHttp;
bool mIsFirstRun;
// <FS:Ansariel> FIRE-22297: FPS limiter not working properly on Mac/Linux
//U64 mMinMicroSecPerFrame; // frame throttling
bool mIsFirstRun;
// <FS:Zi> Backup Settings
public:
void setSaveSettingsOnExit(bool state) {mSaveSettingsOnExit = state; };

View File

@ -453,7 +453,8 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
{
// Call Tracy first thing to have it allocate memory
// https://github.com/wolfpld/tracy/issues/196
LL_PROFILER_FRAME_END
LL_PROFILER_FRAME_END;
LL_PROFILER_SET_THREAD_NAME("App");
const S32 MAX_HEAPS = 255;
DWORD heap_enable_lfh_error[MAX_HEAPS];

View File

@ -43,14 +43,14 @@ LLBrowserNotification::LLBrowserNotification()
bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification)
{
LLUUID media_id = notification->getPayload()["media_id"].asUUID();
LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(media_id);
auto media_instance = LLMediaCtrl::getInstance(media_id);
if (media_instance)
{
media_instance->showNotification(notification);
}
else if (LLViewerMediaFocus::instance().getControlsMediaID() == media_id)
{
LLViewerMediaImpl* impl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(media_id);
auto impl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(media_id);
if (impl)
{
impl->showNotification(notification);

View File

@ -265,7 +265,6 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds)
LLAvatarName av_name;
LLAvatarNameCache::get(agent_id, &av_name);
addChangedMask(LLFriendObserver::ADD, agent_id);
LL_DEBUGS() << "Added buddy " << agent_id
<< ", " << (mBuddyInfo[agent_id]->isOnline() ? "Online" : "Offline")
<< ", TO: " << mBuddyInfo[agent_id]->getRightsGrantedTo()
@ -504,6 +503,7 @@ void LLAvatarTracker::notifyObservers()
// new masks and ids will be processed later from idle.
return;
}
LL_PROFILE_ZONE_SCOPED
mIsNotifyObservers = TRUE;
observer_list_t observers(mObservers);
@ -762,6 +762,7 @@ void LLAvatarTracker::processChangeUserRights(LLMessageSystem* msg, void**)
void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
{
LL_PROFILE_ZONE_SCOPED
S32 count = msg->getNumberOfBlocksFast(_PREHASH_AgentBlock);
// <FS:PP> Attempt to speed up things a little
@ -801,8 +802,6 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
// we were tracking someone who went offline
deleteTrackingData();
}
// *TODO: get actual inventory id
gInventory.addChangedMask(LLInventoryObserver::CALLING_CARD, LLUUID::null);
}
//[FIX FIRE-3522 : SJ] Notify Online/Offline to Nearby Chat even if chat_notify isnt true

View File

@ -241,7 +241,7 @@ void LLControlAvatar::matchVolumeTransform()
if (skin_info)
{
LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
bind_rot = LLSkinningUtil::getUnscaledQuaternion(skin_info->mBindShapeMatrix);
bind_rot = LLSkinningUtil::getUnscaledQuaternion(LLMatrix4(skin_info->mBindShapeMatrix));
}
#endif
setRotation(bind_rot*obj_rot);

View File

@ -243,8 +243,6 @@ void LLDrawable::markDead()
LLVOVolume* LLDrawable::getVOVolume() const
{
LL_PROFILE_ZONE_SCOPED
LLViewerObject* objectp = mVObjp;
if ( !isDead() && objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
{

View File

@ -404,6 +404,7 @@ void LLRenderPass::renderTexture(U32 type, U32 mask, BOOL batch_textures)
void LLRenderPass::pushBatches(U32 type, U32 mask, BOOL texture, BOOL batch_textures)
{
LL_PROFILE_ZONE_SCOPED;
for (LLCullResult::drawinfo_iterator i = gPipeline.beginRenderMap(type); i != gPipeline.endRenderMap(type); ++i)
{
LLDrawInfo* pparams = *i;
@ -452,6 +453,7 @@ void LLRenderPass::applyModelMatrix(const LLDrawInfo& params)
void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL batch_textures)
{
LL_PROFILE_ZONE_SCOPED;
if (!params.mCount)
{
return;
@ -469,7 +471,7 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL ba
{
if (params.mTextureList[i].notNull())
{
gGL.getTexUnit(i)->bind(params.mTextureList[i], TRUE);
gGL.getTexUnit(i)->bindFast(params.mTextureList[i]);
}
}
}
@ -477,8 +479,7 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL ba
{ //not batching textures or batch has only 1 texture -- might need a texture matrix
if (params.mTexture.notNull())
{
params.mTexture->addTextureStats(params.mVSize);
gGL.getTexUnit(0)->bind(params.mTexture, TRUE) ;
gGL.getTexUnit(0)->bindFast(params.mTexture);
if (params.mTextureMatrix)
{
tex_setup = true;
@ -490,24 +491,20 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL ba
}
else
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(0)->unbindFast(LLTexUnit::TT_TEXTURE);
}
}
}
if (params.mVertexBuffer.notNull())
{
if (params.mGroup)
{
params.mGroup->rebuildMesh();
}
if (params.mGroup)
{
params.mGroup->rebuildMesh();
}
LLGLEnableFunc stencil_test(GL_STENCIL_TEST, params.mSelected, &LLGLCommonFunc::selected_stencil_test);
params.mVertexBuffer->setBuffer(mask);
params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode);
}
LLGLEnableFunc stencil_test(GL_STENCIL_TEST, params.mSelected, &LLGLCommonFunc::selected_stencil_test);
params.mVertexBuffer->setBufferFast(mask);
params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
if (tex_setup)
{

View File

@ -55,19 +55,7 @@ static BOOL deferred_render = FALSE;
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_SETUP("Alpha Setup");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_GROUP_LOOP("Alpha Group");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_PUSH("Alpha Push Verts");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DEFERRED("Alpha Deferred");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_SETBUFFER("Alpha SetBuffer");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DRAW("Alpha Draw");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_TEX_BINDS("Alpha Tex Binds");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_MATS("Alpha Mat Tex Binds");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_GLOW("Alpha Glow Binds");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_SHADER_BINDS("Alpha Shader Binds");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DEFERRED_SHADER_BINDS("Alpha Def Binds");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DEFERRED_TEX_BINDS("Alpha Def Tex Binds");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_MESH_REBUILD("Alpha Mesh Rebuild");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_EMISSIVE("Alpha Emissive");
static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_LIGHT_SETUP("Alpha Light Setup");
LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) :
LLRenderPass(type), current_shader(NULL), target_shader(NULL),
@ -86,6 +74,10 @@ LLDrawPoolAlpha::~LLDrawPoolAlpha()
void LLDrawPoolAlpha::prerender()
{
mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT);
// TODO: is this even necessay? These are probably set to never discard
LLViewerFetchedTexture::sFlatNormalImagep->addTextureStats(1024.f*1024.f);
LLViewerFetchedTexture::sWhiteImagep->addTextureStats(1024.f * 1024.f);
}
S32 LLDrawPoolAlpha::getNumPostDeferredPasses()
@ -314,7 +306,7 @@ void LLDrawPoolAlpha::render(S32 pass)
gGL.diffuseColor4f(1,0,0,1);
LLViewerFetchedTexture::sSmokeImagep->addTextureStats(1024.f*1024.f);
gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sSmokeImagep, TRUE) ;
gGL.getTexUnit(0)->bindFast(LLViewerFetchedTexture::sSmokeImagep);
renderAlphaHighlight(LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_TEXCOORD0);
@ -363,9 +355,8 @@ void LLDrawPoolAlpha::renderAlphaHighlight(U32 mask)
{
params.mGroup->rebuildMesh();
}
params.mVertexBuffer->setBuffer(mask);
params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode);
params.mVertexBuffer->setBufferFast(mask);
params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
}
}
}
@ -388,27 +379,23 @@ inline bool IsEmissive(LLDrawInfo& params)
inline void Draw(LLDrawInfo* draw, U32 mask)
{
draw->mVertexBuffer->setBuffer(mask);
draw->mVertexBuffer->setBufferFast(mask);
LLRenderPass::applyModelMatrix(*draw);
draw->mVertexBuffer->drawRange(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
gPipeline.addTrianglesDrawn(draw->mCount, draw->mDrawMode);
draw->mVertexBuffer->drawRangeFast(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
}
bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_shaders, bool use_material, LLGLSLShader* current_shader)
bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material, LLGLSLShader* current_shader)
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_TEX_BINDS);
bool tex_setup = false;
if (deferred_render && use_material && current_shader)
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_TEX_BINDS);
if (draw->mNormalMap)
{
{
draw->mNormalMap->addTextureStats(draw->mVSize);
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap);
}
if (draw->mSpecularMap)
{
draw->mSpecularMap->addTextureStats(draw->mVSize);
@ -417,18 +404,16 @@ bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_shaders, bool use_mate
}
else if (current_shader == simple_shader)
{
LLViewerFetchedTexture::sFlatNormalImagep->addTextureStats(draw->mVSize);
LLViewerFetchedTexture::sWhiteImagep->addTextureStats(draw->mVSize);
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep);
}
if (use_shaders && draw->mTextureList.size() > 1)
if (draw->mTextureList.size() > 1)
{
for (U32 i = 0; i < draw->mTextureList.size(); ++i)
{
if (draw->mTextureList[i].notNull())
{
gGL.getTexUnit(i)->bind(draw->mTextureList[i], TRUE);
gGL.getTexUnit(i)->bindFast(draw->mTextureList[i]);
}
}
}
@ -436,16 +421,15 @@ bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_shaders, bool use_mate
{ //not batching textures or batch has only 1 texture -- might need a texture matrix
if (draw->mTexture.notNull())
{
draw->mTexture->addTextureStats(draw->mVSize);
if (use_shaders && use_material)
if (use_material)
{
current_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, draw->mTexture);
}
else
{
gGL.getTexUnit(0)->bind(draw->mTexture, TRUE) ;
gGL.getTexUnit(0)->bindFast(draw->mTexture);
}
if (draw->mTextureMatrix)
{
tex_setup = true;
@ -457,7 +441,7 @@ bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_shaders, bool use_mate
}
else
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(0)->unbindFast(LLTexUnit::TT_TEXTURE);
}
}
@ -475,37 +459,15 @@ void LLDrawPoolAlpha::RestoreTexSetup(bool tex_setup)
}
}
void LLDrawPoolAlpha::renderSimples(U32 mask, std::vector<LLDrawInfo*>& simples)
{
gPipeline.enableLightsDynamic();
simple_shader->bind();
simple_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
simple_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep);
simple_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
simple_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, 0.0f);
simple_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 0.0f);
bool use_shaders = gPipeline.canUseVertexShaders();
for (LLDrawInfo* draw : simples)
{
bool tex_setup = TexSetup(draw, use_shaders, false, simple_shader);
LLGLEnableFunc stencil_test(GL_STENCIL_TEST, draw->mSelected, &LLGLCommonFunc::selected_stencil_test);
gGL.blendFunc((LLRender::eBlendFactor) draw->mBlendFuncSrc, (LLRender::eBlendFactor) draw->mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
Draw(draw, mask);
RestoreTexSetup(tex_setup);
}
simple_shader->unbind();
}
void LLDrawPoolAlpha::renderFullbrights(U32 mask, std::vector<LLDrawInfo*>& fullbrights)
{
gPipeline.enableLightsFullbright();
fullbright_shader->bind();
fullbright_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.0f);
bool use_shaders = gPipeline.canUseVertexShaders();
for (LLDrawInfo* draw : fullbrights)
{
bool tex_setup = TexSetup(draw, use_shaders, false, fullbright_shader);
bool tex_setup = TexSetup(draw, false, fullbright_shader);
LLGLEnableFunc stencil_test(GL_STENCIL_TEST, draw->mSelected, &LLGLCommonFunc::selected_stencil_test);
gGL.blendFunc((LLRender::eBlendFactor) draw->mBlendFuncSrc, (LLRender::eBlendFactor) draw->mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
@ -516,65 +478,10 @@ void LLDrawPoolAlpha::renderFullbrights(U32 mask, std::vector<LLDrawInfo*>& full
fullbright_shader->unbind();
}
void LLDrawPoolAlpha::renderMaterials(U32 mask, std::vector<LLDrawInfo*>& materials)
{
LLGLSLShader::bindNoShader();
current_shader = NULL;
gPipeline.enableLightsDynamic();
bool use_shaders = gPipeline.canUseVertexShaders();
for (LLDrawInfo* draw : materials)
{
U32 mask = draw->mShaderMask;
llassert(mask < LLMaterial::SHADER_COUNT);
target_shader = (LLPipeline::sUnderWaterRender) ? &(gDeferredMaterialWaterProgram[mask]) : &(gDeferredMaterialProgram[mask]);
if (current_shader != target_shader)
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_SHADER_BINDS);
if (current_shader)
{
gPipeline.unbindDeferredShader(*current_shader);
}
gPipeline.bindDeferredShader(*target_shader);
current_shader = target_shader;
}
bool tex_setup = TexSetup(draw, use_shaders, true, current_shader);
current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, draw->mSpecColor.mV[0], draw->mSpecColor.mV[1], draw->mSpecColor.mV[2], draw->mSpecColor.mV[3]);
current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, draw->mEnvIntensity);
current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, draw->mFullbright ? 1.f : 0.f);
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_TEX_BINDS);
if (draw->mNormalMap)
{
draw->mNormalMap->addTextureStats(draw->mVSize);
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap);
}
if (draw->mSpecularMap)
{
draw->mSpecularMap->addTextureStats(draw->mVSize);
current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap);
}
}
LLGLEnableFunc stencil_test(GL_STENCIL_TEST, draw->mSelected, &LLGLCommonFunc::selected_stencil_test);
gGL.blendFunc((LLRender::eBlendFactor) draw->mBlendFuncSrc, (LLRender::eBlendFactor) draw->mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
Draw(draw, mask);
RestoreTexSetup(tex_setup);
}
}
void LLDrawPoolAlpha::drawEmissive(U32 mask, LLDrawInfo* draw)
{
draw->mVertexBuffer->setBuffer((mask & ~LLVertexBuffer::MAP_COLOR) | LLVertexBuffer::MAP_EMISSIVE);
draw->mVertexBuffer->drawRange(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
gPipeline.addTrianglesDrawn(draw->mCount, draw->mDrawMode);
draw->mVertexBuffer->setBufferFast((mask & ~LLVertexBuffer::MAP_COLOR) | LLVertexBuffer::MAP_EMISSIVE);
draw->mVertexBuffer->drawRangeFast(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset);
}
void LLDrawPoolAlpha::drawEmissiveInline(U32 mask, LLDrawInfo* draw)
@ -604,10 +511,10 @@ void LLDrawPoolAlpha::renderEmissives(U32 mask, std::vector<LLDrawInfo*>& emissi
// install glow-accumulating blend mode
// don't touch color, add to alpha (glow)
gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, LLRender::BF_ONE, LLRender::BF_ONE);
bool use_shaders = gPipeline.canUseVertexShaders();
for (LLDrawInfo* draw : emissives)
{
bool tex_setup = TexSetup(draw, use_shaders, false, emissive_shader);
bool tex_setup = TexSetup(draw, false, emissive_shader);
drawEmissive(mask, draw);
RestoreTexSetup(tex_setup);
}
@ -629,8 +536,6 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
BOOL initialized_lighting = FALSE;
BOOL light_enabled = TRUE;
BOOL use_shaders = gPipeline.canUseVertexShaders();
for (LLCullResult::sg_iterator i = gPipeline.beginAlphaGroups(); i != gPipeline.endAlphaGroups(); ++i)
{
LLSpatialGroup* group = *i;
@ -640,8 +545,10 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
if (group->getSpatialPartition()->mRenderByGroup &&
!group->isDead())
{
std::vector<LLDrawInfo*> emissives;
std::vector<LLDrawInfo*> fullbrights;
static std::vector<LLDrawInfo*> emissives;
static std::vector<LLDrawInfo*> fullbrights;
emissives.resize(0);
fullbrights.resize(0);
bool is_particle_or_hud_particle = group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_PARTICLE
|| group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_HUD_PARTICLE;
@ -666,6 +573,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)
{
LL_PROFILE_ZONE_NAMED("ra - push batch")
LLDrawInfo& params = **k;
U32 have_mask = params.mVertexBuffer->getTypeMask() & mask;
if (have_mask != mask)
@ -716,34 +624,17 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
// Turn off lighting if it hasn't already been so.
if (light_enabled || !initialized_lighting)
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_LIGHT_SETUP);
initialized_lighting = TRUE;
if (use_shaders)
{
target_shader = fullbright_shader;
}
else
{
gPipeline.enableLightsFullbright();
}
target_shader = fullbright_shader;
light_enabled = FALSE;
}
}
// Turn on lighting if it isn't already.
else if (!light_enabled || !initialized_lighting)
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_LIGHT_SETUP);
initialized_lighting = TRUE;
if (use_shaders)
{
target_shader = simple_shader;
}
else
{
gPipeline.enableLightsDynamic();
}
target_shader = simple_shader;
light_enabled = TRUE;
}
@ -761,7 +652,6 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
if (current_shader != target_shader)
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_SHADER_BINDS);
gPipeline.bindDeferredShader(*target_shader);
current_shader = target_shader;
}
@ -775,25 +665,19 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
target_shader = fullbright_shader;
}
if(use_shaders && (current_shader != target_shader))
if(current_shader != target_shader)
{// If we need shaders, and we're not ALREADY using the proper shader, then bind it
// (this way we won't rebind shaders unnecessarily).
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_SHADER_BINDS);
current_shader = target_shader;
current_shader->bind();
}
else if (!use_shaders && current_shader != NULL)
{
LLGLSLShader::bindNoShader();
current_shader = NULL;
}
LLVector4 spec_color(1, 1, 1, 1);
F32 env_intensity = 0.0f;
F32 brightness = 1.0f;
// We have a material. Supply the appropriate data here.
if (use_shaders && mat && deferred_render)
if (mat && deferred_render)
{
spec_color = params.mSpecColor;
env_intensity = params.mEnvIntensity;
@ -812,20 +696,16 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
params.mGroup->rebuildMesh();
}
bool tex_setup = TexSetup(&params, use_shaders, use_shaders && (mat != nullptr), current_shader);
bool tex_setup = TexSetup(&params, (mat != nullptr), current_shader);
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_PUSH);
LLGLEnableFunc stencil_test(GL_STENCIL_TEST, params.mSelected, &LLGLCommonFunc::selected_stencil_test);
gGL.blendFunc((LLRender::eBlendFactor) params.mBlendFuncSrc, (LLRender::eBlendFactor) params.mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
params.mVertexBuffer->setBuffer(mask & ~(params.mFullbright ? (LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2) : 0));
params.mVertexBuffer->setBufferFast(mask & ~(params.mFullbright ? (LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2) : 0));
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DRAW);
params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode);
params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
}
}
@ -838,8 +718,6 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
(!params.mParticle || params.mHasGlow))
// </FS:Ansariel>
{
LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_EMISSIVE);
if (batch_emissives)
{
emissives.push_back(&params);
@ -859,19 +737,29 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass)
}
}
bool rebind = false;
if (batch_fullbrights)
{
light_enabled = false;
renderFullbrights(mask, fullbrights);
if (!fullbrights.empty())
{
light_enabled = false;
renderFullbrights(mask, fullbrights);
rebind = true;
}
}
if (batch_emissives)
{
light_enabled = true;
renderEmissives(mask, emissives);
if (!emissives.empty())
{
light_enabled = true;
renderEmissives(mask, emissives);
rebind = true;
}
}
if (current_shader)
if (current_shader && rebind)
{
current_shader->bind();
}

View File

@ -75,15 +75,13 @@ private:
LLGLSLShader* fullbright_shader;
LLGLSLShader* emissive_shader;
void renderSimples(U32 mask, std::vector<LLDrawInfo*>& simples);
void renderFullbrights(U32 mask, std::vector<LLDrawInfo*>& fullbrights);
void renderMaterials(U32 mask, std::vector<LLDrawInfo*>& fullbrights);
void renderEmissives(U32 mask, std::vector<LLDrawInfo*>& emissives);
void drawEmissive(U32 mask, LLDrawInfo* draw);
void drawEmissiveInline(U32 mask, LLDrawInfo* draw);
bool TexSetup(LLDrawInfo* draw, bool use_shaders, bool use_material, LLGLSLShader* current_shader);
bool TexSetup(LLDrawInfo* draw, bool use_material, LLGLSLShader* current_shader);
void RestoreTexSetup(bool tex_setup);
// our 'normal' alpha blend function for this pass

View File

@ -176,6 +176,7 @@ void LLDrawPoolAvatar::prerender()
{
LLVOAvatar* avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
updateRiggedVertexBuffers(avatarp);
updateSkinInfoMatrixPalettes(avatarp);
}
}
}
@ -461,225 +462,229 @@ S32 LLDrawPoolAvatar::getNumShadowPasses()
void LLDrawPoolAvatar::beginShadowPass(S32 pass)
{
LL_RECORD_BLOCK_TIME(FTM_SHADOW_AVATAR);
{
LL_PROFILE_ZONE_SCOPED;
if (pass == SHADOW_PASS_AVATAR_OPAQUE)
{
sVertexProgram = &gDeferredAvatarShadowProgram;
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
gGL.diffuseColor4f(1,1,1,1);
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_BLEND)
{
sVertexProgram = &gDeferredAvatarAlphaShadowProgram;
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
if (pass == SHADOW_PASS_AVATAR_OPAQUE)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
sVertexProgram = &gDeferredAvatarShadowProgram;
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
gGL.diffuseColor4f(1,1,1,1);
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_MASK)
{
sVertexProgram = &gDeferredAvatarAlphaMaskShadowProgram;
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
gGL.diffuseColor4f(1, 1, 1, 1);
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_BLEND)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
sVertexProgram = &gDeferredAvatarAlphaShadowProgram;
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
gGL.diffuseColor4f(1,1,1,1);
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_BLEND)
{
sVertexProgram = &gDeferredAttachmentAlphaShadowProgram;
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
gGL.diffuseColor4f(1, 1, 1, 1);
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_MASK)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
sVertexProgram = &gDeferredAvatarAlphaMaskShadowProgram;
gGL.diffuseColor4f(1,1,1,1);
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_MASK)
{
sVertexProgram = &gDeferredAttachmentAlphaMaskShadowProgram;
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
gGL.diffuseColor4f(1, 1, 1, 1);
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_BLEND)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
sVertexProgram = &gDeferredAttachmentAlphaShadowProgram;
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
gGL.diffuseColor4f(1,1,1,1);
}
else // SHADOW_PASS_ATTACHMENT_OPAQUE
{
sVertexProgram = &gDeferredAttachmentShadowProgram;
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
gGL.diffuseColor4f(1, 1, 1, 1);
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_MASK)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
sVertexProgram->bind();
}
sVertexProgram = &gDeferredAttachmentAlphaMaskShadowProgram;
// bind diffuse tex so we can reference the alpha channel...
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
if ((sShaderLevel > 0)) // for hardware blending
{
sRenderingSkinned = TRUE;
sVertexProgram->bind();
}
gGL.diffuseColor4f(1, 1, 1, 1);
}
else // SHADOW_PASS_ATTACHMENT_OPAQUE
{
sVertexProgram = &gDeferredAttachmentShadowProgram;
S32 loc = sVertexProgram->getUniformLocation(LLViewerShaderMgr::DIFFUSE_MAP);
sDiffuseChannel = 0;
if (loc != -1)
{
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}
sVertexProgram->bind();
}
}
}
void LLDrawPoolAvatar::endShadowPass(S32 pass)
{
LL_RECORD_BLOCK_TIME(FTM_SHADOW_AVATAR);
{
LL_PROFILE_ZONE_SCOPED;
if (pass == SHADOW_PASS_ATTACHMENT_OPAQUE)
{
LLVertexBuffer::unbind();
}
if (pass == SHADOW_PASS_ATTACHMENT_OPAQUE)
{
LLVertexBuffer::unbind();
}
if (sShaderLevel > 0)
{
sVertexProgram->unbind();
}
sVertexProgram = NULL;
sRenderingSkinned = FALSE;
LLDrawPoolAvatar::sShadowPass = -1;
if (sShaderLevel > 0)
{
sVertexProgram->unbind();
}
sVertexProgram = NULL;
sRenderingSkinned = FALSE;
LLDrawPoolAvatar::sShadowPass = -1;
}
}
void LLDrawPoolAvatar::renderShadow(S32 pass)
{
LL_RECORD_BLOCK_TIME(FTM_SHADOW_AVATAR);
LL_RECORD_BLOCK_TIME(FTM_SHADOW_AVATAR);
{
LL_PROFILE_ZONE_SCOPED;
if (mDrawFace.empty())
{
return;
}
if (mDrawFace.empty())
{
return;
}
const LLFace *facep = mDrawFace[0];
if (!facep->getDrawable())
{
return;
}
LLVOAvatar *avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
const LLFace *facep = mDrawFace[0];
if (!facep->getDrawable())
{
return;
}
LLVOAvatar *avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
if (avatarp->isDead() || avatarp->isUIAvatar() || avatarp->mDrawable.isNull())
{
return;
}
LLVOAvatar::AvatarOverallAppearance oa = avatarp->getOverallAppearance();
BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor();
// <FS:Beq> plain old impostors are passing through the shadow pipeline
// if (oa == LLVOAvatar::AOA_INVISIBLE ||
// (impostor && oa == LLVOAvatar::AOA_JELLYDOLL))
// Note: Impostors should not cast shadows, also all JDs are impostor nowadays so we do not need the extra check at all.
if (impostor || (oa == LLVOAvatar::AOA_INVISIBLE) )
// </FS:Beq>
{
// No shadows for jellydolled or invisible avs.
return;
}
LLDrawPoolAvatar::sShadowPass = pass;
if (avatarp->isDead() || avatarp->isUIAvatar() || avatarp->mDrawable.isNull())
{
return;
}
LLVOAvatar::AvatarOverallAppearance oa = avatarp->getOverallAppearance();
BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor();
if (impostor || (oa == LLVOAvatar::AOA_INVISIBLE))
{
// No shadows for impostored (including jellydolled) or invisible avs.
return;
}
if (pass == SHADOW_PASS_AVATAR_OPAQUE)
{
LLDrawPoolAvatar::sSkipTransparent = true;
avatarp->renderSkinned();
LLDrawPoolAvatar::sSkipTransparent = false;
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_BLEND)
{
LLDrawPoolAvatar::sSkipOpaque = true;
avatarp->renderSkinned();
LLDrawPoolAvatar::sSkipOpaque = false;
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_MASK)
{
LLDrawPoolAvatar::sSkipOpaque = true;
avatarp->renderSkinned();
LLDrawPoolAvatar::sSkipOpaque = false;
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_BLEND) // rigged alpha
{
LLDrawPoolAvatar::sSkipOpaque = true;
renderRigged(avatarp, RIGGED_MATERIAL_ALPHA);
renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_EMISSIVE);
renderRigged(avatarp, RIGGED_ALPHA);
renderRigged(avatarp, RIGGED_FULLBRIGHT_ALPHA);
renderRigged(avatarp, RIGGED_GLOW);
renderRigged(avatarp, RIGGED_SPECMAP_BLEND);
renderRigged(avatarp, RIGGED_NORMMAP_BLEND);
renderRigged(avatarp, RIGGED_NORMSPEC_BLEND);
LLDrawPoolAvatar::sSkipOpaque = false;
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_MASK) // rigged alpha mask
{
LLDrawPoolAvatar::sSkipOpaque = true;
renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_MASK);
renderRigged(avatarp, RIGGED_NORMMAP_MASK);
renderRigged(avatarp, RIGGED_SPECMAP_MASK);
renderRigged(avatarp, RIGGED_NORMSPEC_MASK);
renderRigged(avatarp, RIGGED_GLOW);
LLDrawPoolAvatar::sSkipOpaque = false;
}
else // rigged opaque (SHADOW_PASS_ATTACHMENT_OPAQUE
{
LLDrawPoolAvatar::sSkipTransparent = true;
renderRigged(avatarp, RIGGED_MATERIAL);
renderRigged(avatarp, RIGGED_SPECMAP);
renderRigged(avatarp, RIGGED_SPECMAP_EMISSIVE);
renderRigged(avatarp, RIGGED_NORMMAP);
renderRigged(avatarp, RIGGED_NORMMAP_EMISSIVE);
renderRigged(avatarp, RIGGED_NORMSPEC);
renderRigged(avatarp, RIGGED_NORMSPEC_EMISSIVE);
renderRigged(avatarp, RIGGED_SIMPLE);
renderRigged(avatarp, RIGGED_FULLBRIGHT);
renderRigged(avatarp, RIGGED_SHINY);
renderRigged(avatarp, RIGGED_FULLBRIGHT_SHINY);
renderRigged(avatarp, RIGGED_GLOW);
renderRigged(avatarp, RIGGED_DEFERRED_BUMP);
renderRigged(avatarp, RIGGED_DEFERRED_SIMPLE);
LLDrawPoolAvatar::sSkipTransparent = false;
}
LLDrawPoolAvatar::sShadowPass = pass;
if (pass == SHADOW_PASS_AVATAR_OPAQUE)
{
LLDrawPoolAvatar::sSkipTransparent = true;
avatarp->renderSkinned();
LLDrawPoolAvatar::sSkipTransparent = false;
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_BLEND)
{
LLDrawPoolAvatar::sSkipOpaque = true;
avatarp->renderSkinned();
LLDrawPoolAvatar::sSkipOpaque = false;
}
else if (pass == SHADOW_PASS_AVATAR_ALPHA_MASK)
{
LLDrawPoolAvatar::sSkipOpaque = true;
avatarp->renderSkinned();
LLDrawPoolAvatar::sSkipOpaque = false;
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_BLEND) // rigged alpha
{
LLDrawPoolAvatar::sSkipOpaque = true;
renderRigged(avatarp, RIGGED_MATERIAL_ALPHA);
renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_EMISSIVE);
renderRigged(avatarp, RIGGED_ALPHA);
renderRigged(avatarp, RIGGED_FULLBRIGHT_ALPHA);
renderRigged(avatarp, RIGGED_GLOW);
renderRigged(avatarp, RIGGED_SPECMAP_BLEND);
renderRigged(avatarp, RIGGED_NORMMAP_BLEND);
renderRigged(avatarp, RIGGED_NORMSPEC_BLEND);
LLDrawPoolAvatar::sSkipOpaque = false;
}
else if (pass == SHADOW_PASS_ATTACHMENT_ALPHA_MASK) // rigged alpha mask
{
LLDrawPoolAvatar::sSkipOpaque = true;
renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_MASK);
renderRigged(avatarp, RIGGED_NORMMAP_MASK);
renderRigged(avatarp, RIGGED_SPECMAP_MASK);
renderRigged(avatarp, RIGGED_NORMSPEC_MASK);
renderRigged(avatarp, RIGGED_GLOW);
LLDrawPoolAvatar::sSkipOpaque = false;
}
else // rigged opaque (SHADOW_PASS_ATTACHMENT_OPAQUE
{
LLDrawPoolAvatar::sSkipTransparent = true;
renderRigged(avatarp, RIGGED_MATERIAL);
renderRigged(avatarp, RIGGED_SPECMAP);
renderRigged(avatarp, RIGGED_SPECMAP_EMISSIVE);
renderRigged(avatarp, RIGGED_NORMMAP);
renderRigged(avatarp, RIGGED_NORMMAP_EMISSIVE);
renderRigged(avatarp, RIGGED_NORMSPEC);
renderRigged(avatarp, RIGGED_NORMSPEC_EMISSIVE);
renderRigged(avatarp, RIGGED_SIMPLE);
renderRigged(avatarp, RIGGED_FULLBRIGHT);
renderRigged(avatarp, RIGGED_SHINY);
renderRigged(avatarp, RIGGED_FULLBRIGHT_SHINY);
renderRigged(avatarp, RIGGED_GLOW);
renderRigged(avatarp, RIGGED_DEFERRED_BUMP);
renderRigged(avatarp, RIGGED_DEFERRED_SIMPLE);
LLDrawPoolAvatar::sSkipTransparent = false;
}
}
}
S32 LLDrawPoolAvatar::getNumPasses()
@ -1781,7 +1786,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
renderRigged(avatarp, RIGGED_MATERIAL_ALPHA_EMISSIVE);
renderRigged(avatarp, RIGGED_NORMMAP);
renderRigged(avatarp, RIGGED_NORMMAP_MASK);
renderRigged(avatarp, RIGGED_NORMMAP_EMISSIVE);
renderRigged(avatarp, RIGGED_NORMMAP_EMISSIVE);
renderRigged(avatarp, RIGGED_SPECMAP);
renderRigged(avatarp, RIGGED_SPECMAP_MASK);
renderRigged(avatarp, RIGGED_SPECMAP_EMISSIVE);
@ -2009,7 +2014,7 @@ bool LLDrawPoolAvatar::getRiggedGeometry(
U16 offset = 0;
LLMatrix4 mat_vert = skin->mBindShapeMatrix;
LLMatrix4 mat_vert = LLMatrix4(skin->mBindShapeMatrix);
glh::matrix4f m((F32*) mat_vert.mMatrix);
m = m.inverse().transpose();
@ -2054,7 +2059,7 @@ bool LLDrawPoolAvatar::getRiggedGeometry(
void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
LLVOAvatar* avatar,
LLFace* face,
const LLMeshSkinInfo* skin,
const LLVOVolume* vobj,
LLVolume* volume,
LLVolumeFace& vol_face)
{
@ -2066,6 +2071,11 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
return;
}
if (!vobj || vobj->isNoLOD())
{
return;
}
// <FS> Fix bogus rigged mesh crash
if (vol_face.mNumVertices > 65536 || vol_face.mNumVertices < 0 || vol_face.mNumIndices < 0)
{
@ -2073,15 +2083,9 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
}
// </FS>
LLPointer<LLVertexBuffer> buffer = face->getVertexBuffer();
LLDrawable* drawable = face->getDrawable();
if (drawable->getVOVolume() && drawable->getVOVolume()->isNoLOD())
{
return;
}
const U32 max_joints = LLSkinningUtil::getMaxJointCount();
#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
@ -2121,23 +2125,26 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
}
#endif
// FIXME ugly const cast
LLSkinningUtil::scrubInvalidJoints(avatar, const_cast<LLMeshSkinInfo*>(skin));
U32 data_mask = face->getRiggedVertexBufferDataMask();
const LLMeshSkinInfo* skin = nullptr;
U32 data_mask = face->getRiggedVertexBufferDataMask();
if (!vol_face.mWeightsScrubbed)
{
LLSkinningUtil::scrubSkinWeights(weights, vol_face.mNumVertices, skin);
vol_face.mWeightsScrubbed = TRUE;
}
if (buffer.isNull() ||
buffer->getTypeMask() != data_mask ||
buffer->getNumVerts() != vol_face.mNumVertices ||
buffer->getNumIndices() != vol_face.mNumIndices ||
(drawable && drawable->isState(LLDrawable::REBUILD_ALL)))
{
LL_PROFILE_ZONE_NAMED("Rigged VBO Rebuild");
skin = vobj->getSkinInfo();
// FIXME ugly const cast
LLSkinningUtil::scrubInvalidJoints(avatar, const_cast<LLMeshSkinInfo*>(skin));
if (!vol_face.mWeightsScrubbed)
{
LLSkinningUtil::scrubSkinWeights(weights, vol_face.mNumVertices, skin);
vol_face.mWeightsScrubbed = TRUE;
}
if (drawable && drawable->isState(LLDrawable::REBUILD_ALL))
{
//rebuild EVERY face in the drawable, not just this one, to avoid missing drawable wide rebuild issues
@ -2175,18 +2182,13 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
}
}
if (buffer.isNull() ||
buffer->getNumVerts() != vol_face.mNumVertices ||
buffer->getNumIndices() != vol_face.mNumIndices)
{
// Allocation failed
return;
}
if (!buffer.isNull() &&
sShaderLevel <= 0 &&
face->mLastSkinTime < avatar->getLastSkinTime())
if (sShaderLevel <= 0 &&
face->mLastSkinTime < avatar->getLastSkinTime() &&
!buffer.isNull() &&
buffer->getNumVerts() == vol_face.mNumVertices &&
buffer->getNumIndices() == vol_face.mNumIndices)
{
LL_PROFILE_ZONE_NAMED("Software Skinning");
//perform software vertex skinning for this face
LLStrider<LLVector3> position;
LLStrider<LLVector3> normal;
@ -2202,58 +2204,12 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
LLVector4a* pos = (LLVector4a*) position.get();
LLVector4a* norm = has_normal ? (LLVector4a*) normal.get() : NULL;
//build matrix palette
//<FS:Beq> per frame cache of skinning matrices
//LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
//U32 count = LLSkinningUtil::getMeshJointCount(skin);
//LLSkinningUtil::initSkinningMatrixPalette(mat, count, skin, avatar);
U32 count = LLSkinningUtil::getMeshJointCount(skin);
auto mat = getCacheSkinningMats(drawable, skin, count, avatar);
//</FS:Beq>
LLSkinningUtil::checkSkinWeights(weights, buffer->getNumVerts(), skin);
LLMatrix4a bind_shape_matrix;
bind_shape_matrix.loadu(skin->mBindShapeMatrix);
const MatrixPaletteCache& mpc = updateSkinInfoMatrixPalette(avatar, vobj->getMeshID());
const LLMatrix4a* mat = &(mpc.mMatrixPalette[0]);
const LLMatrix4a& bind_shape_matrix = mpc.mBindShapeMatrix;
#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
U8* joint_indices_cursor = vol_face.mJointIndices;
// fast path with joint indices separate from weights
if (joint_indices_cursor)
{
LLMatrix4a src[4];
for (U32 j = 0; j < buffer->getNumVerts(); ++j)
{
LLMatrix4a final_mat;
//LLMatrix4a final_mat_correct;
F32* jw = just_weights[j].getF32ptr();
LLSkinningUtil::getPerVertexSkinMatrixWithIndices(jw, joint_indices_cursor, mat, final_mat, src);
joint_indices_cursor += 4;
LLVector4a& v = vol_face.mPositions[j];
LLVector4a t;
LLVector4a dst;
bind_shape_matrix.affineTransform(v, t);
final_mat.affineTransform(t, dst);
pos[j] = dst;
if (norm)
{
LLVector4a& n = vol_face.mNormals[j];
bind_shape_matrix.rotate(n, t);
final_mat.rotate(t, dst);
dst.normalize3fast();
norm[j] = dst;
}
}
}
// slow path with joint indices calculated from weights
else
#endif
if (!mpc.mMatrixPalette.empty())
{
for (U32 j = 0; j < buffer->getNumVerts(); ++j)
{
@ -2283,40 +2239,6 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
}
}
//<FS:Beq> cache per frame Skinning mats
LLMatrix4a* LLDrawPoolAvatar::getCacheSkinningMats(LLDrawable* drawable, const LLMeshSkinInfo* skin,
U32 count, LLVOAvatar* avatar)
{
if (drawable->mCacheSize < count || !drawable->mSkinningMatCache)
{
// delete[](drawable->mSkinningMatCache);
if (drawable->mSkinningMatCache)
{
ll_aligned_free_16(drawable->mSkinningMatCache);
}
drawable->mCacheSize = count;
// drawable->mSkinningMatCache = new LLMatrix4a[count];
drawable->mSkinningMatCache = (LLMatrix4a*)ll_aligned_malloc_16(sizeof(LLMatrix4a)*count);
}
static LLCachedControl<bool> disableMatCache(gSavedSettings, "FSDisableRiggedMeshMatrixCaching"); // <FS:Beq> FIRE-23331 - disable matrix caching during appearance update due to weird side effects
if (disableMatCache ||
(avatar->isSelf() && avatar->isEditingAppearance()) ||
(drawable->mSkinningMatCache && LLFrameTimer::getFrameCount() != drawable->mLastSkinningMatCacheFrame))
{
// LL_DEBUGS("Skinning") << "Call InitSkinningMatrixPalette for drawable @" << (U64)drawable << LL_ENDL;
//<FS:Beq> add caching of matrix pallette as high up the stack as we can
drawable->mLastSkinningMatCacheFrame = LLFrameTimer::getFrameCount();
LLSkinningUtil::initSkinningMatrixPalette(drawable->mSkinningMatCache, count, skin, avatar);
}
else
{
// LL_DEBUGS("Skinning") << "Avoiding InitSkinningMatrixPalette for drawable @" << (U64)drawable << LL_ENDL;
}
return drawable->mSkinningMatCache;
}
//</FS:Beq>
void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
{
LL_PROFILE_ZONE_SCOPED
@ -2326,17 +2248,18 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
return;
}
stop_glerror();
LLUUID lastMeshId;
for (U32 i = 0; i < mRiggedFace[type].size(); ++i)
{
LL_PROFILE_ZONE_NAMED("Render Rigged Face");
LLFace* face = mRiggedFace[type][i];
S32 offset = face->getIndicesStart();
U32 count = face->getIndicesCount();
U16 start = face->getGeomStart();
U16 end = start + face->getGeomCount()-1;
U16 end = start + face->getGeomCount()-1;
LLDrawable* drawable = face->getDrawable();
if (!drawable)
@ -2359,12 +2282,6 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
continue;
}
const LLMeshSkinInfo* skin = vobj->getSkinInfo();
if (!skin)
{
continue;
}
// <FS:Ansariel> Niran's optimization
const LLTextureEntry* tex_entry = face->getTextureEntry();
if (tex_entry && tex_entry->getAlpha() == 0.f)
@ -2373,13 +2290,6 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
}
// </FS:Ansariel>
//stop_glerror();
//const LLVolumeFace& vol_face = volume->getVolumeFace(te);
//updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face);
//stop_glerror();
U32 data_mask = LLFace::getRiggedDataMask(type);
LLVertexBuffer* buff = face->getVertexBuffer();
@ -2467,62 +2377,36 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
}
if (buff)
{
{
if (sShaderLevel > 0)
{
// upload matrix palette to shader
auto& meshId = vobj->getMeshID();
if (lastMeshId != meshId) // <== only upload matrix palette to GL if the skininfo changed
{
// upload matrix palette to shader
const MatrixPaletteCache& mpc = updateSkinInfoMatrixPalette(avatar, meshId);
U32 count = mpc.mMatrixPalette.size();
//<FS:Beq> per frame cache of skinning matrices
//LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT];
//U32 count = LLSkinningUtil::getMeshJointCount(skin);
//LLSkinningUtil::initSkinningMatrixPalette(mat, count, skin, avatar);
U32 count = LLSkinningUtil::getMeshJointCount(skin);
auto mat = getCacheSkinningMats(drawable, skin, count, avatar);
//</FS:Beq>
stop_glerror();
if (count == 0)
{
//skin info not loaded yet, don't render
continue;
}
F32 mp[LL_MAX_JOINTS_PER_MESH_OBJECT*12];
LLDrawPoolAvatar::sVertexProgram->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
count,
FALSE,
(GLfloat*) &(mpc.mGLMp[0]));
}
for (U32 i = 0; i < count; ++i)
{
F32* m = (F32*) mat[i].mMatrix[0].getF32ptr();
U32 idx = i*12;
mp[idx+0] = m[0];
mp[idx+1] = m[1];
mp[idx+2] = m[2];
mp[idx+3] = m[12];
mp[idx+4] = m[4];
mp[idx+5] = m[5];
mp[idx+6] = m[6];
mp[idx+7] = m[13];
mp[idx+8] = m[8];
mp[idx+9] = m[9];
mp[idx+10] = m[10];
mp[idx+11] = m[14];
}
LLDrawPoolAvatar::sVertexProgram->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
count,
FALSE,
(GLfloat*) mp);
stop_glerror();
lastMeshId = meshId;
}
else
{
data_mask &= ~LLVertexBuffer::MAP_WEIGHT4;
}
/*if (glow)
{
gGL.diffuseColor4f(0,0,0,face->getTextureEntry()->getGlow());
}*/
if (mat)
{
//order is important here LLRender::DIFFUSE_MAP should be last, becouse it change
@ -2537,13 +2421,25 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
{
specular = face->getTexture(LLRender::SPECULAR_MAP);
}
if (specular)
if (specular && specular_channel >= 0)
{
gGL.getTexUnit(specular_channel)->bind(specular);
gGL.getTexUnit(specular_channel)->bindFast(specular);
}
gGL.getTexUnit(normal_channel)->bind(face->getTexture(LLRender::NORMAL_MAP));
gGL.getTexUnit(sDiffuseChannel)->bind(face->getTexture(LLRender::DIFFUSE_MAP), false, true);
if (normal_channel >= 0)
{
auto* texture = face->getTexture(LLRender::NORMAL_MAP);
if (texture)
{
gGL.getTexUnit(normal_channel)->bindFast(texture);
}
//else
//{
// TODO handle missing normal map
//}
}
gGL.getTexUnit(sDiffuseChannel)->bindFast(face->getTexture(LLRender::DIFFUSE_MAP));
LLColor4 col = mat->getSpecularLightColor();
@ -2574,23 +2470,28 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
sVertexProgram->setMinimumAlpha(0.f);
}
for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
{
LLViewerTexture* tex = face->getTexture(i);
if (tex)
{
tex->addTextureStats(avatar->getPixelArea());
}
}
if (!LLPipeline::sShadowRender && !LLPipeline::sReflectionRender)
{
for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
{
LLViewerTexture* tex = face->getTexture(i);
if (tex)
{
tex->addTextureStats(avatar->getPixelArea());
}
}
}
}
else
{
gGL.getTexUnit(sDiffuseChannel)->bind(face->getTexture());
sVertexProgram->setMinimumAlpha(0.f);
if (normal_channel > -1)
{
LLDrawPoolBump::bindBumpMap(face, normal_channel);
}
gGL.getTexUnit(sDiffuseChannel)->bindFast(face->getTexture());
}
if (face->mTextureMatrix && vobj->mTexAnimMode)
@ -2604,8 +2505,8 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
gGL.loadMatrix((F32*) face->mTextureMatrix->mMatrix);
}
buff->setBuffer(data_mask);
buff->drawRange(LLRender::TRIANGLES, start, end, count, offset);
buff->setBufferFast(data_mask);
buff->drawRangeFast(LLRender::TRIANGLES, start, end, count, offset);
if (tex_index <= 1)
{
@ -2616,11 +2517,9 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
}
else
{
buff->setBuffer(data_mask);
buff->drawRange(LLRender::TRIANGLES, start, end, count, offset);
buff->setBufferFast(data_mask);
buff->drawRangeFast(LLRender::TRIANGLES, start, end, count, offset);
}
gPipeline.addTrianglesDrawn(count, LLRender::TRIANGLES);
}
}
}
@ -2681,20 +2580,87 @@ void LLDrawPoolAvatar::updateRiggedVertexBuffers(LLVOAvatar* avatar)
continue;
}
const LLMeshSkinInfo* skin = vobj->getSkinInfo();
if (!skin)
{
continue;
}
stop_glerror();
LLVolumeFace& vol_face = volume->getVolumeFace(te);
updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face);
updateRiggedFaceVertexBuffer(avatar, face, vobj, volume, vol_face);
}
}
}
void LLDrawPoolAvatar::updateSkinInfoMatrixPalettes(LLVOAvatar* avatarp)
{
LL_PROFILE_ZONE_SCOPED;
//evict matrix palettes from the cache that haven't been updated in 10 frames
for (matrix_palette_cache_t::iterator iter = mMatrixPaletteCache.begin(); iter != mMatrixPaletteCache.end(); )
{
if (gFrameCount - iter->second.mFrame > 10)
{
iter = mMatrixPaletteCache.erase(iter);
}
else
{
++iter;
}
}
}
const LLDrawPoolAvatar::MatrixPaletteCache& LLDrawPoolAvatar::updateSkinInfoMatrixPalette(LLVOAvatar * avatarp, const LLUUID& meshId)
{
MatrixPaletteCache& entry = mMatrixPaletteCache[meshId];
if (entry.mFrame != gFrameCount)
{
LL_PROFILE_ZONE_SCOPED;
const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(meshId);
entry.mFrame = gFrameCount;
if (skin != nullptr)
{
entry.mBindShapeMatrix = skin->mBindShapeMatrix;
//build matrix palette
U32 count = LLSkinningUtil::getMeshJointCount(skin);
entry.mMatrixPalette.resize(count);
LLSkinningUtil::initSkinningMatrixPalette(&(entry.mMatrixPalette[0]), count, skin, avatarp);
const LLMatrix4a* mat = &(entry.mMatrixPalette[0]);
entry.mGLMp.resize(count * 12);
F32* mp = &(entry.mGLMp[0]);
for (U32 i = 0; i < count; ++i)
{
F32* m = (F32*)mat[i].mMatrix[0].getF32ptr();
U32 idx = i * 12;
mp[idx + 0] = m[0];
mp[idx + 1] = m[1];
mp[idx + 2] = m[2];
mp[idx + 3] = m[12];
mp[idx + 4] = m[4];
mp[idx + 5] = m[5];
mp[idx + 6] = m[6];
mp[idx + 7] = m[13];
mp[idx + 8] = m[8];
mp[idx + 9] = m[9];
mp[idx + 10] = m[10];
mp[idx + 11] = m[14];
}
}
else
{
entry.mMatrixPalette.resize(0);
entry.mGLMp.resize(0);
}
}
return entry;
}
void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar)
{
LL_PROFILE_ZONE_SCOPED

View File

@ -28,15 +28,20 @@
#define LL_LLDRAWPOOLAVATAR_H
#include "lldrawpool.h"
#include "llmodel.h"
#include <unordered_map>
class LLVOAvatar;
class LLVOVolume;
class LLGLSLShader;
class LLFace;
class LLMeshSkinInfo;
class LLVolume;
class LLVolumeFace;
class LLDrawable;
extern U32 gFrameCount;
class LLDrawPoolAvatar : public LLFacePool
{
public:
@ -182,11 +187,6 @@ typedef enum
static LLMatrix4& getModelView();
//<FS:Beq> per frame cache
static LLMatrix4a* getCacheSkinningMats(LLDrawable* drawable, const LLMeshSkinInfo* skin, U32 count,
LLVOAvatar* avatar);
//</FS:Beq>
/*virtual*/ S32 getNumPasses();
/*virtual*/ void beginRenderPass(S32 pass);
/*virtual*/ void endRenderPass(S32 pass);
@ -261,11 +261,13 @@ typedef enum
// </FS>
void updateRiggedFaceVertexBuffer(LLVOAvatar* avatar,
LLFace* facep,
const LLMeshSkinInfo* skin,
const LLVOVolume* vobj,
LLVolume* volume,
LLVolumeFace& vol_face);
void updateRiggedVertexBuffers(LLVOAvatar* avatar);
void updateSkinInfoMatrixPalettes(LLVOAvatar* avatarp);
void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false);
void renderRiggedSimple(LLVOAvatar* avatar);
void renderRiggedAlpha(LLVOAvatar* avatar);
@ -285,6 +287,27 @@ typedef enum
std::vector<LLFace*> mRiggedFace[NUM_RIGGED_PASSES];
LL_ALIGN_PREFIX(16)
class MatrixPaletteCache
{
public:
U32 mFrame;
LLMeshSkinInfo::matrix_list_t mMatrixPalette;
LL_ALIGN_16(LLMatrix4a mBindShapeMatrix);
// Float array ready to be sent to GL
std::vector<F32> mGLMp;
MatrixPaletteCache() :
mFrame(gFrameCount-1)
{
}
} LL_ALIGN_POSTFIX(16);
const MatrixPaletteCache& updateSkinInfoMatrixPalette(LLVOAvatar* avatarp, const LLUUID& meshId);
typedef std::unordered_map<LLUUID, MatrixPaletteCache> matrix_palette_cache_t;
matrix_palette_cache_t mMatrixPaletteCache;
/*virtual*/ LLViewerTexture *getDebugTexture();
/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display

View File

@ -684,6 +684,7 @@ BOOL LLDrawPoolBump::bindBumpMap(LLFace* face, S32 channel)
//static
BOOL LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, F32 vsize, S32 channel)
{
LL_PROFILE_ZONE_SCOPED;
//Note: texture atlas does not support bump texture now.
LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(texture) ;
if(!tex)
@ -700,7 +701,7 @@ BOOL LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, F32 vsi
break;
case BE_BRIGHTNESS:
case BE_DARKNESS:
bump = gBumpImageList.getBrightnessDarknessImage( tex, bump_code );
bump = gBumpImageList.getBrightnessDarknessImage( tex, bump_code );
break;
default:
@ -716,12 +717,13 @@ BOOL LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, F32 vsi
{
if (channel == -2)
{
gGL.getTexUnit(1)->bind(bump);
gGL.getTexUnit(0)->bind(bump);
gGL.getTexUnit(1)->bindFast(bump);
gGL.getTexUnit(0)->bindFast(bump);
}
else
{
gGL.getTexUnit(channel)->bind(bump);
// NOTE: do not use bindFast here (see SL-16222)
gGL.getTexUnit(channel)->bind(bump);
}
return TRUE;
@ -1072,6 +1074,7 @@ void LLBumpImageList::updateImages()
// Note: the caller SHOULD NOT keep the pointer that this function returns. It may be updated as more data arrives.
LLViewerTexture* LLBumpImageList::getBrightnessDarknessImage(LLViewerFetchedTexture* src_image, U8 bump_code )
{
LL_PROFILE_ZONE_SCOPED;
llassert( (bump_code == BE_BRIGHTNESS) || (bump_code == BE_DARKNESS) );
LLViewerTexture* bump = NULL;
@ -1512,6 +1515,7 @@ void LLDrawPoolBump::renderBump(U32 type, U32 mask)
void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL batch_textures)
{
LL_PROFILE_ZONE_SCOPED;
applyModelMatrix(params);
bool tex_setup = false;
@ -1522,7 +1526,7 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL
{
if (params.mTextureList[i].notNull())
{
gGL.getTexUnit(i)->bind(params.mTextureList[i], TRUE);
gGL.getTexUnit(i)->bindFast(params.mTextureList[i]);
}
}
}
@ -1537,13 +1541,6 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL
}
else
{
if (!LLGLSLShader::sNoFixedFunction)
{
gGL.getTexUnit(1)->activate();
gGL.matrixMode(LLRender::MM_TEXTURE);
gGL.loadMatrix((GLfloat*) params.mTextureMatrix->mMatrix);
}
gGL.getTexUnit(0)->activate();
gGL.matrixMode(LLRender::MM_TEXTURE);
gGL.loadMatrix((GLfloat*) params.mTextureMatrix->mMatrix);
@ -1560,8 +1557,7 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL
{
if (params.mTexture.notNull())
{
gGL.getTexUnit(diffuse_channel)->bind(params.mTexture);
params.mTexture->addTextureStats(params.mVSize);
gGL.getTexUnit(diffuse_channel)->bindFast(params.mTexture);
}
else
{
@ -1574,10 +1570,10 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL
{
params.mGroup->rebuildMesh();
}
params.mVertexBuffer->setBuffer(mask);
params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode);
if (tex_setup)
params.mVertexBuffer->setBufferFast(mask);
params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
if (tex_setup)
{
if (mShiny)
{
@ -1585,12 +1581,6 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL
}
else
{
if (!LLGLSLShader::sNoFixedFunction)
{
gGL.getTexUnit(1)->activate();
gGL.matrixMode(LLRender::MM_TEXTURE);
gGL.loadIdentity();
}
gGL.getTexUnit(0)->activate();
gGL.matrixMode(LLRender::MM_TEXTURE);
}

View File

@ -32,6 +32,8 @@
#include "lltextureentry.h"
#include "lluuid.h"
#include <unordered_map>
class LLImageRaw;
class LLSpatialGroup;
class LLDrawInfo;
@ -161,7 +163,7 @@ private:
static void onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump );
private:
typedef std::map<LLUUID, LLPointer<LLViewerTexture> > bump_image_map_t;
typedef std::unordered_map<LLUUID, LLPointer<LLViewerTexture> > bump_image_map_t;
bump_image_map_t mBrightnessEntries;
bump_image_map_t mDarknessEntries;
};

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