diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 367a39bf..421cf9e3 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -1915,6 +1915,52 @@ void Session::execInternal() break; } + // Allow the renderer to handle the state change without being recreated + if (m_VideoDecoder) { + bool forceRecreation = false; + + WINDOW_STATE_CHANGE_INFO windowChangeInfo = {}; + windowChangeInfo.window = m_Window; + + if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_SIZE; + + windowChangeInfo.width = event.window.data1; + windowChangeInfo.height = event.window.data2; + } + + int newDisplayIndex = SDL_GetWindowDisplayIndex(m_Window); + if (newDisplayIndex != currentDisplayIndex) { + windowChangeInfo.stateChangeFlags |= WINDOW_STATE_CHANGE_DISPLAY; + + windowChangeInfo.displayIndex = newDisplayIndex; + + // If the refresh rates have changed, we will need to go through the full + // decoder recreation path to ensure Pacer is switched to the new display + // and that we apply any V-Sync disablement rules that may be needed for + // this display. + SDL_DisplayMode oldMode, newMode; + if (SDL_GetCurrentDisplayMode(currentDisplayIndex, &oldMode) < 0 || + SDL_GetCurrentDisplayMode(newDisplayIndex, &newMode) < 0 || + oldMode.refresh_rate != newMode.refresh_rate) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Forcing renderer recreation due to refresh rate change between displays"); + forceRecreation = true; + } + } + + if (!forceRecreation && m_VideoDecoder->notifyWindowChanged(&windowChangeInfo)) { + // Update the window display mode based on our current monitor + // NB: Avoid a useless modeset by only doing this if it changed. + if (newDisplayIndex != currentDisplayIndex) { + currentDisplayIndex = newDisplayIndex; + updateOptimalWindowDisplayMode(); + } + + break; + } + } + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Recreating renderer for window event: %d (%d %d)", event.window.event, diff --git a/app/streaming/video/decoder.h b/app/streaming/video/decoder.h index 85721b0a..24708d82 100644 --- a/app/streaming/video/decoder.h +++ b/app/streaming/video/decoder.h @@ -45,6 +45,21 @@ typedef struct _DECODER_PARAMETERS { bool testOnly; } DECODER_PARAMETERS, *PDECODER_PARAMETERS; +#define WINDOW_STATE_CHANGE_SIZE 0x01 +#define WINDOW_STATE_CHANGE_DISPLAY 0x02 + +typedef struct _WINDOW_STATE_CHANGE_INFO { + SDL_Window* window; + uint32_t stateChangeFlags; + + // Populated if WINDOW_STATE_CHANGE_SIZE is set + int width; + int height; + + // Populated if WINDOW_STATE_CHANGE_DISPLAY is set + int displayIndex; +} WINDOW_STATE_CHANGE_INFO, *PWINDOW_STATE_CHANGE_INFO; + class IVideoDecoder { public: virtual ~IVideoDecoder() {} @@ -59,4 +74,5 @@ public: virtual int submitDecodeUnit(PDECODE_UNIT du) = 0; virtual void renderFrameOnMainThread() = 0; virtual void setHdrMode(bool enabled) = 0; + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) = 0; }; diff --git a/app/streaming/video/ffmpeg-renderers/eglvid.cpp b/app/streaming/video/ffmpeg-renderers/eglvid.cpp index 605eab06..91f9f523 100644 --- a/app/streaming/video/ffmpeg-renderers/eglvid.cpp +++ b/app/streaming/video/ffmpeg-renderers/eglvid.cpp @@ -168,6 +168,12 @@ void EGLRenderer::notifyOverlayUpdated(Overlay::OverlayType type) } } +bool EGLRenderer::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) +{ + // We can transparently handle size and display changes + return !(info->stateChangeFlags & ~(WINDOW_STATE_CHANGE_SIZE | WINDOW_STATE_CHANGE_DISPLAY)); +} + bool EGLRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat) { // Pixel format support should be determined by the backend renderer diff --git a/app/streaming/video/ffmpeg-renderers/eglvid.h b/app/streaming/video/ffmpeg-renderers/eglvid.h index 6fc66473..ec7b994f 100644 --- a/app/streaming/video/ffmpeg-renderers/eglvid.h +++ b/app/streaming/video/ffmpeg-renderers/eglvid.h @@ -17,6 +17,7 @@ public: virtual void renderFrame(AVFrame* frame) override; virtual bool testRenderFrame(AVFrame* frame) override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override; + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override; virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override; virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override; diff --git a/app/streaming/video/ffmpeg-renderers/plvk.cpp b/app/streaming/video/ffmpeg-renderers/plvk.cpp index 44c5bf90..c312a85e 100644 --- a/app/streaming/video/ffmpeg-renderers/plvk.cpp +++ b/app/streaming/video/ffmpeg-renderers/plvk.cpp @@ -320,6 +320,8 @@ bool PlVkRenderer::isExtensionSupportedByPhysicalDevice(VkPhysicalDevice device, bool PlVkRenderer::initialize(PDECODER_PARAMETERS params) { + m_Window = params->window; + unsigned int instanceExtensionCount = 0; if (!SDL_Vulkan_GetInstanceExtensions(params->window, &instanceExtensionCount, nullptr)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, @@ -429,10 +431,6 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params) return false; } - int vkDrawableW, vkDrawableH; - SDL_Vulkan_GetDrawableSize(params->window, &vkDrawableW, &vkDrawableH); - pl_swapchain_resize(m_Swapchain, &vkDrawableW, &vkDrawableH); - m_Renderer = pl_renderer_create(m_Log, m_Vulkan->gpu); if (m_Renderer == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, @@ -599,6 +597,11 @@ bool PlVkRenderer::isSurfacePresentationSupportedByPhysicalDevice(VkPhysicalDevi void PlVkRenderer::waitToRender() { + // Handle the swapchain being resized + int vkDrawableW, vkDrawableH; + SDL_Vulkan_GetDrawableSize(m_Window, &vkDrawableW, &vkDrawableH); + pl_swapchain_resize(m_Swapchain, &vkDrawableW, &vkDrawableH); + // Get the next swapchain buffer for rendering. If this fails, renderFrame() // will try again. // @@ -866,6 +869,12 @@ void PlVkRenderer::notifyOverlayUpdated(Overlay::OverlayType type) SDL_AtomicUnlock(&m_OverlayLock); } +bool PlVkRenderer::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) +{ + // We can transparently handle size and display changes + return !(info->stateChangeFlags & ~(WINDOW_STATE_CHANGE_SIZE | WINDOW_STATE_CHANGE_DISPLAY)); +} + int PlVkRenderer::getRendererAttributes() { int attributes = 0; diff --git a/app/streaming/video/ffmpeg-renderers/plvk.h b/app/streaming/video/ffmpeg-renderers/plvk.h index 51ae0db5..6683da86 100644 --- a/app/streaming/video/ffmpeg-renderers/plvk.h +++ b/app/streaming/video/ffmpeg-renderers/plvk.h @@ -17,6 +17,7 @@ public: virtual void waitToRender() override; virtual void cleanupRenderContext() override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override; + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override; virtual int getRendererAttributes() override; virtual int getDecoderCapabilities() override; virtual bool needsTestFrame() override; @@ -41,6 +42,9 @@ private: // The backend renderer if we're frontend-only IFFmpegRenderer* m_Backend; + // SDL state + SDL_Window* m_Window = nullptr; + // The libplacebo rendering state pl_log m_Log = nullptr; pl_vk_inst m_PlVkInstance = nullptr; diff --git a/app/streaming/video/ffmpeg-renderers/renderer.h b/app/streaming/video/ffmpeg-renderers/renderer.h index add15639..f0540ae4 100644 --- a/app/streaming/video/ffmpeg-renderers/renderer.h +++ b/app/streaming/video/ffmpeg-renderers/renderer.h @@ -233,6 +233,11 @@ public: return true; } + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) { + // Assume the renderer cannot handle window state changes + return false; + } + // Allow renderers to expose their type enum class RendererType { Unknown, diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp index 277102ef..66c5c9e5 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.cpp +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.cpp @@ -492,3 +492,9 @@ bool SdlRenderer::testRenderFrame(AVFrame* frame) return true; } + +bool SdlRenderer::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) +{ + // We can transparently handle size and display changes + return !(info->stateChangeFlags & ~(WINDOW_STATE_CHANGE_SIZE | WINDOW_STATE_CHANGE_DISPLAY)); +} diff --git a/app/streaming/video/ffmpeg-renderers/sdlvid.h b/app/streaming/video/ffmpeg-renderers/sdlvid.h index 077ef6ef..9a450bf2 100644 --- a/app/streaming/video/ffmpeg-renderers/sdlvid.h +++ b/app/streaming/video/ffmpeg-renderers/sdlvid.h @@ -17,6 +17,7 @@ public: virtual bool isRenderThreadSupported() override; virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override; virtual bool testRenderFrame(AVFrame* frame) override; + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override; private: void renderOverlay(Overlay::OverlayType type); diff --git a/app/streaming/video/ffmpeg-renderers/vaapi.cpp b/app/streaming/video/ffmpeg-renderers/vaapi.cpp index 52d1b7c6..8827aac0 100644 --- a/app/streaming/video/ffmpeg-renderers/vaapi.cpp +++ b/app/streaming/video/ffmpeg-renderers/vaapi.cpp @@ -681,6 +681,12 @@ void VAAPIRenderer::notifyOverlayUpdated(Overlay::OverlayType type) } } +bool VAAPIRenderer::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) +{ + // We can transparently handle size and display changes + return !(info->stateChangeFlags & ~(WINDOW_STATE_CHANGE_SIZE | WINDOW_STATE_CHANGE_DISPLAY)); +} + void VAAPIRenderer::renderFrame(AVFrame* frame) { diff --git a/app/streaming/video/ffmpeg-renderers/vaapi.h b/app/streaming/video/ffmpeg-renderers/vaapi.h index 834ec7b4..ad69744e 100644 --- a/app/streaming/video/ffmpeg-renderers/vaapi.h +++ b/app/streaming/video/ffmpeg-renderers/vaapi.h @@ -66,6 +66,7 @@ public: virtual int getDecoderColorspace() override; virtual int getDecoderCapabilities() override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override; + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override; #ifdef HAVE_EGL virtual bool canExportEGL() override; diff --git a/app/streaming/video/ffmpeg.cpp b/app/streaming/video/ffmpeg.cpp index dacf791e..6ecc6c88 100644 --- a/app/streaming/video/ffmpeg.cpp +++ b/app/streaming/video/ffmpeg.cpp @@ -99,6 +99,11 @@ void FFmpegVideoDecoder::setHdrMode(bool enabled) m_FrontendRenderer->setHdrMode(enabled); } +bool FFmpegVideoDecoder::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) +{ + return m_FrontendRenderer->notifyWindowChanged(info); +} + int FFmpegVideoDecoder::getDecoderCapabilities() { bool ok; diff --git a/app/streaming/video/ffmpeg.h b/app/streaming/video/ffmpeg.h index de0bd303..eb3c1812 100644 --- a/app/streaming/video/ffmpeg.h +++ b/app/streaming/video/ffmpeg.h @@ -26,6 +26,7 @@ public: virtual int submitDecodeUnit(PDECODE_UNIT du) override; virtual void renderFrameOnMainThread() override; virtual void setHdrMode(bool enabled) override; + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) override; virtual IFFmpegRenderer* getBackendRenderer(); diff --git a/app/streaming/video/slvid.h b/app/streaming/video/slvid.h index e6adea6d..80a6ada9 100644 --- a/app/streaming/video/slvid.h +++ b/app/streaming/video/slvid.h @@ -29,6 +29,11 @@ public: return false; } + // Window state changes are not supported by SLVideo + virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) { + return false; + } + private: static void slLogCallback(void* context, ESLVideoLog logLevel, const char* message);