8dadfb4878
Cmake: Add FTE_WERROR option, defaults to true in debug builds and off in release builds (in case future compilers have issues). Cmake: Pull in libXscreensaver so we don't get interrupted by screensavers when playing demos. Make: Added `make webcl-rel` for a web build without server bloat (eg for sites focused on demo playback. Yes, this means you XantoM). fteqcc: Include the decompiler in fteqcc (non-gui) builds ('-d' arg). fteqcc: Decompiler can now mostly handle hexen2 mods without any unknown opcodes. Allow ezHud and OpenSSL to be compiled as in-engine plugins, potentially for web and windows ports respectively. Web: Fix support for ogg vorbis. Add support for voip. Web: Added basic support for WebXR. QTV: Don't try seeking on unseekable qtv streams. Don't spam when developer 1 is set. QTV: add support for some eztv extensions. MVD: added hack to use ktx's vweps in mvd where mvdsv doesn't bother to record the info. qwfwd: hack around a hack in qwfwd, allowing it to work again. recording: favour qwd in single player, instead of mvd. Protocol: reduce client memory used for precache names. Bump maximum precache counts - some people are just abusive, yes you Orl. hexen2: add enough clientside protocol compat to play the demo included with h2mp. lacks effects. in_xflip: restored this setting. fs_hidesyspaths: new cvar, defaults to enabled so you won't find your username or whatever turning up in screenshots or the like. change it to 0 before debuging stuff eg via 'path'. gl_overbright_models: Added cvar to match QS. netchan: Added MTU determination, we'll no longer fail to connect when routers stupidly drop icmp packets. Win: try a few other versions of xinput too. CSQC: Added a CSQC_GenerateMaterial function, to give the csqc a chance to generate custom materials. MenuQC: Added support for the skeletal objects API.
377 lines
11 KiB
C
377 lines
11 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// gl_ngraph.c
|
|
|
|
#include "quakedef.h"
|
|
#include "shader.h"
|
|
|
|
void Draw_ExpandedString(struct font_s *font, float x, float y, conchar_t *str);
|
|
static float timehistory[NET_TIMINGS];
|
|
static int findex;
|
|
|
|
#define NET_GRAPHHEIGHT 32
|
|
|
|
//#define GRAPHTEX
|
|
#ifdef GRAPHTEX
|
|
static texid_t netgraphtexture; // netgraph texture
|
|
static shader_t *netgraphshader;
|
|
static unsigned int ngraph_texels[NET_GRAPHHEIGHT][NET_TIMINGS];
|
|
#else
|
|
static struct
|
|
{
|
|
unsigned int col;
|
|
float height;
|
|
} ngraph[NET_TIMINGS];
|
|
#endif
|
|
|
|
static void R_LineGraph (int x, float h)
|
|
{
|
|
int s;
|
|
unsigned color;
|
|
|
|
s = NET_GRAPHHEIGHT;
|
|
|
|
if (h == 10000 || h<0)
|
|
{
|
|
color = 0xff00ffff; // yellow
|
|
h=fabs(h);
|
|
}
|
|
else if (h == 9999)
|
|
color = 0xff0000ff; // red
|
|
else if (h == 9998)
|
|
color = 0xffff0000; // blue
|
|
else
|
|
color = 0xffffffff; // white
|
|
|
|
#ifdef GRAPHTEX
|
|
if (h>s)
|
|
h = s;
|
|
|
|
for (i=0 ; i<h ; i++)
|
|
if (i & 1)
|
|
ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = color&0xffefefef;
|
|
else
|
|
ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = color;
|
|
|
|
for ( ; i<s ; i++)
|
|
ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = 0x00000000;
|
|
#else
|
|
ngraph[x].col = color;
|
|
if (h > s)
|
|
ngraph[x].height = 1;
|
|
else
|
|
ngraph[x].height = h/(float)s;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_NetGraph
|
|
==============
|
|
*/
|
|
void R_NetGraph (void)
|
|
{
|
|
int a, x, i;
|
|
float y;
|
|
float pi, po, bi, bo;
|
|
int errorbar;
|
|
|
|
vec2_t p[4];
|
|
vec2_t tc[4];
|
|
vec4_t rgba[4];
|
|
extern shader_t *shader_draw_fill;
|
|
conchar_t line[2048];
|
|
float textheight, graphtop;
|
|
|
|
float pings, pings_min, pings_max, pingms_stddev, pingfr, dropped, choked, invalid;
|
|
int pingfr_min, pingfr_max;
|
|
|
|
x = 0;
|
|
if (r_netgraph.value < 0)
|
|
{
|
|
if (!cl.paused)
|
|
timehistory[++findex&NET_TIMINGSMASK] = (cl.currentpackentities?(cl.currentpackentities->servertime - cl.servertime)*NET_GRAPHHEIGHT*5:0);
|
|
for (a=0 ; a<NET_TIMINGS ; a++)
|
|
{
|
|
i = (findex-a) & NET_TIMINGSMASK;
|
|
R_LineGraph (NET_TIMINGS-1-a, timehistory[i]<0?10000:timehistory[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float last = 10000;
|
|
CL_CalcNet(r_netgraph.value);
|
|
for (a=0 ; a<NET_TIMINGS ; a++)
|
|
{
|
|
i = (cl.movesequence-a) & NET_TIMINGSMASK;
|
|
// if (packet_latency[i] != 10000)
|
|
last = packet_latency[i];
|
|
// else if (last >= 0)
|
|
// last = -last;
|
|
R_LineGraph (NET_TIMINGS-1-a, last);
|
|
}
|
|
}
|
|
|
|
textheight = 4;
|
|
#ifdef HAVE_SERVER
|
|
if (sv.state && sv.allocated_client_slots != 1)
|
|
textheight+=2;
|
|
#endif
|
|
textheight = ceil(textheight*Font_CharVHeight(font_console)/8)*8; //might have a small gap underneath
|
|
|
|
x = ((vid.width - 320)>>1); //eww
|
|
x=-x;
|
|
y = vid.height - sb_lines - textheight - NET_GRAPHHEIGHT - 2*8/*box borders*/;
|
|
|
|
M_DrawTextBox (x, y, NET_TIMINGS/8, (NET_GRAPHHEIGHT + textheight)/8);
|
|
x = 8;
|
|
if (r_netgraph.ival > 1)
|
|
CL_ShowTrafficUsage(x + NET_TIMINGS + 8, y);
|
|
y += 8; //top border
|
|
graphtop = y+textheight;
|
|
|
|
CL_CalcNet2(&pings, &pings_min, &pings_max, &pingms_stddev, &pingfr, &pingfr_min, &pingfr_max, &dropped, &choked, &invalid);
|
|
{
|
|
COM_ParseFunString(CON_WHITEMASK, va("%3.0f%% lost, %3.0f%% choked, %3.0f%% bad", dropped*100, choked*100, invalid*100), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
|
|
COM_ParseFunString(CON_WHITEMASK, va(" ping: %4.1fms %6.2f (%.1f-%.1f)\n", pings*1000, pingms_stddev, pings_min*1000, pings_max*1000), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
}
|
|
|
|
if (NET_GetRates(cls.sockets, &pi, &po, &bi, &bo))
|
|
{
|
|
COM_ParseFunString(CON_WHITEMASK, va(" in: %.1f %.0fb\n", pi, bi), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
COM_ParseFunString(CON_WHITEMASK, va(" out: %.1f %.0fb\n", po, bo), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
}
|
|
#ifdef HAVE_SERVER
|
|
if (sv.state && sv.allocated_client_slots != 1 && NET_GetRates(svs.sockets, &pi, &po, &bi, &bo))
|
|
{
|
|
COM_ParseFunString(CON_WHITEMASK, va("sv in: %.1f %.0fb\n", pi, bi), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
COM_ParseFunString(CON_WHITEMASK, va("svout: %.1f %.0fb\n", po, bo), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
}
|
|
#endif
|
|
|
|
y = graphtop; //rounding makes it ugly.
|
|
|
|
#ifdef GRAPHTEX
|
|
Image_Upload(netgraphtexture, TF_RGBA32, ngraph_texels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP);
|
|
R2D_Image(x, y, NET_TIMINGS, NET_GRAPHHEIGHT, 0, 0, 1, 1, netgraphshader);
|
|
#else
|
|
Vector2Set(p[2], 0,0);
|
|
Vector2Set(p[3], 0,0);
|
|
Vector4Set(rgba[2], 0,0,0,0);
|
|
Vector4Set(rgba[3], 0,0,0,0);
|
|
errorbar = 1; //first is discontinuous
|
|
for (a=0 ; a<NET_TIMINGS ; a++)
|
|
{
|
|
Vector2Copy(p[3], p[0]); Vector4Copy(rgba[3], rgba[0]);
|
|
Vector2Copy(p[2], p[1]); Vector4Copy(rgba[2], rgba[1]);
|
|
|
|
Vector2Set(p[2+0], x+a, y+(1-ngraph[a].height)*NET_GRAPHHEIGHT);
|
|
Vector2Set(p[2+1], x+a, y+NET_GRAPHHEIGHT);
|
|
|
|
Vector2Set(tc[2+0], a/(float)NET_TIMINGS, (1-ngraph[a].height));
|
|
Vector2Set(tc[2+1], a/(float)NET_TIMINGS, 1);
|
|
Vector4Set(rgba[2+0], ((ngraph[a].col>>0)&0xff)/255.0, ((ngraph[a].col>>8)&0xff)/255.0, ((ngraph[a].col>>16)&0xff)/255.0, ((ngraph[a].col>>24)&0xff)/255.0);
|
|
Vector4Copy(rgba[2+0], rgba[2+1]);
|
|
|
|
if (ngraph[a].height==1)
|
|
errorbar = 2; //this one and the following should be discontiguous
|
|
if (errorbar --> 0)
|
|
{ //if this is a full-height bar, break the smooth curve and just make it discontinuous
|
|
p[0][1] = p[3][1];
|
|
p[1][1] = p[2][1];
|
|
Vector4Copy(rgba[3], rgba[0]);
|
|
Vector4Copy(rgba[2], rgba[1]);
|
|
}
|
|
|
|
if (a)
|
|
R2D_Image2dQuad((const vec2_t*)p, (const vec2_t*)tc, (const vec4_t*)rgba, shader_draw_fill);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void R_FrameTimeGraph (float frametime, float scale)
|
|
{
|
|
float bias = 0, h, lh;
|
|
int a, b, x, i, y;
|
|
|
|
struct{
|
|
vec2_t xy[4];
|
|
vec2_t tc[4];
|
|
vec4_t rgba[4];
|
|
} g[3];
|
|
extern shader_t *shader_draw_fill;
|
|
|
|
conchar_t line[128];
|
|
int textheight;
|
|
float minv=FLT_MAX, maxv=FLT_MIN, avg=0, dev=0;
|
|
|
|
static struct
|
|
{
|
|
float time[countof(g)];
|
|
} history[NET_TIMINGS];
|
|
static unsigned int findex;
|
|
|
|
#ifdef LOADERTHREAD
|
|
extern int com_hadwork[WG_COUNT];
|
|
#endif
|
|
extern double r_loaderstalltime;
|
|
#ifdef HAVE_SERVER
|
|
extern double server_frametime;
|
|
#endif
|
|
|
|
history[findex&NET_TIMINGSMASK].time[0] = max(0,frametime); //server band
|
|
#ifdef HAVE_SERVER
|
|
frametime -= server_frametime; server_frametime = 0;
|
|
#endif
|
|
|
|
history[findex&NET_TIMINGSMASK].time[1] = max(0,frametime); //stalls band
|
|
frametime -= r_loaderstalltime; r_loaderstalltime = 0;
|
|
|
|
history[findex&NET_TIMINGSMASK].time[2] = max(0,frametime); //client band (max is needed because we might have been failing to clear the other timers)
|
|
|
|
findex++;
|
|
|
|
#ifdef LOADERTHREAD
|
|
if (com_hadwork[WG_MAIN])
|
|
{ //recolour the graph red if the main thread processed something from a worker.
|
|
//show three, because its not so easy to see when its whizzing past.
|
|
com_hadwork[WG_MAIN] = 0;
|
|
// history[(findex-1)&NET_TIMINGSMASK].col = 0xff0000ff;
|
|
// history[(findex-2)&NET_TIMINGSMASK].col = 0xff0000ff;
|
|
// history[(findex-3)&NET_TIMINGSMASK].col = 0xff0000ff;
|
|
}
|
|
#endif
|
|
|
|
x = 0;
|
|
for (a=0 ; a<NET_TIMINGS ; a++)
|
|
{
|
|
avg += history[a].time[0];
|
|
if (minv > history[a].time[0])
|
|
minv = history[a].time[0];
|
|
if (maxv < history[a].time[0])
|
|
maxv = history[a].time[0];
|
|
}
|
|
if (!scale)
|
|
{
|
|
bias = minv;
|
|
scale = NET_GRAPHHEIGHT/(maxv-minv);
|
|
}
|
|
else
|
|
scale *= 1000;
|
|
avg/=a;
|
|
for (a = 0; a < NET_TIMINGS; a++)
|
|
dev += 1000*1000*(history[a].time[0] - avg)*(history[a].time[0] - avg);
|
|
dev /= a;
|
|
dev = sqrt(dev);
|
|
|
|
|
|
x = ((vid.width - 320)>>1);
|
|
x=-x;
|
|
|
|
textheight = 4;
|
|
textheight = ceil(textheight*Font_CharVHeight(font_console)/8)*8; //might have a small gap underneath
|
|
|
|
y = vid.height - sb_lines - 16 - NET_GRAPHHEIGHT - textheight;
|
|
|
|
M_DrawTextBox (x, y, NET_TIMINGS/8, (textheight + NET_GRAPHHEIGHT)/8);
|
|
x=8;
|
|
y += 8;
|
|
|
|
COM_ParseFunString(CON_WHITEMASK, va("mean: %.3ffps (%.3fms)", 1/avg, 1000*avg), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
COM_ParseFunString(CON_WHITEMASK, va("fastest: %.3ffps (%.3fms)", 1/minv, 1000*minv), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
COM_ParseFunString(CON_WHITEMASK, va("slowest: %.3ffps (%.3fms)", 1/maxv, 1000*maxv), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
COM_ParseFunString(CON_WHITEMASK, va("deviation: %.3fms (max %.3fms)", dev, (maxv-minv)*1000/2), line, sizeof(line), false);
|
|
Draw_ExpandedString(font_console, x, y, line);
|
|
y += Font_CharVHeight(font_console);
|
|
|
|
for (b = 0; b < countof(g); b++)
|
|
{
|
|
Vector2Set(g[b].xy[2], 0,0);
|
|
Vector2Set(g[b].xy[3], 0,0);
|
|
}
|
|
for (a=0 ; a<4 ; a++)
|
|
{
|
|
Vector4Set(g[0].rgba[a], 1.0,0.1,0.1,1.0); //server = red
|
|
Vector4Set(g[1].rgba[a], 0.1,1.0,0.1,1.0); //lightmap/stalls = green
|
|
Vector4Set(g[2].rgba[a], 1.0,1.0,1.0,1.0); //client/other = white.
|
|
}
|
|
|
|
for (a=0 ; a<NET_TIMINGS ; a++)
|
|
{
|
|
i = (findex-NET_TIMINGS+a)&(NET_TIMINGS-1);
|
|
lh = NET_GRAPHHEIGHT;
|
|
for (b = countof(g); b-- > 0; lh = h)
|
|
{
|
|
h = (history[i].time[b]-bias) * scale;
|
|
|
|
if (h > NET_GRAPHHEIGHT)
|
|
h = NET_GRAPHHEIGHT;
|
|
h = (NET_GRAPHHEIGHT-h);
|
|
|
|
Vector2Copy(g[b].xy[3], g[b].xy[0]); Vector4Copy(g[b].rgba[3], g[b].rgba[0]);
|
|
Vector2Copy(g[b].xy[2], g[b].xy[1]); Vector4Copy(g[b].rgba[2], g[b].rgba[1]);
|
|
|
|
Vector2Set(g[b].xy[2+0], x+a, y+h);
|
|
Vector2Set(g[b].xy[2+1], x+a, y+lh);
|
|
|
|
Vector2Set(g[b].tc[2+0], x/(float)NET_TIMINGS, (NET_GRAPHHEIGHT-h)/NET_GRAPHHEIGHT);
|
|
Vector2Set(g[b].tc[2+1], x/(float)NET_TIMINGS, 1);
|
|
|
|
if (a && (h!=lh || g[b].xy[0][1]!=g[b].xy[1][1]))
|
|
R2D_Image2dQuad((const vec2_t*)g[b].xy, (const vec2_t*)g[b].tc, (const vec4_t*)g[b].rgba, shader_draw_fill);
|
|
}
|
|
}
|
|
}
|
|
|
|
void R_NetgraphInit(void)
|
|
{
|
|
#ifdef GRAPHTEX
|
|
TEXASSIGN(netgraphtexture, Image_CreateTexture("***netgraph***", NULL, IF_UIPIC|IF_NOMIPMAP|IF_CLAMP));
|
|
netgraphshader = R_RegisterShader("netgraph", SUF_NONE,
|
|
"{\n"
|
|
"program default2d\n"
|
|
"{\n"
|
|
"map $diffuse\n"
|
|
"blendfunc blend\n"
|
|
"}\n"
|
|
"}\n"
|
|
);
|
|
netgraphshader->defaulttextures->base = netgraphtexture;
|
|
#endif
|
|
}
|