Enable transparent resizing and display changes for supported renderers

This commit is contained in:
Cameron Gutman 2023-12-17 21:15:27 -06:00
parent 481f23b6e9
commit 2a05b890d8
14 changed files with 116 additions and 4 deletions

View File

@ -1915,6 +1915,52 @@ void Session::execInternal()
break; 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, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Recreating renderer for window event: %d (%d %d)", "Recreating renderer for window event: %d (%d %d)",
event.window.event, event.window.event,

View File

@ -45,6 +45,21 @@ typedef struct _DECODER_PARAMETERS {
bool testOnly; bool testOnly;
} DECODER_PARAMETERS, *PDECODER_PARAMETERS; } 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 { class IVideoDecoder {
public: public:
virtual ~IVideoDecoder() {} virtual ~IVideoDecoder() {}
@ -59,4 +74,5 @@ public:
virtual int submitDecodeUnit(PDECODE_UNIT du) = 0; virtual int submitDecodeUnit(PDECODE_UNIT du) = 0;
virtual void renderFrameOnMainThread() = 0; virtual void renderFrameOnMainThread() = 0;
virtual void setHdrMode(bool enabled) = 0; virtual void setHdrMode(bool enabled) = 0;
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) = 0;
}; };

View File

@ -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) bool EGLRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFormat)
{ {
// Pixel format support should be determined by the backend renderer // Pixel format support should be determined by the backend renderer

View File

@ -17,6 +17,7 @@ public:
virtual void renderFrame(AVFrame* frame) override; virtual void renderFrame(AVFrame* frame) override;
virtual bool testRenderFrame(AVFrame* frame) override; virtual bool testRenderFrame(AVFrame* frame) override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) 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 bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override; virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override;

View File

@ -320,6 +320,8 @@ bool PlVkRenderer::isExtensionSupportedByPhysicalDevice(VkPhysicalDevice device,
bool PlVkRenderer::initialize(PDECODER_PARAMETERS params) bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
{ {
m_Window = params->window;
unsigned int instanceExtensionCount = 0; unsigned int instanceExtensionCount = 0;
if (!SDL_Vulkan_GetInstanceExtensions(params->window, &instanceExtensionCount, nullptr)) { if (!SDL_Vulkan_GetInstanceExtensions(params->window, &instanceExtensionCount, nullptr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -429,10 +431,6 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
return false; 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); m_Renderer = pl_renderer_create(m_Log, m_Vulkan->gpu);
if (m_Renderer == nullptr) { if (m_Renderer == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@ -599,6 +597,11 @@ bool PlVkRenderer::isSurfacePresentationSupportedByPhysicalDevice(VkPhysicalDevi
void PlVkRenderer::waitToRender() 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() // Get the next swapchain buffer for rendering. If this fails, renderFrame()
// will try again. // will try again.
// //
@ -866,6 +869,12 @@ void PlVkRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
SDL_AtomicUnlock(&m_OverlayLock); 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 PlVkRenderer::getRendererAttributes()
{ {
int attributes = 0; int attributes = 0;

View File

@ -17,6 +17,7 @@ public:
virtual void waitToRender() override; virtual void waitToRender() override;
virtual void cleanupRenderContext() override; virtual void cleanupRenderContext() override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override;
virtual int getRendererAttributes() override; virtual int getRendererAttributes() override;
virtual int getDecoderCapabilities() override; virtual int getDecoderCapabilities() override;
virtual bool needsTestFrame() override; virtual bool needsTestFrame() override;
@ -41,6 +42,9 @@ private:
// The backend renderer if we're frontend-only // The backend renderer if we're frontend-only
IFFmpegRenderer* m_Backend; IFFmpegRenderer* m_Backend;
// SDL state
SDL_Window* m_Window = nullptr;
// The libplacebo rendering state // The libplacebo rendering state
pl_log m_Log = nullptr; pl_log m_Log = nullptr;
pl_vk_inst m_PlVkInstance = nullptr; pl_vk_inst m_PlVkInstance = nullptr;

View File

@ -233,6 +233,11 @@ public:
return true; 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 // Allow renderers to expose their type
enum class RendererType { enum class RendererType {
Unknown, Unknown,

View File

@ -492,3 +492,9 @@ bool SdlRenderer::testRenderFrame(AVFrame* frame)
return true; 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));
}

View File

@ -17,6 +17,7 @@ public:
virtual bool isRenderThreadSupported() override; virtual bool isRenderThreadSupported() override;
virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override; virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
virtual bool testRenderFrame(AVFrame* frame) override; virtual bool testRenderFrame(AVFrame* frame) override;
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override;
private: private:
void renderOverlay(Overlay::OverlayType type); void renderOverlay(Overlay::OverlayType type);

View File

@ -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 void
VAAPIRenderer::renderFrame(AVFrame* frame) VAAPIRenderer::renderFrame(AVFrame* frame)
{ {

View File

@ -66,6 +66,7 @@ public:
virtual int getDecoderColorspace() override; virtual int getDecoderColorspace() override;
virtual int getDecoderCapabilities() override; virtual int getDecoderCapabilities() override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override;
#ifdef HAVE_EGL #ifdef HAVE_EGL
virtual bool canExportEGL() override; virtual bool canExportEGL() override;

View File

@ -99,6 +99,11 @@ void FFmpegVideoDecoder::setHdrMode(bool enabled)
m_FrontendRenderer->setHdrMode(enabled); m_FrontendRenderer->setHdrMode(enabled);
} }
bool FFmpegVideoDecoder::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info)
{
return m_FrontendRenderer->notifyWindowChanged(info);
}
int FFmpegVideoDecoder::getDecoderCapabilities() int FFmpegVideoDecoder::getDecoderCapabilities()
{ {
bool ok; bool ok;

View File

@ -26,6 +26,7 @@ public:
virtual int submitDecodeUnit(PDECODE_UNIT du) override; virtual int submitDecodeUnit(PDECODE_UNIT du) override;
virtual void renderFrameOnMainThread() override; virtual void renderFrameOnMainThread() override;
virtual void setHdrMode(bool enabled) override; virtual void setHdrMode(bool enabled) override;
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info) override;
virtual IFFmpegRenderer* getBackendRenderer(); virtual IFFmpegRenderer* getBackendRenderer();

View File

@ -29,6 +29,11 @@ public:
return false; return false;
} }
// Window state changes are not supported by SLVideo
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) {
return false;
}
private: private:
static void slLogCallback(void* context, ESLVideoLog logLevel, const char* message); static void slLogCallback(void* context, ESLVideoLog logLevel, const char* message);