Merge branch 'DRTVWR-588-maint-W' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/llui/llurlregistry.cpp
#	indra/newview/llcontrolavatar.cpp
master
Ansariel 2024-03-01 21:00:56 +01:00
commit c63b3eca4c
21 changed files with 232 additions and 99 deletions

View File

@ -25,6 +25,7 @@ jobs:
viewer_channel: ${{ steps.build.outputs.viewer_channel }}
viewer_version: ${{ steps.build.outputs.viewer_version }}
viewer_branch: ${{ steps.which-branch.outputs.branch }}
relnotes: ${{ steps.which-branch.outputs.relnotes }}
imagename: ${{ steps.build.outputs.imagename }}
env:
AUTOBUILD_ADDRSIZE: 64
@ -102,7 +103,7 @@ jobs:
- name: Determine source branch
id: which-branch
uses: secondlife/viewer-build-util/which-branch@v1
uses: secondlife/viewer-build-util/which-branch@relnotes
with:
token: ${{ github.token }}
@ -366,24 +367,31 @@ jobs:
mv newview/viewer_version.txt macOS-viewer_version.txt
# forked from softprops/action-gh-release
- uses: secondlife-3p/action-gh-release@v1
- name: Create GitHub release
id: release
uses: secondlife-3p/action-gh-release@v1
with:
# name the release page for the build number so we can find it
# easily (analogous to looking up a codeticket build page)
name: "v${{ github.run_id }}"
# name the release page for the branch
name: "${{ needs.build.outputs.viewer_branch }}"
# SL-20546: want the channel and version to be visible on the
# release page
body: |
Build ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
${{ needs.build.outputs.viewer_channel }}
${{ needs.build.outputs.viewer_version }}
${{ needs.build.outputs.viewer_branch }}
${{ needs.build.outputs.relnotes }}
prerelease: true
generate_release_notes: true
target_commitish: ${{ github.sha }}
previous_tag: release
append_body: true
# the only reason we generate a GH release is to post build products
fail_on_unmatched_files: true
files: |
*.dmg
*.exe
*-autobuild-package.xml
*-viewer_version.txt
- name: post release URL
run: |
echo "::notice::Release ${{ steps.release.outputs.url }}"

View File

@ -42,7 +42,7 @@ std::string LLBase64::encode(const U8* input, size_t input_size)
&& input_size > 0)
{
// Yes, it returns int.
int b64_buffer_length = apr_base64_encode_len(narrow(input_size));
int b64_buffer_length = apr_base64_encode_len(narrow<size_t>(input_size));
char* b64_buffer = new char[b64_buffer_length];
// This is faster than apr_base64_encode() if you know
@ -52,7 +52,7 @@ std::string LLBase64::encode(const U8* input, size_t input_size)
b64_buffer_length = apr_base64_encode_binary(
b64_buffer,
input,
narrow(input_size));
narrow<size_t>(input_size));
output.assign(b64_buffer);
delete[] b64_buffer;
}

View File

@ -85,7 +85,7 @@ inline F32 ll_internal_random<F32>()
// Per Monty, it's important to clamp using the correct fmodf() rather
// than expanding to F64 for fmod() and then truncating back to F32. Prior
// to this change, we were getting sporadic ll_frand() == 1.0 results.
F32 rv{ narrow(gRandomGenerator()) };
F32 rv{ narrow<F32>(gRandomGenerator()) };
if(!((rv >= 0.0f) && (rv < 1.0f))) return fmodf(rv, 1.0f);
return rv;
}

View File

@ -197,12 +197,12 @@ public:
typename std::enable_if<std::is_integral<VALUE>::value &&
! std::is_same<VALUE, Boolean>::value,
bool>::type = true>
LLSD(VALUE v): LLSD(Integer(narrow(v))) {}
LLSD(VALUE v): LLSD(Integer(narrow<VALUE>(v))) {}
// support construction from F32 et al.
template <typename VALUE,
typename std::enable_if<std::is_floating_point<VALUE>::value,
bool>::type = true>
LLSD(VALUE v): LLSD(Real(narrow(v))) {}
LLSD(VALUE v): LLSD(Real(narrow<VALUE>(v))) {}
//@}
/** @name Scalar Assignment */

View File

@ -2174,7 +2174,7 @@ std::string zip_llsd(LLSD& data)
U8 out[CHUNK];
strm.avail_in = narrow(source.size());
strm.avail_in = narrow<size_t>(source.size());
strm.next_in = (U8*) source.data();
U8* output = NULL;

View File

@ -197,12 +197,12 @@ S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr,
// *FIX: memory inefficient.
// *TODO: convert to use LLBase64
ostr << pre << "<binary encoding=\"base64\">";
int b64_buffer_length = apr_base64_encode_len(narrow(buffer.size()));
int b64_buffer_length = apr_base64_encode_len(narrow<size_t>(buffer.size()));
char* b64_buffer = new char[b64_buffer_length];
b64_buffer_length = apr_base64_encode_binary(
b64_buffer,
&buffer[0],
narrow(buffer.size()));
narrow<size_t>(buffer.size()));
ostr.write(b64_buffer, b64_buffer_length - 1);
delete[] b64_buffer;
ostr << "</binary>" << post;
@ -415,11 +415,18 @@ S32 LLSDXMLParser::Impl::parse(std::istream& input, LLSD& data)
if (buffer)
{
((char*) buffer)[count ? count - 1 : 0] = '\0';
if (mEmitErrors)
{
LL_INFOS() << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*)buffer << LL_ENDL;
}
}
if (mEmitErrors)
{
LL_INFOS() << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*) buffer << LL_ENDL;
}
else
{
if (mEmitErrors)
{
LL_INFOS() << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR, null buffer" << LL_ENDL;
}
}
data = LLSD();
return LLSDParser::PARSE_FAILURE;
}

View File

@ -929,7 +929,7 @@ void LLMemoryInfo::stream(std::ostream& s) const
// Now stream stats
BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
{
s << pfx << std::setw(narrow(key_width+1)) << (pair.first + ':') << ' ';
s << pfx << std::setw(narrow<size_t>(key_width+1)) << (pair.first + ':') << ' ';
LLSD value(pair.second);
if (value.isInteger())
s << std::setw(12) << value.asInteger();

View File

@ -63,7 +63,7 @@ void TimeBlockTreeNode::setParent( BlockTimerStatHandle* parent )
llassert_always(parent != mBlock);
llassert_always(parent != NULL);
TimeBlockTreeNode* parent_tree_node = get_thread_recorder()->getTimeBlockTreeNode(narrow(parent->getIndex()));
TimeBlockTreeNode* parent_tree_node = get_thread_recorder()->getTimeBlockTreeNode(narrow<size_t>(parent->getIndex()));
if (!parent_tree_node) return;
if (mParent)

View File

@ -77,7 +77,7 @@ void AccumulatorBufferGroup::makeCurrent()
// update stacktimer parent pointers
for (size_t i = 0, end_i = mStackTimers.size(); i < end_i; i++)
{
TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(narrow(i));
TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(narrow<size_t>(i));
if (tree_node)
{
timer_accumulator_buffer[i].mParent = tree_node->mParent;

View File

@ -164,8 +164,10 @@ void LLUriParser::extractParts()
#if LL_DARWIN
typedef void(*sighandler_t)(int);
jmp_buf return_to_normalize;
static int sLastSignal = 0;
void uri_signal_handler(int signal)
{
sLastSignal = signal;
// Apparently signal handler throwing an exception doesn't work.
// This is ugly and unsafe due to not unwinding content of uriparser library,
// but unless we have a way to catch this as NSexception, jump appears to be the only option.
@ -179,8 +181,10 @@ S32 LLUriParser::normalize()
if (!mRes)
{
#if LL_DARWIN
sighandler_t last_handler;
last_handler = signal(SIGILL, &uri_signal_handler); // illegal instruction
sighandler_t last_sigill_handler, last_sigbus_handler;
last_sigill_handler = signal(SIGILL, &uri_signal_handler); // illegal instruction
last_sigbus_handler = signal(SIGBUS, &uri_signal_handler);
if (setjmp(return_to_normalize))
{
// Issue: external library crashed via signal
@ -194,8 +198,9 @@ S32 LLUriParser::normalize()
// if this can be handled by NSexception, it needs to be remade
llassert(0);
LL_WARNS() << "Uriparser crashed with SIGILL, while processing: " << mNormalizedUri << LL_ENDL;
signal(SIGILL, last_handler);
LL_WARNS() << "Uriparser crashed with " << sLastSignal << " , while processing: " << mNormalizedUri << LL_ENDL;
signal(SIGILL, last_sigill_handler);
signal(SIGBUS, last_sigbus_handler);
return 1;
}
#endif
@ -203,7 +208,8 @@ S32 LLUriParser::normalize()
mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST);
#if LL_DARWIN
signal(SIGILL, last_handler);
signal(SIGILL, last_sigill_handler);
signal(SIGBUS, last_sigbus_handler);
#endif
if (!mRes)
@ -226,7 +232,7 @@ S32 LLUriParser::normalize()
}
}
if(mTmpScheme)
if(mTmpScheme && mNormalizedUri.size() > 7)
{
mNormalizedUri = mNormalizedUri.substr(7);
mTmpScheme = false;

View File

@ -121,8 +121,11 @@ void LL::ThreadPoolBase::close()
mQueue->close();
for (auto& pair: mThreads)
{
LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL;
pair.second.join();
if (pair.second.joinable())
{
LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL;
pair.second.join();
}
}
LL_DEBUGS("ThreadPool") << mName << " shutdown complete" << LL_ENDL;
}

View File

@ -87,6 +87,7 @@ namespace LL
protected:
std::unique_ptr<WorkQueueBase> mQueue;
std::vector<std::pair<std::string, std::thread>> mThreads;
bool mAutomaticShutdown;
private:
@ -94,7 +95,6 @@ namespace LL
std::string mName;
size_t mThreadCount;
std::vector<std::pair<std::string, std::thread>> mThreads;
};
/**

View File

@ -288,7 +288,7 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset)
if (pos >= mLen)
return -1; // Doesn't exist
const int block_limit(narrow(mBlocks.size()));
const int block_limit(narrow<size_t>(mBlocks.size()));
for (int i(0); i < block_limit; ++i)
{
if (pos < mBlocks[i]->mUsed)

View File

@ -5630,9 +5630,9 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
U32 stream_count = data.w.empty() ? 4 : 5;
U32 vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count);
size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count);
if (vert_count < 65535)
if (vert_count < 65535 && vert_count != 0)
{
std::vector<U32> indices;
indices.resize(mNumIndices);
@ -5651,6 +5651,13 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
{
U32 src_idx = i;
U32 dst_idx = remap[i];
if (dst_idx >= mNumVertices)
{
dst_idx = mNumVertices - 1;
// Shouldn't happen, figure out what gets returned in remap and why.
llassert(false);
LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL;
}
mIndices[i] = dst_idx;
mPositions[dst_idx].load3(data.p[src_idx].mV);
@ -5684,6 +5691,10 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
}
else
{
if (vert_count == 0)
{
LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL;
}
// blew past the max vertex size limit, use legacy tangent generation which never adds verts
createTangents();
}

View File

@ -237,14 +237,20 @@ bool LLUrlEntryBase::isWikiLinkCorrect(const std::string &labeled_url) const
std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const
{
if (url.empty())
{
return url;
}
LLUriParser up(escapeUrl(url));
up.normalize();
if (up.normalize() == 0)
{
std::string label;
up.extractParts();
up.glueFirst(label);
std::string label;
up.extractParts();
up.glueFirst(label);
return unescapeUrl(label);
return unescapeUrl(label);
}
return std::string();
}
std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const

View File

@ -293,16 +293,18 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL
//if (match_entry == mUrlEntryTrusted)
//{
// LLUriParser up(url);
// up.normalize();
// url = up.normalizedUri();
// if (up.normalize() == 0)
// {
// url = up.normalizedUri();
// }
//}
if (match_entry != mUrlEntryNoLink && match_entry == mUrlEntryTrustedUrl)
{
LLUriParser up(url);
if (!up.normalize())
{
url = up.normalizedUri();
}
if (up.normalize() == 0)
{
url = up.normalizedUri();
}
}
// </FS:Ansariel>

View File

@ -352,6 +352,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
void run() override;
// closes queue, wakes thread, waits until thread closes
void wakeAndDestroy();
void glReady()
{
mGLReady = true;
@ -363,6 +366,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
// initialize D3D (if DXGI cannot be used)
void initD3D();
//clean up DXGI/D3D resources
void cleanupDX();
// call periodically to update available VRAM
void updateVRAMUsage();
@ -989,47 +995,10 @@ void LLWindowWin32::close()
LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
mWindowThread->post([this, self = mWindowThread]()
{
if (IsWindow(self->mWindowHandleThrd))
{
if (self->mhDCThrd)
{
if (!ReleaseDC(self->mWindowHandleThrd, self->mhDCThrd))
{
LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL;
}
}
// Make sure we don't leave a blank toolbar button.
ShowWindow(self->mWindowHandleThrd, SW_HIDE);
// This causes WM_DESTROY to be sent *immediately*
if (!destroy_window_handler(self->mWindowHandleThrd))
{
OSMessageBox(mCallbacks->translateString("MBDestroyWinFailed"),
mCallbacks->translateString("MBShutdownErr"),
OSMB_OK);
}
}
else
{
// Something killed the window while we were busy destroying gl or handle somehow got broken
LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL;
}
self->mWindowHandleThrd = NULL;
self->mhDCThrd = NULL;
self->mGLReady = false;
});
mhDC = NULL;
mWindowHandle = NULL;
// Window thread might be waiting for a getMessage(), give it
// a push to enshure it will process destroy_window_handler
kickWindowThread();
mWindowThread->close();
mWindowThread->wakeAndDestroy();
}
BOOL LLWindowWin32::isValid()
@ -4882,6 +4851,28 @@ void LLWindowWin32::LLWindowWin32Thread::initD3D()
}
}
void LLWindowWin32::LLWindowWin32Thread::cleanupDX()
{
//clean up DXGI/D3D resources
if (mDXGIAdapter)
{
mDXGIAdapter->Release();
mDXGIAdapter = nullptr;
}
if (mD3DDevice)
{
mD3DDevice->Release();
mD3DDevice = nullptr;
}
if (mD3D)
{
mD3D->Release();
mD3D = nullptr;
}
}
void LLWindowWin32::LLWindowWin32Thread::updateVRAMUsage()
{
LL_PROFILE_ZONE_SCOPED;
@ -5027,25 +5018,109 @@ void LLWindowWin32::LLWindowWin32Thread::run()
#endif
}
//clean up DXGI/D3D resources
if (mDXGIAdapter)
cleanupDX();
}
void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy()
{
if (mQueue->isClosed())
{
mDXGIAdapter->Release();
mDXGIAdapter = nullptr;
LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." <<LL_ENDL;
}
if (mD3DDevice)
// Make sure we don't leave a blank toolbar button.
// Also hiding window now prevents user from suspending it
// via some action (like dragging it around)
ShowWindow(mWindowHandleThrd, SW_HIDE);
// Schedule destruction
HWND old_handle = mWindowHandleThrd;
post([this]()
{
if (IsWindow(mWindowHandleThrd))
{
if (mhDCThrd)
{
if (!ReleaseDC(mWindowHandleThrd, mhDCThrd))
{
LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL;
}
mhDCThrd = NULL;
}
// This causes WM_DESTROY to be sent *immediately*
if (!destroy_window_handler(mWindowHandleThrd))
{
LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL;
}
}
else
{
// Something killed the window while we were busy destroying gl or handle somehow got broken
LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL;
}
mWindowHandleThrd = NULL;
mhDCThrd = NULL;
mGLReady = false;
});
LL_DEBUGS("Window") << "Closing window's pool queue" << LL_ENDL;
mQueue->close();
// Post a nonsense user message to wake up the thread in
// case it is waiting for a getMessage()
if (old_handle)
{
mD3DDevice->Release();
mD3DDevice = nullptr;
WPARAM wparam{ 0xB0B0 };
LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle
<< ", " << WM_DUMMY_
<< ", " << wparam << ")" << std::dec << LL_ENDL;
PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337);
}
if (mD3D)
// There are cases where window will refuse to close,
// can't wait forever on join, check state instead
LLTimer timeout;
timeout.setTimerExpirySec(2.0);
while (!getQueue().done() && !timeout.hasExpired() && mWindowHandleThrd)
{
mD3D->Release();
mD3D = nullptr;
ms_sleep(100);
}
if (getQueue().done() || mWindowHandleThrd == NULL)
{
// Window is closed, started closing or is cleaning up
// now wait for our single thread to die.
if (mWindowHandleThrd)
{
LL_INFOS("Window") << "Window is closing, waiting on pool's thread to join, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL;
}
else
{
LL_DEBUGS("Window") << "Waiting on pool's thread, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL;
}
for (auto& pair : mThreads)
{
pair.second.join();
}
}
else
{
// Something suspended window thread, can't afford to wait forever
// so kill thread instead
// Ex: This can happen if user starts dragging window arround (if it
// was visible) or a modal notification pops up
LL_WARNS("Window") << "Window is frozen, couldn't perform clean exit" << LL_ENDL;
for (auto& pair : mThreads)
{
// very unsafe
TerminateThread(pair.second.native_handle(), 0);
pair.second.detach();
cleanupDX();
}
}
LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL;
}
void LLWindowWin32::post(const std::function<void()>& func)

View File

@ -101,7 +101,6 @@ LLVOAvatar *LLControlAvatar::getAttachedAvatar()
void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const
{
F32 max_legal_offset = MAX_LEGAL_OFFSET;
if (gSavedSettings.getControl("AnimatedObjectsMaxLegalOffset"))
{
@ -364,6 +363,9 @@ LLControlAvatar *LLControlAvatar::createControlAvatar(LLVOVolume *obj)
void LLControlAvatar::markForDeath()
{
mMarkedForDeath = true;
// object unlinked cav and might be dead already
// might need to clean mControlAVBridge here as well
mRootVolp = NULL;
mVolumep = nullptr;
}
@ -443,7 +445,7 @@ void LLControlAvatar::updateDebugText()
F32 streaming_cost = 0.f;
std::string cam_dist_string = "";
S32 cam_dist_count = 0;
F32 lod_radius = mRootVolp->mLODRadius;
F32 lod_radius = mRootVolp ? mRootVolp->mLODRadius : 0.f;
for (std::vector<LLVOVolume*>::iterator it = volumes.begin();
it != volumes.end(); ++it)

View File

@ -603,8 +603,12 @@ void LLInventoryAddItemByAssetObserver::changed(U32 mask)
for (uuid_set_t::iterator it = added.begin(); it != added.end(); ++it)
{
LLInventoryItem *item = gInventory.getItem(*it);
if (!item)
{
continue;
}
const LLUUID& asset_uuid = item->getAssetUUID();
if (item && item->getUUID().notNull() && asset_uuid.notNull())
if (item->getUUID().notNull() && asset_uuid.notNull())
{
if (isAssetWatched(asset_uuid))
{

View File

@ -1094,8 +1094,11 @@ bool LLOutfitGalleryItem::openOutfitsContent()
{
outfit_list->setSelectedOutfitByUUID(mUUID);
LLAccordionCtrlTab* tab = accordion->getSelectedTab();
tab->showAndFocusHeader();
return true;
if (tab)
{
tab->showAndFocusHeader();
return true;
}
}
}
}

View File

@ -411,6 +411,12 @@ LLViewerObject::~LLViewerObject()
sNumZombieObjects--;
llassert(mChildList.size() == 0);
llassert(mControlAvatar.isNull()); // Should have been cleaned by now
if (mControlAvatar.notNull())
{
mControlAvatar->markForDeath();
mControlAvatar = NULL;
LL_WARNS() << "Dead object owned a live control avatar" << LL_ENDL;
}
clearInventoryListeners();
}