Deferred patch updating

This commit is contained in:
Lactozilla 2023-09-24 16:23:45 -03:00
parent 5db5e65b90
commit dca643676c
12 changed files with 406 additions and 342 deletions

View file

@ -262,37 +262,29 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
INT32 pwidth, INT32 pheight,
const patch_t *realpatch)
{
INT32 ncols;
fixed_t xfrac, xfracstep;
fixed_t yfracstep, scale_y;
const column_t *patchcol;
UINT8 *block = mipmap->data;
INT32 bpp;
INT32 blockmodulo;
if (pwidth <= 0 || pheight <= 0)
return;
ncols = pwidth;
// source advance
xfrac = 0;
xfracstep = FRACUNIT;
yfracstep = FRACUNIT;
scale_y = FRACUNIT;
fixed_t xfrac = 0;
fixed_t xfracstep = FRACUNIT;
fixed_t yfracstep = FRACUNIT;
fixed_t scale_y = FRACUNIT;
bpp = format2bpp(mipmap->format);
INT32 bpp = format2bpp(mipmap->format);
if (bpp < 1 || bpp > 4)
I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
// NOTE: should this actually be pblockwidth*bpp?
blockmodulo = pblockwidth*bpp;
INT32 blockmodulo = pblockwidth*bpp;
// Draw each column to the block cache
for (; ncols--; block += bpp, xfrac += xfracstep)
UINT8 *block = mipmap->data;
for (int x = 0; x < pwidth; x++, block += bpp, xfrac += xfracstep)
{
patchcol = &realpatch->columns[xfrac>>FRACBITS];
const column_t *patchcol = &realpatch->columns[xfrac>>FRACBITS];
HWR_DrawColumnInCache(patchcol, block, mipmap,
pblockheight, blockmodulo,
@ -542,7 +534,6 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm
}
Z_Free(grMipmap->data);
grMipmap->data = NULL;
if (makebitmap)
{
@ -576,8 +567,8 @@ void HWR_FreeTextureData(patch_t *patch)
if (vid.glstate == VID_GL_LIBRARY_LOADED)
HWD.pfnDeleteTexture(grPatch->mipmap);
if (grPatch->mipmap->data)
Z_Free(grPatch->mipmap->data);
Z_Free(grPatch->mipmap->data);
}
void HWR_FreeTexture(patch_t *patch)
@ -641,8 +632,6 @@ void HWR_FreeTextureColormaps(patch_t *patch)
Z_Free(next->data);
if (next->colormap)
Z_Free(next->colormap);
next->data = NULL;
next->colormap = NULL;
HWD.pfnDeleteTexture(next);
// Free the old colormap mipmap from memory.
@ -697,9 +686,7 @@ void HWR_InitMapTextures(void)
static void FreeMapTexture(GLMapTexture_t *tex)
{
HWD.pfnDeleteTexture(&tex->mipmap);
if (tex->mipmap.data)
Z_Free(tex->mipmap.data);
tex->mipmap.data = NULL;
Z_Free(tex->mipmap.data);
}
void HWR_FreeMapTextures(void)
@ -897,6 +884,88 @@ static void HWR_UpdatePatchMipmap(patch_t *patch, GLMipmap_t *grMipmap)
Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
}
static void HWR_UpdatePatchPixels(GLMipmap_t *mipmap, UINT8 *pixels, INT16 patchheight, bitarray_t *pixels_opaque, INT16 left, INT16 top, INT16 right, INT16 bottom)
{
INT32 bpp = format2bpp(mipmap->format);
if (bpp < 1 || bpp > 4)
I_Error("HWR_UpdatePatchPixels: no drawer defined for this bpp (%d)\n",bpp);
UINT8 *dest_pixels = (UINT8*)mipmap->data;
for (INT16 y = top; y < bottom; y++)
{
UINT8 *dest = &dest_pixels[(y * (mipmap->width * bpp)) + (left * bpp)];
for (INT16 x = left; x < right; x++)
{
UINT8 texel = 0x00;
UINT8 alpha = 0x00;
size_t position = (x * patchheight) + y;
if (in_bit_array(pixels_opaque, position))
{
texel = pixels[position];
alpha = 0xFF;
// Make pixel transparent if chroma keyed
if ((mipmap->flags & TF_CHROMAKEYED) && texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)
alpha = 0x00;
}
UINT16 texelu16;
RGBA_t colortemp;
switch (bpp)
{
case 2:
texelu16 = (UINT16)((alpha<<8) | texel);
memcpy(dest, &texelu16, sizeof(UINT16));
break;
case 3:
colortemp = V_GetColor(texel);
memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
break;
case 4:
colortemp = V_GetColor(texel);
colortemp.s.alpha = alpha;
memcpy(dest, &colortemp, sizeof(RGBA_t));
break;
// default is 1
default:
*dest = texel;
break;
}
dest += bpp;
}
}
}
static void HWR_UpdateMipmapRegion(patch_t *patch, GLMipmap_t *grMipmap, INT16 left, INT16 top, INT16 right, INT16 bottom)
{
GLPatch_t *grPatch = patch->hardware;
bitarray_t *pixels_opaque = Patch_GetOpaqueRegions(patch);
if (pixels_opaque == NULL || grMipmap->width == 0 || grMipmap->data == NULL)
HWR_MakePatch(patch, grPatch, grMipmap, true);
else
HWR_UpdatePatchPixels(grMipmap, patch->pixels, patch->height, pixels_opaque, left, top, right, bottom);
// If hardware does not have the texture, then call pfnSetTexture to upload it
// If it does have the texture, then call pfnUpdateTextureRegion to update it
if (!grMipmap->downloaded)
HWD.pfnSetTexture(grMipmap);
else
HWD.pfnUpdateTextureRegion(grMipmap, left, top, right - left, bottom - top);
HWR_SetCurrentTexture(grMipmap);
// The system-memory data can be purged now.
Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
}
// -----------------+
// HWR_GetPatch : Downloads a patch to the hardware cache and make it ready for use
// -----------------+
@ -907,11 +976,12 @@ void HWR_GetPatch(patch_t *patch)
HWR_LoadPatchMipmap(patch, ((GLPatch_t *)patch->hardware)->mipmap);
}
void HWR_UpdatePatch(patch_t *patch)
void HWR_UpdatePatchRegion(patch_t *patch, INT16 left, INT16 top, INT16 right, INT16 bottom)
{
if (!patch->hardware)
Patch_CreateGL(patch);
HWR_UpdatePatchMipmap(patch, ((GLPatch_t *)patch->hardware)->mipmap);
HWR_UpdateMipmapRegion(patch, ((GLPatch_t *)patch->hardware)->mipmap, left, top, right, bottom);
}
// -------------------+

View file

@ -42,6 +42,7 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags);
EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor);
EXPORT void HWRAPI(SetTexture) (GLMipmap_t *TexInfo);
EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *TexInfo);
EXPORT void HWRAPI(UpdateTextureRegion) (GLMipmap_t *TexInfo, INT32 x, INT32 y, INT32 width, INT32 height);
EXPORT void HWRAPI(DeleteTexture) (GLMipmap_t *TexInfo);
EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 *dst_data);
EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip);
@ -95,6 +96,7 @@ struct hwdriver_s
ClearBuffer pfnClearBuffer;
SetTexture pfnSetTexture;
UpdateTexture pfnUpdateTexture;
UpdateTextureRegion pfnUpdateTextureRegion;
DeleteTexture pfnDeleteTexture;
ReadRect pfnReadRect;
GClipRect pfnGClipRect;

View file

@ -117,7 +117,7 @@ patch_t *HWR_GetCachedGLPatchPwad(UINT16 wad, UINT16 lump);
patch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum);
void HWR_GetPatch(patch_t *patch);
void HWR_UpdatePatch(patch_t *patch);
void HWR_UpdatePatchRegion(patch_t *patch, INT16 left, INT16 top, INT16 right, INT16 bottom);
void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap);
void HWR_GetFadeMask(lumpnum_t fademasklumpnum);
patch_t *HWR_GetPic(lumpnum_t lumpnum);

View file

@ -1333,18 +1333,15 @@ void Flush(void)
while (TexCacheHead)
{
FTextureInfo *pTexInfo = TexCacheHead;
GLMipmap_t *texture = pTexInfo->texture;
TexCacheHead = pTexInfo->next;
if (pTexInfo->downloaded)
{
pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
pTexInfo->downloaded = 0;
}
GLMipmap_t *texture = pTexInfo->texture;
if (texture)
texture->downloaded = 0;
TexCacheHead = pTexInfo->next;
free(pTexInfo);
}
@ -1746,9 +1743,9 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags)
CurrentPolyFlags = PolyFlags;
}
static void AllocTextureBuffer(GLMipmap_t *pTexInfo)
static RGBA_t *AllocTextureBuffer(unsigned width, unsigned height)
{
size_t size = pTexInfo->width * pTexInfo->height;
size_t size = width * height;
if (size > textureBufferSize)
{
textureBuffer = realloc(textureBuffer, size * sizeof(RGBA_t));
@ -1756,116 +1753,143 @@ static void AllocTextureBuffer(GLMipmap_t *pTexInfo)
I_Error("AllocTextureBuffer: out of memory allocating %s bytes", sizeu1(size * sizeof(RGBA_t)));
textureBufferSize = size;
}
return textureBuffer;
}
// -----------------+
// UpdateTexture : Updates texture data.
// -----------------+
EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo)
EXPORT void HWRAPI(UpdateTextureRegion) (GLMipmap_t *pTexInfo, INT32 x, INT32 y, INT32 width, INT32 height)
{
// Upload a texture
GLuint num = pTexInfo->downloaded;
boolean update = true;
INT32 w = pTexInfo->width, h = pTexInfo->height;
INT32 i, j;
INT32 i, j, dy;
const GLubyte *pImgData = (const GLubyte *)pTexInfo->data;
const GLvoid *ptex = NULL;
RGBA_t *tex = NULL;
// Generate a new texture name.
if (!num)
// Generate a new texture ID if there is none
if (pTexInfo->downloaded == 0)
{
pglGenTextures(1, &num);
pTexInfo->downloaded = num;
GLuint id = 0;
pglGenTextures(1, &id);
pTexInfo->downloaded = id;
update = false;
}
//GL_DBG_Printf("UpdateTexture %d %x\n", (INT32)num, pImgData);
if ((pTexInfo->format == GL_TEXFMT_P_8) || (pTexInfo->format == GL_TEXFMT_AP_88))
if (pTexInfo->format == GL_TEXFMT_P_8 || pTexInfo->format == GL_TEXFMT_AP_88)
{
AllocTextureBuffer(pTexInfo);
ptex = tex = textureBuffer;
ptex = AllocTextureBuffer(width, height);
for (j = 0; j < h; j++)
for (j = y, dy = 0; j < y + height; j++, dy++)
{
for (i = 0; i < w; i++)
const GLubyte *src = &pImgData[pTexInfo->width*j+x];
RGBA_t *tex = &textureBuffer[width*dy];
for (i = x; i < x + width; i++)
{
if ((*pImgData == HWR_PATCHES_CHROMAKEY_COLORINDEX) &&
if ((*src == HWR_PATCHES_CHROMAKEY_COLORINDEX) &&
(pTexInfo->flags & TF_CHROMAKEYED))
{
tex[w*j+i].s.red = 0;
tex[w*j+i].s.green = 0;
tex[w*j+i].s.blue = 0;
tex[w*j+i].s.alpha = 0;
tex->s.red = 0;
tex->s.green = 0;
tex->s.blue = 0;
tex->s.alpha = 0;
pTexInfo->flags |= TF_TRANSPARENT; // there is a hole in it
}
else
{
tex[w*j+i].s.red = myPaletteData[*pImgData].s.red;
tex[w*j+i].s.green = myPaletteData[*pImgData].s.green;
tex[w*j+i].s.blue = myPaletteData[*pImgData].s.blue;
tex[w*j+i].s.alpha = myPaletteData[*pImgData].s.alpha;
tex->s.red = myPaletteData[*src].s.red;
tex->s.green = myPaletteData[*src].s.green;
tex->s.blue = myPaletteData[*src].s.blue;
tex->s.alpha = myPaletteData[*src].s.alpha;
}
pImgData++;
src++;
if (pTexInfo->format == GL_TEXFMT_AP_88)
{
if (!(pTexInfo->flags & TF_CHROMAKEYED))
tex[w*j+i].s.alpha = *pImgData;
pImgData++;
tex->s.alpha = *src;
src++;
}
tex++;
}
}
}
else if (pTexInfo->format == GL_TEXFMT_RGBA)
{
// Directly upload the texture data without any kind of conversion.
ptex = pImgData;
if (x == 0 && y == 0 && width == pTexInfo->width && height == pTexInfo->height)
{
// Directly upload the texture data without any kind of conversion.
ptex = pImgData;
}
else
{
RGBA_t *src_pixels = (RGBA_t *)pTexInfo->data;
ptex = AllocTextureBuffer(width, height);
for (j = y, dy = 0; j < y + height; j++, dy++)
{
RGBA_t *src = &src_pixels[pTexInfo->width*j+x];
RGBA_t *tex = &textureBuffer[width*dy];
for (i = x; i < x + width; i++)
{
*tex = *src;
tex++;
src++;
}
}
}
}
else if (pTexInfo->format == GL_TEXFMT_ALPHA_INTENSITY_88)
{
AllocTextureBuffer(pTexInfo);
ptex = tex = textureBuffer;
ptex = AllocTextureBuffer(width, height);
for (j = 0; j < h; j++)
for (j = y, dy = 0; j < y + height; j++, dy++)
{
for (i = 0; i < w; i++)
const GLubyte *src = &pImgData[pTexInfo->width*j+x];
RGBA_t *tex = &textureBuffer[width*dy];
for (i = x; i < x + width; i++)
{
tex[w*j+i].s.red = *pImgData;
tex[w*j+i].s.green = *pImgData;
tex[w*j+i].s.blue = *pImgData;
pImgData++;
tex[w*j+i].s.alpha = *pImgData;
pImgData++;
tex->s.red = *src;
tex->s.green = *src;
tex->s.blue = *src;
src++;
tex->s.alpha = *src;
src++;
tex++;
}
}
}
else if (pTexInfo->format == GL_TEXFMT_ALPHA_8) // Used for fade masks
{
AllocTextureBuffer(pTexInfo);
ptex = tex = textureBuffer;
ptex = AllocTextureBuffer(width, height);
for (j = 0; j < h; j++)
for (j = y, dy = 0; j < y + height; j++, dy++)
{
for (i = 0; i < w; i++)
const GLubyte *src = &pImgData[width*j+x];
RGBA_t *tex = &textureBuffer[width*dy];
for (i = x; i < x + width; i++)
{
tex[w*j+i].s.red = 255; // 255 because the fade mask is modulated with the screen texture, so alpha affects it while the colours don't
tex[w*j+i].s.green = 255;
tex[w*j+i].s.blue = 255;
tex[w*j+i].s.alpha = *pImgData;
pImgData++;
// 255 because the fade mask is modulated with the screen texture, so alpha affects it while the colours don't
tex->s.red = 255;
tex->s.green = 255;
tex->s.blue = 255;
tex->s.alpha = *src;
src++;
tex++;
}
}
}
else
GL_MSG_Warning("UpdateTexture: bad format %d\n", pTexInfo->format);
pglBindTexture(GL_TEXTURE_2D, num);
tex_downloaded = num;
pglBindTexture(GL_TEXTURE_2D, pTexInfo->downloaded);
tex_downloaded = pTexInfo->downloaded;
// disable texture filtering on any texture that has holes so there's no dumb borders or blending issues
if (pTexInfo->flags & TF_TRANSPARENT)
@ -1881,64 +1905,60 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo)
if (pTexInfo->format == GL_TEXFMT_ALPHA_INTENSITY_88)
{
//pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
if (MipMap)
{
pgluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pgluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, pTexInfo->width, pTexInfo->height, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
if (pTexInfo->flags & TF_TRANSPARENT)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mippmaps on transparent stuff
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mipmaps on transparent stuff
else
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4);
//pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
if (update)
pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
else
pglTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, pTexInfo->width, pTexInfo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
}
}
else if (pTexInfo->format == GL_TEXFMT_ALPHA_8)
{
//pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
if (MipMap)
{
pgluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pgluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, pTexInfo->width, pTexInfo->height, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
if (pTexInfo->flags & TF_TRANSPARENT)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mippmaps on transparent stuff
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mipmaps on transparent stuff
else
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4);
//pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
if (update)
pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
else
pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, pTexInfo->width, pTexInfo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
}
}
else
{
if (MipMap)
{
pgluBuild2DMipmaps(GL_TEXTURE_2D, textureformatGL, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pgluBuild2DMipmaps(GL_TEXTURE_2D, textureformatGL, pTexInfo->width, pTexInfo->height, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
// Control the mipmap level of detail
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); // the lower the number, the higer the detail
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); // the lower the number, the higher the detail
if (pTexInfo->flags & TF_TRANSPARENT)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mippmaps on transparent stuff
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mipmaps on transparent stuff
else
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 5);
}
else
{
if (update)
pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
else
pglTexImage2D(GL_TEXTURE_2D, 0, textureformatGL, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexImage2D(GL_TEXTURE_2D, 0, textureformatGL, pTexInfo->width, pTexInfo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
}
}
@ -1956,6 +1976,11 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropic_filter);
}
EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo)
{
UpdateTextureRegion(pTexInfo, 0, 0, pTexInfo->width, pTexInfo->height);
}
// -----------------+
// SetTexture : The mipmap becomes the current texture source
// -----------------+

View file

@ -424,7 +424,7 @@ static int lib_patch_copy(lua_State *L)
lua_pop(L, 2);
}
Patch_Update(patch, patch_update_buffer, src_img_width, src_img_height, PICFMT_FLAT16, sx, sy, sw, sh, dx, dy, copy_transparent);
Patch_UpdatePixels(patch, patch_update_buffer, src_img_width, src_img_height, PICFMT_FLAT16, sx, sy, sw, sh, dx, dy, copy_transparent);
return 0;
}

View file

@ -809,15 +809,14 @@ typedef struct
//
typedef struct
{
UINT8 type;
INT16 width, height;
INT16 leftoffset, topoffset;
INT32 width_mask;
UINT8 *pixels;
column_t *columns;
post_t *posts;
UINT8 type;
void *hardware; // OpenGL patch, allocated whenever necessary
void *flats[4]; // The patch as flats
@ -827,6 +826,25 @@ typedef struct
#endif
} patch_t;
typedef struct
{
patch_t patch;
post_t *posts;
} staticpatch_t;
typedef struct
{
patch_t patch;
boolean is_dirty;
boolean update_columns;
INT16 rect_dirty[4]; // left, top, right, bottom
bitarray_t *pixels_opaque;
} dynamicpatch_t;
#if defined(_MSC_VER)
#pragma pack(1)
#endif

View file

@ -19,13 +19,12 @@
#include "hardware/hw_glob.h"
#endif
//
// Creates a patch.
//
static void Patch_MarkDirtyRect(dynamicpatch_t *dpatch, INT16 left, INT16 top, INT16 right, INT16 bottom);
static void Patch_ClearDirtyRect(dynamicpatch_t *dpatch);
patch_t *Patch_Create(INT16 width, INT16 height)
{
patch_t *patch = Z_Calloc(sizeof(patch_t), PU_PATCH, NULL);
patch_t *patch = Z_Calloc(sizeof(staticpatch_t), PU_PATCH, NULL);
patch->width = width;
patch->height = height;
@ -36,18 +35,57 @@ patch_t *Patch_Create(INT16 width, INT16 height)
patch_t *Patch_CreateDynamic(INT16 width, INT16 height)
{
patch_t *patch = Z_Calloc(sizeof(patch_t), PU_PATCH, NULL);
patch_t *patch = Z_Calloc(sizeof(dynamicpatch_t), PU_PATCH, NULL);
patch->width = width;
patch->height = height;
patch->type = PATCH_TYPE_DYNAMIC;
dynamicpatch_t *dpatch = (dynamicpatch_t*)patch;
dpatch->pixels_opaque = Z_Calloc(BIT_ARRAY_SIZE(width * height), PU_PATCH_DATA, NULL);
dpatch->is_dirty = false;
dpatch->update_columns = false;
Patch_ClearDirtyRect(dpatch);
return patch;
}
static void Patch_MarkDirtyRect(dynamicpatch_t *dpatch, INT16 left, INT16 top, INT16 right, INT16 bottom)
{
if (left < dpatch->rect_dirty[0])
dpatch->rect_dirty[0] = left;
if (top < dpatch->rect_dirty[1])
dpatch->rect_dirty[1] = top;
if (right > dpatch->rect_dirty[2])
dpatch->rect_dirty[2] = right;
if (bottom > dpatch->rect_dirty[3])
dpatch->rect_dirty[3] = bottom;
}
static void Patch_ClearDirtyRect(dynamicpatch_t *dpatch)
{
dpatch->rect_dirty[0] = INT16_MAX;
dpatch->rect_dirty[1] = INT16_MAX;
dpatch->rect_dirty[2] = INT16_MIN;
dpatch->rect_dirty[3] = INT16_MIN;
}
static void Patch_InitDynamicColumns(patch_t *patch)
{
if (patch->columns == NULL)
patch->columns = Z_Calloc(sizeof(column_t) * patch->width, PU_PATCH_DATA, NULL);
for (INT32 x = 0; x < patch->width; x++)
{
column_t *column = &patch->columns[x];
column->pixels = &patch->pixels[patch->height * x];
}
}
patch_t *Patch_CreateFromDoomPatch(softwarepatch_t *source)
{
patch_t *patch = Patch_Create(0, 0);
patch_t *patch = (patch_t*)Patch_Create(0, 0);
if (!source)
return patch;
@ -67,10 +105,12 @@ patch_t *Patch_CreateFromDoomPatch(softwarepatch_t *source)
patch->width_mask = width_po2 - 1;
patch->columns = Z_Calloc(sizeof(column_t) * patch->width, PU_PATCH_DATA, NULL);
patch->posts = Z_Calloc(sizeof(post_t) * total_posts, PU_PATCH_DATA, NULL);
patch->pixels = Z_Calloc(sizeof(UINT8) * total_pixels, PU_PATCH_DATA, NULL);
Patch_MakeColumns(source, patch->width, patch->width, patch->pixels, patch->columns, patch->posts, false);
staticpatch_t *spatch = (staticpatch_t*)patch;
spatch->posts = Z_Calloc(sizeof(post_t) * total_posts, PU_PATCH_DATA, NULL);
Patch_MakeColumns(source, patch->width, patch->width, patch->pixels, patch->columns, spatch->posts, false);
return patch;
}
@ -141,9 +181,9 @@ void Patch_MakeColumns(softwarepatch_t *source, size_t num_columns, INT16 width,
// Other functions
//
static void Patch_RebuildColumn(column_t *column, INT16 height, UINT8 *is_opaque)
static void Patch_RebuildColumn(column_t *column, INT32 x, INT16 height, bitarray_t *is_opaque)
{
post_t *post;
post_t *post = NULL;
boolean was_opaque = false;
unsigned post_count = column->num_posts;
@ -152,7 +192,7 @@ static void Patch_RebuildColumn(column_t *column, INT16 height, UINT8 *is_opaque
for (INT32 y = 0; y < height; y++)
{
// End span if we have a transparent pixel
if (!is_opaque[y])
if (!in_bit_array(is_opaque, (x * height) + y))
{
was_opaque = false;
continue;
@ -195,7 +235,13 @@ column_t *Patch_GetColumn(patch_t *patch, unsigned column)
void *Patch_GetPixel(patch_t *patch, INT32 x, INT32 y)
{
if (x < 0 || x >= patch->width || patch->columns == NULL)
if (x < 0 || x >= patch->width)
return NULL;
if (Patch_NeedsUpdate(patch))
Patch_DoDynamicUpdate(patch);
if (patch->columns == NULL)
return NULL;
column_t *column = &patch->columns[x];
@ -249,12 +295,8 @@ void Patch_SetPixel(patch_t *patch, void *pixel, pictureformat_t informat, INT32
else if (inbpp == PICDEPTH_8BPP)
writePixelFunc = PicFmt_WritePixel_i8o8;
column_t *column = NULL;
boolean did_update_column = false;
// If the patch is empty
if (!patch->columns)
if (!patch->pixels)
{
if (!pixel_is_opaque)
{
@ -264,167 +306,39 @@ void Patch_SetPixel(patch_t *patch, void *pixel, pictureformat_t informat, INT32
if (patch->pixels == NULL)
patch->pixels = Z_Calloc(patch->width * patch->height, PU_PATCH_DATA, NULL);
if (patch->columns == NULL)
patch->columns = Z_Calloc(sizeof(column_t) * patch->width, PU_PATCH_DATA, NULL);
}
column = &patch->columns[x];
column->pixels = &patch->pixels[patch->height * x];
dynamicpatch_t *dpatch = (dynamicpatch_t *)patch;
size_t position = (x * patch->height) + y;
if (!pixel_is_opaque)
{
if (transparent_overwrite)
{
// No longer a pixel in this position, so columns need to be rebuilt
unset_bit_array(dpatch->pixels_opaque, position);
dpatch->update_columns = true;
dpatch->is_dirty = true;
}
}
else
{
column = &patch->columns[x];
column->pixels = &patch->pixels[patch->height * x];
// No pixel in this position, so columns need to be rebuilt
if (!in_bit_array(dpatch->pixels_opaque, position))
dpatch->update_columns = true;
for (unsigned i = 0; i < column->num_posts; i++)
{
post_t *post = &column->posts[i];
set_bit_array(dpatch->pixels_opaque, position);
writePixelFunc(&patch->pixels[position], pixel);
int this_topdelta = (int)post->topdelta;
int next_topdelta;
if (i < column->num_posts - 1)
next_topdelta = column->posts[i + 1].topdelta;
else
next_topdelta = patch->height;
// Handle the case where this is the first post, and the pixel is before it
if (i == 0 && y < this_topdelta)
{
if (!pixel_is_opaque)
{
// If the pixel is transparent, ignore
continue;
}
column->posts = Z_Realloc(column->posts, sizeof(post_t) * (column->num_posts + 1), PU_PATCH_DATA, NULL);
memmove(&column->posts[1], &column->posts[0], column->num_posts * sizeof(post_t));
column->num_posts++;
post_t *dest_post = &column->posts[0];
dest_post->topdelta = (unsigned)y;
dest_post->length = 1;
dest_post->data_offset = dest_post->topdelta;
writePixelFunc(&column->pixels[dest_post->data_offset], pixel);
did_update_column = true;
break;
}
// Pixel is inside a post
else if (y >= this_topdelta && y < this_topdelta + (signed)post->length)
{
// Handle the case where the pixel needs to be removed
if (!pixel_is_opaque)
{
if (!transparent_overwrite)
continue;
int resulting_post_length = y - post->topdelta;
if ((resulting_post_length == (signed)post->length - 1 && i == column->num_posts - 1)
|| (resulting_post_length == 0))
{
if (resulting_post_length == 0)
post->topdelta++;
post->length--;
if (post->length == 0)
{
if (column->num_posts > 1 && i < column->num_posts - 1)
memmove(&column->posts[i], &column->posts[i + 1], (column->num_posts - i) * sizeof(post_t));
column->num_posts--;
}
}
else
{
column->num_posts++;
column->posts = Z_Realloc(column->posts, sizeof(post_t) * column->num_posts, PU_PATCH_DATA, NULL);
if (i != column->num_posts)
memmove(&column->posts[i + 1], &column->posts[i], ((column->num_posts - 1) - i) * sizeof(post_t));
column->posts[i + 1].length = column->posts[i].length - resulting_post_length - 1;
column->posts[i + 1].topdelta = y + 1;
column->posts[i + 1].data_offset = column->posts[i + 1].topdelta;
column->posts[i].length = resulting_post_length;
}
}
else
writePixelFunc(&column->pixels[post->data_offset + (y - post->topdelta)], pixel);
did_update_column = true;
break;
}
// After this post, but before the next one
else if (y >= this_topdelta + (signed)post->length && y < next_topdelta)
{
if (!pixel_is_opaque)
{
// If the pixel is transparent, ignore
continue;
}
post_t *dest_post = NULL;
column->posts = Z_Realloc(column->posts, sizeof(post_t) * (column->num_posts + 1), PU_PATCH_DATA, NULL);
if (i == column->num_posts - 1)
dest_post = &column->posts[column->num_posts];
else
{
memmove(&column->posts[i + 1], &column->posts[i], (column->num_posts - i) * sizeof(post_t));
dest_post = &column->posts[i];
}
column->num_posts++;
dest_post->topdelta = (unsigned)y;
dest_post->length = 1;
dest_post->data_offset = dest_post->topdelta;
writePixelFunc(&column->pixels[dest_post->data_offset], pixel);
did_update_column = true;
break;
}
}
dpatch->is_dirty = true;
}
if (!did_update_column && column && column->num_posts == 0)
{
if (!pixel_is_opaque)
{
// If the pixel is transparent, do nothing
return;
}
column->num_posts = 1;
column->posts = Z_Realloc(column->posts, sizeof(post_t) * column->num_posts, PU_PATCH_DATA, NULL);
post_t *post = &column->posts[0];
post->topdelta = (unsigned)y;
post->length = 1;
post->data_offset = post->topdelta;
writePixelFunc(&column->pixels[post->data_offset], pixel);
did_update_column = true;
}
if (did_update_column)
{
Patch_FreeMiscData(patch);
#ifdef HWRENDER
if (patch->hardware)
HWR_UpdatePatch(patch);
#endif
}
if (dpatch->is_dirty)
Patch_MarkDirtyRect(dpatch, x, y, x + 1, y + 1);
}
void Patch_Update(patch_t *patch,
void Patch_UpdatePixels(patch_t *patch,
void *pixels, INT32 src_img_width, INT32 src_img_height,
pictureformat_t informat,
INT32 sx, INT32 sy, INT32 sw, INT32 sh, INT32 dx, INT32 dy,
@ -436,10 +350,6 @@ void Patch_Update(patch_t *patch,
if (src_img_width <= 0 || src_img_height <= 0 || sw <= 0 || sh <= 0)
return;
boolean did_update = false;
if (patch->columns == NULL)
patch->columns = Z_Calloc(sizeof(column_t) * patch->width, PU_PATCH_DATA, NULL);
if (patch->pixels == NULL)
patch->pixels = Z_Calloc(patch->width * patch->height, PU_PATCH_DATA, NULL);
@ -467,35 +377,34 @@ void Patch_Update(patch_t *patch,
}
}
if (readPixelFunc == NULL)
I_Error("Patch_Update: unsupported input format");
INT32 inbpp = Picture_FormatBPP(informat);
if (inbpp == PICDEPTH_32BPP)
switch (Picture_FormatBPP(informat))
{
case PICDEPTH_32BPP:
getAlphaFunc = PicFmt_GetAlpha_32bpp;
writePixelFunc = PicFmt_WritePixel_i32o8;
}
else if (inbpp == PICDEPTH_16BPP)
{
break;
case PICDEPTH_16BPP:
getAlphaFunc = PicFmt_GetAlpha_16bpp;
writePixelFunc = PicFmt_WritePixel_i16o8;
}
else if (inbpp == PICDEPTH_8BPP)
{
break;
case PICDEPTH_8BPP:
getAlphaFunc = PicFmt_GetAlpha_8bpp;
writePixelFunc = PicFmt_WritePixel_i8o8;
break;
}
UINT8 *is_opaque = malloc(patch->height * sizeof(UINT8));
if (!is_opaque)
{
abort();
return;
}
if (readPixelFunc == NULL || writePixelFunc == NULL || getAlphaFunc == NULL)
I_Error("Patch_UpdatePixels: unsupported input format");
dynamicpatch_t *dpatch = (dynamicpatch_t *)patch;
INT32 dest_x1 = max(0, dx);
INT32 dest_x2 = min(dx + sw, patch->width);
INT32 dest_y1 = max(0, dy);
INT32 dest_y2 = min(dy + sh, patch->height);
Patch_MarkDirtyRect(dpatch, dest_x1, dest_y1, dest_x2, dest_y2);
// Write columns
for (INT32 x = dx; x < dx + sw; x++, sx++)
{
if (x < 0 || sx < 0)
@ -503,19 +412,6 @@ void Patch_Update(patch_t *patch,
else if (x >= patch->width || sx >= src_img_width)
break;
column_t *column = &patch->columns[x];
column->pixels = &patch->pixels[patch->height * x];
memset(is_opaque, 0, patch->height * sizeof(UINT8));
for (unsigned i = 0; i < column->num_posts; i++)
{
post_t *post = &column->posts[i];
memset(&is_opaque[post->topdelta], 1, post->length);
}
boolean did_update_column = false;
INT32 src_y = sy;
for (INT32 y = dy; y < dy + sh; y++, src_y++)
@ -534,39 +430,73 @@ void Patch_Update(patch_t *patch,
if (input != NULL)
opaque = getAlphaFunc(input, 0) > 0;
size_t position = (x * patch->height) + y;
if (!opaque)
{
if (transparent_overwrite)
{
is_opaque[y] = false;
did_update = did_update_column = true;
unset_bit_array(dpatch->pixels_opaque, position);
dpatch->update_columns = dpatch->is_dirty = true;
}
}
else
{
did_update = did_update_column = true;
is_opaque[y] = true;
writePixelFunc(&column->pixels[y], input);
set_bit_array(dpatch->pixels_opaque, position);
writePixelFunc(&patch->pixels[position], input);
dpatch->update_columns = dpatch->is_dirty = true;
}
}
}
}
if (!did_update_column)
continue;
boolean Patch_NeedsUpdate(patch_t *patch)
{
if (patch->type != PATCH_TYPE_DYNAMIC)
return false;
Patch_RebuildColumn(column, patch->height, is_opaque);
dynamicpatch_t *dpatch = (dynamicpatch_t *)patch;
return dpatch->is_dirty;
}
void Patch_DoDynamicUpdate(patch_t *patch)
{
if (patch->type != PATCH_TYPE_DYNAMIC)
return;
dynamicpatch_t *dpatch = (dynamicpatch_t *)patch;
if (!dpatch->is_dirty)
return;
if (patch->columns == NULL)
Patch_InitDynamicColumns(patch);
if (dpatch->update_columns)
{
for (INT32 x = dpatch->rect_dirty[0]; x < dpatch->rect_dirty[2]; x++)
Patch_RebuildColumn(&patch->columns[x], x, patch->height, dpatch->pixels_opaque);
}
if (did_update)
{
Patch_FreeMiscData(patch);
Patch_FreeMiscData(patch);
#ifdef HWRENDER
if (patch->hardware)
HWR_UpdatePatch(patch);
if (patch->hardware)
HWR_UpdatePatchRegion(patch, dpatch->rect_dirty[0], dpatch->rect_dirty[1], dpatch->rect_dirty[2], dpatch->rect_dirty[3]);
#endif
}
free(is_opaque);
dpatch->is_dirty = false;
dpatch->update_columns = false;
Patch_ClearDirtyRect(dpatch);
}
bitarray_t *Patch_GetOpaqueRegions(patch_t *patch)
{
if (patch->type != PATCH_TYPE_DYNAMIC)
return NULL;
dynamicpatch_t *dpatch = (dynamicpatch_t *)patch;
return dpatch->pixels_opaque;
}
//
@ -606,18 +536,21 @@ static void Patch_FreeData(patch_t *patch)
Patch_FreeMiscData(patch);
if (patch->type == PATCH_TYPE_DYNAMIC)
if (patch->type == PATCH_TYPE_STATIC)
{
for (INT32 x = 0; x < patch->width; x++)
Z_Free(patch->columns[x].posts);
staticpatch_t *spatch = (staticpatch_t*)patch;
Z_Free(spatch->posts);
}
else if (patch->type == PATCH_TYPE_DYNAMIC)
{
dynamicpatch_t *dpatch = (dynamicpatch_t*)patch;
for (INT32 x = 0; x < dpatch->patch.width; x++)
Z_Free(dpatch->patch.columns[x].posts);
Z_Free(dpatch->pixels_opaque);
}
if (patch->pixels)
Z_Free(patch->pixels);
if (patch->columns)
Z_Free(patch->columns);
if (patch->posts)
Z_Free(patch->posts);
Z_Free(patch->pixels);
Z_Free(patch->columns);
}
void Patch_Free(patch_t *patch)

View file

@ -29,12 +29,17 @@ patch_t *Patch_CreateDynamic(INT16 width, INT16 height);
void *Patch_GetPixel(patch_t *patch, INT32 x, INT32 y);
void Patch_SetPixel(patch_t *patch, void *pixel, pictureformat_t informat, INT32 x, INT32 y, boolean transparent_overwrite);
void Patch_Update(patch_t *patch,
void Patch_UpdatePixels(patch_t *patch,
void *pixels, INT32 src_img_width, INT32 src_img_height,
pictureformat_t informat,
INT32 sx, INT32 sy, INT32 sw, INT32 sh, INT32 dx, INT32 dy,
boolean transparent_overwrite);
boolean Patch_NeedsUpdate(patch_t *patch);
void Patch_DoDynamicUpdate(patch_t *patch);
bitarray_t *Patch_GetOpaqueRegions(patch_t *patch);
void Patch_Free(patch_t *patch);
void Patch_FreeMiscData(patch_t *patch);

View file

@ -364,7 +364,6 @@ void *Picture_PatchConvert(
out->columns = Z_Calloc(sizeof(column_t) * out->width, PU_PATCH_DATA, NULL);
out->pixels = Z_Calloc(max_pixels * (outbpp / 8), PU_PATCH_DATA, NULL);
out->posts = NULL;
UINT8 *imgptr = out->pixels;
@ -407,8 +406,9 @@ void *Picture_PatchConvert(
{
num_posts++;
out->posts = Z_Realloc(out->posts, sizeof(post_t) * num_posts, PU_PATCH_DATA, NULL);
post = &out->posts[num_posts - 1];
staticpatch_t *spatch = (staticpatch_t*)out;
spatch->posts = Z_Realloc(spatch->posts, sizeof(post_t) * num_posts, PU_PATCH_DATA, NULL);
post = &spatch->posts[num_posts - 1];
post->topdelta = (unsigned)y;
post->length = 0;
post->data_offset = post_data_offset;
@ -437,7 +437,10 @@ void *Picture_PatchConvert(
{
column_t *column = &out->columns[x];
if (column->num_posts > 0)
column->posts = &out->posts[column_posts[x]];
{
staticpatch_t *spatch = (staticpatch_t*)out;
column->posts = &spatch->posts[column_posts[x]];
}
if (old_pixels != out->pixels)
column->pixels = out->pixels + (column->pixels - old_pixels);
}

View file

@ -86,6 +86,7 @@ void *hwSym(const char *funcName,void *handle)
GETFUNC(ClearBuffer);
GETFUNC(SetTexture);
GETFUNC(UpdateTexture);
GETFUNC(UpdateTextureRegion);
GETFUNC(DeleteTexture);
GETFUNC(ReadRect);
GETFUNC(GClipRect);

View file

@ -1934,6 +1934,7 @@ void VID_StartupOpenGL(void)
HWD.pfnClearBuffer = hwSym("ClearBuffer",NULL);
HWD.pfnSetTexture = hwSym("SetTexture",NULL);
HWD.pfnUpdateTexture = hwSym("UpdateTexture",NULL);
HWD.pfnUpdateTextureRegion = hwSym("UpdateTextureRegion", NULL);
HWD.pfnDeleteTexture = hwSym("DeleteTexture",NULL);
HWD.pfnReadRect = hwSym("ReadRect",NULL);
HWD.pfnGClipRect = hwSym("GClipRect",NULL);

View file

@ -514,6 +514,9 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
UINT8 perplayershuffle = 0;
if (Patch_NeedsUpdate(patch))
Patch_DoDynamicUpdate(patch);
if (patch->columns == NULL || rendermode == render_none)
return;
@ -798,6 +801,9 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
UINT8 perplayershuffle = 0;
if (Patch_NeedsUpdate(patch))
Patch_DoDynamicUpdate(patch);
if (patch->columns == NULL || rendermode == render_none)
return;