]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_textures.c
added RENDERPATH_GLES2
[xonotic/darkplaces.git] / gl_textures.c
index 67301b1c9eb56d74b06e1eea7aee7b4dad5315e9..03dc5ef11a135e0bad63ec828680fc793b8cc375 100644 (file)
@@ -1,9 +1,14 @@
 
 #include "quakedef.h"
+#ifdef SUPPORTD3D
+#include <d3d9.h>
+extern LPDIRECT3DDEVICE9 vid_d3d9dev;
+#endif
 #include "image.h"
 #include "jpeg.h"
 #include "image_png.h"
 #include "intoverflow.h"
+#include "dpsoftrast.h"
 
 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
@@ -26,11 +31,26 @@ cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompressio
 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
 cvar_t gl_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
-cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
+cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
+cvar_t r_texture_dds_load_alphamode = {0, "r_texture_dds_load_alphamode", "1", "0: trust DDPF_ALPHAPIXELS flag, 1: texture format and brute force search if ambiguous, 2: texture format only"};
+cvar_t r_texture_dds_load_logfailure = {0, "r_texture_dds_load_logfailure", "1", "log missing DDS textures to ddstexturefailures.log"};
+cvar_t r_texture_dds_swdecode = {0, "r_texture_dds_swdecode", "0", "0: don't software decode DDS, 1: software decode DDS if unsupported, 2: always software decode DDS"};
 
 qboolean       gl_filter_force = false;
 int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
 int            gl_filter_mag = GL_LINEAR;
+DPSOFTRAST_TEXTURE_FILTER dpsoftrast_filter_mipmap = DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE;
+DPSOFTRAST_TEXTURE_FILTER dpsoftrast_filter_nomipmap = DPSOFTRAST_TEXTURE_FILTER_LINEAR;
+
+#ifdef SUPPORTD3D
+int d3d_filter_flatmin = D3DTEXF_LINEAR;
+int d3d_filter_flatmag = D3DTEXF_LINEAR;
+int d3d_filter_flatmix = D3DTEXF_POINT;
+int d3d_filter_mipmin = D3DTEXF_LINEAR;
+int d3d_filter_mipmag = D3DTEXF_LINEAR;
+int d3d_filter_mipmix = D3DTEXF_LINEAR;
+int d3d_filter_nomip = false;
+#endif
 
 
 static mempool_t *texturemempool;
@@ -80,13 +100,12 @@ typedef enum gltexturetype_e
        GLTEXTURETYPE_2D,
        GLTEXTURETYPE_3D,
        GLTEXTURETYPE_CUBEMAP,
-       GLTEXTURETYPE_RECTANGLE,
        GLTEXTURETYPE_TOTAL
 }
 gltexturetype_t;
 
-static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
-static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
+static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB};
+static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2};
 static int cubemapside[6] =
 {
        GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
@@ -104,6 +123,23 @@ typedef struct gltexture_s
        int texnum; // GL texture slot number
        qboolean dirty; // indicates that R_RealGetTexture should be called
        int gltexturetypeenum; // used by R_Mesh_TexBind
+       // d3d stuff the backend needs
+       void *d3dtexture;
+#ifdef SUPPORTD3D
+       qboolean d3disdepthsurface; // for depth/stencil surfaces
+       int d3dformat;
+       int d3dusage;
+       int d3dpool;
+       int d3daddressu;
+       int d3daddressv;
+       int d3daddressw;
+       int d3dmagfilter;
+       int d3dminfilter;
+       int d3dmipfilter;
+       int d3dmaxmiplevelfilter;
+       int d3dmipmaplodbias;
+       int d3dmaxmiplevel;
+#endif
 
        // dynamic texture stuff [11/22/2007 Black]
        updatecallback_t updatecallback;
@@ -143,6 +179,8 @@ typedef struct gltexture_s
        int tilewidth, tileheight, tiledepth;
        // 1 or 6 depending on texturetype
        int sides;
+       // how many mipmap levels in this texture
+       int miplevels;
        // bytes per pixel
        int bytesperpixel;
        // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
@@ -185,11 +223,11 @@ static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
        case TEXTYPE_PALETTE:
                return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
        case TEXTYPE_RGBA:
-               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
+               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.ext_texture_compression_s3tc)
                        return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
                return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
        case TEXTYPE_BGRA:
-               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
+               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.ext_texture_compression_s3tc)
                        return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
                return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
        case TEXTYPE_ALPHA:
@@ -259,10 +297,42 @@ void R_FreeTexture(rtexture_t *rt)
        else
                Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
 
-       if (glt->texnum)
+       switch(vid.renderpath)
        {
-               CHECKGLERROR
-               qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               if (glt->texnum)
+               {
+                       CHECKGLERROR
+                       qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+               }
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               if (glt->d3disdepthsurface)
+                       IDirect3DSurface9_Release((IDirect3DSurface9 *)glt->d3dtexture);
+               else if (glt->tiledepth > 1)
+                       IDirect3DVolumeTexture9_Release((IDirect3DVolumeTexture9 *)glt->d3dtexture);
+               else if (glt->sides == 6)
+                       IDirect3DCubeTexture9_Release((IDirect3DCubeTexture9 *)glt->d3dtexture);
+               else
+                       IDirect3DTexture9_Release((IDirect3DTexture9 *)glt->d3dtexture);
+               glt->d3dtexture = NULL;
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               if (glt->texnum)
+                       DPSOFTRAST_Texture_Free(glt->texnum);
+               break;
        }
 
        if (glt->inputtexels)
@@ -308,21 +378,41 @@ void R_FreeTexturePool(rtexturepool_t **rtexturepool)
 
 typedef struct glmode_s
 {
-       char *name;
+       const char *name;
        int minification, magnification;
+       DPSOFTRAST_TEXTURE_FILTER dpsoftrastfilter_mipmap, dpsoftrastfilter_nomipmap;
 }
 glmode_t;
 
 static glmode_t modes[6] =
 {
-       {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
-       {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
-       {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
-       {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
-       {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
-       {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
+       {"GL_NEAREST", GL_NEAREST, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR", GL_LINEAR, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR},
+       {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_LINEAR},
+       {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_LINEAR}
 };
 
+#ifdef SUPPORTD3D
+typedef struct d3dmode_s
+{
+       const char *name;
+       int m1, m2;
+}
+d3dmode_t;
+
+static d3dmode_t d3dmodes[6] =
+{
+       {"GL_NEAREST", D3DTEXF_POINT, D3DTEXF_POINT},
+       {"GL_LINEAR", D3DTEXF_LINEAR, D3DTEXF_POINT},
+       {"GL_NEAREST_MIPMAP_NEAREST", D3DTEXF_POINT, D3DTEXF_POINT},
+       {"GL_LINEAR_MIPMAP_NEAREST", D3DTEXF_LINEAR, D3DTEXF_POINT},
+       {"GL_NEAREST_MIPMAP_LINEAR", D3DTEXF_POINT, D3DTEXF_LINEAR},
+       {"GL_LINEAR_MIPMAP_LINEAR", D3DTEXF_LINEAR, D3DTEXF_LINEAR}
+};
+#endif
+
 static void GL_TextureMode_f (void)
 {
        int i;
@@ -358,37 +448,99 @@ static void GL_TextureMode_f (void)
        gl_filter_mag = modes[i].magnification;
        gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
 
-       // change all the existing mipmap texture objects
-       // FIXME: force renderer(/client/something?) restart instead?
-       CHECKGLERROR
-       GL_ActiveTexture(0);
-       for (pool = gltexturepoolchain;pool;pool = pool->next)
+       dpsoftrast_filter_mipmap = modes[i].dpsoftrastfilter_mipmap;
+       dpsoftrast_filter_nomipmap = modes[i].dpsoftrastfilter_nomipmap;
+
+       switch(vid.renderpath)
        {
-               for (glt = pool->gltchain;glt;glt = glt->chain)
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               // change all the existing mipmap texture objects
+               // FIXME: force renderer(/client/something?) restart instead?
+               CHECKGLERROR
+               GL_ActiveTexture(0);
+               for (pool = gltexturepoolchain;pool;pool = pool->next)
                {
-                       // only update already uploaded images
-                       if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
+                       for (glt = pool->gltchain;glt;glt = glt->chain)
                        {
-                               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
-                               qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
-                               if (glt->flags & TEXF_MIPMAP)
+                               // only update already uploaded images
+                               if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
                                {
-                                       qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
+                                       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+                                       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+                                       if (glt->flags & TEXF_MIPMAP)
+                                       {
+                                               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
+                                       }
+                                       else
+                                       {
+                                               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
+                                       }
+                                       qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
+                                       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
                                }
-                               else
+                       }
+               }
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               d3d_filter_flatmin = d3dmodes[i].m1;
+               d3d_filter_flatmag = d3dmodes[i].m1;
+               d3d_filter_flatmix = D3DTEXF_POINT;
+               d3d_filter_mipmin = d3dmodes[i].m1;
+               d3d_filter_mipmag = d3dmodes[i].m1;
+               d3d_filter_mipmix = d3dmodes[i].m2;
+               d3d_filter_nomip = i < 2;
+               if (gl_texture_anisotropy.integer > 1 && i == 5)
+                       d3d_filter_mipmin = d3d_filter_mipmag = D3DTEXF_ANISOTROPIC;
+               for (pool = gltexturepoolchain;pool;pool = pool->next)
+               {
+                       for (glt = pool->gltchain;glt;glt = glt->chain)
+                       {
+                               // only update already uploaded images
+                               if (glt->d3dtexture && !glt->d3disdepthsurface && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
                                {
-                                       qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
+                                       if (glt->flags & TEXF_MIPMAP)
+                                       {
+                                               glt->d3dminfilter = d3d_filter_mipmin;
+                                               glt->d3dmagfilter = d3d_filter_mipmag;
+                                               glt->d3dmipfilter = d3d_filter_mipmix;
+                                               glt->d3dmaxmiplevelfilter = 0;
+                                       }
+                                       else
+                                       {
+                                               glt->d3dminfilter = d3d_filter_flatmin;
+                                               glt->d3dmagfilter = d3d_filter_flatmag;
+                                               glt->d3dmipfilter = d3d_filter_flatmix;
+                                               glt->d3dmaxmiplevelfilter = 0;
+                                       }
                                }
-                               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
-                               qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
                        }
                }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               // change all the existing texture objects
+               for (pool = gltexturepoolchain;pool;pool = pool->next)
+                       for (glt = pool->gltchain;glt;glt = glt->chain)
+                               if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
+                                       DPSOFTRAST_Texture_Filter(glt->texnum, (glt->flags & TEXF_MIPMAP) ? dpsoftrast_filter_mipmap : dpsoftrast_filter_nomipmap);
+               break;
        }
 }
 
-static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
+static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth, int *outmiplevels)
 {
-       int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
+       int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1, miplevels = 1;
 
        switch (texturetype)
        {
@@ -409,39 +561,61 @@ static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, i
                break;
        }
 
-       if (outwidth)
+       if (vid.support.arb_texture_non_power_of_two)
        {
-               if (vid.support.arb_texture_non_power_of_two)
-                       width2 = min(inwidth >> picmip, maxsize);
-               else
-               {
-                       for (width2 = 1;width2 < inwidth;width2 <<= 1);
-                       for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
-               }
-               *outwidth = max(1, width2);
+               width2 = min(inwidth >> picmip, maxsize);
+               height2 = min(inheight >> picmip, maxsize);
+               depth2 = min(indepth >> picmip, maxsize);
        }
-       if (outheight)
+       else
        {
-               if (vid.support.arb_texture_non_power_of_two)
-                       height2 = min(inheight >> picmip, maxsize);
-               else
-               {
-                       for (height2 = 1;height2 < inheight;height2 <<= 1);
-                       for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
-               }
-               *outheight = max(1, height2);
+               for (width2 = 1;width2 < inwidth;width2 <<= 1);
+               for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
+               for (height2 = 1;height2 < inheight;height2 <<= 1);
+               for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
+               for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
+               for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
        }
-       if (outdepth)
+
+       switch(vid.renderpath)
        {
-               if (vid.support.arb_texture_non_power_of_two)
-                       depth2 = min(indepth >> picmip, maxsize);
-               else
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
+               break;
+       case RENDERPATH_D3D9:
+#if 0
+               // for some reason the REF rasterizer (and hence the PIX debugger) does not like small textures...
+               if (texturetype == GLTEXTURETYPE_2D)
                {
-                       for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
-                       for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
+                       width2 = max(width2, 2);
+                       height2 = max(height2, 2);
                }
-               *outdepth = max(1, depth2);
+#endif
+               break;
+       }
+
+       miplevels = 1;
+       if (flags & TEXF_MIPMAP)
+       {
+               int extent = max(width2, max(height2, depth2));
+               while(extent >>= 1)
+                       miplevels++;
        }
+
+       if (outwidth)
+               *outwidth = max(1, width2);
+       if (outheight)
+               *outheight = max(1, height2);
+       if (outdepth)
+               *outdepth = max(1, depth2);
+       if (outmiplevels)
+               *outmiplevels = miplevels;
 }
 
 
@@ -449,7 +623,7 @@ static int R_CalcTexelDataSize (gltexture_t *glt)
 {
        int width2, height2, depth2, size;
 
-       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
+       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2, NULL);
 
        size = width2 * height2 * depth2;
 
@@ -524,10 +698,29 @@ static void R_TextureStats_f(void)
 
 static void r_textures_start(void)
 {
-       // LordHavoc: allow any alignment
-       CHECKGLERROR
-       qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
-       qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               // LordHavoc: allow any alignment
+               CHECKGLERROR
+               qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
+               qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               break;
+       }
 
        texturemempool = Mem_AllocPool("texture management", 0, NULL);
        Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
@@ -563,6 +756,107 @@ static void r_textures_newmap(void)
 {
 }
 
+static void r_textures_devicelost(void)
+{
+       int i, endindex;
+       gltexture_t *glt;
+       endindex = Mem_ExpandableArray_IndexRange(&texturearray);
+       for (i = 0;i < endindex;i++)
+       {
+               glt = (gltexture_t *) Mem_ExpandableArray_RecordAtIndex(&texturearray, i);
+               if (!glt || !(glt->flags & TEXF_RENDERTARGET))
+                       continue;
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       if (glt->d3disdepthsurface)
+                               IDirect3DSurface9_Release((IDirect3DSurface9 *)glt->d3dtexture);
+                       else if (glt->tiledepth > 1)
+                               IDirect3DVolumeTexture9_Release((IDirect3DVolumeTexture9 *)glt->d3dtexture);
+                       else if (glt->sides == 6)
+                               IDirect3DCubeTexture9_Release((IDirect3DCubeTexture9 *)glt->d3dtexture);
+                       else
+                               IDirect3DTexture9_Release((IDirect3DTexture9 *)glt->d3dtexture);
+                       glt->d3dtexture = NULL;
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_SOFT:
+                       break;
+               }
+       }
+}
+
+static void r_textures_devicerestored(void)
+{
+       int i, endindex;
+       gltexture_t *glt;
+       endindex = Mem_ExpandableArray_IndexRange(&texturearray);
+       for (i = 0;i < endindex;i++)
+       {
+               glt = (gltexture_t *) Mem_ExpandableArray_RecordAtIndex(&texturearray, i);
+               if (!glt || !(glt->flags & TEXF_RENDERTARGET))
+                       continue;
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       {
+                               HRESULT d3dresult;
+                               if (glt->d3disdepthsurface)
+                               {
+                                       if (FAILED(d3dresult = IDirect3DDevice9_CreateDepthStencilSurface(vid_d3d9dev, glt->tilewidth, glt->tileheight, (D3DFORMAT)glt->d3dformat, D3DMULTISAMPLE_NONE, 0, false, (IDirect3DSurface9 **)&glt->d3dtexture, NULL)))
+                                               Sys_Error("IDirect3DDevice9_CreateDepthStencilSurface failed!");
+                               }
+                               else if (glt->tiledepth > 1)
+                               {
+                                       if (FAILED(d3dresult = IDirect3DDevice9_CreateVolumeTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DVolumeTexture9 **)&glt->d3dtexture, NULL)))
+                                               Sys_Error("IDirect3DDevice9_CreateVolumeTexture failed!");
+                               }
+                               else if (glt->sides == 6)
+                               {
+                                       if (FAILED(d3dresult = IDirect3DDevice9_CreateCubeTexture(vid_d3d9dev, glt->tilewidth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DCubeTexture9 **)&glt->d3dtexture, NULL)))
+                                               Sys_Error("IDirect3DDevice9_CreateCubeTexture failed!");
+                               }
+                               else
+                               {
+                                       if (FAILED(d3dresult = IDirect3DDevice9_CreateTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DTexture9 **)&glt->d3dtexture, NULL)))
+                                               Sys_Error("IDirect3DDevice9_CreateTexture failed!");
+                               }
+                       }
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_SOFT:
+                       break;
+               }
+       }
+}
+
+
 void R_Textures_Init (void)
 {
        Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc); an additional argument 'force' forces the texture mode even in cases where it may not be appropriate");
@@ -589,8 +883,11 @@ void R_Textures_Init (void)
        Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
        Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
        Cvar_RegisterVariable (&gl_nopartialtextureupdates);
+       Cvar_RegisterVariable (&r_texture_dds_load_alphamode);
+       Cvar_RegisterVariable (&r_texture_dds_load_logfailure);
+       Cvar_RegisterVariable (&r_texture_dds_swdecode);
 
-       R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
+       R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap, r_textures_devicelost, r_textures_devicerestored);
 }
 
 void R_Textures_Frame (void)
@@ -623,23 +920,37 @@ void R_Textures_Frame (void)
 
                Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
 
-               CHECKGLERROR
-               GL_ActiveTexture(0);
-               for (pool = gltexturepoolchain;pool;pool = pool->next)
+               switch(vid.renderpath)
                {
-                       for (glt = pool->gltchain;glt;glt = glt->chain)
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
+                       CHECKGLERROR
+                       GL_ActiveTexture(0);
+                       for (pool = gltexturepoolchain;pool;pool = pool->next)
                        {
-                               // only update already uploaded images
-                               if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
+                               for (glt = pool->gltchain;glt;glt = glt->chain)
                                {
-                                       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+                                       // only update already uploaded images
+                                       if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
+                                       {
+                                               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
 
-                                       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
-                                       qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
+                                               qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+                                               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
 
-                                       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+                                               qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+                                       }
                                }
                        }
+                       break;
+               case RENDERPATH_D3D9:
+               case RENDERPATH_D3D10:
+               case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -746,88 +1057,152 @@ static void GL_SetupTextureParameters(int flags, textype_t textype, int texturet
        CHECKGLERROR
 }
 
-static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
+static void R_UploadPartialTexture(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
 {
-       int i, mip, width, height, depth;
-       GLint oldbindtexnum;
-       const unsigned char *prevbuffer;
-       prevbuffer = data;
+       if (data == NULL)
+               Sys_Error("R_UploadPartialTexture \"%s\": partial update with NULL pixels", glt->identifier);
 
-       CHECKGLERROR
+       if (glt->texturetype != GLTEXTURETYPE_2D)
+               Sys_Error("R_UploadPartialTexture \"%s\": partial update of type other than 2D", glt->identifier);
 
-       // we need to restore the texture binding after finishing the upload
-       GL_ActiveTexture(0);
-       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
-       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+       if (glt->textype->textype == TEXTYPE_PALETTE)
+               Sys_Error("R_UploadPartialTexture \"%s\": partial update of paletted texture", glt->identifier);
 
-       // these are rounded up versions of the size to do better resampling
-       if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
-       {
-               width = glt->inputwidth;
-               height = glt->inputheight;
-               depth = glt->inputdepth;
-       }
-       else
+       if (glt->flags & (TEXF_MIPMAP | TEXF_PICMIP))
+               Sys_Error("R_UploadPartialTexture \"%s\": partial update not supported with MIPMAP or PICMIP flags", glt->identifier);
+
+       if (glt->inputwidth != glt->tilewidth || glt->inputheight != glt->tileheight || glt->tiledepth != 1)
+               Sys_Error("R_UploadPartialTexture \"%s\": partial update not supported with stretched or special textures", glt->identifier);
+
+       // update a portion of the image
+
+       switch(vid.renderpath)
        {
-               for (width  = 1;width  < glt->inputwidth ;width  <<= 1);
-               for (height = 1;height < glt->inputheight;height <<= 1);
-               for (depth  = 1;depth  < glt->inputdepth ;depth  <<= 1);
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               {
+                       int oldbindtexnum;
+                       CHECKGLERROR
+                       // we need to restore the texture binding after finishing the upload
+                       GL_ActiveTexture(0);
+                       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+                       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+                       qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, data);CHECKGLERROR
+                       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+               }
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               {
+                       RECT d3drect;
+                       D3DLOCKED_RECT d3dlockedrect;
+                       int y;
+                       memset(&d3drect, 0, sizeof(d3drect));
+                       d3drect.left = fragx;
+                       d3drect.top = fragy;
+                       d3drect.right = fragx+fragwidth;
+                       d3drect.bottom = fragy+fragheight;
+                       if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, 0, &d3dlockedrect, &d3drect, 0) == D3D_OK && d3dlockedrect.pBits)
+                       {
+                               for (y = 0;y < fragheight;y++)
+                                       memcpy((unsigned char *)d3dlockedrect.pBits + d3dlockedrect.Pitch * y, data + fragwidth*glt->bytesperpixel * y, fragwidth*glt->bytesperpixel);
+                               IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, 0);
+                       }
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_Texture_UpdatePartial(glt->texnum, 0, data, fragx, fragy, fragwidth, fragheight);
+               break;
        }
+}
+
+static void R_UploadFullTexture(gltexture_t *glt, const unsigned char *data)
+{
+       int i, mip = 0, width, height, depth;
+       GLint oldbindtexnum = 0;
+       const unsigned char *prevbuffer;
+       prevbuffer = data;
 
+       // error out if a stretch is needed on special texture types
+       if (glt->texturetype != GLTEXTURETYPE_2D && (glt->tilewidth != glt->inputwidth || glt->tileheight != glt->inputheight || glt->tiledepth != glt->inputdepth))
+               Sys_Error("R_UploadFullTexture \"%s\": stretch uploads allowed only on 2D textures\n", glt->identifier);
+
+       // when picmip or maxsize is applied, we scale up to a power of 2 multiple
+       // of the target size and then use the mipmap reduction function to get
+       // high quality supersampled results
+       for (width  = glt->tilewidth;width  < glt->inputwidth ;width  <<= 1);
+       for (height = glt->tileheight;height < glt->inputheight;height <<= 1);
+       for (depth  = glt->tiledepth;depth  < glt->inputdepth ;depth  <<= 1);
        R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
-       R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
 
        if (prevbuffer == NULL)
        {
-               memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
+               width = glt->tilewidth;
+               height = glt->tileheight;
+               depth = glt->tiledepth;
+               memset(resizebuffer, 0, width * height * depth * glt->sides * glt->bytesperpixel);
                prevbuffer = resizebuffer;
        }
        else if (glt->textype->textype == TEXTYPE_PALETTE)
        {
                // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
-               Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
+               Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, glt->inputwidth * glt->inputheight * glt->inputdepth * glt->sides, glt->palette);
                prevbuffer = colorconvertbuffer;
        }
 
-       // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
-
-       if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
+       if (glt->flags & TEXF_RGBMULTIPLYBYALPHA)
        {
-               // update a portion of the image
-               switch(glt->texturetype)
+               // multiply RGB channels by A channel before uploading
+               int alpha;
+               for (i = 0;i < width*height*depth*4;i += 4)
                {
-               case GLTEXTURETYPE_2D:
-                       qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
-                       break;
-               case GLTEXTURETYPE_3D:
-                       qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
-                       break;
-               default:
-                       Host_Error("R_Upload: partial update of type other than 2D");
-                       break;
+                       alpha = prevbuffer[i+3];
+                       colorconvertbuffer[i] = (prevbuffer[i] * alpha) >> 8;
+                       colorconvertbuffer[i+1] = (prevbuffer[i+1] * alpha) >> 8;
+                       colorconvertbuffer[i+2] = (prevbuffer[i+2] * alpha) >> 8;
+                       colorconvertbuffer[i+3] = alpha;
                }
+               prevbuffer = colorconvertbuffer;
        }
-       else
+
+       // scale up to a power of 2 size (if appropriate)
+       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+       {
+               Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+               prevbuffer = resizebuffer;
+       }
+       // apply mipmap reduction algorithm to get down to picmip/max_size
+       while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
        {
-               if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
-                       Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
+               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+               prevbuffer = resizebuffer;
+       }
+
+       // do the appropriate upload type...
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               CHECKGLERROR
+
+               // we need to restore the texture binding after finishing the upload
+               GL_ActiveTexture(0);
+               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+               qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
 
-               // cubemaps contain multiple images and thus get processed a bit differently
-               if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
-               {
-                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
-                       {
-                               Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
-                               prevbuffer = resizebuffer;
-                       }
-                       // picmip/max_size
-                       while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
-                       {
-                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
-                               prevbuffer = resizebuffer;
-                       }
-               }
-               mip = 0;
                if (qglGetCompressedTexImageARB)
                {
                        if (gl_texturecompression.integer >= 2)
@@ -894,13 +1269,212 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
                                }
                        }
                        break;
-               case GLTEXTURETYPE_RECTANGLE:
-                       qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
-                       break;
                }
                GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+               qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               if (!(glt->flags & TEXF_RENDERTARGET))
+               {
+                       D3DLOCKED_RECT d3dlockedrect;
+                       D3DLOCKED_BOX d3dlockedbox;
+                       switch(glt->texturetype)
+                       {
+                       case GLTEXTURETYPE_2D:
+                               if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                               {
+                                       if (prevbuffer)
+                                               memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                       else
+                                               memset(d3dlockedrect.pBits, 255, width*height*glt->bytesperpixel);
+                                       IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, mip);
+                               }
+                               mip++;
+                               if ((glt->flags & TEXF_MIPMAP) && prevbuffer)
+                               {
+                                       while (width > 1 || height > 1 || depth > 1)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                               prevbuffer = resizebuffer;
+                                               if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                                               {
+                                                       memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                                       IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, mip);
+                                               }
+                                               mip++;
+                                       }
+                               }
+                               break;
+                       case GLTEXTURETYPE_3D:
+                               if (IDirect3DVolumeTexture9_LockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip, &d3dlockedbox, NULL, 0) == D3D_OK && d3dlockedbox.pBits)
+                               {
+                                       // we are not honoring the RowPitch or SlicePitch, hopefully this works with all sizes
+                                       memcpy(d3dlockedbox.pBits, prevbuffer, width*height*depth*glt->bytesperpixel);
+                                       IDirect3DVolumeTexture9_UnlockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip);
+                               }
+                               mip++;
+                               if (glt->flags & TEXF_MIPMAP)
+                               {
+                                       while (width > 1 || height > 1 || depth > 1)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                               prevbuffer = resizebuffer;
+                                               if (IDirect3DVolumeTexture9_LockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip, &d3dlockedbox, NULL, 0) == D3D_OK && d3dlockedbox.pBits)
+                                               {
+                                                       // we are not honoring the RowPitch or SlicePitch, hopefully this works with all sizes
+                                                       memcpy(d3dlockedbox.pBits, prevbuffer, width*height*depth*glt->bytesperpixel);
+                                                       IDirect3DVolumeTexture9_UnlockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip);
+                                               }
+                                               mip++;
+                                       }
+                               }
+                               break;
+                       case GLTEXTURETYPE_CUBEMAP:
+                               // convert and upload each side in turn,
+                               // from a continuous block of input texels
+                               texturebuffer = (unsigned char *)prevbuffer;
+                               for (i = 0;i < 6;i++)
+                               {
+                                       prevbuffer = texturebuffer;
+                                       texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
+                                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                                       {
+                                               Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       // picmip/max_size
+                                       while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       mip = 0;
+                                       if (IDirect3DCubeTexture9_LockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                                       {
+                                               memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                               IDirect3DCubeTexture9_UnlockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip);
+                                       }
+                                       mip++;
+                                       if (glt->flags & TEXF_MIPMAP)
+                                       {
+                                               while (width > 1 || height > 1 || depth > 1)
+                                               {
+                                                       Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                                       prevbuffer = resizebuffer;
+                                                       if (IDirect3DCubeTexture9_LockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                                                       {
+                                                               memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                                               IDirect3DCubeTexture9_UnlockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip);
+                                                       }
+                                                       mip++;
+                                               }
+                                       }
+                               }
+                               break;
+                       }
+               }
+               glt->d3daddressw = 0;
+               if (glt->flags & TEXF_CLAMP)
+               {
+                       glt->d3daddressu = D3DTADDRESS_CLAMP;
+                       glt->d3daddressv = D3DTADDRESS_CLAMP;
+                       if (glt->tiledepth > 1)
+                               glt->d3daddressw = D3DTADDRESS_CLAMP;
+               }
+               else
+               {
+                       glt->d3daddressu = D3DTADDRESS_WRAP;
+                       glt->d3daddressv = D3DTADDRESS_WRAP;
+                       if (glt->tiledepth > 1)
+                               glt->d3daddressw = D3DTADDRESS_WRAP;
+               }
+               glt->d3dmipmaplodbias = 0;
+               glt->d3dmaxmiplevel = 0;
+               glt->d3dmaxmiplevelfilter = d3d_filter_nomip ? 0 : glt->d3dmaxmiplevel;
+               if (glt->flags & TEXF_FORCELINEAR)
+               {
+                       glt->d3dminfilter = D3DTEXF_LINEAR;
+                       glt->d3dmagfilter = D3DTEXF_LINEAR;
+                       glt->d3dmipfilter = D3DTEXF_POINT;
+               }
+               else if (glt->flags & TEXF_FORCENEAREST)
+               {
+                       glt->d3dminfilter = D3DTEXF_POINT;
+                       glt->d3dmagfilter = D3DTEXF_POINT;
+                       glt->d3dmipfilter = D3DTEXF_POINT;
+               }
+               else if (glt->flags & TEXF_MIPMAP)
+               {
+                       glt->d3dminfilter = d3d_filter_mipmin;
+                       glt->d3dmagfilter = d3d_filter_mipmag;
+                       glt->d3dmipfilter = d3d_filter_mipmix;
+               }
+               else
+               {
+                       glt->d3dminfilter = d3d_filter_flatmin;
+                       glt->d3dmagfilter = d3d_filter_flatmag;
+                       glt->d3dmipfilter = d3d_filter_flatmix;
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               switch(glt->texturetype)
+               {
+               case GLTEXTURETYPE_2D:
+                       DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               case GLTEXTURETYPE_3D:
+                       DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               case GLTEXTURETYPE_CUBEMAP:
+                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                       {
+                               unsigned char *combinedbuffer = (unsigned char *)Mem_Alloc(tempmempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
+                               // convert and upload each side in turn,
+                               // from a continuous block of input texels
+                               // copy the results into combinedbuffer
+                               texturebuffer = (unsigned char *)prevbuffer;
+                               for (i = 0;i < 6;i++)
+                               {
+                                       prevbuffer = texturebuffer;
+                                       texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
+                                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                                       {
+                                               Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       // picmip/max_size
+                                       while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       memcpy(combinedbuffer + i*glt->tilewidth*glt->tileheight*glt->tiledepth*glt->bytesperpixel, prevbuffer, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->bytesperpixel);
+                               }
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, combinedbuffer);
+                               Mem_Free(combinedbuffer);
+                       }
+                       else
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               }
+               if (glt->flags & TEXF_FORCELINEAR)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_LINEAR);
+               else if (glt->flags & TEXF_FORCENEAREST)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_NEAREST);
+               else if (glt->flags & TEXF_MIPMAP)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_mipmap);
+               else
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_nomipmap);
+               break;
        }
-       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
 }
 
 static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int miplevel, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
@@ -909,15 +1483,11 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        gltexture_t *glt;
        gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
        textypeinfo_t *texinfo, *texinfo2;
+       unsigned char *temppixels = NULL;
 
        if (cls.state == ca_dedicated)
                return NULL;
 
-       if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
-       {
-               Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
-               return NULL;
-       }
        if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
        {
                Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
@@ -937,6 +1507,17 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                return NULL;
        }
 
+       if (textype == TEXTYPE_RGBA)
+       {
+               // swap bytes
+               static int rgbaswapindices[4] = {2, 1, 0, 3};
+               textype = TEXTYPE_BGRA;
+               texinfo = R_GetTexTypeInfo(textype, flags);
+               temppixels = (unsigned char *)Mem_Alloc(tempmempool, width * height * depth * sides * 4);
+               Image_CopyMux(temppixels, data, width, height*depth*sides, false, false, false, 4, 4, rgbaswapindices);
+               data = temppixels;
+       }
+
        // clear the alpha flag if the texture has no transparent pixels
        switch(textype)
        {
@@ -991,7 +1572,7 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                flags |= TEXF_ALPHA;
                break;
        default:
-               Host_Error("R_LoadTexture: unknown texture type");
+               Sys_Error("R_LoadTexture: unknown texture type");
        }
 
        texinfo2 = R_GetTexTypeInfo(textype, flags);
@@ -1027,18 +1608,110 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        glt->updatecallback = NULL;
        glt->updatacallback_data = NULL;
 
-       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
+       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth, &glt->miplevels);
 
        // upload the texture
        // data may be NULL (blank texture for dynamic rendering)
-       CHECKGLERROR
-       qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
-       R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               CHECKGLERROR
+               qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               {
+                       D3DFORMAT d3dformat;
+                       D3DPOOL d3dpool;
+                       DWORD d3dusage;
+                       HRESULT d3dresult;
+                       d3dusage = 0;
+                       d3dpool = D3DPOOL_MANAGED;
+                       if (flags & TEXF_RENDERTARGET)
+                       {
+                               d3dusage |= D3DUSAGE_RENDERTARGET;
+                               d3dpool = D3DPOOL_DEFAULT;
+                       }
+                       switch(textype)
+                       {
+                       case TEXTYPE_PALETTE: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_RGBA: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8B8G8R8 : D3DFMT_X8B8G8R8;break;
+                       case TEXTYPE_BGRA: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_COLORBUFFER: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_SHADOWMAP: d3dformat = D3DFMT_D16;d3dusage = D3DUSAGE_DEPTHSTENCIL;break; // note: can not use D3DUSAGE_RENDERTARGET here
+                       case TEXTYPE_ALPHA: d3dformat = D3DFMT_A8;break;
+                       default: d3dformat = D3DFMT_A8R8G8B8;Sys_Error("R_LoadTexture: unsupported texture type %i when picking D3DFMT", (int)textype);break;
+                       }
+                       glt->d3dformat = d3dformat;
+                       glt->d3dusage = d3dusage;
+                       glt->d3dpool = d3dpool;
+                       glt->d3disdepthsurface = textype == TEXTYPE_SHADOWMAP;
+                       if (glt->d3disdepthsurface)
+                       {
+                               if (FAILED(d3dresult = IDirect3DDevice9_CreateDepthStencilSurface(vid_d3d9dev, glt->tilewidth, glt->tileheight, (D3DFORMAT)glt->d3dformat, D3DMULTISAMPLE_NONE, 0, false, (IDirect3DSurface9 **)&glt->d3dtexture, NULL)))
+                                       Sys_Error("IDirect3DDevice9_CreateDepthStencilSurface failed!");
+                       }
+                       else if (glt->tiledepth > 1)
+                       {
+                               if (FAILED(d3dresult = IDirect3DDevice9_CreateVolumeTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DVolumeTexture9 **)&glt->d3dtexture, NULL)))
+                                       Sys_Error("IDirect3DDevice9_CreateVolumeTexture failed!");
+                       }
+                       else if (glt->sides == 6)
+                       {
+                               if (FAILED(d3dresult = IDirect3DDevice9_CreateCubeTexture(vid_d3d9dev, glt->tilewidth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DCubeTexture9 **)&glt->d3dtexture, NULL)))
+                                       Sys_Error("IDirect3DDevice9_CreateCubeTexture failed!");
+                       }
+                       else
+                       {
+                               if (FAILED(d3dresult = IDirect3DDevice9_CreateTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DTexture9 **)&glt->d3dtexture, NULL)))
+                                       Sys_Error("IDirect3DDevice9_CreateTexture failed!");
+                       }
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               {
+                       int tflags = 0;
+                       switch(textype)
+                       {
+                       case TEXTYPE_PALETTE: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_RGBA: tflags = DPSOFTRAST_TEXTURE_FORMAT_RGBA8;break;
+                       case TEXTYPE_BGRA: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_COLORBUFFER: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_SHADOWMAP: tflags = DPSOFTRAST_TEXTURE_FORMAT_DEPTH;break;
+                       case TEXTYPE_ALPHA: tflags = DPSOFTRAST_TEXTURE_FORMAT_ALPHA8;break;
+                       default: Sys_Error("R_LoadTexture: unsupported texture type %i when picking DPSOFTRAST_TEXTURE_FLAGS", (int)textype);
+                       }
+                       if (glt->miplevels > 1) tflags |= DPSOFTRAST_TEXTURE_FLAG_MIPMAP;
+                       if (flags & TEXF_ALPHA) tflags |= DPSOFTRAST_TEXTURE_FLAG_USEALPHA;
+                       if (glt->sides == 6) tflags |= DPSOFTRAST_TEXTURE_FLAG_CUBEMAP;
+                       if (glt->flags & TEXF_CLAMP) tflags |= DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE;
+                       glt->texnum = DPSOFTRAST_Texture_New(tflags, glt->tilewidth, glt->tileheight, glt->tiledepth);
+               }
+               break;
+       }
+
+       R_UploadFullTexture(glt, data);
        if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
-               glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
+               glt->bufferpixels = (unsigned char *)Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
+
+       // free any temporary processing buffer we allocated...
+       if (temppixels)
+               Mem_Free(temppixels);
 
        // texture converting and uploading can take a while, so make sure we're sending keepalives
-       CL_KeepaliveMessage(false);
+       // FIXME: this causes rendering during R_Shadow_DrawLights
+//     CL_KeepaliveMessage(false);
 
        return (rtexture_t *)glt;
 }
@@ -1058,14 +1731,9 @@ rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *ident
        return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, miplevel, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
 }
 
-rtexture_t *R_LoadTextureRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
-{
-       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
-}
-
 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
 {
-       int flags = TEXF_CLAMP;
+       int flags = TEXF_RENDERTARGET | TEXF_CLAMP;
        if (filter)
                flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
        else
@@ -1075,22 +1743,12 @@ static int R_ShadowMapTextureFlags(int precision, qboolean filter)
        return flags;
 }
 
-rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
-{
-       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
-}
-
 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
 {
        return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
 }
 
-rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
-{
-    return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
-}
-
-int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
+int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed, qboolean hasalpha)
 {
        gltexture_t *glt = (gltexture_t *)rt;
        unsigned char *dds;
@@ -1126,6 +1784,15 @@ int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipunco
        case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
        case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
        }
+       // if premultiplied alpha, say so in the DDS file
+       if(glt->flags & TEXF_RGBMULTIPLYBYALPHA)
+       {
+               switch(internalformat)
+               {
+                       case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT2";break;
+                       case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT4";break;
+               }
+       }
        if (!bytesperblock && skipuncompressed)
                return -3; // skipped
        memset(mipinfo, 0, sizeof(mipinfo));
@@ -1152,7 +1819,7 @@ int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipunco
                mipinfo[mip][3] = ddssize;
                ddssize += mipinfo[mip][2];
        }
-       dds = Mem_Alloc(tempmempool, ddssize);
+       dds = (unsigned char *)Mem_Alloc(tempmempool, ddssize);
        if (!dds)
                return -4;
        dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
@@ -1165,13 +1832,15 @@ int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipunco
        else
        {
                dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
-               dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
+               dds_format_flags = 0x40; // DDPF_RGB
        }
        if (mipmaps)
        {
                dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
                dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
        }
+       if(hasalpha)
+               dds_format_flags |= 0x1; // DDPF_ALPHAPIXELS
        memcpy(dds, "DDS ", 4);
        StoreLittleLong(dds+4, ddssize);
        StoreLittleLong(dds+8, dds_flags);
@@ -1208,7 +1877,7 @@ int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipunco
        return ret ? ddssize : -5;
 }
 
-rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
+rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor, int miplevel) // DDS textures are opaque, so miplevel isn't a pointer but just seen as a hint
 {
        int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
        //int dds_flags;
@@ -1218,13 +1887,14 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        gltexture_t *glt;
        gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
        textypeinfo_t *texinfo;
-       int mip, mipwidth, mipheight, mipsize;
+       int mip, mipwidth, mipheight, mipsize, mipsize_total;
        unsigned int c;
-       GLint oldbindtexnum;
-       const unsigned char *mippixels, *ddspixels;
+       GLint oldbindtexnum = 0;
+       const unsigned char *mippixels, *ddspixels, *mippixels_start;
        unsigned char *dds;
        fs_offset_t ddsfilesize;
        unsigned int ddssize;
+       qboolean force_swdecode = (r_texture_dds_swdecode.integer > 1);
 
        if (cls.state == ca_dedicated)
                return NULL;
@@ -1234,7 +1904,8 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
 
        if (!dds)
        {
-               Log_Printf("ddstexturefailures.log", "%s\n", filename);
+               if(r_texture_dds_load_logfailure.integer)
+                       Log_Printf("ddstexturefailures.log", "%s\n", filename);
                return NULL; // not found
        }
 
@@ -1252,7 +1923,11 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        dds_height = BuffLittleLong(dds+12);
        ddspixels = dds + 128;
 
-       flags &= ~TEXF_ALPHA;
+       if(r_texture_dds_load_alphamode.integer == 0)
+               if(!(dds_format_flags & 0x1)) // DDPF_ALPHAPIXELS
+                       flags &= ~TEXF_ALPHA;
+
+       //flags &= ~TEXF_ALPHA; // disabled, as we DISABLE TEXF_ALPHA in the alpha detection, not enable it!
        if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
        {
                // very sloppy BGRA 32bit identification
@@ -1266,12 +1941,15 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
                        return NULL;
                }
-               // check alpha
-               for (i = 3;i < size;i += 4)
-                       if (ddspixels[i] < 255)
-                               break;
-               if (i >= size)
-                       flags &= ~TEXF_ALPHA;
+               if((r_texture_dds_load_alphamode.integer == 1) && (flags & TEXF_ALPHA))
+               {
+                       // check alpha
+                       for (i = 3;i < size;i += 4)
+                               if (ddspixels[i] < 255)
+                                       break;
+                       if (i >= size)
+                               flags &= ~TEXF_ALPHA;
+               }
        }
        else if (!memcmp(dds+84, "DXT1", 4))
        {
@@ -1289,16 +1967,47 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
                        return NULL;
                }
-               for (i = 0;i < size;i += bytesperblock)
-                       if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
-                               break;
-               if (i < size)
-                       textype = TEXTYPE_DXT1A;
-               else
-                       flags &= ~TEXF_ALPHA;
+               if(r_texture_dds_load_alphamode.integer && (flags & TEXF_ALPHA))
+               {
+                       if(r_texture_dds_load_alphamode.integer == 1)
+                       {
+                               // check alpha
+                               for (i = 0;i < size;i += bytesperblock)
+                                       if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
+                                       {
+                                               // NOTE: this assumes sizeof(unsigned int) == 4
+                                               unsigned int data = * (unsigned int *) &(ddspixels[i+4]);
+                                               // check if data, in base 4, contains a digit 3 (DXT1: transparent pixel)
+                                               if(data & (data<<1) & 0xAAAAAAAA)//rgh
+                                                       break;
+                                       }
+                               if (i < size)
+                                       textype = TEXTYPE_DXT1A;
+                               else
+                                       flags &= ~TEXF_ALPHA;
+                       }
+                       else
+                       {
+                               flags &= ~TEXF_ALPHA;
+                       }
+               }
        }
-       else if (!memcmp(dds+84, "DXT3", 4))
+       else if (!memcmp(dds+84, "DXT3", 4) || !memcmp(dds+84, "DXT2", 4))
        {
+               if(!memcmp(dds+84, "DXT2", 4))
+               {
+                       if(!(flags & TEXF_RGBMULTIPLYBYALPHA))
+                       {
+                               Con_Printf("^1%s: expecting DXT3 image without premultiplied alpha, got DXT2 image with premultiplied alpha\n", filename);
+                       }
+               }
+               else
+               {
+                       if(flags & TEXF_RGBMULTIPLYBYALPHA)
+                       {
+                               Con_Printf("^1%s: expecting DXT2 image without premultiplied alpha, got DXT3 image without premultiplied alpha\n", filename);
+                       }
+               }
                textype = TEXTYPE_DXT3;
                bytesperblock = 16;
                bytesperpixel = 0;
@@ -1309,9 +2018,24 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
                        return NULL;
                }
+               // we currently always assume alpha
        }
-       else if (!memcmp(dds+84, "DXT5", 4))
+       else if (!memcmp(dds+84, "DXT5", 4) || !memcmp(dds+84, "DXT4", 4))
        {
+               if(!memcmp(dds+84, "DXT4", 4))
+               {
+                       if(!(flags & TEXF_RGBMULTIPLYBYALPHA))
+                       {
+                               Con_Printf("^1%s: expecting DXT5 image without premultiplied alpha, got DXT4 image with premultiplied alpha\n", filename);
+                       }
+               }
+               else
+               {
+                       if(flags & TEXF_RGBMULTIPLYBYALPHA)
+                       {
+                               Con_Printf("^1%s: expecting DXT4 image without premultiplied alpha, got DXT5 image without premultiplied alpha\n", filename);
+                       }
+               }
                textype = TEXTYPE_DXT5;
                bytesperblock = 16;
                bytesperpixel = 0;
@@ -1322,6 +2046,7 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
                        return NULL;
                }
+               // we currently always assume alpha
        }
        else
        {
@@ -1330,10 +2055,106 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                return NULL;
        }
 
+       force_swdecode = false;
+       if(bytesperblock)
+       {
+               if(vid.support.arb_texture_compression && vid.support.ext_texture_compression_s3tc)
+               {
+                       if(r_texture_dds_swdecode.integer > 1)
+                               force_swdecode = true;
+               }
+               else
+               {
+                       if(r_texture_dds_swdecode.integer < 1)
+                       {
+                               // unsupported
+                               Mem_Free(dds);
+                               return NULL;
+                       }
+                       force_swdecode = true;
+               }
+       }
+
        // return whether this texture is transparent
        if (hasalphaflag)
                *hasalphaflag = (flags & TEXF_ALPHA) != 0;
 
+       // if we SW decode, choose 2 sizes bigger
+       if(force_swdecode)
+       {
+               // this is quarter res, so do not scale down more than we have to
+               miplevel -= 2;
+
+               if(miplevel < 0)
+                       Con_DPrintf("WARNING: fake software decoding of compressed texture %s degraded quality\n", filename);
+       }
+
+       // this is where we apply gl_picmip
+       mippixels_start = ddspixels;
+       mipwidth = dds_width;
+       mipheight = dds_height;
+       while(miplevel >= 1 && dds_miplevels >= 1)
+       {
+               if (mipwidth <= 1 && mipheight <= 1)
+                       break;
+               mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+               mippixels_start += mipsize; // just skip
+               --dds_miplevels;
+               --miplevel;
+               if (mipwidth > 1)
+                       mipwidth >>= 1;
+               if (mipheight > 1)
+                       mipheight >>= 1;
+       }
+       mipsize_total = ddssize - 128 - (mippixels_start - ddspixels);
+       mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+
+       // from here on, we do not need the ddspixels and ddssize any more (apart from the statistics entry in glt)
+
+       // fake decode S3TC if needed
+       if(force_swdecode)
+       {
+               int mipsize_new = mipsize_total / bytesperblock * 4;
+               unsigned char *mipnewpixels = (unsigned char *) Mem_Alloc(tempmempool, mipsize_new);
+               unsigned char *p = mipnewpixels;
+               for (i = bytesperblock == 16 ? 8 : 0;i < (int)mipsize_total;i += bytesperblock, p += 4)
+               {
+                       c = mippixels_start[i] + 256*mippixels_start[i+1] + 65536*mippixels_start[i+2] + 16777216*mippixels_start[i+3];
+                       p[2] = (((c >> 11) & 0x1F) + ((c >> 27) & 0x1F)) * (0.5f / 31.0f * 255.0f);
+                       p[1] = (((c >>  5) & 0x3F) + ((c >> 21) & 0x3F)) * (0.5f / 63.0f * 255.0f);
+                       p[0] = (((c      ) & 0x1F) + ((c >> 16) & 0x1F)) * (0.5f / 31.0f * 255.0f);
+                       if(textype == TEXTYPE_DXT5)
+                               p[3] = (0.5 * mippixels_start[i-8] + 0.5 * mippixels_start[i-7]);
+                       else if(textype == TEXTYPE_DXT3)
+                               p[3] = (
+                                         (mippixels_start[i-8] & 0x0F)
+                                       + (mippixels_start[i-8] >> 4)
+                                       + (mippixels_start[i-7] & 0x0F)
+                                       + (mippixels_start[i-7] >> 4)
+                                       + (mippixels_start[i-6] & 0x0F)
+                                       + (mippixels_start[i-6] >> 4)
+                                       + (mippixels_start[i-5] & 0x0F)
+                                       + (mippixels_start[i-5] >> 4)
+                                      ) * (0.125f / 15.0f * 255.0f);
+                       else
+                               p[3] = 255;
+               }
+
+               textype = TEXTYPE_BGRA;
+               bytesperblock = 0;
+               bytesperpixel = 4;
+
+               // as each block becomes a pixel, we must use pixel count for this
+               mipwidth = (mipwidth + 3) / 4;
+               mipheight = (mipheight + 3) / 4;
+               mipsize = bytesperpixel * mipwidth * mipheight;
+               mippixels_start = mipnewpixels;
+               mipsize_total = mipsize_new;
+       }
+
+       // start mip counting
+       mippixels = mippixels_start;
+
        // calculate average color if requested
        if (avgcolor)
        {
@@ -1341,27 +2162,42 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                Vector4Clear(avgcolor);
                if (bytesperblock)
                {
-                       for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
+                       for (i = bytesperblock == 16 ? 8 : 0;i < mipsize;i += bytesperblock)
                        {
-                               c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
+                               c = mippixels[i] + 256*mippixels[i+1] + 65536*mippixels[i+2] + 16777216*mippixels[i+3];
                                avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
                                avgcolor[1] += ((c >>  5) & 0x3F) + ((c >> 21) & 0x3F);
                                avgcolor[2] += ((c      ) & 0x1F) + ((c >> 16) & 0x1F);
+                               if(textype == TEXTYPE_DXT5)
+                                       avgcolor[3] += (0.5 * mippixels[i-8] + 0.5 * mippixels[i-7]);
+                               else if(textype == TEXTYPE_DXT3)
+                                       avgcolor[3] += (
+                                                 (mippixels_start[i-8] & 0x0F)
+                                               + (mippixels_start[i-8] >> 4)
+                                               + (mippixels_start[i-7] & 0x0F)
+                                               + (mippixels_start[i-7] >> 4)
+                                               + (mippixels_start[i-6] & 0x0F)
+                                               + (mippixels_start[i-6] >> 4)
+                                               + (mippixels_start[i-5] & 0x0F)
+                                               + (mippixels_start[i-5] >> 4)
+                                              ) * (0.125f / 15.0f * 255.0f);
+                               else
+                                       avgcolor[3] += 255;
                        }
                        f = (float)bytesperblock / size;
                        avgcolor[0] *= (0.5f / 31.0f) * f;
                        avgcolor[1] *= (0.5f / 63.0f) * f;
                        avgcolor[2] *= (0.5f / 31.0f) * f;
-                       avgcolor[3] = 1; // too hard to calculate
+                       avgcolor[3] *= f;
                }
                else
                {
-                       for (i = 0;i < size;i += 4)
+                       for (i = 0;i < mipsize;i += 4)
                        {
-                               avgcolor[0] += ddspixels[i+2];
-                               avgcolor[1] += ddspixels[i+1];
-                               avgcolor[2] += ddspixels[i];
-                               avgcolor[3] += ddspixels[i+3];
+                               avgcolor[0] += mippixels[i+2];
+                               avgcolor[1] += mippixels[i+1];
+                               avgcolor[2] += mippixels[i];
+                               avgcolor[3] += mippixels[i+3];
                        }
                        f = (1.0f / 255.0f) * bytesperpixel / size;
                        avgcolor[0] *= f;
@@ -1371,19 +2207,15 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                }
        }
 
-       if (dds_miplevels > 1)
+       // when not requesting mipmaps, do not load them
+       if(!(flags & TEXF_MIPMAP))
+               dds_miplevels = 0;
+
+       if (dds_miplevels >= 1)
                flags |= TEXF_MIPMAP;
        else
                flags &= ~TEXF_MIPMAP;
 
-       // if S3TC is not supported, there's very little we can do about it
-       if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
-       {
-               Mem_Free(dds);
-               Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
-               return NULL;
-       }
-
        texinfo = R_GetTexTypeInfo(textype, flags);
 
        glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
@@ -1391,8 +2223,8 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        glt->pool = pool;
        glt->chain = pool->gltchain;
        pool->gltchain = glt;
-       glt->inputwidth = dds_width;
-       glt->inputheight = dds_height;
+       glt->inputwidth = mipwidth;
+       glt->inputheight = mipheight;
        glt->inputdepth = 1;
        glt->flags = flags;
        glt->textype = texinfo;
@@ -1404,36 +2236,111 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        glt->bytesperpixel = texinfo->internalbytesperpixel;
        glt->sides = 1;
        glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
-       glt->tilewidth = dds_width;
-       glt->tileheight = dds_height;
+       glt->tilewidth = mipwidth;
+       glt->tileheight = mipheight;
        glt->tiledepth = 1;
+       glt->miplevels = dds_miplevels;
 
        // texture uploading can take a while, so make sure we're sending keepalives
        CL_KeepaliveMessage(false);
 
+       // create the texture object
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               CHECKGLERROR
+               GL_ActiveTexture(0);
+               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+               qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+               qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               {
+                       D3DFORMAT d3dformat;
+                       D3DPOOL d3dpool;
+                       DWORD d3dusage;
+                       switch(textype)
+                       {
+                       case TEXTYPE_BGRA: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_DXT1: case TEXTYPE_DXT1A: d3dformat = D3DFMT_DXT1;break;
+                       case TEXTYPE_DXT3: d3dformat = D3DFMT_DXT3;break;
+                       case TEXTYPE_DXT5: d3dformat = D3DFMT_DXT5;break;
+                       default: d3dformat = D3DFMT_A8R8G8B8;Host_Error("R_LoadTextureDDSFile: unsupported texture type %i when picking D3DFMT", (int)textype);break;
+                       }
+                       d3dusage = 0;
+                       d3dpool = D3DPOOL_MANAGED;
+                       IDirect3DDevice9_CreateTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->miplevels, d3dusage, d3dformat, d3dpool, (IDirect3DTexture9 **)&glt->d3dtexture, NULL);
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               glt->texnum = DPSOFTRAST_Texture_New(((glt->flags & TEXF_CLAMP) ? DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE : 0) | (dds_miplevels > 1 ? DPSOFTRAST_TEXTURE_FLAG_MIPMAP : 0), glt->tilewidth, glt->tileheight, glt->tiledepth);
+               break;
+       }
+
        // upload the texture
        // we need to restore the texture binding after finishing the upload
-       CHECKGLERROR
-       GL_ActiveTexture(0);
-       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
-       qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
-       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
-       mippixels = ddspixels;
-       mipwidth = dds_width;
-       mipheight = dds_height;
        mipcomplete = false;
-       for (mip = 0;mip < dds_miplevels+1;mip++)
+
+       for (mip = 0;mip <= dds_miplevels;mip++) // <= to include the not-counted "largest" miplevel
        {
                mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
-               if (mippixels + mipsize > dds + ddssize)
+               if (mippixels + mipsize > mippixels_start + mipsize_total)
                        break;
-               if (bytesperblock)
-               {
-                       qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
-               }
-               else
+               switch(vid.renderpath)
                {
-                       qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+               case RENDERPATH_GLES2:
+                       if (bytesperblock)
+                       {
+                               qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
+                       }
+                       else
+                       {
+                               qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
+                       }
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       {
+                               D3DLOCKED_RECT d3dlockedrect;
+                               if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                               {
+                                       memcpy(d3dlockedrect.pBits, mippixels, mipsize);
+                                       IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, mip);
+                               }
+                               break;
+                       }
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_SOFT:
+                       if (bytesperblock)
+                               Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       else
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, mippixels);
+                       // DPSOFTRAST calculates its own mipmaps
+                       mip = dds_miplevels;
+                       break;
                }
                mippixels += mipsize;
                if (mipwidth <= 1 && mipheight <= 1)
@@ -1446,15 +2353,78 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                if (mipheight > 1)
                        mipheight >>= 1;
        }
-       if (dds_miplevels > 1 && !mipcomplete)
+
+       // after upload we have to set some parameters...
+       switch(vid.renderpath)
        {
-               // need to set GL_TEXTURE_MAX_LEVEL
-               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GLES2:
+               if (dds_miplevels >= 1 && !mipcomplete)
+               {
+                       // need to set GL_TEXTURE_MAX_LEVEL
+                       qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
+               }
+               GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+               qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               glt->d3daddressw = 0;
+               if (glt->flags & TEXF_CLAMP)
+               {
+                       glt->d3daddressu = D3DTADDRESS_CLAMP;
+                       glt->d3daddressv = D3DTADDRESS_CLAMP;
+                       if (glt->tiledepth > 1)
+                               glt->d3daddressw = D3DTADDRESS_CLAMP;
+               }
+               else
+               {
+                       glt->d3daddressu = D3DTADDRESS_WRAP;
+                       glt->d3daddressv = D3DTADDRESS_WRAP;
+                       if (glt->tiledepth > 1)
+                               glt->d3daddressw = D3DTADDRESS_WRAP;
+               }
+               glt->d3dmipmaplodbias = 0;
+               glt->d3dmaxmiplevel = 0;
+               glt->d3dmaxmiplevelfilter = 0;
+               if (glt->flags & TEXF_MIPMAP)
+               {
+                       glt->d3dminfilter = d3d_filter_mipmin;
+                       glt->d3dmagfilter = d3d_filter_mipmag;
+                       glt->d3dmipfilter = d3d_filter_mipmix;
+               }
+               else
+               {
+                       glt->d3dminfilter = d3d_filter_flatmin;
+                       glt->d3dmagfilter = d3d_filter_flatmag;
+                       glt->d3dmipfilter = d3d_filter_flatmix;
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               if (glt->flags & TEXF_FORCELINEAR)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_LINEAR);
+               else if (glt->flags & TEXF_FORCENEAREST)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_NEAREST);
+               else if (glt->flags & TEXF_MIPMAP)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_mipmap);
+               else
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_nomipmap);
+               break;
        }
-       GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
-       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
 
        Mem_Free(dds);
+       if(force_swdecode)
+               Mem_Free((unsigned char *) mippixels_start);
        return (rtexture_t *)glt;
 }
 
@@ -1475,8 +2445,11 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in
                Host_Error("R_UpdateTexture: no data supplied");
        if (glt == NULL)
                Host_Error("R_UpdateTexture: no texture supplied");
-       if (!glt->texnum)
-               Host_Error("R_UpdateTexture: texture has not been uploaded yet");
+       if (!glt->texnum && !glt->d3dtexture)
+       {
+               Con_DPrintf("R_UpdateTexture: texture %p \"%s\" in pool %p has not been uploaded yet\n", (void *)glt, glt->identifier, (void *)glt->pool);
+               return;
+       }
        // update part of the texture
        if (glt->bufferpixels)
        {
@@ -1510,8 +2483,10 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in
                for (j = 0;j < height;j++, output += outputskip, input += inputskip)
                        memcpy(output, input, width*bpp);
        }
+       else if (x || y || width != glt->inputwidth || height != glt->inputheight)
+               R_UploadPartialTexture(glt, data, x, y, 0, width, height, 1);
        else
-               R_Upload(glt, data, x, y, 0, width, height, 1);
+               R_UploadFullTexture(glt, data);
 }
 
 int R_RealGetTexture(rtexture_t *rt)
@@ -1525,7 +2500,7 @@ int R_RealGetTexture(rtexture_t *rt)
                if (glt->buffermodified && glt->bufferpixels)
                {
                        glt->buffermodified = false;
-                       R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
+                       R_UploadFullTexture(glt, glt->bufferpixels);
                }
                glt->dirty = false;
                return glt->texnum;
@@ -1538,7 +2513,7 @@ void R_ClearTexture (rtexture_t *rt)
 {
        gltexture_t *glt = (gltexture_t *)rt;
 
-       R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
+       R_UploadFullTexture(glt, NULL);
 }
 
 int R_PicmipForFlags(int flags)
@@ -1564,5 +2539,5 @@ int R_PicmipForFlags(int flags)
                else
                        miplevel += gl_picmip_other.integer;
        }
-       return miplevel;
+       return max(0, miplevel);
 }