diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 2aba473c5c1191b828c217754e719f5c040fe9c1..c7c85a24af077fad63407c00d985ea7b4a5e53d1 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -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); } // -------------------+ diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index 1c4cd99ab03d34498fa13d132a01ef53af9e6e61..63e82f10f3708867362f1f99653f5a7ceca26eb1 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -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; diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index 352ff1e962a82ca35ae55fe5b65c19dd4687ca7d..0bd84a473a5bf95cb62a39da9367e5a091d35750 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -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); diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 71cb5ca70375629ee06c7943e0c5be881555a0c1..f6cfab3ea0224688052b115c0890d53c8c1c9100 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -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 // -----------------+ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index de5cc77537ba0f7f275a9e9d33f07bce1505ef1d..cf7d0343b343d471cca00ee0b8ba040cbe3002c1 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -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; } diff --git a/src/r_defs.h b/src/r_defs.h index 66f699624d53969c72453df87c323c278cebadfe..598408e659530689afe6234280e52b9c46947f37 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -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 diff --git a/src/r_patch.c b/src/r_patch.c index 1257deb73d14343abd10fb409085a11ae4e7f1dc..dab600ed10844b48c9c9ee79d999b66a232b749b 100644 --- a/src/r_patch.c +++ b/src/r_patch.c @@ -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]; } - else - { - column = &patch->columns[x]; - column->pixels = &patch->pixels[patch->height * x]; - for (unsigned i = 0; i < column->num_posts; i++) - { - post_t *post = &column->posts[i]; - - 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; + dynamicpatch_t *dpatch = (dynamicpatch_t *)patch; - // 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; - } - } - } + size_t position = (x * patch->height) + y; - if (!did_update_column && column && column->num_posts == 0) + if (!pixel_is_opaque) { - if (!pixel_is_opaque) + if (transparent_overwrite) { - // If the pixel is transparent, do nothing - return; + // 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; } - - 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) + else { - Patch_FreeMiscData(patch); + // No pixel in this position, so columns need to be rebuilt + if (!in_bit_array(dpatch->pixels_opaque, position)) + dpatch->update_columns = true; -#ifdef HWRENDER - if (patch->hardware) - HWR_UpdatePatch(patch); -#endif + set_bit_array(dpatch->pixels_opaque, position); + writePixelFunc(&patch->pixels[position], pixel); + + dpatch->is_dirty = true; } + + 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; +} - if (did_update) +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) { - Patch_FreeMiscData(patch); + for (INT32 x = dpatch->rect_dirty[0]; x < dpatch->rect_dirty[2]; x++) + Patch_RebuildColumn(&patch->columns[x], x, patch->height, dpatch->pixels_opaque); + } + + 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) + { + staticpatch_t *spatch = (staticpatch_t*)patch; + Z_Free(spatch->posts); + } + else if (patch->type == PATCH_TYPE_DYNAMIC) { - for (INT32 x = 0; x < patch->width; x++) - Z_Free(patch->columns[x].posts); + 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) diff --git a/src/r_patch.h b/src/r_patch.h index 1b8fd8f7060150f6da4ddf862390e526d75a381a..9ee5892fad59c319601c20cfb3770aacd540c398 100644 --- a/src/r_patch.h +++ b/src/r_patch.h @@ -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); diff --git a/src/r_picformats.c b/src/r_picformats.c index c2eb16355643987cbf4c44bbd4f4d92b3cb5c7de..ba63ce95bdaa71942859600fcd77c6405b5ffcf8 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -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); } diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c index 96e3d7d6926ef23771c8dcf489b4d8d2a16c0a1c..40f88a22c379bb91a5fadd2cbb2fafbb6fb4a9ae 100644 --- a/src/sdl/hwsym_sdl.c +++ b/src/sdl/hwsym_sdl.c @@ -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); diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 590d7d142a7a536678bfb96d3891c78264a19fba..d33c2b0d09f57c50f868f1dd90c0130944b855e6 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -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); diff --git a/src/v_video.c b/src/v_video.c index 4b2cb5dfb2907d0eb408910a3059d6b916493571..150bd0d338754b574fbd44b60193933b1b6a5d0a 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -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;