diff --git a/engine/Makefile b/engine/Makefile index acc44e2c9..262c25f20 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -890,8 +890,12 @@ GLB_DIR=gl_$(FTE_FULLTARGET) GLCL_DIR=glcl_$(FTE_FULLTARGET) SV_DIR?=sv_$(FTE_FULLTARGET) -VKCL_OBJS=$(VKQUAKE_OBJS) $(BOTLIB_OBJS) snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) +VKCL_OBJS=$(VKQUAKE_OBJS) $(D3DGL_OBJS) gl_bloom.o $(BOTLIB_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o VK_CFLAGS=-DFTE_SDL $(VKCFLAGS) `$(SDLCONFIG) --cflags` +VKB_DIR=vk_$(FTE_FULLTARGET) +VKCL_DIR=vk_$(FTE_FULLTARGET) +VK_EXE_NAME=../$(EXE_NAME)-vk$(FTE_FULLTARGET) +VKCL_EXE_NAME=../$(EXE_NAME)-vkcl$(FTE_FULLTARGET) SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SERVERONLY_OBJS) $(BOTLIB_OBJS) SV_EXE_NAME=../$(EXE_NAME)-sv$(FTE_FULLTARGET) diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index f6086acf0..12f7a6670 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1863,10 +1863,9 @@ void CL_SendCmd (double frametime, qboolean mainloop) #ifndef CLIENTONLY if (sv.state && cls.state != ca_active) - { - fullsend = -1; + { //HACK: if we're also the server, spam like a crazy person until we're on the server, for faster apparent load times. + fullsend = -1; //send no movement command. msecstouse = usetime = msecs; - msecs = 0; } else #endif diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index de8bddf18..da91643a9 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1619,7 +1619,7 @@ void CL_ClearState (void) #ifdef Q2CLIENT for (i = 0; i < countof(cl.configstring_general); i++) { - if (cl.configstring_general) + if (cl.configstring_general[i]) Z_Free(cl.configstring_general[i]); } #endif diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 60c5785fc..34424cbdc 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -4242,7 +4242,7 @@ static void CLQ2_ParseConfigString (void) return; } - if (i < 0 || i >= Q2MAX_CONFIGSTRINGS) + if ((unsigned int)i >= Q2MAX_CONFIGSTRINGS) Host_EndGame ("configstring > Q2MAX_CONFIGSTRINGS"); // strncpy (olds, cl.configstrings[i], sizeof(olds)); @@ -7937,7 +7937,7 @@ void CLNQ_ParseServerMessage (void) break; case svcdp_updatestatbyte: //case svcneh_fog: - if (CPNQ_IS_BJP || cls.protocol_nq == PROTOCOL_VERSION_NEHD) + if (CPNQ_IS_BJP || cls.protocol_nq == CPNQ_NEHAHRA) { CL_ResetFog(0); if (MSG_ReadByte()) diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index 60d9cd0d6..592999460 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -1085,9 +1085,9 @@ static void CLQ2_DeltaEntity (q2frame_t *frame, int newnum, entity_state_t *old, || state->modelindex2 != ent->current.modelindex2 || state->u.q2.modelindex3 != ent->current.u.q2.modelindex3 || state->u.q2.modelindex4 != ent->current.u.q2.modelindex4 - || abs(state->origin[0] - ent->current.origin[0]) > 512 - || abs(state->origin[1] - ent->current.origin[1]) > 512 - || abs(state->origin[2] - ent->current.origin[2]) > 512 + || fabs(state->origin[0] - ent->current.origin[0]) > 512 + || fabs(state->origin[1] - ent->current.origin[1]) > 512 + || fabs(state->origin[2] - ent->current.origin[2]) > 512 || state->u.q2.event == Q2EV_PLAYER_TELEPORT || state->u.q2.event == Q2EV_OTHER_TELEPORT ) @@ -2442,7 +2442,7 @@ void CLQ2_CalcViewValues (int seat) ops = &oldframe->playerstate[seat]; // see if the player entity was teleported this frame - if ( fabs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256*8 + if ( abs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256*8 || abs(ops->pmove.origin[1] - ps->pmove.origin[1]) > 256*8 || abs(ops->pmove.origin[2] - ps->pmove.origin[2]) > 256*8) ops = ps; // don't interpolate diff --git a/engine/client/image.c b/engine/client/image.c index 93af48494..412fc1bb8 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -2197,7 +2197,8 @@ static qbyte *ReadRawBMPFile(qbyte *buf, int length, int *width, int *height, si data32[i] = pal[buf[x]]; i++; } - buf += h.Width; + //BMP rows are 32-bit aligned. + buf += (h.Width+3)&~3; } if (!OffsetofBMPBits) diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 9239311c0..e4d5eea65 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -3128,7 +3128,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor info->maxplayers = bound(0, ping, 255); ping = atoi(Info_ValueForKey(msg, "timelimit")); - info->tl = bound(-327678, ping, 32767); + info->tl = bound(-32768, ping, 32767); ping = atoi(Info_ValueForKey(msg, "fraglimit")); info->fl = bound(-32768, ping, 32767); diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 8a80da867..f78a0fed8 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -1188,7 +1188,8 @@ int menuentsize; // cvars #define MENUPROGSGROUP "Menu progs control" cvar_t forceqmenu = CVAR("forceqmenu", "0"); -cvar_t pr_menuqc_coreonerror = CVAR("pr_menuqc_coreonerror", "1"); +cvar_t pr_menu_coreonerror = CVAR("pr_menu_coreonerror", "1"); +cvar_t pr_menu_memsize = CVAR("pr_menu_memsize", "64m"); //new generic functions. @@ -2447,7 +2448,7 @@ void VARGS Menu_Abort (char *format, ...) Con_Printf("Menu_Abort: %s\nShutting down menu.dat\n", string); - if (pr_menuqc_coreonerror.value) + if (pr_menu_coreonerror.value) { char *buffer; size_t size = 1024*1024*8; @@ -2585,7 +2586,7 @@ qboolean MP_Init (void) int mprogs; Con_DPrintf("Initializing menu.dat\n"); menu_world.progs = InitProgs(&menuprogparms); - PR_Configure(menu_world.progs, 64*1024*1024, 1, pr_enable_profiling.ival); + PR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 1, pr_enable_profiling.ival); mprogs = PR_LoadProgs(menu_world.progs, "menu.dat"); if (mprogs < 0) //no per-progs builtins. { @@ -2746,7 +2747,8 @@ void MP_RegisterCvarsAndCmds(void) Cvar_Register(&forceqmenu, MENUPROGSGROUP); - Cvar_Register(&pr_menuqc_coreonerror, MENUPROGSGROUP); + Cvar_Register(&pr_menu_coreonerror, MENUPROGSGROUP); + Cvar_Register(&pr_menu_memsize, MENUPROGSGROUP); if (COM_CheckParm("-qmenu")) Cvar_Set(&forceqmenu, "1"); diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 9c5a1e12f..f7167cb38 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -463,15 +463,16 @@ cvar_t gl_screenangle = CVAR("gl_screenangle", "0"); #ifdef VKQUAKE cvar_t vk_stagingbuffers = CVARD ("vk_stagingbuffers", "", "Configures which dynamic buffers are copied into gpu memory for rendering, instead of reading from shared memory. Empty for default settings.\nAccepted chars are u, e, v, 0."); cvar_t vk_submissionthread = CVARD ("vk_submissionthread", "", "Execute submits+presents on a thread dedicated to executing them. This may be a significant speedup on certain drivers."); -cvar_t vk_debug = CVARD ("vk_debug", "0", "Register a debug handler to display driver/layer messages. 2 enables the standard validation layers."); -cvar_t vk_dualqueue = CVARD ("vk_dualqueue", "", "Attempt to use a separate queue for presentation. Blank for default."); +cvar_t vk_debug = CVARFD("vk_debug", "0", CVAR_VIDEOLATCH, "Register a debug handler to display driver/layer messages. 2 enables the standard validation layers."); +cvar_t vk_dualqueue = CVARFD("vk_dualqueue", "", CVAR_VIDEOLATCH, "Attempt to use a separate queue for presentation. Blank for default."); cvar_t vk_busywait = CVARD ("vk_busywait", "", "Force busy waiting until the GPU finishes doing its thing."); cvar_t vk_waitfence = CVARD ("vk_waitfence", "", "Waits on fences, instead of semaphores. This is more likely to result in gpu stalls while the cpu waits."); -cvar_t vk_nv_glsl_shader = CVARD ("vk_loadglsl", "", "Enable direct loading of glsl, where supported by drivers. Do not use in combination with vk_debug 2 (vk_debug should be 1 if you want to see any glsl compile errors). Don't forget to do a vid_restart after."); -cvar_t vk_nv_dedicated_allocation = CVARD ("vk_nv_dedicated_allocation", "", "Flag vulkan memory allocations as dedicated, where applicable."); -cvar_t vk_khr_dedicated_allocation = CVARD ("vk_khr_dedicated_allocation", "", "Flag vulkan memory allocations as dedicated, where applicable."); -cvar_t vk_khr_push_descriptor = CVARD ("vk_khr_push_descriptor", "", "Enables better descriptor streaming."); -cvar_t vk_amd_rasterization_order = CVARD ("vk_amd_rasterization_order", "", "Enables the use of relaxed rasterization ordering, for a small speedup at the minor risk of a little zfighting."); +cvar_t vk_usememorypools = CVARFD("vk_usememorypools", "", CVAR_VIDEOLATCH, "Allocates memory pools for sub allocations. Vulkan has a limit to the number of memory allocations allowed so this should always be enabled, however at this time FTE is unable to reclaim pool memory, and would require periodic vid_restarts to flush them."); +cvar_t vk_nv_glsl_shader = CVARFD("vk_loadglsl", "", CVAR_VIDEOLATCH, "Enable direct loading of glsl, where supported by drivers. Do not use in combination with vk_debug 2 (vk_debug should be 1 if you want to see any glsl compile errors). Don't forget to do a vid_restart after."); +cvar_t vk_khr_get_memory_requirements2 = CVARFD("vk_khr_get_memory_requirements2", "", CVAR_VIDEOLATCH, "Enable extended memory info querires"); +cvar_t vk_khr_dedicated_allocation = CVARFD("vk_khr_dedicated_allocation", "", CVAR_VIDEOLATCH, "Flag vulkan memory allocations as dedicated, where applicable."); +cvar_t vk_khr_push_descriptor = CVARFD("vk_khr_push_descriptor", "", CVAR_VIDEOLATCH, "Enables better descriptor streaming."); +cvar_t vk_amd_rasterization_order = CVARFD("vk_amd_rasterization_order", "", CVAR_VIDEOLATCH, "Enables the use of relaxed rasterization ordering, for a small speedup at the minor risk of a little zfighting."); #endif #ifdef D3D9QUAKE @@ -1011,9 +1012,10 @@ void Renderer_Init(void) Cvar_Register (&vk_dualqueue, VKRENDEREROPTIONS); Cvar_Register (&vk_busywait, VKRENDEREROPTIONS); Cvar_Register (&vk_waitfence, VKRENDEREROPTIONS); + Cvar_Register (&vk_usememorypools, VKRENDEREROPTIONS); Cvar_Register (&vk_nv_glsl_shader, VKRENDEREROPTIONS); - Cvar_Register (&vk_nv_dedicated_allocation, VKRENDEREROPTIONS); + Cvar_Register (&vk_khr_get_memory_requirements2,VKRENDEREROPTIONS); Cvar_Register (&vk_khr_dedicated_allocation,VKRENDEREROPTIONS); Cvar_Register (&vk_khr_push_descriptor, VKRENDEREROPTIONS); Cvar_Register (&vk_amd_rasterization_order, VKRENDEREROPTIONS); @@ -1944,6 +1946,7 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) } else { + int bestpri = -2, pri; for (i = 0; i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { if (!rendererinfo[i] || !rendererinfo[i]->description) @@ -1954,8 +1957,21 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) continue; if (!stricmp(rendererinfo[i]->name[j], com_token)) { - newr->renderer = rendererinfo[i]; - break; + if (rendererinfo[i]->VID_GetPriority) + pri = rendererinfo[i]->VID_GetPriority(); + else if (rendererinfo[i]->rtype == QR_HEADLESS) + pri = -1; //headless renderers are a really poor choice, and will make the user think it buggy. + else if (rendererinfo[i]->rtype == QR_NONE) + pri = 0; //dedicated servers are possible, but we really don't want to use them unless we have no other choice. + else + pri = 1; + + if (pri > bestpri) + { + bestpri = pri; + newr->renderer = rendererinfo[i]; + } + break; //try the next renderer now. } } } diff --git a/engine/client/snd_mix.c b/engine/client/snd_mix.c index dcfdfa50b..b192c752c 100644 --- a/engine/client/snd_mix.c +++ b/engine/client/snd_mix.c @@ -236,7 +236,7 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime) { //hit eof, loop it or stop it if (s->loopstart != -1) /*some wavs contain a loop offset directly in the sound file, such samples loop even if a non-looping builtin was used*/ { - ch->pos &= ~((-1)<pos &= ~((~0u)<pos += s->loopstart<flags & CF_FORCELOOP) /*(static)channels which are explicitly looping always loop from the start*/ diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index abe5833b0..2aac2fd25 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -452,7 +452,7 @@ int Sys_FileTime (char *path) void Sys_mkdir (const char *path) { - mkdir (path, 0777); + mkdir (path, 0760); } qboolean Sys_rmdir (const char *path) { @@ -464,7 +464,10 @@ qboolean Sys_rmdir (const char *path) } qboolean Sys_remove (const char *path) { - return system(va("rm \"%s\"", path)); + //remove is part of c89. + if (remove(path) == -1) + return false; + return true; } qboolean Sys_Rename (const char *oldfname, const char *newfname) { @@ -1003,7 +1006,8 @@ int main (int c, const char **v) sleeptime = Host_Frame(time); oldtime = newtime; - Sys_Sleep(sleeptime); + if (sleeptime) + Sys_Sleep(sleeptime); } } diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 14c366c6e..712d54bd4 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -373,42 +373,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef WAYLANDQUAKE #endif -#ifdef NO_MULTITHREAD - #undef MULTITHREAD -#endif -#ifdef NO_LIBRARIES //catch-all... - #define NO_DIRECTX - #define NO_PNG - #define NO_JPEG - #define NO_ZLIB - #define NO_OGG - #define NO_FREETYPE -#endif -#ifdef NO_OPENAL - #undef AVAIL_OPENAL -#endif -#ifdef NO_PNG - #undef AVAIL_PNGLIB -#endif -#ifdef NO_JPEG - #undef AVAIL_JPEGLIB -#endif -#ifdef NO_OGG - #undef AVAIL_OGGVORBIS -#endif -#ifdef NO_FREETYPE - #undef AVAIL_FREETYPE -#endif -#ifdef NO_ZLIB - #undef AVAIL_ZLIB - #undef AVAIL_PNGLIB - #undef AVAIL_XZDEC - #undef AVAIL_GZDEC -#endif -#if (defined(_MSC_VER) && (_MSC_VER < 1500)) || defined(FTE_SDL) - #undef AVAIL_WASAPI //wasapi is available in the vista sdk, while that's compatible with earlier versions, its not really expected until 2008 -#endif - //include a file to update the various configurations for game-specific configs (hopefully just names) #ifdef BRANDING_INC #include STRINGIFY(BRANDING_INC) @@ -580,6 +544,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifdef ANDROID #define GLESONLY //should reduce the conditions a little // #undef HEADLESSQUAKE + #define NO_FREETYPE + #define NO_OPENAL #endif #if defined(NACL) //stuff is sandboxed. @@ -599,12 +565,47 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef HEADLESSQUAKE #define NO_FREETYPE #endif +#if (defined(_MSC_VER) && (_MSC_VER < 1500)) || defined(FTE_SDL) + #undef AVAIL_WASAPI //wasapi is available in the vista sdk, while that's compatible with earlier versions, its not really expected until 2008 +#endif +#ifdef NO_MULTITHREAD + #undef MULTITHREAD +#endif #ifndef MULTITHREAD //database code requires threads to do stuff async. #undef USE_SQLITE #undef USE_MYSQL #endif +#ifdef NO_LIBRARIES //catch-all... +#define NO_DIRECTX + #define NO_PNG + #define NO_JPEG + #define NO_ZLIB + #define NO_OGG + #define NO_FREETYPE +#endif +#ifdef NO_OPENAL + #undef AVAIL_OPENAL +#endif +#ifdef NO_PNG + #undef AVAIL_PNGLIB +#endif +#ifdef NO_JPEG + #undef AVAIL_JPEGLIB +#endif +#ifdef NO_OGG + #undef AVAIL_OGGVORBIS +#endif +#ifdef NO_FREETYPE + #undef AVAIL_FREETYPE +#endif +#ifdef NO_ZLIB + #undef AVAIL_ZLIB + #undef AVAIL_PNGLIB + #undef AVAIL_XZDEC + #undef AVAIL_GZDEC +#endif #if defined(HAVE_WINSSPI) || defined(HAVE_GNUTLS) #define HAVE_SSL diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index ff1db656e..cb3812380 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -6052,9 +6052,9 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) group[j].boneofs = animmatrix + 12*num_boneinfo*frameinfo[j].firstpose; group[j].numposes = frameinfo[j].posecount; if (*frameinfo[j].name) - snprintf(group[j].name, sizeof(group[j].name), "%s", frameinfo[j].name); + Q_snprintfz(group[j].name, sizeof(group[j].name), "%s", frameinfo[j].name); else - snprintf(group[j].name, sizeof(group[j].name), "frame_%i", j); + Q_snprintfz(group[j].name, sizeof(group[j].name), "frame_%i", j); group[j].loop = frameinfo[j].loop; group[j].rate = frameinfo[j].fps; group[j].skeltype = SKEL_RELATIVE; @@ -6077,7 +6077,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) { group[iframe].boneofs = animmatrix + 12*num_boneinfo*(animinfo[j].firstframe+i); group[iframe].numposes = 1; - snprintf(group[iframe].name, sizeof(group[iframe].name), "%s_%i", animinfo[j].name, i); + Q_snprintfz(group[iframe].name, sizeof(group[iframe].name), "%s_%i", animinfo[j].name, i); group[iframe].loop = true; group[iframe].rate = animinfo[j].fps; group[iframe].skeltype = SKEL_RELATIVE; diff --git a/engine/common/fs.c b/engine/common/fs.c index 506cb80b9..13ef0c916 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -3119,8 +3119,8 @@ const gamemode_info_t gamemode_info[] = { #ifndef NOLEGACY //cmdline switch exename protocol name(dpmaster) identifying file exec dir1 dir2 dir3 dir(fte) full name //two quakes - one without extra game dirs which should avoid fuckups from nquake's configs (which screw over cvars that every nq progs.dat depends upon but which the ezquake id1-only less-compatible gamecode ignores). - {"-quake", "qw", "FTE-Quake DarkPlaces-Quake", {"id1/pak0.pak", "id1/quake.rc"},QCFG, {"id1", "qw", "*fte"}, "Quake", "https://fte.triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, - {"-netquake", "q1", "FTE-Quake DarkPlaces-Quake", {"id1/pak0.pak", "id1/quake.rc"},QCFG, {"id1"}, "Quake", "https://fte.triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, + {"-quake", "q1", "FTE-Quake DarkPlaces-Quake", {"id1/pak0.pak", "id1/quake.rc"},QCFG, {"id1", "qw", "*fte"}, "Quake", "https://fte.triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, + {"-netquake", "nq", "FTE-Quake DarkPlaces-Quake", {"id1/pak0.pak", "id1/quake.rc"},QCFG, {"id1"}, "Quake", "https://fte.triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, //quake's mission packs should not be favoured over the base game nor autodetected //third part mods also tend to depend upon the mission packs for their huds, even if they don't use any other content. //and q2 also has a rogue/pak0.pak file that we don't want to find and cause quake2 to look like dissolution of eternity @@ -6177,14 +6177,54 @@ void COM_InitFilesystem (void) #endif } #else - //yay for unix!. - ev = getenv("HOME"); + //on unix, we use environment settings. + //if $HOME/.fte/ exists then we use that because of legacy reasons. + //but if it doesn't exist then we use $XDG_DATA_HOME/.fte instead + //we used to use $HOME/.#HOMESUBDIR/ but this is now only used if it actually exists AND the new path doesn't. + //new installs use $XDG_DATA_HOME/#HOMESUBDIR/ instead + + ev = getenv("FTEHOME"); if (ev && *ev) { if (ev[strlen(ev)-1] == '/') - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s.fte/", ev); + Q_strncpyz(com_homepath, ev, sizeof(com_homepath)); else - Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/.fte/", ev); + Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/", ev); + usehome = true; // always use home on unix unless told not to + ev = NULL; + } + else + ev = getenv("HOME"); + if (ev && *ev) + { + const char *xdghome; + char oldhome[MAX_OSPATH]; + char newhome[MAX_OSPATH]; + struct stat s; + +#ifdef GAME_SHORTNAME +#define HOMESUBDIR GAME_SHORTNAME +#else +#define HOMESUBDIR "fte" //FIXME: this should come from the manifest, as fte_GAME or something +#endif + + if (ev[strlen(ev)-1] == '/') + Q_snprintfz(oldhome, sizeof(oldhome), "%s."HOMESUBDIR"/", ev); + else + Q_snprintfz(oldhome, sizeof(oldhome), "%s/."HOMESUBDIR"/", ev); + + xdghome = getenv("XDG_DATA_HOME"); + if (!xdghome || !*xdghome) + xdghome = va("%s/.local/share", ev); + if (xdghome[strlen(xdghome)-1] == '/') + Q_snprintfz(newhome, sizeof(newhome), "%s"HOMESUBDIR"/", xdghome); + else + Q_snprintfz(newhome, sizeof(newhome), "%s/"HOMESUBDIR"/", xdghome); + + if (stat(newhome, &s) == -1 && stat(oldhome, &s) != -1) + Q_strncpyz(com_homepath, oldhome, sizeof(com_homepath)); + else + Q_strncpyz(com_homepath, newhome, sizeof(com_homepath)); usehome = true; // always use home on unix unless told not to } #endif diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index ba04af2c0..a47a18b58 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -373,7 +373,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) Con_Printf("NET_CompareAdr: Bad address type\n"); return false; } - + /* =================== NET_CompareBaseAdr @@ -524,7 +524,7 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) char *rs = s; char *prot = ""; #ifdef IPPROTO_IPV6 - qboolean doneblank; + int doneblank; #endif switch(a->prot) @@ -590,7 +590,7 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) { char *p; int i; - if (!*(int*)&a->address.ip6[0] && + if (!*(int*)&a->address.ip6[0] && !*(int*)&a->address.ip6[4] && !*(short*)&a->address.ip6[8] && *(short*)&a->address.ip6[10] == (short)0xffff) @@ -756,7 +756,7 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) { char *p; int i, doneblank; - if (!*(int*)&a->address.ip6[0] && + if (!*(int*)&a->address.ip6[0] && !*(int*)&a->address.ip6[4] && !*(short*)&a->address.ip6[8] && *(short*)&a->address.ip6[10] == (short)0xffff) @@ -996,7 +996,7 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qs { if (addrfamily) addrfamily[i] = ((struct sockaddr*)sadr)->sa_family; - + if (((struct sockaddr*)&sadr[i])->sa_family == AF_INET) { if (!((struct sockaddr_in *)&sadr[i])->sin_port) @@ -2108,7 +2108,7 @@ static void FTENET_NATPMP_Refresh(pmpcon_t *pmp, short oldport, ftenet_connectio { int i, m; netadr_t adr; - + netadr_t addr[64]; struct ftenet_generic_connection_s *con[sizeof(addr)/sizeof(addr[0])]; int flags[sizeof(addr)/sizeof(addr[0])]; @@ -2145,7 +2145,7 @@ static void FTENET_NATPMP_Refresh(pmpcon_t *pmp, short oldport, ftenet_connectio !*(int*)&adr.address.ip6[0] && !*(int*)&adr.address.ip6[4] && !*(short*)&adr.address.ip6[8] && - *(short*)&adr.address.ip6[10]==(short)0xffff && + *(short*)&adr.address.ip6[10]==(short)0xffff && !*(int*)&adr.address.ip6[12]) { *(int*)adr.address.ip = *(int*)&adr.address.ip6[12]; @@ -2228,7 +2228,7 @@ qboolean FTENET_NATPMP_GetPacket(struct ftenet_generic_connection_s *con) } neterr_t FTENET_NATPMP_SendPacket(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to) { - return false; + return NETERR_NOROUTE; } void FTENET_NATPMP_Close(struct ftenet_generic_connection_s *con) { @@ -2786,7 +2786,7 @@ int FTENET_Generic_GetLocalAddresses(struct ftenet_generic_connection_s *con, un !*(int*)&adr.address.ip6[0] && !*(int*)&adr.address.ip6[4] && !*(short*)&adr.address.ip6[8] && - *(short*)&adr.address.ip6[10]==(short)0xffff && + *(short*)&adr.address.ip6[10]==(short)0xffff && !*(int*)&adr.address.ip6[12]) { //ipv6 socket bound to the ipv4-any address is a bit weird, but oh well. @@ -3105,7 +3105,7 @@ ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(qboolean isserv protocol = 0; break; } - + if (adr.type == NA_INVALID) { @@ -4050,7 +4050,7 @@ qboolean FTENET_TCP_ParseHTTPRequest(ftenet_tcpconnect_connection_t *con, ftenet } else if (j+4 <= i && !strncmp(&st->inbuffer[j], "gzip", 4) && (j+4==i || st->inbuffer[j+4] == ';' || st->inbuffer[j+4] == ',')) acceptsgzip = true; - + while (j < i && st->inbuffer[j] != ',') j++; if (j < i && st->inbuffer[j] == ',') @@ -4585,7 +4585,7 @@ closesvstream: if (ctrl & 0x7000) { Con_Printf ("%s: reserved bits set\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); - goto closesvstream; + goto closesvstream; } if ((ctrl & 0x7f) == 127) { @@ -4594,13 +4594,13 @@ closesvstream: if (sizeof(ullpaylen) < 8) { Con_Printf ("%s: payload frame too large\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); - goto closesvstream; + goto closesvstream; } else { if (payoffs + 8 > st->inlen) break; - ullpaylen = + ullpaylen = (quint64_t)((unsigned char*)st->inbuffer)[payoffs+0]<<56u | (quint64_t)((unsigned char*)st->inbuffer)[payoffs+1]<<48u | (quint64_t)((unsigned char*)st->inbuffer)[payoffs+2]<<40u | @@ -4612,12 +4612,12 @@ closesvstream: if (ullpaylen < 0x10000) { Con_Printf ("%s: payload size (%"PRIu64") encoded badly\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), ullpaylen); - goto closesvstream; + goto closesvstream; } if (ullpaylen > 0x40000) { Con_Printf ("%s: payload size (%"PRIu64") is abusive\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), ullpaylen); - goto closesvstream; + goto closesvstream; } paylen = ullpaylen; payoffs += 8; @@ -4627,13 +4627,13 @@ closesvstream: { if (payoffs + 2 > st->inlen) break; - paylen = + paylen = ((unsigned char*)st->inbuffer)[payoffs+0]<<8 | ((unsigned char*)st->inbuffer)[payoffs+1]<<0; if (paylen < 126) { Con_Printf ("%s: payload size encoded badly\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); - goto closesvstream; + goto closesvstream; } payoffs += 2; } @@ -5783,7 +5783,7 @@ typedef struct int datasock; //only if we're a client size_t numclients; - struct + struct { netadr_t remoteadr; int datasock; @@ -6413,7 +6413,7 @@ qboolean NET_UpdateRates(ftenet_connections_t *collection, qboolean inbound, siz int ctime; if (!collection) return false; - + if (inbound) { cls.sockets->bytesin += size; diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 5982df6c4..d59da8ac5 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -1073,7 +1073,7 @@ qintptr_t VARGS Plug_Net_SetTLSClient(void *offset, quintptr_t mask, const qintp { pluginstream_t *stream; unsigned int handle = VM_LONG(arg[0]); - if (handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug) + if (handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug) { Con_Printf("Plug_Net_SetTLSClient: socket does not belong to you (or is invalid)\n"); return -2; @@ -1106,7 +1106,7 @@ qintptr_t VARGS Plug_Net_GetTLSBinding(void *offset, quintptr_t mask, const qint return -2; if (VM_OOB(arg[1], *bindsize)) return -2; - if (handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug) + if ((size_t)handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug) { Con_Printf("Plug_Net_GetTLSBinding: socket does not belong to you (or is invalid)\n"); return -2; diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 212559582..240393545 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -100,14 +100,15 @@ void PF_Common_RegisterCvars(void) qofs_t PR_ReadBytesString(char *str) { - size_t u = strtoul(str, &str, 0); + //use doubles, so we can cope with eg "5.3mb" or much larger values + double d = strtod(str, &str); if (*str == 'g') - u *= 1024*1024*1024; + d *= 1024*1024*1024; if (*str == 'm') - u *= 1024*1024; + d *= 1024*1024; if (*str == 'k') - u *= 1024; - return u; + d *= 1024; + return d; } //just prints out a warning with stack trace. so I can throttle spammy stack traces. diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index f01ca011f..e164d0361 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -76,8 +76,8 @@ typedef unsigned int FT_Pixel_Mode; //for consistency even without freetype supp #ifndef FT_PIXEL_MODE_BGRA #define FT_PIXEL_MODE_BGRA 7 //added in FT 2.5 #endif -#define FT_PIXEL_MODE_RGBA_SA (~(FT_Pixel_Mode)0) //RGBA, straight alpha. not in freetype. -#define FT_PIXEL_MODE_RGBA (~(FT_Pixel_Mode)1) //RGBA, premultiplied alpha. not in freetype. +#define FT_PIXEL_MODE_RGBA_SA (100) //RGBA, straight alpha. not in freetype. +#define FT_PIXEL_MODE_RGBA (101) //RGBA, premultiplied alpha. not in freetype. static const char *imgs[] = { @@ -672,7 +672,7 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, FT out += PLANEWIDTH*4; } } - else if (pixelmode == FT_PIXEL_MODE_RGBA_SA) + else if ((unsigned int)pixelmode == FT_PIXEL_MODE_RGBA_SA) { //rgba font for (y = -pad; y < 0; y++) { @@ -739,7 +739,7 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, FT out += PLANEWIDTH*4; } } - else if (pixelmode == FT_PIXEL_MODE_RGBA) + else if ((unsigned int)pixelmode == FT_PIXEL_MODE_RGBA) { //bgra srgb font, already premultiplied for (y = -pad; y < 0; y++) { diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 8a299dcc8..9a1269f7f 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -6577,7 +6577,7 @@ static void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elem PR_BIError(prinst, "brush: elementcount %u is too large\n", (unsigned int)elementcount); return NULL; } - if (qcptr < 0 || qcptr+(elementsize*elementcount) > prinst->stringtablesize) + if (qcptr+(elementsize*elementcount) > (size_t)prinst->stringtablesize) { PR_BIError(prinst, "brush: invalid qc pointer\n"); return NULL; @@ -6803,7 +6803,7 @@ void QCBUILTIN PF_brush_selected(pubprogfuncs_t *prinst, struct globalvars_s *pr heightmap_t *hm = mod?mod->terrain:NULL; unsigned int brushid = G_INT(OFS_PARM1); // unsigned int faceid = G_INT(OFS_PARM2); - unsigned int state = G_FLOAT(OFS_PARM3); + int state = G_FLOAT(OFS_PARM3); unsigned int i; brushes_t *br; @@ -6842,17 +6842,17 @@ void QCBUILTIN PF_brush_selected(pubprogfuncs_t *prinst, struct globalvars_s *pr // {"brush_calcfacepoints",PF_brush_calcfacepoints,0,0, 0, 0, D("int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints)", "Determines the points of the specified face, if the specified brush were to actually be created.")}, void QCBUILTIN PF_brush_calcfacepoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - unsigned int faceid = G_INT(OFS_PARM0); - unsigned int numfaces = G_INT(OFS_PARM2); + size_t faceid = G_INT(OFS_PARM0); + size_t numfaces = G_INT(OFS_PARM2); qcbrushface_t *in_faces = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, false); - unsigned int maxpoints = G_INT(OFS_PARM4); + size_t maxpoints = G_INT(OFS_PARM4); vec3_t *out_verts = validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*out_verts), maxpoints, false); vecV_t facepoints[256]; vec4_t planes[256]; unsigned int j, numpoints; faceid--; - if (faceid < 0 || faceid >= numfaces) + if ((size_t)faceid >= numfaces) { G_INT(OFS_RETURN) = 0; return; diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 422a6863f..06ce980c3 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -3153,7 +3153,7 @@ qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int psl) #ifdef VKQUAKE case PSL_VULKAN: visinfo = &vinfodef; - if (!x11.pXMatchVisualInfo(vid_dpy, scrnum, info->bpp?info->bpp:DefaultDepth(vid_dpy, scrnum), TrueColor, visinfo)) + if (!x11.pXMatchVisualInfo(vid_dpy, scrnum, min(24,info->bpp?info->bpp:DefaultDepth(vid_dpy, scrnum)), TrueColor, visinfo)) { Sys_Error("Couldn't choose visual for vulkan\n"); } diff --git a/engine/gl/gl_vidsdl.c b/engine/gl/gl_vidsdl.c index 03fee99b8..aa2bd2ef9 100644 --- a/engine/gl/gl_vidsdl.c +++ b/engine/gl/gl_vidsdl.c @@ -221,7 +221,7 @@ static qboolean SDLVID_Init (rendererstate_t *info, unsigned char *palette, r_qr sdlwindow = SDL_CreateWindow(FULLENGINENAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, info->width, info->height, flags); if (!sdlwindow) { - Con_Printf("Couldn't set video mode: %s\n", SDL_GetError()); + Con_Printf("SDL_CreateWindow failed: %s\n", SDL_GetError()); return false; } CL_UpdateWindowTitle(); @@ -379,7 +379,9 @@ void GLVID_DeInit (void) #endif SDL_QuitSubSystem(SDL_INIT_VIDEO); +#ifdef OPENGL_SDL GL_ForgetPointers(); +#endif } diff --git a/engine/gl/gl_vidwayland.c b/engine/gl/gl_vidwayland.c index 3881b0db7..6dc7afb26 100644 --- a/engine/gl/gl_vidwayland.c +++ b/engine/gl/gl_vidwayland.c @@ -700,7 +700,20 @@ static void WL_SetCaption(const char *text) static int WL_GetPriority(void) { - char *dpyname = getenv("WAYLAND_DISPLAY"); + //2 = above x11, 0 = below x11. + char *stype = getenv("XDG_SESSION_TYPE"); + char *dpyname; + if (!strcmp(stype, "wayland")) + return 2; + if (!strcmp(stype, "x11")) + return 0; + if (!strcmp(stype, "tty")) //FIXME: support this! + return 0; + + //otherwise if both WAYLAND_DISPLAY and DISPLAY are defined, then we assume that we were started from xwayland wrapper thing, and that the native/preferred windowing system is wayland. + //(lets just hope our wayland support is comparable) + + dpyname = getenv("WAYLAND_DISPLAY"); if (dpyname && *dpyname) return 2; //something above X11. return 0; //default. diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index c18e78fb2..daa90f1d0 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -1448,7 +1448,7 @@ qboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyF dl->threadenable = true; #if defined(LOADERTHREAD) && !defined(NPFTE) - if (dlthreads < 0) + if (dlthreads < 4) #endif { dl->threadctx = Sys_CreateThread("download", DL_Thread_Work, dl, THREADP_NORMAL, 0); diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 709593d4a..88c46db6d 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -205,7 +205,7 @@ static void PR_memvalidate (progfuncs_t *progfuncs) l = 0; while (b) { - if (b < 0 || b >= prinst.addressableused) + if ((size_t)b >= (size_t)prinst.addressableused) { printf("PF_memalloc: memory corruption\n"); PR_StackTrace(&progfuncs->funcs, false); diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index e339d391b..bce8fb12e 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -310,7 +310,11 @@ typedef union QCC_eval_s { QCC_string_t string; float _float; - float vector[1]; +#ifdef __GNUC__ + float vector[0]; //gnuc extension. I'm using it to mute clang warnings. +#else + float vector[1]; //should be 3, except that then eval_t would be too big. +#endif func_t function; int _int; // union QCC_eval_s *ptr; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 4d8ffe561..4e13c2937 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -7360,7 +7360,7 @@ vectorarrayindex: i = tmp.sym->symboldata[tmp.ofs]._float; else i = -1; - if (i < 0 || i >= 3) + if ((unsigned)i >= 3u) QCC_PR_ParseErrorPrintSRef(0, r->base, "(vector) array index out of bounds"); } else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && flag_boundchecks) @@ -7382,7 +7382,7 @@ vectorarrayindex: i = tmp.sym->symboldata[tmp.ofs]._float; else i = -1; - if (i < 0 || i >= 3) + if ((unsigned)i >= 3u) QCC_PR_ParseErrorPrintSRef(0, r->base, "(vector) array index out of bounds"); } else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && flag_boundchecks) @@ -7405,7 +7405,7 @@ vectorarrayindex: i = tmp.sym->symboldata[tmp.ofs]._float; else i = -1; - if (i < 0 || i >= arraysize) + if ((unsigned)i >= (unsigned)arraysize) QCC_PR_ParseErrorPrintSRef(0, r->base, "(constant) array index out of bounds (0 <= %i < %i)", i, arraysize); } else diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 65570cd04..e1732d2d8 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -351,7 +351,7 @@ compiler_flag_t compiler_flag[] = { {&keyword_accumulate, nondefaultkeyword,"accumulate", "Keyword: accumulate", "Disables the 'accumulate' keyword."}, //options - {&flag_acc, 0, "acc", "Reacc support", "Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows ¦ as EOF. Whilst case insensitivity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc."}, //reacc like behaviour of src files. + {&flag_acc, 0, "acc", "Reacc support", "Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows | for linebreaks. Whilst case insensitivity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc."}, //reacc like behaviour of src files. {&flag_qccx, FLAG_MIDCOMPILE,"qccx", "QCCX syntax", "WARNING: This syntax makes mods inherantly engine specific.\nDo NOT use unless you know what you're doing.This is provided for compatibility only\nAny entity hacks will be unsupported in FTEQW, DP, and others, resulting in engine crashes if the code in question is executed."}, {&keywords_coexist, FLAG_ASDEFAULT, "kce", "Keywords Coexist", "If you want keywords to NOT be disabled when they a variable by the same name is defined, check here."}, {&output_parms, 0, "parms", "Define offset parms", "if PARM0 PARM1 etc should be defined by the compiler. These are useful if you make use of the asm keyword for function calls, or you wish to create your own variable arguments. This is an easy way to break decompilers."}, //controls weather to define PARMx for the parms (note - this can screw over some decompilers) @@ -2050,18 +2050,9 @@ strofs = (strofs+3)&~3; qtst[i].line = statements[i].linenum; qtst[i].op = PRLittleShort((unsigned short)statements[i].op); - if (a < 0) - qtst[i].a = PRLittleShort((short)a); - else - qtst[i].a = (unsigned short)PRLittleShort((unsigned short)a); - if (b < 0) - qtst[i].b = PRLittleShort((short)b); - else - qtst[i].b = (unsigned short)PRLittleShort((unsigned short)b); - if (c < 0) - qtst[i].c = PRLittleShort((short)c); - else - qtst[i].c = (unsigned short)PRLittleShort((unsigned short)c); + qtst[i].a = (unsigned short)PRLittleShort((unsigned short)a); + qtst[i].b = (unsigned short)PRLittleShort((unsigned short)b); + qtst[i].c = (unsigned short)PRLittleShort((unsigned short)c); } // no compression @@ -2106,18 +2097,10 @@ strofs = (strofs+3)&~3; if (((signed)a >= (signed)numpr_globals && statements[i].a.sym) || ((signed)b >= (signed)numpr_globals && statements[i].b.sym) || ((signed)c >= (signed)numpr_globals && statements[i].c.sym)) printf("invalid offset on %s instruction\n", pr_opcodes[statements[i].op].opname); #endif - if (a < 0) - statements16[i].a = PRLittleShort(a); - else - statements16[i].a = (unsigned short)PRLittleShort(a); - if (b < 0) - statements16[i].b = PRLittleShort((short)b); - else - statements16[i].b = (unsigned short)PRLittleShort((unsigned short)b); - if (c < 0) - statements16[i].c = PRLittleShort((short)c); - else - statements16[i].c = (unsigned short)PRLittleShort((unsigned short)c); + //truncate to 16bit. should probably warn if the high bits are not 0x0000 or 0xffff + statements16[i].a = (unsigned short)PRLittleShort((unsigned short)a); + statements16[i].b = (unsigned short)PRLittleShort((unsigned short)b); + statements16[i].c = (unsigned short)PRLittleShort((unsigned short)c); } if (progs.blockscompressed&1) diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index abd891241..b90854bf6 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -2265,7 +2265,7 @@ qboolean PR_LoadQ1QVM(void) q1qvmprogfuncs.edicttable_length = sv.world.max_edicts; limit = VM_MemoryMask(q1qvm); - if (gd.sizeofent < 0 || gd.sizeofent > 0xffffffff / gd.maxedicts) + if (gd.sizeofent > 0xffffffff / gd.maxedicts) gd.sizeofent = 0xffffffff / gd.maxedicts; if ((quintptr_t)gd.ents+(gd.sizeofent*gd.maxedicts) < (quintptr_t)gd.ents || (quintptr_t)gd.ents > (quintptr_t)limit) gd.ents = 0; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index c06526984..306f38d94 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -104,6 +104,7 @@ cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond #ifdef QWOVERQ3 cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0"); #endif +cvar_t sv_reconnectlimit = CVARD("sv_reconnectlimit", "0", "Blocks dupe connection within the specified length of time ."); extern cvar_t net_enable_dtls; cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once."); cvar_t sv_highchars = CVAR("sv_highchars", "1"); @@ -2782,6 +2783,12 @@ client_t *SVC_DirectConnect(void) if (NET_CompareBaseAdr (&adr, &cl->netchan.remote_address) && ((protocol == SCP_QUAKEWORLD && cl->netchan.qport == qport) || adr.port == cl->netchan.remote_address.port )) { + if (realtime - cl->connection_started < sv_reconnectlimit.value) + { + Con_Printf ("%s:reconnect rejected: too soon\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + return NULL; + } + if (cl->state == cs_connected) { if (cl->protocol != protocol) @@ -5141,6 +5148,7 @@ void SV_InitLocal (void) Cvar_Register (&sv_listen_q3, cvargroup_servercontrol); #endif sv_listen_qw.restriction = RESTRICT_MAX; //no disabling this over rcon. + Cvar_Register (&sv_reconnectlimit, cvargroup_servercontrol); Cvar_Register (&fraglog_public, cvargroup_servercontrol); SVNET_RegisterCvars(); diff --git a/engine/server/sv_sql.c b/engine/server/sv_sql.c index e17d3686a..3d8dc2036 100644 --- a/engine/server/sv_sql.c +++ b/engine/server/sv_sql.c @@ -1208,7 +1208,7 @@ void SQL_ServerCycle (void) qreq->results = qres; if (developer.ival) - if (qres->error) + if (*qres->error) Con_Printf("%s\n", qres->error); if (qreq->state == SR_ABORTED) { diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index 7e8bd3b2a..5586fcaad 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -775,6 +775,22 @@ static int Sys_CheckChRoot(void) //SSL_InitGlobal(true); //make sure we load our public cert from outside the sandbox. an exploit might still be able to find it in memory though. FIXME: disabled in case this reads from somewhere bad - we're still root. #endif + { //this protects against stray setuid programs like su reading passwords from /etc/passwd et al + //there shouldn't be anyway so really this is pure paranoia. + //(the length thing is to avoid overflows inside va giving false negatives.) + struct stat s; + if (strlen(newroot) > 4096 || lstat(va("%s/etc/", newroot), &s) != -1) + { + printf("refusing to chroot to %s - contains an /etc directory\n", newroot); + return -1; + } + if (strlen(newroot) > 4096 || lstat(va("%s/proc/", newroot), &s) != -1) + { + printf("refusing to chroot to %s - contains a /proc directory\n", newroot); + return -1; + } + } + printf("Changing root dir to \"%s\"\n", newroot); if (chroot(newroot)) { @@ -783,10 +799,16 @@ static int Sys_CheckChRoot(void) } chdir("/"); //chroot does NOT change the working directory, so we need to make sure that happens otherwise still a way out. + //signal to the fs.c code to use an explicit base home dir. if (newhome) - setenv("HOME", va("/user/%s", newhome), true); + setenv("FTEHOME", va("/user/%s", newhome), true); else - setenv("HOME", va("/user/%i", ruid), true); + setenv("FTEHOME", va("/user/%i", ruid), true); + + //these paths are no longer valid. + setenv("HOME", "", true); + setenv("XDG_DATA_HOME", "", true); + setenv("PWD", "/", true); ret = true; diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 795fb1562..16e388336 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -802,14 +802,7 @@ void SVQ2_ConfigStrings_f (void) return; } - start = atoi(Cmd_Argv(2)); - - if (start < 0) - { - Con_Printf ("SV_Configstrings_f: %s tried crashing us\n", host_client->name); - host_client->drop = true; - return; - } + start = strtoul(Cmd_Argv(2), NULL, 0); // write a packet full of data @@ -820,7 +813,7 @@ void SVQ2_ConfigStrings_f (void) if (str && *str) { MSG_WriteByte (&host_client->netchan.message, svcq2_configstring); - MSG_WriteShort (&host_client->netchan.message, start); + MSG_WriteShort (&host_client->netchan.message, (unsigned short)start); MSG_WriteString (&host_client->netchan.message, str); } start++; @@ -7898,7 +7891,7 @@ void SV_ExecuteClientMessage (client_t *cl) #ifdef Q2SERVER void SVQ2_ExecuteClientMessage (client_t *cl) { - enum clcq2_ops_e c; + int c; char *s; usercmd_t oldest, oldcmd, newcmd; q2client_frame_t *frame; @@ -7978,7 +7971,7 @@ void SVQ2_ExecuteClientMessage (client_t *cl) if (c == -1) break; - switch (c) + switch ((enum clcq2_ops_e)c) { default: Con_Printf ("SVQ2_ReadClientMessage: unknown command char %i\n", c); diff --git a/engine/shaders/Makefile b/engine/shaders/Makefile index 0b31a6fe2..44c6eaabc 100644 --- a/engine/shaders/Makefile +++ b/engine/shaders/Makefile @@ -1,4 +1,8 @@ -CC=i686-pc-mingw32-gcc + +#NOTE: if you're on windows, you might want to use CC=i686-pc-mingw32-gcc + +CC ?= gcc +VKSDKPATH ?= ~/VulkanSDK/1.1.73.0/x86_64/bin/ all: @@ -22,12 +26,12 @@ makevulkanblob: makevulkanblob.c $(CC) $< -o $@ vulkanblobs/%.fvb: vulkan/%.glsl makevulkanblob vulkan/sys/defs.h vulkan/sys/fog.h vulkan/sys/offsetmapping.h vulkan/sys/skeletal.h - ./makevulkanblob $< $@ + @echo Making $< + @PATH=$(PATH):$(VKSDKPATH) ./makevulkanblob $< $@ #vulkanblobs/%.fvb: glsl/%.glsl makevulkanblob vulkan/sys/defs.h vulkan/sys/fog.h vulkan/sys/offsetmapping.h vulkan/sys/skeletal.h # ./makevulkanblob $< $@ all: generatebuiltinsl $(ALLNAMES) - which glslangValidator - ./generatebuiltinsl \ No newline at end of file + ./generatebuiltinsl diff --git a/engine/shaders/makevulkanblob.c b/engine/shaders/makevulkanblob.c index 774ec371f..4d4d8a192 100644 --- a/engine/shaders/makevulkanblob.c +++ b/engine/shaders/makevulkanblob.c @@ -108,8 +108,13 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char FILE *glsl = fopen(glslname, "rt"); if (!glsl) + { + printf("Unable to read %s\n", glslname); return 0; + } FILE *temp = fopen(tempname, "wt"); + if (!temp) + printf("Unable to write %s\n", tempname); while(fgets(command, sizeof(command), glsl)) { if (inheader && !strncmp(command, "!!", 2)) @@ -130,14 +135,14 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char { type = command[5] == 'i' || command[5] == 'f' || command[5] == 'b'; size = type?1:(command[5]-'0'); - arg = strtok(command+7, " ,=\n"); + arg = strtok(command+7, " ,=\r\n"); type = command[6-type] - 'a' + 'A'; } else { type = command[6] == 'i' || command[6] == 'f' || command[6] == 'b'; size = type?1:(command[6]-'0'); - arg = strtok(command+8, " ,=\n"); + arg = strtok(command+8, " ,=\r\n"); type = command[7-type]; } @@ -154,8 +159,13 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char { if (arg) { - arg = strtok(NULL, " ,=\n"); - if (type == 'f' || type == 'F') + arg = strtok(NULL, " ,=\r\n"); + if (!arg) + { + printf("%s has no default value. Assuming 0\n", cb+4); + u[i].u = 0; //0 either way. + } + else if (type == 'f' || type == 'F') u[i].f = atof(arg); else u[i].u = atoi(arg); @@ -172,7 +182,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char } else if (!strncmp(command, "!!permu", 7)) { - char *arg = strtok(command+7, " ,\n"); + char *arg = strtok(command+7, " ,\r\n"); for (i = 0; permutationnames[i]; i++) { if (!strcmp(arg, permutationnames[i])) @@ -191,7 +201,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char } else if (!strncmp(command, "!!samps", 7)) { - char *arg = strtok(command+7, " ,\n"); + char *arg = strtok(command+7, " ,\r\n"); do { //light @@ -235,7 +245,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char blob->numtextures = atoi(arg); else printf("Unknown texture: \"%s\"\n", arg); - } while((arg = strtok(NULL, " ,\n"))); + } while((arg = strtok(NULL, " ,\r\n"))); } continue; } @@ -397,26 +407,34 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char snprintf(command, sizeof(command), /*preprocess the vertex shader*/ +#ifdef _WIN32 "echo #version 450 core > %s && " +#else + "echo \"#version 450 core\" > %s && " +#endif "cpp %s -DVULKAN -DVERTEX_SHADER -P >> %s && " /*preprocess the fragment shader*/ +#ifdef _WIN32 "echo #version 450 core > %s && " +#else + "echo \"#version 450 core\" > %s && " +#endif "cpp %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" /*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" +// " && spirv-remap -i vert.spv frag.spv -o vulkan/remap" ,tempvert, tempname, tempvert, tempfrag, tempname, tempfrag, tempvert, tempfrag); system(command); - unlink(tempname); - unlink(tempvert); - unlink(tempfrag); + remove(tempname); + remove(tempvert); + remove(tempfrag); return 1; } @@ -451,12 +469,20 @@ int main(int argc, const char **argv) fclose(o); r = 0; } + else + printf("Unable to write blob %s\n", blobname); } } - fclose(f); - fclose(v); - unlink("vert.spv"); - unlink("frag.spv"); + if (f) + fclose(f); + else + printf("Unable to read frag.spv\n"); + if (v) + fclose(v); + else + printf("Unable to read vert.spv\n"); + remove("vert.spv"); + remove("frag.spv"); return r; -} \ No newline at end of file +} diff --git a/engine/shaders/vulkan/bloom_blur.glsl b/engine/shaders/vulkan/bloom_blur.glsl index 52d1bbf75..6a1ff4b6e 100644 --- a/engine/shaders/vulkan/bloom_blur.glsl +++ b/engine/shaders/vulkan/bloom_blur.glsl @@ -2,7 +2,7 @@ #include "sys/defs.h" //apply gaussian filter -varying vec2 tc; +layout(location=0) varying vec2 tc; #ifdef VERTEX_SHADER void main () @@ -20,4 +20,4 @@ void main () 0.375 * texture2D(s_t0, tc) + 0.3125 * texture2D(s_t0, tc + e_glowmod.st); } -#endif \ No newline at end of file +#endif diff --git a/engine/shaders/vulkan/bloom_filter.glsl b/engine/shaders/vulkan/bloom_filter.glsl index 38e4536b4..5a1dddadd 100644 --- a/engine/shaders/vulkan/bloom_filter.glsl +++ b/engine/shaders/vulkan/bloom_filter.glsl @@ -4,7 +4,7 @@ //the bloom filter //filter out any texels which are not to bloom -varying vec2 tc; +layout(location=0) varying vec2 tc; #ifdef VERTEX_SHADER void main () @@ -18,4 +18,4 @@ void main () { gl_FragColor.rgb = (texture2D(s_t0, tc).rgb - cvar_r_bloom_filter)/(1.0-cvar_r_bloom_filter); } -#endif \ No newline at end of file +#endif diff --git a/engine/shaders/vulkan/bloom_final.glsl b/engine/shaders/vulkan/bloom_final.glsl index d169693c7..e82f27c78 100644 --- a/engine/shaders/vulkan/bloom_final.glsl +++ b/engine/shaders/vulkan/bloom_final.glsl @@ -5,7 +5,7 @@ //add them together //optionally apply tonemapping -varying vec2 tc; +layout(location=0) varying vec2 tc; #ifdef VERTEX_SHADER void main () diff --git a/engine/shaders/vulkan/defaultfill.glsl b/engine/shaders/vulkan/defaultfill.glsl index a2d5ad8a9..c4045b1b6 100644 --- a/engine/shaders/vulkan/defaultfill.glsl +++ b/engine/shaders/vulkan/defaultfill.glsl @@ -1,8 +1,8 @@ #include "sys/defs.h" -#ifdef VERTEX_SHADER -varying vec4 vc; +layout(location=0) varying vec4 vc; +#ifdef VERTEX_SHADER void main () { vc = v_colour; @@ -11,9 +11,8 @@ void main () #endif #ifdef FRAGMENT_SHADER -varying vec4 vc; void main () { gl_FragColor = vc; } -#endif \ No newline at end of file +#endif diff --git a/engine/shaders/vulkan/defaultsky.glsl b/engine/shaders/vulkan/defaultsky.glsl index a0199ddb7..e5e56ab8d 100644 --- a/engine/shaders/vulkan/defaultsky.glsl +++ b/engine/shaders/vulkan/defaultsky.glsl @@ -8,8 +8,9 @@ //regular sky shader for scrolling q1 skies //the sky surfaces are thrown through this as-is. +layout(location=0) varying vec3 pos; + #ifdef VERTEX_SHADER -varying vec3 pos; void main () { pos = v_position.xyz; @@ -17,7 +18,6 @@ void main () } #endif #ifdef FRAGMENT_SHADER -varying vec3 pos; void main () { vec2 tccoord; diff --git a/engine/shaders/vulkan/defaultskybox.glsl b/engine/shaders/vulkan/defaultskybox.glsl index 684e9173f..29882a0c4 100644 --- a/engine/shaders/vulkan/defaultskybox.glsl +++ b/engine/shaders/vulkan/defaultskybox.glsl @@ -6,7 +6,7 @@ //simple shader for simple skyboxes. -varying vec3 pos; +layout(location=0) varying vec3 pos; #ifdef VERTEX_SHADER void main () { diff --git a/engine/shaders/vulkan/defaultsprite.glsl b/engine/shaders/vulkan/defaultsprite.glsl index ed51efdb0..995e2d318 100644 --- a/engine/shaders/vulkan/defaultsprite.glsl +++ b/engine/shaders/vulkan/defaultsprite.glsl @@ -8,9 +8,11 @@ #include "sys/defs.h" #include "sys/fog.h" + +layout(location=0) varying vec2 tc; +layout(location=1) varying vec4 vc; + #ifdef VERTEX_SHADER -varying vec2 tc; -varying vec4 vc; void main () { tc = v_texcoord; @@ -19,8 +21,6 @@ void main () } #endif #ifdef FRAGMENT_SHADER -varying vec2 tc; -varying vec4 vc; void main () { vec4 col = texture2D(s_t0, tc); diff --git a/engine/shaders/vulkan/defaultwarp.glsl b/engine/shaders/vulkan/defaultwarp.glsl index 97101395e..ee41a0eff 100644 --- a/engine/shaders/vulkan/defaultwarp.glsl +++ b/engine/shaders/vulkan/defaultwarp.glsl @@ -10,7 +10,8 @@ //this is expected to be moderately fast. #include "sys/fog.h" -varying vec2 tc; +layout(location=0) varying vec2 tc; + #ifdef VERTEX_SHADER void main () { diff --git a/engine/shaders/vulkan/drawflat_wall.glsl b/engine/shaders/vulkan/drawflat_wall.glsl index 680105704..007dcc384 100644 --- a/engine/shaders/vulkan/drawflat_wall.glsl +++ b/engine/shaders/vulkan/drawflat_wall.glsl @@ -1,6 +1,6 @@ !!permu FOG -!!cvar3f r_floorcolor -!!cvar3f r_wallcolor +!!cvar3f r_floorcolor=0.5,0.5,0.5 +!!cvar3f r_wallcolor=0.25,0.25,0.5 !!cvarb r_fog_exp2=true !!samps 1 #include "sys/defs.h" @@ -8,14 +8,10 @@ //this is for the '286' preset walls, and just draws lightmaps coloured based upon surface normals. #include "sys/fog.h" -varying vec4 col; +layout(location=0) varying vec4 col; +layout(location=1) varying vec2 lm; + #ifdef VERTEX_SHADER -//attribute vec3 v_normal; -//attribute vec2 v_lmcoord; -varying vec2 lm; -//uniform vec3 cvar_r_wallcolor; -//uniform vec3 cvar_r_floorcolor; -//uniform vec4 e_lmscale; void main () { col = vec4(e_lmscale.rgb/255.0 * ((v_normal.z < 0.73)?cvar_r_wallcolor:cvar_r_floorcolor), e_lmscale.a); @@ -24,8 +20,6 @@ void main () } #endif #ifdef FRAGMENT_SHADER -//uniform sampler2D s_t0; -varying vec2 lm; void main () { gl_FragColor = fog4(col * texture2D(s_t0, lm)); diff --git a/engine/shaders/vulkan/fxaa.glsl b/engine/shaders/vulkan/fxaa.glsl index d6de52975..992b6c8b5 100644 --- a/engine/shaders/vulkan/fxaa.glsl +++ b/engine/shaders/vulkan/fxaa.glsl @@ -4,8 +4,8 @@ //This shader implements super-sampled anti-aliasing. // -varying vec2 texcoord; -varying vec2 e_sourcesize; +layout(location=0) varying vec2 texcoord; +layout(location=1) varying vec2 e_sourcesize; #ifdef VERTEX_SHADER void main() diff --git a/engine/shaders/vulkan/postproc_fisheye.glsl b/engine/shaders/vulkan/postproc_fisheye.glsl index 99801dc1c..96c69688b 100644 --- a/engine/shaders/vulkan/postproc_fisheye.glsl +++ b/engine/shaders/vulkan/postproc_fisheye.glsl @@ -5,8 +5,8 @@ //fisheye view rendering, for silly fovs that are still playable. +layout(location=0) varying vec2 texcoord; #ifdef VERTEX_SHADER -varying vec2 texcoord; void main() { texcoord = v_texcoord.xy; @@ -14,8 +14,6 @@ void main() } #endif #ifdef FRAGMENT_SHADER -varying vec2 texcoord; -//uniform float cvar_ffov; void main() { vec3 tc; diff --git a/engine/shaders/vulkan/postproc_panorama.glsl b/engine/shaders/vulkan/postproc_panorama.glsl index 0d2f02c1a..80c20cc1f 100644 --- a/engine/shaders/vulkan/postproc_panorama.glsl +++ b/engine/shaders/vulkan/postproc_panorama.glsl @@ -4,8 +4,8 @@ //panoramic view rendering, for promo map shots or whatever. +layout(location=0) varying vec2 texcoord; #ifdef VERTEX_SHADER -varying vec2 texcoord; void main() { texcoord = v_texcoord.xy; @@ -13,7 +13,6 @@ void main() } #endif #ifdef FRAGMENT_SHADER -varying vec2 texcoord; void main() { vec3 tc; diff --git a/engine/shaders/vulkan/postproc_stereographic.glsl b/engine/shaders/vulkan/postproc_stereographic.glsl index 7778f6d9f..d9b92b7f4 100644 --- a/engine/shaders/vulkan/postproc_stereographic.glsl +++ b/engine/shaders/vulkan/postproc_stereographic.glsl @@ -4,8 +4,8 @@ //stereographic view rendering, for high fovs that are still playable. +layout(location=0) varying vec2 texcoord; #ifdef VERTEX_SHADER -varying vec2 texcoord; //uniform float cvar_ffov; void main() { @@ -18,7 +18,6 @@ void main() } #endif #ifdef FRAGMENT_SHADER -varying vec2 texcoord; void main() { vec3 tc; diff --git a/engine/shaders/vulkan/underwaterwarp.glsl b/engine/shaders/vulkan/underwaterwarp.glsl index 95d71a0c6..b3efd3d02 100644 --- a/engine/shaders/vulkan/underwaterwarp.glsl +++ b/engine/shaders/vulkan/underwaterwarp.glsl @@ -6,9 +6,9 @@ //this is a post processing shader that is drawn fullscreen whenever the view is underwater. //its generally expected to warp the view a little. -varying vec2 v_stc; -varying vec2 v_warp; -varying vec2 v_edge; +layout(location=0) varying vec2 v_stc; +layout(location=1) varying vec2 v_warp; +layout(location=2) varying vec2 v_edge; #ifdef VERTEX_SHADER void main () diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index 0b13e8e94..6586d5ec4 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -247,7 +247,7 @@ typedef struct vec4_t lightinfo; //org+radius VkBuffer staticbuf; //holds fallback vertex info so we don't crash from it - VkDeviceMemory staticbufmem; + vk_poolmem_t staticbufmem; texid_t tex_currentrender; @@ -1647,7 +1647,7 @@ void VKBE_Shutdown(void) shaderstate.wbatches = NULL; vkDestroyBuffer(vk.device, shaderstate.staticbuf, vkallocationcb); - vkFreeMemory(vk.device, shaderstate.staticbufmem, vkallocationcb); + VK_ReleasePoolMemory(&shaderstate.staticbufmem); } static texid_t SelectPassTexture(const shaderpass_t *pass) @@ -2849,7 +2849,7 @@ static void BE_CreatePipeline(program_t *p, unsigned int shaderflags, unsigned i ((blendflags&SBITS_MASK_BLUE)?0:VK_COLOR_COMPONENT_B_BIT) | ((blendflags&SBITS_MASK_ALPHA)?0:VK_COLOR_COMPONENT_A_BIT); - if (blendflags & SBITS_BLEND_BITS) + if ((blendflags & SBITS_BLEND_BITS) && (blendflags & SBITS_BLEND_BITS)!=(SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO)) { switch(blendflags & SBITS_SRCBLEND_BITS) { @@ -3017,7 +3017,7 @@ static void BE_CreatePipeline(program_t *p, unsigned int shaderflags, unsigned i pipeCreateInfo.basePipelineHandle = VK_NULL_HANDLE; pipeCreateInfo.basePipelineIndex = -1; //used to create derivatives for pipelines created in the same call. -// pipeCreateInfo.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT; +// pipeCreateInfo.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT; err = vkCreateGraphicsPipelines(vk.device, vk.pipelinecache, 1, &pipeCreateInfo, vkallocationcb, &pipe->pipeline); @@ -3776,7 +3776,7 @@ void VKBE_SelectEntity(entity_t *ent) BE_RotateForEntity(ent, ent->model); } -//fixme: create allocations within larger buffers, use separate staging. +//fixme: create allocations within larger ring buffers, use separate staging. void *VKBE_CreateStagingBuffer(struct stagingbuf *n, size_t size, VkBufferUsageFlags usage) { void *ptr; @@ -3784,6 +3784,8 @@ void *VKBE_CreateStagingBuffer(struct stagingbuf *n, size_t size, VkBufferUsageF VkMemoryRequirements mem_reqs; VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; + memset(&n->mem, 0, sizeof(n->mem)); + n->retbuf = VK_NULL_HANDLE; n->usage = usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufinf.flags = 0; @@ -3801,9 +3803,9 @@ void *VKBE_CreateStagingBuffer(struct stagingbuf *n, size_t size, VkBufferUsageF if (memAllocInfo.memoryTypeIndex == ~0) Sys_Error("Unable to allocate buffer memory"); - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &n->memory)); - VkAssert(vkBindBufferMemory(vk.device, n->buf, n->memory, 0)); - VkAssert(vkMapMemory(vk.device, n->memory, 0, n->size, 0, &ptr)); + VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &n->mem.memory)); + VkAssert(vkBindBufferMemory(vk.device, n->buf, n->mem.memory, n->mem.offset)); + VkAssert(vkMapMemory(vk.device, n->mem.memory, 0, n->size, 0, &ptr)); return ptr; } @@ -3813,21 +3815,21 @@ struct fencedbufferwork struct vk_fencework fw; VkBuffer buf; - VkDeviceMemory mem; + vk_poolmem_t mem; }; static void VKBE_DoneBufferStaging(void *staging) { struct fencedbufferwork *n = staging; vkDestroyBuffer(vk.device, n->buf, vkallocationcb); - vkFreeMemory(vk.device, n->mem, vkallocationcb); + VK_ReleasePoolMemory(&n->mem); } -VkBuffer VKBE_FinishStaging(struct stagingbuf *n, VkDeviceMemory *memptr) +VkBuffer VKBE_FinishStaging(struct stagingbuf *n, vk_poolmem_t *mem) { struct fencedbufferwork *fence; VkBuffer retbuf; //caller filled the staging buffer, and now wants to copy stuff to the gpu. - vkUnmapMemory(vk.device, n->memory); + vkUnmapMemory(vk.device, n->mem.memory); //create the hardware buffer if (n->retbuf) @@ -3848,20 +3850,38 @@ VkBuffer VKBE_FinishStaging(struct stagingbuf *n, VkDeviceMemory *memptr) //sort out its memory { VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; vkGetBufferMemoryRequirements(vk.device, retbuf, &mem_reqs); - memAllocInfo.allocationSize = mem_reqs.size; - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (memAllocInfo.memoryTypeIndex == ~0) - Sys_Error("Unable to allocate buffer memory"); - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, memptr)); - VkAssert(vkBindBufferMemory(vk.device, retbuf, *memptr, 0)); + if (!VK_AllocatePoolMemory(vk_find_memory_require(mem_reqs.memoryTypeBits, 0), mem_reqs.size, mem_reqs.alignment, mem)) + { + VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; + VkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR}; + + //shouldn't really happen, but just in case... + mem_reqs.size = max(1,mem_reqs.size); + + memAllocInfo.allocationSize = mem_reqs.size; + memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); + if (vk.khr_dedicated_allocation) + { + khr_mdai.buffer = retbuf; + khr_mdai.pNext = memAllocInfo.pNext; + memAllocInfo.pNext = &khr_mdai; + } + + mem->pool = NULL; + mem->offset = 0; + mem->size = mem_reqs.size; + mem->memory = VK_NULL_HANDLE; + + VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &mem->memory)); + } + VkAssert(vkBindBufferMemory(vk.device, retbuf, mem->memory, mem->offset)); } fence = VK_FencedBegin(VKBE_DoneBufferStaging, sizeof(*fence)); fence->buf = n->buf; - fence->mem = n->memory; + fence->mem = n->mem; //FIXME: barrier? @@ -3893,7 +3913,7 @@ void VKBE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch index_t *vboedata; qbyte *vbovdatastart, *vbovdata; struct stagingbuf vbuf, ebuf; - VkDeviceMemory *retarded; + vk_poolmem_t *poolmem; vbo = Z_Malloc(sizeof(*vbo)); @@ -3960,17 +3980,17 @@ void VKBE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch } } - vbo->vbomem = retarded = Z_Malloc(sizeof(*retarded)); + vbo->vbomem = poolmem = Z_Malloc(sizeof(*poolmem)); vbo->coord.vk.buff = vbo->texcoord.vk.buff = vbo->lmcoord[0].vk.buff = vbo->normals.vk.buff = vbo->svector.vk.buff = vbo->tvector.vk.buff = - vbo->colours[0].vk.buff = VKBE_FinishStaging(&vbuf, retarded); + vbo->colours[0].vk.buff = VKBE_FinishStaging(&vbuf, poolmem); - vbo->ebomem = retarded = Z_Malloc(sizeof(*retarded)); - vbo->indicies.vk.buff = VKBE_FinishStaging(&ebuf, retarded); + vbo->ebomem = poolmem = Z_Malloc(sizeof(*poolmem)); + vbo->indicies.vk.buff = VKBE_FinishStaging(&ebuf, poolmem); vbo->indicies.vk.offs = 0; vbo->indexcount = maxvboelements; @@ -4022,22 +4042,19 @@ struct vkbe_clearvbo static void VKBE_SafeClearVBO(void *vboptr) { vbo_t *vbo = *(vbo_t**)vboptr; - VkDeviceMemory *retarded; if (vbo->indicies.vk.buff) { vkDestroyBuffer(vk.device, vbo->indicies.vk.buff, vkallocationcb); - retarded = vbo->ebomem; - vkFreeMemory(vk.device, *retarded, vkallocationcb); - BZ_Free(retarded); + VK_ReleasePoolMemory(vbo->ebomem); + BZ_Free(vbo->ebomem); } if (vbo->coord.vk.buff) { vkDestroyBuffer(vk.device, vbo->coord.vk.buff, vkallocationcb); - retarded = vbo->vbomem; - vkFreeMemory(vk.device, *retarded, vkallocationcb); - BZ_Free(retarded); + VK_ReleasePoolMemory(vbo->vbomem); + BZ_Free(vbo->vbomem); } BZ_Free(vbo); @@ -4678,26 +4695,8 @@ void VKBE_RT_Gen(struct vk_rendertarg *targ, uint32_t width, uint32_t height, qb depth_imginfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT; VkAssert(vkCreateImage(vk.device, &depth_imginfo, vkallocationcb, &targ->depth.image)); - - { - VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - vkGetImageMemoryRequirements(vk.device, targ->colour.image, &mem_reqs); - memAllocInfo.allocationSize = mem_reqs.size; - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &targ->colour.memory)); - VkAssert(vkBindImageMemory(vk.device, targ->colour.image, targ->colour.memory, 0)); - } - - { - VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - vkGetImageMemoryRequirements(vk.device, targ->depth.image, &mem_reqs); - memAllocInfo.allocationSize = mem_reqs.size; - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &targ->depth.memory)); - VkAssert(vkBindImageMemory(vk.device, targ->depth.image, targ->depth.memory, 0)); - } + VK_AllocateBindImageMemory(&targ->colour, true); + VK_AllocateBindImageMemory(&targ->depth, true); // set_image_layout(vk.frame->cbuf, targ->colour.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // set_image_layout(vk.frame->cbuf, targ->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -4867,26 +4866,8 @@ void VKBE_RT_Gen_Cube(struct vk_rendertarg_cube *targ, uint32_t size, qboolean c depth_imginfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT; VkAssert(vkCreateImage(vk.device, &depth_imginfo, vkallocationcb, &targ->depth.image)); - - { - VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - vkGetImageMemoryRequirements(vk.device, targ->colour.image, &mem_reqs); - memAllocInfo.allocationSize = mem_reqs.size; - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &targ->colour.memory)); - VkAssert(vkBindImageMemory(vk.device, targ->colour.image, targ->colour.memory, 0)); - } - - { - VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - vkGetImageMemoryRequirements(vk.device, targ->depth.image, &mem_reqs); - memAllocInfo.allocationSize = mem_reqs.size; - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &targ->depth.memory)); - VkAssert(vkBindImageMemory(vk.device, targ->depth.image, targ->depth.memory, 0)); - } + VK_AllocateBindImageMemory(&targ->colour, true); + VK_AllocateBindImageMemory(&targ->depth, true); // set_image_layout(vk.frame->cbuf, targ->colour.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // set_image_layout(vk.frame->cbuf, targ->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -5809,12 +5790,12 @@ struct vk_shadowbuffer VkBuffer vbuffer; VkDeviceSize voffset; - VkDeviceMemory vmemory; + vk_poolmem_t vmemory; unsigned int numverts; VkBuffer ibuffer; VkDeviceSize ioffset; - VkDeviceMemory imemory; + vk_poolmem_t imemory; unsigned int numindicies; }; //FIXME: needs context for threading @@ -5830,12 +5811,10 @@ struct vk_shadowbuffer *VKBE_GenerateShadowBuffer(vecV_t *verts, int numverts, i map = VKBE_AllocateBufferSpace(DB_VBO, sizeof(*verts)*numverts, &buf->vbuffer, &buf->voffset); memcpy(map, verts, sizeof(*verts)*numverts); - buf->vmemory = VK_NULL_HANDLE; buf->numverts = numverts; map = VKBE_AllocateBufferSpace(DB_EBO, sizeof(*indicies)*numindicies, &buf->ibuffer, &buf->ioffset); memcpy(map, indicies, sizeof(*indicies)*numindicies); - buf->imemory = VK_NULL_HANDLE; buf->numindicies = numindicies; return buf; } @@ -5866,8 +5845,8 @@ static void VKBE_DestroyShadowBuffer_Delayed(void *ctx) struct vk_shadowbuffer *buf = ctx; vkDestroyBuffer(vk.device, buf->vbuffer, vkallocationcb); vkDestroyBuffer(vk.device, buf->ibuffer, vkallocationcb); - vkFreeMemory(vk.device, buf->vmemory, vkallocationcb); - vkFreeMemory(vk.device, buf->imemory, vkallocationcb); + VK_ReleasePoolMemory(&buf->vmemory); + VK_ReleasePoolMemory(&buf->imemory); } void VKBE_DestroyShadowBuffer(struct vk_shadowbuffer *buf) { @@ -6359,33 +6338,33 @@ void VKBE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earr { struct stagingbuf *n; struct stagingbuf ebo; - VkDeviceMemory *retarded; + vk_poolmem_t *poolmem; index_t *map = VKBE_CreateStagingBuffer(&ebo, esize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); memcpy(map, edata, esize); - *ebomem = retarded = Z_Malloc(sizeof(*retarded)); - earray->vk.buff = VKBE_FinishStaging(&ebo, retarded); + *ebomem = poolmem = Z_Malloc(sizeof(*poolmem)); + earray->vk.buff = VKBE_FinishStaging(&ebo, poolmem); earray->vk.offs = 0; if (ctx) { n = ctx->vboptr[0]; - *vbomem = retarded = Z_Malloc(sizeof(*retarded)); - VKBE_FinishStaging(n, retarded); + *vbomem = poolmem = Z_Malloc(sizeof(*poolmem)); + /*buffer was pre-created*/VKBE_FinishStaging(n, poolmem); Z_Free(n); } } void VKBE_VBO_Destroy(vboarray_t *vearray, void *mem) { - VkDeviceMemory *retarded = mem; + vk_poolmem_t *poolmem = mem; struct fencedbufferwork *fence; if (!vearray->vk.buff) return; //not actually allocated... fence = VK_AtFrameEnd(VKBE_DoneBufferStaging, NULL, sizeof(*fence)); fence->buf = vearray->vk.buff; - fence->mem = *retarded; + fence->mem = *poolmem; - Z_Free(retarded); + Z_Free(poolmem); } void VKBE_Scissor(srect_t *rect) diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 954eb53ce..68a26f64f 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -12,10 +12,11 @@ extern cvar_t vk_dualqueue; extern cvar_t vk_busywait; extern cvar_t vk_waitfence; extern cvar_t vk_nv_glsl_shader; -extern cvar_t vk_nv_dedicated_allocation; +extern cvar_t vk_khr_get_memory_requirements2; extern cvar_t vk_khr_dedicated_allocation; extern cvar_t vk_khr_push_descriptor; extern cvar_t vk_amd_rasterization_order; +extern cvar_t vk_usememorypools; extern cvar_t vid_srgb, vid_vsync, vid_triplebuffer, r_stereo_method, vid_multisample, vid_bpp; void R2D_Console_Resize(void); @@ -182,8 +183,7 @@ void VK_DestroyVkTexture(vk_image_t *img) vkDestroyImageView(vk.device, img->view, vkallocationcb); if (img->image) vkDestroyImage(vk.device, img->image, vkallocationcb); - if (img->memory) - vkFreeMemory(vk.device, img->memory, vkallocationcb); + VK_ReleasePoolMemory(&img->mem); } static void VK_DestroyVkTexture_Delayed(void *w) { @@ -303,7 +303,7 @@ static qboolean VK_CreateSwapChain(void) VkPresentModeKHR *presentmode; VkSurfaceCapabilitiesKHR surfcaps; VkSwapchainCreateInfoKHR swapinfo = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; - uint32_t i, curpri; + uint32_t i, curpri, preaquirecount; VkSwapchainKHR newvkswapchain; VkImage *images; VkDeviceMemory *memories; @@ -369,9 +369,7 @@ static qboolean VK_CreateSwapChain(void) VkMemoryRequirements mem_reqs; VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; VkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR}; - VkDedicatedAllocationMemoryAllocateInfoNV nv_damai = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV}; VkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - VkDedicatedAllocationImageCreateInfoNV nv_daici = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV}; ici.flags = 0; ici.imageType = VK_IMAGE_TYPE_2D; @@ -389,13 +387,6 @@ static qboolean VK_CreateSwapChain(void) ici.pQueueFamilyIndices = NULL; ici.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - if (vk.nv_dedicated_allocation) - { //render targets should always have dedicated allocations, supposedly. and we're doing it anyway. - nv_daici.dedicatedAllocation = true; - nv_daici.pNext = ici.pNext; - ici.pNext = &nv_daici; - } - VkAssert(vkCreateImage(vk.device, &ici, vkallocationcb, &images[i])); vkGetImageMemoryRequirements(vk.device, images[i], &mem_reqs); @@ -409,12 +400,6 @@ static qboolean VK_CreateSwapChain(void) if (memAllocInfo.memoryTypeIndex == ~0) memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - if (vk.nv_dedicated_allocation) - { //annoying, but hey. - nv_damai.pNext = memAllocInfo.pNext; - nv_damai.image = images[i]; - memAllocInfo.pNext = &nv_damai; - } if (vk.khr_dedicated_allocation) { khr_mdai.pNext = memAllocInfo.pNext; @@ -478,11 +463,20 @@ static qboolean VK_CreateSwapChain(void) if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; else if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) + { swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; + Con_Printf(CON_WARNING"Vulkan swapchain using composite alpha premultiplied\n"); + } else if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) + { swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + Con_Printf(CON_WARNING"Vulkan swapchain using composite alpha postmultiplied\n"); + } else + { swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; //erk? + Con_Printf(CON_WARNING"composite alpha inherit\n"); + } swapinfo.imageArrayLayers = /*(r_stereo_method.ival==1)?2:*/1; swapinfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapinfo.queueFamilyIndexCount = 0; @@ -663,7 +657,7 @@ static qboolean VK_CreateSwapChain(void) vk.aquirelast = vk.aquirenext = 0; for (i = 0; i < ACQUIRELIMIT; i++) { - if (vk_waitfence.ival) + if (vk_waitfence.ival || !*vk_waitfence.string) { VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; VkAssert(vkCreateFence(vk.device,&fci,vkallocationcb,&vk.acquirefences[i])); @@ -676,8 +670,12 @@ static qboolean VK_CreateSwapChain(void) vk.acquirefences[i] = VK_NULL_HANDLE; } } + if (!vk_submissionthread.value && *vk_submissionthread.string) + preaquirecount = 1; + else + preaquirecount = vk.backbuf_count; /*-1 to hide any weird thread issues*/ - while (vk.aquirelast < ACQUIRELIMIT-1 && vk.aquirelast < vk.backbuf_count && vk.aquirelast <= vk.backbuf_count-surfcaps.minImageCount) + while (vk.aquirelast < ACQUIRELIMIT-1 && vk.aquirelast < preaquirecount && vk.aquirelast <= vk.backbuf_count-surfcaps.minImageCount) { VkAssert(vkAcquireNextImageKHR(vk.device, vk.swapchain, UINT64_MAX, vk.acquiresemaphores[vk.aquirelast%ACQUIRELIMIT], vk.acquirefences[vk.aquirelast%ACQUIRELIMIT], &vk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT])); vk.aquirelast++; @@ -733,10 +731,10 @@ static qboolean VK_CreateSwapChain(void) { VkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; ivci.format = vk.backbufformat; - ivci.components.r = VK_COMPONENT_SWIZZLE_R; - ivci.components.g = VK_COMPONENT_SWIZZLE_G; - ivci.components.b = VK_COMPONENT_SWIZZLE_B; - ivci.components.a = VK_COMPONENT_SWIZZLE_A; +// ivci.components.r = VK_COMPONENT_SWIZZLE_R; +// ivci.components.g = VK_COMPONENT_SWIZZLE_G; +// ivci.components.b = VK_COMPONENT_SWIZZLE_B; +// ivci.components.a = VK_COMPONENT_SWIZZLE_A; ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ivci.subresourceRange.baseMipLevel = 0; ivci.subresourceRange.levelCount = 1; @@ -747,7 +745,7 @@ static qboolean VK_CreateSwapChain(void) ivci.image = images[i]; vk.backbufs[i].colour.image = images[i]; if (memories) - vk.backbufs[i].colour.memory = memories[i]; + vk.backbufs[i].colour.mem.memory = memories[i]; vk.backbufs[i].colour.width = swapinfo.imageExtent.width; vk.backbufs[i].colour.height = swapinfo.imageExtent.height; VkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &vk.backbufs[i].colour.view)); @@ -759,7 +757,6 @@ static qboolean VK_CreateSwapChain(void) //depth image { VkImageCreateInfo depthinfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - VkDedicatedAllocationImageCreateInfoNV nv_daici = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV}; depthinfo.flags = 0; depthinfo.imageType = VK_IMAGE_TYPE_2D; depthinfo.format = vk.depthformat; @@ -775,39 +772,11 @@ static qboolean VK_CreateSwapChain(void) depthinfo.queueFamilyIndexCount = 0; depthinfo.pQueueFamilyIndices = NULL; depthinfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - if (vk.nv_dedicated_allocation) - { - nv_daici.dedicatedAllocation = true; - nv_daici.pNext = depthinfo.pNext; - depthinfo.pNext = &nv_daici; - } VkAssert(vkCreateImage(vk.device, &depthinfo, vkallocationcb, &vk.backbufs[i].depth.image)); } //depth memory - { - VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - VkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR}; - VkDedicatedAllocationMemoryAllocateInfoNV nv_damai = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV}; - vkGetImageMemoryRequirements(vk.device, vk.backbufs[i].depth.image, &mem_reqs); - memAllocInfo.allocationSize = mem_reqs.size; - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - if (vk.nv_dedicated_allocation) - { - nv_damai.image = vk.backbufs[i].depth.image; - nv_damai.pNext = memAllocInfo.pNext; - memAllocInfo.pNext = &nv_damai; - } - if (vk.khr_dedicated_allocation) - { - khr_mdai.image = vk.backbufs[i].depth.image; - khr_mdai.pNext = memAllocInfo.pNext; - memAllocInfo.pNext = &khr_mdai; - } - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &vk.backbufs[i].depth.memory)); - VkAssert(vkBindImageMemory(vk.device, vk.backbufs[i].depth.image, vk.backbufs[i].depth.memory, 0)); - } + VK_AllocateBindImageMemory(&vk.backbufs[i].depth, true); //depth view { @@ -836,7 +805,6 @@ static qboolean VK_CreateSwapChain(void) //mscolour image { VkImageCreateInfo mscolourinfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - VkDedicatedAllocationImageCreateInfoNV nv_daici = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV}; mscolourinfo.flags = 0; mscolourinfo.imageType = VK_IMAGE_TYPE_2D; mscolourinfo.format = vk.backbufformat; @@ -852,40 +820,11 @@ static qboolean VK_CreateSwapChain(void) mscolourinfo.queueFamilyIndexCount = 0; mscolourinfo.pQueueFamilyIndices = NULL; mscolourinfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - if (vk.nv_dedicated_allocation) - { - nv_daici.dedicatedAllocation = true; - nv_daici.pNext = mscolourinfo.pNext; - mscolourinfo.pNext = &nv_daici; - } VkAssert(vkCreateImage(vk.device, &mscolourinfo, vkallocationcb, &vk.backbufs[i].mscolour.image)); } //mscolour memory - { - VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - VkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR}; - VkDedicatedAllocationMemoryAllocateInfoNV nv_damai = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV}; - vkGetImageMemoryRequirements(vk.device, vk.backbufs[i].mscolour.image, &mem_reqs); - memAllocInfo.allocationSize = mem_reqs.size; - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - if (vk.nv_dedicated_allocation) - { - nv_damai.image = vk.backbufs[i].mscolour.image; - nv_damai.pNext = memAllocInfo.pNext; - memAllocInfo.pNext = &nv_damai; - } - if (vk.khr_dedicated_allocation) - { - khr_mdai.image = vk.backbufs[i].mscolour.image; - khr_mdai.pNext = memAllocInfo.pNext; - memAllocInfo.pNext = &khr_mdai; - } - - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &vk.backbufs[i].mscolour.memory)); - VkAssert(vkBindImageMemory(vk.device, vk.backbufs[i].mscolour.image, vk.backbufs[i].mscolour.memory, 0)); - } + VK_AllocateBindImageMemory(&vk.backbufs[i].mscolour, true); //mscolour view { @@ -1020,21 +959,133 @@ void VK_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], } } +qboolean VK_AllocatePoolMemory(uint32_t pooltype, VkDeviceSize memsize, VkDeviceSize poolalignment, vk_poolmem_t *mem) +{ + struct vk_mempool_s *p; + VkDeviceSize pad; + + if (!vk_usememorypools.ival) + return false; + + if (memsize > 1024*1024*4) + return false; + for (p = vk.mempools; p; p = p->next) + { + if (p->memtype == pooltype) + { + if (p->memoryoffset + poolalignment + memsize < p->memorysize) + break; + } + } + if (!p) + { + VkMemoryAllocateInfo poolai = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; + p = Z_Malloc(sizeof(*p)); + p->memorysize = poolai.allocationSize = 512*1024*1024; //lets just allocate big... + p->memtype = poolai.memoryTypeIndex = pooltype; + + if (VK_SUCCESS != vkAllocateMemory(vk.device, &poolai, vkallocationcb, &p->memory)) + { //out of memory? oh well, a smaller dedicated allocation might still work. + Z_Free(p); + return false; + } + p->next = vk.mempools; + vk.mempools = p; + } + pad = ((p->memoryoffset+poolalignment-1)&~(poolalignment-1)) - p->memoryoffset; + p->memoryoffset = (p->memoryoffset+poolalignment-1)&~(poolalignment-1); + p->gaps += pad; + mem->offset = p->memoryoffset; + mem->size = memsize; //FIXME: we have no way to deal with gaps due to alignment + mem->memory = p->memory; + mem->pool = p; + + p->memoryoffset += memsize; + return true; +} +void VK_ReleasePoolMemory(vk_poolmem_t *mem) +{ + if (mem->pool) + { + //FIXME: track power-of-two holes? + mem->pool->gaps += mem->size; + mem->pool = NULL; + mem->memory = VK_NULL_HANDLE; + } + else if (mem->memory) + { + vkFreeMemory(vk.device, mem->memory, vkallocationcb); + mem->memory = VK_NULL_HANDLE; + } +} + + +//does NOT bind. +//image memory is NOT expected to be host-visible. you'll get what vulkan gives you. +qboolean VK_AllocateImageMemory(VkImage image, qboolean dedicated, vk_poolmem_t *mem) +{ + uint32_t pooltype; + VkMemoryRequirements2KHR mem_reqs2 = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR}; + + if (!dedicated && vk.khr_get_memory_requirements2) + { + VkImageMemoryRequirementsInfo2KHR imri = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR}; + VkMemoryDedicatedRequirementsKHR mdr = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR}; + imri.image = image; + if (vk.khr_dedicated_allocation) + mem_reqs2.pNext = &mdr; //chain the result struct + vkGetImageMemoryRequirements2KHR(vk.device, &imri, &mem_reqs2); + + //and now we know if it should be dedicated or not. + dedicated |= mdr.prefersDedicatedAllocation || mdr.requiresDedicatedAllocation; + } + else + vkGetImageMemoryRequirements(vk.device, image, &mem_reqs2.memoryRequirements); + + pooltype = vk_find_memory_try(mem_reqs2.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (pooltype == ~0) + pooltype = vk_find_memory_require(mem_reqs2.memoryRequirements.memoryTypeBits, 0); + + if (!dedicated && VK_AllocatePoolMemory(pooltype, mem_reqs2.memoryRequirements.size, mem_reqs2.memoryRequirements.alignment, mem)) + return true; //got a shared allocation. + else + { //make it dedicated one way or another. + VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; + VkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR}; + + //shouldn't really happen, but just in case... + mem_reqs2.memoryRequirements.size = max(1,mem_reqs2.memoryRequirements.size); + + memAllocInfo.allocationSize = mem_reqs2.memoryRequirements.size; + memAllocInfo.memoryTypeIndex = pooltype; + if (vk.khr_dedicated_allocation) + { + khr_mdai.image = image; + khr_mdai.pNext = memAllocInfo.pNext; + memAllocInfo.pNext = &khr_mdai; + } + + mem->pool = NULL; + mem->offset = 0; + mem->size = mem_reqs2.memoryRequirements.size; + mem->memory = VK_NULL_HANDLE; + + VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &mem->memory)); + return true; + } +} +void VK_AllocateBindImageMemory(vk_image_t *image, qboolean dedicated) +{ + if (VK_AllocateImageMemory(image->image, dedicated, &image->mem)) + VkAssert(vkBindImageMemory(vk.device, image->image, image->mem.memory, image->mem.offset)); +} + + vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t layers, uint32_t mips, uploadfmt_t encoding, unsigned int type, qboolean rendertarget) { vk_image_t ret; -#ifdef USE_STAGING_BUFFERS - qboolean staging = layers == 0; -#else - const qboolean staging = false; -#endif - VkMemoryRequirements mem_reqs; - VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - VkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR}; - VkDedicatedAllocationMemoryAllocateInfoNV nv_damai = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV}; - + VkImageViewCreateInfo viewInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; VkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - VkDedicatedAllocationImageCreateInfoNV nv_daici = {VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV}; VkFormat format = VK_FORMAT_UNDEFINED;; ret.width = width; @@ -1043,7 +1094,7 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay ret.mipcount = mips; ret.encoding = encoding; ret.type = type; - ret.layout = staging?VK_IMAGE_LAYOUT_PREINITIALIZED:VK_IMAGE_LAYOUT_UNDEFINED; + ret.layout = VK_IMAGE_LAYOUT_UNDEFINED; //vulkan expresses packed formats in terms of native endian (if big-endian, then everything makes sense), non-packed formats are expressed in byte order (consistent with big-endian). //PTI formats are less well-defined... @@ -1165,102 +1216,67 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay ici.extent.height = height; ici.extent.depth = 1; ici.mipLevels = mips; - ici.arrayLayers = staging?1:layers; + ici.arrayLayers = layers; ici.samples = VK_SAMPLE_COUNT_1_BIT; - ici.tiling = staging?VK_IMAGE_TILING_LINEAR:VK_IMAGE_TILING_OPTIMAL; - ici.usage = staging?VK_IMAGE_USAGE_TRANSFER_SRC_BIT:(VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT); + ici.tiling = VK_IMAGE_TILING_OPTIMAL; + ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT|(rendertarget?0:VK_IMAGE_USAGE_TRANSFER_DST_BIT); ici.sharingMode = VK_SHARING_MODE_EXCLUSIVE; ici.queueFamilyIndexCount = 0; ici.pQueueFamilyIndices = NULL; ici.initialLayout = ret.layout; - if (vk.nv_dedicated_allocation/* && (size > foo || rendertarget)*/) //FIXME: should only really be used for rendertargets or large images, other stuff should be merged. however, as we don't support any memory suballocations, we're going to just flag everything for now. - { - nv_daici.dedicatedAllocation = true; - nv_daici.pNext = ici.pNext; - ici.pNext = &nv_daici; - } + VkAssert(vkCreateImage(vk.device, &ici, vkallocationcb, &ret.image)); - vkGetImageMemoryRequirements(vk.device, ret.image, &mem_reqs); - - memAllocInfo.allocationSize = mem_reqs.size; - if (staging) - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - else - { - memAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (memAllocInfo.memoryTypeIndex == ~0) - memAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (memAllocInfo.memoryTypeIndex == ~0) - memAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - if (memAllocInfo.memoryTypeIndex == ~0) - memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0); - } - if (nv_daici.dedicatedAllocation) - { - nv_damai.image = ret.image; - nv_damai.pNext = memAllocInfo.pNext; - memAllocInfo.pNext = &nv_damai; - } - if (vk.khr_dedicated_allocation) - { - khr_mdai.image = ret.image; - khr_mdai.pNext = memAllocInfo.pNext; - memAllocInfo.pNext = &khr_mdai; - } - VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &ret.memory)); - VkAssert(vkBindImageMemory(vk.device, ret.image, ret.memory, 0)); + VK_AllocateBindImageMemory(&ret, false); ret.view = VK_NULL_HANDLE; ret.sampler = VK_NULL_HANDLE; - if (!staging) + + viewInfo.flags = 0; + viewInfo.image = ret.image; + viewInfo.viewType = (ret.type==PTI_CUBEMAP)?VK_IMAGE_VIEW_TYPE_CUBE:VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = format; + switch(encoding) { - VkImageViewCreateInfo viewInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; - viewInfo.flags = 0; - viewInfo.image = ret.image; - viewInfo.viewType = (ret.type==PTI_CUBEMAP)?VK_IMAGE_VIEW_TYPE_CUBE:VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = format; - switch(encoding) - { - //formats that explicitly drop the alpha - case PTI_BC1_RGB: - case PTI_BC1_RGB_SRGB: - case PTI_RGBX8: - case PTI_RGBX8_SRGB: - case PTI_BGRX8: - case PTI_BGRX8_SRGB: - viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE; - break; - case PTI_L8: //must be an R8 texture - viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_R; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_R; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE; - break; - case PTI_L8A8: //must be an RG8 texture - viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_R; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_R; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_G; - break; - default: - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - break; - } - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = mips; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = layers; - VkAssert(vkCreateImageView(vk.device, &viewInfo, NULL, &ret.view)); + //formats that explicitly drop the alpha + case PTI_BC1_RGB: + case PTI_BC1_RGB_SRGB: + case PTI_RGBX8: + case PTI_RGBX8_SRGB: + case PTI_BGRX8: + case PTI_BGRX8_SRGB: + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE; + break; + case PTI_L8: //must be an R8 texture + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE; + break; + case PTI_L8A8: //must be an RG8 texture + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_G; + break; + default: + viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + break; } + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = mips; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = layers; + VkAssert(vkCreateImageView(vk.device, &viewInfo, NULL, &ret.view)); + return ret; } void set_image_layout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, @@ -1366,6 +1382,7 @@ void VK_FencedSubmit(void *work) //check if we can release anything yet. VK_FencedCheck(); + //FIXME: this seems to be an excessively expensive function. vkCreateFence(vk.device, &fenceinfo, vkallocationcb, &w->fence); VK_Submit_Work(w->cbuf, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, w->fence, NULL, w); @@ -1423,45 +1440,26 @@ void *VK_AtFrameEnd(void (*frameended)(void *work), void *workdata, size_t works return w+1; } -#define USE_STAGING_BUFFERS struct texturefence { struct vk_fencework w; int mips; -#ifdef USE_STAGING_BUFFERS VkBuffer stagingbuffer; VkDeviceMemory stagingmemory; -#else - vk_image_t staging[32]; -#endif }; static void VK_TextureLoaded(void *ctx) { struct texturefence *w = ctx; -#ifdef USE_STAGING_BUFFERS vkDestroyBuffer(vk.device, w->stagingbuffer, vkallocationcb); vkFreeMemory(vk.device, w->stagingmemory, vkallocationcb); -#else - unsigned int i; - for (i = 0; i < w->mips; i++) - if (w->staging[i].image != VK_NULL_HANDLE) - { - vkDestroyImage(vk.device, w->staging[i].image, vkallocationcb); - vkFreeMemory(vk.device, w->staging[i].memory, vkallocationcb); - } -#endif } qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips) { -#ifdef USE_STAGING_BUFFERS VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; VkMemoryRequirements mem_reqs; VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; void *mapdata; -#else - uint32_t y; -#endif struct texturefence *fence; VkCommandBuffer vkloadcmd; @@ -1571,7 +1569,6 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips) } } -#ifdef USE_STAGING_BUFFERS //figure out how big our staging buffer needs to be bci.size = 0; for (i = 0; i < mipcount; i++) @@ -1636,63 +1633,6 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips) bci.size += blockswidth*blocksheight*blockbytes; } vkUnmapMemory(vk.device, fence->stagingmemory); -#else -//create the staging images and fill them - for (i = 0; i < mipcount; i++) - { - VkImageSubresource subres = {0}; - VkSubresourceLayout layout; - void *mapdata; - //figure out the number of 'blocks' in the image. - //for non-compressed formats this is just the width directly. - //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; - - fence->staging[i] = VK_CreateTexture2DArray(mips->mip[i].width, mips->mip[i].height, 0, 1, mips->encoding, PTI_2D); - subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subres.mipLevel = 0; - subres.arrayLayer = 0; - vkGetImageSubresourceLayout(vk.device, fence->staging[i].image, &subres, &layout); - VkAssert(vkMapMemory(vk.device, fence->staging[i].memory, 0, layout.size, 0, &mapdata)); - if (mapdata) - { - for (y = 0; y < blockheight; y++) - memcpy((char*)mapdata + layout.offset + y*layout.rowPitch, (char*)mips->mip[i].data + y*blockswidth*blockbytes, blockswidth*blockbytes); - } - else - Sys_Error("Unable to map staging image\n"); - - vkUnmapMemory(vk.device, fence->staging[i].memory); - - //queue up an image copy for this mip - { - VkImageCopy region; - region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.srcSubresource.mipLevel = 0; - region.srcSubresource.baseArrayLayer = 0; - region.srcSubresource.layerCount = 1; - region.srcOffset.x = 0; - region.srcOffset.y = 0; - region.srcOffset.z = 0; - region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.dstSubresource.mipLevel = i%(mipcount/layers); - region.dstSubresource.baseArrayLayer = i/(mipcount/layers); - region.dstSubresource.layerCount = 1; - region.dstOffset.x = 0; - region.dstOffset.y = 0; - region.dstOffset.z = 0; - region.extent.width = mips->mip[i].width; - region.extent.height = mips->mip[i].height; - region.extent.depth = 1; - - set_image_layout(vkloadcmd, fence->staging[i].image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, VK_ACCESS_HOST_WRITE_BIT, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT); - vkCmdCopyImage(vkloadcmd, fence->staging[i].image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, target.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - } - } -#endif //layouts are annoying. and weird. { @@ -3079,7 +3019,7 @@ qboolean VK_SCR_GrabBackBuffer(void) imgbarrier.pNext = NULL; imgbarrier.srcAccessMask = 0;//VK_ACCESS_MEMORY_READ_BIT; imgbarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - imgbarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;//vk.rendertarg->colour.layout; //'Alternately, oldLayout can be VK_IMAGE_LAYOUT_UNDEFINED, if the image’s contents need not be preserved.' + imgbarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;//vk.rendertarg->colour.layout; //'Alternately, oldLayout can be VK_IMAGE_LAYOUT_UNDEFINED, if the image's contents need not be preserved.' imgbarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; imgbarrier.image = vk.frame->backbuf->colour.image; imgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -3216,7 +3156,7 @@ void VK_DebugFramerate(void) qboolean VK_SCR_UpdateScreen (void) { - uint32_t fblayout; + VkImageLayout fblayout; VK_FencedCheck(); @@ -3519,7 +3459,12 @@ void VK_DoPresent(struct vkframe *theframe) RSpeedMark(); if (err) { - Con_Printf("ERROR: vkQueuePresentKHR: %x\n", err); + if (err == VK_SUBOPTIMAL_KHR) + Con_DPrintf("vkQueuePresentKHR: VK_SUBOPTIMAL_KHR\n"); + else if (err == VK_ERROR_OUT_OF_DATE_KHR) + Con_DPrintf("vkQueuePresentKHR: VK_ERROR_OUT_OF_DATE_KHR\n"); + else + Con_Printf("ERROR: vkQueuePresentKHR: %i\n", err); vk.neednewswapchain = true; } else @@ -3527,7 +3472,7 @@ void VK_DoPresent(struct vkframe *theframe) err = vkAcquireNextImageKHR(vk.device, vk.swapchain, 0, vk.acquiresemaphores[vk.aquirelast%ACQUIRELIMIT], vk.acquirefences[vk.aquirelast%ACQUIRELIMIT], &vk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT]); if (err) { - Con_Printf("ERROR: vkAcquireNextImageKHR: %x\n", err); + Con_Printf("ERROR: vkAcquireNextImageKHR: %i\n", err); vk.neednewswapchain = true; vk.devicelost |= (err == VK_ERROR_DEVICE_LOST); } @@ -3657,7 +3602,12 @@ void VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStage *link = work; #ifdef MULTITHREAD - if (vk.submitthread && !vk.neednewswapchain) + if (vk.neednewswapchain && vk.submitthread) + { //if we're trying to kill the submission thread, don't post work to it - instead wait for it to die cleanly then do it ourselves. + Sys_WaitOnThread(vk.submitthread); + vk.submitthread = NULL; + } + if (vk.submitthread) Sys_ConditionSignal(vk.submitcondition); else #endif @@ -3796,6 +3746,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre VkResult err; VkApplicationInfo app; VkInstanceCreateInfo inst_info; + int gpuidx; const char *extensions[8]; uint32_t extensions_count = 0; @@ -3812,12 +3763,12 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre qboolean supported; } knowndevexts[] = { - {&vk.khr_swapchain, VK_KHR_SWAPCHAIN_EXTENSION_NAME, NULL, true, NULL, " Nothing will be drawn!"}, - {&vk.nv_glsl_shader, VK_NV_GLSL_SHADER_EXTENSION_NAME, &vk_nv_glsl_shader, false, NULL, " Direct use of glsl is not supported."}, - {&vk.nv_dedicated_allocation, VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME, &vk_nv_dedicated_allocation, true, &vk.khr_dedicated_allocation, NULL}, - {&vk.khr_dedicated_allocation, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, &vk_khr_dedicated_allocation, true, NULL, NULL}, - {&vk.khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, &vk_khr_push_descriptor, true, NULL, NULL}, - {&vk.amd_rasterization_order, VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME, &vk_amd_rasterization_order, false, NULL, NULL}, + {&vk.khr_swapchain, VK_KHR_SWAPCHAIN_EXTENSION_NAME, NULL, true, NULL, " Nothing will be drawn!"}, + {&vk.nv_glsl_shader, VK_NV_GLSL_SHADER_EXTENSION_NAME, &vk_nv_glsl_shader, false, NULL, " Direct use of glsl is not supported."}, + {&vk.khr_get_memory_requirements2, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,&vk_khr_get_memory_requirements2,true, NULL, NULL}, + {&vk.khr_dedicated_allocation, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, &vk_khr_dedicated_allocation, true, NULL, NULL}, + {&vk.khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, &vk_khr_push_descriptor, true, NULL, NULL}, + {&vk.amd_rasterization_order, VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME, &vk_amd_rasterization_order, false, NULL, NULL}, }; size_t e; @@ -3959,6 +3910,17 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre uint32_t gpucount = 0, i; uint32_t bestpri = ~0u, pri; VkPhysicalDevice *devs; + char *s = info->subrenderer; + int wantdev = -1; + if (*s) + { + if (!Q_strncasecmp(s, "GPU", 3)) + s += 3; + wantdev = strtoul(s, &s, 0); + if (*s) //its a named device. + wantdev = -1; + } + vkEnumeratePhysicalDevices(vk.instance, &gpucount, NULL); if (!gpucount) { @@ -3993,7 +3955,10 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre Con_DPrintf("Found Vulkan Device \"%s\"\n", props.deviceName); if (!vk.gpu) + { + gpuidx = i; vk.gpu = devs[i]; + } switch(props.deviceType) { default: @@ -4013,17 +3978,27 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre pri = 4; break; } - if (!Q_strcasecmp(props.deviceName, info->subrenderer)) - pri = 0; + if (wantdev >= 0) + { + if (wantdev == i) + pri = 0; + } + else + { + if (!Q_strcasecmp(props.deviceName, info->subrenderer)) + pri = 0; + } + if (pri < bestpri) { - vk.gpu = devs[i]; + gpuidx = i; + vk.gpu = devs[gpuidx]; bestpri = pri; } } free(devs); - if (bestpri == ~0u) + if (!vk.gpu) { Con_Printf("vulkan: unable to pick a usable device\n"); return false; @@ -4037,7 +4012,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre switch(props.vendorID) { - //explicit vendors + //explicit registered vendors case 0x10001: vendor = "Vivante"; break; case 0x10002: vendor = "VeriSilicon"; break; @@ -4053,6 +4028,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre case 0x1AEE: vendor = "Imagination";break; case 0x1957: vendor = "Freescale"; break; + //I really have no idea who makes mobile gpus nowadays, but lets make some guesses. case 0x1AE0: vendor = "Google"; break; case 0x5333: vendor = "S3"; break; case 0xA200: vendor = "NEC"; break; @@ -4076,8 +4052,8 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre case VK_PHYSICAL_DEVICE_TYPE_CPU: type = "software"; break; } - Con_Printf("Vulkan %u.%u.%u: %s %s %s (%u.%u.%u)\n", VK_VERSION_MAJOR(props.apiVersion), VK_VERSION_MINOR(props.apiVersion), VK_VERSION_PATCH(props.apiVersion), - type, vendor, props.deviceName, + Con_Printf("Vulkan %u.%u.%u: GPU%i %s %s %s (%u.%u.%u)\n", VK_VERSION_MAJOR(props.apiVersion), VK_VERSION_MINOR(props.apiVersion), VK_VERSION_PATCH(props.apiVersion), + gpuidx, type, vendor, props.deviceName, VK_VERSION_MAJOR(props.driverVersion), VK_VERSION_MINOR(props.driverVersion), VK_VERSION_PATCH(props.driverVersion) ); } @@ -4205,7 +4181,7 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre Con_DPrintf("Using %s.\n", knowndevexts[e].name); devextensions[numdevextensions++] = knowndevexts[e].name; } - else if (knowndevexts[e].var->ival) + else if (knowndevexts[e].var && knowndevexts[e].var->ival) Con_Printf("unable to enable %s extension.%s\n", knowndevexts[e].name, knowndevexts[e].warningtext?knowndevexts[e].warningtext:""); else if (knowndevexts[e].supported) Con_DPrintf("Ignoring %s.\n", knowndevexts[e].name); @@ -4255,7 +4231,43 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre devinf.ppEnabledExtensionNames = devextensions; devinf.pEnabledFeatures = &features; - err = vkCreateDevice(vk.gpu, &devinf, NULL, &vk.device); +#if 0 + if (vkEnumeratePhysicalDeviceGroupsKHR && vk_afr.ival) + { + //'Every physical device must be in exactly one device group'. So we can just use the first group that lists it and automatically get AFR. + uint32_t gpugroups = 0; + VkDeviceGroupDeviceCreateInfoKHX dgdci = {VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR}; + + VkPhysicalDeviceGroupPropertiesKHR *groups; + vkEnumeratePhysicalDeviceGroupsKHR(vk.instance, &gpugroups, NULL); + groups = malloc(sizeof(*groups)*gpugroups); + vkEnumeratePhysicalDeviceGroupsKHR(vk.instance, &gpugroups, groups); + for (i = 0; i < gpugroups; i++) + { + for (j = 0; j < groups[i].physicalDeviceCount; j++) + if (groups[i].physicalDevices[j] == vk.gpu) + { + dgdci.physicalDeviceCount = groups[i].physicalDeviceCount; + dgdci.pPhysicalDevices = groups[i].physicalDevices; + break; + } + } + + if (dgdci.physicalDeviceCount > 1) + { + vk.subdevices = dgdci.physicalDeviceCount; + dgdci.pNext = devinf.pNext; + devinf.pNext = &dgdci; + } + + err = vkCreateDevice(vk.gpu, &devinf, NULL, &vk.device); + + free(groups); + } + else +#endif + err = vkCreateDevice(vk.gpu, &devinf, NULL, &vk.device); + switch(err) { case VK_ERROR_INCOMPATIBLE_DRIVER: @@ -4396,6 +4408,15 @@ void VK_Shutdown(void) vkDestroyPipelineCache(vk.device, vk.pipelinecache, vkallocationcb); } + while(vk.mempools) + { + void *l; + vkFreeMemory(vk.device, vk.mempools->memory, vkallocationcb); + l = vk.mempools; + vk.mempools = vk.mempools->next; + Z_Free(l); + } + if (vk.device) vkDestroyDevice(vk.device, vkallocationcb); if (vk_debugcallback) diff --git a/engine/vk/vkrenderer.h b/engine/vk/vkrenderer.h index 77adb7906..bccc09964 100644 --- a/engine/vk/vkrenderer.h +++ b/engine/vk/vkrenderer.h @@ -58,6 +58,7 @@ //funcs specific to an instance #define VKInst2Funcs \ VKFunc(EnumeratePhysicalDevices) \ + VKFunc(EnumeratePhysicalDeviceGroupsKHX) \ VKFunc(EnumerateDeviceExtensionProperties) \ VKFunc(GetPhysicalDeviceProperties) \ VKFunc(GetPhysicalDeviceQueueFamilyProperties) \ @@ -126,6 +127,7 @@ VKFunc(GetDeviceQueue) \ VKFunc(GetBufferMemoryRequirements) \ VKFunc(GetImageMemoryRequirements) \ + VKFunc(GetImageMemoryRequirements2KHR) \ VKFunc(GetImageSubresourceLayout) \ VKFunc(CreateFramebuffer) \ VKFunc(DestroyFramebuffer) \ @@ -193,10 +195,18 @@ #define VkWarnAssert(f) f #endif +typedef struct +{ + struct vk_mempool_s *pool; + VkDeviceMemory memory; + size_t size; + size_t offset; +} vk_poolmem_t; + typedef struct vk_image_s { VkImage image; - VkDeviceMemory memory; + vk_poolmem_t mem; VkImageView view; VkSampler sampler; VkImageLayout layout; @@ -256,12 +266,12 @@ extern struct vulkaninfo_s qboolean vsync; qboolean allowsubmissionthread; - qboolean khr_swapchain; //aka: not headless. we're actually rendering stuff! - qboolean nv_glsl_shader; //we can load glsl shaders. probably missing lots of reflection info though, so this is probably too limited. - qboolean nv_dedicated_allocation; //nvidia-specific extension that provides hints that there's no memory aliasing going on. - qboolean khr_dedicated_allocation; //standardised version of the above where the driver decides whether a resource is worth a dedicated allocation. - qboolean khr_push_descriptor; //more efficient descriptor streaming - qboolean amd_rasterization_order; //allows primitives to draw in any order + qboolean khr_swapchain; //aka: not headless. we're actually rendering stuff! + qboolean nv_glsl_shader; //we can load glsl shaders. probably missing lots of reflection info though, so this is probably too limited. + qboolean khr_get_memory_requirements2; //slightly richer info + qboolean khr_dedicated_allocation; //standardised version of the above where the driver decides whether a resource is worth a dedicated allocation. + qboolean khr_push_descriptor; //more efficient descriptor streaming + qboolean amd_rasterization_order; //allows primitives to draw in any order VkInstance instance; VkDevice device; @@ -299,6 +309,19 @@ extern struct vulkaninfo_s float max_anistophy; float max_anistophy_limit; + struct vk_mempool_s + { + struct vk_mempool_s *next; + + uint32_t memtype; + VkDeviceMemory memory; + + //FIXME: replace with an ordered list of free blocks. + VkDeviceSize gaps; + VkDeviceSize memoryoffset; + VkDeviceSize memorysize; + } *mempools; + struct descpool { VkDescriptorPool pool; @@ -450,11 +473,15 @@ void VKBE_RT_End(struct vk_rendertarg *targ); void VKBE_RT_Destroy(struct vk_rendertarg *targ); +qboolean VK_AllocatePoolMemory(uint32_t pooltype, VkDeviceSize memsize, VkDeviceSize poolalignment, vk_poolmem_t *mem); +void VK_ReleasePoolMemory(vk_poolmem_t *mem); +qboolean VK_AllocateImageMemory(VkImage image, qboolean dedicated, vk_poolmem_t *mem); //dedicated should normally be TRUE for render targets +void VK_AllocateBindImageMemory(vk_image_t *image, qboolean dedicated); //dedicated should normally be TRUE for render targets struct stagingbuf { VkBuffer buf; VkBuffer retbuf; - VkDeviceMemory memory; + vk_poolmem_t mem; size_t size; VkBufferUsageFlags usage; }; @@ -462,7 +489,7 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay void set_image_layout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkAccessFlags srcaccess, VkPipelineStageFlagBits srcstagemask, VkImageLayout new_image_layout, VkAccessFlags dstaccess, VkPipelineStageFlagBits dststagemask); void VK_CreateSampler(unsigned int flags, vk_image_t *img); void *VKBE_CreateStagingBuffer(struct stagingbuf *n, size_t size, VkBufferUsageFlags usage); -VkBuffer VKBE_FinishStaging(struct stagingbuf *n, VkDeviceMemory *memptr); +VkBuffer VKBE_FinishStaging(struct stagingbuf *n, vk_poolmem_t *memptr); void *VK_FencedBegin(void (*passed)(void *work), size_t worksize); void VK_FencedSubmit(void *work); void VK_FencedCheck(void);