Add connection status overlay for DXVA2 and SDL

This commit is contained in:
Cameron Gutman 2019-03-17 15:08:21 -07:00
parent 9a6f5ba1a8
commit c75b9c9221
9 changed files with 186 additions and 95 deletions

View File

@ -39,7 +39,8 @@ CONNECTION_LISTENER_CALLBACKS Session::k_ConnCallbacks = {
nullptr, nullptr,
nullptr, nullptr,
Session::clLogMessage, Session::clLogMessage,
Session::clRumble Session::clRumble,
Session::clConnectionStatusUpdate
}; };
AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = { AUDIO_RENDERER_CALLBACKS Session::k_AudioCallbacks = {
@ -114,6 +115,21 @@ void Session::clRumble(unsigned short controllerNumber, unsigned short lowFreqMo
SDL_AtomicUnlock(&s_ActiveSession->m_InputHandlerLock); SDL_AtomicUnlock(&s_ActiveSession->m_InputHandlerLock);
} }
void Session::clConnectionStatusUpdate(int connectionStatus)
{
switch (connectionStatus)
{
case CONN_STATUS_POOR:
strcpy(s_ActiveSession->m_OverlayManager.getOverlayText(Overlay::OverlayStatusUpdate), "Poor network connection");
s_ActiveSession->m_OverlayManager.setOverlayTextUpdated(Overlay::OverlayStatusUpdate);
s_ActiveSession->m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, true);
break;
case CONN_STATUS_OKAY:
s_ActiveSession->m_OverlayManager.setOverlayState(Overlay::OverlayStatusUpdate, false);
break;
}
}
#define CALL_INITIALIZE(dec) (dec)->initialize(vds, window, videoFormat, width, height, frameRate, enableVsync, enableFramePacing) #define CALL_INITIALIZE(dec) (dec)->initialize(vds, window, videoFormat, width, height, frameRate, enableVsync, enableFramePacing)
bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds,

View File

@ -101,6 +101,9 @@ private:
static static
void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor); void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor);
static
void clConnectionStatusUpdate(int connectionStatus);
static static
int arInit(int audioConfiguration, int arInit(int audioConfiguration,
const POPUS_MULTISTREAM_CONFIGURATION opusConfig, const POPUS_MULTISTREAM_CONFIGURATION opusConfig,

View File

@ -28,6 +28,7 @@ DXVA2Renderer::DXVA2Renderer() :
m_Processor(nullptr), m_Processor(nullptr),
m_FrameIndex(0), m_FrameIndex(0),
m_DebugOverlayFont(nullptr), m_DebugOverlayFont(nullptr),
m_StatusOverlayFont(nullptr),
m_BlockingPresent(false) m_BlockingPresent(false)
{ {
RtlZeroMemory(m_DecSurfaces, sizeof(m_DecSurfaces)); RtlZeroMemory(m_DecSurfaces, sizeof(m_DecSurfaces));
@ -48,6 +49,7 @@ DXVA2Renderer::~DXVA2Renderer()
SAFE_COM_RELEASE(m_ProcService); SAFE_COM_RELEASE(m_ProcService);
SAFE_COM_RELEASE(m_Processor); SAFE_COM_RELEASE(m_Processor);
SAFE_COM_RELEASE(m_DebugOverlayFont); SAFE_COM_RELEASE(m_DebugOverlayFont);
SAFE_COM_RELEASE(m_StatusOverlayFont);
for (int i = 0; i < ARRAYSIZE(m_DecSurfaces); i++) { for (int i = 0; i < ARRAYSIZE(m_DecSurfaces); i++) {
SAFE_COM_RELEASE(m_DecSurfaces[i]); SAFE_COM_RELEASE(m_DecSurfaces[i]);
@ -707,30 +709,61 @@ IFFmpegRenderer::FramePacingConstraint DXVA2Renderer::getFramePacingConstraint()
return PACING_ANY; return PACING_ANY;
} }
void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType) void DXVA2Renderer::notifyOverlayUpdated(Overlay::OverlayType type)
{ {
HRESULT hr; HRESULT hr;
// Initialize the overlay font if it's not already created switch (type)
if (m_DebugOverlayFont == nullptr) { {
hr = D3DXCreateFontA(m_Device, case Overlay::OverlayDebug:
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug), if (m_DebugOverlayFont == nullptr) {
0, hr = D3DXCreateFontA(m_Device,
FW_HEAVY, Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug),
1, 0,
false, FW_HEAVY,
ANSI_CHARSET, 1,
OUT_DEFAULT_PRECIS, false,
DEFAULT_QUALITY, ANSI_CHARSET,
DEFAULT_PITCH | FF_DONTCARE, OUT_DEFAULT_PRECIS,
"", DEFAULT_QUALITY,
&m_DebugOverlayFont); DEFAULT_PITCH | FF_DONTCARE,
if (FAILED(hr)) { "",
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, &m_DebugOverlayFont);
"D3DXCreateFontA() failed: %x", if (FAILED(hr)) {
hr); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
m_DebugOverlayFont = nullptr; "D3DXCreateFontA() failed: %x",
hr);
m_DebugOverlayFont = nullptr;
}
} }
break;
case Overlay::OverlayStatusUpdate:
if (m_StatusOverlayFont == nullptr) {
hr = D3DXCreateFontA(m_Device,
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayNotification),
0,
FW_HEAVY,
1,
false,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"",
&m_StatusOverlayFont);
if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"D3DXCreateFontA() failed: %x",
hr);
m_StatusOverlayFont = nullptr;
}
}
break;
default:
SDL_assert(false);
break;
} }
} }
@ -929,6 +962,18 @@ void DXVA2Renderer::renderFrame(AVFrame *frame)
} }
} }
if (m_StatusOverlayFont != nullptr) {
if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayStatusUpdate)) {
SDL_Color color = Session::get()->getOverlayManager().getOverlayColor(Overlay::OverlayStatusUpdate);
m_StatusOverlayFont->DrawTextA(nullptr,
Session::get()->getOverlayManager().getOverlayText(Overlay::OverlayStatusUpdate),
-1,
&sample.DstRect,
DT_RIGHT | DT_NOCLIP,
D3DCOLOR_ARGB(color.a, color.r, color.g, color.b));
}
}
hr = m_Device->EndScene(); hr = m_Device->EndScene();
if (FAILED(hr)) { if (FAILED(hr)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,

View File

@ -71,5 +71,6 @@ private:
DXVA2_VideoDesc m_Desc; DXVA2_VideoDesc m_Desc;
REFERENCE_TIME m_FrameIndex; REFERENCE_TIME m_FrameIndex;
LPD3DXFONT m_DebugOverlayFont; LPD3DXFONT m_DebugOverlayFont;
LPD3DXFONT m_StatusOverlayFont;
bool m_BlockingPresent; bool m_BlockingPresent;
}; };

View File

@ -9,10 +9,7 @@
SdlRenderer::SdlRenderer() SdlRenderer::SdlRenderer()
: m_Renderer(nullptr), : m_Renderer(nullptr),
m_Texture(nullptr), m_Texture(nullptr)
m_DebugOverlayFont(nullptr),
m_DebugOverlaySurface(nullptr),
m_DebugOverlayTexture(nullptr)
{ {
SDL_assert(TTF_WasInit() == 0); SDL_assert(TTF_WasInit() == 0);
if (TTF_Init() != 0) { if (TTF_Init() != 0) {
@ -21,23 +18,33 @@ SdlRenderer::SdlRenderer()
TTF_GetError()); TTF_GetError());
return; return;
} }
SDL_zero(m_OverlayFonts);
SDL_zero(m_OverlaySurfaces);
SDL_zero(m_OverlayTextures);
} }
SdlRenderer::~SdlRenderer() SdlRenderer::~SdlRenderer()
{ {
if (m_DebugOverlayFont != nullptr) { for (int i = 0; i < Overlay::OverlayMax; i++) {
TTF_CloseFont(m_DebugOverlayFont); if (m_OverlayFonts[i] != nullptr) {
TTF_CloseFont(m_OverlayFonts[i]);
}
} }
TTF_Quit(); TTF_Quit();
SDL_assert(TTF_WasInit() == 0); SDL_assert(TTF_WasInit() == 0);
if (m_DebugOverlayTexture != nullptr) { for (int i = 0; i < Overlay::OverlayMax; i++) {
SDL_DestroyTexture(m_DebugOverlayTexture); if (m_OverlayTextures[i] != nullptr) {
SDL_DestroyTexture(m_OverlayTextures[i]);
}
} }
if (m_DebugOverlaySurface != nullptr) { for (int i = 0; i < Overlay::OverlayMax; i++) {
SDL_FreeSurface(m_DebugOverlaySurface); if (m_OverlaySurfaces[i] != nullptr) {
SDL_FreeSurface(m_OverlaySurfaces[i]);
}
} }
if (m_Texture != nullptr) { if (m_Texture != nullptr) {
@ -79,45 +86,41 @@ IFFmpegRenderer::FramePacingConstraint SdlRenderer::getFramePacingConstraint()
void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type) void SdlRenderer::notifyOverlayUpdated(Overlay::OverlayType type)
{ {
if (type == Overlay::OverlayDebug) { // Construct the required font to render the overlay
if (m_DebugOverlayFont == nullptr) { if (m_OverlayFonts[type] == nullptr) {
QByteArray fontPath = QDir::toNativeSeparators(Path::getDataFilePath("ModeSeven.ttf")).toUtf8(); QByteArray fontPath = QDir::toNativeSeparators(Path::getDataFilePath("ModeSeven.ttf")).toUtf8();
if (fontPath.isEmpty()) { if (fontPath.isEmpty()) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Unable to locate SDL overlay font"); "Unable to locate SDL overlay font");
return; return;
}
m_DebugOverlayFont = TTF_OpenFont(fontPath.data(),
Session::get()->getOverlayManager().getOverlayFontSize(Overlay::OverlayDebug));
if (m_DebugOverlayFont == nullptr) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"TTF_OpenFont() failed: %s",
TTF_GetError());
// Can't proceed without a font
return;
}
} }
SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_DebugOverlaySurface); m_OverlayFonts[type] = TTF_OpenFont(fontPath.data(),
Session::get()->getOverlayManager().getOverlayFontSize(type));
if (m_OverlayFonts[type] == nullptr) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"TTF_OpenFont() failed: %s",
TTF_GetError());
// Free the old surface // Can't proceed without a font
if (oldSurface != nullptr && SDL_AtomicCASPtr((void**)&m_DebugOverlaySurface, oldSurface, nullptr)) { return;
SDL_FreeSurface(oldSurface);
}
if (Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// The _Wrapped variant is required for line breaks to work
SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_DebugOverlayFont,
Session::get()->getOverlayManager().getOverlayText(type),
Session::get()->getOverlayManager().getOverlayColor(type),
1000);
SDL_AtomicSetPtr((void**)&m_DebugOverlaySurface, surface);
} }
} }
else {
SDL_assert(false); SDL_Surface* oldSurface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]);
// Free the old surface
if (oldSurface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], oldSurface, nullptr)) {
SDL_FreeSurface(oldSurface);
}
if (Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// The _Wrapped variant is required for line breaks to work
SDL_Surface* surface = TTF_RenderText_Blended_Wrapped(m_OverlayFonts[type],
Session::get()->getOverlayManager().getOverlayText(type),
Session::get()->getOverlayManager().getOverlayColor(type),
1000);
SDL_AtomicSetPtr((void**)&m_OverlaySurfaces[type], surface);
} }
} }
@ -180,6 +183,45 @@ bool SdlRenderer::initialize(SDL_Window* window,
return true; return true;
} }
void SdlRenderer::renderOverlay(Overlay::OverlayType type)
{
if (Session::get()->getOverlayManager().isOverlayEnabled(type)) {
// If a new surface has been created for updated overlay data, convert it into a texture.
// NB: We have to do this conversion at render-time because we can only interact
// with the renderer on a single thread.
SDL_Surface* surface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_OverlaySurfaces[type]);
if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_OverlaySurfaces[type], surface, nullptr)) {
if (m_OverlayTextures[type] != nullptr) {
SDL_DestroyTexture(m_OverlayTextures[type]);
}
if (type == Overlay::OverlayStatusUpdate) {
// Top right
int unused;
SDL_RenderGetLogicalSize(m_Renderer, &m_OverlayRects[type].x, &unused);
m_OverlayRects[type].x -= surface->w;
m_OverlayRects[type].y = 0;
}
else if (type == Overlay::OverlayDebug) {
// Top left
m_OverlayRects[type].x = 0;
m_OverlayRects[type].y = 0;
}
m_OverlayRects[type].w = surface->w;
m_OverlayRects[type].h = surface->h;
m_OverlayTextures[type] = SDL_CreateTextureFromSurface(m_Renderer, surface);
SDL_FreeSurface(surface);
}
// If we have an overlay texture, render it too
if (m_OverlayTextures[type] != nullptr) {
SDL_RenderCopy(m_Renderer, m_OverlayTextures[type], nullptr, &m_OverlayRects[type]);
}
}
}
void SdlRenderer::renderFrame(AVFrame* frame) void SdlRenderer::renderFrame(AVFrame* frame)
{ {
SDL_UpdateYUVTexture(m_Texture, nullptr, SDL_UpdateYUVTexture(m_Texture, nullptr,
@ -196,29 +238,8 @@ void SdlRenderer::renderFrame(AVFrame* frame)
SDL_RenderCopy(m_Renderer, m_Texture, nullptr, nullptr); SDL_RenderCopy(m_Renderer, m_Texture, nullptr, nullptr);
// Draw the overlays // Draw the overlays
if (Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)) { for (int i = 0; i < Overlay::OverlayMax; i++) {
// If a new surface has been created for updated overlay data, convert it into a texture. renderOverlay((Overlay::OverlayType)i);
// NB: We have to do this conversion at render-time because we can only interact
// with the renderer on a single thread.
SDL_Surface* surface = (SDL_Surface*)SDL_AtomicGetPtr((void**)&m_DebugOverlaySurface);
if (surface != nullptr && SDL_AtomicCASPtr((void**)&m_DebugOverlaySurface, surface, nullptr)) {
if (m_DebugOverlayTexture != nullptr) {
SDL_DestroyTexture(m_DebugOverlayTexture);
}
m_DebugOverlayRect.x = 0;
m_DebugOverlayRect.y = 0;
m_DebugOverlayRect.w = surface->w;
m_DebugOverlayRect.h = surface->h;
m_DebugOverlayTexture = SDL_CreateTextureFromSurface(m_Renderer, surface);
SDL_FreeSurface(surface);
}
// If we have a debug overlay texture, render it too
if (m_DebugOverlayTexture != nullptr) {
SDL_RenderCopy(m_Renderer, m_DebugOverlayTexture, nullptr, &m_DebugOverlayRect);
}
} }
SDL_RenderPresent(m_Renderer); SDL_RenderPresent(m_Renderer);

View File

@ -22,11 +22,13 @@ public:
virtual void notifyOverlayUpdated(Overlay::OverlayType) override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
private: private:
void renderOverlay(Overlay::OverlayType type);
SDL_Renderer* m_Renderer; SDL_Renderer* m_Renderer;
SDL_Texture* m_Texture; SDL_Texture* m_Texture;
TTF_Font* m_DebugOverlayFont; TTF_Font* m_OverlayFonts[Overlay::OverlayMax];
SDL_Surface* m_DebugOverlaySurface; SDL_Surface* m_OverlaySurfaces[Overlay::OverlayMax];
SDL_Texture* m_DebugOverlayTexture; SDL_Texture* m_OverlayTextures[Overlay::OverlayMax];
SDL_Rect m_DebugOverlayRect; SDL_Rect m_OverlayRects[Overlay::OverlayMax];
}; };

View File

@ -9,6 +9,9 @@ OverlayManager::OverlayManager() :
m_Overlays[OverlayType::OverlayDebug].color = {0xD0, 0xD0, 0x00, 0xFF}; m_Overlays[OverlayType::OverlayDebug].color = {0xD0, 0xD0, 0x00, 0xFF};
m_Overlays[OverlayType::OverlayDebug].fontSize = 20; m_Overlays[OverlayType::OverlayDebug].fontSize = 20;
m_Overlays[OverlayType::OverlayStatusUpdate].color = {0xCC, 0x00, 0x00, 0xFF};
m_Overlays[OverlayType::OverlayStatusUpdate].fontSize = 36;
} }
bool OverlayManager::isOverlayEnabled(OverlayType type) bool OverlayManager::isOverlayEnabled(OverlayType type)

View File

@ -8,8 +8,8 @@ namespace Overlay {
enum OverlayType { enum OverlayType {
OverlayDebug, OverlayDebug,
OverlayMinorNotification, OverlayStatusUpdate,
OverlayMajorNotification, OverlayNotification,
OverlayMax OverlayMax
}; };

@ -1 +1 @@
Subproject commit 6eb17a8e73349f24309d86f81812dc8869bd54c1 Subproject commit c9745855fd3d0eb2f5c4c4546a30c849b7453a7c