diff options
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llcommon/llleap.cpp | 220 | ||||
| -rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 163 | ||||
| -rw-r--r-- | indra/newview/llpanelface.cpp | 105 | ||||
| -rw-r--r-- | indra/newview/llpanelface.h | 2 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml | 16 | 
5 files changed, 240 insertions, 266 deletions
| diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index d0fb586459..e93ba83434 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -18,9 +18,6 @@  #include <algorithm>  // std headers  // external library headers -#include <boost/bind.hpp> -#include <boost/scoped_ptr.hpp> -#include <boost/tokenizer.hpp>  // other Linden headers  #include "llerror.h"  #include "llstring.h" @@ -64,7 +61,9 @@ public:          // Pass it a callback to our connect() method, so it can send events          // from a particular LLEventPump to the plugin without having to know          // this class or method name. -        mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2))) +        mListener(new LLLeapListener( +                      [this](LLEventPump& pump, const std::string& listener) +                      { return connect(pump, listener); }))      {          // Rule out unpopulated Params block          if (! cparams.executable.isProvided()) @@ -93,7 +92,7 @@ public:          }          // Listen for child "termination" right away to catch launch errors. -        mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1)); +        mDonePump.listen("LLLeap", [this](const LLSD& data){ return bad_launch(data); });          // Okay, launch child.          // Get a modifiable copy of params block to set files and postend. @@ -113,7 +112,7 @@ public:          // Okay, launch apparently worked. Change our mDonePump listener.          mDonePump.stopListening("LLLeap"); -        mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::done, this, _1)); +        mDonePump.listen("LLLeap", [this](const LLSD& data){ return done(data); });          // Child might pump large volumes of data through either stdout or          // stderr. Don't bother copying all that data into notification event. @@ -128,13 +127,9 @@ public:          // Listening on stdout is stateful. In general, we're either waiting          // for the length prefix or waiting for the specified length of data. -        // We address that with two different listener methods -- one of which -        // is blocked at any given time. +        mReadPrefix = true;          mStdoutConnection = childout.getPump() -            .listen("prefix", boost::bind(&LLLeapImpl::rstdout, this, _1)); -        mStdoutDataConnection = childout.getPump() -            .listen("data",   boost::bind(&LLLeapImpl::rstdoutData, this, _1)); -        mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection)); +            .listen("LLLeap", [this](const LLSD& data){ return rstdout(data); });          // Log anything sent up through stderr. When a typical program          // encounters an error, it writes its error message to stderr and @@ -142,7 +137,7 @@ public:          // interpreter behaves that way. More generally, though, a plugin          // author can log whatever s/he wants to the viewer log using stderr.          mStderrConnection = childerr.getPump() -            .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1)); +            .listen("LLLeap", [this](const LLSD& data){ return rstderr(data); });          // For our lifespan, intercept any LL_ERRS so we can notify plugin          mRecorder = LLError::addGenericRecorder( @@ -255,120 +250,120 @@ public:          return false;      } -    // Initial state of stateful listening on child stdout: wait for a length -    // prefix, followed by ':'. -    bool rstdout(const LLSD& data) +    // Stateful listening on child stdout: +    // wait for a length prefix, followed by ':'. +    bool rstdout(const LLSD&)      {          LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT)); -        // It's possible we got notified of a couple digit characters without -        // seeing the ':' -- unlikely, but still. Until we see ':', keep -        // waiting. -        if (childout.contains(':')) +        while (childout.size())          { -            std::istream& childstream(childout.get_istream()); -            // Saw ':', read length prefix and store in mExpect. -            size_t expect; -            childstream >> expect; -            int colon(childstream.get()); -            if (colon != ':') +            /*----------------- waiting for length prefix ------------------*/ +            if (mReadPrefix)              { -                // Protocol failure. Clear out the rest of the pending data in -                // childout (well, up to a max length) to log what was wrong. -                LLProcess::ReadPipe::size_type -                    readlen((std::min)(childout.size(), LLProcess::ReadPipe::size_type(80))); -                bad_protocol(STRINGIZE(expect << char(colon) << childout.read(readlen))); +                // It's possible we got notified of a couple digit characters without +                // seeing the ':' -- unlikely, but still. Until we see ':', keep +                // waiting. +                if (! childout.contains(':')) +                { +                    if (childout.contains('\n')) +                    { +                        // Since this is the initial listening state, this is where we'd +                        // arrive if the child isn't following protocol at all -- say +                        // because the user specified 'ls' or some darn thing. +                        bad_protocol(childout.getline()); +                    } +                    // Either way, stop looping. +                    break; +                } + +                // Saw ':', read length prefix and store in mExpect. +                std::istream& childstream(childout.get_istream()); +                size_t expect; +                childstream >> expect; +                int colon(childstream.get()); +                if (colon != ':') +                { +                    // Protocol failure. Clear out the rest of the pending data in +                    // childout (well, up to a max length) to log what was wrong. +                    LLProcess::ReadPipe::size_type +                        readlen((std::min)(childout.size(), +                                           LLProcess::ReadPipe::size_type(80))); +                    bad_protocol(stringize(expect, char(colon), childout.read(readlen))); +                    break; +                } +                else +                { +                    // Saw length prefix, saw colon, life is good. Now wait for +                    // that length of data to arrive. +                    mExpect = expect; +                    LL_DEBUGS("LLLeap") << "got length, waiting for " +                                        << mExpect << " bytes of data" << LL_ENDL; +                    // Transition to "read data" mode and loop back to check +                    // if we've already received all the advertised data. +                    mReadPrefix = false; +                    continue; +                }              } +            /*----------------- saw prefix, wait for data ------------------*/              else              { -                // Saw length prefix, saw colon, life is good. Now wait for -                // that length of data to arrive. -                mExpect = expect; -                LL_DEBUGS("LLLeap") << "got length, waiting for " -                                    << mExpect << " bytes of data" << LL_ENDL; -                // Block calls to this method; resetting mBlocker unblocks -                // calls to the other method. -                mBlocker.reset(new LLEventPump::Blocker(mStdoutConnection)); -                // Go check if we've already received all the advertised data. -                if (childout.size()) +                // Until we've accumulated the promised length of data, keep waiting. +                if (childout.size() < mExpect)                  { -                    LLSD updata(data); -                    updata["len"] = LLSD::Integer(childout.size()); -                    rstdoutData(updata); +                    break;                  } -            } -        } -        else if (childout.contains('\n')) -        { -            // Since this is the initial listening state, this is where we'd -            // arrive if the child isn't following protocol at all -- say -            // because the user specified 'ls' or some darn thing. -            bad_protocol(childout.getline()); -        } -        return false; -    } -    // State in which we listen on stdout for the specified length of data to -    // arrive. -    bool rstdoutData(const LLSD& data) -    { -        LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT)); -        // Until we've accumulated the promised length of data, keep waiting. -        if (childout.size() >= mExpect) -        { -            // Ready to rock and roll. -            LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got " -                                << childout.size() << ", parsing LLSD" << LL_ENDL; -            LLSD data; +                // We have the data we were told to expect! Ready to rock and roll. +                LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got " +                                    << childout.size() << ", parsing LLSD" << LL_ENDL; +                LLSD data;  #if 1 -            // specifically require notation LLSD from child -            LLPointer<LLSDParser> parser(new LLSDNotationParser()); -            S32 parse_status(parser->parse(childout.get_istream(), data, mExpect)); -            if (parse_status == LLSDParser::PARSE_FAILURE) +                // specifically require notation LLSD from child +                LLPointer<LLSDParser> parser(new LLSDNotationParser()); +                S32 parse_status(parser->parse(childout.get_istream(), data, mExpect)); +                if (parse_status == LLSDParser::PARSE_FAILURE)  #else -            // SL-18330: accept any valid LLSD serialization format from child -            // Unfortunately this runs into trouble we have not yet debugged. -            bool parse_status(LLSDSerialize::deserialize(data, childout.get_istream(), mExpect)); -            if (! parse_status) +                // SL-18330: accept any valid LLSD serialization format from child +                // Unfortunately this runs into trouble we have not yet debugged. +                bool parse_status(LLSDSerialize::deserialize(data, childout.get_istream(), mExpect)); +                if (! parse_status)  #endif -            { -                bad_protocol("unparseable LLSD data"); -            } -            else if (! (data.isMap() && data["pump"].isString() && data.has("data"))) -            { -                // we got an LLSD object, but it lacks required keys -                bad_protocol("missing 'pump' or 'data'"); -            } -            else -            { -                try                  { -                    // The LLSD object we got from our stream contains the -                    // keys we need. -                    LLEventPumps::instance().obtain(data["pump"]).post(data["data"]); +                    bad_protocol("unparseable LLSD data"); +                    break;                  } -                catch (const std::exception& err) +                else if (! (data.isMap() && data["pump"].isString() && data.has("data")))                  { -                    // No plugin should be allowed to crash the viewer by -                    // driving an exception -- intentionally or not. -                    LOG_UNHANDLED_EXCEPTION(stringize("handling request ", data)); -                    // Whether or not the plugin added a "reply" key to the -                    // request, send a reply. We happen to know who originated -                    // this request, and the reply LLEventPump of interest. -                    // Not our problem if the plugin ignores the reply event. -                    data["reply"] = mReplyPump.getName(); -                    sendReply(llsd::map("error", -                                        stringize(LLError::Log::classname(err), ": ", err.what())), -                              data); +                    // we got an LLSD object, but it lacks required keys +                    bad_protocol("missing 'pump' or 'data'"); +                    break;                  } -                // Block calls to this method; resetting mBlocker unblocks -                // calls to the other method. -                mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection)); -                // Go check for any more pending events in the buffer. -                if (childout.size()) +                else                  { -                    LLSD updata(data); -                    data["len"] = LLSD::Integer(childout.size()); -                    rstdout(updata); +                    try +                    { +                        // The LLSD object we got from our stream contains the +                        // keys we need. +                        LLEventPumps::instance().obtain(data["pump"]).post(data["data"]); +                    } +                    catch (const std::exception& err) +                    { +                        // No plugin should be allowed to crash the viewer by +                        // driving an exception -- intentionally or not. +                        LOG_UNHANDLED_EXCEPTION(stringize("handling request ", data)); +                        // Whether or not the plugin added a "reply" key to the +                        // request, send a reply. We happen to know who originated +                        // this request, and the reply LLEventPump of interest. +                        // Not our problem if the plugin ignores the reply event. +                        data["reply"] = mReplyPump.getName(); +                        sendReply(llsd::map("error", +                                            stringize(LLError::Log::classname(err), ": ", err.what())), +                                  data); +                    } +                    // Transition to "read prefix" mode and go check for any +                    // more pending events in the buffer. +                    mReadPrefix = true; +                    continue;                  }              }          } @@ -453,7 +448,8 @@ private:          // child's stdin, suitably enriched with the pump name on which it was          // received.          return pump.listen(listener, -                           boost::bind(&LLLeapImpl::wstdin, this, pump.getName(), _1)); +                           [this, name=pump.getName()](const LLSD& data) +                           { return wstdin(name, data); });      }      std::string mDesc; @@ -461,11 +457,11 @@ private:      LLEventStream mReplyPump;      LLProcessPtr mChild;      LLTempBoundListener -        mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection; -    std::unique_ptr<LLEventPump::Blocker> mBlocker; +        mStdinConnection, mStdoutConnection, mStderrConnection;      LLProcess::ReadPipe::size_type mExpect;      LLError::RecorderPtr mRecorder;      std::unique_ptr<LLLeapListener> mListener; +    bool mReadPrefix;  };  // These must follow the declaration of LLLeapImpl, so they may as well be last. diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 5326ed1d76..c57258fbce 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -361,14 +361,8 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool          mGLReady = true;      } -    // initialzie DXGI adapter (for querying available VRAM) -    void initDX(); - -    // initialize D3D (if DXGI cannot be used) -    void initD3D(); - -    //clean up DXGI/D3D resources -    void cleanupDX(); +    // Use DXGI to check memory (because WMI doesn't report more than 4Gb) +    void checkDXMem();      /// called by main thread to post work to this window thread      template <typename CALLABLE> @@ -417,12 +411,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool      // *HACK: Attempt to prevent startup crashes by deferring memory accounting      // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18      bool mGLReady = false; +    bool mGotGLBuffer = false;      U32 mMaxVRAM = 0; // maximum amount of vram to allow in the "budget", or 0 for no maximum (see updateVRAMUsage) - -    IDXGIAdapter3* mDXGIAdapter = nullptr; -    LPDIRECT3D9 mD3D = nullptr; -    LPDIRECT3DDEVICE9 mD3DDevice = nullptr;  }; @@ -4631,39 +4622,55 @@ private:      std::string mPrev;  }; -// Print hardware debug info about available graphics adapters in ordinal order -void debugEnumerateGraphicsAdapters() +void LLWindowWin32::LLWindowWin32Thread::checkDXMem()  { -    LL_INFOS("Window") << "Enumerating graphics adapters..." << LL_ENDL; +    if (!mGLReady || mGotGLBuffer) { return; } + +    IDXGIFactory4* p_factory = nullptr; + +    HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&p_factory); -    IDXGIFactory1* factory; -    HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory); -    if (FAILED(res) || !factory) +    if (FAILED(res))      {          LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL;      }      else      { +        IDXGIAdapter3* p_dxgi_adapter = nullptr;          UINT graphics_adapter_index = 0; -        IDXGIAdapter3* dxgi_adapter;          while (true)          { -            res = factory->EnumAdapters(graphics_adapter_index, reinterpret_cast<IDXGIAdapter**>(&dxgi_adapter)); +            res = p_factory->EnumAdapters(graphics_adapter_index, reinterpret_cast<IDXGIAdapter**>(&p_dxgi_adapter));              if (FAILED(res))              {                  if (graphics_adapter_index == 0)                  {                      LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL;                  } -                else -                { -                    LL_INFOS("Window") << "Done enumerating graphics adapters" << LL_ENDL; -                }              }              else              { +                if (graphics_adapter_index == 0) // Should it check largest one isntead of first? +                { +                    DXGI_QUERY_VIDEO_MEMORY_INFO info; +                    p_dxgi_adapter->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &info); + +                    // Alternatively use GetDesc from below to get adapter's memory +                    UINT64 budget_mb = info.Budget / (1024 * 1024); +                    if (gGLManager.mVRAM < (S32)budget_mb) +                    { +                        gGLManager.mVRAM = (S32)budget_mb; +                        LL_INFOS("RenderInit") << "New VRAM Budget (DX9): " << gGLManager.mVRAM << " MB" << LL_ENDL; +                    } +                    else +                    { +                        LL_INFOS("RenderInit") << "VRAM Budget (DX9): " << budget_mb +                            << " MB, current (WMI): " << gGLManager.mVRAM << " MB" << LL_ENDL; +                    } +                } +                  DXGI_ADAPTER_DESC desc; -                dxgi_adapter->GetDesc(&desc); +                p_dxgi_adapter->GetDesc(&desc);                  std::wstring description_w((wchar_t*)desc.Description);                  std::string description(description_w.begin(), description_w.end());                  LL_INFOS("Window") << "Graphics adapter index: " << graphics_adapter_index << ", " @@ -4676,10 +4683,10 @@ void debugEnumerateGraphicsAdapters()                      << "SharedSystemMemory: " << desc.SharedSystemMemory / 1024 / 1024 << LL_ENDL;              } -            if (dxgi_adapter) +            if (p_dxgi_adapter)              { -                dxgi_adapter->Release(); -                dxgi_adapter = NULL; +                p_dxgi_adapter->Release(); +                p_dxgi_adapter = NULL;              }              else              { @@ -4690,95 +4697,12 @@ void debugEnumerateGraphicsAdapters()          }      } -    if (factory) +    if (p_factory)      { -        factory->Release(); +        p_factory->Release();      } -} - -void LLWindowWin32::LLWindowWin32Thread::initDX() -{ -    if (!mGLReady) { return; } - -    if (mDXGIAdapter == NULL) -    { -        debugEnumerateGraphicsAdapters(); -        IDXGIFactory4* pFactory = nullptr; - -        HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&pFactory); - -        if (FAILED(res)) -        { -            LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL; -        } -        else -        { -            res = pFactory->EnumAdapters(0, reinterpret_cast<IDXGIAdapter**>(&mDXGIAdapter)); -            if (FAILED(res)) -            { -                LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL; -            } -            else -            { -                LL_INFOS() << "EnumAdapters success" << LL_ENDL; -            } -        } - -        if (pFactory) -        { -            pFactory->Release(); -        } -    } -} - -void LLWindowWin32::LLWindowWin32Thread::initD3D() -{ -    if (!mGLReady) { return; } - -    if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandleThrd != 0) -    { -        mD3D = Direct3DCreate9(D3D_SDK_VERSION); - -        D3DPRESENT_PARAMETERS d3dpp; - -        ZeroMemory(&d3dpp, sizeof(d3dpp)); -        d3dpp.Windowed = TRUE; -        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; - -        HRESULT res = mD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandleThrd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &mD3DDevice); - -        if (FAILED(res)) -        { -            LL_WARNS() << "(fallback) CreateDevice failed: 0x" << std::hex << res << LL_ENDL; -        } -        else -        { -            LL_INFOS() << "(fallback) CreateDevice success" << LL_ENDL; -        } -    } -} - -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; -    } +    mGotGLBuffer = true;  }  void LLWindowWin32::LLWindowWin32Thread::run() @@ -4798,15 +4722,11 @@ void LLWindowWin32::LLWindowWin32Thread::run()      {          LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; -        // lazily call initD3D inside this loop to catch when mGLReady has been set to true -        initDX(); +        // Check memory budget using DirectX +        checkDXMem();          if (mWindowHandleThrd != 0)          { -            // lazily call initD3D inside this loop to catch when mWindowHandle has been set, and mGLReady has been set to true -            // *TODO: Shutdown if this fails when mWindowHandle exists -            initD3D(); -              MSG msg;              BOOL status;              if (mhDCThrd == 0) @@ -4847,8 +4767,6 @@ void LLWindowWin32::LLWindowWin32Thread::run()          }  #endif      } - -    cleanupDX();  }  void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() @@ -4948,7 +4866,6 @@ void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy()              // very unsafe              TerminateThread(pair.second.native_handle(), 0);              pair.second.detach(); -            cleanupDX();          }      }      LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL; diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 8d6cdb4d8d..50a1bbc8f3 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -1237,27 +1237,35 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)          // Texture          { -            mIsAlpha = false;              LLGLenum image_format = GL_RGB;              bool identical_image_format = false; -            LLSelectedTE::getImageFormat(image_format, identical_image_format); +            bool missing_asset = false; +            LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset); -            mIsAlpha = false; -            switch (image_format) +            if (!missing_asset)              { +                mIsAlpha = false; +                switch (image_format) +                {                  case GL_RGBA:                  case GL_ALPHA: -                { -                    mIsAlpha = true; -                } -                break; +                    { +                        mIsAlpha = true; +                    } +                    break;                  case GL_RGB: break;                  default: -                { -                    LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL; +                    { +                        LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL; +                    } +                    break;                  } -                break; +            } +            else +            { +                // Don't know image's properties, use material's mode value +                mIsAlpha = true;              }              if (LLViewerMedia::getInstance()->textureHasMedia(id)) @@ -1303,10 +1311,12 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)                      texture_ctrl->setTentative(false);                      texture_ctrl->setEnabled(editable && !has_pbr_material);                      texture_ctrl->setImageAssetID(id); -                    getChildView("combobox alphamode")->setEnabled(editable && mIsAlpha && transparency <= 0.f && !has_pbr_material); -                    getChildView("label alphamode")->setEnabled(editable && mIsAlpha && !has_pbr_material); -                    getChildView("maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material); -                    getChildView("label maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material); + +                    bool can_change_alpha = editable && mIsAlpha && !missing_asset && !has_pbr_material; +                    getChildView("combobox alphamode")->setEnabled(can_change_alpha && transparency <= 0.f); +                    getChildView("label alphamode")->setEnabled(can_change_alpha); +                    getChildView("maskcutoff")->setEnabled(can_change_alpha); +                    getChildView("label maskcutoff")->setEnabled(can_change_alpha);                      texture_ctrl->setBakeTextureEnabled(true);                  } @@ -1329,10 +1339,12 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)                      texture_ctrl->setTentative(true);                      texture_ctrl->setEnabled(editable && !has_pbr_material);                      texture_ctrl->setImageAssetID(id); -                    getChildView("combobox alphamode")->setEnabled(editable && mIsAlpha && transparency <= 0.f && !has_pbr_material); -                    getChildView("label alphamode")->setEnabled(editable && mIsAlpha && !has_pbr_material); -                    getChildView("maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material); -                    getChildView("label maskcutoff")->setEnabled(editable && mIsAlpha && !has_pbr_material); + +                    bool can_change_alpha = editable && mIsAlpha && !missing_asset && !has_pbr_material; +                    getChildView("combobox alphamode")->setEnabled(can_change_alpha && transparency <= 0.f); +                    getChildView("label alphamode")->setEnabled(can_change_alpha); +                    getChildView("maskcutoff")->setEnabled(can_change_alpha); +                    getChildView("label maskcutoff")->setEnabled(can_change_alpha);                      texture_ctrl->setBakeTextureEnabled(true);                  } @@ -3321,13 +3333,14 @@ void LLPanelFace::onSelectTexture(const LLSD& data)      LLGLenum image_format;      bool identical_image_format = false; -    LLSelectedTE::getImageFormat(image_format, identical_image_format); +    bool missing_asset = false; +    LLSelectedTE::getImageFormat(image_format, identical_image_format, missing_asset);      LLCtrlSelectionInterface* combobox_alphamode =          childGetSelectionInterface("combobox alphamode");      U32 alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE; -    if (combobox_alphamode) +    if (combobox_alphamode && !missing_asset)      {          switch (image_format)          { @@ -5227,19 +5240,51 @@ void LLPanelFace::LLSelectedTE::getFace(LLFace*& face_to_return, bool& identical      identical_face = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_te_face_func, face_to_return, false, (LLFace*)nullptr);  } -void LLPanelFace::LLSelectedTE::getImageFormat(LLGLenum& image_format_to_return, bool& identical_face) +void LLPanelFace::LLSelectedTE::getImageFormat(LLGLenum& image_format_to_return, bool& identical_face, bool& missing_asset)  { -    LLGLenum image_format; -    struct LLSelectedTEGetImageFormat : public LLSelectedTEGetFunctor<LLGLenum> +    struct LLSelectedTEGetmatId : public LLSelectedTEFunctor      { -        LLGLenum get(LLViewerObject* object, S32 te_index) +        LLSelectedTEGetmatId() +            : mImageFormat(GL_RGB) +            , mIdentical(true) +            , mMissingAsset(false) +            , mFirstRun(true)          { -            LLViewerTexture* image = object->getTEImage(te_index); -            return image ? image->getPrimaryFormat() : GL_RGB;          } -    } get_glenum; -    identical_face = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_glenum, image_format); -    image_format_to_return = image_format; +        bool apply(LLViewerObject* object, S32 te_index) override +        { +            LLViewerTexture* image = object ? object->getTEImage(te_index) : nullptr; +            LLGLenum format = GL_RGB; +            bool missing = false; +            if (image) +            { +                format = image->getPrimaryFormat(); +                missing = image->isMissingAsset(); +            } + +            if (mFirstRun) +            { +                mFirstRun = false; +                mImageFormat = format; +                mMissingAsset = missing; +            } +            else +            { +                mIdentical &= (mImageFormat == format); +                mIdentical &= (mMissingAsset == missing); +            } +            return true; +        } +        LLGLenum mImageFormat; +        bool mIdentical; +        bool mMissingAsset; +        bool mFirstRun; +    } func; +    LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func); + +    image_format_to_return = func.mImageFormat; +    identical_face = func.mIdentical; +    missing_asset = func.mMissingAsset;  }  void LLPanelFace::LLSelectedTE::getTexId(LLUUID& id, bool& identical) diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 7736a68f3c..7f1c801d31 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -599,7 +599,7 @@ public:      {      public:          static void getFace(class LLFace*& face_to_return, bool& identical_face); -        static void getImageFormat(LLGLenum& image_format_to_return, bool& identical_face); +        static void getImageFormat(LLGLenum& image_format_to_return, bool& identical_face, bool& missing_asset);          static void getTexId(LLUUID& id, bool& identical);          static void getPbrMaterialId(LLUUID& id, bool& identical, bool& has_pbr, bool& has_faces_without_pbr);          static void getObjectScaleS(F32& scale_s, bool& identical); diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index 8346628762..f7e1a76b93 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -102,6 +102,22 @@      width="65">         Low    </text> +  <slider +    control_name="RenderLocalLightCount" +    decimal_digits="0" +    follows="left|top" +    height="16" +    increment="1" +    initial_value="256" +    label="Max. nearby lights:" +    label_width="185" +    layout="topleft" +    left="30" +    min_val="0" +    max_val="8192" +    name="MaxLights" +    top_delta="16" +    width="336" />    <check_box      control_name="RenderVSyncEnable" | 
