Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions indra/llfilesystem/lldir.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@ class LLDir
const std::string findFile(const std::string& filename, const std::vector<std::string> filenames) const;
const std::string findFile(const std::string& filename, const std::string& searchPath1 = "", const std::string& searchPath2 = "", const std::string& searchPath3 = "") const;

virtual std::string getLLPluginLauncher() = 0; // full path and name for the plugin shell
virtual std::string getLLPluginFilename(std::string base_name) = 0; // full path and name to the plugin DSO for this base_name (i.e. 'FOO' -> '/bar/baz/libFOO.so')
// full path and name of the plugin's host executable for this base_name
// (i.e. 'media_plugin_cef' -> '/bar/baz/llplugin/media_plugin_cef'); each
// plugin is its own executable now, launched directly (no SLPlugin shell).
virtual std::string getLLPluginFilename(std::string base_name) = 0;

const std::string &getExecutablePathAndName() const; // Full pathname of the executable
const std::string &getAppName() const; // install directory under progams/ ie "AlchemyViewer"
Expand Down
12 changes: 4 additions & 8 deletions indra/llfilesystem/lldir_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,10 @@ std::string LLDir_Linux::getCurPath()
return tmp_str;
}

/*virtual*/ std::string LLDir_Linux::getLLPluginLauncher()
{
return gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() +
"SLPlugin";
}

/*virtual*/ std::string LLDir_Linux::getLLPluginFilename(std::string base_name)
{
return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
"lib" + base_name + ".so";
// Each plugin is now its own host executable named exactly for the plugin
// (e.g. media_plugin_cef), launched directly - there is no separate SLPlugin
// launcher or dlopen'd .so any more.
return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() + base_name;
}
1 change: 0 additions & 1 deletion indra/llfilesystem/lldir_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ class LLDir_Linux : public LLDir
virtual std::string getCurPath();
virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);

/*virtual*/ std::string getLLPluginLauncher();
/*virtual*/ std::string getLLPluginFilename(std::string base_name);

private:
Expand Down
1 change: 0 additions & 1 deletion indra/llfilesystem/lldir_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class LLDir_Mac : public LLDir

virtual std::string getCurPath();

/*virtual*/ std::string getLLPluginLauncher();
/*virtual*/ std::string getLLPluginFilename(std::string base_name);
};

Expand Down
13 changes: 5 additions & 8 deletions indra/llfilesystem/lldir_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,13 @@ static bool CreateDirectory(const std::string &parent,
return std::filesystem::path( std::filesystem::current_path() ).string();
}

/*virtual*/ std::string LLDir_Mac::getLLPluginLauncher()
{
return gDirUtilp->getAppRODataDir() + gDirUtilp->getDirDelimiter() +
"SLPlugin.app/Contents/MacOS/SLPlugin";
}

/*virtual*/ std::string LLDir_Mac::getLLPluginFilename(std::string base_name)
{
return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
base_name + ".dylib";
// Each plugin is now its own host .app bundle named for the plugin
// (e.g. media_plugin_cef.app), launched directly - there is no separate
// SLPlugin launcher or dlopen'd .dylib any more.
return gDirUtilp->getAppRODataDir() + gDirUtilp->getDirDelimiter() +
base_name + ".app/Contents/MacOS/" + base_name;
}


Expand Down
11 changes: 4 additions & 7 deletions indra/llfilesystem/lldir_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,16 +373,13 @@ std::string LLDir_Win32::getCurPath()
return ll_convert<std::string>(std::wstring(w_str));
}

/*virtual*/ std::string LLDir_Win32::getLLPluginLauncher()
{
return gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() +
"SLPlugin.exe";
}

/*virtual*/ std::string LLDir_Win32::getLLPluginFilename(std::string base_name)
{
// Each plugin is now its own host executable named for the plugin
// (e.g. media_plugin_cef.exe), launched directly - there is no separate
// SLPlugin launcher or dlopen'd .dll any more.
return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
base_name + ".dll";
base_name + ".exe";
}


Expand Down
1 change: 0 additions & 1 deletion indra/llfilesystem/lldir_win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ class LLDir_Win32 : public LLDir
/*virtual*/ std::string getCurPath();
/*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask);

/*virtual*/ std::string getLLPluginLauncher();
/*virtual*/ std::string getLLPluginFilename(std::string base_name);

private:
Expand Down
5 changes: 0 additions & 5 deletions indra/llfilesystem/tests/lldir_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,6 @@ struct LLDir_Dummy: public LLDir
return (mFilesystem.find(pathname) != mFilesystem.end());
}

virtual std::string getLLPluginLauncher()
{
// Implement this when we write a test that needs it
return "";
}

virtual std::string getLLPluginFilename(std::string base_name)
{
Expand Down
54 changes: 54 additions & 0 deletions indra/llplugin/llpluginclassmedia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
#include "llpluginmessageclasses.h"
#include "llcontrol.h"

#if LL_WINDOWS
#include <process.h> // _getpid (host pid for accelerated-paint handle dup)
#else
#include <unistd.h> // getpid
#endif

extern LLControlGroup gSavedSettings;
#if LL_DARWIN || LL_LINUX
extern bool gHiDPISupport;
Expand All @@ -56,6 +62,11 @@ LLPluginClassMedia::LLPluginClassMedia(LLPluginClassMediaOwner *owner)
mOwner = owner;
reset();

// Unique per-media id for the macOS accelerated-paint mach-port demux. Media
// sources are created on the main thread, so a plain counter is fine.
static int sNextAccelId = 1;
mAccelId = sNextAccelId++;

//debug use
mDeleteOK = true ;
}
Expand All @@ -75,11 +86,28 @@ bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::s

mPlugin = LLPluginProcessParent::create(this);
mPlugin->setSleepTime(mSleepTime);
mPlugin->setUseDaemon(mUseDaemon, mDaemonRendezvous);

// Queue up the media init message -- it will be sent after all the currently queued messages.
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init");
message.setValue("target", mTarget);
message.setValueReal("factor", mZoomFactor);
// Zero-copy paint: ask for GPU shared-texture handles, and hand the plugin
// this (viewer) process id so it can DuplicateHandle the shared texture into
// us across the process boundary.
message.setValueBoolean("accelerated_paint", mUseAcceleratedPaint);
// macOS shares the accelerated-paint IOSurface over a mach channel; the plugin
// rendezvous via the bootstrap name derived from host_pid, and tags each frame
// with accel_id so the viewer demuxes it back to this media.
message.setValueS32("accel_id", mAccelId);
#if LL_WINDOWS
message.setValueS32("host_pid", (S32)_getpid());
#else
message.setValueS32("host_pid", (S32)getpid());
#endif
// Linux: which windowing backend the viewer chose, so the CEF plugin can pin
// its Ozone platform (X11 vs Wayland) to match. Empty on other platforms.
message.setValue("display_server", mDisplayServer);
sendMessage(message);

mPlugin->init(launcher_filename, plugin_dir, plugin_filename, debug);
Expand Down Expand Up @@ -1049,6 +1077,32 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)

mTextureParamsReceived = true;
}
else if(message_name == "accelerated_paint")
{
// Zero-copy frame ready. The plugin holds one persistent keyed-mutex
// shared texture and sends its viewer-side handle ONLY when that
// texture is (re)created (handle != 0, once per size); a "0" handle
// means "same texture, new frame". Keep the last real handle so a
// per-frame ping doesn't clear it before the consumer takes it. The
// value is a decimal string so a 64-bit handle survives intact.
// On Windows the handle is the persistent shared-texture handle, sent
// only when it is (re)created (handle != 0, once per size); "0" means
// "same texture, new frame", so keep the last real handle. On macOS and
// Linux the frame travels out-of-band (IOSurface mach port / dma-buf
// SCM_RIGHTS), so handle is always "0" and this message is just the
// per-frame dirty trigger. Value is a decimal string so a 64-bit handle
// survives intact.
unsigned long long h = strtoull(message.getValue("handle").c_str(), nullptr, 10);
if (h != 0)
{
mAcceleratedPaintHandle = h;
}
mAcceleratedPaintFormat = message.getValueS32("format");
mAcceleratedPaintWidth = message.getValueS32("width");
mAcceleratedPaintHeight = message.getValueS32("height");
mAcceleratedPaintDirty = true;
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CONTENT_UPDATED);
}
else if(message_name == "updated")
{
if(message.hasValue("left"))
Expand Down
52 changes: 52 additions & 0 deletions indra/llplugin/llpluginclassmedia.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,44 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
bool getDisableTimeout() { return mPlugin?mPlugin->getDisableTimeout():false; };
void setDisableTimeout(bool disable) { if(mPlugin) mPlugin->setDisableTimeout(disable); };

// Route this media instance through the shared CEF daemon host, using a
// user-writable rendezvous path (see LLPluginProcessParent::setUseDaemon).
// Set before init().
void setUseDaemon(bool use_daemon, const std::string& rendezvous_path = std::string())
{ mUseDaemon = use_daemon; mDaemonRendezvous = rendezvous_path; if(mPlugin) mPlugin->setUseDaemon(use_daemon, rendezvous_path); };
bool getUseDaemon() const { return mUseDaemon; };

// Zero-copy paint: ask the plugin to deliver a GPU shared-texture handle
// (duplicated into this process) instead of CPU pixels. Set before init().
void setUseAcceleratedPaint(bool b) { mUseAcceleratedPaint = b; };
bool getUseAcceleratedPaint() const { return mUseAcceleratedPaint; };

// Linux: the viewer's windowing backend ("wayland"/"x11"), forwarded to the
// CEF plugin in init() so its Ozone platform matches ours. Empty lets the
// plugin auto-detect. Set before init().
void setDisplayServer(const std::string& display_server) { mDisplayServer = display_server; };

// Per-media id sent to the plugin in init() and tagged onto each macOS
// IOSurface mach-port message, so the process-global viewer-side receiver can
// demux surfaces from many tabs/plugin processes back to the right media.
int getAccelId() const { return mAccelId; }

// The plugin's stable shared texture: a native handle already duplicated into
// THIS process (Windows: a D3D11 shared-texture HANDLE), plus its
// cef_color_type format and coded size. The handle is PERSISTENT - it is only
// (re)sent when the plugin recreates the texture (per size), so it is kept,
// not consumed: the consumer compares it against what it last bound and only
// re-opens when it changes. mAcceleratedPaintDirty marks a fresh frame.
bool getAcceleratedPaintDirty() const { return mAcceleratedPaintDirty; };
int getAcceleratedPaintFormat() const { return mAcceleratedPaintFormat; };
int getAcceleratedPaintWidth() const { return mAcceleratedPaintWidth; };
int getAcceleratedPaintHeight() const { return mAcceleratedPaintHeight; };
unsigned long long getAcceleratedPaintHandle() const { return mAcceleratedPaintHandle; };
void clearAcceleratedPaintDirty() { mAcceleratedPaintDirty = false; };
// The accelerated frame's pixel layout/handle travels out-of-band on macOS
// (IOSurface mach port) and Linux (dma-buf fds via SCM_RIGHTS, demuxed by accel
// id - see LLCEFSurfaceReceiver), so no dma-buf plumbing is carried here.

// Inherited from LLPluginProcessParentOwner
/* virtual */ void receivePluginMessage(const LLPluginMessage &message);
/* virtual */ void pluginLaunchFailed();
Expand Down Expand Up @@ -430,6 +468,20 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner


LLPluginProcessParent::ptr_t mPlugin;
bool mUseDaemon = false;
std::string mDaemonRendezvous;

// accelerated (zero-copy) paint - see setUseAcceleratedPaint / the getters above
bool mUseAcceleratedPaint = false;
// Linux windowing backend name forwarded to the CEF plugin - see setDisplayServer.
std::string mDisplayServer;
// Unique per-media id (macOS mach-port demux); assigned at construction.
int mAccelId = 0;
unsigned long long mAcceleratedPaintHandle = 0; // native handle, dup'd into this process
int mAcceleratedPaintFormat = 0;
int mAcceleratedPaintWidth = 0;
int mAcceleratedPaintHeight = 0;
bool mAcceleratedPaintDirty = false;

LLRect mDirtyRect;

Expand Down
85 changes: 22 additions & 63 deletions indra/llplugin/llplugininstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,18 @@

#include "llplugininstance.h"

#include <boost/dll/shared_library.hpp>
#include <boost/dll/shared_library_load_mode.hpp>

#if LL_WINDOWS
#include "direct.h" // needed for _chdir()
#endif

/** Virtual destructor. */
LLPluginInstanceMessageListener::~LLPluginInstanceMessageListener()
{
}

/**
* TODO:DOC describe how it's used
*/
const char *LLPluginInstance::PLUGIN_INIT_FUNCTION_NAME = "LLPluginInitEntryPoint";
LLPluginInstance::pluginInitFunction LLPluginInstance::sStaticInitFunction = NULL;

// static
void LLPluginInstance::setStaticInitFunction(pluginInitFunction func)
{
sStaticInitFunction = func;
}

/**
* Constructor.
Expand All @@ -64,70 +60,33 @@ LLPluginInstance::LLPluginInstance(LLPluginInstanceMessageListener *owner) :
*/
LLPluginInstance::~LLPluginInstance()
{
// mDSOHandle's destructor unloads the shared library if loaded.
}

/**
* Dynamically loads the plugin and runs the plugin's init function.
* Runs the statically-linked plugin's init function.
*
* Each plugin now lives in its own host executable that statically links exactly
* one plugin and registers its entry point via setStaticInitFunction(), so there
* is no longer a dlopen path. plugin_dir/plugin_file are vestigial - they remain
* in the load_plugin message contract but are ignored here.
*
* @param[in] plugin_file Name of plugin dll/dylib/so. TODO:DOC is this correct? see .h
* @return 0 if successful, APR error code or error code from the plugin's init function on failure.
* @return 0 if successful, or the error code returned from the plugin's init function.
*/
int LLPluginInstance::load(const std::string& plugin_dir, std::string &plugin_file)
{
pluginInitFunction init_function = NULL;
(void)plugin_dir;
(void)plugin_file;

if ( plugin_dir.length() )
if (!sStaticInitFunction)
{
#if LL_WINDOWS
// VWR-21275:
// *SOME* Windows systems fail to load the Qt plugins if the current working
// directory is not the same as the directory with the Qt DLLs in.
// This should not cause any run time issues since we are changing the cwd for the
// plugin shell process and not the viewer.
// Changing back to the previous directory is not necessary since the plugin shell
// quits once the plugin exits.
_chdir( plugin_dir.c_str() );
#endif
};

int result = 0;

try
{
mDSOHandle.load(boost::dll::fs::path(plugin_file),
boost::dll::load_mode::rtld_now);
}
catch (const std::exception& e)
{
LL_WARNS("Plugin") << "boost::dll load of " << plugin_file
<< " failed: " << e.what() << LL_ENDL;
result = -1;
LL_WARNS("Plugin") << "no statically-linked plugin init function registered" << LL_ENDL;
return -1;
}

if(result == 0)
int result = sStaticInitFunction(staticReceiveMessage, (void*)this, &mPluginSendMessageFunction, &mPluginUserData);
if (result != 0)
{
try
{
init_function = &mDSOHandle.get<std::remove_pointer_t<pluginInitFunction>>(
PLUGIN_INIT_FUNCTION_NAME);
}
catch (const std::exception& e)
{
LL_WARNS("Plugin") << "symbol lookup for " << PLUGIN_INIT_FUNCTION_NAME
<< " failed: " << e.what() << LL_ENDL;
result = -1;
}
}

if(result == 0)
{
result = init_function(staticReceiveMessage, (void*)this, &mPluginSendMessageFunction, &mPluginUserData);

if(result != 0)
{
LL_WARNS("Plugin") << "call to init function failed with error " << result << LL_ENDL;
}
LL_WARNS("Plugin") << "call to init function failed with error " << result << LL_ENDL;
}

return result;
Expand Down
Loading
Loading