4870: Don't render tees or nameplates offscreen r=def- a=sjrc6

It seems difficult to measure the fps from the ingame graph or fps counter so these numbers are from NVidia fps overlay

FPS on full multeasymap server, fullscreen, vulkan renderer
/showall 0 - 4500
/showall 1 - 2100
/showall 1 (after preventing offscreen render) - 4200

## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [x] Tested in combination with possibly related configuration options
- [ ] Written a unit test if it works standalone, system.c especially
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Tater <Mr.Potatooh@gmail.com>
This commit is contained in:
bors[bot] 2022-03-24 14:44:26 +00:00 committed by GitHub
commit 309283d551
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 197 additions and 100 deletions

View file

@ -344,6 +344,7 @@ void CGhost::OnRender()
Player.m_AttackTick += Client()->GameTick(g_Config.m_ClDummy) - GhostTick; Player.m_AttackTick += Client()->GameTick(g_Config.m_ClDummy) - GhostTick;
m_pClient->m_Players.RenderHook(&Prev, &Player, &Ghost.m_RenderInfo, -2, IntraTick); m_pClient->m_Players.RenderHook(&Prev, &Player, &Ghost.m_RenderInfo, -2, IntraTick);
m_pClient->m_Players.RenderHookCollLine(&Prev, &Player, -2, IntraTick);
m_pClient->m_Players.RenderPlayer(&Prev, &Player, &Ghost.m_RenderInfo, -2, IntraTick); m_pClient->m_Players.RenderPlayer(&Prev, &Player, &Ghost.m_RenderInfo, -2, IntraTick);
} }
} }

View file

@ -305,6 +305,18 @@ void CNamePlates::OnRender()
if(!g_Config.m_ClNameplates && ShowDirection == 0) if(!g_Config.m_ClNameplates && ShowDirection == 0)
return; return;
// get screen edges to avoid rendering offscreen
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
// expand the edges to prevent popping in/out onscreen
//
// it is assumed that the nameplate and all its components fit into a 800x800 box placed directly above the tee
// this may need to be changed or calculated differently in the future
ScreenX0 -= 400;
ScreenX1 += 400;
//ScreenY0 -= 0;
ScreenY1 += 800;
for(int i = 0; i < MAX_CLIENTS; i++) for(int i = 0; i < MAX_CLIENTS; i++)
{ {
const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paPlayerInfos[i]; const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paPlayerInfos[i];
@ -313,6 +325,13 @@ void CNamePlates::OnRender()
continue; continue;
} }
// don't render offscreen
vec2 *pRenderPos = &m_pClient->m_aClients[i].m_RenderPos;
if(pRenderPos->x < ScreenX0 || pRenderPos->x > ScreenX1 || pRenderPos->y < ScreenY0 || pRenderPos->y > ScreenY1)
{
continue;
}
if(m_pClient->m_aClients[i].m_SpecCharPresent) if(m_pClient->m_aClients[i].m_SpecCharPresent)
{ {
RenderNameplatePos(m_pClient->m_aClients[i].m_SpecChar, pInfo, 0.4f, true); RenderNameplatePos(m_pClient->m_aClients[i].m_SpecChar, pInfo, 0.4f, true);

View file

@ -76,7 +76,156 @@ inline float AngularApproach(float Src, float Dst, float Amount)
return Dst; return Dst;
return n; return n;
} }
void CPlayers::RenderHookCollLine(
const CNetObj_Character *pPrevChar,
const CNetObj_Character *pPlayerChar,
int ClientID,
float Intra)
{
CNetObj_Character Prev;
CNetObj_Character Player;
Prev = *pPrevChar;
Player = *pPlayerChar;
bool Local = m_pClient->m_Snap.m_LocalClientID == ClientID;
bool OtherTeam = m_pClient->IsOtherTeam(ClientID);
float Alpha = (OtherTeam || ClientID < 0) ? g_Config.m_ClShowOthersAlpha / 100.0f : 1.0f;
float IntraTick = Intra;
if(ClientID >= 0)
IntraTick = m_pClient->m_aClients[ClientID].m_IsPredicted ? Client()->PredIntraGameTick(g_Config.m_ClDummy) : Client()->IntraGameTick(g_Config.m_ClDummy);
float Angle;
if(Local && Client()->State() != IClient::STATE_DEMOPLAYBACK)
{
// just use the direct input if it's the local player we are rendering
Angle = angle(m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy]);
}
else
{
float AngleIntraTick = IntraTick;
// using unpredicted angle when rendering other players in-game
if(ClientID >= 0)
AngleIntraTick = Client()->IntraGameTick(g_Config.m_ClDummy);
// If the player moves their weapon through top, then change
// the end angle by 2*Pi, so that the mix function will use the
// short path and not the long one.
if(Player.m_Angle > (256.0f * pi) && Prev.m_Angle < 0)
Player.m_Angle -= 256.0f * 2 * pi;
else if(Player.m_Angle < 0 && Prev.m_Angle > (256.0f * pi))
Player.m_Angle += 256.0f * 2 * pi;
Angle = mix((float)Prev.m_Angle, (float)Player.m_Angle, AngleIntraTick) / 256.0f;
}
vec2 Direction = direction(Angle);
vec2 Position;
if(in_range(ClientID, MAX_CLIENTS - 1))
Position = m_pClient->m_aClients[ClientID].m_RenderPos;
else
Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick);
// draw hook collision line
{
bool AlwaysRenderHookColl = GameClient()->m_GameInfo.m_AllowHookColl && (Local ? g_Config.m_ClShowHookCollOwn : g_Config.m_ClShowHookCollOther) == 2;
bool RenderHookCollPlayer = ClientID >= 0 && Player.m_PlayerFlags & PLAYERFLAG_AIM && (Local ? g_Config.m_ClShowHookCollOwn : g_Config.m_ClShowHookCollOther) > 0;
bool RenderHookCollVideo = true;
#if defined(CONF_VIDEORECORDER)
RenderHookCollVideo = !IVideo::Current() || g_Config.m_ClVideoShowHookCollOther || Local;
#endif
if((AlwaysRenderHookColl || RenderHookCollPlayer) && RenderHookCollVideo)
{
vec2 ExDirection = Direction;
if(Local && Client()->State() != IClient::STATE_DEMOPLAYBACK)
ExDirection = normalize(vec2((int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].x, (int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].y));
Graphics()->TextureClear();
vec2 InitPos = Position;
vec2 FinishPos = InitPos + ExDirection * (m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength - 42.0f);
if(g_Config.m_ClHookCollSize > 0)
Graphics()->QuadsBegin();
else
Graphics()->LinesBegin();
ColorRGBA HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorNoColl));
float PhysSize = 28.0f;
vec2 OldPos = InitPos + ExDirection * PhysSize * 1.5f;
vec2 NewPos = OldPos;
bool DoBreak = false;
int Hit = 0;
do
{
OldPos = NewPos;
NewPos = OldPos + ExDirection * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookFireSpeed;
if(distance(InitPos, NewPos) > m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength)
{
NewPos = InitPos + normalize(NewPos - InitPos) * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength;
DoBreak = true;
}
int TeleNr = 0;
Hit = Collision()->IntersectLineTeleHook(OldPos, NewPos, &FinishPos, 0x0, &TeleNr);
if(!DoBreak && Hit)
{
if(Hit != TILE_NOHOOK)
{
HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorHookableColl));
}
}
if(m_pClient->IntersectCharacter(OldPos, FinishPos, FinishPos, ClientID) != -1)
{
HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorTeeColl));
break;
}
if(Hit)
break;
NewPos.x = round_to_int(NewPos.x);
NewPos.y = round_to_int(NewPos.y);
if(OldPos == NewPos)
break;
ExDirection.x = round_to_int(ExDirection.x * 256.0f) / 256.0f;
ExDirection.y = round_to_int(ExDirection.y * 256.0f) / 256.0f;
} while(!DoBreak);
if(AlwaysRenderHookColl && RenderHookCollPlayer)
{
// invert the hook coll colors when using cl_show_hook_coll_always and +showhookcoll is pressed
HookCollColor = color_invert(HookCollColor);
}
Graphics()->SetColor(HookCollColor.WithAlpha(Alpha));
if(g_Config.m_ClHookCollSize > 0)
{
float LineWidth = 0.5f + (float)(g_Config.m_ClHookCollSize - 1) * 0.25f;
vec2 PerpToAngle = normalize(vec2(ExDirection.y, -ExDirection.x)) * GameClient()->m_Camera.m_Zoom;
vec2 Pos0 = FinishPos + PerpToAngle * -LineWidth;
vec2 Pos1 = FinishPos + PerpToAngle * LineWidth;
vec2 Pos2 = InitPos + PerpToAngle * -LineWidth;
vec2 Pos3 = InitPos + PerpToAngle * LineWidth;
IGraphics::CFreeformItem FreeformItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y, Pos2.x, Pos2.y, Pos3.x, Pos3.y);
Graphics()->QuadsDrawFreeform(&FreeformItem, 1);
Graphics()->QuadsEnd();
}
else
{
IGraphics::CLineItem LineItem(InitPos.x, InitPos.y, FinishPos.x, FinishPos.y);
Graphics()->LinesDraw(&LineItem, 1);
Graphics()->LinesEnd();
}
}
}
}
void CPlayers::RenderHook( void CPlayers::RenderHook(
const CNetObj_Character *pPrevChar, const CNetObj_Character *pPrevChar,
const CNetObj_Character *pPlayerChar, const CNetObj_Character *pPlayerChar,
@ -231,7 +380,6 @@ void CPlayers::RenderPlayer(
Position = m_pClient->m_aClients[ClientID].m_RenderPos; Position = m_pClient->m_aClients[ClientID].m_RenderPos;
else else
Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick);
vec2 Vel = mix(vec2(Prev.m_VelX / 256.0f, Prev.m_VelY / 256.0f), vec2(Player.m_VelX / 256.0f, Player.m_VelY / 256.0f), IntraTick); vec2 Vel = mix(vec2(Prev.m_VelX / 256.0f, Prev.m_VelY / 256.0f), vec2(Player.m_VelX / 256.0f, Player.m_VelY / 256.0f), IntraTick);
m_pClient->m_Flow.Add(Position, Vel * 100.0f, 10.0f); m_pClient->m_Flow.Add(Position, Vel * 100.0f, 10.0f);
@ -277,105 +425,6 @@ void CPlayers::RenderPlayer(
// draw gun // draw gun
{ {
bool AlwaysRenderHookColl = GameClient()->m_GameInfo.m_AllowHookColl && (Local ? g_Config.m_ClShowHookCollOwn : g_Config.m_ClShowHookCollOther) == 2;
bool RenderHookCollPlayer = ClientID >= 0 && Player.m_PlayerFlags & PLAYERFLAG_AIM && (Local ? g_Config.m_ClShowHookCollOwn : g_Config.m_ClShowHookCollOther) > 0;
bool RenderHookCollVideo = true;
#if defined(CONF_VIDEORECORDER)
RenderHookCollVideo = !IVideo::Current() || g_Config.m_ClVideoShowHookCollOther || Local;
#endif
if((AlwaysRenderHookColl || RenderHookCollPlayer) && RenderHookCollVideo)
{
vec2 ExDirection = Direction;
if(Local && Client()->State() != IClient::STATE_DEMOPLAYBACK)
ExDirection = normalize(vec2((int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].x, (int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].y));
Graphics()->TextureClear();
vec2 InitPos = Position;
vec2 FinishPos = InitPos + ExDirection * (m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength - 42.0f);
if(g_Config.m_ClHookCollSize > 0)
Graphics()->QuadsBegin();
else
Graphics()->LinesBegin();
ColorRGBA HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorNoColl));
float PhysSize = 28.0f;
vec2 OldPos = InitPos + ExDirection * PhysSize * 1.5f;
vec2 NewPos = OldPos;
bool DoBreak = false;
int Hit = 0;
do
{
OldPos = NewPos;
NewPos = OldPos + ExDirection * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookFireSpeed;
if(distance(InitPos, NewPos) > m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength)
{
NewPos = InitPos + normalize(NewPos - InitPos) * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength;
DoBreak = true;
}
int TeleNr = 0;
Hit = Collision()->IntersectLineTeleHook(OldPos, NewPos, &FinishPos, 0x0, &TeleNr);
if(!DoBreak && Hit)
{
if(Hit != TILE_NOHOOK)
{
HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorHookableColl));
}
}
if(m_pClient->IntersectCharacter(OldPos, FinishPos, FinishPos, ClientID) != -1)
{
HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorTeeColl));
break;
}
if(Hit)
break;
NewPos.x = round_to_int(NewPos.x);
NewPos.y = round_to_int(NewPos.y);
if(OldPos == NewPos)
break;
ExDirection.x = round_to_int(ExDirection.x * 256.0f) / 256.0f;
ExDirection.y = round_to_int(ExDirection.y * 256.0f) / 256.0f;
} while(!DoBreak);
if(AlwaysRenderHookColl && RenderHookCollPlayer)
{
// invert the hook coll colors when using cl_show_hook_coll_always and +showhookcoll is pressed
HookCollColor = color_invert(HookCollColor);
}
Graphics()->SetColor(HookCollColor.WithAlpha(Alpha));
if(g_Config.m_ClHookCollSize > 0)
{
float LineWidth = 0.5f + (float)(g_Config.m_ClHookCollSize - 1) * 0.25f;
vec2 PerpToAngle = normalize(vec2(ExDirection.y, -ExDirection.x)) * GameClient()->m_Camera.m_Zoom;
vec2 Pos0 = FinishPos + PerpToAngle * -LineWidth;
vec2 Pos1 = FinishPos + PerpToAngle * LineWidth;
vec2 Pos2 = InitPos + PerpToAngle * -LineWidth;
vec2 Pos3 = InitPos + PerpToAngle * LineWidth;
IGraphics::CFreeformItem FreeformItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y, Pos2.x, Pos2.y, Pos3.x, Pos3.y);
Graphics()->QuadsDrawFreeform(&FreeformItem, 1);
Graphics()->QuadsEnd();
}
else
{
IGraphics::CLineItem LineItem(InitPos.x, InitPos.y, FinishPos.x, FinishPos.y);
Graphics()->LinesDraw(&LineItem, 1);
Graphics()->LinesEnd();
}
}
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
Graphics()->QuadsSetRotation(State.GetAttach()->m_Angle * pi * 2 + Angle); Graphics()->QuadsSetRotation(State.GetAttach()->m_Angle * pi * 2 + Angle);
@ -681,6 +730,19 @@ void CPlayers::OnRender()
m_RenderInfoSpec.m_Size = 64.0f; m_RenderInfoSpec.m_Size = 64.0f;
int LocalClientID = m_pClient->m_Snap.m_LocalClientID; int LocalClientID = m_pClient->m_Snap.m_LocalClientID;
// get screen edges to avoid rendering offscreen
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
// expand the edges to prevent popping in/out onscreen
//
// it is assumed that the tee, all its weapons, and emotes fit into a 200x200 box centered on the tee
// this may need to be changed or calculated differently in the future
float BorderBuffer = 100;
ScreenX0 -= BorderBuffer;
ScreenX1 += BorderBuffer;
ScreenY0 -= BorderBuffer;
ScreenY1 += BorderBuffer;
// render everyone else's hook, then our own // render everyone else's hook, then our own
for(int ClientID = 0; ClientID < MAX_CLIENTS; ClientID++) for(int ClientID = 0; ClientID < MAX_CLIENTS; ClientID++)
{ {
@ -713,11 +775,21 @@ void CPlayers::OnRender()
{ {
continue; continue;
} }
RenderHookCollLine(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, ClientID);
//don't render offscreen
vec2 *pRenderPos = &m_pClient->m_aClients[ClientID].m_RenderPos;
if(pRenderPos->x < ScreenX0 || pRenderPos->x > ScreenX1 || pRenderPos->y < ScreenY0 || pRenderPos->y > ScreenY1)
{
continue;
}
RenderPlayer(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &m_aRenderInfo[ClientID], ClientID); RenderPlayer(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &m_aRenderInfo[ClientID], ClientID);
} }
if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID)) if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID))
{ {
const CGameClient::CClientData *pLocalClientData = &m_pClient->m_aClients[LocalClientID]; const CGameClient::CClientData *pLocalClientData = &m_pClient->m_aClients[LocalClientID];
RenderHookCollLine(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, LocalClientID);
RenderPlayer(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &m_aRenderInfo[LocalClientID], LocalClientID); RenderPlayer(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &m_aRenderInfo[LocalClientID], LocalClientID);
} }
} }

View file

@ -26,6 +26,11 @@ class CPlayers : public CComponent
const CTeeRenderInfo *pRenderInfo, const CTeeRenderInfo *pRenderInfo,
int ClientID, int ClientID,
float Intra = 0.f); float Intra = 0.f);
void RenderHookCollLine(
const CNetObj_Character *pPrevChar,
const CNetObj_Character *pPlayerChar,
int ClientID,
float Intra = 0.f);
bool IsPlayerInfoAvailable(int ClientID) const; bool IsPlayerInfoAvailable(int ClientID) const;
int m_WeaponEmoteQuadContainerIndex; int m_WeaponEmoteQuadContainerIndex;