diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c
index 03b315ff9..29ca02341 100644
--- a/engine/gl/gl_shader.c
+++ b/engine/gl/gl_shader.c
@@ -4118,9 +4118,9 @@ qboolean Shader_Init (void)
 	else
 	{
 		memset(wibuf, 0xff, sizeof(wibuf));
-		r_whiteimage = R_LoadTexture("$whiteimage", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA);
+		r_whiteimage = R_LoadTexture("$whiteimage", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA|IF_NOPURGE);
 		memset(wibuf, 0, sizeof(wibuf));
-		r_blackimage = R_LoadTexture("$blackimage", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA);
+		r_blackimage = R_LoadTexture("$blackimage", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA|IF_NOPURGE);
 	}
 
 	Shader_NeedReload(true);
diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c
index ae82d6aa5..12d206b8d 100644
--- a/engine/gl/gl_vidlinuxglx.c
+++ b/engine/gl/gl_vidlinuxglx.c
@@ -103,6 +103,7 @@ static Window vid_root;
 #ifdef GLQUAKE
 static GLXContext ctx = NULL;
 #endif
+extern qboolean vid_isfullscreen;
 static int scrnum;
 static long vid_x_eventmask;
 static enum
@@ -4430,6 +4431,7 @@ static qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int
 		x11.pXMoveResizeWindow(vid_dpy, vid_window, fullscreenx, fullscreeny, fullscreenwidth, fullscreenheight);
 	if (fullscreenflags)
 		fullscreenflags |= FULLSCREEN_ACTIVE;
+	vid_isfullscreen = !!(fullscreenflags &FULLSCREEN_ACTIVE);
 
 	if (X11_CheckFeature("xi2", true) && XI2_Init())
 	{
@@ -4891,6 +4893,7 @@ void Sys_SendKeyEvents(void)
 				}
 				if (fullscreenflags)
 					fullscreenflags |= FULLSCREEN_ACTIVE;
+				vid_isfullscreen = !!(fullscreenflags &FULLSCREEN_ACTIVE);
 			}
 			if (modeswitchpending < 0)
 			{
@@ -4923,6 +4926,7 @@ void Sys_SendKeyEvents(void)
 						x11.pXMapWindow(vid_dpy, vid_decoywindow);
 					}
 					fullscreenflags &= ~FULLSCREEN_ACTIVE;
+					vid_isfullscreen = !!(fullscreenflags &FULLSCREEN_ACTIVE);
 				}
 			}
 			modeswitchpending = 0;
diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c
index 6cfd48c76..649845e98 100644
--- a/engine/vk/vk_init.c
+++ b/engine/vk/vk_init.c
@@ -48,6 +48,7 @@ void VK_RegisterVulkanCvars(void)
 #endif
 }
 void R2D_Console_Resize(void);
+static void VK_DestroySampler(VkSampler s);
 
 extern qboolean		scr_con_forcedraw;
 
@@ -408,7 +409,7 @@ void VK_DestroyVkTexture(vk_image_t *img)
 	if (!img)
 		return;
 	if (img->sampler)
-		vkDestroySampler(vk.device, img->sampler, vkallocationcb);
+		VK_DestroySampler(img->sampler);
 	if (img->view)
 		vkDestroyImageView(vk.device, img->view, vkallocationcb);
 	if (img->image)
@@ -880,6 +881,8 @@ static qboolean VK_CreateSwapChain(void)
 		free(presentmode);
 		free(surffmts);
 
+		if (vid_isfullscreen)	//nvidia really doesn't like this. its fine when windowed though.
+			VK_DestroySwapChain();
 		swapinfo.oldSwapchain = vk.swapchain;
 
 		newvkswapchain = VK_NULL_HANDLE;
@@ -1149,13 +1152,38 @@ void	VK_Draw_Shutdown(void)
 	Image_Shutdown();
 }
 
+static void VK_DestroySampler(VkSampler s)
+{
+	struct vksamplers_s *ref;
+	for (ref = vk.samplers; ref; ref = ref->next)
+	{
+		if (ref->samp == s)
+		{
+			if (--ref->usages == 0)
+			{
+				vkDestroySampler(vk.device, ref->samp, vkallocationcb);
+				*ref->link = ref->next;
+				if (ref->next)
+					ref->next->link = ref->link;
+				Z_Free(ref);
+			}
+		}
+	}
+}
+static void VK_DestroySampler_FrameEnd(void *w)
+{
+	VK_DestroySampler(*(VkSampler*)w);
+}
+
 void VK_CreateSampler(unsigned int flags, vk_image_t *img)
 {
+	struct vksamplers_s *ref;
 	qboolean clamptoedge = flags & IF_CLAMP;
 	VkSamplerCreateInfo lmsampinfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
 
+
 	if (img->sampler)
-		vkDestroySampler(vk.device, img->sampler, vkallocationcb);
+		VK_DestroySampler(img->sampler);
 
 	if (flags & IF_LINEAR)
 	{
@@ -1196,14 +1224,28 @@ void VK_CreateSampler(unsigned int flags, vk_image_t *img)
 	lmsampinfo.maxLod = vk.mipcap[1];
 	lmsampinfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
 	lmsampinfo.unnormalizedCoordinates = VK_FALSE;
-	VkAssert(vkCreateSampler(vk.device, &lmsampinfo, NULL, &img->sampler));
+
+	for (ref = vk.samplers; ref; ref = ref->next)
+		if (ref->flags == flags)
+			if (!memcmp(&ref->props, &lmsampinfo, sizeof(lmsampinfo)))
+				break;
+
+	if (!ref)
+	{
+		ref = Z_Malloc(sizeof(*ref));
+		ref->flags = flags;
+		ref->props = lmsampinfo;
+		ref->next = vk.samplers;
+		ref->link = &vk.samplers;
+		if (vk.samplers)
+			vk.samplers->link = &ref->next;
+		vk.samplers = ref;
+		VkAssert(vkCreateSampler(vk.device, &ref->props, NULL, &ref->samp));
+	}
+	ref->usages++;
+	img->sampler = ref->samp;
 }
 
-static void VK_DestroySampler(void *w)
-{
-	VkSampler s = *(VkSampler*)w;
-	vkDestroySampler(vk.device, s, vkallocationcb);
-}
 void VK_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis)
 {
 	uint32_t i;
@@ -1223,7 +1265,7 @@ void VK_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3],
 			if (imagelist->vkimage->sampler)
 			{	//the sampler might still be in use, so clean it up at the end of the frame.
 				//all this to avoid syncing all the queues...
-				VK_AtFrameEnd(VK_DestroySampler, &imagelist->vkimage->sampler, sizeof(imagelist->vkimage->sampler));
+				VK_AtFrameEnd(VK_DestroySampler_FrameEnd, &imagelist->vkimage->sampler, sizeof(imagelist->vkimage->sampler));
 				imagelist->vkimage->sampler = VK_NULL_HANDLE;
 			}
 			VK_CreateSampler(imagelist->flags, imagelist->vkimage);
@@ -2118,10 +2160,10 @@ void    VK_DestroyTexture			(texid_t tex)
 void	VK_R_Init					(void)
 {
 	uint32_t white[6] = {~0u,~0u,~0u,~0u,~0u,~0u};
-	r_blackcubeimage = Image_CreateTexture("***blackcube***", NULL, IF_NEAREST|IF_TEXTYPE_CUBE);
+	r_blackcubeimage = Image_CreateTexture("***blackcube***", NULL, IF_NEAREST|IF_TEXTYPE_CUBE|IF_NOPURGE);
 	Image_Upload(r_blackcubeimage, TF_RGBX32, NULL, NULL, 1, 1, 6, IF_NEAREST|IF_NOMIPMAP|IF_NOGAMMA|IF_TEXTYPE_CUBE);
 
-	r_whitecubeimage = Image_CreateTexture("***whitecube***", NULL, IF_NEAREST|IF_TEXTYPE_CUBE);
+	r_whitecubeimage = Image_CreateTexture("***whitecube***", NULL, IF_NEAREST|IF_TEXTYPE_CUBE|IF_NOPURGE);
 	Image_Upload(r_whitecubeimage, TF_RGBX32, white, NULL, 1, 1, 6, IF_NEAREST|IF_NOMIPMAP|IF_NOGAMMA|IF_TEXTYPE_CUBE);
 }
 void	VK_R_DeInit					(void)
diff --git a/engine/vk/vkrenderer.h b/engine/vk/vkrenderer.h
index ac33429a4..121db2ac4 100644
--- a/engine/vk/vkrenderer.h
+++ b/engine/vk/vkrenderer.h
@@ -395,6 +395,16 @@ extern struct vulkaninfo_s
 	qboolean neednewswapchain;	//something changed that invalidates the old one.
 	qboolean devicelost;		//we seriously fucked up somewhere. or the gpu is shite.
 
+	struct vksamplers_s
+	{
+		VkSampler samp;
+		unsigned int usages;	//refcounted.
+		unsigned int flags;
+		VkSamplerCreateInfo props;
+		struct vksamplers_s *next;
+		struct vksamplers_s **link;
+	} *samplers;
+
 	struct vkwork_s
 	{
 		struct vkwork_s *next;