diff --git a/autobuild.xml b/autobuild.xml
index 6e48cbf5093..67029300ef0 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -450,11 +450,11 @@
archive
name
darwin64
@@ -464,11 +464,11 @@
archive
name
linux64
@@ -478,11 +478,11 @@
archive
name
windows64
@@ -495,7 +495,7 @@
copyright
Copyright (c) 2017, Linden Research, Inc.
version
- 1.24.0.202510081737_139.0.40_g465474a_chromium-139.0.7258.139
+ 1.26.0.202604010801_139.0.40_g465474a_chromium-139.0.7258.139
name
dullahan
description
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index 88d428c2334..6d998e9b517 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -67,7 +67,7 @@ LLPluginClassMedia::~LLPluginClassMedia()
reset();
}
-bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug)
+bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug, S32 adapter_luid_high, U32 adapter_luid_low, bool shared_texture_enable)
{
LL_DEBUGS("Plugin") << "launcher: " << launcher_filename << LL_ENDL;
LL_DEBUGS("Plugin") << "dir: " << plugin_dir << LL_ENDL;
@@ -80,6 +80,12 @@ bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::s
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init");
message.setValue("target", mTarget);
message.setValueReal("factor", mZoomFactor);
+#if LL_WINDOWS
+ message.setValueU32("viewer_pid", (U32)GetCurrentProcessId());
+ message.setValueS32("adapter_luid_high", adapter_luid_high);
+ message.setValueU32("adapter_luid_low", adapter_luid_low);
+#endif
+ message.setValueBoolean("shared_texture_enable", shared_texture_enable);
sendMessage(message);
mPlugin->init(launcher_filename, plugin_dir, plugin_filename, debug);
@@ -142,6 +148,8 @@ void LLPluginClassMedia::reset()
mMediaName.clear();
mMediaDescription.clear();
mBackgroundColor = LLColor4(1.0f, 1.0f, 1.0f, 1.0f);
+ mAcceleratedTextureHandle = nullptr;
+ mAcceleratedTextureId = 0;
// media_browser class
mNavigateURI.clear();
@@ -373,16 +381,6 @@ void LLPluginClassMedia::setSizeInternal(void)
mRequestedMediaHeight = nextPowerOf2(mRequestedMediaHeight);
}
-#if LL_DARWIN
- if (!gHiDPISupport)
-#endif
- {
- if (mRequestedMediaWidth > 2048)
- mRequestedMediaWidth = 2048;
-
- if (mRequestedMediaHeight > 2048)
- mRequestedMediaHeight = 2048;
- }
}
void LLPluginClassMedia::setAutoScale(bool auto_scale)
@@ -1275,6 +1273,19 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
{
mHoverText = message.getValue("tooltip");
}
+ else if (message_name == "accelerated_update")
+ {
+ mAcceleratedTextureHandle = message.getValuePointer("handle");
+ mAcceleratedTextureId = message.getValueU32("texture_id");
+
+ // For the accelerated path, the texture dimensions are the full
+ // media size (what was given to CEF via setSize), not the dirty
+ // rect bounds which may be smaller
+ mTextureWidth = mMediaWidth;
+ mTextureHeight = mMediaHeight;
+
+ mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_ACCELERATED_UPDATE);
+ }
else
{
LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL;
@@ -1401,6 +1412,14 @@ void LLPluginClassMedia::mediaEvent(LLPluginClassMediaOwner::EMediaEvent event)
}
}
+void LLPluginClassMedia::releaseAcceleratedTextureHandle()
+{
+ if (mAcceleratedTextureHandle)
+ {
+ mAcceleratedTextureHandle = nullptr;
+ }
+}
+
void LLPluginClassMedia::sendMessage(const LLPluginMessage &message)
{
if(mPlugin && mPlugin->isRunning())
diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h
index 71522bcd7d5..c60e4f99e8f 100644
--- a/indra/llplugin/llpluginclassmedia.h
+++ b/indra/llplugin/llpluginclassmedia.h
@@ -46,7 +46,10 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
bool init(const std::string &launcher_filename,
const std::string &plugin_dir,
const std::string &plugin_filename,
- bool debug);
+ bool debug,
+ S32 adapter_luid_high = 0,
+ U32 adapter_luid_low = 0,
+ bool shared_texture_enable = false);
// undoes everything init() didm called by the media manager when destroying a source
void reset();
@@ -315,6 +318,10 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
std::string getHoverText() const { return mHoverText; };
std::string getHoverLink() const { return mHoverLink; };
+ // Valid after MEDIA_EVENT_ACCELERATED_UPDATE
+ void* getAcceleratedTextureHandle() const { return mAcceleratedTextureHandle; };
+ void releaseAcceleratedTextureHandle();
+
// these are valid during MEDIA_EVENT_LINK_HOVERED
std::string getFileDownloadFilename() const { return mFileDownloadFilename; }
@@ -477,6 +484,9 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
std::string mFileDownloadFilename;
bool mIsMultipleFilePick;
+ void* mAcceleratedTextureHandle;
+ U32 mAcceleratedTextureId;
+
/////////////////////////////////////////
// media_time class
F64 mCurrentTime;
diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h
index a75d0b9960e..1b9d18454c8 100644
--- a/indra/llplugin/llpluginclassmediaowner.h
+++ b/indra/llplugin/llpluginclassmediaowner.h
@@ -67,7 +67,9 @@ class LLPluginClassMediaOwner
MEDIA_EVENT_DEBUG_MESSAGE, // plugin sending back debug information for host to process
- MEDIA_EVENT_LINK_HOVERED // Got a "link hovered" event from the plugin
+ MEDIA_EVENT_LINK_HOVERED, // Got a "link hovered" event from the plugin
+
+ MEDIA_EVENT_ACCELERATED_UPDATE // Accelerated (GPU) texture handle update from the plugin
} EMediaEvent;
diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt
index fcd287bbb39..a861bd671d4 100644
--- a/indra/llrender/CMakeLists.txt
+++ b/indra/llrender/CMakeLists.txt
@@ -106,3 +106,7 @@ target_link_libraries(llrender
OpenGL::GLU
)
+if (WINDOWS)
+ target_link_libraries(llrender d3d11 dxgi)
+endif (WINDOWS)
+
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 4584ed1d865..fd43cc0d112 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -56,6 +56,8 @@
#if LL_WINDOWS
#include "lldxhardware.h"
+#include
+#include
#endif
#ifdef _DEBUG
@@ -236,6 +238,14 @@ PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC wglBlitContextFramebufferAMD = n
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = nullptr;
+// WGL_NV_DX_interop
+PFNWGLDXOPENDEVICENVPROC wglDXOpenDeviceNV = nullptr;
+PFNWGLDXCLOSEDEVICENVPROC wglDXCloseDeviceNV = nullptr;
+PFNWGLDXREGISTEROBJECTNVPROC wglDXRegisterObjectNV = nullptr;
+PFNWGLDXUNREGISTEROBJECTNVPROC wglDXUnregisterObjectNV = nullptr;
+PFNWGLDXLOCKOBJECTSNVPROC wglDXLockObjectsNV = nullptr;
+PFNWGLDXUNLOCKOBJECTSNVPROC wglDXUnlockObjectsNV = nullptr;
+
#endif
// GL_VERSION_1_2
@@ -1058,6 +1068,57 @@ void LLGLManager::initWGL()
{
LL_WARNS("RenderInit") << "No ARB WGL render texture extensions" << LL_ENDL;
}
+
+ mHasNVDXInterop = ExtensionExists("WGL_NV_DX_interop", gGLHExts.mSysExts);
+ if (mHasNVDXInterop)
+ {
+ wglDXOpenDeviceNV = (PFNWGLDXOPENDEVICENVPROC)GLH_EXT_GET_PROC_ADDRESS("wglDXOpenDeviceNV");
+ wglDXCloseDeviceNV = (PFNWGLDXCLOSEDEVICENVPROC)GLH_EXT_GET_PROC_ADDRESS("wglDXCloseDeviceNV");
+ wglDXRegisterObjectNV = (PFNWGLDXREGISTEROBJECTNVPROC)GLH_EXT_GET_PROC_ADDRESS("wglDXRegisterObjectNV");
+ wglDXUnregisterObjectNV = (PFNWGLDXUNREGISTEROBJECTNVPROC)GLH_EXT_GET_PROC_ADDRESS("wglDXUnregisterObjectNV");
+ wglDXLockObjectsNV = (PFNWGLDXLOCKOBJECTSNVPROC)GLH_EXT_GET_PROC_ADDRESS("wglDXLockObjectsNV");
+ wglDXUnlockObjectsNV = (PFNWGLDXUNLOCKOBJECTSNVPROC)GLH_EXT_GET_PROC_ADDRESS("wglDXUnlockObjectsNV");
+
+ // Discover which adapter the GL context lives on by probing wglDXOpenDeviceNV
+ IDXGIFactory1* dxgi_factory = nullptr;
+ if (SUCCEEDED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&dxgi_factory)))
+ {
+ IDXGIAdapter* adapter = nullptr;
+ for (UINT i = 0; dxgi_factory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND; ++i)
+ {
+ ID3D11Device* probe_device = nullptr;
+ HRESULT hr = D3D11CreateDevice(
+ adapter, D3D_DRIVER_TYPE_UNKNOWN,
+ nullptr, 0, nullptr, 0,
+ D3D11_SDK_VERSION,
+ &probe_device, nullptr, nullptr);
+
+ if (SUCCEEDED(hr) && probe_device)
+ {
+ HANDLE probe_handle = wglDXOpenDeviceNV(probe_device);
+ if (probe_handle)
+ {
+ // This adapter matches the GL context
+ DXGI_ADAPTER_DESC adesc;
+ if (SUCCEEDED(adapter->GetDesc(&adesc)))
+ {
+ mGLAdapterLuidHigh = adesc.AdapterLuid.HighPart;
+ mGLAdapterLuidLow = adesc.AdapterLuid.LowPart;
+ LL_INFOS("RenderInit") << "GL adapter LUID: "
+ << std::hex << mGLAdapterLuidHigh << ":" << mGLAdapterLuidLow << LL_ENDL;
+ }
+ wglDXCloseDeviceNV(probe_handle);
+ probe_device->Release();
+ adapter->Release();
+ break;
+ }
+ probe_device->Release();
+ }
+ adapter->Release();
+ }
+ dxgi_factory->Release();
+ }
+ }
#endif
}
diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h
index e1ab2a49e6d..a3f57753697 100644
--- a/indra/llrender/llgl.h
+++ b/indra/llrender/llgl.h
@@ -100,6 +100,9 @@ class LLGLManager
// Vendor-specific extensions
bool mHasAMDAssociations = false;
bool mHasNVXGpuMemoryInfo = false;
+ bool mHasNVDXInterop = false;
+ S32 mGLAdapterLuidHigh = 0;
+ U32 mGLAdapterLuidLow = 0;
bool mIsAMD;
bool mIsNVIDIA;
diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h
index 3d4dc5e6982..d6a929cf449 100644
--- a/indra/llrender/llglheaders.h
+++ b/indra/llrender/llglheaders.h
@@ -74,6 +74,14 @@ extern PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
// WGL_ARB_create_context
extern PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
+// WGL_NV_DX_interop
+extern PFNWGLDXOPENDEVICENVPROC wglDXOpenDeviceNV;
+extern PFNWGLDXCLOSEDEVICENVPROC wglDXCloseDeviceNV;
+extern PFNWGLDXREGISTEROBJECTNVPROC wglDXRegisterObjectNV;
+extern PFNWGLDXUNREGISTEROBJECTNVPROC wglDXUnregisterObjectNV;
+extern PFNWGLDXLOCKOBJECTSNVPROC wglDXLockObjectsNV;
+extern PFNWGLDXUNLOCKOBJECTSNVPROC wglDXUnlockObjectsNV;
+
// GL_VERSION_1_3
extern PFNGLACTIVETEXTUREPROC glActiveTexture;
extern PFNGLSAMPLECOVERAGEPROC glSampleCoverage;
diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp
index 4dcca5a726a..e67c15cf76b 100644
--- a/indra/llrender/llgltexture.cpp
+++ b/indra/llrender/llgltexture.cpp
@@ -25,7 +25,12 @@
*/
#include "linden_common.h"
#include "llgltexture.h"
+#include "llimagegl.h"
+#if LL_WINDOWS
+#include
+#include
+#endif
LLGLTexture::LLGLTexture(bool usemipmaps)
{
@@ -77,6 +82,9 @@ void LLGLTexture::init()
void LLGLTexture::cleanup()
{
+#if LL_WINDOWS
+ releaseInteropResources();
+#endif
if(mGLTexturep)
{
mGLTexturep->cleanup();
@@ -367,6 +375,340 @@ void LLGLTexture::destroyGLTexture()
}
}
+bool LLGLTexture::createGLTextureFromHandle(void* handle, S32 width, S32 height, LLGLuint* tex_name)
+{
+#if LL_WINDOWS
+ if (!gGLManager.mHasNVDXInterop)
+ {
+ LL_WARNS("Texture") << "WGL_NV_DX_interop not available" << LL_ENDL;
+ return false;
+ }
+
+ ID3D11Device1* d3d_device = (ID3D11Device1*)mInteropDevice;
+ ID3D11DeviceContext* d3d_context = (ID3D11DeviceContext*)mInteropContext;
+ HANDLE gl_device = mInteropGLDevice;
+
+ // Lazily create the D3D device and GL interop device — reuse across frames
+ if (!d3d_device)
+ {
+ IDXGIAdapter* gl_adapter = nullptr;
+ IDXGIFactory1* factory = nullptr;
+ HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory);
+ if (SUCCEEDED(hr) && factory)
+ {
+ if (gGLManager.mGLAdapterLuidHigh || gGLManager.mGLAdapterLuidLow)
+ {
+ IDXGIAdapter* adapter = nullptr;
+ for (UINT i = 0; factory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND; ++i)
+ {
+ DXGI_ADAPTER_DESC adesc;
+ if (SUCCEEDED(adapter->GetDesc(&adesc)) &&
+ adesc.AdapterLuid.HighPart == (LONG)gGLManager.mGLAdapterLuidHigh &&
+ adesc.AdapterLuid.LowPart == (DWORD)gGLManager.mGLAdapterLuidLow)
+ {
+ gl_adapter = adapter;
+ break;
+ }
+ adapter->Release();
+ }
+ }
+ factory->Release();
+ }
+
+ ID3D11Device* base_device = nullptr;
+ hr = D3D11CreateDevice(
+ gl_adapter,
+ gl_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE,
+ nullptr, 0, nullptr, 0,
+ D3D11_SDK_VERSION,
+ &base_device, nullptr, &d3d_context);
+
+ if (gl_adapter)
+ {
+ gl_adapter->Release();
+ }
+
+ if (FAILED(hr) || !base_device)
+ {
+ LL_WARNS("Texture") << "Failed to create D3D11 device, hr=0x" << std::hex << hr << LL_ENDL;
+ return false;
+ }
+
+ hr = base_device->QueryInterface(__uuidof(ID3D11Device1), (void**)&d3d_device);
+ base_device->Release();
+ if (FAILED(hr) || !d3d_device)
+ {
+ LL_WARNS("Texture") << "Failed to get ID3D11Device1, hr=0x" << std::hex << hr << LL_ENDL;
+ d3d_context->Release();
+ return false;
+ }
+
+ gl_device = wglDXOpenDeviceNV(d3d_device);
+ if (!gl_device)
+ {
+ LL_WARNS("Texture") << "wglDXOpenDeviceNV failed" << LL_ENDL;
+ d3d_context->Release();
+ d3d_device->Release();
+ return false;
+ }
+
+ mInteropDevice = d3d_device;
+ mInteropContext = d3d_context;
+ mInteropGLDevice = gl_device;
+ }
+
+ // Open the shared texture — NT handle from CEF via DuplicateHandle
+ ID3D11Texture2D* src_texture = nullptr;
+ HRESULT hr = d3d_device->OpenSharedResource1((HANDLE)handle, __uuidof(ID3D11Texture2D), (void**)&src_texture);
+ if (FAILED(hr) || !src_texture)
+ {
+ LL_WARNS("Texture") << "OpenSharedResource1 failed, handle=0x" << std::hex << (uintptr_t)handle
+ << " hr=0x" << hr << LL_ENDL;
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC desc;
+ src_texture->GetDesc(&desc);
+
+ // (Re)create the copy texture if dimensions changed or first call
+ ID3D11Texture2D* d3d_texture = (ID3D11Texture2D*)mInteropTexture;
+ bool need_new_texture = !d3d_texture;
+ if (d3d_texture)
+ {
+ D3D11_TEXTURE2D_DESC existing_desc;
+ d3d_texture->GetDesc(&existing_desc);
+ if (existing_desc.Width != (UINT)width || existing_desc.Height != (UINT)height || existing_desc.Format != desc.Format)
+ {
+ need_new_texture = true;
+ }
+ }
+
+ if (need_new_texture)
+ {
+ // Build the new texture and interop BEFORE tearing down the old one,
+ // so a failure leaves the previous frame intact
+ D3D11_TEXTURE2D_DESC copy_desc = {};
+ copy_desc.Width = width;
+ copy_desc.Height = height;
+ copy_desc.MipLevels = 1;
+ copy_desc.ArraySize = 1;
+ copy_desc.Format = desc.Format;
+ copy_desc.SampleDesc.Count = 1;
+ copy_desc.SampleDesc.Quality = 0;
+ copy_desc.Usage = D3D11_USAGE_DEFAULT;
+ copy_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+
+ ID3D11Texture2D* new_texture = nullptr;
+ hr = d3d_device->CreateTexture2D(©_desc, nullptr, &new_texture);
+ if (FAILED(hr) || !new_texture)
+ {
+ LL_WARNS("Texture") << "Failed to create copy texture, hr=0x" << std::hex << hr << LL_ENDL;
+ src_texture->Release();
+ return (mInteropGLHandle != nullptr); // keep old frame if available
+ }
+
+ LLGLuint new_gl_name = 0;
+ LLImageGL::generateTextures(1, &new_gl_name);
+ if (!new_gl_name)
+ {
+ LL_WARNS("Texture") << "Failed to generate GL texture name" << LL_ENDL;
+ new_texture->Release();
+ src_texture->Release();
+ return (mInteropGLHandle != nullptr);
+ }
+
+ HANDLE new_gl_handle = wglDXRegisterObjectNV(gl_device, new_texture, new_gl_name, GL_TEXTURE_2D, WGL_ACCESS_READ_ONLY_NV);
+ if (!new_gl_handle)
+ {
+ LL_WARNS("Texture") << "wglDXRegisterObjectNV failed" << LL_ENDL;
+ LLImageGL::deleteTextures(1, &new_gl_name);
+ new_texture->Release();
+ src_texture->Release();
+ return (mInteropGLHandle != nullptr);
+ }
+
+ if (!wglDXLockObjectsNV(gl_device, 1, &new_gl_handle))
+ {
+ LL_WARNS("Texture") << "wglDXLockObjectsNV failed after registration" << LL_ENDL;
+ wglDXUnregisterObjectNV(gl_device, new_gl_handle);
+ LLImageGL::deleteTextures(1, &new_gl_name);
+ new_texture->Release();
+ src_texture->Release();
+ return (mInteropGLHandle != nullptr);
+ }
+
+ // New resources are ready — now tear down the old ones
+ if (mInteropGLHandle)
+ {
+ wglDXUnlockObjectsNV(gl_device, 1, &mInteropGLHandle);
+ wglDXUnregisterObjectNV(gl_device, mInteropGLHandle);
+ }
+ if (d3d_texture)
+ {
+ d3d_texture->Release();
+ }
+
+ d3d_texture = new_texture;
+ mInteropTexture = new_texture;
+ mInteropGLHandle = new_gl_handle;
+ mInteropSrcTex = new_gl_name;
+
+ // Create persistent output texture for the flipped result
+ if (mInteropOutputTex)
+ {
+ LLImageGL::deleteTextures(1, &mInteropOutputTex);
+ }
+ LLImageGL::generateTextures(1, &mInteropOutputTex);
+ glBindTexture(GL_TEXTURE_2D, mInteropOutputTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Create FBO for the blit
+ if (mInteropBlitFBO)
+ {
+ glDeleteFramebuffers(1, &mInteropBlitFBO);
+ }
+ glGenFramebuffers(1, &mInteropBlitFBO);
+
+ if (mGLTexturep.isNull())
+ {
+ generateGLTexture();
+ }
+ mGLTexturep->setTexName(mInteropOutputTex);
+ mGLTexturep->setGLTextureCreated(true);
+
+ mFullWidth = width;
+ mFullHeight = height;
+ mComponents = 4;
+ setTexelsPerImage();
+ }
+
+ // Unlock GL, copy D3D, re-lock GL — the lock/unlock handles D3D↔GL sync
+ HANDLE gl_handle = mInteropGLHandle;
+ wglDXUnlockObjectsNV(gl_device, 1, &gl_handle);
+
+ // Acquire keyed mutex on the source if present — use 0 timeout to avoid stalling
+ IDXGIKeyedMutex* keyed_mutex = nullptr;
+ src_texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyed_mutex);
+ if (keyed_mutex)
+ {
+ hr = keyed_mutex->AcquireSync(0, 0);
+ if (FAILED(hr))
+ {
+ // Texture not ready yet — skip this frame, keep previous content
+ keyed_mutex->Release();
+ src_texture->Release();
+ wglDXLockObjectsNV(gl_device, 1, &gl_handle);
+ return true;
+ }
+ }
+
+ D3D11_BOX src_box = {};
+ src_box.left = 0;
+ src_box.right = desc.Width;
+ src_box.top = 0;
+ src_box.bottom = desc.Height;
+ src_box.front = 0;
+ src_box.back = 1;
+ d3d_context->CopySubresourceRegion(d3d_texture, 0, 0, 0, 0, src_texture, 0, &src_box);
+
+ if (keyed_mutex)
+ {
+ keyed_mutex->ReleaseSync(0);
+ keyed_mutex->Release();
+ }
+ src_texture->Release();
+
+ // Re-lock interop for GL access
+ wglDXLockObjectsNV(gl_device, 1, &gl_handle);
+
+ // Blit from interop texture to output texture with vertical flip
+ // Blit from interop texture to output texture with vertical flip
+ GLint prev_read_fbo = 0, prev_draw_fbo = 0;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &prev_read_fbo);
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_draw_fbo);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mInteropBlitFBO);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mInteropSrcTex, 0);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mInteropOutputTex, 0);
+ glReadBuffer(GL_COLOR_ATTACHMENT0);
+ glDrawBuffer(GL_COLOR_ATTACHMENT1);
+
+ // Blit only the content region (desc.Width x desc.Height), not the power-of-two padding.
+ // Flip Y and place at bottom-left of the destination (GL row 0 = bottom).
+ glBlitFramebuffer(
+ 0, 0, desc.Width, desc.Height, // src: content region
+ 0, desc.Height, desc.Width, 0, // dst: flipped into bottom-left
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, prev_read_fbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prev_draw_fbo);
+
+ if (tex_name)
+ {
+ *tex_name = mInteropOutputTex;
+ }
+
+ return true;
+#else
+ LL_WARNS("Texture") << "createGLTextureFromHandle is only supported on Windows" << LL_ENDL;
+ return false;
+#endif
+}
+
+void LLGLTexture::releaseInteropResources()
+{
+#if LL_WINDOWS
+ if (mInteropBlitFBO)
+ {
+ glDeleteFramebuffers(1, &mInteropBlitFBO);
+ mInteropBlitFBO = 0;
+ }
+ if (mInteropOutputTex)
+ {
+ LLImageGL::deleteTextures(1, &mInteropOutputTex);
+ mInteropOutputTex = 0;
+ }
+ if (mInteropSrcTex)
+ {
+ LLImageGL::deleteTextures(1, &mInteropSrcTex);
+ mInteropSrcTex = 0;
+ }
+ if (mInteropGLHandle)
+ {
+ wglDXUnlockObjectsNV(mInteropGLDevice, 1, &mInteropGLHandle);
+ wglDXUnregisterObjectNV(mInteropGLDevice, mInteropGLHandle);
+ mInteropGLHandle = nullptr;
+ }
+ if (mInteropGLDevice)
+ {
+ wglDXCloseDeviceNV(mInteropGLDevice);
+ mInteropGLDevice = nullptr;
+ }
+ if (mInteropTexture)
+ {
+ ((ID3D11Texture2D*)mInteropTexture)->Release();
+ mInteropTexture = nullptr;
+ }
+ if (mInteropContext)
+ {
+ ((ID3D11DeviceContext*)mInteropContext)->Release();
+ mInteropContext = nullptr;
+ }
+ if (mInteropDevice)
+ {
+ ((ID3D11Device1*)mInteropDevice)->Release();
+ mInteropDevice = nullptr;
+ }
+#endif
+}
+
void LLGLTexture::setTexelsPerImage()
{
U32 fullwidth = llmin(mFullWidth,U32(MAX_IMAGE_SIZE_DEFAULT));
diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h
index 122d2a7f9ca..6eb931792ad 100644
--- a/indra/llrender/llgltexture.h
+++ b/indra/llrender/llgltexture.h
@@ -138,6 +138,16 @@ class LLGLTexture : public LLTexture
void setTexName(LLGLuint); // for forcing w/ externally created textures only
void setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target);
+ bool createGLTextureFromHandle(void* handle, S32 width, S32 height, LLGLuint* tex_name = nullptr);
+ bool hasInteropTexture() const {
+#ifdef LL_WINDOWS
+ return mInteropGLHandle != nullptr;
+#else
+ return false;
+#endif
+ }
+ void releaseInteropResources();
+
LLTexUnit::eTextureAddressMode getAddressMode(void) const ;
S32 getMaxDiscardLevel() const;
S32 getDiscardLevel() const;
@@ -194,8 +204,19 @@ class LLGLTexture : public LLTexture
protected:
LLGLTextureState mTextureState ;
-
+#if LL_WINDOWS
+ // NV_DX_interop resources kept alive while the GL texture is in use
+ void* mInteropDevice = nullptr; // ID3D11Device1*
+ void* mInteropContext = nullptr; // ID3D11DeviceContext*
+ void* mInteropTexture = nullptr; // ID3D11Texture2D*
+ void* mInteropGLDevice = nullptr; // HANDLE from wglDXOpenDeviceNV
+ void* mInteropGLHandle = nullptr; // HANDLE from wglDXRegisterObjectNV
+ LLGLuint mInteropSrcTex = 0; // GL name from NV_DX_interop registration
+ LLGLuint mInteropBlitFBO = 0; // FBO for flip blit
+ LLGLuint mInteropOutputTex = 0; // Persistent flipped GL texture
+#endif
};
#endif // LL_GL_TEXTURE_H
+
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 4a3d32c7ffc..0cfe4db54d8 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1165,7 +1165,7 @@ bool LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
<< " getHeight() " << getHeight()
<< LL_ENDL;
}
-
+
if ((x_pos + width) > data_width ||
(y_pos + height) > data_height)
{
@@ -1180,7 +1180,6 @@ bool LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
<< LL_ENDL;
}
-
glPixelStorei(GL_UNPACK_ROW_LENGTH, data_width);
stop_glerror();
@@ -1489,7 +1488,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
}
//create an empty GL texture: just create a texture name
-//the texture is assiciate with some image by calling glTexImage outside LLImageGL
+//the texture is associated with some image by calling glTexImage outside LLImageGL
bool LLImageGL::createGLTexture()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt
index dc2d82017d0..0fb0e4a0b9a 100644
--- a/indra/media_plugins/cef/CMakeLists.txt
+++ b/indra/media_plugins/cef/CMakeLists.txt
@@ -54,6 +54,10 @@ target_link_libraries(media_plugin_cef
ll::cef
)
+if (WINDOWS)
+ target_link_libraries(media_plugin_cef d3d11 dxgi)
+endif (WINDOWS)
+
if (WINDOWS)
set_target_properties(
media_plugin_cef
diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp
index caf804f915d..b018c7b3ef7 100644
--- a/indra/media_plugins/cef/media_plugin_cef.cpp
+++ b/indra/media_plugins/cef/media_plugin_cef.cpp
@@ -41,10 +41,13 @@
// _getpid()/getpid()
#if LL_WINDOWS
#include
+#include
+#include
#else
#include
#endif
+#include
#include "dullahan.h"
////////////////////////////////////////////////////////////////////////////////
@@ -63,6 +66,7 @@ class MediaPluginCEF :
bool init();
void onPageChangedCallback(const unsigned char* pixels, int x, int y, const int width, const int height);
+ void onAcceleratedPageChangedCallback(void* handle, const std::vector& dirty_rects);
void onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect);
void onConsoleMessageCallback(std::string message, std::string source, int line);
void onStatusMessageCallback(std::string value);
@@ -120,6 +124,16 @@ class MediaPluginCEF :
VolumeCatcher mVolumeCatcher;
F32 mCurVolume;
dullahan* mCEFLib;
+#if LL_WINDOWS
+ DWORD mViewerProcessID;
+ LUID mViewerAdapterLuid;
+ bool mSharedTextureEnable;
+ ID3D11Device* mD3DDevice;
+ ID3D11Device1* mD3DDevice1;
+ ID3D11DeviceContext* mD3DContext;
+ U32 mNextTextureId;
+ std::unordered_map mCopyTextures;
+#endif
};
////////////////////////////////////////////////////////////////////////////////
@@ -162,6 +176,16 @@ MediaPluginBase(host_send_func, host_user_data)
mCEFLib = new dullahan();
+#if LL_WINDOWS
+ mViewerProcessID = 0;
+ mViewerAdapterLuid = {};
+ mSharedTextureEnable = false;
+ mD3DDevice = nullptr;
+ mD3DDevice1 = nullptr;
+ mD3DContext = nullptr;
+ mNextTextureId = 1;
+#endif
+
setVolume();
}
@@ -170,6 +194,32 @@ MediaPluginBase(host_send_func, host_user_data)
MediaPluginCEF::~MediaPluginCEF()
{
mCEFLib->shutdown();
+
+#if LL_WINDOWS
+ for (auto& pair : mCopyTextures)
+ {
+ if (pair.second)
+ {
+ pair.second->Release();
+ }
+ }
+ mCopyTextures.clear();
+ if (mD3DDevice1)
+ {
+ mD3DDevice1->Release();
+ mD3DDevice1 = nullptr;
+ }
+ if (mD3DContext)
+ {
+ mD3DContext->Release();
+ mD3DContext = nullptr;
+ }
+ if (mD3DDevice)
+ {
+ mD3DDevice->Release();
+ mD3DDevice = nullptr;
+ }
+#endif
}
////////////////////////////////////////////////////////////////////////////////
@@ -206,6 +256,49 @@ void MediaPluginCEF::onPageChangedCallback(const unsigned char* pixels, int x, i
}
}
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginCEF::onAcceleratedPageChangedCallback(void* handle, const std::vector& dirty_rects)
+{
+#if LL_WINDOWS
+ // DuplicateHandle CEF's NT handle into the viewer process
+ HANDLE viewer_handle = nullptr;
+ HANDLE viewer_process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, mViewerProcessID);
+ if (viewer_process)
+ {
+ DuplicateHandle(
+ GetCurrentProcess(),
+ handle,
+ viewer_process,
+ &viewer_handle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+ CloseHandle(viewer_process);
+ }
+
+ if (!viewer_handle)
+ {
+ return;
+ }
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "accelerated_update");
+ message.setValuePointer("handle", viewer_handle);
+ message.setValueS32("dirty_count", (S32)dirty_rects.size());
+
+ for (size_t i = 0; i < dirty_rects.size(); ++i)
+ {
+ std::string prefix = "dirty_" + std::to_string(i) + "_";
+ message.setValueS32(prefix + "x", dirty_rects[i].x);
+ message.setValueS32(prefix + "y", dirty_rects[i].y);
+ message.setValueS32(prefix + "width", dirty_rects[i].width);
+ message.setValueS32(prefix + "height", dirty_rects[i].height);
+ }
+
+ sendMessage(message);
+#endif
+}
+
////////////////////////////////////////////////////////////////////////////////
//
void MediaPluginCEF::onConsoleMessageCallback(std::string message, std::string source, int line)
@@ -612,8 +705,15 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
if (message_name == "init")
{
+#if LL_WINDOWS
+ mViewerProcessID = message_in.getValueU32("viewer_pid");
+ mViewerAdapterLuid.HighPart = (LONG)message_in.getValueS32("adapter_luid_high");
+ mViewerAdapterLuid.LowPart = (DWORD)message_in.getValueU32("adapter_luid_low");
+ mSharedTextureEnable = message_in.getValueBoolean("shared_texture_enable");
+#endif
// event callbacks from Dullahan
mCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
+ mCEFLib->setOnAcceleratedPageChangedCallback(std::bind(&MediaPluginCEF::onAcceleratedPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2));
mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1));
@@ -711,6 +811,13 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
settings.log_verbose = mCefLogVerbose;
settings.autoplay_without_gesture = true;
+#if LL_WINDOWS
+ settings.use_adapter_luid = true;
+ settings.adapter_luid.high_part = mViewerAdapterLuid.HighPart;
+ settings.adapter_luid.low_part = mViewerAdapterLuid.LowPart;
+ settings.shared_texture_enable = mSharedTextureEnable;
+#endif
+
std::vector custom_schemes(1, "secondlife");
mCEFLib->setCustomSchemes(custom_schemes);
@@ -933,6 +1040,21 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
mEnableMediaPluginDebugging = message_in.getValueBoolean("enable");
}
+#if LL_WINDOWS0
+ else if (message_name == "accelerated_release")
+ {
+ U32 texture_id = message_in.getValueU32("texture_id");
+ auto it = mCopyTextures.find(texture_id);
+ if (it != mCopyTextures.end())
+ {
+ if (it->second)
+ {
+ it->second->Release();
+ }
+ mCopyTextures.erase(it);
+ }
+ }
+#endif
if (message_name == "pick_file_response")
{
LLSD file_list_llsd = message_in.getValueLLSD("file_list");
diff --git a/indra/media_plugins/example/CMakeLists.txt b/indra/media_plugins/example/CMakeLists.txt
index be8ffe5a40a..67a0287d686 100644
--- a/indra/media_plugins/example/CMakeLists.txt
+++ b/indra/media_plugins/example/CMakeLists.txt
@@ -10,6 +10,7 @@ include(Linking)
include(PluginAPI)
include(ExamplePlugin)
+include(CEFPlugin)
### media_plugin_example
@@ -22,7 +23,11 @@ add_library(media_plugin_example
${media_plugin_example_SOURCE_FILES}
)
-target_link_libraries(media_plugin_example media_plugin_base )
+target_link_libraries(media_plugin_example media_plugin_base ll::cef)
+
+if (WINDOWS)
+ target_link_libraries(media_plugin_example d3d11 dxgi)
+endif (WINDOWS)
if (WINDOWS)
set_target_properties(
diff --git a/indra/media_plugins/example/media_plugin_example.cpp b/indra/media_plugins/example/media_plugin_example.cpp
index 0b22b7833ff..6231b3f7b78 100644
--- a/indra/media_plugins/example/media_plugin_example.cpp
+++ b/indra/media_plugins/example/media_plugin_example.cpp
@@ -34,6 +34,13 @@
#include "llpluginmessageclasses.h"
#include "media_plugin_base.h"
+#if LL_WINDOWS
+#include
+#include
+#endif
+
+#include "dullahan.h"
+
#include
////////////////////////////////////////////////////////////////////////////////
@@ -50,8 +57,15 @@ class mediaPluginExample :
private:
bool init();
void update(F64 milliseconds);
+ void determineAdapterLuid();
bool mFirstTime;
+ dullahan* mCEFLib;
+#if LL_WINDOWS
+ S32 mAdapterLuidHigh;
+ U32 mAdapterLuidLow;
+#endif
+
time_t mLastUpdateTime;
enum Constants { ENumObjects = 64 };
unsigned char* mBackgroundPixels;
@@ -79,6 +93,12 @@ MediaPluginBase(host_send_func, host_user_data)
mPixels = 0;
mLastUpdateTime = 0;
mBackgroundPixels = 0;
+
+ mCEFLib = new dullahan();
+#if LL_WINDOWS
+ mAdapterLuidHigh = 0;
+ mAdapterLuidLow = 0;
+#endif
}
////////////////////////////////////////////////////////////////////////////////
@@ -369,6 +389,30 @@ void mediaPluginExample::update(F64 milliseconds)
setDirty(0, 0, mWidth, mHeight);
};
+////////////////////////////////////////////////////////////////////////////////
+//
+void mediaPluginExample::determineAdapterLuid()
+{
+#if LL_WINDOWS
+ IDXGIFactory1* dxgi_factory = nullptr;
+ if (SUCCEEDED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&dxgi_factory)))
+ {
+ IDXGIAdapter* adapter = nullptr;
+ if (SUCCEEDED(dxgi_factory->EnumAdapters(0, &adapter)))
+ {
+ DXGI_ADAPTER_DESC adesc;
+ if (SUCCEEDED(adapter->GetDesc(&adesc)))
+ {
+ mAdapterLuidHigh = adesc.AdapterLuid.HighPart;
+ mAdapterLuidLow = adesc.AdapterLuid.LowPart;
+ }
+ adapter->Release();
+ }
+ dxgi_factory->Release();
+ }
+#endif
+}
+
////////////////////////////////////////////////////////////////////////////////
//
bool mediaPluginExample::init()
@@ -377,6 +421,16 @@ bool mediaPluginExample::init()
message.setValue("name", "Example Plugin");
sendMessage(message);
+ determineAdapterLuid();
+
+ dullahan::dullahan_settings settings;
+#if LL_WINDOWS
+ settings.use_adapter_luid = true;
+ settings.adapter_luid.high_part = mAdapterLuidHigh;
+ settings.adapter_luid.low_part = mAdapterLuidLow;
+#endif
+ mCEFLib->init(settings);
+
return true;
};
diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp
index c7b60b2fd56..03e625fbea8 100644
--- a/indra/newview/llmediactrl.cpp
+++ b/indra/newview/llmediactrl.cpp
@@ -1000,6 +1000,12 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
};
break;
+ case MEDIA_EVENT_ACCELERATED_UPDATE:
+ {
+ // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_ACCELERATED_UPDATE " << LL_ENDL;
+ };
+ break;
+
case MEDIA_EVENT_TIME_DURATION_UPDATED:
{
// LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << LL_ENDL;
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index be4961e3c44..f22c75bb31b 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -39,7 +39,11 @@
#include "llfilepicker.h"
#include "llfloaterwebcontent.h" // for handling window close requests and geometry change requests in media browser windows.
#include "llfocusmgr.h"
+#include "llgl.h"
#include "llimagegl.h"
+#if LL_WINDOWS
+#include "llwin32headers.h"
+#endif
#include "llkeyboard.h"
#include "lllogininstance.h"
#include "llmarketplacefunctions.h"
@@ -1879,7 +1883,8 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
media_source->setTarget(target);
const std::string plugin_dir = gDirUtilp->getLLPluginDir();
- if (media_source->init(launcher_name, plugin_dir, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")))
+ if (media_source->init(launcher_name, plugin_dir, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"),
+ gGLManager.mGLAdapterLuidHigh, gGLManager.mGLAdapterLuidLow, gGLManager.mHasNVDXInterop))
{
return media_source;
}
@@ -3101,6 +3106,13 @@ void LLViewerMediaImpl::doMediaTexUpdate(LLViewerMediaTexture* media_tex, U8* da
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
LLCoros::LockType lock(mLock); // don't allow media source tear-down during update
+ // If the accelerated (D3D interop) texture is active, skip the software path —
+ // the GL texture is updated on the main thread via MEDIA_EVENT_ACCELERATED_UPDATE
+ if (media_tex->hasInteropTexture())
+ {
+ return;
+ }
+
// wrap "data" in an LLImageRaw but do NOT make a copy
LLPointer raw = new LLImageRaw(data, media_tex->getWidth(), media_tex->getHeight(), media_tex->getComponents(), true);
@@ -3152,6 +3164,12 @@ LLViewerMediaTexture* LLViewerMediaImpl::updateMediaImage()
// *TODO: Consider enabling mipmaps (they have been disabled for a long time). Likely has a significant performance impact for tiled/high texture repeat media. Mip generation in a shader may also be an option if necessary.
LLViewerMediaTexture* media_tex = LLViewerTextureManager::getMediaTexture( mTextureId, USE_MIPMAPS );
+ // Don't destroy/recreate the texture when it's backed by D3D interop
+ if (media_tex->hasInteropTexture())
+ {
+ return media_tex;
+ }
+
if ( mNeedsNewTexture
|| (media_tex->getWidth() != mMediaSource->getTextureWidth())
|| (media_tex->getHeight() != mMediaSource->getTextureHeight())
@@ -3164,6 +3182,7 @@ LLViewerMediaTexture* LLViewerMediaImpl::updateMediaImage()
int texture_width = mMediaSource->getTextureWidth();
int texture_height = mMediaSource->getTextureHeight();
+
int texture_depth = mMediaSource->getTextureDepth();
// MEDIAOPT: check to see if size actually changed before doing work
@@ -3613,6 +3632,44 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
}
break;
+ case LLViewerMediaObserver::MEDIA_EVENT_ACCELERATED_UPDATE:
+ {
+ void* accel_handle = plugin->getAcceleratedTextureHandle();
+ if (accel_handle)
+ {
+ LLViewerMediaTexture* media_tex = LLViewerTextureManager::getMediaTexture(mTextureId, USE_MIPMAPS);
+ if (media_tex)
+ {
+ // Pass power-of-two dimensions so the copy texture matches
+ // what the renderer expects (content in top-left sub-region)
+ S32 tex_width = llmin(plugin->getTextureWidth(), gGLManager.mGLMaxTextureSize);
+ S32 tex_height = llmin(plugin->getTextureHeight(), gGLManager.mGLMaxTextureSize);
+
+ LLGLuint tex_name = 0;
+ if (media_tex->createGLTextureFromHandle(accel_handle, tex_width, tex_height, &tex_name))
+ {
+ media_tex->getGLTexture()->syncTexName(tex_name);
+
+ mTextureUsedWidth = plugin->getWidth();
+ mTextureUsedHeight = plugin->getHeight();
+ mNeedsNewTexture = false;
+ }
+ else
+ {
+ // Handle was stale (e.g. resize in progress) — release interop
+ // so the system can recover on the next successful frame
+ media_tex->releaseInteropResources();
+ mNeedsNewTexture = true;
+ }
+ }
+#if LL_WINDOWS
+ CloseHandle(accel_handle);
+#endif
+ plugin->releaseAcceleratedTextureHandle();
+ }
+ }
+ break;
+
default:
break;
}
diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp
index 9af0062f1ef..25d6a0de80f 100644
--- a/indra/newview/llviewerparcelmedia.cpp
+++ b/indra/newview/llviewerparcelmedia.cpp
@@ -488,6 +488,12 @@ void LLViewerParcelMedia::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
};
break;
+ case MEDIA_EVENT_ACCELERATED_UPDATE:
+ {
+ // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_ACCELERATED_UPDATE " << LL_ENDL;
+ };
+ break;
+
case MEDIA_EVENT_TIME_DURATION_UPDATED:
{
// LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << LL_ENDL;