diff --git a/src/dedicated/i_video.c b/src/dedicated/i_video.c
index 72fe0cdd50c324f783cd22f67254feca262bbb23..850eef60e97237b60f7c6b7d7366492a80f84f94 100644
--- a/src/dedicated/i_video.c
+++ b/src/dedicated/i_video.c
@@ -1,6 +1,7 @@
 #include "../doomdef.h"
 #include "../command.h"
 #include "../i_video.h"
+#include "../screen.h"
 
 rendermode_t rendermode = render_none;
 rendermode_t chosenrendermode = render_none;
@@ -57,8 +58,21 @@ void VID_SetSize(INT32 width, INT32 height)
 	(void)height;
 }
 
+resolution_t *VID_GetSupportedResolutions(INT32 *count)
+{
+	*count = 0;
+	return NULL;
+}
+
 boolean VID_IsMaximized(void)
 {
 	return false;
 }
 void VID_RestoreWindow(void){}
+
+boolean SCR_IsValidResolution(INT32 width, INT32 height)
+{
+	(void)width;
+	(void)height;
+	return false;
+}
diff --git a/src/f_finale.c b/src/f_finale.c
index 31c68be736f658d90792691adf69860bac53372a..c4973a1ae6a42a14055af3a61999aea40c67c169 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2638,10 +2638,13 @@ static void F_FigureActiveTtScale(void)
 			break;
 	}
 
-	for (; newttscale <= 6; newttscale++)
+	if (newttscale < 1)
 	{
-		if (ttavailable[newttscale-1])
-			break;
+		for (newttscale++; newttscale <= 6; newttscale++)
+		{
+			if (ttavailable[newttscale-1])
+				break;
+		}
 	}
 
 	activettscale = (newttscale >= 1 && newttscale <= 6) ? newttscale : 0;
diff --git a/src/i_video.h b/src/i_video.h
index 0cbdd8447a6e897c3c58320ea3ae82a33485037e..847faf2913f5b6435eed98373efbb0d8bc06301d 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -32,6 +32,13 @@ typedef enum
 	render_none = 3  // for dedicated server
 } rendermode_t;
 
+typedef struct
+{
+	UINT16 width;
+	UINT16 height;
+	UINT8 index;
+} resolution_t;
+
 /**	\brief current render mode
 */
 extern rendermode_t rendermode;
@@ -85,6 +92,10 @@ void VID_RestoreWindow(void);
 */
 boolean VID_GetNativeResolution(INT32 *width, INT32 *height);
 
+/**	\brief List resolutions that the current display supports
+*/
+resolution_t *VID_GetSupportedResolutions(INT32 *count);
+
 /**	\brief can video system do fullscreen
 */
 extern boolean allow_fullscreen;
diff --git a/src/m_menu.c b/src/m_menu.c
index cd23ef77bd5c1798b4a7721774014f79e3e0655f..4a608b3c804777564cce4743ad2c6a0ca407e8f9 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -13811,18 +13811,20 @@ static modedesc_t modedescs[MAXMODEDESCS];
 static void M_VideoModeMenu(INT32 choice)
 {
 	(void)choice;
+	INT32 count;
+	resolution_t *res = VID_GetSupportedResolutions(&count);
 
 	memset(modedescs, 0, sizeof(modedescs));
 
 	vidm_nummodes = 0;
 	vidm_selected = -1;
 
-	for (INT32 i = 0; i < MAXWINMODES && vidm_nummodes < MAXMODEDESCS; i++)
+	for (INT32 i = 0; i < count; i++)
 	{
 		modedesc_t *desc = &modedescs[vidm_nummodes];
 
-		INT32 width = windowedModes[i][0];
-		INT32 height = windowedModes[i][1];
+		INT32 width = res[i].width;
+		INT32 height = res[i].height;
 
 		desc->width = width;
 		desc->height = height;
diff --git a/src/r_draw8.c b/src/r_draw8.c
index 7ebfefa432af6ef34e23226d16c365a63b4b03af..349139f5e4a7700f35e93b304cbf71cbc25776e9 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -25,9 +25,9 @@
 void R_DrawColumn_8(void)
 {
 	INT32 count;
-	register UINT8 *dest;
-	register fixed_t frac;
-	fixed_t fracstep;
+	UINT8 *restrict dest;
+	intptr_t frac;
+	intptr_t fracstep;
 
 	count = dc_yh - dc_yl;
 
@@ -51,9 +51,9 @@ void R_DrawColumn_8(void)
 	// Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
 	// This is as fast as it gets.
 	{
-		register const UINT8 *source = dc_source;
-		register const lighttable_t *colormap = dc_colormap;
-		register INT32 heightmask = dc_texheight-1;
+		const UINT8 *restrict source = dc_source;
+		const lighttable_t *restrict colormap = dc_colormap;
+		intptr_t heightmask = dc_texheight-1;
 		if (dc_texheight & heightmask)   // not a power of 2 -- killough
 		{
 			heightmask++;
@@ -62,8 +62,7 @@ void R_DrawColumn_8(void)
 			if (frac < 0)
 				while ((frac += heightmask) <  0);
 			else
-				while (frac >= heightmask)
-					frac -= heightmask;
+				frac %= heightmask;
 
 			do
 			{
@@ -73,14 +72,15 @@ void R_DrawColumn_8(void)
 				*dest = colormap[source[frac>>FRACBITS]];
 				dest += vid.width;
 
+#if __SIZEOF_POINTER__ < 8 // 64-bit systems have large enough numbers for this to be a non-issue
 				// Avoid overflow.
 				if (fracstep > 0x7FFFFFFF - frac)
 					frac += fracstep - heightmask;
 				else
+#endif
 					frac += fracstep;
 
-				while (frac >= heightmask)
-					frac -= heightmask;
+				frac %= heightmask;
 			} while (--count);
 		}
 		else
@@ -106,9 +106,9 @@ void R_DrawColumn_8(void)
 void R_DrawColumnClamped_8(void)
 {
 	INT32 count;
-	UINT8 *dest;
-	fixed_t frac;
-	fixed_t fracstep;
+	UINT8 *restrict dest;
+	intptr_t frac;
+	intptr_t fracstep;
 
 	count = dc_yh - dc_yl;
 
@@ -132,10 +132,10 @@ void R_DrawColumnClamped_8(void)
 	// Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
 	// This is as fast as it gets.
 	{
-		const UINT8 *source = dc_source;
-		const lighttable_t *colormap = dc_colormap;
-		INT32 heightmask = dc_texheight-1;
-		INT32 idx;
+		const UINT8 *restrict source = dc_source;
+		const lighttable_t *restrict colormap = dc_colormap;
+		intptr_t heightmask = dc_texheight-1;
+		intptr_t idx;
 		if (dc_texheight & heightmask)   // not a power of 2 -- killough
 		{
 			heightmask++;
@@ -158,13 +158,14 @@ void R_DrawColumnClamped_8(void)
 				dest += vid.width;
 
 				// Avoid overflow.
+#if __SIZEOF_POINTER__ < 8
 				if (fracstep > 0x7FFFFFFF - frac)
 					frac += fracstep - heightmask;
 				else
+#endif
 					frac += fracstep;
 
-				while (frac >= heightmask)
-					frac -= heightmask;
+				frac %= heightmask;
 			} while (--count);
 		}
 		else
@@ -195,9 +196,9 @@ void R_DrawColumnClamped_8(void)
 void R_Draw2sMultiPatchColumn_8(void)
 {
 	INT32 count;
-	register UINT8 *dest;
-	register fixed_t frac;
-	fixed_t fracstep;
+	UINT8 *restrict dest;
+	intptr_t frac;
+	intptr_t fracstep;
 
 	count = dc_yh - dc_yl;
 
@@ -221,10 +222,10 @@ void R_Draw2sMultiPatchColumn_8(void)
 	// Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
 	// This is as fast as it gets.
 	{
-		register const UINT8 *source = dc_source;
-		register const lighttable_t *colormap = dc_colormap;
-		register INT32 heightmask = dc_texheight-1;
-		register UINT8 val;
+		const UINT8 *restrict source = dc_source;
+		const lighttable_t *restrict colormap = dc_colormap;
+		intptr_t heightmask = dc_texheight-1;
+		UINT8 val;
 		if (dc_texheight & heightmask)   // not a power of 2 -- killough
 		{
 			heightmask++;
@@ -233,8 +234,7 @@ void R_Draw2sMultiPatchColumn_8(void)
 			if (frac < 0)
 				while ((frac += heightmask) <  0);
 			else
-				while (frac >= heightmask)
-					frac -= heightmask;
+				frac %= heightmask;
 
 			do
 			{
@@ -249,13 +249,14 @@ void R_Draw2sMultiPatchColumn_8(void)
 				dest += vid.width;
 
 				// Avoid overflow.
+#if __SIZEOF_POINTER__ < 8
 				if (fracstep > 0x7FFFFFFF - frac)
 					frac += fracstep - heightmask;
 				else
+#endif
 					frac += fracstep;
 
-				while (frac >= heightmask)
-					frac -= heightmask;
+				frac %= heightmask;
 			} while (--count);
 		}
 		else
@@ -726,14 +727,14 @@ void R_DrawTranslatedColumn_8(void)
 */
 void R_DrawSpan_8 (void)
 {
-	fixed_t xposition;
-	fixed_t yposition;
-	fixed_t xstep, ystep;
-
-	UINT8 *source;
-	UINT8 *colormap;
-	UINT8 *dest;
-	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+	uintptr_t xposition;
+	uintptr_t yposition;
+	uintptr_t xstep, ystep;
+
+	UINT8 *restrict source;
+	UINT8 *restrict colormap;
+	UINT8 *restrict dest;
+	const UINT8 *restrict deststop = screens[0] + vid.rowbytes * vid.height;
 
 	size_t count = (ds_x2 - ds_x1 + 1);
 
diff --git a/src/r_segs.c b/src/r_segs.c
index bee349492e5b827f06820301e90cf5c6c3149585..5fdfdf271e6047f648c48a42e4b3a77d7b2dfc6c 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -1464,7 +1464,7 @@ static void R_RenderSegLoop (void)
 		// calculate texture offset
 		angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT;
 		textureoffset = rw_offset - FixedMul(FINETANGENT(angle & TANMASK), rw_distance);
-		texturecolumn = FixedDiv(textureoffset, rw_invmidtexturescalex);
+		texturecolumn = FixedMul(textureoffset, rw_midtexturescalex);
 
 		// texturecolumn and lighting are independent of wall tiers
 		if (segtextured)
@@ -1564,7 +1564,7 @@ static void R_RenderSegLoop (void)
 				if (mid >= floorclip[rw_x])
 					mid = floorclip[rw_x]-1;
 
-				toptexturecolumn = FixedDiv(textureoffset, rw_invtoptexturescalex);
+				toptexturecolumn = FixedMul(textureoffset, rw_toptexturescalex);
 
 				if (mid >= yl) // back ceiling lower than front ceiling ?
 				{
@@ -1611,7 +1611,7 @@ static void R_RenderSegLoop (void)
 				if (mid <= ceilingclip[rw_x])
 					mid = ceilingclip[rw_x]+1;
 
-				bottomtexturecolumn = FixedDiv(textureoffset, rw_invbottomtexturescalex);
+				bottomtexturecolumn = FixedMul(textureoffset, rw_bottomtexturescalex);
 
 				if (mid <= yh) // back floor higher than front floor ?
 				{
diff --git a/src/r_things.c b/src/r_things.c
index 7c096519fdeb461945d3e1a395d0f6a94d0f765a..6b1c531ae2d215df3f4df4bb5b5b1975729d2ad5 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1234,7 +1234,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 #endif
 
 		// Non-paper drawing loop
-		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
+		for (dc_x = vis->x1; dc_x <= vis->x2 && (frac>>FRACBITS) < patch->width; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
 		{
 			column = &patch->columns[frac>>FRACBITS];
 			localcolfunc (column, lengthcol);
diff --git a/src/screen.c b/src/screen.c
index fff97dca7e54f7d834afc3ebe93ef9a9c37188f2..8978ed47f6f5a99490c96ce2c96ec65f4a9f254d 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -61,29 +61,6 @@ void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void);
 // ------------------
 viddef_t vid;
 
-// windowed video modes from which to choose from.
-INT32 windowedModes[MAXWINMODES][2] =
-{
-	{1920,1200}, // 1.60,6.00
-	{1920,1080}, // 1.66
-	{1680,1050}, // 1.60,5.25
-	{1600,1200}, // 1.33
-	{1600, 900}, // 1.66
-	{1366, 768}, // 1.66
-	{1440, 900}, // 1.60,4.50
-	{1280,1024}, // 1.33?
-	{1280, 960}, // 1.33,4.00
-	{1280, 800}, // 1.60,4.00
-	{1280, 720}, // 1.66
-	{1152, 864}, // 1.33,3.60
-	{1024, 768}, // 1.33,3.20
-	{ 800, 600}, // 1.33,2.50
-	{ 640, 480}, // 1.33,2.00
-	{ 640, 400}, // 1.60,2.00
-	{ 320, 240}, // 1.33,1.00
-	{ 320, 200}, // 1.60,1.00
-};
-
 static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"}, {24, "24 bits"}, {32, "32 bits"}, {0, NULL}};
 
 //added : 03-02-98: default screen mode, as loaded/saved in config
@@ -267,15 +244,6 @@ void SCR_Recalc(void)
 #endif
 }
 
-boolean SCR_IsValidResolution(INT32 width, INT32 height)
-{
-	if (width < BASEVIDWIDTH || width > MAXVIDWIDTH)
-		return false;
-	if (height < BASEVIDHEIGHT || height > MAXVIDHEIGHT)
-		return false;
-	return true;
-}
-
 static boolean SCR_SetSize(INT32 width, INT32 height)
 {
 	if (SCR_IsValidResolution(width, height))
@@ -362,9 +330,9 @@ void SCR_CheckDefaultMode(void)
 
 		if (!SCR_IsValidResolution(width, height))
 		{
-			CONS_Alert(CONS_WARNING, "Invalid resolution given, defaulting to base resolution\n");
-			width = BASEVIDWIDTH;
-			height = BASEVIDHEIGHT;
+			CONS_Alert(CONS_WARNING, "Invalid resolution given, defaulting to a safe resolution\n");
+			width = 800;
+			height = 600;
 		}
 
 		SCR_ChangeResolution(width, height);
@@ -463,33 +431,6 @@ boolean SCR_IsAspectCorrect(INT32 width, INT32 height)
 	 );
 }
 
-const char *SCR_GetModeName(INT32 modeNum)
-{
-	static char vidModeName[MAXWINMODES][32];
-
-	if (modeNum == -1)
-		return "Fallback";
-	else if (modeNum > MAXWINMODES)
-		return NULL;
-
-	snprintf(&vidModeName[modeNum][0], 32, "%dx%d", windowedModes[modeNum][0], windowedModes[modeNum][1]);
-
-	return &vidModeName[modeNum][0];
-}
-
-INT32 SCR_GetModeForSize(INT32 w, INT32 h)
-{
-	int i;
-	for (i = 0; i < MAXWINMODES; i++)
-	{
-		if (windowedModes[i][0] == w && windowedModes[i][1] == h)
-		{
-			return i;
-		}
-	}
-	return -1;
-}
-
 // XMOD FPS display
 // moved out of os-specific code for consistency
 static boolean ticsgraph[TICRATE];
diff --git a/src/screen.h b/src/screen.h
index 2a9767dbd30aa62640df72a5be7fea4e6f0c647c..5cb53e0779ca68b590d56be780cd8e87a97cbe48 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -85,10 +85,6 @@ enum
 	VID_GL_LIBRARY_ERROR
 };
 
-#define MAXWINMODES 18
-
-extern INT32 windowedModes[MAXWINMODES][2];
-
 // ---------------------------------------------
 // color mode dependent drawer function pointers
 // ---------------------------------------------
@@ -164,9 +160,6 @@ void SCR_SetWindowSize(INT32 width, INT32 height);
 void SCR_SetSizeNoRestore(INT32 width, INT32 height);
 void SCR_ChangeRenderer(void);
 
-const char *SCR_GetModeName(INT32 modeNum);
-INT32 SCR_GetModeForSize(INT32 w, INT32 h);
-
 boolean SCR_IsValidResolution(INT32 width, INT32 height);
 
 extern CV_PossibleValue_t cv_renderer_t[];
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index d745eec8d3953a3ade4906b6791623e469526203..b8d7d88140bf00795d97919574812561b625dc1e 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -70,6 +70,7 @@
 #include "../st_stuff.h"
 #include "../hu_stuff.h"
 #include "../g_game.h"
+#include "../z_zone.h"
 #include "../i_video.h"
 #include "../console.h"
 #include "../command.h"
@@ -115,21 +116,14 @@ static SDL_bool disable_mouse = SDL_FALSE;
 static      INT32        mousemovex = 0, mousemovey = 0;
 
 // SDL vars
-static      SDL_Surface *vidSurface = NULL;
-static      SDL_Surface *bufSurface = NULL;
-static      SDL_Color    localPalette[256];
+static      UINT32       softPalette[256];
 static       SDL_bool    mousegrabok = SDL_TRUE;
 static       SDL_bool    wrapmouseok = SDL_FALSE;
 #define HalfWarpMouse(x,y) if (wrapmouseok) SDL_WarpMouseInWindow(window, (Uint16)(x/2),(Uint16)(y/2))
-static       SDL_bool    usesdl2soft = SDL_FALSE;
 static       SDL_bool    borderlesswindow = SDL_FALSE;
 
-Uint16      realwidth = BASEVIDWIDTH;
-Uint16      realheight = BASEVIDHEIGHT;
-
 SDL_Window   *window;
 SDL_Renderer *renderer;
-static SDL_Texture  *texture;
 static SDL_bool      havefocus = SDL_TRUE;
 
 static UINT32 refresh_rate;
@@ -138,9 +132,6 @@ static boolean video_init = false;
 
 static SDL_bool Impl_CreateWindow(SDL_bool fullscreen);
 
-static void Impl_VideoSetupSurfaces(int width, int height);
-static void Impl_VideoSetupBuffer(void);
-
 static void Impl_SetupSoftwareBuffer(void);
 
 static void Impl_InitOpenGL(void);
@@ -167,32 +158,8 @@ static SDL_bool Impl_RenderContextCreate(void)
 {
 	if (rendermode != render_opengl)
 	{
-		int flags = 0; // Use this to set SDL_RENDERER_* flags now
-
-		if (usesdl2soft)
-			flags |= SDL_RENDERER_SOFTWARE;
-		else if (cv_vidwait.value)
-		{
-#if SDL_VERSION_ATLEAST(2, 0, 18)
-			// If SDL is new enough, we can turn off vsync later.
-			flags |= SDL_RENDERER_PRESENTVSYNC;
-#else
-			// However, if it isn't, we should just silently turn vid_wait off
-			// This is because the renderer will be created before the config
-			// is read and vid_wait is set from the user's preferences, and thus
-			// vid_wait will have no effect.
-			CV_StealthSetValue(&cv_vidwait, 0);
-#endif
-		}
-
-		if (!renderer)
-			renderer = SDL_CreateRenderer(window, -1, flags);
-
-		if (renderer == NULL)
-		{
-			VIDEO_INIT_ERROR("Couldn't create rendering context: %s");
-			return SDL_FALSE;
-		}
+		// we do rasterization ourselves, so nothing to do
+		return SDL_TRUE;
 	}
 
 #ifdef HWRENDER
@@ -216,79 +183,23 @@ static SDL_bool Impl_RenderContextCreate(void)
 
 static SDL_bool Impl_RenderContextReset(void)
 {
-	if (renderer)
-	{
-		SDL_DestroyRenderer(renderer);
-		texture = NULL; // Destroying a renderer also destroys all of its textures
-	}
-	renderer = NULL;
-
 	if (Impl_RenderContextCreate() == SDL_FALSE)
 		return SDL_FALSE;
 
-	if (vidSurface != NULL)
-	{
-		SDL_FreeSurface(vidSurface);
-		vidSurface = NULL;
-	}
-
-	if (bufSurface != NULL)
-	{
-		SDL_FreeSurface(bufSurface);
-		bufSurface = NULL;
-	}
-
 #ifdef HWRENDER
 	if (rendermode == render_opengl)
 	{
 		SDL_GL_MakeCurrent(window, sdlglcontext);
 		SDL_GL_SetSwapInterval(cv_vidwait.value ? 1 : 0);
 
-		OglSdlSurface(realwidth, realheight);
+		OglSdlSurface(vid.width, vid.height);
 		HWR_Startup();
 	}
-	else
 #endif
-	{
-		SDL_RenderClear(renderer);
-		SDL_RenderSetLogicalSize(renderer, realwidth, realheight);
-		Impl_VideoSetupSurfaces(realwidth, realheight);
-	}
 
 	return SDL_TRUE;
 }
 
-static void Impl_VideoSetupSurfaces(int width, int height)
-{
-	int bpp = 16;
-	int sw_texture_format = SDL_PIXELFORMAT_ABGR8888;
-
-	if (!usesdl2soft)
-	{
-		sw_texture_format = SDL_PIXELFORMAT_RGB565;
-	}
-	else
-	{
-		bpp = 32;
-		sw_texture_format = SDL_PIXELFORMAT_RGBA8888;
-	}
-
-	if (texture == NULL)
-		texture = SDL_CreateTexture(renderer, sw_texture_format, SDL_TEXTUREACCESS_STREAMING, width, height);
-
-	// Set up SW surface
-	if (vidSurface == NULL)
-	{
-		Uint32 rmask;
-		Uint32 gmask;
-		Uint32 bmask;
-		Uint32 amask;
-
-		SDL_PixelFormatEnumToMasks(sw_texture_format, &bpp, &rmask, &gmask, &bmask, &amask);
-		vidSurface = SDL_CreateRGBSurface(0, width, height, bpp, rmask, gmask, bmask, amask);
-	}
-}
-
 static void Impl_SetupSoftwareBuffer(void)
 {
 	// Set up game's software render buffer
@@ -311,33 +222,62 @@ static void Impl_SetupSoftwareBuffer(void)
 		I_Error("%s", M_GetText("Not enough memory for video buffer\n"));
 }
 
-static SDL_Rect src_rect = { 0, 0, 0, 0 };
-
-static SDL_bool SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool reposition)
+resolution_t *VID_GetSupportedResolutions(INT32 *count)
 {
-	static SDL_bool wasfullscreen = SDL_FALSE;
-	int fullscreen_type = SDL_WINDOW_FULLSCREEN_DESKTOP;
+	static resolution_t *res;
+	static INT32 numres;
+	if (res != NULL)
+	{
+		*count = numres;
+		return res;
+	}
 
-	boolean should_set_window_size = realwidth != width || realheight != height;
+	// TODO: add support for multiple monitors
+	INT32 nummodes = SDL_GetNumDisplayModes(0);
+	SDL_DisplayMode mode, prevmode;
 
-	src_rect.w = realwidth = width;
-	src_rect.h = realheight = height;
+	res = Z_Malloc(sizeof(resolution_t *) * nummodes, PU_STATIC, NULL);
+	numres = 0;
+	CONS_Printf("Available resolutions:\n");
+	for (INT32 i = 0; i < nummodes; i++)
+	{
+		SDL_GetDisplayMode(0, i, &mode);
+		CONS_Printf(" * %dx%d@%d\n", mode.w, mode.h, mode.refresh_rate);
+		if (i > 0 && prevmode.w == mode.w && prevmode.h == mode.h)
+		{
+			// pick the highest refresh rate of all duplicate resolutions
+			if (prevmode.refresh_rate < mode.refresh_rate)
+			{
+				res[numres-1].index = i;
+				prevmode = mode;
+			}
+			continue;
+		}
 
+		res[numres].width = mode.w;
+		res[numres].height = mode.h;
+		res[numres++].index = i;
+		prevmode = mode;
+	}
+
+	*count = numres;
+	return res;
+}
+
+static SDL_bool SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool reposition)
+{
 	if (window)
 	{
 		if (fullscreen)
 		{
-			wasfullscreen = SDL_TRUE;
-			SDL_SetWindowFullscreen(window, fullscreen_type);
+			SDL_DestroyWindow(window);
+			window = NULL;
+			// create a new window to fix resolution quirks
+			return SDLSetMode(width, height, fullscreen, reposition);
 		}
 		else // windowed mode
 		{
-			if (wasfullscreen)
-			{
-				wasfullscreen = SDL_FALSE;
-				SDL_SetWindowFullscreen(window, 0);
-			}
-
+			SDL_SetWindowFullscreen(window, 0);
 			SDL_SetWindowSize(window, width, height);
 
 			if (reposition)
@@ -355,11 +295,42 @@ static SDL_bool SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_b
 		if (Impl_CreateWindow(fullscreen) == SDL_FALSE)
 			return SDL_FALSE;
 
-		wasfullscreen = fullscreen;
-		if (should_set_window_size)
-			SDL_SetWindowSize(window, width, height);
+		SDL_SetWindowSize(window, width, height);
 		if (fullscreen)
-			SDL_SetWindowFullscreen(window, fullscreen_type);
+		{
+			resolution_t *res;
+			INT32 numres, i;
+
+			res = VID_GetSupportedResolutions(&numres);
+			for (i = 0; i < numres; i++)
+			{
+				if (res[i].width == width && res[i].height == height)
+				{
+					SDL_DisplayMode mode;
+					SDL_GetDisplayMode(0, res[i].index, &mode);
+					if (SDL_SetWindowDisplayMode(window, &mode))
+					{
+						CONS_Alert(CONS_WARNING, "Couldn't change resolution to %dx%d: %s\n", width, height, SDL_GetError());
+						continue;
+					}
+					break;
+				}
+			}
+
+			if (i == numres)
+			{
+				// resolution is not supported, so default to native
+				CONS_Alert(CONS_WARNING, "Resolution %dx%d is not supported, defaulting to native\n", width, height);
+				SDL_DisplayMode resolution;
+
+				if (SDL_GetDesktopDisplayMode(i, &resolution) == 0)
+				{
+					vid.width = width = (INT32)(resolution.w);
+					vid.height = height = (INT32)(resolution.h);
+					SDL_SetWindowDisplayMode(window, &resolution);
+				}
+			}
+		}
 
 		Impl_SetDefaultWindowSizes();
 	}
@@ -606,43 +577,17 @@ void I_SetMouseGrab(boolean grab)
 
 static void VID_Command_NumModes_f (void)
 {
-	CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), MAXWINMODES);
-}
-
-// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had
-static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
-{
-	INT32 vfBPP;
-
-	if (!infoSurface)
-		return;
-
-	if (!SurfaceText)
-		SurfaceText = M_GetText("Unknown Surface");
-
-	vfBPP = infoSurface->format?infoSurface->format->BitsPerPixel:0;
-
-	CONS_Printf("\x82" "%s\n", SurfaceText);
-	CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP);
-
-	if (infoSurface->flags&SDL_PREALLOC)
-		CONS_Printf("%s", M_GetText(" Uses preallocated memory\n"));
-	else
-		CONS_Printf("%s", M_GetText(" Stored in system memory\n"));
-	if (infoSurface->flags&SDL_RLEACCEL)
-		CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n"));
-}
-
-static void VID_Command_Info_f (void)
-{
-	SurfaceInfo(bufSurface, M_GetText("Current Engine Mode"));
-	SurfaceInfo(vidSurface, M_GetText("Current Video Mode"));
+	INT32 numres;
+	VID_GetSupportedResolutions(&numres);
+	CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), numres);
 }
 
 static void VID_Command_ModeList_f(void)
 {
-	for (INT32 i = 0; i < MAXWINMODES; i++)
-		CONS_Printf("%2d: %dx%d\n", i, windowedModes[i][0], windowedModes[i][1]);
+	INT32 numres;
+	resolution_t *res = VID_GetSupportedResolutions(&numres);
+	for (INT32 i = 0; i < numres; i++)
+		CONS_Printf("%2d: %dx%d\n", i, res[i].width, res[i].height);
 }
 
 static void VID_Command_Mode_f (void)
@@ -653,20 +598,43 @@ static void VID_Command_Mode_f (void)
 		return;
 	}
 
+	INT32 numres;
+	resolution_t *res = VID_GetSupportedResolutions(&numres);
 	INT32 modenum = atoi(COM_Argv(1));
-	if (modenum >= MAXWINMODES)
+	if (modenum >= numres)
 		CONS_Printf(M_GetText("Video mode not present\n"));
 	else
 	{
 		if (modenum < 0)
 			modenum = 0;
 
-		vid.change.width = windowedModes[modenum][0];
-		vid.change.height = windowedModes[modenum][1];
+		vid.change.width = res[modenum].width;
+		vid.change.height = res[modenum].height;
 		vid.change.set = VID_RESOLUTION_CHANGED;
 	}
 }
 
+boolean SCR_IsValidResolution(INT32 width, INT32 height)
+{
+	if (width < BASEVIDWIDTH || width > MAXVIDWIDTH)
+		return false;
+	if (height < BASEVIDHEIGHT || height > MAXVIDHEIGHT)
+		return false;
+
+	if (USE_FULLSCREEN)
+	{
+		INT32 numres, i;
+		resolution_t *res = VID_GetSupportedResolutions(&numres);
+		for (i = 0; i < numres; i++)
+		{
+			if (res[i].width == width && res[i].height == height)
+				return true;
+		}
+		return false;
+	}
+	return true;
+}
+
 static void VID_Command_Width_f (void)
 {
 	if (COM_Argc() != 2)
@@ -745,9 +713,9 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
 			mousefocus = SDL_FALSE;
 			break;
 		case SDL_WINDOWEVENT_SIZE_CHANGED:
-			if ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0)
+			if ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) == 0)
 			{
-				if (realwidth != evt.data1 || realheight != evt.data2)
+				if (vid.width != evt.data1 || vid.height != evt.data2)
 					SCR_SetSizeNoRestore(evt.data1, evt.data2);
 				SCR_SetDefaultMode(evt.data1, evt.data2);
 			}
@@ -845,7 +813,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 
 		// If the event is from warping the pointer to middle
 		// of the screen then ignore it.
-		if ((evt.x == realwidth/2) && (evt.y == realheight/2))
+		if ((evt.x == vid.width/2) && (evt.y == vid.height/2))
 		{
 			firstmove = false;
 			return;
@@ -1265,8 +1233,8 @@ void I_GetEvent(void)
 		//SDL_memset(&event, 0, sizeof(event_t));
 		event.type = ev_mouse;
 		event.key = 0;
-		event.x = (INT32)lround(mousemovex * ((float)wwidth / (float)realwidth));
-		event.y = (INT32)lround(mousemovey * ((float)wheight / (float)realheight));
+		event.x = (INT32)lround(mousemovex * ((float)wwidth / (float)vid.width));
+		event.y = (INT32)lround(mousemovey * ((float)wheight / (float)vid.height));
 		D_PostEvent(&event);
 	}
 
@@ -1284,7 +1252,7 @@ void I_StartupMouse(void)
 
 	if (!firsttimeonmouse)
 	{
-		HalfWarpMouse(realwidth, realheight); // warp to center
+		HalfWarpMouse(vid.width, vid.height); // warp to center
 	}
 	else
 		firsttimeonmouse = SDL_FALSE;
@@ -1360,18 +1328,40 @@ void I_FinishUpdate(void)
 
 	if (rendermode == render_soft && screens[0])
 	{
-		if (!bufSurface) // Double-check
-			Impl_VideoSetupBuffer();
-
-		SDL_BlitSurface(bufSurface, &src_rect, vidSurface, &src_rect);
-		// Fury -- there's no way around UpdateTexture, the GL backend uses it anyway
-		SDL_LockSurface(vidSurface);
-		SDL_UpdateTexture(texture, &src_rect, vidSurface->pixels, vidSurface->pitch);
-		SDL_UnlockSurface(vidSurface);
+		SDL_Surface *surface = SDL_GetWindowSurface(window);
+		// make sure the surface is ready before we render
+		if (surface != NULL)
+		{
+			if (surface->w < vid.width || surface->h < vid.height)
+				return; // sway/x11 forced us into a lower resolution than we support, don't render
 
-		SDL_RenderClear(renderer);
-		SDL_RenderCopy(renderer, texture, &src_rect, NULL);
-		SDL_RenderPresent(renderer);
+			SDL_LockSurface(surface);
+			UINT32 *restrict pixels = surface->pixels;
+			const UINT8 *restrict source = screens[0];
+			if (surface->w != vid.width || surface->h != vid.height)
+			{
+				// the screen doesn't allow the resolution even though it was supported
+				// this happens on wayland for some reason
+				// as a workaround, scale manually so graphics don't get fucked
+				INT32 skip = surface->w - vid.width;
+				INT32 i, j;
+				for (i = 0; i < vid.height; i++)
+				{
+					for (j = 0; j < vid.width; j++)
+						*pixels++ = softPalette[*source++];
+					pixels += skip;
+				}
+			}
+			else
+			{
+				INT32 size = surface->w * surface->h;
+				INT32 i;
+				for (i = 0; i < size; i++)
+					*pixels++ = softPalette[*source++];
+			}
+			SDL_UnlockSurface(surface);
+		}
+		SDL_UpdateWindowSurface(window);
 	}
 #ifdef HWRENDER
 	else if (rendermode == render_opengl)
@@ -1419,15 +1409,15 @@ void I_ReadScreen(UINT8 *scr)
 void I_SetPalette(RGBA_t *palette)
 {
 	size_t i;
+	SDL_Surface *surface = SDL_GetWindowSurface(window);
 	for (i=0; i<256; i++)
 	{
-		localPalette[i].r = palette[i].s.red;
-		localPalette[i].g = palette[i].s.green;
-		localPalette[i].b = palette[i].s.blue;
+		UINT32 color = palette[i].s.red << surface->format->Rshift;
+		color |= palette[i].s.green << surface->format->Gshift;
+		color |= palette[i].s.blue << surface->format->Bshift;
+		color |= surface->format->Amask;
+		softPalette[i] = color;
 	}
-	//if (vidSurface) SDL_SetPaletteColors(vidSurface->format->palette, localPalette, 0, 256);
-	// Fury -- SDL2 vidSurface is a 32-bit surface buffer copied to the texture. It's not palletized, like bufSurface.
-	if (bufSurface) SDL_SetPaletteColors(bufSurface->format->palette, localPalette, 0, 256);
 }
 
 void VID_CheckGLLoaded(rendermode_t oldrender)
@@ -1591,7 +1581,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 		return SDL_TRUE;
 
 	if (fullscreen)
-		flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+		flags |= SDL_WINDOW_FULLSCREEN;
 
 	if (borderlesswindow)
 		flags |= SDL_WINDOW_BORDERLESS;
@@ -1606,14 +1596,15 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 #endif
 
 	// Create a window
-	window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, realwidth, realheight, flags);
+	window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, vid.width, vid.height, flags);
 	if (window == NULL)
 	{
 		VIDEO_INIT_ERROR("Couldn't create window: %s");
 		return SDL_FALSE;
 	}
 
-	SDL_SetWindowMinimumSize(window, BASEVIDWIDTH, BASEVIDHEIGHT);
+	if (!fullscreen)
+		SDL_SetWindowMinimumSize(window, BASEVIDWIDTH, BASEVIDHEIGHT);
 
 #ifdef USE_WINDOW_ICON
 	Impl_SetWindowIcon();
@@ -1630,34 +1621,6 @@ static void Impl_SetWindowIcon(void)
 }
 #endif
 
-static void Impl_VideoSetupBuffer(void)
-{
-	if (bufSurface != NULL)
-	{
-		SDL_FreeSurface(bufSurface);
-		bufSurface = NULL;
-	}
-	// Set up the SDL palletized buffer (copied to vidbuffer before being rendered to texture)
-	if (vid.bpp == 1)
-	{
-		bufSurface = SDL_CreateRGBSurfaceFrom(screens[0],vid.width,vid.height,8,
-			(int)vid.rowbytes,0x00000000,0x00000000,0x00000000,0x00000000); // 256 mode
-	}
-	else if (vid.bpp == 2) // Fury -- don't think this is used at all anymore
-	{
-		bufSurface = SDL_CreateRGBSurfaceFrom(screens[0],vid.width,vid.height,15,
-			(int)vid.rowbytes,0x00007C00,0x000003E0,0x0000001F,0x00000000); // 555 mode
-	}
-	if (bufSurface)
-	{
-		SDL_SetPaletteColors(bufSurface->format->palette, localPalette, 0, 256);
-	}
-	else
-	{
-		I_Error("%s", M_GetText("No system memory for SDL buffer surface\n"));
-	}
-}
-
 static void Impl_InitVideoSubSystem(void)
 {
 	if (video_init)
@@ -1683,7 +1646,6 @@ void I_StartupGraphics(void)
 		return;
 
 	COM_AddCommand ("vid_nummodes", NULL, VID_Command_NumModes_f, COM_LUA);
-	COM_AddCommand ("vid_info", NULL, VID_Command_Info_f, COM_LUA);
 	COM_AddCommand ("vid_modelist", NULL, VID_Command_ModeList_f, COM_LUA);
 	COM_AddCommand ("vid_mode", NULL, VID_Command_Mode_f, 0);
 	COM_AddCommand ("vid_width", NULL, VID_Command_Width_f, 0);
@@ -1754,7 +1716,6 @@ void I_StartupGraphics(void)
 	if (chosenrendermode != render_none)
 		rendermode = chosenrendermode;
 
-	usesdl2soft = M_CheckParm("-softblit");
 	borderlesswindow = M_CheckParm("-borderless");
 
 #ifdef HWRENDER
@@ -1783,7 +1744,6 @@ void I_StartupGraphics(void)
 	if (M_CheckParm("-nomousegrab"))
 		mousegrabok = SDL_FALSE;
 
-	VID_Command_Info_f();
 	SDLdoUngrabMouse();
 
 	SDL_RaiseWindow(window);
@@ -1863,17 +1823,6 @@ void I_ShutdownGraphics(void)
 	icoSurface = NULL;
 #endif
 
-	if (rendermode == render_soft)
-	{
-		if (vidSurface)
-			SDL_FreeSurface(vidSurface);
-		vidSurface = NULL;
-
-		if (bufSurface)
-			SDL_FreeSurface(bufSurface);
-		bufSurface = NULL;
-	}
-
 	free(vid.buffer);
 	vid.buffer = NULL;
 
diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c
index c78b43ec4097cafad8420ceb7497e2883220a67e..ea02279958ddac3e39c07caa8185ae8ae84880ba 100644
--- a/src/sdl/ogl_sdl.c
+++ b/src/sdl/ogl_sdl.c
@@ -189,7 +189,7 @@ void OglSdlFinishUpdate(boolean waitvbl)
 	HWR_DrawScreenFinalTexture(sdlw, sdlh);
 	SDL_GL_SwapWindow(window);
 
-	GClipRect(0, 0, realwidth, realheight, NZCLIP_PLANE);
+	GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
 
 	// Sryder:	We need to draw the final screen texture again into the other buffer in the original position so that
 	//			effects that want to take the old screen can do so after this
diff --git a/src/z_zone.c b/src/z_zone.c
index dabd68dfa40dd880400b53f03d3a8ffaf851d579..0a84efe963269e3f0d480d351065a1d6e27f94a5 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -54,8 +54,8 @@ static boolean Z_calloc = false;
 #endif
 
 #if !defined(ASAN_POISON_MEMORY_REGION)
-#    define ASAN_POISON_MEMORY_REGION(a, b) do {} while(0)
-#    define ASAN_UNPOISON_MEMORY_REGION(a, b) do {} while(0)
+#    define ASAN_POISON_MEMORY_REGION(a, b) {}
+#    define ASAN_UNPOISON_MEMORY_REGION(a, b) {}
 #endif
 
 #define ZONEID 0xa441d13d