mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-15 22:02:29 +00:00
Add overlay support to VDPAU renderer
This commit is contained in:
parent
94b46a2173
commit
027c8dcd41
2 changed files with 186 additions and 0 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
#include <streaming/session.h>
|
||||||
#include "vdpau.h"
|
#include "vdpau.h"
|
||||||
#include <streaming/streamutils.h>
|
#include <streaming/streamutils.h>
|
||||||
|
|
||||||
|
@ -27,9 +28,11 @@ VDPAURenderer::VDPAURenderer()
|
||||||
m_PresentationQueueTarget(0),
|
m_PresentationQueueTarget(0),
|
||||||
m_PresentationQueue(0),
|
m_PresentationQueue(0),
|
||||||
m_VideoMixer(0),
|
m_VideoMixer(0),
|
||||||
|
m_OverlayMutex(nullptr),
|
||||||
m_NextSurfaceIndex(0)
|
m_NextSurfaceIndex(0)
|
||||||
{
|
{
|
||||||
SDL_zero(m_OutputSurface);
|
SDL_zero(m_OutputSurface);
|
||||||
|
SDL_zero(m_OverlaySurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
VDPAURenderer::~VDPAURenderer()
|
VDPAURenderer::~VDPAURenderer()
|
||||||
|
@ -52,6 +55,16 @@ VDPAURenderer::~VDPAURenderer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < Overlay::OverlayMax; i++) {
|
||||||
|
if (m_OverlaySurface[i] != 0) {
|
||||||
|
m_VdpBitmapSurfaceDestroy(m_OverlaySurface[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_OverlayMutex != nullptr) {
|
||||||
|
SDL_DestroyMutex(m_OverlayMutex);
|
||||||
|
}
|
||||||
|
|
||||||
// This must be done last as it frees VDPAU context required to call
|
// This must be done last as it frees VDPAU context required to call
|
||||||
// the functions above.
|
// the functions above.
|
||||||
if (m_HwContext != nullptr) {
|
if (m_HwContext != nullptr) {
|
||||||
|
@ -136,6 +149,10 @@ bool VDPAURenderer::initialize(PDECODER_PARAMETERS params)
|
||||||
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, &m_VdpOutputSurfaceCreate);
|
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, &m_VdpOutputSurfaceCreate);
|
||||||
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY, &m_VdpOutputSurfaceDestroy);
|
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY, &m_VdpOutputSurfaceDestroy);
|
||||||
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_CAPABILITIES, &m_VdpOutputSurfaceQueryCapabilities);
|
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_QUERY_CAPABILITIES, &m_VdpOutputSurfaceQueryCapabilities);
|
||||||
|
GET_PROC_ADDRESS(VDP_FUNC_ID_BITMAP_SURFACE_CREATE, &m_VdpBitmapSurfaceCreate);
|
||||||
|
GET_PROC_ADDRESS(VDP_FUNC_ID_BITMAP_SURFACE_DESTROY, &m_VdpBitmapSurfaceDestroy);
|
||||||
|
GET_PROC_ADDRESS(VDP_FUNC_ID_BITMAP_SURFACE_PUT_BITS_NATIVE, &m_VdpBitmapSurfacePutBitsNative);
|
||||||
|
GET_PROC_ADDRESS(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_BITMAP_SURFACE, &m_VdpOutputSurfaceRenderBitmapSurface);
|
||||||
GET_PROC_ADDRESS(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, &m_VdpVideoSurfaceGetParameters);
|
GET_PROC_ADDRESS(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS, &m_VdpVideoSurfaceGetParameters);
|
||||||
GET_PROC_ADDRESS(VDP_FUNC_ID_GET_INFORMATION_STRING, &m_VdpGetInformationString);
|
GET_PROC_ADDRESS(VDP_FUNC_ID_GET_INFORMATION_STRING, &m_VdpGetInformationString);
|
||||||
|
|
||||||
|
@ -263,6 +280,24 @@ bool VDPAURenderer::initialize(PDECODER_PARAMETERS params)
|
||||||
VdpColor color = {0.0, 0.0, 0.0, 1.0};
|
VdpColor color = {0.0, 0.0, 0.0, 1.0};
|
||||||
m_VdpPresentationQueueSetBackgroundColor(m_PresentationQueue, &color);
|
m_VdpPresentationQueueSetBackgroundColor(m_PresentationQueue, &color);
|
||||||
|
|
||||||
|
// Populate blend state for overlays
|
||||||
|
m_OverlayBlendState.struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION;
|
||||||
|
m_OverlayBlendState.blend_factor_destination_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||||
|
m_OverlayBlendState.blend_factor_destination_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||||
|
m_OverlayBlendState.blend_factor_source_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA;
|
||||||
|
m_OverlayBlendState.blend_factor_source_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA;
|
||||||
|
m_OverlayBlendState.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
|
||||||
|
m_OverlayBlendState.blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
|
||||||
|
m_OverlayBlendState.blend_constant = {};
|
||||||
|
|
||||||
|
// Allocate mutex to synchronize overlay updates and rendering
|
||||||
|
m_OverlayMutex = SDL_CreateMutex();
|
||||||
|
if (m_OverlayMutex == nullptr) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Failed to create overlay mutex");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,6 +319,98 @@ bool VDPAURenderer::prepareDecoderContext(AVCodecContext* context, AVDictionary*
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VDPAURenderer::notifyOverlayUpdated(Overlay::OverlayType type)
|
||||||
|
{
|
||||||
|
VdpStatus status;
|
||||||
|
|
||||||
|
SDL_Surface* newSurface = Session::get()->getOverlayManager().getUpdatedOverlaySurface(type);
|
||||||
|
if (newSurface == nullptr && Session::get()->getOverlayManager().isOverlayEnabled(type)) {
|
||||||
|
// There's no updated surface and the overlay is enabled, so just leave the old surface alone.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the old surface
|
||||||
|
// NB: The mutex ensures the surface is not currently being read for rendering.
|
||||||
|
// NB 2: It is safe to unlock here because this thread is the only surface producer.
|
||||||
|
SDL_LockMutex(m_OverlayMutex);
|
||||||
|
VdpBitmapSurface oldBitmapSurface = m_OverlaySurface[type];
|
||||||
|
m_OverlaySurface[type] = 0;
|
||||||
|
SDL_UnlockMutex(m_OverlayMutex);
|
||||||
|
|
||||||
|
if (oldBitmapSurface != 0) {
|
||||||
|
status = m_VdpBitmapSurfaceDestroy(oldBitmapSurface);
|
||||||
|
if (status != VDP_STATUS_OK) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"VdpBitmapSurfaceDestroy() failed: %s",
|
||||||
|
m_VdpGetErrorString(status));
|
||||||
|
|
||||||
|
// This should never happen.
|
||||||
|
SDL_assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
|
||||||
|
SDL_FreeSurface(newSurface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newSurface != nullptr) {
|
||||||
|
SDL_assert(!SDL_MUSTLOCK(newSurface));
|
||||||
|
|
||||||
|
VdpBitmapSurface newBitmapSurface = 0;
|
||||||
|
status = m_VdpBitmapSurfaceCreate(m_Device,
|
||||||
|
VDP_RGBA_FORMAT_B8G8R8A8,
|
||||||
|
newSurface->w,
|
||||||
|
newSurface->h,
|
||||||
|
VDP_TRUE, // Is this correct?
|
||||||
|
&newBitmapSurface);
|
||||||
|
if (status != VDP_STATUS_OK) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"VdpBitmapSurfaceCreate() failed: %s",
|
||||||
|
m_VdpGetErrorString(status));
|
||||||
|
SDL_FreeSurface(newSurface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = m_VdpBitmapSurfacePutBitsNative(newBitmapSurface,
|
||||||
|
&newSurface->pixels,
|
||||||
|
(const uint32_t*)&newSurface->pitch,
|
||||||
|
nullptr);
|
||||||
|
if (status != VDP_STATUS_OK) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"VdpBitmapSurfacePutBitsNative() failed: %s",
|
||||||
|
m_VdpGetErrorString(status));
|
||||||
|
m_VdpBitmapSurfaceDestroy(newBitmapSurface);
|
||||||
|
SDL_FreeSurface(newSurface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surface data is no longer needed
|
||||||
|
SDL_FreeSurface(newSurface);
|
||||||
|
|
||||||
|
VdpRect overlayRect;
|
||||||
|
|
||||||
|
if (type == Overlay::OverlayStatusUpdate) {
|
||||||
|
// Bottom Left
|
||||||
|
overlayRect.x0 = 0;
|
||||||
|
overlayRect.y0 = m_DisplayHeight - newSurface->h;
|
||||||
|
}
|
||||||
|
else if (type == Overlay::OverlayDebug) {
|
||||||
|
// Top left
|
||||||
|
overlayRect.x0 = 0;
|
||||||
|
overlayRect.y0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
overlayRect.x1 = overlayRect.x0 + newSurface->w;
|
||||||
|
overlayRect.y1 = overlayRect.y0 + newSurface->h;
|
||||||
|
|
||||||
|
SDL_LockMutex(m_OverlayMutex);
|
||||||
|
m_OverlaySurface[type] = newBitmapSurface;
|
||||||
|
m_OverlayRect[type] = overlayRect;
|
||||||
|
SDL_UnlockMutex(m_OverlayMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VDPAURenderer::needsTestFrame()
|
bool VDPAURenderer::needsTestFrame()
|
||||||
{
|
{
|
||||||
// We need a test frame to see if this VDPAU driver
|
// We need a test frame to see if this VDPAU driver
|
||||||
|
@ -300,6 +427,44 @@ int VDPAURenderer::getDecoderColorspace()
|
||||||
return COLORSPACE_REC_601;
|
return COLORSPACE_REC_601;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VDPAURenderer::renderOverlay(VdpOutputSurface destination, Overlay::OverlayType type)
|
||||||
|
{
|
||||||
|
VdpStatus status;
|
||||||
|
|
||||||
|
// Don't even bother locking the mutex if the overlay is disabled
|
||||||
|
if (!Session::get()->getOverlayManager().isOverlayEnabled(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SDL_TryLockMutex(m_OverlayMutex) != 0) {
|
||||||
|
// If the overlay is currently being updated, skip rendering it this frame.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there's a surface to render
|
||||||
|
if (m_OverlaySurface[type] == 0) {
|
||||||
|
SDL_UnlockMutex(m_OverlayMutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = m_VdpOutputSurfaceRenderBitmapSurface(destination,
|
||||||
|
&m_OverlayRect[type],
|
||||||
|
m_OverlaySurface[type],
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
&m_OverlayBlendState,
|
||||||
|
0);
|
||||||
|
|
||||||
|
SDL_UnlockMutex(m_OverlayMutex);
|
||||||
|
|
||||||
|
if (status != VDP_STATUS_OK) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"VdpOutputSurfaceRenderBitmapSurface() failed: %s",
|
||||||
|
m_VdpGetErrorString(status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VDPAURenderer::renderFrame(AVFrame* frame)
|
void VDPAURenderer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
if (frame == nullptr) {
|
if (frame == nullptr) {
|
||||||
|
@ -400,6 +565,11 @@ void VDPAURenderer::renderFrame(AVFrame* frame)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render overlays into the output surface before display
|
||||||
|
for (int i = 0; i < Overlay::OverlayMax; i++) {
|
||||||
|
renderOverlay(chosenSurface, (Overlay::OverlayType)i);
|
||||||
|
}
|
||||||
|
|
||||||
// Queue the frame for display immediately
|
// Queue the frame for display immediately
|
||||||
status = m_VdpPresentationQueueDisplay(m_PresentationQueue, chosenSurface, 0, 0, 0);
|
status = m_VdpPresentationQueueDisplay(m_PresentationQueue, chosenSurface, 0, 0, 0);
|
||||||
if (status != VDP_STATUS_OK) {
|
if (status != VDP_STATUS_OK) {
|
||||||
|
|
|
@ -15,11 +15,14 @@ public:
|
||||||
virtual ~VDPAURenderer() override;
|
virtual ~VDPAURenderer() override;
|
||||||
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
||||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
||||||
|
virtual void notifyOverlayUpdated(Overlay::OverlayType type) override;
|
||||||
virtual void renderFrame(AVFrame* frame) override;
|
virtual void renderFrame(AVFrame* frame) override;
|
||||||
virtual bool needsTestFrame() override;
|
virtual bool needsTestFrame() override;
|
||||||
virtual int getDecoderColorspace() override;
|
virtual int getDecoderColorspace() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void renderOverlay(VdpOutputSurface destination, Overlay::OverlayType type);
|
||||||
|
|
||||||
uint32_t m_VideoWidth, m_VideoHeight;
|
uint32_t m_VideoWidth, m_VideoHeight;
|
||||||
uint32_t m_DisplayWidth, m_DisplayHeight;
|
uint32_t m_DisplayWidth, m_DisplayHeight;
|
||||||
AVBufferRef* m_HwContext;
|
AVBufferRef* m_HwContext;
|
||||||
|
@ -29,6 +32,15 @@ private:
|
||||||
VdpRGBAFormat m_OutputSurfaceFormat;
|
VdpRGBAFormat m_OutputSurfaceFormat;
|
||||||
VdpDevice m_Device;
|
VdpDevice m_Device;
|
||||||
|
|
||||||
|
// We just have a single mutex to protect all overlay slots.
|
||||||
|
// This is fine because the majority of time spent in the mutex
|
||||||
|
// is by the render thread, which cannot contend with itself
|
||||||
|
// because overlays are rendered sequentially.
|
||||||
|
SDL_mutex* m_OverlayMutex;
|
||||||
|
VdpBitmapSurface m_OverlaySurface[Overlay::OverlayMax];
|
||||||
|
VdpRect m_OverlayRect[Overlay::OverlayMax];
|
||||||
|
VdpOutputSurfaceRenderBlendState m_OverlayBlendState;
|
||||||
|
|
||||||
#define OUTPUT_SURFACE_COUNT 3
|
#define OUTPUT_SURFACE_COUNT 3
|
||||||
VdpOutputSurface m_OutputSurface[OUTPUT_SURFACE_COUNT];
|
VdpOutputSurface m_OutputSurface[OUTPUT_SURFACE_COUNT];
|
||||||
int m_NextSurfaceIndex;
|
int m_NextSurfaceIndex;
|
||||||
|
@ -50,6 +62,10 @@ private:
|
||||||
VdpOutputSurfaceCreate* m_VdpOutputSurfaceCreate;
|
VdpOutputSurfaceCreate* m_VdpOutputSurfaceCreate;
|
||||||
VdpOutputSurfaceDestroy* m_VdpOutputSurfaceDestroy;
|
VdpOutputSurfaceDestroy* m_VdpOutputSurfaceDestroy;
|
||||||
VdpOutputSurfaceQueryCapabilities* m_VdpOutputSurfaceQueryCapabilities;
|
VdpOutputSurfaceQueryCapabilities* m_VdpOutputSurfaceQueryCapabilities;
|
||||||
|
VdpBitmapSurfaceCreate* m_VdpBitmapSurfaceCreate;
|
||||||
|
VdpBitmapSurfaceDestroy* m_VdpBitmapSurfaceDestroy;
|
||||||
|
VdpBitmapSurfacePutBitsNative* m_VdpBitmapSurfacePutBitsNative;
|
||||||
|
VdpOutputSurfaceRenderBitmapSurface* m_VdpOutputSurfaceRenderBitmapSurface;
|
||||||
VdpVideoSurfaceGetParameters* m_VdpVideoSurfaceGetParameters;
|
VdpVideoSurfaceGetParameters* m_VdpVideoSurfaceGetParameters;
|
||||||
VdpGetInformationString* m_VdpGetInformationString;
|
VdpGetInformationString* m_VdpGetInformationString;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue