diff --git a/autobuild.xml b/autobuild.xml index 6e48cbf5093..67029300ef0 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -450,11 +450,11 @@ archive hash - 126e0fa4c16dfd433c9fb7d1d242da98f213d933 + 2a4147260900b4a8ebc781cd8d0dc385b9983c89 hash_algorithm sha1 url - https://github.com/secondlife/dullahan/releases/download/v1.24.0-CEF_139.0.40/dullahan-1.24.0.202510081737_139.0.40_g465474a_chromium-139.0.7258.139-darwin64-18353103947.tar.zst + https://github.com/secondlife/dullahan/releases/download/v1.26.1-CEF_139.0.40.accel/dullahan-1.26.0.202604010801_139.0.40_g465474a_chromium-139.0.7258.139-darwin64-23838332395.tar.zst name darwin64 @@ -464,11 +464,11 @@ archive hash - 209d031ae67bc66d8e8f8509c71d259014c65ceb + 6f8d73431199fb0f669ca510ae1bdb5d797cd31e hash_algorithm sha1 url - https://github.com/secondlife/dullahan/releases/download/v1.14.0-r3/dullahan-1.14.0.202408091637_118.4.1_g3dd6078_chromium-118.0.5993.54-linux64-10322607516.tar.zst + https://github.com/secondlife/dullahan/releases/download/v1.26.1-CEF_139.0.40.accel/dullahan-1.26.0.202604010801_139.0.40_g465474a_chromium-139.0.7258.139-linux64-23838332395.tar.zst name linux64 @@ -478,11 +478,11 @@ archive hash - 20de62c9e57d9e6539c1e2437ec4b46c3ca237bc + 7a477ea33efaf1e6cea662ea602de914a718771e hash_algorithm sha1 url - https://github.com/secondlife/dullahan/releases/download/v1.24.0-CEF_139.0.40/dullahan-1.24.0.202510081738_139.0.40_g465474a_chromium-139.0.7258.139-windows64-18353103947.tar.zst + https://github.com/secondlife/dullahan/releases/download/v1.26.1-CEF_139.0.40.accel/dullahan-1.26.0.202604010803_139.0.40_g465474a_chromium-139.0.7258.139-windows64-23838332395.tar.zst 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;