diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 328a1565..52aa85df 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -452,7 +452,7 @@ void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state void CL_CatagorizePosition (playerview_t *pv, float *org) { //fixme: in nq, we are told by the server and should skip this, which avoids needing to know the player's size. - if (pv->spectator) + if (pv->spectator && !CAM_ISLOCKED(pv)) { pv->onground = false; // in air return; @@ -787,7 +787,7 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st memset(plstate, 0, sizeof(*plstate)); plstate->jump_held = jumpheld; - switch(state->u.q1.pmovetype & 0x7f) + switch(state->u.q1.pmovetype & 0x3f) { case MOVETYPE_NOCLIP: if (cls.z_ext & Z_EXT_PM_TYPE_NEW) @@ -828,6 +828,7 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st { VectorScale(state->u.q1.velocity, 1/8.0, plstate->velocity); plstate->onground = !!(state->u.q1.pmovetype&128); + plstate->jump_held = !!(state->u.q1.pmovetype&64); } plstate->pm_type = pmtype; @@ -917,15 +918,18 @@ void CL_PredictMovePNum (int seat) //interpolation state should be updated to match prediction state, so entities move correctly in mirrors/portals. //this entire function is pure convolouted bollocks. + struct { + int frame; + double time; + player_state_t *state; + usercmd_t *cmd; + } from, to; playerview_t *pv = &cl.playerview[seat]; int i; float f; - int fromframe, toframe; outframe_t *backdate; - player_state_t *fromstate, *tostate, framebuf[2]; //need two framebufs so we can interpolate between two states. + player_state_t framebuf[2]; //need two framebufs so we can interpolate between two states. static player_state_t nullstate; - usercmd_t *cmdfrom = NULL, *cmdto = NULL; - double fromtime, totime; int oldphysent; double simtime; //this is server time if nopred is set (lerp-only), and local time if we're predicting extern cvar_t cl_netfps; @@ -934,6 +938,7 @@ void CL_PredictMovePNum (int seat) qboolean lerpangles = false; int trackent; qboolean cam_nowlocked = false; + usercmd_t indcmd; //these are to make svc_viewentity work better float netfps = cl_netfps.value; @@ -1076,17 +1081,18 @@ void CL_PredictMovePNum (int seat) if (nopred) { //match interpolation info - fromframe = ((char*)cl.previouspackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t); - fromtime = cl.inframes[fromframe & UPDATE_MASK].packet_entities.servertime; - toframe = ((char*)cl.currentpackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t); - totime = cl.inframes[toframe & UPDATE_MASK].packet_entities.servertime; + from.frame = ((char*)cl.previouspackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t); + from.time = cl.inframes[from.frame & UPDATE_MASK].packet_entities.servertime; + to.frame = ((char*)cl.currentpackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t); + to.time = cl.inframes[to.frame & UPDATE_MASK].packet_entities.servertime; simtime = cl.currentpacktime; + to.cmd = from.cmd = NULL; } else { - fromframe = 0; - toframe = 0; - totime = fromtime = 0; + to.frame = from.frame = 0; + to.time = from.time = 0; + to.cmd = from.cmd = NULL; //try to find the inbound frame that sandwiches the realtime that we're trying to simulate. //if we're predicting, this will be some time in the future, and thus we'll be forced to pick the most recent frame. @@ -1116,18 +1122,20 @@ void CL_PredictMovePNum (int seat) //okay, looks valid //if this is the first one we found, make sure both from+to are set properly - if (!fromframe) + if (!from.frame) { - fromframe = i; - fromtime = backdate->senttime; + from.frame = i; + from.time = backdate->senttime; } - toframe = fromframe; - totime = fromtime; - cmdto = cmdfrom; - fromframe = i; - fromtime = backdate->senttime; - cmdfrom = &backdate->cmd[seat]; - if (fromtime < simtime && fromframe != toframe) + to = from; + to.state = NULL; + + from.frame = i; + from.time = backdate->senttime; + from.cmd = &backdate->cmd[seat]; + if (to.frame > pv->prop.sequence) + continue; //if we didn't predict to this frame yet, then the waterjump etc state will be invalid, so try to go for an older frame so that it actually propagates properly. + if (from.time < simtime && from.frame != to.frame) break; //okay, we found the first frame that is older, no need to continue looking } } @@ -1136,61 +1144,61 @@ void CL_PredictMovePNum (int seat) if ((pv->cam_state == CAM_WALLCAM || pv->cam_state == CAM_EYECAM) && trackent && trackent <= cl.allocated_client_slots) { - fromstate = &cl.inframes[fromframe & UPDATE_MASK].playerstate[trackent-1]; - tostate = &cl.inframes[toframe & UPDATE_MASK].playerstate[trackent-1]; + from.state = &cl.inframes[from.frame & UPDATE_MASK].playerstate[trackent-1]; + to.state = &cl.inframes[to.frame & UPDATE_MASK].playerstate[trackent-1]; } else { if (cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV) { pv->nolocalplayer = false; - fromstate = &cl.inframes[cl.ackedmovesequence & UPDATE_MASK].playerstate[pv->playernum]; - tostate = &cl.inframes[cl.movesequence & UPDATE_MASK].playerstate[pv->playernum]; + from.state = &cl.inframes[cl.ackedmovesequence & UPDATE_MASK].playerstate[pv->playernum]; + to.state = &cl.inframes[cl.movesequence & UPDATE_MASK].playerstate[pv->playernum]; } else { - fromstate = &cl.inframes[fromframe & UPDATE_MASK].playerstate[pv->playernum]; - tostate = &cl.inframes[toframe & UPDATE_MASK].playerstate[pv->playernum]; + from.state = &cl.inframes[from.frame & UPDATE_MASK].playerstate[pv->playernum]; + to.state = &cl.inframes[to.frame & UPDATE_MASK].playerstate[pv->playernum]; } } - pv->pmovetype = tostate->pm_type; + pv->pmovetype = to.state->pm_type; le = &cl.lerpplayers[pv->playernum]; - if (!cmdfrom) - cmdfrom = &cl.outframes[fromframe & UPDATE_MASK].cmd[pv->playernum]; - if (!cmdto) - cmdto = &cl.outframes[toframe & UPDATE_MASK].cmd[pv->playernum]; + if (!from.cmd) + from.cmd = &cl.outframes[from.frame & UPDATE_MASK].cmd[pv->playernum]; + if (!to.cmd) + to.cmd = &cl.outframes[to.frame & UPDATE_MASK].cmd[pv->playernum]; //if our network protocol doesn't have a concept of separate players, make sure our player states are updated from those entities //fixme: use entity states instead of player states to avoid the extra work here if (pv->nolocalplayer) { packet_entities_t *pe; - pe = &cl.inframes[fromframe & UPDATE_MASK].packet_entities; + pe = &cl.inframes[from.frame & UPDATE_MASK].packet_entities; for (i = 0; i < pe->num_entities; i++) { if (pe->entities[i].number == trackent) { - CL_EntStateToPlayerState(fromstate, &pe->entities[i]); + CL_EntStateToPlayerState(from.state, &pe->entities[i]); if (nopred) - fromtime -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players + from.time -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players break; } } if (i == pe->num_entities && pv->nolocalplayer) { - fromstate = &nullstate; + from.state = &nullstate; nopred = true; } - pe = &cl.inframes[toframe & UPDATE_MASK].packet_entities; + pe = &cl.inframes[to.frame & UPDATE_MASK].packet_entities; for (i = 0; i < pe->num_entities; i++) { if (pe->entities[i].number == trackent) { - CL_EntStateToPlayerState(tostate, &pe->entities[i]); + CL_EntStateToPlayerState(to.state, &pe->entities[i]); if (nopred) - totime -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players. FIXME: this can push the simtime into the 'future' resulting in stuttering + to.time -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players. FIXME: this can push the simtime into the 'future' resulting in stuttering if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) { #ifdef QUAKESTATS @@ -1201,14 +1209,14 @@ void CL_PredictMovePNum (int seat) pv->statsf[STAT_WEAPONFRAME] = cl.players[pv->playernum].statsf[STAT_WEAPONFRAME] = pe->entities[i].u.q1.weaponframe; } #endif - pv->pmovetype = tostate->pm_type; + pv->pmovetype = to.state->pm_type; } break; } } if (i == pe->num_entities && pv->nolocalplayer) { - tostate = &nullstate; + to.state = &nullstate; nopred = true; } if (pv->nolocalplayer && trackent < cl.maxlerpents) @@ -1225,82 +1233,77 @@ void CL_PredictMovePNum (int seat) pmove.skipent = trackent; //just in case we don't run any prediction - VectorCopy(tostate->gravitydir, pmove.gravitydir); + VectorCopy(to.state->gravitydir, pmove.gravitydir); //if all else fails... - pmove.pm_type = tostate->pm_type; - pmove.onground = tostate->onground; - VectorCopy(tostate->szmins, pmove.player_mins); - VectorCopy(tostate->szmaxs, pmove.player_maxs); + pmove.pm_type = to.state->pm_type; + pmove.onground = to.state->onground; + VectorCopy(to.state->szmins, pmove.player_mins); + VectorCopy(to.state->szmaxs, pmove.player_maxs); if (!nopred) - { - for (i=1 ; i= simtime) - { - if (i == 1) - { - //we must always predict a frame, just to ensure that the playerstate's jump status etc is valid for the next frame, even if we're not going to use it for interpolation. - //this assumes that we always have at least one video frame to each network frame, of course. - //note that q2 updates its values via networking rather than propagation. - player_state_t tmp, *next; -// Con_DPrintf(" propagate %i: %f-%f\n", cl.ackedmovesequence+i, fromtime, totime); - CL_PredictUsercmd (seat, pv->viewentity, tostate, &tmp, &of->cmd[seat]); - next = &cl.inframes[(toframe+i) & UPDATE_MASK].playerstate[pv->playernum]; - next->onground = tmp.onground; - next->jump_held = tmp.jump_held; - next->jump_msec = tmp.jump_msec; - VectorCopy(tmp.gravitydir, next->gravitydir); - } + outframe_t *of = &cl.outframes[i & UPDATE_MASK]; + if (to.time >= simtime) break; - } - if (of->cmd_sequence != cl.ackedmovesequence+i) + if (of->cmd_sequence != i) { // Con_DPrintf("trying to predict a frame which is no longer valid\n"); break; } - fromtime = totime; - fromstate = tostate; - fromframe = toframe; //qw debug - cmdfrom = cmdto; + //okay, move it forward a frame. + from = to; - cmdto = &of->cmd[seat]; - totime = of->senttime; - toframe = cl.ackedmovesequence+i;//qw debug + to.cmd = &of->cmd[seat]; + to.time = of->senttime; + to.frame = i;//qw debug + to.state = &framebuf[to.frame&1]; - if (i == 1)//I've no idea how else to propogate event state from one frame to the next - tostate = &cl.inframes[(fromframe+i) & UPDATE_MASK].playerstate[pv->playernum]; - else - tostate = &framebuf[i&1]; - -// Con_DPrintf(" pred %i: %f-%f\n", cl.ackedmovesequence+i, fromtime, totime); - CL_PredictUsercmd (seat, trackent, fromstate, tostate, cmdto); + if (from.frame == pv->prop.sequence && pv->prop.sequence) + { + if (!(cls.z_ext & Z_EXT_PF_ONGROUND)) + from.state->onground = pv->prop.onground; + if (!(cls.z_ext & Z_EXT_PM_TYPE)) + from.state->jump_held = pv->prop.jump_held; + from.state->jump_msec = pv->prop.jump_msec; + from.state->waterjumptime = pv->prop.waterjumptime; + if (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) + VectorCopy(pv->prop.gravitydir, from.state->gravitydir); + } + CL_PredictUsercmd (seat, trackent, from.state, to.state, to.cmd); + if (i <= cl.validsequence && simtime >= to.time) + { //this frame is final keep track of our propagated values. + pv->prop.onground = pmove.onground; + pv->prop.jump_held = pmove.jump_held; + pv->prop.jump_msec = pmove.jump_msec; + pv->prop.waterjumptime = pmove.waterjumptime; + VectorCopy(pmove.gravitydir, pv->prop.gravitydir); + pv->prop.sequence = i; + } } - if (simtime > totime) + if (simtime > to.time) { //extrapolate X extra seconds float msec; - usercmd_t indcmd; - msec = ((simtime - totime) * 1000); + msec = ((simtime - to.time) * 1000); if (msec >= 1) { - fromstate = tostate; - fromtime = totime; - fromframe = toframe; - cmdfrom = cmdto; + from = to; - tostate = &framebuf[i++&1]; if (cl_pendingcmd[seat].msec && !cls.demoplayback) indcmd = cl_pendingcmd[seat]; else - indcmd = *cmdto; - cmdto = &indcmd; - totime = simtime; - toframe+=1; + indcmd = *to.cmd; + to.cmd = &indcmd; + to.time = simtime; + to.frame+=1; + to.state = &framebuf[to.frame&1]; if (cls.demoplayback) { @@ -1308,66 +1311,77 @@ void CL_PredictMovePNum (int seat) msec *= cl_demospeed.value; } - cmdto->msec = bound(0, msec, 250); + to.cmd->msec = bound(0, msec, 250); + if (from.frame == pv->prop.sequence && pv->prop.sequence) + { //overwrite non-networked state, to propagate it as required. + if (!(cls.z_ext & Z_EXT_PF_ONGROUND)) + from.state->onground = pv->prop.onground; + if (!(cls.z_ext & Z_EXT_PM_TYPE)) + from.state->jump_held = pv->prop.jump_held; + from.state->jump_msec = pv->prop.jump_msec; + from.state->waterjumptime = pv->prop.waterjumptime; + if (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) + VectorCopy(pv->prop.gravitydir, from.state->gravitydir); + } // Con_DPrintf(" extrap %i: %f-%f (%g)\n", toframe, fromtime, simtime, simtime-fromtime); - CL_PredictUsercmd (seat, trackent, fromstate, tostate, cmdto); + CL_PredictUsercmd (seat, trackent, from.state, to.state, to.cmd); } } pv->onground = pmove.onground; - pv->pmovetype = tostate->pm_type; + pv->pmovetype = to.state->pm_type; } pmove.numphysent = oldphysent; - if (totime == fromtime) + if (to.time == from.time) { - VectorCopy (tostate->velocity, pv->simvel); - VectorCopy (tostate->origin, pv->simorg); + VectorCopy (to.state->velocity, pv->simvel); + VectorCopy (to.state->origin, pv->simorg); if (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM) - VectorCopy(tostate->viewangles, pv->simangles); + VectorCopy(to.state->viewangles, pv->simangles); //Con_DPrintf("%f %f %f\n", fromtime, simtime, totime); } else { vec3_t move; // now interpolate some fraction of the final frame - f = (simtime - fromtime) / (totime - fromtime); + f = (simtime - from.time) / (to.time - from.time); if (f < 0) f = 0; if (f > 1) f = 1; //Con_DPrintf("%i:%f %f %i:%f (%f)\n", fromframe, fromtime, simtime, toframe, totime, f); - VectorSubtract(tostate->origin, fromstate->origin, move); + VectorSubtract(to.state->origin, from.state->origin, move); if (DotProduct(move, move) > 128*128) { // teleported, so don't lerp - VectorCopy (tostate->velocity, pv->simvel); - VectorCopy (tostate->origin, pv->simorg); + VectorCopy (to.state->velocity, pv->simvel); + VectorCopy (to.state->origin, pv->simorg); } else { for (i=0 ; i<3 ; i++) { - pv->simorg[i] = (1-f)*fromstate->origin[i] + f*tostate->origin[i]; - pv->simvel[i] = (1-f)*fromstate->velocity[i] + f*tostate->velocity[i]; + pv->simorg[i] = (1-f)*from.state->origin[i] + f*to.state->origin[i]; + pv->simvel[i] = (1-f)*from.state->velocity[i] + f*to.state->velocity[i]; if (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM) { - pv->simangles[i] = LerpAngles360(fromstate->viewangles[i], tostate->viewangles[i], f);// * (360.0/65535); + pv->simangles[i] = LerpAngles360(from.state->viewangles[i], to.state->viewangles[i], f);// * (360.0/65535); // pv->viewangles[i] = LerpAngles16(fromstate->command.angles[i], tostate->command.angles[i], f) * (360.0/65535); } else if (lerpangles) - pv->simangles[i] = LerpAngles16(cmdfrom->angles[i], cmdto->angles[i], f) * (360.0/65535); + pv->simangles[i] = LerpAngles16(from.cmd->angles[i], to.cmd->angles[i], f) * (360.0/65535); } } } if (cls.protocol == CP_NETQUAKE && nopred) - pv->onground = tostate->onground; + pv->onground = to.state->onground; else - CL_CatagorizePosition(pv, tostate->origin); + CL_CatagorizePosition(pv, to.state->origin); CL_CalcCrouch (pv); pv->waterlevel = pmove.waterlevel; diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 0292c828..24583897 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -2498,10 +2498,25 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload int width, height; void *buf; qboolean okay = false; + qboolean usefbo; + qboolean oldwarndraw = r_refdef.warndraw; - Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname)); - R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, 1, RT_IMAGEFLAGS); - BE_RenderToTextureUpdate2d(true); + if (fbwidth == vid.fbpwidth && fbheight == vid.fbpheight && qrenderer != QR_VULKAN) + usefbo = false; + else if (qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects) + usefbo = true; + else + return NULL; + + if (usefbo) + { + r_refdef.warndraw = true; + Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname)); + /*vid.framebuffer =*/R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, 1, RT_IMAGEFLAGS); + BE_RenderToTextureUpdate2d(true); + } + else + r_refdef.warndraw = false; R2D_FillBlock(0, 0, vid.fbvwidth, vid.fbvheight); @@ -2512,7 +2527,7 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload #ifdef CSQC_DAT if (!okay && CSQC_DrawView()) okay = true; -// if (!*r_refdef.rt_destcolour[0].texname) + if (usefbo)// && !*r_refdef.rt_destcolour[0].texname) { //csqc protects its own. lazily. Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname)); BE_RenderToTextureUpdate2d(true); @@ -2523,6 +2538,9 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload V_RenderView (no2d); okay = true; } + if (R2D_Flush) + R2D_Flush(); + //fixme: add a way to get+save the depth values too if (!okay) { @@ -2533,9 +2551,14 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload else buf = VID_GetRGBInfo(stride, &width, &height, fmt); - R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0, RT_IMAGEFLAGS); - Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname)); - BE_RenderToTextureUpdate2d(true); + if (usefbo) + { + R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0, RT_IMAGEFLAGS); + Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname)); + BE_RenderToTextureUpdate2d(true); + } + + r_refdef.warndraw = oldwarndraw; if (!buf || width != fbwidth || height != fbheight) { @@ -3431,8 +3454,6 @@ void SCR_Init (void) Cmd_AddCommandD ("screenshot_cubemap",SCR_ScreenShot_Cubemap_f, "screenshot_cubemap [size]\nTakes 6 screenshots forming a single cubemap."); Cmd_AddCommandD ("envmap",SCR_ScreenShot_Cubemap_f, "Legacy name for the screenshot_cubemap command."); //legacy Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); - Cmd_AddCommand ("sizeup",SCR_SizeUp_f); - Cmd_AddCommand ("sizedown",SCR_SizeDown_f); scr_net = R2D_SafePicFromWad ("net"); scr_turtle = R2D_SafePicFromWad ("turtle"); @@ -3473,7 +3494,5 @@ void SCR_DeInit (void) Cmd_RemoveCommand ("screenshot_360"); Cmd_RemoveCommand ("screenshot_cubemap"); Cmd_RemoveCommand ("envmap"); - Cmd_RemoveCommand ("sizeup"); - Cmd_RemoveCommand ("sizedown"); } } diff --git a/engine/client/client.h b/engine/client/client.h index 59c6fa1c..4b257a05 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -50,10 +50,11 @@ typedef struct double state_time; // not the same as the packet time, // because player commands come asyncronously + usercmd_t command; // last command for prediction vec3_t origin; - vec3_t predorigin; // pre-predicted pos + vec3_t predorigin; // pre-predicted pos for other players (allowing us to just lerp when active) vec3_t viewangles; // only for demos, not from server vec3_t velocity; int weaponframe; @@ -75,15 +76,15 @@ typedef struct int flags; // dead, gib, etc int pm_type; - float waterjumptime; - qboolean onground; - qboolean jump_held; - int jump_msec; // hack for fixing bunny-hop flickering on non-ZQuake servers - vec3_t szmins, szmaxs; - vec3_t gravitydir; - float lerpstarttime; - int oldframe; + vec3_t szmins, szmaxs; + + //maybe-propagated... use the networked value if available. + float waterjumptime; //never networked... + qboolean onground; //networked with Z_EXT_PF_ONGROUND||replacementdeltas + qboolean jump_held; //networked with Z_EXT_PM_TYPE + int jump_msec; // hack for fixing bunny-hop flickering on non-ZQuake servers + vec3_t gravitydir; //networked with replacementdeltas } player_state_t; @@ -686,6 +687,20 @@ struct playerview_s float viewheight; int waterlevel; //for smartjump + //for values that are propagated from one frame to the next + //the next frame will always predict from the one we're tracking, where possible. + //these values should all regenerate naturally from networked state (misses should be small/rare and fade when the physics catches up). + struct playerpredprop_s + { + float waterjumptime; + qboolean onground; + vec3_t gravitydir; + qboolean jump_held; + int jump_msec; // hack for fixing bunny-hop flickering on non-ZQuake servers + + int sequence; + } prop; + #ifdef Q2CLIENT vec3_t predicted_origin; vec3_t predicted_angles; diff --git a/engine/client/image.c b/engine/client/image.c index ebdd9c20..4a323943 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -10,8 +10,12 @@ #define LittleShort(s) s #define LittleLong(s) s #else +#ifdef IMAGEFMT_TGA cvar_t r_dodgytgafiles = CVARD("r_dodgytgafiles", "0", "Many old glquake engines had a buggy tga loader that ignored bottom-up flags. Naturally people worked around this and the world was plagued with buggy images. Most engines have now fixed the bug, but you can reenable it if you have bugged tga files."); +#endif +#ifdef IMAGEFMT_PCX cvar_t r_dodgypcxfiles = CVARD("r_dodgypcxfiles", "0", "When enabled, this will ignore the palette stored within pcx files, for compatibility with quake2."); +#endif cvar_t r_dodgymiptex = CVARD("r_dodgymiptex", "1", "When enabled, this will force regeneration of mipmaps, discarding mips1-4 like glquake did. This may eg solve fullbright issues with some maps, but may reduce distant detail levels."); char *r_defaultimageextensions = @@ -21,15 +25,17 @@ char *r_defaultimageextensions = #ifdef IMAGEFMT_KTX "ktx " //compressed or something. not to be confused with the qw mod by the same name. GL requires that etc2 compression is supported by modern drivers, but not necessarily the hardware. as such, dds with its s3tc bias should always come first (as the patents mean that drivers are much less likely to advertise it when they don't support it properly). #endif +#ifdef IMAGEFMT_TGA "tga" //fairly fast to load -#if defined(AVAIL_PNGLIB) || defined(FTE_TARGET_WEB) +#endif +#if defined(IMAGEFMT_PNG) || defined(FTE_TARGET_WEB) " png" //pngs, fairly common, but slow #endif #ifdef IMAGEFMT_BMP //" bmp" //wtf? at least not lossy //" ico" //noone wants this... #endif -#if defined(AVAIL_JPEGLIB) || defined(FTE_TARGET_WEB) +#if defined(IMAGEFMT_JPG) || defined(FTE_TARGET_WEB) " jpg" //q3 uses some jpegs, for some reason //" jpeg" //thankfuly the quake community stuck to .jpg instead #endif @@ -49,10 +55,102 @@ char *r_defaultimageextensions = #ifdef IMAGEFMT_PKM //" pkm" //compressed format, but lacks mipmaps which makes it terrible to use. #endif +#ifdef IMAGEFMT_ASTC + //" astc" //compressed format, but lacks mipmaps which makes it terrible to use. +#endif +#ifdef IMAGEFMT_GIF + //" gif" +#endif +#ifdef IMAGEFMT_PIC + //" pic" +#endif #ifdef IMAGEFMT_PCX " pcx" //pcxes are the original gamedata of q2. So we don't want them to override pngs. +#endif +#ifdef IMAGEFMT_LMP + //" lmp" //lame outdated junk. any code that expects a lmp will use that extension, so don't bother swapping out extensions for it. #endif ; + +#ifdef AVAIL_STBI + #if defined(IMAGEFMT_PNG) && !defined(AVAIL_PNGLIB) && !defined(FTE_TARGET_WEB) + #define STBI_ONLY_PNG + #define STBIW_ONLY_PNG + #undef IMAGEFMT_PNG + #endif + #if defined(IMAGEFMT_JPG) && !defined(AVAIL_JPEGLIB) && !defined(FTE_TARGET_WEB) + #define STBI_ONLY_JPEG + #define STBIW_ONLY_JPEG + #undef IMAGEFMT_JPG + #endif + #if defined(IMAGEFMT_BMP) && 0 //use our own implementation, giving .ico too + #define STBI_ONLY_BMP + #define STBIW_ONLY_BMP + #undef IMAGEFMT_BMP + #endif + #if defined(IMAGEFMT_PSD) && 0 //use our own implementation + #define STBI_ONLY_PSD + #undef IMAGEFMT_PSD + #endif + #if defined(IMAGEFMT_TGA) && 0 //use our own implementation, giving htga and some twiddles. + #define STBI_ONLY_TGA + #define STBIW_ONLY_TGA + #undef IMAGEFMT_TGA + #endif + #if defined(IMAGEFMT_GIF) //&& 0 + #define STBI_ONLY_GIF + #undef IMAGEFMT_GIF + #endif + #if defined(IMAGEFMT_HDR) && 0 //use our own implementation, we're not using the stbi_loadf stuff anyway + #define STBI_ONLY_HDR + #define STBIW_ONLY_HDR + #undef IMAGEFMT_HDR + #endif + #if defined(IMAGEFMT_PIC) //&& 0 + #define STBI_ONLY_PIC + #undef IMAGEFMT_PIC + #endif + #if defined(IMAGEFMT_PBM) && 0 //use our own implementation, giving pfm.pbm.pam too + #define STBI_ONLY_PNM + #undef IMAGEFMT_PBM + #endif + + //now we know whether we need stbi or not, pull in the right stuff. + #if defined(STBI_ONLY_PNG) || defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_BMP) || defined(STBI_ONLY_PSD) || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) + #define STBI_NO_STDIO + #define STBI_MALLOC BZ_Malloc + #define STBI_REALLOC BZ_Realloc + #define STBI_FREE BZ_Free + //#define STBI_NO_FAILURE_STRINGS //not thread safe, so don't bother showing messages. + #define STB_IMAGE_IMPLEMENTATION + #include "../libs/stb_image.h" //from https://raw.githubusercontent.com/nothings/stb/master/stb_image.h + #endif + #if defined(STBIW_ONLY_PNG) || defined(STBIW_ONLY_JPEG) || defined(STBIW_ONLY_BMP) || defined(STBIW_ONLY_TGA) || defined(STBIW_ONLY_HDR) + #define STBI_WRITE_NO_STDIO + #define STB_IMAGE_WRITE_STATIC + #define STBIWDEF fte_inlinestatic + #define STB_IMAGE_WRITE_IMPLEMENTATION + #include "../libs/stb_image_write.h" //from https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h + #endif +#endif + +#ifdef IMAGEFMT_GIF + #pragma message("IMAGEFMT_GIF requires AVAIL_STBI") + #undef IMAGEFMT_GIF +#endif +#if defined(IMAGEFMT_PNG) && !defined(AVAIL_PNGLIB) + #pragma message("IMAGEFMT_PNG requires AVAIL_PNGLIB or AVAIL_STBI") + #undef IMAGEFMT_PNG +#elif defined(AVAIL_PNGLIB) + #undef AVAIL_PNGLIB +#endif +#if defined(IMAGEFMT_JPG) && !defined(AVAIL_JPEGLIB) + #pragma message("IMAGEFMT_JPG requires AVAIL_JPEGLIB or AVAIL_STBI") + #undef IMAGEFMT_JPG +#elif defined(AVAIL_JPEGLIB) + #undef AVAIL_JPEGLIB +#endif + static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename); static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue); cvar_t r_imageextensions = CVARCD("r_imageextensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which might exist on disk (note that this does not list all supported formats, only the extensions that should be searched for)."); @@ -119,6 +217,7 @@ static void GenerateXMPData(char *blob, size_t blobsize, int width, int height, #endif #endif +#ifdef IMAGEFMT_TGA #ifndef _WIN32 #include #endif @@ -1082,6 +1181,7 @@ qboolean WriteTGA(char *filename, enum fs_relative fsroot, const qbyte *fte_rest } return success; } +#endif #ifdef AVAIL_PNGLIB #ifndef AVAIL_ZLIB @@ -3368,6 +3468,117 @@ static void *ReadPSDFile(qbyte *buf, size_t len, const char *fname, int *outwidt } #endif +#ifdef IMAGEFMT_EXR +#if 0 + #include "OpenEXR/ImfCRgbaFile.h" +#else + typedef void ImfInputFile; + typedef void ImfHeader; + typedef void ImfRgba; +#endif +static struct +{ + void *handle; + ImfInputFile *(*OpenInputFile) (const char name[]); + int (*CloseInputFile) (ImfInputFile *in); + int (*InputSetFrameBuffer) (ImfInputFile *in, ImfRgba *base, size_t xStride, size_t yStride); + int (*InputReadPixels) (ImfInputFile *in, int scanLine1, int scanLine2); + const ImfHeader*(*InputHeader) (const ImfInputFile *in); + + void (*HeaderDataWindow) (const ImfHeader *hdr, int *xMin, int *yMin, int *xMax, int *yMax); +} exr; +static void InitLibrary_OpenEXR(void) +{ +#ifdef IMF_MAGIC + exr.OpenInputFile = ImfOpenInputFile; + exr.CloseInputFile = ImfCloseInputFile; + exr.InputSetFrameBuffer = ImfInputSetFrameBuffer; + exr.InputReadPixels = ImfInputReadPixels; + exr.InputHeader = ImfInputHeader; + exr.HeaderDataWindow = ImfHeaderDataWindow; +#else + dllfunction_t funcs[] = + { + {(void**)&exr.OpenInputFile, "ImfOpenInputFile"}, + {(void**)&exr.CloseInputFile, "ImfCloseInputFile"}, + {(void**)&exr.InputSetFrameBuffer, "ImfInputSetFrameBuffer"}, + {(void**)&exr.InputReadPixels, "ImfInputReadPixels"}, + {(void**)&exr.InputHeader, "ImfInputHeader"}, + {(void**)&exr.HeaderDataWindow, "ImfHeaderDataWindow"}, + {NULL} + }; + #ifdef __linux__ + //its some shitty c++ library, so while the C api that we use is stable, nothing else is so it has lots of random names. + if (!exr.handle) + exr.handle = Sys_LoadLibrary("libIlmImf-2_3.so.24", funcs); //debian sid(bullseye) + if (!exr.handle) + exr.handle = Sys_LoadLibrary("libIlmImf-2_2.so.23", funcs); //debian buster + if (!exr.handle) + exr.handle = Sys_LoadLibrary("libIlmImf-2_2.so.22", funcs); //debian stretch + #endif + + //try the generic/dev name. + if (!exr.handle) + exr.handle = Sys_LoadLibrary("libIlmImf", funcs); +#endif +} +#ifdef _WIN32 +#include +#endif +static void *ReadEXRFile(qbyte *buf, size_t len, const char *fname, int *outwidth, int *outheight, uploadfmt_t *outformat) +{ + char tname[] = "/tmp/exr.XXXXXX"; +#ifndef _WIN32 + int fd; +#endif + ImfInputFile *ctx; + const ImfHeader *hdr; + void *result; + + if (!exr.handle) + { + Con_Printf("%s: libIlmImf not loaded\n", fname); + return NULL; + } + + //shitty API that only supports filenames, so now that we've read it from a file, write it to a file so that it can be read. See, shitty. +#ifdef _WIN32 + if (!_mktemp(tname)) + { + DWORD sizewritten; + HANDLE fd = CreateFileA(tname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL); + WriteFile(fd, buf, len, &sizewritten, NULL); //assume we managed to write it all + ctx = exr.OpenInputFile(tname); //is this be ansi or oem or utf-16 or what? lets assume that it has no idea either. + CloseHandle(fd); +#else + fd = mkstemp(tname); //bsd4.3/posix1-2001 + if (fd >= 0) + { + write(fd, buf, len); + ctx = exr.OpenInputFile(tname); + close(fd); //we don't need the input file now. + Sys_remove(tname); +#endif + + if (ctx) + { + int xmin, xmax, ymin, ymax; + hdr = exr.InputHeader(ctx); + exr.HeaderDataWindow(hdr, &xmin,&ymin, &xmax,&ymax); + *outwidth = (xmax-xmin)+1; + *outheight = (ymax-ymin)+1; + result = BZ_Malloc(sizeof(short)*4**outwidth**outheight); + exr.InputSetFrameBuffer(ctx, (char*)result-xmin*8-ymin**outwidth*8, 1, *outwidth); + exr.InputReadPixels(ctx, ymin, ymax); + exr.CloseInputFile(ctx); + *outformat = PTI_RGBA16F; //output is always half-floats. + return result; + } + } + return NULL; +} +#endif + #ifndef NPFTE @@ -3672,6 +3883,8 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc case PTI_L8A8_SRGB: header.glinternalformat = 0x8C45/*GL_SLUMINANCE8_ALPHA8*/; header.glbaseinternalformat = 0x190A/*GL_LUMINANCE_ALPHA*/; header.glformat = 0x190A/*GL_LUMINANCE_ALPHA*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_RGB8: header.glinternalformat = 0x8051/*GL_RGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_BGR8: header.glinternalformat = 0x8051/*GL_RGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x80E0/*GL_BGR*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; + case PTI_RGB8_SRGB: header.glinternalformat = 0x8C41/*GL_SRGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; + case PTI_BGR8_SRGB: header.glinternalformat = 0x8C41/*GL_SRGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x80E0/*GL_BGR*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_R16: header.glinternalformat = 0x822A/*GL_R16*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1403/*GL_UNSIGNED_SHORT*/; header.gltypesize = 2; break; case PTI_RGBA16: header.glinternalformat = 0x805B/*GL_RGBA16*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1403/*GL_UNSIGNED_SHORT*/; header.gltypesize = 2; break; case PTI_R16F: header.glinternalformat = 0x822D/*GL_R16F*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x140B/*GL_HALF_FLOAT*/; header.gltypesize = 2; break; @@ -4597,6 +4810,8 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc case PTI_L8A8_SRGB: return false; //unsupported case PTI_RGB8: return false; //unsupported case PTI_BGR8: return false; //unsupported + case PTI_RGB8_SRGB: return false; //unsupported + case PTI_BGR8_SRGB: return false; //unsupported case PTI_R16: h10.dxgiformat = 56/*DXGI_FORMAT_R16_UNORM*/; break; case PTI_RGBA16: h10.dxgiformat = 11/*DXGI_FORMAT_R16G16B16A16_UNORM*/; break; case PTI_R16F: h10.dxgiformat = 54/*DXGI_FORMAT_R16_FLOAT*/; break; @@ -5060,16 +5275,120 @@ static struct pendingtextureinfo *Image_ReadVTFFile(unsigned int flags, const ch } #endif +//This is for the version command +void Image_PrintInputFormatVersions(void) +{ + Con_Printf("^3Image Formats:^7"); + + #ifdef IMAGEFMT_DDS + Con_Printf(" dds"); + #endif + #ifdef IMAGEFMT_KTX + Con_Printf(" ktx"); + #endif + #ifdef IMAGEFMT_VTF + Con_Printf(" vtf"); + #endif + #ifdef IMAGEFMT_TGA + Con_Printf(" tga"); + #endif + #ifdef AVAIL_PNGLIB + Con_Printf(" png"); + #ifdef DYNAMIC_LIBPNG + if (!LIBPNG_LOADED()) + Con_Printf("^h(unavailable, %s)", PNG_LIBPNG_VER_STRING); + else + Con_Printf("^h(dynamic, %s)", PNG_LIBPNG_VER_STRING); + #else + Con_Printf("^h(%s)", PNG_LIBPNG_VER_STRING); + #endif + #endif + #ifdef IMAGEFMT_BMP + Con_Printf(" bmp+ico"); + #endif + #ifdef AVAIL_JPEGLIB + Con_Printf(" jpeg"); + #ifdef DYNAMIC_LIBJPEG + if (!LIBJPEG_LOADED()) + Con_Printf("^h(unavailable, %s)", PNG_LIBPNG_VER_STRING); + else + Con_Printf("^h(dynamic, %i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) ); + #else + Con_Printf("^h(%i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) ); + #endif + #endif + #ifdef IMAGEFMT_BLP + Con_Printf(" BLP"); + #endif + #ifdef IMAGEFMT_PBM + Con_Printf(" pfm+pbm+pgm+ppm"/*"+pam"*/); + #endif + #ifdef IMAGEFMT_PSD + Con_Printf(" psd"); + #endif + #ifdef IMAGEFMT_HDR + Con_Printf(" hdr"); + #endif + #ifdef IMAGEFMT_ASTC + Con_Printf(" astc"); + #endif + #ifdef IMAGEFMT_PKM + Con_Printf(" pkm"); + #endif + #ifdef IMAGEFMT_EXR + Con_Printf(" exr"); + if (!exr.handle) + Con_Printf("^h(unavailable)"); + #endif + #ifdef IMAGEFMT_PCX + Con_Printf(" pcx"); + #endif + + #ifdef STB_IMAGE_IMPLEMENTATION + #ifdef STBI_ONLY_PNG + Con_Printf(" png^h(stbi)"); + #endif + #ifdef STBI_ONLY_JPEG + Con_Printf(" jpeg^h(stbi)"); + #endif + #ifdef STBI_ONLY_BMP + Con_Printf(" bmp^h(stbi)"); + #endif + #ifdef STBI_ONLY_PSD + Con_Printf(" psd^h(stbi)"); + #endif + #ifdef STBI_ONLY_TGA + Con_Printf(" tga^h(stbi)"); + #endif + #ifdef STBI_ONLY_GIF + Con_Printf(" gif^h(stbi)"); + #endif + #ifdef STBI_ONLY_HDR + Con_Printf(" hdr^h(stbi)"); + #endif + #ifdef STBI_ONLY_PIC + Con_Printf(" pic^h(stbi)"); + #endif + #ifdef STBI_ONLY_PNM + Con_Printf(" pnm^h(stbi)"); + #endif + #endif + + Con_Printf("\n"); +} + //if force_rgba8 then it guarentees rgba8 or rgbx8, otherwise can return l8, etc qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_t *format, qboolean force_rgba8, const char *fname) { qbyte *data; *format = PTI_RGBX8; +#ifdef IMAGEFMT_TGA if ((data = ReadTargaFile(buf, len, width, height, format, false, force_rgba8?PTI_RGBA8:PTI_INVALID))) { TRACE(("dbg: Read32BitImageFile: tga\n")); return data; } +#endif #ifdef AVAIL_PNGLIB if (len > 4 && (buf[0] == 137 && buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G') && (data = ReadPNGFile(fname, buf, len, width, height, format))) @@ -5124,7 +5443,40 @@ qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_ return data; #endif -#if 1//def IMAGEFMT_LMP +#ifdef IMAGEFMT_EXR + if (len > 4 && (buf[0]|(buf[1]<<8)|(buf[2]<<16)|(buf[3]<<24)) == 20000630 && (data = ReadEXRFile(buf, len, fname, width, height, format))) + return data; +#endif + +#ifdef STB_IMAGE_IMPLEMENTATION + { + int components; + //Note: I don't like passing STBI_default, because it'll return 24bit data which we'll then have to pad anyway. + //but there's no other easy way to check how many channels are actually valid. + if ((data = stbi_load_from_memory(buf, len, width, height, &components, STBI_default))) + { + switch(components) + { + case STBI_grey: + *format = PTI_L8; + return data; + case STBI_grey_alpha: + *format = PTI_L8A8; + return data; + case STBI_rgb: + *format = PTI_RGB8; + return data; + case STBI_rgb_alpha: + *format = PTI_RGBA8; + return data; + } + //erk...? + stbi_image_free(data); + } + } +#endif + +#ifdef IMAGEFMT_LMP if (len >= 8) //.lmp has no magic id. guess at it. { int w = LittleLong(((int*)buf)[0]); @@ -5278,6 +5630,58 @@ static struct #endif }; +static void Image_MipMap3X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) +{ + int i, j; + qbyte *inrow; + + int rowwidth = inwidth*3; //rowwidth is the byte width of the input + inrow = in; + + //mips round down, except for when the input is 1. which bugs out. + if (inwidth <= 1 && inheight <= 1) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + } + else if (inheight <= 1) + { + //single row, don't peek at the next + for (in = inrow, j=0 ; j>1; + out[1] = (in[1] + in[4])>>1; + out[2] = (in[2] + in[5])>>1; + } + } + else if (inwidth <= 1) + { + //single colum, peek only at this pixel + for (i=0 ; i>1; + out[1] = (in[1] + in[rowwidth+1])>>1; + out[2] = (in[2] + in[rowwidth+2])>>1; + } + } + } + else + { + for (i=0 ; i>2; + out[1] = (in[1] + in[4] + in[rowwidth+1] + in[rowwidth+4])>>2; + out[2] = (in[2] + in[5] + in[rowwidth+2] + in[rowwidth+5])>>2; + } + } + } +} + static void Image_MipMap4X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) { int i, j; @@ -5391,6 +5795,101 @@ static void Image_MipMap4X16 (unsigned short *in, int inwidth, int inheight, uns } } +static float HalfToFloat(unsigned short val) +{ + union + { + float f; + unsigned int u; + } u; + u.u = (((val&0x7c00)>>10)-15+127)<<23; //read exponent, rebias it, and reshift. + u.u |= ((val & 0x3ff)<<13) | ((val & 0x3ff)<<3) | ((val & 0x3ff)>>7); //shift up the mantissa, and fold a little + u.u |= (val&0x8000)<<16; //retain the sign bit. + return u.f; +} +static unsigned short FloatToHalf(float val) +{ + union + { + float f; + unsigned int u; + } u = {val}; + int e = 0; + int m; + + e = ((u.u>>23)&0xff) - 127; + if (e < -15) + return 0; //too small exponent, treat it as a 0 denormal + if (e > 15) + m = 0; //infinity instead of a nan + else + m = (u.u&((1<<23)-1))>>13; + return ((u.u>>16)&0x8000) | ((e+15)<<10) | m; +} +fte_inlinestatic unsigned short HalfFloatBlend2(unsigned short a, unsigned short b) +{ + return FloatToHalf((HalfToFloat(a) + HalfToFloat(b))/2); +} +fte_inlinestatic unsigned short HalfFloatBlend4(unsigned short a, unsigned short b, unsigned short c, unsigned short d) +{ + return FloatToHalf((HalfToFloat(a) + HalfToFloat(b) + HalfToFloat(c) + HalfToFloat(d))/4); +} +static void Image_MipMap4X16F (unsigned short *in, int inwidth, int inheight, unsigned short *out, int outwidth, int outheight) +{ + int i, j; + unsigned short *inrow; + + int rowwidth = inwidth*4; //rowwidth is the byte width of the input + inrow = in; + + //mips round down, except for when the input is 1. which bugs out. + if (inwidth <= 1 && inheight <= 1) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + } + else if (inheight <= 1) + { + //single row, don't peek at the next + for (in = inrow, j=0 ; jmipcount = mip+1; } break; + case PTI_RGBA16F: + for (mip = mips->mipcount; mip < 32; mip++) + { + mips->mip[mip].width = mips->mip[mip-1].width >> 1; + mips->mip[mip].height = mips->mip[mip-1].height >> 1; + mips->mip[mip].depth = 1; + if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1) + break; + if (mips->mip[mip].width < 1) + mips->mip[mip].width = 1; + if (mips->mip[mip].height < 1) + mips->mip[mip].height = 1; + mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height * sizeof(unsigned short)*4; + mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); + mips->mip[mip].needfree = true; + + Image_MipMap4X16F(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + mips->mipcount = mip+1; + } + break; case PTI_RGBA16: for (mip = mips->mipcount; mip < 32; mip++) { @@ -5617,6 +6136,29 @@ static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int fla mips->mipcount = mip+1; } break; + case PTI_RGB8_SRGB: + case PTI_BGR8_SRGB: + case PTI_RGB8: + case PTI_BGR8: + for (mip = mips->mipcount; mip < 32; mip++) + { + mips->mip[mip].width = mips->mip[mip-1].width >> 1; + mips->mip[mip].height = mips->mip[mip-1].height >> 1; + mips->mip[mip].depth = 1; + if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1) + break; + if (mips->mip[mip].width < 1) + mips->mip[mip].width = 1; + if (mips->mip[mip].height < 1) + mips->mip[mip].height = 1; + mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height * sizeof(qbyte)*4; + mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); + mips->mip[mip].needfree = true; + + Image_MipMap3X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + mips->mipcount = mip+1; + } + break; case PTI_RGBA8_SRGB: case PTI_RGBX8_SRGB: case PTI_BGRA8_SRGB: @@ -7913,6 +8455,8 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, case PTI_RGB8: case PTI_BGR8: + case PTI_RGB8_SRGB: + case PTI_BGR8_SRGB: b = 3; break; case PTI_L8: @@ -8051,6 +8595,8 @@ const char *Image_FormatName(uploadfmt_t fmt) case PTI_DEPTH24_8: return "DEPTH24_8"; case PTI_RGB8: return "RGB8"; case PTI_BGR8: return "BGR8"; + case PTI_RGB8_SRGB: return "RGB8_SRGB"; + case PTI_BGR8_SRGB: return "BGR8_SRGB"; case PTI_L8: return "L8"; case PTI_L8_SRGB: return "L8_SRGB"; case PTI_L8A8: return "L8A8"; @@ -9201,6 +9747,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_RGB565: case PTI_RGB8: case PTI_BGR8: + case PTI_RGB8_SRGB: + case PTI_BGR8_SRGB: case PTI_E5BGR9: case PTI_RGBX8: case PTI_BGRX8: @@ -9241,6 +9789,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag uploadfmt_t nf = PTI_MAX; switch(mips->encoding) { + case PTI_R8: nf = PTI_INVALID; break; case PTI_L8: nf = PTI_L8_SRGB; break; case PTI_L8A8: nf = PTI_L8A8_SRGB; break; case PTI_LLLX8: nf = PTI_RGBX8_SRGB; break; @@ -9249,6 +9798,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_RGBX8: nf = PTI_RGBX8_SRGB; break; case PTI_BGRA8: nf = PTI_BGRA8_SRGB; break; case PTI_BGRX8: nf = PTI_BGRX8_SRGB; break; + case PTI_RGB8: nf = PTI_RGB8_SRGB; break; + case PTI_BGR8: nf = PTI_BGR8_SRGB; break; case PTI_BC1_RGB: nf = PTI_BC1_RGB_SRGB; break; case PTI_BC1_RGBA: nf = PTI_BC1_RGBA_SRGB; break; case PTI_BC2_RGBA: nf = PTI_BC2_RGBA_SRGB; break; @@ -9272,6 +9823,16 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_ASTC_10X10_LDR:nf = PTI_ASTC_10X10_SRGB; break; case PTI_ASTC_12X10_LDR:nf = PTI_ASTC_12X10_SRGB; break; case PTI_ASTC_12X12_LDR:nf = PTI_ASTC_12X12_SRGB; break; + + //these formats are inherantly linear. oh well. + case PTI_R16: + case PTI_R16F: + case PTI_R32F: + case PTI_RGBA16: + case PTI_RGBA16F: + case PTI_RGBA32F: + nf = mips->encoding; + break; default: if (freedata) BZ_Free(rgbadata); @@ -9283,8 +9844,12 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag { //srgb->linear int m = mips->mip[0].width*mips->mip[0].height*mips->mip[0].depth; - switch(nf) + switch(mips->encoding) { + case PTI_RGB8: + case PTI_BGR8: + m*=3; + //fallthrough case PTI_R8: case PTI_L8: for (i = 0; i < m; i++) @@ -9296,7 +9861,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag ((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255)); break; case PTI_R16: - for (i = 0; i < m; i+=4) + for (i = 0; i < m; i++) ((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff)); break; case PTI_RGBA16: @@ -9406,6 +9971,18 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag } } break; + case PTI_RGBA16F: + { + unsigned short *fte_restrict premul = (unsigned short*)mips->mip[0].data; + for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4) + { + float a = HalfToFloat(premul[3]); + premul[0] = FloatToHalf(HalfToFloat(premul[0]) * a); + premul[1] = FloatToHalf(HalfToFloat(premul[1]) * a); + premul[2] = FloatToHalf(HalfToFloat(premul[2]) * a); + } + } + break; case PTI_RGBA16: { unsigned short *fte_restrict premul = (unsigned short*)mips->mip[0].data; @@ -9458,7 +10035,7 @@ static qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawd { struct pendingtextureinfo *mips; mips = Z_Malloc(sizeof(*mips)); - mips->type = (flags & IF_3DMAP)?PTI_3D:PTI_2D; + mips->type = (flags&IF_TEXTYPE)>>IF_TEXTYPESHIFT; if (!Image_GenMip0(mips, flags, rawdata, palettedata, imgwidth, imgheight, fmt, true)) { @@ -9519,6 +10096,37 @@ static struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char mips = Image_ReadASTCFile(flags, fname, filedata, filesize); #endif +#ifdef STBI_ONLY_GIF + if (!mips) + { + int *delays = NULL; + int w, h, d; + int comp; + stbi_uc *data = stbi_load_gif_from_memory(filedata, filesize, &delays, &w, &h, &d, &comp, 4); //force 4, we're not really ready for other types of 2d arrays. + if (data) + { + mips = Z_Malloc(sizeof(*mips)); + mips->mipcount = 1; //this format doesn't support mipmaps. so there's only one level. + mips->type = PTI_2D_ARRAY; //2d arrays are basically just a 3d texture with weird mips (meaning they can load with gl's glTexStorage3D + mips->extrafree = delays; + switch(comp) + { + case 1: mips->encoding = PTI_L8; break; + case 2: mips->encoding = PTI_L8A8; break; + case 3: mips->encoding = PTI_RGB8; break; + case 4: mips->encoding = PTI_RGBA8; break; + default: mips->encoding = PTI_INVALID; break; + } + mips->mip[0].data = data; + mips->mip[0].datasize = comp*w*h*d; + mips->mip[0].width = w; + mips->mip[0].height = h; + mips->mip[0].depth = d; + mips->mip[0].needfree = true; + } + } +#endif + //the above formats are assumed to have consumed filedata somehow (probably storing into mips->extradata) if (mips) { @@ -10142,6 +10750,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t VFS_READ(f, buf, fsize); VFS_CLOSE(f); +#ifdef IMAGEFMT_TGA if (locflags & IF_TRYBUMP) { //it was supposed to be a heightmap image (that we need to convert to normalmap) qbyte *d; @@ -10159,6 +10768,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t } //guess not, fall back to normalmaps } +#endif if (Image_LoadTextureFromMemory(tex, tex->flags, tex->ident, fname, buf, fsize)) { @@ -10785,6 +11395,21 @@ void Image_Formats_f(void) //may not create any images yet. void Image_Init(void) { + static qboolean initedlibs; + if (!initedlibs) + { + initedlibs = true; + #ifdef AVAIL_JPEGLIB + LibJPEG_Init(); + #endif + #ifdef AVAIL_PNGLIB + LibPNG_Init(); + #endif + #ifdef IMAGEFMT_EXR + InitLibrary_OpenEXR(); + #endif + } + wadmutex = Sys_CreateMutex(); memset(imagetablebuckets, 0, sizeof(imagetablebuckets)); Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets); @@ -11046,6 +11671,14 @@ int MipColor(int r, int g, int b) return best; } +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION +static void stbiwritefunc(void *context, void *data, int size) +{ + vfsfile_t *f = context; + VFS_WRITE(f, data, size); +} +#endif + qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, qintptr_t bytestride, int width, int height, enum uploadfmt fmt, qboolean writemeta) { char ext[8]; @@ -11070,6 +11703,70 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, COM_FileExtension(filename, ext, sizeof(ext)); +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + if (numbuffers == 1) + { + vfsfile_t *outfile = NULL; + qboolean ret = false; + int comp = 0; + if (comp == PTI_L8) + comp = 1; + else if (comp == PTI_L8A8) + comp = 2; + else if (comp == PTI_RGB8) + comp = 3; + else if (comp == PTI_RGBA8) + comp = 4; + else if (comp == PTI_RGBA32F) + comp = -4; + else if (comp == PTI_R32F) + comp = -1; + +#ifdef STBIW_ONLY_PNG + if (!Q_strcasecmp(ext, "png") && comp>0) + { //does NOT support pngs + //lacks metadata + outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY); + ret = stbi_write_png_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0], bytestride); + } +#endif +#ifdef STBIW_ONLY_JPEG + if ((!Q_strcasecmp(ext, "jpg") || !Q_strcasecmp(ext, "jpeg")) && comp>0 && bytestride == width*sizeof(float)*comp) + { + //lacks metadata + outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY); + ret = stbi_write_jpg_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0], scr_sshot_compression.value/*its in percent*/); + } +#endif +#ifdef STBIW_ONLY_HDR + if (!Q_strcasecmp(ext, "hdr") && comp<0 && bytestride == width*sizeof(float)*-comp) + { + outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY); + ret = stbi_write_hdr_to_func(stbiwritefunc, outfile, width, height, -comp, buffer[0]); + } +#endif +#ifdef STBIW_ONLY_TGA + if (!Q_strcasecmp(ext, "tga") && comp>0 && bytestride == width*sizeof(float)*comp) + { + outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY); + ret = stbi_write_tga_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0]); + } +#endif +#ifdef STBIW_ONLY_BMP + if (!Q_strcasecmp(ext, "bmp") && comp>0 && bytestride == width*sizeof(float)*comp) + { + outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY); + ret = stbi_write_bmp_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0]); + } +#endif + + if (outfile) + { + VFS_CLOSE(outfile); + return ret; + } + } +#endif #ifdef AVAIL_PNGLIB if (!Q_strcasecmp(ext, "png") || !Q_strcasecmp(ext, "pns")) { @@ -11078,21 +11775,18 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, //actual stereo is also supported. huzzah. return Image_WritePNG(filename, fsroot, scr_sshot_compression.value, buffer, numbuffers, bytestride, width, height, fmt, writemeta); } - else #endif #ifdef AVAIL_JPEGLIB if (!Q_strcasecmp(ext, "jpeg") || !Q_strcasecmp(ext, "jpg") || !Q_strcasecmp(ext, "jps")) { return screenshotJPEG(filename, fsroot, scr_sshot_compression.value, buffer[0], bytestride, width, height, fmt, writemeta); } - else #endif #ifdef IMAGEFMT_BMP if (!Q_strcasecmp(ext, "bmp")) { return WriteBMPFile(filename, fsroot, buffer[0], bytestride, width, height, fmt); } - else #endif #ifdef IMAGEFMT_PCX if (!Q_strcasecmp(ext, "pcx")) @@ -11137,13 +11831,15 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, WritePCXfile (filename, fsroot, dstbuf, width, height, width, host_basepal, false); free(dstbuf); + return true; } - else #endif +#ifdef IMAGEFMT_TGA if (!Q_strcasecmp(ext, "tga")) //tga return WriteTGA(filename, fsroot, buffer[0], bytestride, width, height, fmt); +#endif #ifdef IMAGEFMT_KTX - else if (!Q_strcasecmp(ext, "ktx") && bytestride > 0) //ktx + if (!Q_strcasecmp(ext, "ktx") && bytestride > 0) //ktx { struct pendingtextureinfo out = {PTI_2D}; out.encoding = fmt; @@ -11157,7 +11853,7 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, } #endif #ifdef IMAGEFMT_DDS - else if (!Q_strcasecmp(ext, "dds") && bytestride > 0) //dds + if (!Q_strcasecmp(ext, "dds") && bytestride > 0) //dds { struct pendingtextureinfo out = {PTI_2D}; out.encoding = fmt; @@ -11170,7 +11866,7 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, return Image_WriteDDSFile(filename, fsroot, &out); } #endif - else //extension / type not recognised. - return false; - return true; + + //extension / type not recognised. + return false; } diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 751c6900..f6fcdeef 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -1923,7 +1923,7 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode) if (currentmenu->key(key, currentmenu)) return; - if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN || key == K_GP_A || key == K_GP_B || key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_TAB)) + if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN || key == K_GP_A || key == K_GP_B || key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_TAB || key == K_MWHEELUP || key == K_MWHEELDOWN)) if (currentmenu->selecteditem->custom.key) if (currentmenu->selecteditem->custom.key(¤tmenu->selecteditem->custom, currentmenu, key, unicode)) return; diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index ac5fc68c..06144d43 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -1647,9 +1647,10 @@ struct cin_s #endif struct { - qbyte *filmimage; //rgba - int imagewidth; - int imageheight; + qbyte *data; + uploadfmt_t format; + int width; + int height; } image; struct { @@ -2276,14 +2277,14 @@ static cin_t *Media_RoQ_TryLoad(char *name) #ifndef MINIMAL static void Media_Static_Shutdown(struct cin_s *cin) { - BZ_Free(cin->image.filmimage); - cin->image.filmimage = NULL; + BZ_Free(cin->image.data); + cin->image.data = NULL; } static qboolean Media_Static_DecodeFrame (cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx) { if (forcevideo) - uploadtexture(ctx, TF_RGBA32, cin->image.imagewidth, cin->image.imageheight, cin->image.filmimage, NULL); + uploadtexture(ctx, cin->image.format, cin->image.width, cin->image.height, cin->image.data, NULL); return true; } @@ -2313,21 +2314,10 @@ static cin_t *Media_Static_TryLoad(char *name) return NULL; } - if ((staticfilmimage = ReadPCXFile(file, fsize, &imagewidth, &imageheight)) || //convert to 32 rgba if not corrupt - (staticfilmimage = ReadTargaFile(file, fsize, &imagewidth, &imageheight, &format, false, PTI_RGBA8)) || -#ifdef AVAIL_JPEGLIB - (staticfilmimage = ReadJPEGFile(file, fsize, &imagewidth, &imageheight)) || -#endif -#ifdef AVAIL_PNGLIB - (staticfilmimage = ReadPNGFile(fullname, file, fsize, &imagewidth, &imageheight, &format)) || -#endif - 0) + staticfilmimage = ReadRawImageFile(file, fsize, &imagewidth, &imageheight, &format, false, fullname); + FS_FreeFile(file); //done with the file now + if (!staticfilmimage) { - FS_FreeFile(file); //got image data - } - else - { - FS_FreeFile(file); //got image data Con_Printf("Static cinematic format not supported.\n"); //not supported format return NULL; } @@ -2336,9 +2326,10 @@ static cin_t *Media_Static_TryLoad(char *name) cin->decodeframe = Media_Static_DecodeFrame; cin->shutdown = Media_Static_Shutdown; - cin->image.filmimage = staticfilmimage; - cin->image.imagewidth = imagewidth; - cin->image.imageheight = imageheight; + cin->image.data = staticfilmimage; + cin->image.format = format; + cin->image.width = imagewidth; + cin->image.height = imageheight; return cin; } diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 840169ec..f784c326 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -880,6 +880,7 @@ const char *presetexec[] = "seta cl_fullpitch 1;seta maxpitch \"\";seta minpitch \"\";" //mimic quakespasm where possible. "seta r_graphics 1;" "seta r_renderscale 1;" + "seta gl_texture_anisotropic_filtering 0;" , // fast options (for deathmatch) "gl_texturemode ln;" diff --git a/engine/client/menu.c b/engine/client/menu.c index 7271d1f0..6ec8b1ca 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -131,25 +131,26 @@ void Menu_Push(menu_t *menu, qboolean prompt) } void Menu_PopAll(void) { - qboolean popped = false; - menu_t **link, *menu; - for (link = &topmenu; *link;) + menu_t **menus, *menu; + size_t count, i; + //first loop to count them + for (count = 0, menu = topmenu; menu; menu = menu->prev) { - menu = *link; if (menu->persist) - link = &(*link)->prev; - else - { - *link = menu->prev; - menu->prev = NULL; - if (menu->release) - menu->release(menu); - popped = true; - } + continue; + count++; } - - if (popped) - Menu_UpdateFocus(); + menus = alloca(sizeof(*menus)*count); + //second loop to track them + for (i = 0, menu = topmenu; i < count && menu; menu = menu->prev) + { + if (menu->persist) + continue; + menus[i++] = menu; + } + //third link to actually unlink them safely without unlinking multiple times etc (grr menuqc mods re-grabbing focus when closing) + for (i = 0; i < count; i++) + Menu_Unlink(menus[i]); } void Menu_Draw(void) diff --git a/engine/client/merged.h b/engine/client/merged.h index b65407ed..05ccb39b 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -131,6 +131,7 @@ extern void (*VID_DeInit) (void); extern char *(*VID_GetRGBInfo) (int *stride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); //if stride is negative, then the return value points to the last line intead of the first. this allows it to be freed normally. extern void (*VID_SetWindowCaption) (const char *msg); +extern void *SCR_ScreenShot_Capture (int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d); extern void SCR_Init (void); extern void SCR_DeInit (void); extern qboolean (*SCR_UpdateScreen) (void); diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 4fe59e30..3b510c35 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -7082,7 +7082,7 @@ void CSQC_Shutdown(void) PR_ReleaseFonts(kdm_game); PR_Common_Shutdown(csqcprogs, false); World_Destroy(&csqc_world); - csqcprogs->CloseProgs(csqcprogs); + csqcprogs->Shutdown(csqcprogs); csqc_world.progs = csqcprogs = NULL; } else diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index aa51f78a..d6cc602f 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -1465,14 +1465,15 @@ void QCBUILTIN PF_isdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_global //float clientstate(void) = #62; void QCBUILTIN PF_clientstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - if (isDedicated) - G_FLOAT(OFS_RETURN) = 0; + //menuqc was originally implemented in DP, so these return values follow NQ norms. + if (isDedicated) //unreachable + G_FLOAT(OFS_RETURN) = 0/*nq ca_dedicated*/; + else if ( cls.state >= ca_connected //we're on a server + || CL_TryingToConnect() //or we're trying to connect (avoids bugs with certain menuqc mods) + || sv_state>=ss_loading ) //or we're going to connect to ourselves once we get our act together + G_FLOAT(OFS_RETURN) = 2/*nq ca_connected*/; else - { - //fit in with netquake (we never run a menu.dat dedicated) - //we return 2 for trying to connect, to avoid bugs with certain menuqc mods - G_FLOAT(OFS_RETURN) = (cls.state >= ca_connected||CL_TryingToConnect()) ? 2 : 1; - } + G_FLOAT(OFS_RETURN) = 1/*nq ca_disconnected*/; } //too specific to the prinst's builtins. @@ -2688,7 +2689,7 @@ void MP_Shutdown (void) PR_ExecuteProgram(menu_world.progs, temp); PR_Common_Shutdown(menu_world.progs, false); - menu_world.progs->CloseProgs(menu_world.progs); + menu_world.progs->Shutdown(menu_world.progs); memset(&menu_world, 0, sizeof(menu_world)); PR_ReleaseFonts(kdm_menu); @@ -2857,7 +2858,6 @@ qboolean MP_Init (void) menuqc.keyevent = MP_KeyEvent; menuqc.joyaxis = MP_JoystickAxis; menuqc.release = MP_TryRelease; - menuqc.persist = true; //don't bother trying to kill it... menutime = Sys_DoubleTime(); if (!menu_world.progs) diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index 817bd043..35f32f9c 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -359,7 +359,7 @@ void COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, si qboolean COM_HasWork(void); void COM_WorkerFullSync(void); void COM_DestroyWorkerThread(void); -void COM_WorkerPartialSync(void *priorityctx, int *address, int value); +void COM_WorkerPartialSync(void *priorityctx, int *address, int value); //aka: while(*address==value)wait(); extern void *com_resourcemutex; //random mutex to simplify resource creation type stuff. void COM_WorkerAbort(char *message); //calls sys_error on the main thread, if running on a worker. #ifdef _DEBUG diff --git a/engine/client/render.h b/engine/client/render.h index d70ab64f..aef9496b 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -425,7 +425,7 @@ enum imageflags IF_CUBEMAP = 1<<11, /*waning - don't test directly*/ IF_2DARRAY = IF_3DMAP|IF_CUBEMAP, IF_TEXTYPE = (1<<10) | (1<<11), /*0=2d, 1=3d, 2=cubeface, 3=2d array texture*/ - IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2-7=cubeface*/ + IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2=cubeface, 3=array*/ IF_MIPCAP = 1<<12, //allow the use of d_mipcap IF_PREMULTIPLYALPHA = 1<<13, //rgb *= alpha @@ -468,6 +468,7 @@ void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, in void Image_Purge(void); //purge any textures which are not needed any more (releases memory, but doesn't give null pointers). void Image_Init(void); void Image_Shutdown(void); +void Image_PrintInputFormatVersions(void); //for version info qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips); qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips); void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight); diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 44f20777..95cc2ff8 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -729,13 +729,6 @@ void R_ToggleFullscreen_f(void) void Renderer_Init(void) { - #ifdef AVAIL_JPEGLIB - LibJPEG_Init(); - #endif - #ifdef AVAIL_PNGLIB - LibPNG_Init(); - #endif - currentrendererstate.renderer = NULL; qrenderer = QR_NONE; @@ -843,8 +836,12 @@ void Renderer_Init(void) Cmd_AddCommand("sky", R_ForceSky_f); //QS compat Cmd_AddCommand("loadsky", R_ForceSky_f);//DP compat +#ifdef IMAGEFMT_TGA Cvar_Register(&r_dodgytgafiles, "Hacky bug workarounds"); +#endif +#ifdef IMAGEFMT_PCX Cvar_Register(&r_dodgypcxfiles, "Hacky bug workarounds"); +#endif Cvar_Register(&r_dodgymiptex, "Hacky bug workarounds"); r_imageextensions.enginevalue = r_defaultimageextensions; Cvar_Register(&r_imageextensions, GRAPHICALNICETIES); diff --git a/engine/client/screen.h b/engine/client/screen.h index 0986c015..b55d0116 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -99,6 +99,8 @@ typedef enum uploadfmt PTI_BGRX8_SRGB, //no alpha channel PTI_RGB8, //24bit packed format. generally not supported PTI_BGR8, //24bit packed format. generally not supported + PTI_RGB8_SRGB, //24bit packed format. generally not supported + PTI_BGR8_SRGB, //24bit packed format. generally not supported PTI_L8, //8bit format. luminance gets flooded to all RGB channels. might be supported using swizzles. PTI_L8A8, //16bit format. L=luminance. might be supported using swizzles. PTI_L8_SRGB, //8bit format. luminance gets flooded to all RGB channels. might be supported using swizzles. diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index f0e8f18a..f1ec0f75 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -706,7 +706,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //I'm making my own restrict, because msvc's headers can't cope if I #define restrict to __restrict, and quite possibly other platforms too #if __STDC_VERSION__ >= 199901L #define fte_restrict restrict -#elif defined(_MSC_VER) && _MSC_VER >= 1400 +#elif defined(_MSC_VER) && _MSC_VER >= 1400 || __GNUC__ >= 4 #define fte_restrict __restrict #else #define fte_restrict @@ -748,7 +748,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //gcc will generally inline where it can - so long as its static. but that doesn't stop it warning #define fte_inline __attribute__((unused)) static #define fte_inlinebody static - #define fte_inlinestatic static + #if __GNUC__ > 5 + #define fte_inlinestatic static inline + #else + #define fte_inlinestatic static + #endif #else //make it static so we at least don't get errors (might still get warnings. see above) #define fte_inline static diff --git a/engine/common/common.c b/engine/common/common.c index b938535e..bab88f98 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -2117,7 +2117,7 @@ const char *COM_GetFileExtension (const char *in, const char *term) if (!term) term = in + strlen(in); - for (dot = term; dot >= in && *dot != '/' && *dot != '\\'; dot--) + for (dot = term-1; dot >= in && *dot != '/' && *dot != '\\'; dot--) { if (*dot == '.') return dot; @@ -4975,53 +4975,7 @@ static void COM_Version_f (void) #endif #ifdef HAVE_CLIENT - Con_Printf("^3Image Formats:^7"); - #ifdef IMAGEFMT_DDS - Con_Printf(" dds"); - #endif - #ifdef IMAGEFMT_KTX - Con_Printf(" ktx"); - #endif - Con_Printf(" tga"); - #if defined(AVAIL_PNGLIB) - Con_Printf(" png"); - #ifdef DYNAMIC_LIBPNG - Con_Printf("^h(dynamic, %s)", PNG_LIBPNG_VER_STRING); - #else - Con_Printf("^h(%s)", PNG_LIBPNG_VER_STRING); - #endif - #else - Con_DPrintf(" ^h(disabled: png)"); - #endif - #ifdef IMAGEFMT_BMP - Con_Printf(" bmp+ico"); - #endif - #if defined(AVAIL_JPEGLIB) - Con_Printf(" jpeg"); - #ifdef DYNAMIC_LIBJPEG - Con_Printf("^h(dynamic, %i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) ); - #else - Con_Printf("^h(%i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) ); - #endif - #else - Con_DPrintf(" ^h(disabled: jpeg)"); - #endif - #ifdef IMAGEFMT_PBM - Con_Printf(" pfm+pbm+pgm+ppm"/*"+pam"*/); - #endif - #ifdef IMAGEFMT_PSD - Con_Printf(" psd"); - #endif - #ifdef IMAGEFMT_HDR - Con_Printf(" hdr"); - #endif - #ifdef IMAGEFMT_PKM - Con_Printf(" pkm"); - #endif - #ifdef IMAGEFMT_PCX - Con_Printf(" pcx"); - #endif - Con_Printf("\n"); + Image_PrintInputFormatVersions(); Con_Printf("^3VoiceChat:^7"); #if !defined(VOICECHAT) diff --git a/engine/common/config_freecs.h b/engine/common/config_freecs.h index 389b16c2..e1a40e13 100644 --- a/engine/common/config_freecs.h +++ b/engine/common/config_freecs.h @@ -156,7 +156,7 @@ #define HAVE_MEDIA_ENCODER //capture/capturedemo work. #undef HAVE_SPEECHTOTEXT //windows speech-to-text thing -//FIXME +//FIXME: Stuff that Spike has added that Eukara needs to decide whether to keep or not. #define HAVE_OPUS //#define HAVE_SPEEX //#define HAVE_OPENSSL @@ -174,6 +174,18 @@ //#define USE_INTERNAL_ODE //#define USE_INTERNAL_BULLET //#define MENU_NATIVECODE +//#define DECOMPRESS_ASTC +//#define HAVE_HTTPSV +//#define IMAGEFMT_ASTC +//#define IMAGEFMT_JPG +//#define IMAGEFMT_GIF +//#define IMAGEFMT_PNG +#define IMAGEFMT_TGA +#define IMAGEFMT_LMP +//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data. +//#define MODELFMT_MDX +//#define MODELFMT_OBJ +//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies. #ifdef COMPILE_OPTS diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index 10cc6360..8a032555 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -95,12 +95,19 @@ #define IMAGEFMT_PSD //baselayer only. #define IMAGEFMT_HDR //an RGBE format. #define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. +#define IMAGEFMT_TGA //somewhat mandatory +#define IMAGEFMT_LMP //mandatory for quake +#define IMAGEFMT_PNG //common in quakeworld, useful for screenshots. +#define IMAGEFMT_JPG //common in quake3, useful for screenshots. +//#define IMAGEFMT_GIF //for the luls. loads as a texture2DArray //#define IMAGEFMT_BLP //legacy crap #define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls #define IMAGEFMT_PCX //paletted junk. required for qw player skins, q2 and a few old skyboxes. +#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data. //#define IMAGEFMT_VTF //hl2 image format #define AVAIL_PNGLIB //.png image format support (read+screenshots) #define AVAIL_JPEGLIB //.jpeg image format support (read+screenshots) +//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies. #define PACKAGE_TEXWAD //quake's image wad support #define AVAIL_FREETYPE //for truetype font rendering #define DECOMPRESS_ETC2 //decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan). diff --git a/engine/common/config_minimal.h b/engine/common/config_minimal.h index 0c7fde92..9ca85457 100644 --- a/engine/common/config_minimal.h +++ b/engine/common/config_minimal.h @@ -85,27 +85,38 @@ //#define PSKMODELS //unreal's interchange format. Undesirable in terms of load times. //#define HALFLIFEMODELS //horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons). //#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective. +//#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets). +//#define MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too //#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...). //Image formats //#define IMAGEFMT_KTX //Khronos TeXture. common on gles3 devices for etc2 compression //#define IMAGEFMT_PKM //file format generally written by etcpack or android's etc1tool. doesn't support mips. +//#define IMAGEFMT_ASTC //lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files. //#define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats. //#define IMAGEFMT_PSD //baselayer only. //#define IMAGEFMT_HDR //an RGBE format. //#define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. +#define IMAGEFMT_TGA //somewhat mandatory +#define IMAGEFMT_LMP //mandatory for quake +#define IMAGEFMT_PNG //common in quakeworld, useful for screenshots. +//#define IMAGEFMT_JPG //common in quake3, useful for screenshots. +//#define IMAGEFMT_GIF //for the luls. loads as a texture2DArray //#define IMAGEFMT_BLP //legacy crap //#define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls //#define IMAGEFMT_PCX //paletted junk. required for qw player skins, q2 and a few old skyboxes. +//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data. //#define IMAGEFMT_VTF //hl2 image format #define AVAIL_PNGLIB //.png image format support (read+screenshots) //#define AVAIL_JPEGLIB //.jpeg image format support (read+screenshots) +//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies. #define PACKAGE_TEXWAD //quake's image wad support //#define AVAIL_FREETYPE //for truetype font rendering //#define DECOMPRESS_ETC2 //decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan). //#define DECOMPRESS_S3TC //allows bc1-3 to work even when drivers don't support it. This is probably only an issue on mobile chips. WARNING: not entirely sure if all patents expired yet... //#define DECOMPRESS_RGTC //bc4+bc5 //#define DECOMPRESS_BPTC //bc6+bc7 +//#define DECOMPRESS_ASTC //ASTC, for drivers that don't support it properly. // Game/Gamecode Support //#define CSQC_DAT @@ -140,6 +151,7 @@ //#define HAVE_WINSSPI //on windows //#define FTPSERVER //sv_ftp cvar. //#define WEBCLIENT //uri_get+any internal downloads etc +#define HAVE_HTTPSV //net_enable_http/websocket //#define TCPCONNECT //support for playing over tcp sockets, instead of just udp. compatible with qizmo. //#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea. //#define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections. diff --git a/engine/common/config_nocompat.h b/engine/common/config_nocompat.h index e305fdaa..b05b0d59 100644 --- a/engine/common/config_nocompat.h +++ b/engine/common/config_nocompat.h @@ -83,27 +83,38 @@ //#define PSKMODELS //unreal's interchange format. Undesirable in terms of load times. //#define HALFLIFEMODELS //horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons). #define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective. +//#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets). +//#define MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too #define RAGDOLL //ragdoll support. requires RBE support (via a plugin...). //Image formats #define IMAGEFMT_KTX //Khronos TeXture. common on gles3 devices for etc2 compression //#define IMAGEFMT_PKM //file format generally written by etcpack or android's etc1tool. doesn't support mips. +//#define IMAGEFMT_ASTC //lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files. //#define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats. //#define IMAGEFMT_PSD //baselayer only. //#define IMAGEFMT_HDR //an RGBE format. #define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. +#define IMAGEFMT_TGA //somewhat mandatory +#define IMAGEFMT_LMP //mandatory for quake +#define IMAGEFMT_PNG //common in quakeworld, useful for screenshots. +//#define IMAGEFMT_JPG //common in quake3, useful for screenshots. +//#define IMAGEFMT_GIF //for the luls. loads as a texture2DArray //#define IMAGEFMT_BLP //legacy crap //#define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls //#define IMAGEFMT_PCX //paletted junk. required for qw player skins, q2 and a few old skyboxes. +//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data. #define IMAGEFMT_VTF //hl2 image format #define AVAIL_PNGLIB //.png image format support (read+screenshots) //#define AVAIL_JPEGLIB //.jpeg image format support (read+screenshots) +//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies. //#define PACKAGE_TEXWAD //quake's image wad support #define AVAIL_FREETYPE //for truetype font rendering #define DECOMPRESS_ETC2 //decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan). //#define DECOMPRESS_S3TC //allows bc1-3 to work even when drivers don't support it. This is probably only an issue on mobile chips. WARNING: not entirely sure if all patents expired yet... //#define DECOMPRESS_RGTC //bc4+bc5 //#define DECOMPRESS_BPTC //bc6+bc7 +//#define DECOMPRESS_ASTC //ASTC, for drivers that don't support it properly. // Game/Gamecode Support #define CSQC_DAT @@ -138,6 +149,7 @@ #define HAVE_WINSSPI //on windows //#define FTPSERVER //sv_ftp cvar. #define WEBCLIENT //uri_get+any internal downloads etc +#define HAVE_HTTPSV //net_enable_http/websocket //#define TCPCONNECT //support for playing over tcp sockets, instead of just udp. compatible with qizmo. //#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea. #define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections. diff --git a/engine/common/config_wastes.h b/engine/common/config_wastes.h index 6dd4cb4f..4a557538 100644 --- a/engine/common/config_wastes.h +++ b/engine/common/config_wastes.h @@ -172,15 +172,27 @@ #undef AVAIL_FREETYPE // for truetype font rendering #undef SERVER_DEMO_PLAYBACK //outdated crap -//FIXME +//FIXME: Stuff that Spike has added that eukara needs to decide whether to use or not. #define HAVE_OPUS //#define HAVE_OPENSSL //#define IMAGEFMT_HDR //#define IMAGEFMT_PBM //#define IMAGEFMT_PSD +#define IMAGEFMT_TGA +#define IMAGEFMT_LMP +//#define IMAGEFMT_PNG +//#define IMAGEFMT_JPG +//#define IMAGEFMT_GIF +//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data. +//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies. //#define IPLOG //#define AVAIL_BOTLIB //#define AVAIL_BZLIB +//#define DECOMPRESS_ASTC +//#define IMAGEFMT_ASTC +//#define HAVE_HTTPSV +//#define MODELFMT_MDX +//#define MODELFMT_OBJ #ifdef COMPILE_OPTS //things to configure qclib, which annoyingly doesn't include this file itself diff --git a/engine/common/fs.c b/engine/common/fs.c index 8c2c6b97..be853946 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -784,6 +784,7 @@ COM_Dir_f static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void *parm, searchpathfuncs_t *spath) { searchpath_t *s; + const char *ext; char link[512]; char *colour; flocation_t loc; @@ -806,43 +807,56 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void else if (loc.search->handle == spath) { colour = "^2"; - COM_FileExtension(name, link, sizeof(link)); - if ((!Q_strcasecmp(link, "bsp") || !Q_strcasecmp(link, "map") || !Q_strcasecmp(link, "hmp")) && !strncmp(name, "maps/", 5) && strncmp(name, "maps/b_", 7)) + + ext = COM_GetFileExtension(name, NULL); + if (!Q_strcasecmp(ext, ".gz")) + ext = COM_GetFileExtension(name, ext); + if (*ext == '.') + { + ext++; + if (strchr(ext, '.')) + { + COM_StripAllExtensions(ext, link, sizeof(link)); + ext = link; + } + } + + if ((!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "map") || !Q_strcasecmp(ext, "hmp")) && !strncmp(name, "maps/", 5) && strncmp(name, "maps/b_", 7)) { Q_snprintfz(link, sizeof(link), "\\tip\\Change Map\\map\\%s", name+5); colour = "^4"; //disconnects } - else if (!Q_strcasecmp(link, "bsp") || !Q_strcasecmp(link, "spr") || !Q_strcasecmp(link, "mdl") || !Q_strcasecmp(link, "md3") || !Q_strcasecmp(link, "iqm") || - !Q_strcasecmp(link, "vvm") || !Q_strcasecmp(link, "psk") || !Q_strcasecmp(link, "dpm") || !Q_strcasecmp(link, "zym") || !Q_strcasecmp(link, "md5mesh") || - !Q_strcasecmp(link, "mdx") || !Q_strcasecmp(link, "md2") || !Q_strcasecmp(link, "obj") || - !Q_strcasecmp(link, "md5anim") || !Q_strcasecmp(link, "gltf") || !Q_strcasecmp(link, "glb") || !Q_strcasecmp(link, "ase") || !Q_strcasecmp(link, "lwo")) + else if (!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "md3") || !Q_strcasecmp(ext, "iqm") || + !Q_strcasecmp(ext, "vvm") || !Q_strcasecmp(ext, "psk") || !Q_strcasecmp(ext, "dpm") || !Q_strcasecmp(ext, "zym") || !Q_strcasecmp(ext, "md5mesh") || + !Q_strcasecmp(ext, "mdx") || !Q_strcasecmp(ext, "md2") || !Q_strcasecmp(ext, "obj") || + !Q_strcasecmp(ext, "md5anim") || !Q_strcasecmp(ext, "gltf") || !Q_strcasecmp(ext, "glb") || !Q_strcasecmp(ext, "ase") || !Q_strcasecmp(ext, "lwo")) Q_snprintfz(link, sizeof(link), "\\tip\\Open in Model Viewer\\modelviewer\\%s", name); - else if (!Q_strcasecmp(link, "qc") || !Q_strcasecmp(link, "src") || !Q_strcasecmp(link, "qh") || !Q_strcasecmp(link, "h") || !Q_strcasecmp(link, "c") - || !Q_strcasecmp(link, "cfg") || !Q_strcasecmp(link, "rc") - || !Q_strcasecmp(link, "txt") || !Q_strcasecmp(link, "log") - || !Q_strcasecmp(link, "ent") || !Q_strcasecmp(link, "rtlights") - || !Q_strcasecmp(link, "glsl") || !Q_strcasecmp(link, "hlsl") - || !Q_strcasecmp(link, "shader") || !Q_strcasecmp(link, "framegroups") - || !Q_strcasecmp(link, "vmt") + else if (!Q_strcasecmp(ext, "qc") || !Q_strcasecmp(ext, "src") || !Q_strcasecmp(ext, "qh") || !Q_strcasecmp(ext, "h") || !Q_strcasecmp(ext, "c") + || !Q_strcasecmp(ext, "cfg") || !Q_strcasecmp(ext, "rc") + || !Q_strcasecmp(ext, "txt") || !Q_strcasecmp(ext, "log") + || !Q_strcasecmp(ext, "ent") || !Q_strcasecmp(ext, "rtlights") + || !Q_strcasecmp(ext, "glsl") || !Q_strcasecmp(ext, "hlsl") + || !Q_strcasecmp(ext, "shader") || !Q_strcasecmp(ext, "framegroups") + || !Q_strcasecmp(ext, "vmt") ) Q_snprintfz(link, sizeof(link), "\\tip\\Open in Text Editor\\edit\\%s", name); - else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg")|| !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "ico") || - !Q_strcasecmp(link, "pcx") || !Q_strcasecmp(link, "bmp") || !Q_strcasecmp(link, "dds") || !Q_strcasecmp(link, "ktx") || !Q_strcasecmp(link, "vtf") || !Q_strcasecmp(link, "psd") || - !Q_strcasecmp(link, "astc")|| !Q_strcasecmp(link, "htga")|| - !Q_strcasecmp(link, "pbm") || !Q_strcasecmp(link, "ppm") || !Q_strcasecmp(link, "pgm") || !Q_strcasecmp(link, "pam") || !Q_strcasecmp(link, "pfm") || !Q_strcasecmp(link, "hdr") ) + else if (!Q_strcasecmp(ext, "tga") || !Q_strcasecmp(ext, "png") || !Q_strcasecmp(ext, "jpg") || !Q_strcasecmp(ext, "jpeg")|| !Q_strcasecmp(ext, "lmp") || !Q_strcasecmp(ext, "ico") || + !Q_strcasecmp(ext, "pcx") || !Q_strcasecmp(ext, "bmp") || !Q_strcasecmp(ext, "dds") || !Q_strcasecmp(ext, "ktx") || !Q_strcasecmp(ext, "vtf") || !Q_strcasecmp(ext, "psd") || + !Q_strcasecmp(ext, "astc")|| !Q_strcasecmp(ext, "htga")|| !Q_strcasecmp(ext, "exr") || + !Q_strcasecmp(ext, "pbm") || !Q_strcasecmp(ext, "ppm") || !Q_strcasecmp(ext, "pgm") || !Q_strcasecmp(ext, "pam") || !Q_strcasecmp(ext, "pfm") || !Q_strcasecmp(ext, "hdr") ) { //FIXME: image replacements are getting in the way here. Q_snprintfz(link, sizeof(link), "\\tiprawimg\\%s\\tip\\(note: image replacement rules are context-dependant, including base path, sub path, extension, or complete replacement via a shader)", name); colour = "^6"; //shown on mouseover } - else if (!Q_strcasecmp(link, "qwd") || !Q_strcasecmp(link, "dem") || !Q_strcasecmp(link, "mvd") || !Q_strcasecmp(link, "dm2")) + else if (!Q_strcasecmp(ext, "qwd") || !Q_strcasecmp(ext, "dem") || !Q_strcasecmp(ext, "mvd") || !Q_strcasecmp(ext, "dm2")) { Q_snprintfz(link, sizeof(link), "\\tip\\Play Demo\\demo\\%s", name); colour = "^4"; //disconnects } - else if (!Q_strcasecmp(link, "roq") || !Q_strcasecmp(link, "cin") || !Q_strcasecmp(link, "avi") || !Q_strcasecmp(link, "mp4") || !Q_strcasecmp(link, "mkv")) + else if (!Q_strcasecmp(ext, "roq") || !Q_strcasecmp(ext, "cin") || !Q_strcasecmp(ext, "avi") || !Q_strcasecmp(ext, "mp4") || !Q_strcasecmp(ext, "mkv")) Q_snprintfz(link, sizeof(link), "\\tip\\Play Film\\film\\%s", name); - else if (!Q_strcasecmp(link, "wav") || !Q_strcasecmp(link, "ogg") || !Q_strcasecmp(link, "mp3") || !Q_strcasecmp(link, "opus") || !Q_strcasecmp(link, "flac")) + else if (!Q_strcasecmp(ext, "wav") || !Q_strcasecmp(ext, "ogg") || !Q_strcasecmp(ext, "mp3") || !Q_strcasecmp(ext, "opus") || !Q_strcasecmp(ext, "flac")) Q_snprintfz(link, sizeof(link), "\\tip\\Play Audio\\playaudio\\%s", name); else { diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index aed5bc50..d477a9c3 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -589,15 +589,15 @@ int PR_DPrintf (const char *fmt, ...) string_t PR_TempString(pubprogfuncs_t *prinst, const char *str) { char *tmp; - if (!prinst->tempstringbase) + if (!prinst->user.tempstringbase) return prinst->TempString(prinst, str); if (!str || !*str) return 0; - if (prinst->tempstringnum == MAX_TEMPSTRS) - prinst->tempstringnum = 0; - tmp = prinst->tempstringbase + (prinst->tempstringnum++)*MAXTEMPBUFFERLEN; + if (prinst->user.tempstringnum == MAX_TEMPSTRS) + prinst->user.tempstringnum = 0; + tmp = prinst->user.tempstringbase + (prinst->user.tempstringnum++)*MAXTEMPBUFFERLEN; Q_strncpyz(tmp, str, MAXTEMPBUFFERLEN); return tmp - prinst->stringtable; @@ -613,10 +613,10 @@ void PF_InitTempStrings(pubprogfuncs_t *prinst) pr_tempstringsize.flags |= CVAR_NOSET; if (pr_tempstringcount.value >= 2) - prinst->tempstringbase = prinst->AddString(prinst, "", MAXTEMPBUFFERLEN*MAX_TEMPSTRS, false); + prinst->user.tempstringbase = prinst->AddString(prinst, "", MAXTEMPBUFFERLEN*MAX_TEMPSTRS, false); else - prinst->tempstringbase = 0; - prinst->tempstringnum = 0; + prinst->user.tempstringbase = 0; + prinst->user.tempstringnum = 0; } //#define RETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e)) @@ -2925,7 +2925,7 @@ void QCBUILTIN PF_edict_for_num(pubprogfuncs_t *prinst, struct globalvars_s *pr_ else { G_INT(OFS_RETURN) = num; //just directly store it. if its not spawned yet we'll need to catch that elsewhere anyway. - if (G_WEDICT(prinst, OFS_RETURN)) + if (!G_WEDICT(prinst, OFS_RETURN)) RETURN_EDICT(prinst, w->edicts); //hoi! it wasn't valid! } } diff --git a/engine/common/world.h b/engine/common/world.h index fa2b8fc1..f4ab4d18 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -376,7 +376,7 @@ void Q23BSP_FindTouchedLeafs(model_t *mod, struct pvscache_s *ent, const float * /*sv_move.c*/ #if defined(CSQC_DAT) || !defined(CLIENTONLY) qboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up); -qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *prinst, trace_t *trace)); +qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *inst, trace_t *trace)); qboolean World_MoveToGoal (world_t *world, wedict_t *ent, float dist); qboolean World_GetEntGravityAxis(wedict_t *ent, vec3_t axis[3]); #endif diff --git a/engine/d3d/d3d11_image.c b/engine/d3d/d3d11_image.c index ff59e3b9..d673e446 100644 --- a/engine/d3d/d3d11_image.c +++ b/engine/d3d/d3d11_image.c @@ -197,6 +197,12 @@ qboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mi case PTI_BGR8: // tdesc.Format = DXGI_FORMAT_B8G8R8_UNORM; break; + case PTI_RGB8_SRGB: +// tdesc.Format = DXGI_FORMAT_R8G8B8_SRGB; + break; + case PTI_BGR8_SRGB: +// tdesc.Format = DXGI_FORMAT_B8G8R8_SRGB; + break; case PTI_RGBA8: tdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; break; diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index a7bae3ad..8c64257e 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -1753,11 +1753,10 @@ static texid_t Font_LoadFallbackConchars(void) texid_t tex; int width, height; unsigned int i; - qbyte *lump; uploadfmt_t format; - lump = ReadTargaFile(default_conchar, sizeof(default_conchar), &width, &height, &format, false, PTI_INVALID); + qbyte *lump = ReadRawImageFile(default_conchar, sizeof(default_conchar), &width, &height, &format, false, "conchars"); if (!lump || (format != PTI_RGBX8 && format != PTI_RGBA8 && format != PTI_LLLX8)) - Sys_Error("Corrupt internal drawchars (%i)", format); + return r_nulltex; /*convert greyscale to alpha*/ for (i = 0; i < width*height; i++) { diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index fe3aada5..99aad42a 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -736,6 +736,11 @@ static int Shader_SetImageFlags(parsestate_t *parsestate, shaderpass_t *pass, ch *name+=6; flags = (flags&~IF_TEXTYPE) | IF_CUBEMAP; } + else if (!Q_strnicmp(*name, "$2darray:", 9)) + { + *name+=9; + flags = (flags&~IF_TEXTYPE) | IF_2DARRAY; + } else if (!Q_strnicmp(*name, "$srgb:", 6)) { *name+=6; diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 9a61b61f..0daff786 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -2484,8 +2484,6 @@ static void install_grabs(void) { if (!mouse_grabbed) { - Con_DLPrintf(2, "Grabbing mouse\n"); - mouse_grabbed = true; //XGrabPointer can cause alt+tab type shortcuts to be skipped by the window manager. This means we don't want to use it unless we have no choice. //the grab is purely to constrain the pointer to the window if (GrabSuccess != x11.pXGrabPointer(vid_dpy, DefaultRootWindow(vid_dpy), @@ -2495,7 +2493,13 @@ static void install_grabs(void) vid_window, None, CurrentTime)) + { Con_Printf("Pointer grab failed\n"); + return; + } + + Con_DLPrintf(2, "Grabbed mouse\n"); + mouse_grabbed = true; if (x11_input_method == XIM_DGA) { @@ -2637,7 +2641,7 @@ static void GetEvent(void) default:button = 0; break; } if (button) - IN_KeyEvent(*qdev, (event.xcookie.evtype==XI_RawButtonPress), button, 0); + IN_KeyEvent(*qdev, (event.xcookie.evtype==XI_RawButtonPress), button, 0); } break; case XI_RawMotion: diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 3ee9251b..5ee3cacb 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -2958,6 +2958,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!cvardf r_tessellation_level=5\n" "!!samps !EIGHTBIT diffuse normalmap specular fullbright upper lower reflectmask reflectcube\n" "!!samps =EIGHTBIT paletted 1\n" +"!!samps =OCCLUDE occlusion\n" //!!permu VC // adds rgba vertex colour multipliers //!!permu SPECULAR // auto-added when gl_specular>0 //!!permu OFFSETMAPPING // auto-added when r_glsl_offsetmapping is set @@ -2966,6 +2967,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND //!!permu SG // specularmap is rgb:F0, a:Roughness (instead of exponent) //!!permu PBR // an attempt at pbr logic (enabled from ORM or SG) //!!permu NOOCCLUDE // ignores the use of ORM's occlusion... yeah, stupid. +//!!permu OCCLUDE // use an explicit occlusion texturemap (separate from roughness+metalness). //!!permu EIGHTBIT // uses software-style paletted colourmap lookups //!!permu ALPHATEST // if defined, this is the required alpha level (more versatile than doing it at the q3shader level) @@ -3274,12 +3276,12 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#define ambientrgb (specrgb+col.rgb)\n" "vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\n" "col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n" -"#elif defined(SG) //pbr-style specular+glossiness\n" +"#elif defined(SG) //pbr-style specular+glossiness, without occlusion\n" //occlusion needs to be baked in. :( "#define roughness (1.0-specs.a)\n" "#define gloss (specs.a)\n" "#define specrgb specs.rgb\n" -"#define ambientrgb (specs.rgb+col.rgb)\n" +"#define ambientrgb (specrgb+col.rgb)\n" "#else //blinn-phong\n" "#define roughness (1.0-specs.a)\n" "#define gloss specs.a\n" @@ -3323,7 +3325,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n" "#endif\n" -"#if defined(occlusion) && !defined(NOOCCLUDE)\n" +"#ifdef OCCLUDE\n" +"col.rgb *= texture2D(s_occlusion, tc).r; \n" +"#elif defined(occlusion) && !defined(NOOCCLUDE)\n" "col.rgb *= occlusion;\n" "#endif\n" "col *= light * e_colourident;\n" diff --git a/engine/qclib/decomp.c b/engine/qclib/decomp.c index f56738e5..1e24bfe2 100644 --- a/engine/qclib/decomp.c +++ b/engine/qclib/decomp.c @@ -664,7 +664,7 @@ int DecompileReadData(char *srcfilename, char *buf, size_t bufsize) } } else - Sys_Error("Unrecognised progs version"); + externs->Sys_Error("Unrecognised progs version"); numfunctions = progs.numfunctions; functions = (dfunction_t*)(buf+progs.ofs_functions); diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index f6c2654f..3fda280b 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -994,7 +994,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); OPC->_float = ptr->_float *= OPA->_float; @@ -1004,7 +1004,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); tmpf = OPA->_float; //don't break on vec*=vec_x; @@ -1020,7 +1020,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); OPC->_float = ptr->_float /= OPA->_float; @@ -1038,7 +1038,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); OPC->_float = ptr->_float += OPA->_float; @@ -1048,7 +1048,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); OPC->_vector[0] = ptr->_vector[0] += OPA->_vector[0]; @@ -1068,7 +1068,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); OPC->_float = ptr->_float -= OPA->_float; @@ -1078,7 +1078,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); OPC->_vector[0] = ptr->_vector[0] -= OPA->_vector[0]; @@ -1093,7 +1093,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); ptr->_float = (int)ptr->_float | (int)OPA->_float; @@ -1106,7 +1106,7 @@ reeval: errorif (QCPOINTERWRITEFAIL(i, sizeof(float))) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused); } ptr = QCPOINTERM(i); ptr->_float = (int)ptr->_float & ~(int)OPA->_float; @@ -1290,7 +1290,7 @@ reeval: errorif (OPB->_int < 0 || OPB->_int*4 >= current_progstate->globals_size) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, (unsigned)prinst.addressableused); } ptr = ((eval_t *)&glob[OPB->_int]); ptr->_int = OPA->_int; @@ -1299,7 +1299,7 @@ reeval: errorif (OPB->_int < 0 || (OPB->_int+2)*4 >= current_progstate->globals_size) { pr_xstatement = st-pr_statements; - PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, prinst.addressableused); + PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, (unsigned)prinst.addressableused); } ptr = ((eval_t *)&glob[OPB->_int]); ptr->_vector[0] = OPA->_vector[0]; diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 1884da3f..70053b55 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -128,7 +128,7 @@ void *PRAddressableExtend(progfuncs_t *progfuncs, void *src, size_t srcsize, int } if (prinst.addressableused + ammount > prinst.addressablesize) - Sys_Error("Not enough addressable memory for progs VM (using %gmb)", prinst.addressablesize/(1024.0*1024)); + externs->Sys_Error("Not enough addressable memory for progs VM (using %gmb)", prinst.addressablesize/(1024.0*1024)); } prinst.addressableused += ammount; @@ -136,7 +136,7 @@ void *PRAddressableExtend(progfuncs_t *progfuncs, void *src, size_t srcsize, int #if defined(_WIN32) && !defined(WINRT) if (!VirtualAlloc (prinst.addressablehunk, prinst.addressableused, MEM_COMMIT, PAGE_READWRITE)) - Sys_Error("VirtualAlloc failed. Blame windows."); + externs->Sys_Error("VirtualAlloc failed. Blame windows."); #endif ptr = &prinst.addressablehunk[prinst.addressableused-ammount]; @@ -472,7 +472,7 @@ void PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount) // memset(prinst.addressablehunk, 0xff, totalammount); #endif if (!prinst.addressablehunk) - Sys_Error("Out of memory\n"); + externs->Sys_Error("Out of memory\n"); prinst.addressablesize = totalammount; progfuncs->funcs.stringtablemaxsize = totalammount; } @@ -692,7 +692,7 @@ func_t PDECL PR_FindFunc(pubprogfuncs_t *ppf, const char *funcname, progsnum_t p return (f - ps->functions) | (pnum << 24); return *(int *)&ps->globals[var32->ofs]; } - Sys_Error("Error with def size (PR_FindFunc)"); + externs->Sys_Error("Error with def size (PR_FindFunc)"); } return 0; } @@ -791,7 +791,7 @@ eval_t *PDECL PR_FindGlobal(pubprogfuncs_t *ppf, const char *globname, progsnum_ *type = var32->type; return (eval_t *)&cp->globals[var32->ofs]; } - Sys_Error("Error with def size (PR_FindGlobal)"); + externs->Sys_Error("Error with def size (PR_FindGlobal)"); return NULL; } @@ -1006,13 +1006,14 @@ const char *ASMCALL PR_StringToNative (pubprogfuncs_t *ppf, string_t str) if (((unsigned int)str & STRING_SPECMASK) == STRING_TEMP) { unsigned int i = str & ~STRING_SPECMASK; - if (i >= prinst.numtempstrings || !prinst.tempstrings[i]) + tempstr_t *ts; + if (i >= prinst.maxtempstrings || !(ts=prinst.tempstrings[i])) { if (!progfuncs->funcs.debug_trace) PR_RunWarning(&progfuncs->funcs, "invalid temp string %x\n", str); return ""; } - return prinst.tempstrings[i]->value; + return ts->value; } if ((unsigned int)str >= (unsigned int)prinst.addressableused) @@ -1030,9 +1031,9 @@ eval_t *PR_GetReadTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t off if (((unsigned int)str & STRING_SPECMASK) != STRING_TEMP) { unsigned int i = str & ~STRING_SPECMASK; - if (i < prinst.numtempstrings && !prinst.tempstrings[i]) + tempstr_t *temp; + if (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i])) { - tempstr_t *temp = prinst.tempstrings[i]; if (offset + datasize < temp->size) return (eval_t*)(temp->value + offset); else @@ -1046,9 +1047,9 @@ eval_t *PR_GetWriteTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t of if (((unsigned int)str & STRING_SPECMASK) != STRING_TEMP) { unsigned int i = str & ~STRING_SPECMASK; - if (i < prinst.numtempstrings && !prinst.tempstrings[i]) + tempstr_t *temp; + if (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i])) { - tempstr_t *temp = prinst.tempstrings[i]; if (offset + datasize >= temp->size) { //access is beyond the current size. expand it. unsigned int newsize; @@ -1109,10 +1110,113 @@ void QCBUILTIN PF_memsetval (pubprogfuncs_t *inst, struct globalvars_s *globals) *(int*)(inst->stringtable + dst) = val; } +//#define GCTIMINGS +#ifdef THREADEDGC +#include "quakedef.h" +struct qcgccontext_s +{ + int done; + size_t clearedtemps; //number of temps that were swept away + progfuncs_t *progfuncs; //careful! + + size_t numtemps; //so it doesn't go stale + tempstr_t **tempstrings;//so we don't get confused over temps added while marking + + size_t memsize; + unsigned int amem[1]; +}; +void PR_QCGC_Done(void *ctx, void *data, size_t a, size_t b) +{ + struct qcgccontext_s *gc = ctx; + gc->done = true; +} +void PR_QCGC_Thread(void *ctx, void *data, size_t a, size_t b) +{ + struct qcgccontext_s *gc = ctx; + unsigned int p, r_d; + char *marked, *t; + unsigned int *str; + size_t numtemps = gc->numtemps; + +#ifdef GCTIMINGS + unsigned int r_l; + double starttime, markedtime, endtime; + starttime = Sys_DoubleTime(); +#endif + + marked = malloc(sizeof(*marked) * numtemps); + memset(marked, 0, sizeof(*marked) * numtemps); + + //mark everything the qc has access to, even if it isn't even a string! + //note that I did try specifically checking only data explicitly marked as a string type, but that was: + //a) a smidge slower (lots of extra loops and conditions I guess) + //b) doesn't work with pointers/structs (yes, we assume it'll all be aligned). + //c) both methods got the same number of false positives in my test (2, probably dead strunzoned references) + for (str = gc->amem, p = 0; p < gc->memsize; p+=sizeof(*str), str++) + { + if ((*str & STRING_SPECMASK) == STRING_TEMP) + { + unsigned int idx = *str &~ STRING_SPECMASK; + if (idx < numtemps) + marked[idx] = true; + } + } + +#ifdef GCTIMINGS + markedtime = Sys_DoubleTime(); +#endif + + //sweep +#ifdef GCTIMINGS + r_l = 0; +#endif + r_d = 0; + for (p = 0; p < numtemps; p++) + { + if (marked[p]) + { +#ifdef GCTIMINGS + r_l++; +#endif + } + else + break; + } +// prinst.nexttempstring = p; + for (; p < numtemps; p++) + { + if (marked[p]) + { //still live... +#ifdef GCTIMINGS + r_l++; +#endif + } + else if (gc->tempstrings[p]) + { //not marked, but was valid at the time our snapshot was taken + r_d++; + + //FIXME: Security Race: its possible for a mod to do weird manipulations to access the tempstring while we're still freeing it, allowing it to read something outside of its sandbox. + //one option would be to have the main thread bounce it back to the worker after its complete, so we can actually free the memory only after main thread has acknowledged that its tempstrings are nulled. + gc->prinst.tempstrings[p] = NULL; + + gc->externs->memfree(gc->tempstrings[p]); + } + } + gc->clearedtemps = r_d; + + free(marked); + +#ifdef GCTIMINGS + endtime = Sys_DoubleTime(); + gc->externs->Printf("live: %u, dead: %u, threadtime: mark=%f, sweep=%f, total=%f\n", r_l, r_d, (markedtime - starttime), (endtime - markedtime), endtime-starttime); +#endif + + COM_InsertWork(WG_MAIN, PR_QCGC_Done, gc, NULL, 0, 0); +} static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) { - progfuncs_t *progfuncs = (progfuncs_t*)ppf; + progfuncs_t *fte_restrict progfuncs = (progfuncs_t *)ppf; tempstr_t **ntable; int newmax; int i; @@ -1120,37 +1224,36 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, if (!str) return 0; - if (prinst.numtempstrings == prinst.maxtempstrings) + if (prinst.livetemps == prinst.maxtempstrings) { - newmax = prinst.maxtempstrings + 1024; + //need to wait for the gc to finish, otherwise it might be wiping freed strings that we're still using. + while (prinst.gccontext) + { + COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false); + PR_RunGC(progfuncs); + } + + newmax = prinst.maxtempstrings*2 + 1024; ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax); - memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings); -#ifdef QCGC - memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.numtempstrings)); -#endif + memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.maxtempstrings); + memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.maxtempstrings)); prinst.maxtempstrings = newmax; if (prinst.tempstrings) progfuncs->funcs.parms->memfree(prinst.tempstrings); prinst.tempstrings = ntable; } -#ifdef QCGC - if (prinst.nexttempstring >= 0x10000000) - return 0; - do + for (i = prinst.nexttempstring; i < prinst.maxtempstrings && prinst.tempstrings[i]; i++) + ; + if (i == prinst.maxtempstrings) { - i = prinst.nexttempstring++; - } while(prinst.tempstrings[i] != NULL); - if (i == prinst.numtempstrings) - prinst.numtempstrings++; -#else - - i = prinst.numtempstrings; - if (i == 0x10000000) - return 0; - - prinst.numtempstrings++; -#endif + for (i = 0; i < prinst.nexttempstring && prinst.tempstrings[i]; i++) + ; + if (i == prinst.nexttempstring) + return 0; //panic! + } + prinst.nexttempstring = i; + prinst.livetemps++; prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len); prinst.tempstrings[i]->size = len; @@ -1158,69 +1261,98 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, return (string_t)((unsigned int)i | STRING_TEMP); } -string_t PDECL PR_AllocTempString (pubprogfuncs_t *ppf, const char *str) +pbool PR_RunGC (progfuncs_t *progfuncs) { -#ifdef QCGC - char *out; - string_t res; - size_t len; - if (!str) - return 0; - len = strlen(str)+1; - res = PR_AllocTempStringLen(ppf, &out, len); - if (res) - memcpy(out, str, len); - return res; -#else - progfuncs_t *progfuncs = (progfuncs_t*)ppf; - char **ntable; - int newmax; - int i; - - if (!str) - return 0; - - if (prinst.numtempstrings == prinst.maxtempstrings) + if (!prinst.gccontext) { - newmax = prinst.maxtempstrings += 1024; - prinst.maxtempstrings += 1024; - ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax); - memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings); - prinst.maxtempstrings = newmax; - if (prinst.tempstrings) - progfuncs->funcs.parms->memfree(prinst.tempstrings); - prinst.tempstrings = ntable; - } - - i = prinst.numtempstrings; - if (i == 0x10000000) - return 0; - - prinst.numtempstrings++; - - prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(strlen(str)+1); - strcpy(prinst.tempstrings[i], str); - - return (string_t)((unsigned int)i | STRING_TEMP); + if (prinst.livetemps < prinst.maxtempstrings/2 || prinst.nexttempstring < prinst.maxtempstrings/2) + { //don't bother yet + return false; + } + else + { +#ifdef GCTIMINGS + double starttime = Sys_DoubleTime(), endtime; #endif + struct qcgccontext_s *gc = prinst.gccontext = malloc(sizeof(*gc) - sizeof(gc->amem) + prinst.addressableused + sizeof(*gc->tempstrings)*prinst.maxtempstrings); + gc->done = false; + gc->clearedtemps = 0; + gc->progfuncs = progfuncs; + + gc->memsize = prinst.addressableused; + memcpy(gc->amem, prinst.addressablehunk, prinst.addressableused); + + gc->numtemps = prinst.maxtempstrings; + gc->tempstrings = (void*)((char*)gc->amem+prinst.addressableused); + memcpy(gc->tempstrings, prinst.tempstrings, sizeof(*gc->tempstrings)*gc->numtemps); + + COM_InsertWork(WG_LOADER, PR_QCGC_Thread, gc, NULL, 0, 0); + +#ifdef GCTIMINGS + endtime = Sys_DoubleTime(); + gc->externs->Printf("preparetime=%f\n", (endtime - starttime)); +#endif + } + } + else if (prinst.gccontext->done) + { + prinst.livetemps -= prinst.gccontext->clearedtemps; + free(prinst.gccontext); + prinst.gccontext = NULL; + + //if over half the (max)strings are still live, just increase the max so we are not spamming collections + if (prinst.livetemps >= prinst.maxtempstrings/2) + { + unsigned int newmax = prinst.maxtempstrings * 2; + tempstr_t **ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax); + memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.maxtempstrings); + memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.maxtempstrings)); + prinst.maxtempstrings = newmax; + if (prinst.tempstrings) + progfuncs->funcs.parms->memfree(prinst.tempstrings); + prinst.tempstrings = ntable; + } + return false; + } + return true; //running... } +static void PR_FreeAllTemps (progfuncs_t *progfuncs) +{ + unsigned int i; + while (prinst.gccontext) + { + COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false); + PR_RunGC(progfuncs); + } + for (i = 0; i < prinst.maxtempstrings; i++) + { + externs->memfree(prinst.tempstrings[i]); + prinst.tempstrings[i] = NULL; + } + prinst.maxtempstrings = 0; + prinst.nexttempstring = 0; +} + +#elif defined(QCGC) + +#include "quakedef.h" -#ifdef QCGC pbool PR_RunGC (progfuncs_t *progfuncs) { unsigned int p; char *marked; unsigned int *str; unsigned int r_l, r_d; -// unsigned long long starttime, markedtime, endtime; - +#ifdef GCTIMINGS + double starttime, markedtime, endtime; +#endif //only run the GC when we've itterated each string at least once. if (prinst.nexttempstring < (prinst.maxtempstrings>>1) || prinst.nexttempstring < 200) return false; - -// starttime = Sys_GetClock(); - +#ifdef GCTIMINGS + starttime = Sys_DoubleTime(); +#endif marked = malloc(sizeof(*marked) * prinst.numtempstrings); memset(marked, 0, sizeof(*marked) * prinst.numtempstrings); @@ -1240,7 +1372,9 @@ pbool PR_RunGC (progfuncs_t *progfuncs) } //sweep -// markedtime = Sys_GetClock(); +#ifdef GCTIMINGS + markedtime = Sys_DoubleTime(); +#endif r_l = 0; r_d = 0; for (p = 0; p < prinst.numtempstrings; p++) @@ -1286,12 +1420,95 @@ pbool PR_RunGC (progfuncs_t *progfuncs) prinst.tempstrings = ntable; } -// endtime = Sys_GetClock(); -// externs->Printf("live: %u, dead: %u, time: mark=%f, sweep=%f\n", r_l, r_d, (double)(markedtime - starttime) / Sys_GetClockRate(), (double)(endtime - markedtime) / Sys_GetClockRate()); +#ifdef GCTIMINGS + endtime = Sys_DoubleTime(); + externs->Printf("live: %u, dead: %u, time: mark=%f, sweep=%f, total=%f\n", r_l, r_d, markedtime - starttime, endtime - markedtime, endtime-starttime); +#endif return true; } +static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) +{ + progfuncs_t *progfuncs = (progfuncs_t*)ppf; + tempstr_t **ntable; + int newmax; + int i; + + if (!str) + return 0; + + if (prinst.numtempstrings == prinst.maxtempstrings) + { + newmax = prinst.maxtempstrings + 1024; + ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax); + memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings); + memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.numtempstrings)); + prinst.maxtempstrings = newmax; + if (prinst.tempstrings) + progfuncs->funcs.parms->memfree(prinst.tempstrings); + prinst.tempstrings = ntable; + } + + if (prinst.nexttempstring >= 0x10000000) + return 0; + do + { + i = prinst.nexttempstring++; + } while(prinst.tempstrings[i] != NULL); + if (i == prinst.numtempstrings) + prinst.numtempstrings++; + + prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len); + prinst.tempstrings[i]->size = len; + *str = prinst.tempstrings[i]->value; + + return (string_t)((unsigned int)i | STRING_TEMP); +} +static void PR_FreeAllTemps (progfuncs_t *progfuncs) +{ + unsigned int i; + for (i = 0; i < prinst.numtempstrings; i++) + { + externs->memfree(prinst.tempstrings[i]); + prinst.tempstrings[i] = NULL; + } + prinst.numtempstrings = 0; + prinst.nexttempstring = 0; +} #else +static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) +{ + progfuncs_t *progfuncs = (progfuncs_t*)ppf; + tempstr_t **ntable; + int newmax; + int i; + + if (!str) + return 0; + + if (prinst.numtempstrings == prinst.maxtempstrings) + { + newmax = prinst.maxtempstrings + 1024; + ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax); + memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings); + prinst.maxtempstrings = newmax; + if (prinst.tempstrings) + progfuncs->funcs.parms->memfree(prinst.tempstrings); + prinst.tempstrings = ntable; + } + + i = prinst.numtempstrings; + if (i == 0x10000000) + return 0; + + prinst.numtempstrings++; + + prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len); + prinst.tempstrings[i]->size = len; + *str = prinst.tempstrings[i]->value; + + return (string_t)((unsigned int)i | STRING_TEMP); +} void PR_FreeTemps (progfuncs_t *progfuncs, int depth) { int i; @@ -1307,7 +1524,6 @@ void PR_FreeTemps (progfuncs_t *progfuncs, int depth) prinst.numtempstrings = depth; } -#endif static void PR_FreeAllTemps (progfuncs_t *progfuncs) { unsigned int i; @@ -1319,6 +1535,21 @@ static void PR_FreeAllTemps (progfuncs_t *progfuncs) prinst.numtempstrings = 0; prinst.nexttempstring = 0; } +#endif + +string_t PDECL PR_AllocTempString (pubprogfuncs_t *ppf, const char *str) +{ + char *out; + string_t res; + size_t len; + if (!str) + return 0; + len = strlen(str)+1; + res = PR_AllocTempStringLen(ppf, &out, len); + if (res) + memcpy(out, str, len); + return res; +} static pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; @@ -1387,13 +1618,11 @@ static pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) return true; } -static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf); - -static void PDECL RegisterBuiltin(pubprogfuncs_t *progfncs, const char *name, builtin_t func); +static void PDECL PR_Shutdown(pubprogfuncs_t *ppf); static pubprogfuncs_t deffuncs = { PROGSTRUCT_VERSION, - PR_CloseProgs, + PR_Shutdown, PR_Configure, PR_LoadProgs, PR_InitEnts, @@ -1411,6 +1640,8 @@ static pubprogfuncs_t deffuncs = { PR_VarString, NULL, //progstate + 0, //numprogs + PR_FindFunc, #if defined(MINIMAL) || defined(OMIT_QCC) NULL, @@ -1431,20 +1662,16 @@ static pubprogfuncs_t deffuncs = { PR_RestoreEnt, PR_FindGlobal, - ED_NewString, - QC_HunkAlloc, QC_GetEdictFieldValue, ProgsToEdict, EdictToProgs, PR_EvaluateDebugString, - 0,//trace PR_StackTrace, - PR_ToggleBreakpoint, - 0, //numprogs + NULL, //parms #if 1//defined(MINIMAL) || defined(OMIT_QCC) NULL, //decompile @@ -1452,7 +1679,6 @@ static pubprogfuncs_t deffuncs = { QC_Decompile, #endif 0, //callargc - RegisterBuiltin, 0, //string table(pointer base address) 0, //string table size @@ -1466,19 +1692,18 @@ static pubprogfuncs_t deffuncs = { QC_RegisterFieldVar, - NULL, //user tempstringbase - 0, //user tempstringnum - + ED_NewString, + QC_HunkAlloc, + PR_memalloc, + PR_memfree, PR_AllocTempString, - + PR_AllocTempStringLen, PR_StringToProgs, PR_StringToNative, + PR_QueryField, QC_ClearEdict, QC_FindPrefixedGlobals, - PR_memalloc, - PR_AllocTempStringLen, - PR_memfree, PR_SetWatchPoint, QC_AddSharedVar, @@ -1490,7 +1715,9 @@ static pubprogfuncs_t deffuncs = { PR_UglyValueString, ED_ParseEval, PR_SetStringField, - PR_DumpProfiles + PR_DumpProfiles, + + 0, NULL, }; static int PDECL qclib_null_printf(const char *s, ...) { @@ -1565,9 +1792,8 @@ static progexterns_t defexterns = { #undef maxedicts #undef sv_num_edicts -static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf) +static void PDECL PR_Shutdown(pubprogfuncs_t *ppf) { -// extensionbuiltin_t *eb; void (VARGS *f) (void *); progfuncs_t *inst = (progfuncs_t*)ppf; @@ -1606,15 +1832,6 @@ static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf) free(inst->inst.watch_name); - -/* - while(inst->prinst.extensionbuiltin) - { - eb = inst->prinst.extensionbuiltin->prev; - f(inst->prinst.extensionbuiltin); - inst->prinst.extensionbuiltin = eb; - } -*/ if (inst->inst.field) f(inst->inst.field); if (inst->inst.shares) @@ -1622,18 +1839,6 @@ static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf) f(inst); } -static void PDECL RegisterBuiltin(pubprogfuncs_t *progfuncs, const char *name, builtin_t func) -{ -/* - extensionbuiltin_t *eb; - eb = memalloc(sizeof(extensionbuiltin_t)); - eb->prev = progfuncs->prinst.extensionbuiltin; - progfuncs->prinst.extensionbuiltin = eb; - eb->name = name; - eb->func = func; -*/ -} - #ifndef WIN32 #define QCLIBINT //don't use dllspecifications #endif diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index 62119192..b11b458d 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -48,7 +48,7 @@ edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num, pbool object, un progfuncs->funcs.AddressableFree(&progfuncs->funcs, e->fields); e->fields = progfuncs->funcs.AddressableAlloc(&progfuncs->funcs, fields_size); if (!e->fields) - Sys_Error ("ED_Alloc: Unable to allocate more field space"); + externs->Sys_Error ("ED_Alloc: Unable to allocate more field space"); e->fieldsize = fields_size; // e->fields = PRAddressableExtend(progfuncs, NULL, fields_size, 0); @@ -98,7 +98,7 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf, pbool object, size_t extras return (struct edict_s *)e; } } - Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts); + externs->Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts); } //define this to wastefully allocate extra ents, to test network capabilities. @@ -146,7 +146,7 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf, pbool object, size_t extras char *buf; buf = PR_SaveEnts(&progfuncs->funcs, NULL, &size, 0, 0); progfuncs->funcs.parms->WriteFile("edalloc.dump", buf, size); - Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts); + externs->Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts); } } @@ -358,7 +358,7 @@ unsigned int ED_FindGlobalOfs (progfuncs_t *progfuncs, char *name) d32 = ED_FindGlobal32(progfuncs, name); return d32?d32->ofs:0; } - Sys_Error("ED_FindGlobalOfs - bad struct type"); + externs->Sys_Error("ED_FindGlobalOfs - bad struct type"); return 0; } @@ -457,7 +457,7 @@ unsigned int *ED_FindGlobalOfsFromProgs (progfuncs_t *progfuncs, progstate_t *ps return NULL; return &def32->ofs; } - Sys_Error("ED_FindGlobalOfsFromProgs - bad struct type"); + externs->Sys_Error("ED_FindGlobalOfsFromProgs - bad struct type"); return 0; } @@ -933,7 +933,7 @@ char *PR_GlobalString (progfuncs_t *progfuncs, int ofs) strcat (line," "); return line; } - Sys_Error("Bad struct type in PR_GlobalString"); + externs->Sys_Error("Bad struct type in PR_GlobalString"); return ""; } @@ -960,7 +960,7 @@ char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs) nameofs = def32->s_name; break; default: - Sys_Error("Bad struct type in PR_GlobalStringNoContents"); + externs->Sys_Error("Bad struct type in PR_GlobalStringNoContents"); } if (nameofs) @@ -1624,7 +1624,7 @@ add32: } break; default: - Sys_Error("Bad struct type in SaveEnts"); + externs->Sys_Error("Bad struct type in SaveEnts"); } return buf; @@ -2023,7 +2023,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD datastart = file; file = QCC_COM_Parse(file); if (qcc_token[0] != '{') - Sys_Error("Progs loading found %s, not '{'", qcc_token); + externs->Sys_Error("Progs loading found %s, not '{'", qcc_token); if (!resethunk) ed = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0); else @@ -2032,7 +2032,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD if (!ed) { - Sys_Error("Edict was not allocated\n"); + externs->Sys_Error("Edict was not allocated\n"); ed = ED_AllocIntoTable(progfuncs, num, false, prinst.fields_size); } } @@ -2050,7 +2050,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD num = atoi(qcc_token); file = QCC_COM_Parse(file); if (qcc_token[0] != '{') - Sys_Error("Progs loading found %s, not '{'", qcc_token); + externs->Sys_Error("Progs loading found %s, not '{'", qcc_token); filename[0] = '\0'; @@ -2060,7 +2060,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD { file = QCC_COM_Parse(file); //read the key if (!file) - Sys_Error("EOF in progs block"); + externs->Sys_Error("EOF in progs block"); if (!strcmp("filename", qcc_token)) //check key get and save values {file = QCC_COM_Parse(file); strcpy(filename, qcc_token);} @@ -2071,7 +2071,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD else if (qcc_token[0] == '}') //end of block break; else - Sys_Error("Bad key \"%s\" in progs block", qcc_token); + externs->Sys_Error("Bad key \"%s\" in progs block", qcc_token); } PR_ReallyLoadProgs(progfuncs, filename, &pr_progstate[num], true); @@ -2113,7 +2113,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD file = QCC_COM_Parse(file); if (qcc_token[0] != '{') - Sys_Error("Globals loading found \'%s\', not '{'", qcc_token); + externs->Sys_Error("Globals loading found \'%s\', not '{'", qcc_token); PR_SwitchProgs(progfuncs, num); while (1) @@ -2122,7 +2122,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD if (qcc_token[0] == '}') break; else if (!qcc_token[0] || !file) - Sys_Error("EOF when parsing global values"); + externs->Sys_Error("EOF when parsing global values"); switch(current_progstate->structtype) { @@ -2153,7 +2153,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD } break; default: - Sys_Error("Bad struct type in LoadEnts"); + externs->Sys_Error("Bad struct type in LoadEnts"); } } PR_SwitchProgs(progfuncs, 0); @@ -2171,13 +2171,13 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD file = QCC_COM_Parse(file); if (qcc_token[0] != '{') - Sys_Error("Progs loading found %s, not '{'", qcc_token); + externs->Sys_Error("Progs loading found %s, not '{'", qcc_token); while(1) { file = QCC_COM_Parse(file); //read the key if (!file) - Sys_Error("EOF in general block"); + externs->Sys_Error("EOF in general block"); if (!strcmp("maxprogs", qcc_token)) //check key get and save values {file = QCC_COM_Parse(file); prinst.maxprogs = atoi(qcc_token);} @@ -2192,7 +2192,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD else if (qcc_token[0] == '}') //end of block break; else - Sys_Error("Bad key \"%s\" in general block", qcc_token); + externs->Sys_Error("Bad key \"%s\" in general block", qcc_token); } if (oldglobals) @@ -2237,7 +2237,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD if (qcc_token[0] == '}') break; else if (!qcc_token[0] || !file) - Sys_Error("EOF when parsing global values"); + externs->Sys_Error("EOF when parsing global values"); switch(current_progstate->structtype) { @@ -2268,7 +2268,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD } break; default: - Sys_Error("Bad struct type in LoadEnts"); + externs->Sys_Error("Bad struct type in LoadEnts"); } } } @@ -2318,7 +2318,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD else if (extendedterm && extendedterm(ppf, ctx, &datastart)) file = datastart; else - Sys_Error("Bad entity lump: '%s' not recognised (last ent was %i)", qcc_token, ed?ed->entnum:0); + externs->Sys_Error("Bad entity lump: '%s' not recognised (last ent was %i)", qcc_token, ed?ed->entnum:0); } if (resethunk) { @@ -2434,7 +2434,7 @@ struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, const char *buf, size_ return NULL; if (strcmp(qcc_token, "{")) - Sys_Error("Restore Ent with no opening brace"); + externs->Sys_Error("Restore Ent with no opening brace"); if (!ed) ent = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0); @@ -2589,7 +2589,6 @@ PR_LoadProgs int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_t *progstate, pbool complain) { unsigned int i, j, type; -// extensionbuiltin_t *eb; // float fl; // int len; // int num; @@ -2789,7 +2788,7 @@ retry: len=sizeof(dstatement32_t)*pr_progs->numstatements; break; default: - Sys_Error("Bad struct type"); + externs->Sys_Error("Bad struct type"); len = 0; } s = PRHunkAlloc(progfuncs, len, "dstatements"); @@ -2808,7 +2807,7 @@ retry: len=sizeof(ddef32_t)*pr_progs->numglobaldefs; break; default: - Sys_Error("Bad struct type"); + externs->Sys_Error("Bad struct type"); len = 0; } s = PRHunkAlloc(progfuncs, len, "dglobaldefs"); @@ -2827,7 +2826,7 @@ retry: len=sizeof(ddef32_t)*pr_progs->numglobaldefs; break; default: - Sys_Error("Bad struct type"); + externs->Sys_Error("Bad struct type"); len = 0; } s = PRHunkAlloc(progfuncs, len, "progfieldtable"); @@ -2937,7 +2936,7 @@ retry: current_progstate->edict_size = pr_progs->entityfields * 4 + externs->edictsize; if (sizeof(mfunction_t) > sizeof(qtest_function_t)) - Sys_Error("assumption no longer works"); + externs->Sys_Error("assumption no longer works"); // byte swap the lumps switch(current_progstate->structtype) @@ -2983,7 +2982,7 @@ retry: } break; default: - Sys_Error("Bad struct type"); + externs->Sys_Error("Bad struct type"); } //actual global values @@ -3147,7 +3146,7 @@ retry: } break; default: - Sys_Error("Bad struct type"); + externs->Sys_Error("Bad struct type"); } //ifstring fixes arn't performed anymore. @@ -3217,7 +3216,7 @@ retry: { isfriked = true; if (current_progstate->structtype != PST_DEFAULT) - Sys_Error("Decompiling a bigprogs"); + externs->Sys_Error("Decompiling a bigprogs"); return true; } */ @@ -3397,9 +3396,9 @@ retry: d32 = ED_FindGlobal32(progfuncs, s); d2 = ED_FindGlobalOfsFromProgs(progfuncs, &pr_progstate[0], s, ev_function); if (!d2) - Sys_Error("Runtime-linked function %s was not found in existing progs", s); + externs->Sys_Error("Runtime-linked function %s was not found in existing progs", s); if (!d32) - Sys_Error("Couldn't find def for \"%s\"", s); + externs->Sys_Error("Couldn't find def for \"%s\"", s); ((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); s+=strlen(s)+1; @@ -3407,7 +3406,7 @@ retry: } break; default: - Sys_Error("Bad struct type"); + externs->Sys_Error("Bad struct type"); } if ((isfriked && prinst.pr_typecurrent)) //friked progs only allow one file. @@ -3502,7 +3501,7 @@ struct edict_s *PDECL QC_EDICT_NUM(pubprogfuncs_t *ppf, unsigned int n) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; if (n >= prinst.maxedicts) - Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n); + externs->Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n); return (struct edict_s*)prinst.edicttable[n]; } @@ -3512,6 +3511,6 @@ unsigned int PDECL QC_NUM_FOR_EDICT(pubprogfuncs_t *ppf, struct edict_s *e) progfuncs_t *progfuncs = (progfuncs_t*)ppf; edictrun_t *er = (edictrun_t*)e; if (!er || er->entnum >= prinst.maxedicts) - Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%p)", e); + externs->Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%p)", e); return er->entnum; } diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index e4f64464..a70a5ce3 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -563,7 +563,7 @@ int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs) prstack_t *st; if (pr_depth <= 0) - Sys_Error ("prog stack underflow"); + externs->Sys_Error ("prog stack underflow"); // up stack st = &pr_stack[--pr_depth]; @@ -666,7 +666,7 @@ ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, const char *name, eval_t return NULL; break; default: - Sys_Error("Bad struct type in ED_FindLocalOrGlobal"); + externs->Sys_Error("Bad struct type in ED_FindLocalOrGlobal"); def32 = NULL; } @@ -1102,7 +1102,7 @@ void SetExecutionToLine(progfuncs_t *progfuncs, int linenum) } break; default: - Sys_Error("Bad struct type"); + externs->Sys_Error("Bad struct type"); snum = 0; } prinst.debugstatement = snum; @@ -1195,7 +1195,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin op = ((dstatement32_t*)cp->statements + i)->op; break; default: - Sys_Error("Bad structtype"); + externs->Sys_Error("Bad structtype"); op = 0; } switch (flag) @@ -1237,7 +1237,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin ((dstatement32_t*)cp->statements + i)->op = op; break; default: - Sys_Error("Bad structtype"); + externs->Sys_Error("Bad structtype"); op = 0; } if (ret) //if its set, only set one breakpoint statement, not all of them. @@ -1269,7 +1269,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin op = ((dstatement32_t*)cp->statements + i)->op; break; default: - Sys_Error("Bad structtype"); + externs->Sys_Error("Bad structtype"); } switch (flag) { @@ -1310,7 +1310,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin ((dstatement32_t*)cp->statements + i)->op = op; break; default: - Sys_Error("Bad structtype"); + externs->Sys_Error("Bad structtype"); } break; } @@ -1782,7 +1782,7 @@ static void PR_ExecuteCode (progfuncs_t *progfuncs, int s) return; continue; default: - Sys_Error("PR_ExecuteProgram - bad structtype"); + externs->Sys_Error("PR_ExecuteProgram - bad structtype"); } } } diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c index 743606e1..a92c331f 100644 --- a/engine/qclib/pr_multi.c +++ b/engine/qclib/pr_multi.c @@ -276,7 +276,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char } if (!progfuncs->funcs.fieldadjust && engineofs>=0) if ((unsigned)engineofs/4 != prinst.field[i].ofs) - Sys_Error("Field %s at wrong offset", name); + externs->Sys_Error("Field %s at wrong offset", name); if (prinst.field[i].progsofs == -1) prinst.field[i].progsofs = progsofs; @@ -324,7 +324,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char } #endif if (engineofs&3) - Sys_Error("field %s is %i&3", name, (int)engineofs); + externs->Sys_Error("field %s is %i&3", name, (int)engineofs); prinst.field[fnum].ofs = ofs = engineofs/4; } else @@ -372,7 +372,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char prinst.fields_size = (ofs+type_size[type])*4; if (prinst.max_fields_size && prinst.fields_size > prinst.max_fields_size) - Sys_Error("Allocated too many additional fields after ents were inited."); + externs->Sys_Error("Allocated too many additional fields after ents were inited."); #ifdef MAPPING_DEBUG externs->Printf("Field %s %i -> %i\n", name, prinst.field[fnum].progsofs,prinst.field[fnum].ofs); @@ -499,14 +499,14 @@ void PDECL QC_AddSharedFieldVar(pubprogfuncs_t *ppf, int num, char *stringtable) //oh well, must be a parameter. if (*(int *)&pr_globals[gd[num].ofs]) - Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", gd[num].s_name+stringtable, *(int *)&pr_globals[gd[num].ofs]); + externs->Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", gd[num].s_name+stringtable, *(int *)&pr_globals[gd[num].ofs]); } return; default: - Sys_Error("Bad bits"); + externs->Sys_Error("Bad bits"); break; } - Sys_Error("Should be unreachable"); + externs->Sys_Error("Should be unreachable"); } void QC_AddFieldGlobal(pubprogfuncs_t *ppf, int *globdata) diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index d768b358..0c0ea5e5 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -92,16 +92,25 @@ typedef struct char value[4]; } tempstr_t; +#if defined(QCGC) && defined(MULTITHREAD) +// #define THREADEDGC +#endif + //FIXME: the defines hidden inside this structure are evil. typedef struct prinst_s { //temp strings are GCed, and can be created by engine, builtins, or just by ent parsing code. tempstr_t **tempstrings; unsigned int maxtempstrings; +#ifdef THREADEDGC + unsigned int nexttempstring; + unsigned int livetemps; //increased on alloc, decremented after sweep + struct qcgccontext_s *gccontext; +#elif defined(QCGC) unsigned int numtempstrings; -#ifdef QCGC unsigned int nexttempstring; #else + unsigned int numtempstrings; unsigned int numtempstringsstack; #endif @@ -235,8 +244,8 @@ extern QCC_opcode_t pr_opcodes[]; // sized by initialization #define sv_edicts (*externs->sv_edicts) #define PR_DPrintf externs->DPrintf -#define printf syntax error -#define Sys_Error externs->Sys_Error +//#define printf syntax error +//#define Sys_Error externs->Sys_Error int PRHunkMark(progfuncs_t *progfuncs); void PRHunkFree(progfuncs_t *progfuncs, int mark); @@ -365,12 +374,6 @@ typedef struct progstate_s #endif } progstate_t; -typedef struct extensionbuiltin_s { - char *name; - builtin_t func; - struct extensionbuiltin_s *prev; -} extensionbuiltin_t; - //============================================================================ diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index ddcf0325..5c142964 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -2,6 +2,7 @@ #ifndef PROGSLIB_H #define PROGSLIB_H +#include "progtype.h" #include #ifdef _MSC_VER #define VARGS __cdecl @@ -90,7 +91,7 @@ struct pubprogfuncs_s { int progsversion; //PROGSTRUCT_VERSION - void (PDECL *CloseProgs) (pubprogfuncs_t *inst); + void (PDECL *Shutdown) (pubprogfuncs_t *inst); void (PDECL *Configure) (pubprogfuncs_t *prinst, size_t addressablesize, int max_progs, pbool enableprofiling); //configure buffers and memory. Used to reset and must be called first. Flushes a running VM. progsnum_t (PDECL *LoadProgs) (pubprogfuncs_t *prinst, const char *s); //load a progs @@ -111,6 +112,7 @@ struct pubprogfuncs_s char *(PDECL *VarString) (pubprogfuncs_t *prinst, int first); //returns a string made up of multiple arguments struct progstate_s **progstate; //internal to the library. + int numprogs; func_t (PDECL *FindFunction) (pubprogfuncs_t *prinst, const char *funcname, progsnum_t num); @@ -131,29 +133,21 @@ struct pubprogfuncs_s struct edict_s *(PDECL *restoreent) (pubprogfuncs_t *prinst, const char *buf, size_t *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed) union eval_s *(PDECL *FindGlobal) (pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type); //find a pointer to the globals value - char *(PDECL *AddString) (pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); //dump a string into the progs memory (for setting globals and whatnot) - void *(PDECL *Tempmem) (pubprogfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded union eval_s *(PDECL *GetEdictFieldValue)(pubprogfuncs_t *prinst, struct edict_s *ent, const char *name, etype_t type, evalc_t *s); //get an entityvar (cache it) and return the possible values struct edict_s *(PDECL *ProgsToEdict) (pubprogfuncs_t *prinst, int progs); //edicts are stored as ints and need to be adjusted int (PDECL *EdictToProgs) (pubprogfuncs_t *prinst, struct edict_s *ed); //edicts are stored as ints and need to be adjusted char *(PDECL *EvaluateDebugString) (pubprogfuncs_t *prinst, const char *key); //evaluate a string and return it's value (according to current progs) (expands edict vars) - int debug_trace; //start calling the editor for each line executed - void (PDECL *StackTrace) (pubprogfuncs_t *prinst, int showlocals); - int (PDECL *ToggleBreak) (pubprogfuncs_t *prinst, const char *filename, int linenum, int mode); - int numprogs; - struct progexterns_s *parms; //these are the initial parms, they may be changed pbool (PDECL *Decompile) (pubprogfuncs_t *prinst, const char *fname); int callargc; //number of args of built-in call - void (PDECL *RegisterBuiltin) (pubprogfuncs_t *prinst, const char *, builtin_t); char *stringtable; //qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates. int stringtablesize; @@ -168,23 +162,20 @@ struct pubprogfuncs_s int (PDECL *RegisterFieldVar) (pubprogfuncs_t *prinst, unsigned int type, const char *name, signed long requestedpos, signed long originalofs); - char *tempstringbase; //for engine's use. Store your base tempstring pointer here. - int tempstringnum; //for engine's use. - + char *(PDECL *AddString) (pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); //dump a string into the progs memory (for setting globals and whatnot) + void *(PDECL *Tempmem) (pubprogfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded + void *(PDECL *AddressableAlloc) (pubprogfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/ + void (PDECL *AddressableFree) (pubprogfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/ string_t (PDECL *TempString) (pubprogfuncs_t *prinst, const char *str); - + string_t (PDECL *AllocTempString) (pubprogfuncs_t *prinst, char **str, unsigned int len); string_t (PDECL *StringToProgs) (pubprogfuncs_t *prinst, const char *str); //commonly makes a semi-permanent mapping from some table to the string value. mapping can be removed via RemoveProgsString - const char *(ASMCALL *StringToNative) (pubprogfuncs_t *prinst, string_t str); + const char *(ASMCALL *StringToNative) (pubprogfuncs_t *prinst, string_t str); int (PDECL *QueryField) (pubprogfuncs_t *prinst, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache); //find info on a field definition at an offset void (PDECL *EntClear) (pubprogfuncs_t *progfuncs, struct edict_s *e); void (PDECL *FindPrefixGlobals) (pubprogfuncs_t *progfuncs, int prnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx); //calls the callback for each named global found - void *(PDECL *AddressableAlloc) (pubprogfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/ - - string_t (PDECL *AllocTempString) (pubprogfuncs_t *prinst, char **str, unsigned int len); - void (PDECL *AddressableFree) (pubprogfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/ pbool (PDECL *SetWatchPoint) (pubprogfuncs_t *prinst, const char *key); void (PDECL *AddSharedVar) (pubprogfuncs_t *progfuncs, int start, int size); @@ -200,6 +191,13 @@ struct pubprogfuncs_s unsigned int edicttable_length; struct edict_s **edicttable; + + //stuff not used by the qclib at all, but provided for lazy user storage. + struct + { + char *tempstringbase; //for engine's use. Store your base tempstring pointer here. + int tempstringnum; //for engine's use. + } user; }; typedef struct progexterns_s { @@ -317,8 +315,6 @@ typedef union eval_s #define PROG_TO_EDICT(pf, ed) (*pf->ProgsToEdict) (pf, ed) #define EDICT_TO_PROG(pf, ed) (*pf->EdictToProgs) (pf, (struct edict_s*)ed) -#define PR_RegisterBuiltin(pf, name, func) (*pf->RegisterBuiltin) (pf, name, func) - #define PR_GetString(pf,s) (*pf->StringToNative) (pf, s) #define PR_GetStringOfs(pf,o) (*pf->StringToNative) (pf, G_INT(o)) #define PR_SetString(pf, s) (*pf->StringToProgs) (pf, s) diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 551ca0cf..5ffc7132 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -1876,7 +1876,7 @@ void QCC_PR_LexString (void) QCC_PR_ParseWarning(WARN_MACROINSTRING, "Macro expansion in string"); if (len+strlen(cnst) >= sizeof(pr_token)-1) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1); strcpy(pr_token+len, cnst); len+=strlen(cnst); @@ -1941,7 +1941,7 @@ forceutf8: //error if needed if (len+bytecount >= sizeof(pr_token)) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1); //output it. if (bytecount == 1) @@ -1974,7 +1974,7 @@ forcequake: forcebyte: if (len >= sizeof(pr_token)-1) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1); pr_token[len] = c; len++; } @@ -1982,7 +1982,7 @@ forcebyte: } if (len > sizeof(pr_immediate_string)-1) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_immediate_string)-1); + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_immediate_string)-1); pr_token[len] = 0; pr_token_type = tt_immediate; diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index ef7ab657..4fda2e3a 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -3810,7 +3810,7 @@ DWORD WINAPI threadwrapper(void *args) MessageBox(mainwindow, "Access Denied", "Cannot Start Engine", 0); break; default: - MessageBox(mainwindow, qcva("gla: %x", hr), "Cannot Start Engine", 0); + MessageBox(mainwindow, qcva("gla: %x", (unsigned)hr), "Cannot Start Engine", 0); break; } hadstatus = true; //don't warn about other stuff diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 1ba9535d..3f8120b2 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -1527,7 +1527,7 @@ static pbool QCC_WriteData (int crc) outputsttype = PST_QTEST; break; default: - Sys_Error("invalid progs type chosen!"); + externs->Sys_Error("invalid progs type chosen!"); } @@ -1670,7 +1670,7 @@ static pbool QCC_WriteData (int crc) } break; default: - Sys_Error("structtype error"); + externs->Sys_Error("structtype error"); funcdata = NULL; funcdatasize = 0; } @@ -2144,7 +2144,7 @@ strofs = (strofs+3)&~3; } break; default: - Sys_Error("structtype error"); + externs->Sys_Error("structtype error"); } progs.ofs_functions = SafeSeek (h, 0, SEEK_CUR); @@ -2292,7 +2292,7 @@ strofs = (strofs+3)&~3; SafeWrite (h, fields16, numfielddefs*sizeof(QCC_ddef16_t)); break; default: - Sys_Error("structtype error"); + externs->Sys_Error("structtype error"); } progs.ofs_globals = SafeSeek (h, 0, SEEK_CUR); diff --git a/engine/qclib/qcd_main.c b/engine/qclib/qcd_main.c index be5fa6df..70371ab9 100644 --- a/engine/qclib/qcd_main.c +++ b/engine/qclib/qcd_main.c @@ -46,12 +46,12 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const int i; if (method == 0) //copy { - if (complen != len) Sys_Error("lengths do not match"); + if (complen != len) externs->Sys_Error("lengths do not match"); memcpy(buffer, info, len); } else if (method == 1) //xor encryption { - if (complen != len) Sys_Error("lengths do not match"); + if (complen != len) externs->Sys_Error("lengths do not match"); for (i = 0; i < len; i++) buffer[i] = ((const char*)info)[i] ^ 0xA5; } @@ -84,13 +84,13 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const else inflateInit(&strm); if (Z_STREAM_END != inflate(&strm, Z_FINISH)) //decompress it in one go. - Sys_Error("Failed block decompression\n"); + externs->Sys_Error("Failed block decompression\n"); inflateEnd(&strm); } #endif //add your decryption/decompression routine here. else - Sys_Error("Bad file encryption routine\n"); + externs->Sys_Error("Bad file encryption routine\n"); return buffer; @@ -165,13 +165,13 @@ int QC_encode(progfuncs_t *progfuncs, int len, int method, const char *in, int h deflateEnd(&strm); return i; #endif - Sys_Error("ZLIB compression not supported in this build"); + externs->Sys_Error("ZLIB compression not supported in this build"); return 0; } //add your compression/decryption routine here. else { - Sys_Error("Wierd method"); + externs->Sys_Error("Wierd method"); return 0; } } diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 8e3eae3a..0fcbeada 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -855,8 +855,8 @@ void PR_Deinit(void) PR_Common_Shutdown(svprogfuncs, false); World_Destroy(&sv.world); - if (svprogfuncs->CloseProgs) - svprogfuncs->CloseProgs(svprogfuncs); + if (svprogfuncs->Shutdown) + svprogfuncs->Shutdown(svprogfuncs); sv.world.progs = NULL; svprogfuncs=NULL; diff --git a/engine/server/progs.h b/engine/server/progs.h index bd34e4b2..101fca2b 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -130,7 +130,7 @@ qboolean PR_QCChat(char *text, int say_type); void PR_ClientUserInfoChanged(char *name, char *oldivalue, char *newvalue); void PR_LocalInfoChanged(char *name, char *oldivalue, char *newvalue); -void PF_InitTempStrings(pubprogfuncs_t *prinst); +void PF_InitTempStrings(pubprogfuncs_t *inst); #ifdef VM_LUA qboolean PR_LoadLua(void); diff --git a/engine/server/savegame.c b/engine/server/savegame.c index 82093226..17d469c2 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -1541,7 +1541,7 @@ void SV_Savegame (const char *savename, qboolean mapchange) VFS_CLOSE(f); -#ifndef SERVERONLY +#ifdef HAVE_CLIENT //try to save screenshots automagically. Q_snprintfz(comment, sizeof(comment), "saves/%s/screeny.%s", savename, "tga");//scr_sshot_type.string); savefilename = comment; @@ -1549,53 +1549,27 @@ void SV_Savegame (const char *savename, qboolean mapchange) if (cls.state == ca_active && qrenderer > QR_NONE && qrenderer != QR_VULKAN/*FIXME*/) { int stride; - int width; - int height; - void *rgbbuffer; + int width = vid.pixelwidth; + int height = vid.pixelheight; image_t *img; - - //poke the various modes into redrawing the screen (without huds), to avoid any menus or console drawn over the top of the current backbuffer. - //FIXME: clear-to-black first - qboolean okay = false; -#ifdef VM_CG - if (!okay && CG_Refresh()) - okay = true; -#endif -#ifdef CSQC_DAT - if (!okay && CSQC_DrawView()) - okay = true; -#endif - if (!okay && r_worldentity.model) + uploadfmt_t format; + void *rgbbuffer = SCR_ScreenShot_Capture(width, height, &stride, &format, false); + if (rgbbuffer) { - V_RenderView (false); - okay = true; - } - if (R2D_Flush) - R2D_Flush(); +// extern cvar_t scr_sshot_type; + SCR_ScreenShot(savefilename, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, format, false); + BZ_Free(rgbbuffer); - //okay, we drew something, we're good to save a screeny. - if (okay) - { - enum uploadfmt fmt; - rgbbuffer = VID_GetRGBInfo(&stride, &width, &height, &fmt); - if (rgbbuffer) + //if any menu code has the shader loaded, we want to avoid them using a cache. + //hopefully the menu code will unload as it goes, because these screenshots could be truely massive, as they're taken at screen resolution. + //should probably use a smaller fbo or something, but whatever. + img = Image_FindTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0); + if (img) { -// extern cvar_t scr_sshot_type; - SCR_ScreenShot(savefilename, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, fmt, false); - BZ_Free(rgbbuffer); - - - //if any menu code has the shader loaded, we want to avoid them using a cache. - //hopefully the menu code will unload as it goes, because these screenshots could be truely massive, as they're taken at screen resolution. - //should probably use a smaller fbo or something, but whatever. - img = Image_FindTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0); - if (img) + if (Image_UnloadTexture(img)) { - if (Image_UnloadTexture(img)) - { - //and then reload it so that any shaders using it don't get confused - Image_GetTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0, NULL, NULL, 0, 0, TF_INVALID); - } + //and then reload it so that any shaders using it don't get confused + Image_GetTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0, NULL, NULL, 0, 0, TF_INVALID); } } } diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index afb5c637..21649080 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -3212,6 +3212,8 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->u.q1.pmovetype = ent->v->movetype; if (state->u.q1.pmovetype && ((int)ent->v->flags & FL_ONGROUND) && (client->zquake_extensions&Z_EXT_PF_ONGROUND)) state->u.q1.pmovetype |= 0x80; + if (state->u.q1.pmovetype && ((int)cl->jump_held) && (client->zquake_extensions&Z_EXT_PM_TYPE)) + state->u.q1.pmovetype |= 0x40; if (cl != client && client) { /*only generate movement values if the client doesn't already know them...*/ state->u.q1.movement[0] = ent->xv->movement[0]; diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index d6a43052..6b3d4060 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -1107,7 +1107,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, { if (svprogfuncs) //we don't want the q1 stuff anymore. { - svprogfuncs->CloseProgs(svprogfuncs); + svprogfuncs->Shutdown(svprogfuncs); sv.world.progs = svprogfuncs = NULL; } } diff --git a/engine/shaders/makevulkanblob.c b/engine/shaders/makevulkanblob.c index e97976d3..7caef291 100644 --- a/engine/shaders/makevulkanblob.c +++ b/engine/shaders/makevulkanblob.c @@ -77,6 +77,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char char tempname[256]; char tempvert[256]; char tempfrag[256]; + char incpath[256], *sl; int inheader = 1; int i; unsigned short constid = 256; //first few are reserved. @@ -94,9 +95,19 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char NULL }; - snprintf(tempname, sizeof(tempname), "vulkan/temp.tmp"); - snprintf(tempvert, sizeof(tempvert), "vulkan/temp.vert"); - snprintf(tempfrag, sizeof(tempfrag), "vulkan/temp.frag"); + const char *tmppath = "/tmp/"; + + char customsamplerlines[16][256]; + + snprintf(tempname, sizeof(tempname), "%stemp.tmp", tmppath); + snprintf(tempvert, sizeof(tempvert), "%stemp.vert", tmppath); + snprintf(tempfrag, sizeof(tempfrag), "%stemp.frag", tmppath); + + memcpy(incpath, glslname, sizeof(incpath)); + if ((sl = strrchr(incpath, '/'))) + sl[1] = 0; + else if ((sl = strrchr(incpath, '\\'))) //in case someone is on crappy windows. + sl[1] = 0; memcpy(blob->blobmagic, "\xffSPV", 4); blob->blobversion = 1; @@ -243,10 +254,34 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char blob->defaulttextures |= 1u<<13 | 1u<<17 | 1u<<18 | 1u<<19; //shader pass - else if (atoi(arg)) - blob->numtextures = atoi(arg); + else if ((i=atoi(arg))) + { //legacy + if (blob->numtextures < i) + blob->numtextures = i; + } else - printf("Unknown texture: \"%s\"\n", arg); + { + char *eq = strrchr(arg, '='); + if (eq) + { + char *type = strrchr(arg, ':'); + int pass = atoi(eq+1); + *eq = 0; + if (type) + *type++ = 0; + else + type = "2D"; + if (pass < 16) + { + snprintf(customsamplerlines[pass], sizeof(customsamplerlines[pass]), "uniform sampler%s s_%s;\n", type, arg); + if (blob->numtextures < ++pass) + blob->numtextures = pass; + } + else printf("sampler binding too high: %s:%s=%i\n", arg, type, pass); + } + else + printf("Unknown texture: \"%s\"\n", arg); + } } while((arg = strtok(NULL, " ,\r\n"))); } continue; @@ -295,7 +330,11 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char } for (i = 0; i < blob->numtextures; i++) { - fprintf(temp, "layout(set=0, binding=%u) uniform sampler2D s_t%u;\n", binding++, i); + fprintf(temp, "layout(set=0, binding=%u) ", binding++); + if (*customsamplerlines[i]) + fprintf(temp, "%s", customsamplerlines[i]); + else + fprintf(temp, "uniform sampler2D s_t%u;\n", i); } fprintf(temp, "#endif\n"); @@ -415,7 +454,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char #else "echo \"#version 450 core\" > %s && " #endif - "cpp %s -DVULKAN -DVERTEX_SHADER -P >> %s && " + "cpp %s -I%s -DVULKAN -DVERTEX_SHADER -P >> %s && " /*preprocess the fragment shader*/ #ifdef _WIN32 @@ -423,7 +462,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char #else "echo \"#version 450 core\" > %s && " #endif - "cpp %s -DVULKAN -DFRAGMENT_SHADER -P >> %s && " + "cpp %s -I%s -DVULKAN -DFRAGMENT_SHADER -P >> %s && " /*convert to spir-v (annoyingly we have no control over the output file names*/ "glslangValidator -V -l -d %s %s" @@ -431,13 +470,15 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char /*strip stuff out, so drivers don't glitch out from stuff that we don't use*/ // " && spirv-remap -i vert.spv frag.spv -o vulkan/remap" - ,tempvert, tempname, tempvert, tempfrag, tempname, tempfrag, tempvert, tempfrag); + ,tempvert, tempname, incpath, tempvert //vertex shader args + ,tempfrag, tempname, incpath, tempfrag //fragment shader args + ,tempvert, tempfrag); //compile/link args. system(command); - remove(tempname); - remove(tempvert); - remove(tempfrag); +// remove(tempname); +// remove(tempvert); +// remove(tempfrag); return 1; } @@ -451,6 +492,12 @@ int main(int argc, const char **argv) char line[256]; int r = 1; + if (argc == 1) + { + printf("%s input.glsl output.fvb\n"); + return 1; + } + if (!generatevulkanblobs((struct blobheader*)proto, sizeof(proto), inname)) return 1; //should have generated two files diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 36d9943e..76072f2c 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -1479,6 +1479,9 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay case PTI_RGB8: format = VK_FORMAT_R8G8B8_UNORM; break; case PTI_BGR8: format = VK_FORMAT_B8G8R8_UNORM; break; + case PTI_RGB8_SRGB: format = VK_FORMAT_R8G8B8_SRGB; break; + case PTI_BGR8_SRGB: format = VK_FORMAT_B8G8R8_SRGB; break; + //unsupported 'formats' case PTI_MAX: #ifdef FTE_TARGET_WEB @@ -1518,7 +1521,20 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay viewInfo.flags = 0; viewInfo.image = ret.image; - viewInfo.viewType = (ret.type==PTI_CUBEMAP)?VK_IMAGE_VIEW_TYPE_CUBE:VK_IMAGE_VIEW_TYPE_2D; + switch(ret.type) + { + default: + return ret; + case PTI_CUBEMAP: + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + break; + case PTI_2D: + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + break; + case PTI_2D_ARRAY: + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + break; + } viewInfo.format = format; switch(encoding) { @@ -1783,12 +1799,27 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips) uint32_t blockbytes; uint32_t layers; uint32_t mipcount = mips->mipcount; - if (mips->type != PTI_2D && mips->type != PTI_CUBEMAP)// && mips->type != PTI_2D_ARRAY) - return false; - if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0) + switch(mips->type) + { + case PTI_2D: + if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth != 1) + return false; + layers = 1; + break; + case PTI_2D_ARRAY: + if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth == 0) + return false; + layers = 1; + break; + case PTI_CUBEMAP: //unfortunately, these use separate layers (yay gl compat) + if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth != 1) + return false; + layers = 6; + break; + default: return false; + } - layers = (mips->type == PTI_CUBEMAP)?6:1; layers *= mips->mip[0].depth; if (layers == 1 && mipcount > 1) @@ -1895,8 +1926,8 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips) { uint32_t blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth; uint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight; - - bci.size += blockswidth*blocksheight*blockbytes; + uint32_t blocksdepth = (mips->mip[i].depth+1-1) / 1; + bci.size += blockswidth*blocksheight*blocksdepth*blockbytes; } bci.flags = 0; bci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; @@ -1931,26 +1962,36 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips) //for compressed formats (ie: s3tc/dxt) we need to round up to deal with npot. uint32_t blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth; uint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight; + uint32_t blocksdepth = (mips->mip[i].depth+1-1) / 1; if (mips->mip[i].data) - memcpy((char*)mapdata + bci.size, (char*)mips->mip[i].data, blockswidth*blockbytes*blocksheight); + memcpy((char*)mapdata + bci.size, (char*)mips->mip[i].data, blockswidth*blockbytes*blocksheight*blocksdepth); else - memset((char*)mapdata + bci.size, 0, blockswidth*blockbytes*blocksheight); + memset((char*)mapdata + bci.size, 0, blockswidth*blockbytes*blocksheight*blocksdepth); //queue up a buffer->image copy for this mip region.bufferOffset = bci.size; region.bufferRowLength = blockswidth*blockwidth; region.bufferImageHeight = blocksheight*blockheight; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = i%(mipcount/layers); - region.imageSubresource.baseArrayLayer = i/(mipcount/layers); - region.imageSubresource.layerCount = 1; + if (mips->type == PTI_CUBEMAP) + { + region.imageSubresource.mipLevel = i%(mipcount/layers); + region.imageSubresource.baseArrayLayer = i/(mipcount/layers); + region.imageSubresource.layerCount = mips->mip[i].depth; + } + else + { + region.imageSubresource.mipLevel = i; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = mips->mip[i].depth; + } region.imageOffset.x = 0; region.imageOffset.y = 0; region.imageOffset.z = 0; - region.imageExtent.width = mips->mip[i].width;//blockswidth*blockwidth; - region.imageExtent.height = mips->mip[i].height;//blocksheight*blockheight; - region.imageExtent.depth = 1; + region.imageExtent.width = mips->mip[i].width; + region.imageExtent.height = mips->mip[i].height; + region.imageExtent.depth = mips->mip[i].depth; vkCmdCopyBufferToImage(vkloadcmd, fence->stagingbuffer, target.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); @@ -4826,9 +4867,9 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre } - sh_config.progpath = NULL; + sh_config.progpath = "vulkan/%s.fvb"; sh_config.blobpath = "spirv"; - sh_config.shadernamefmt = NULL;//".spv"; + sh_config.shadernamefmt = NULL;//"_vulkan"; if (vk.nv_glsl_shader) {