123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- /**
- * @file llviewertexlayer.cpp
- * @brief Viewer texture layer. Used for avatars.
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2010, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llviewertexlayer.h"
- #include "imageids.h"
- #include "llcorehttputil.h"
- #include "llfilesystem.h"
- #include "llimagej2c.h"
- #include "llnotifications.h"
- #include "llsdserialize.h"
- #include "llagent.h"
- #include "llagentwearables.h"
- #include "llpipeline.h"
- #include "llviewercontrol.h"
- #include "llviewershadermgr.h"
- #include "llviewerstats.h"
- #include "llvoavatarself.h"
- using namespace LLAvatarAppearanceDefines;
- static constexpr S32 BAKE_UPLOAD_ATTEMPTS = 7;
- // Actual delay grows by power of 2 each attempt:
- static constexpr F32 BAKE_UPLOAD_RETRY_DELAY = 2.f;
- //-----------------------------------------------------------------------------
- // LLBakedUploadData()
- // Used by LLTexLayerSetBuffer for a callback.
- //-----------------------------------------------------------------------------
- struct LLBakedUploadData
- {
- LLBakedUploadData(const LLVOAvatarSelf* avatar,
- LLViewerTexLayerSet* layerset,
- const LLUUID& id, bool highest_res)
- : mAvatar(avatar),
- mTexLayerSet(layerset),
- mID(id),
- mStartTime(LLFrameTimer::getTotalTime()), // Record starting time
- mIsHighestRes(highest_res)
- {
- }
- const LLUUID mID;
- // Note: backlink only; do not use a LLPointer:
- const LLVOAvatarSelf* mAvatar;
- LLViewerTexLayerSet* mTexLayerSet;
- // For measuring baked texture upload time
- const U64 mStartTime;
- // Whether this is a "final" bake, or intermediate low res
- const bool mIsHighestRes;
- };
- //-----------------------------------------------------------------------------
- // LLViewerTexLayerSetBuffer
- // The composite image that a LLViewerTexLayerSet writes to. Each
- // LLViewerTexLayerSet has one.
- //-----------------------------------------------------------------------------
- //static
- S32 LLViewerTexLayerSetBuffer::sGLByteCount = 0;
- LLViewerTexLayerSetBuffer::LLViewerTexLayerSetBuffer(LLTexLayerSet* const owner,
- S32 width, S32 height)
- : LLTexLayerSetBuffer(owner),
- LLViewerDynamicTexture(width, height, 4,
- // ORDER_LAST => must render these after the hints
- // are created.
- LLViewerDynamicTexture::ORDER_LAST, false),
- // Not used for any logic here, just to sync sending of updates:
- mUploadPending(false),
- mNeedsUpload(false),
- mNumLowresUploads(0),
- mUploadFailCount(0),
- mNeedsUpdate(true),
- mNumLowresUpdates(0)
- {
- mImageGLp->setNeedsAlphaAndPickMask(false);
- sGLByteCount += getSize();
- mNeedsUploadTimer.start();
- mNeedsUpdateTimer.start();
- }
- LLViewerTexLayerSetBuffer::~LLViewerTexLayerSetBuffer()
- {
- sGLByteCount -= getSize();
- destroyGLTexture();
- for (S32 order = 0; order < ORDER_COUNT; ++order)
- {
- // Will fail in all but one case:
- LLViewerDynamicTexture::sInstances[order].erase(this);
- }
- }
- //virtual
- S8 LLViewerTexLayerSetBuffer::getType() const
- {
- return LLViewerDynamicTexture::LL_TEX_LAYER_SET_BUFFER;
- }
- //static
- void LLViewerTexLayerSetBuffer::dumpTotalByteCount()
- {
- llinfos << "Composite System GL Buffers: " << sGLByteCount / 1024 << "KB"
- << llendl;
- }
- void LLViewerTexLayerSetBuffer::requestUpdate()
- {
- restartUpdateTimer();
- mNeedsUpdate = true;
- mNumLowresUpdates = 0;
- // If we are in the middle of uploading a baked texture, we do not care
- // about it any more. When it is downloaded, ignore it.
- mUploadID.setNull();
- }
- void LLViewerTexLayerSetBuffer::requestUpload()
- {
- conditionalRestartUploadTimer();
- mNeedsUpload = true;
- mNumLowresUploads = 0;
- mUploadPending = true;
- }
- void LLViewerTexLayerSetBuffer::conditionalRestartUploadTimer()
- {
- // If we requested a new upload but have not even uploaded a low res
- // version of our last upload request, then keep the timer ticking instead
- // of resetting it.
- if (mNeedsUpload && mNumLowresUploads == 0)
- {
- mNeedsUploadTimer.unpause();
- }
- else
- {
- mNeedsUploadTimer.reset();
- mNeedsUploadTimer.start();
- }
- }
- void LLViewerTexLayerSetBuffer::restartUpdateTimer()
- {
- mNeedsUpdateTimer.reset();
- mNeedsUpdateTimer.start();
- }
- void LLViewerTexLayerSetBuffer::cancelUpload()
- {
- mNeedsUpload = false;
- mUploadPending = false;
- mNeedsUploadTimer.pause();
- mUploadRetryTimer.reset();
- }
- //virtual
- bool LLViewerTexLayerSetBuffer::needsRender()
- {
- llassert(mTexLayerSet->getAvatarAppearance() == gAgentAvatarp);
- if (!isAgentAvatarValid()) return false;
- bool update_now = mNeedsUpdate && isReadyToUpdate();
- bool upload_now = mNeedsUpload && isReadyToUpload();
- // Do not render if we do not want to (or are not ready to) upload or
- // update
- if (!update_now && !upload_now)
- {
- return false;
- }
- // Do not render if we are animating our appearance.
- if (gAgentAvatarp->getIsAppearanceAnimating())
- {
- return false;
- }
- // Do not render if we are trying to create a skirt texture but are not
- // wearing a skirt.
- if (gAgentAvatarp->getBakedTE(getViewerTexLayerSet()) == TEX_SKIRT_BAKED &&
- !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
- {
- cancelUpload();
- return false;
- }
- // Render if we have at least minimal level of detail for each local
- // texture.
- return getViewerTexLayerSet()->isLocalTextureDataAvailable();
- }
- //virtual
- void LLViewerTexLayerSetBuffer::preRenderTexLayerSet()
- {
- LLTexLayerSetBuffer::preRenderTexLayerSet();
- // Keep depth buffer, we do not need to clear it
- LLViewerDynamicTexture::preRender(false);
- }
- //virtual
- void LLViewerTexLayerSetBuffer::postRenderTexLayerSet(bool success)
- {
- LLTexLayerSetBuffer::postRenderTexLayerSet(success);
- LLViewerDynamicTexture::postRender(success);
- }
- //virtual
- void LLViewerTexLayerSetBuffer::midRenderTexLayerSet(bool success)
- {
- // Do we need to upload, and do we have sufficient data to create an
- // uploadable composite ?
- // *TODO: When do we upload the texture if gAgent.mNumPendingQueries is
- // non-zero ?
- bool update_now = mNeedsUpdate && isReadyToUpdate();
- bool upload_now = mNeedsUpload && isReadyToUpload();
- if (upload_now)
- {
- if (success)
- {
- doUpload();
- }
- else
- {
- llinfos << "Failed attempt to bake "
- << mTexLayerSet->getBodyRegionName() << llendl;
- mUploadPending = false;
- }
- }
- if (update_now)
- {
- doUpdate();
- }
- // *TODO: old logic does not check success before setGLTextureCreated
- // We have valid texture data now
- mImageGLp->setGLTextureCreated(true);
- }
- bool LLViewerTexLayerSetBuffer::isInitialized() const
- {
- return mImageGLp.notNull() && mImageGLp->isGLTextureCreated();
- }
- bool LLViewerTexLayerSetBuffer::isReadyToUpload()
- {
- if (!gAgentQueryManager.hasNoPendingQueries())
- {
- return false; // Cannot upload if there are pending queries.
- }
- if (!isAgentAvatarValid() || gAgentAvatarp->isEditingAppearance())
- {
- return false; // Do not upload if avatar is using composites.
- }
- LLViewerTexLayerSet* layer_set = getViewerTexLayerSet();
- if (layer_set->isLocalTextureDataFinal())
- {
- // If we requested an upload and have the final is LOD ready, upload
- // (or wait a while if this is a retry)
- return mUploadFailCount == 0 ||
- mUploadRetryTimer.getElapsedTimeF32() >=
- BAKE_UPLOAD_RETRY_DELAY * (1 << (mUploadFailCount - 1));
- }
- // Upload if we have hit a timeout. Upload is a pretty expensive process so
- // we need to make sure we are not doing uploads too frequently.
- static LLCachedControl<U32> timeout(gSavedSettings,
- "AvatarBakedTextureUploadTimeout");
- if (!timeout)
- {
- return false;
- }
- // The timeout period increases exponentially between every lowres
- // upload in order to prevent spamming the server with frequent uploads.
- U32 threshold = timeout * (1 << mNumLowresUploads);
- // If we hit our timeout and have textures available at even lower
- // resolution, then upload.
- return layer_set->isLocalTextureDataAvailable() &&
- mNeedsUploadTimer.getElapsedTimeF32() >= threshold;
- }
- bool LLViewerTexLayerSetBuffer::isReadyToUpdate()
- {
- // If we requested an update and have the final LOD ready, then update.
- LLViewerTexLayerSet* layer_set = getViewerTexLayerSet();
- if (layer_set->isLocalTextureDataFinal())
- {
- return true;
- }
- // If we have not done an update yet, then just do one now regardless of
- // state of textures.
- if (mNumLowresUpdates == 0)
- {
- return true;
- }
- // Update if we have hit a timeout. Unlike for uploads, we can make this
- // timeout fairly small since render unnecessarily does not cost much.
- static LLCachedControl<U32> timeout(gSavedSettings,
- "AvatarBakedLocalTextureUpdateTimeout");
- if (!timeout)
- {
- return false;
- }
- // If we hit our timeout and have textures available at even lower
- // resolution, then update.
- return layer_set->isLocalTextureDataAvailable() &&
- mNeedsUpdateTimer.getElapsedTimeF32() >= (F32)timeout;
- }
- bool LLViewerTexLayerSetBuffer::requestUpdateImmediate()
- {
- mNeedsUpdate = true;
- bool result = false;
- if (needsRender())
- {
- preRender(false);
- result = render();
- postRender(result);
- }
- return result;
- }
- // If needed, create the baked texture and send it out to the server, then wait
- // for it to come back so we can switch to using it.
- void LLViewerTexLayerSetBuffer::doUpload()
- {
- LLViewerTexLayerSet* layer_set = getViewerTexLayerSet();
- EBakedTextureIndex baked_idx = layer_set->getBakedTexIndex();
- bool visible = layer_set->isVisible();
- bool skip = (U8)baked_idx >= gAgent.mUploadedBakes;
- if (!visible || skip)
- {
- // Do not wait for any upload result: this bake is invisible anyway
- mUploadPending = mNeedsUpload = false;
- mNeedsUploadTimer.pause();
- // Set bake image as invisible
- layer_set->getAvatar()->setNewBakedTexture(baked_idx, IMG_INVISIBLE);
- }
- if (skip)
- {
- // Do not upload this bake
- return;
- }
- bool highest_lod = layer_set->isLocalTextureDataFinal();
- llinfos << "Uploading baked " << layer_set->getBodyRegionName()
- << (highest_lod ? " (full res)" : "(low res)") << llendl;
- gViewerStats.incStat(LLViewerStats::ST_TEX_BAKES);
- // Do not need caches since we are baked now (note: we would not *really*
- // be baked until this image is sent to the server and the AvatarAppearance
- // message is received).
- layer_set->deleteCaches();
- // Get the COLOR information from our texture
- U8* baked_color_data = new U8[mFullWidth * mFullHeight * 4];
- glReadPixels(mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, GL_RGBA,
- GL_UNSIGNED_BYTE, baked_color_data);
- stop_glerror();
- // Get the MASK information from our texture
- LLGLSUIDefault gls_ui;
- LLPointer<LLImageRaw> baked_mask_image = new LLImageRaw(mFullWidth,
- mFullHeight, 1);
- U8* baked_mask_data = baked_mask_image->getData();
- layer_set->gatherMorphMaskAlpha(baked_mask_data,
- mOrigin.mX, mOrigin.mY,
- mFullWidth, mFullHeight);
- // Create the baked image from our color and mask information
- constexpr S32 baked_image_components = 5; // red green blue [bump] clothing
- LLPointer<LLImageRaw> baked_image = new LLImageRaw(mFullWidth, mFullHeight,
- baked_image_components);
- U8* baked_image_data = baked_image->getData();
- S32 i = 0;
- for (S32 u = 0; u < mFullWidth; ++u)
- {
- for (S32 v = 0; v < mFullHeight; ++v)
- {
- S32 k = 4 * i;
- S32 j = k + i; // 5 * i
- baked_image_data[j++] = baked_color_data[k++];
- baked_image_data[j++] = baked_color_data[k++];
- baked_image_data[j++] = baked_color_data[k++];
- // Alpha should be correct for eyelashes:
- baked_image_data[j++] = baked_color_data[k];
- baked_image_data[j] = baked_mask_data[i++];
- }
- }
- LLPointer<LLImageJ2C> j2c_img = new LLImageJ2C;
- // Writes into baked_color_data. 5 channels (RGB, heightfield/alpha, mask)
- if (j2c_img->encode(baked_image, "LL_RGBHM"))
- {
- LLTransactionID tid;
- tid.generate();
- const LLAssetID asset_id =
- tid.makeAssetID(gAgent.getSecureSessionID());
- LLFileSystem j2c_file(asset_id, LLFileSystem::OVERWRITE);
- if (j2c_file.write(j2c_img->getData(), j2c_img->getDataSize()))
- {
- // Read back the file and validate.
- bool valid = false;
- LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
- S32 file_size = 0;
- LLFileSystem file(asset_id);
- file_size = file.getSize();
- // Data buffer MUST be allocated using LLImageBase
- U8* data = integrity_test->allocateData(file_size);
- if (data)
- {
- file.read(data, file_size);
- if (data)
- {
- // integrity_test will delete 'data'
- valid = integrity_test->validate(data, file_size);
- }
- else
- {
- integrity_test->setLastError("Unable to read entire file");
- }
- }
- else
- {
- integrity_test->setLastError("Unable to allocate memory");
- }
- if (valid)
- {
- // Baked_upload_data is owned by the responder and deleted
- // after the request completes.
- LLBakedUploadData* baked_upload_data =
- new LLBakedUploadData(gAgentAvatarp, layer_set, asset_id,
- highest_lod);
- // Upload ID is used to avoid overlaps, e.g. when the user
- // rapidly makes two changes outside of Face Edit.
- mUploadID = asset_id;
- // Upload the image
- static LLCachedControl<bool> use_udp(gSavedSettings,
- "BakedTexUploadForceUDP");
- const std::string& url =
- gAgent.getRegionCapability("UploadBakedTexture");
- if (!url.empty() && !use_udp &&
- // Try last ditch attempt via asset store if cap upload is
- // failing
- mUploadFailCount < BAKE_UPLOAD_ATTEMPTS - 1)
- {
- llinfos << "Baked texture upload via capability of "
- << mUploadID << " to " << url << llendl;
- gCoros.launch("uploadBakedTextureCoro",
- boost::bind(&LLViewerTexLayerSetBuffer::uploadBakedTextureCoro,
- url, mUploadID,
- baked_upload_data));
- }
- else if (gAssetStoragep)
- {
- gAssetStoragep->storeAssetData(tid,
- LLAssetType::AT_TEXTURE,
- onTextureUploadComplete,
- baked_upload_data,
- true, // temp_file
- true, // is_priority
- true); // store_local
- llinfos << "Baked texture upload via Asset Store."
- << llendl;
- }
- if (highest_lod)
- {
- // Sending the final LOD for the baked texture. All done,
- // pause the upload timer so we know how long it took.
- mNeedsUpload = false;
- mNeedsUploadTimer.pause();
- }
- else
- {
- // Sending a lower level LOD for the baked texture. Restart
- // the upload timer.
- ++mNumLowresUploads;
- mNeedsUploadTimer.unpause();
- mNeedsUploadTimer.reset();
- }
- }
- else
- {
- // The read back and validate operation failed. Remove the
- // uploaded file.
- mUploadPending = false;
- LLFileSystem::removeFile(asset_id);
- llinfos << "Unable to create baked upload file (reason: corrupted)."
- << llendl;
- }
- }
- }
- else
- {
- // The cache write file operation failed.
- mUploadPending = false;
- llinfos << "Unable to create baked upload file (reason: failed to write file)"
- << llendl;
- }
- delete[] baked_color_data;
- }
- void upload_failure(const LLUUID& vfile_id, const std::string& reason)
- {
- LLSD args;
- args["FILE"] = vfile_id.asString();
- args["REASON"] = reason;
- gNotifications.add("CannotUploadReason", args);
- }
- //static
- void LLViewerTexLayerSetBuffer::uploadBakedTextureCoro(const std::string& url,
- LLUUID vfile_id,
- LLBakedUploadData* data)
- {
- if (!data)
- {
- llwarns << "No baked upload data for baked teture " << vfile_id
- << ". Baked texture upload aborted." << llendl;
- return;
- }
- if (!LLFileSystem::getExists(vfile_id))
- {
- llwarns << "Non-existent cache file Id: " << vfile_id
- << ". Baked texture upload aborted." << llendl;
- delete data;
- return;
- }
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("uploadBakedTextureCoro");
- LLSD result = adapter.postAndSuspend(url, LLSD());
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- upload_failure(vfile_id, status.toString());
- delete data;
- return;
- }
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- if (!result.has("state"))
- {
- llwarns << "Error: " << result << llendl;
- upload_failure(vfile_id, "malformed response contents.");
- delete data;
- return;
- }
- std::string state = result["state"].asString();
- if (state != "upload")
- {
- llwarns << "Error: " << result << llendl;
- std::string message = result["message"].asString();
- if (message.empty())
- {
- message = "unexpected state in response: " + state;
- }
- upload_failure(vfile_id, message);
- delete data;
- return;
- }
- std::string uploader = result["uploader"].asString();
- if (uploader.empty())
- {
- llwarns << "Error: " << result << llendl;
- upload_failure(vfile_id, "no uploader URL in response.");
- delete data;
- return;
- }
- result = adapter.postFileAndSuspend(uploader, vfile_id,
- LLAssetType::AT_TEXTURE);
- status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- upload_failure(vfile_id, status.toString());
- delete data;
- return;
- }
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- state = result["state"].asString();
- if (state != "complete")
- {
- llwarns << "Error: " << result << llendl;
- std::string message = result["message"].asString();
- if (message.empty())
- {
- message = "unexpected state in response: " + state;
- }
- upload_failure(vfile_id, message);
- delete data;
- return;
- }
- LLUUID new_id = result["new_asset"].asUUID();
- if (new_id.isNull())
- {
- llwarns << "Error: " << result << llendl;
- upload_failure(vfile_id, "missing new asset Id in response.");
- delete data;
- return;
- }
- // Rename the file in the cache to the actual asset id
- LLFileSystem::renameFile(vfile_id, new_id);
- state = result["state"].asString();
- llinfos << "Result: " << state << " - New Id: " << new_id << llendl;
- S32 success = state == "complete" ? 0 : -1;
- // Note: the baked upload data will be deleted by the following call
- onTextureUploadComplete(new_id, data, success);
- }
- // Mostly bookkeeping; don't need to actually "do" anything since render() will
- // actually do the update.
- void LLViewerTexLayerSetBuffer::doUpdate()
- {
- LLViewerTexLayerSet* layer_set = getViewerTexLayerSet();
- if (layer_set->isLocalTextureDataFinal())
- {
- mNeedsUpdate = false;
- }
- else
- {
- ++mNumLowresUpdates;
- }
- restartUpdateTimer();
- // need to swtich to using this layerset if this is the first update after
- // getting the lowest LOD
- layer_set->getAvatar()->updateMeshTextures();
- }
- // StoreAssetData callback (not fixed)
- //static
- void LLViewerTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
- void* userdata,
- S32 result, LLExtStat)
- {
- if (!userdata) return;
- LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata;
- if (isAgentAvatarValid() &&
- // Sanity check: only the user's avatar should be uploading textures:
- baked_upload_data->mAvatar == gAgentAvatarp &&
- baked_upload_data->mTexLayerSet->hasComposite())
- {
- LLViewerTexLayerSetBuffer* layerset_buffer =
- baked_upload_data->mTexLayerSet->getViewerComposite();
- S32 failures = layerset_buffer->mUploadFailCount;
- layerset_buffer->mUploadFailCount = 0;
- if (layerset_buffer->mUploadID.isNull())
- {
- // The upload got cancelled, we should be in the process of baking
- // a new texture so request an upload with the new data.
- // BAP: does this really belong in this callback, as opposed to
- // where the cancellation takes place ? Suspect this does nothing.
- layerset_buffer->requestUpload();
- }
- else if (baked_upload_data->mID == layerset_buffer->mUploadID)
- {
- // This is the upload we are currently waiting for.
- layerset_buffer->mUploadID.setNull();
- const std::string name =
- baked_upload_data->mTexLayerSet->getBodyRegionName();
- const std::string resolution =
- baked_upload_data->mIsHighestRes ? " full res " : " low res ";
- if (result >= 0)
- {
- // Allows sending of AgentSetAppearance later:
- layerset_buffer->mUploadPending = false;
- ETextureIndex baked_te =
- gAgentAvatarp->getBakedTE(layerset_buffer->getViewerTexLayerSet());
- // Update baked texture info with the new UUID
- U64 now = LLFrameTimer::getTotalTime(); // Record starting time
- llinfos << "Baked" << resolution << "texture upload for "
- << name << " took "
- << (S32)((now - baked_upload_data->mStartTime) / 1000)
- << " ms" << llendl;
- gAgentAvatarp->setNewBakedTexture(baked_te, uuid);
- }
- else
- {
- ++failures;
- S32 max_attempts =
- baked_upload_data->mIsHighestRes ? BAKE_UPLOAD_ATTEMPTS
- : 1; // only retry final bakes
- llwarns << "Baked" << resolution << "texture upload for "
- << name << " failed (attempt " << failures << "/"
- << max_attempts << ")" << llendl;
- if (failures < max_attempts)
- {
- layerset_buffer->mUploadFailCount = failures;
- layerset_buffer->mUploadRetryTimer.start();
- layerset_buffer->requestUpload();
- }
- }
- }
- else
- {
- llinfos << "Received baked texture out of date, ignored."
- << llendl;
- }
- gAgentAvatarp->dirtyMesh();
- }
- else
- {
- // Baked texture failed to upload (in which case since we didn't set
- // the new baked texture, it means that they'll try and rebake it at
- // some point in the future (after login ?)), or this response to
- // upload is out of date, in which case a current response should be on
- // the way or already processed.
- llwarns << "Baked upload failed" << llendl;
- }
- delete baked_upload_data;
- }
- //-----------------------------------------------------------------------------
- // LLViewerTexLayerSet
- // An ordered set of texture layers that get composited into a single texture.
- //-----------------------------------------------------------------------------
- LLViewerTexLayerSet::LLViewerTexLayerSet(LLAvatarAppearance* const appearance)
- : LLTexLayerSet(appearance),
- mUpdatesEnabled(false)
- {
- }
- // Returns true if at least one packet of data has been received for each of
- // the textures that this layerset depends on.
- bool LLViewerTexLayerSet::isLocalTextureDataAvailable()
- {
- return mAvatarAppearance->isSelf() &&
- getAvatar()->isLocalTextureDataAvailable(this);
- }
- // Returns true if all of the data for the textures that this layerset depends
- // on have arrived.
- bool LLViewerTexLayerSet::isLocalTextureDataFinal()
- {
- return mAvatarAppearance->isSelf() &&
- getAvatar()->isLocalTextureDataFinal(this);
- }
- void LLViewerTexLayerSet::requestUpdate()
- {
- if (mUpdatesEnabled)
- {
- createComposite();
- getViewerComposite()->requestUpdate();
- }
- }
- void LLViewerTexLayerSet::requestUpload()
- {
- createComposite();
- getViewerComposite()->requestUpload();
- }
- void LLViewerTexLayerSet::cancelUpload()
- {
- LLViewerTexLayerSetBuffer* composite = getViewerComposite();
- if (composite)
- {
- composite->cancelUpload();
- }
- }
- void LLViewerTexLayerSet::updateComposite()
- {
- createComposite();
- getViewerComposite()->requestUpdateImmediate();
- }
- //virtual
- void LLViewerTexLayerSet::createComposite()
- {
- if (!mComposite)
- {
- S32 width = mInfo->getWidth();
- S32 height = mInfo->getHeight();
- if (!mAvatarAppearance->isSelf())
- {
- llerrs << "composites should not be created for non-self avatars !"
- << llendl;
- }
- mComposite = new LLViewerTexLayerSetBuffer(this, width, height);
- }
- }
- LLVOAvatarSelf* LLViewerTexLayerSet::getAvatar()
- {
- // Note: this is a legit static cast, because LLAvatarAppearance is only
- // used as a parent class for LLVOAvatar: should this change in the future,
- // the cast below would become illegal.
- LLVOAvatar* avatarp = (LLVOAvatar*)mAvatarAppearance;
- return avatarp->isSelf() ? (LLVOAvatarSelf*)avatarp : NULL;
- }
- const LLVOAvatarSelf* LLViewerTexLayerSet::getAvatar() const
- {
- // Note: this is a legit static cast, because LLAvatarAppearance is only
- // used as a parent class for LLVOAvatar: should this change in the future,
- // the cast below would become illegal.
- const LLVOAvatar* avatarp = (const LLVOAvatar*)mAvatarAppearance;
- return avatarp->isSelf() ? (const LLVOAvatarSelf*)avatarp : NULL;
- }
- LLViewerTexLayerSetBuffer* LLViewerTexLayerSet::getViewerComposite()
- {
- LLTexLayerSetBuffer* bufferp = getComposite();
- return bufferp ? bufferp->asViewerTexLayerSetBuffer() : NULL;
- }
|