From 2e7bcf9e415a2d4fbe2bda3c0f337de835edf04d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 10 Dec 2018 01:17:39 +0100 Subject: [PATCH] - implemented a proper texture composition cache. This will mostly ensure that each patch used for composition is only loaded once and automatically unloaded once no longer needed. So far only for paletted rendering, but the same logic can be used for true color as well. --- src/gl/system/gl_framebuffer.cpp | 9 +- src/gl/system/gl_framebuffer.h | 1 + src/gl/textures/gl_hwtexture.cpp | 25 ++++ src/gl/textures/gl_hwtexture.h | 1 + src/hwrenderer/textures/hw_precache.cpp | 29 ++++ src/r_data/models/models_voxel.cpp | 4 +- src/swrenderer/line/r_line.cpp | 1 + src/swrenderer/r_swrenderer.cpp | 32 ++++- src/swrenderer/r_swrenderer.h | 1 + src/swrenderer/textures/r_swtexture.h | 8 +- src/tarray.h | 14 +- src/textures/formats/automaptexture.cpp | 4 +- src/textures/formats/buildtexture.cpp | 4 +- src/textures/formats/ddstexture.cpp | 4 +- src/textures/formats/emptytexture.cpp | 4 +- src/textures/formats/flattexture.cpp | 4 +- src/textures/formats/fontchars.cpp | 6 +- src/textures/formats/fontchars.h | 4 +- src/textures/formats/imgztexture.cpp | 4 +- src/textures/formats/jpegtexture.cpp | 4 +- src/textures/formats/multipatchtexture.cpp | 32 ++++- src/textures/formats/multipatchtexture.h | 4 +- src/textures/formats/patchtexture.cpp | 4 +- src/textures/formats/pcxtexture.cpp | 4 +- src/textures/formats/pngtexture.cpp | 4 +- src/textures/formats/rawpagetexture.cpp | 4 +- src/textures/formats/shadertexture.cpp | 2 +- src/textures/formats/tgatexture.cpp | 4 +- src/textures/image.cpp | 151 ++++++++++++++++++++- src/textures/image.h | 41 +++++- src/textures/textures.h | 13 +- src/v_video.h | 1 + 32 files changed, 367 insertions(+), 60 deletions(-) diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 314426ff01..050ff41ff1 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -373,7 +373,14 @@ void OpenGLFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation) FHardwareTexture::UnbindAll(); } -FModelRenderer *OpenGLFrameBuffer::CreateModelRenderer(int mli) +bool OpenGLFrameBuffer::CheckPrecacheMaterial(FMaterial *mat) +{ + if (!mat->tex->GetImage()) return true; + auto base = static_cast(mat->GetLayer(0)); + return base->Exists(0); +} + +FModelRenderer *OpenGLFrameBuffer::CreateModelRenderer(int mli) { return new FGLModelRenderer(nullptr, gl_RenderState, mli); } diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h index c2a26650fb..72ec2a9c22 100644 --- a/src/gl/system/gl_framebuffer.h +++ b/src/gl/system/gl_framebuffer.h @@ -36,6 +36,7 @@ public: void SetTextureFilterMode() override; IHardwareTexture *CreateHardwareTexture(FTexture *tex) override; void PrecacheMaterial(FMaterial *mat, int translation) override; + bool CheckPrecacheMaterial(FMaterial *mat) override; FModelRenderer *CreateModelRenderer(int mli) override; void TextureFilterChanged() override; void BeginFrame() override; diff --git a/src/gl/textures/gl_hwtexture.cpp b/src/gl/textures/gl_hwtexture.cpp index f6cdc8415c..f3c78b6e6d 100644 --- a/src/gl/textures/gl_hwtexture.cpp +++ b/src/gl/textures/gl_hwtexture.cpp @@ -467,4 +467,29 @@ bool FHardwareTexture::BindOrCreate(FTexture *tex, int texunit, int clampmode, i return true; } +//=========================================================================== +// +// Binds a texture to the renderer +// +//=========================================================================== + +bool FHardwareTexture::Exists(int translation) +{ + int usebright = false; + + if (translation <= 0) + { + translation = -translation; + } + else + { + auto remap = TranslationToTable(translation); + translation = remap == nullptr ? 0 : remap->GetUniqueIndex(); + } + + TranslatedTexture *pTex = GetTexID(translation); + return (pTex->glTexID != 0); +} + + } diff --git a/src/gl/textures/gl_hwtexture.h b/src/gl/textures/gl_hwtexture.h index 6c693a9983..f864d073b4 100644 --- a/src/gl/textures/gl_hwtexture.h +++ b/src/gl/textures/gl_hwtexture.h @@ -81,6 +81,7 @@ public: unsigned int Bind(int texunit, int translation, bool needmipmap); bool BindOrCreate(FTexture *tex, int texunit, int clampmode, int translation, int flags); + bool Exists(int translation); void AllocateBuffer(int w, int h, int texelsize); uint8_t *MapBuffer(); diff --git a/src/hwrenderer/textures/hw_precache.cpp b/src/hwrenderer/textures/hw_precache.cpp index 492610a7ce..b927463a26 100644 --- a/src/hwrenderer/textures/hw_precache.cpp +++ b/src/hwrenderer/textures/hw_precache.cpp @@ -33,6 +33,7 @@ #include "r_data/models/models.h" #include "textures/skyboxtexture.h" #include "hwrenderer/textures/hw_material.h" +#include "image.h" //========================================================================== @@ -189,6 +190,31 @@ void hw_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitl if (gl_precache) { + FImageSource::BeginPrecaching(); + + // cache all used textures + for (int i = cnt - 1; i >= 0; i--) + { + FTexture *tex = TexMan.ByIndex(i); + if (tex != nullptr && tex->GetImage() != nullptr) + { + if (texhitlist[i] & (FTextureManager::HIT_Wall | FTextureManager::HIT_Flat | FTextureManager::HIT_Sky)) + { + FMaterial * gltex = FMaterial::ValidateTexture(tex, false); + if (gltex && !screen->CheckPrecacheMaterial(gltex)) + { + FImageSource::RegisterForPrecache(tex->GetImage()); + } + } + + // Only register untranslated sprites. Translated ones are very unlikely to require data that can be reused. + if (spritehitlist[i] != nullptr && (*spritehitlist[i]).CheckKey(0)) + { + FImageSource::RegisterForPrecache(tex->GetImage()); + } + } + } + // cache all used textures for (int i = cnt - 1; i >= 0; i--) { @@ -203,6 +229,9 @@ void hw_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitl } } + + FImageSource::EndPrecaching(); + // cache all used models FModelRenderer *renderer = screen->CreateModelRenderer(-1); for (unsigned i = 0; i < Models.Size(); i++) diff --git a/src/r_data/models/models_voxel.cpp b/src/r_data/models/models_voxel.cpp index 1774354bfc..8cda437bc4 100644 --- a/src/r_data/models/models_voxel.cpp +++ b/src/r_data/models/models_voxel.cpp @@ -53,7 +53,7 @@ public: FVoxelTexture(FVoxel *voxel); int CopyPixels(FBitmap *bmp, int conversion) override; - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; protected: FVoxel *SourceVox; @@ -79,7 +79,7 @@ FVoxelTexture::FVoxelTexture(FVoxel *vox) // //=========================================================================== -TArray FVoxelTexture::GetPalettedPixels(int conversion) +TArray FVoxelTexture::CreatePalettedPixels(int conversion) { // GetPixels gets called when a translated palette is used so we still need to implement it here. TArray Pixels(256, true); diff --git a/src/swrenderer/line/r_line.cpp b/src/swrenderer/line/r_line.cpp index c3bc08d9f7..0a376c7912 100644 --- a/src/swrenderer/line/r_line.cpp +++ b/src/swrenderer/line/r_line.cpp @@ -818,6 +818,7 @@ namespace swrenderer FTexture *tex = TexMan.GetPalettedTexture(sidedef->GetTexture(side_t::top), true); mTopPart.Texture = tex && tex->isValid() ? tex->GetSoftwareTexture() : nullptr; + if (mTopPart.Texture == nullptr) return; mTopPart.TextureOffsetU = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::top)); double rowoffset = sidedef->GetTextureYOffset(side_t::top); diff --git a/src/swrenderer/r_swrenderer.cpp b/src/swrenderer/r_swrenderer.cpp index 721873da1d..9cd3fb309e 100644 --- a/src/swrenderer/r_swrenderer.cpp +++ b/src/swrenderer/r_swrenderer.cpp @@ -51,6 +51,7 @@ #include "polyrenderer/poly_renderer.h" #include "p_setup.h" #include "g_levellocals.h" +#include "image.h" // [BB] Use ZDoom's freelook limit for the sotfware renderer. // Note: ZDoom's limit is chosen such that the sky is rendered properly. @@ -80,6 +81,25 @@ FRenderer *CreateSWRenderer() return new FSoftwareRenderer; } +void FSoftwareRenderer::PreparePrecache(FTexture *ttex, int cache) +{ + bool isbgra = V_IsTrueColor(); + + if (ttex != NULL && ttex->isValid()) + { + FSoftwareTexture *tex = ttex->GetSoftwareTexture(); + + if (tex->CheckPixels()) + { + if (cache == 0) tex->Unload(); + } + else if (cache != 0) + { + FImageSource::RegisterForPrecache(ttex->GetImage()); + } + } +} + void FSoftwareRenderer::PrecacheTexture(FTexture *ttex, int cache) { bool isbgra = V_IsTrueColor(); @@ -102,10 +122,6 @@ void FSoftwareRenderer::PrecacheTexture(FTexture *ttex, int cache) else tex->GetPixels (DefaultRenderStyle()); } - else - { - tex->Unload (); - } } } @@ -152,10 +168,18 @@ void FSoftwareRenderer::Precache(uint8_t *texhitlist, TMap & delete[] spritelist; int cnt = TexMan.NumTextures(); + + FImageSource::BeginPrecaching(); + for (int i = cnt - 1; i >= 0; i--) + { + PreparePrecache(TexMan.ByIndex(i), texhitlist[i]); + } + for (int i = cnt - 1; i >= 0; i--) { PrecacheTexture(TexMan.ByIndex(i), texhitlist[i]); } + FImageSource::EndPrecaching(); } void FSoftwareRenderer::RenderView(player_t *player, DCanvas *target, void *videobuffer) diff --git a/src/swrenderer/r_swrenderer.h b/src/swrenderer/r_swrenderer.h index 864cbb6654..b9bb280194 100644 --- a/src/swrenderer/r_swrenderer.h +++ b/src/swrenderer/r_swrenderer.h @@ -27,6 +27,7 @@ struct FSoftwareRenderer : public FRenderer void Init() override; private: + void PreparePrecache(FTexture *tex, int cache); void PrecacheTexture(FTexture *tex, int cache); swrenderer::RenderScene mScene; diff --git a/src/swrenderer/textures/r_swtexture.h b/src/swrenderer/textures/r_swtexture.h index 3dd5346246..791619e778 100644 --- a/src/swrenderer/textures/r_swtexture.h +++ b/src/swrenderer/textures/r_swtexture.h @@ -1,6 +1,6 @@ #pragma once #include "textures/textures.h" - +#include "v_video.h" struct FSoftwareTextureSpan { @@ -130,6 +130,12 @@ public: return GetPixels(alpha); } + // Checks if the pixel data is loaded. + bool CheckPixels() const + { + return V_IsTrueColor() ? PixelsBgra.Size() > 0 : Pixels.Size() > 0; + } + const uint8_t *GetColumn(FRenderStyle style, unsigned int column, const FSoftwareTextureSpan **spans_out) { bool alpha = !!(style.Flags & STYLEF_RedIsAlpha); diff --git a/src/tarray.h b/src/tarray.h index bdbb7bb82d..f580ae3e41 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -272,6 +272,18 @@ public: return i; } + template const + unsigned int FindEx(Func compare) const + { + unsigned int i; + for (i = 0; i < Count; ++i) + { + if (compare(Array[i])) + break; + } + return i; + } + unsigned int Push (const T &item) { Grow (1); @@ -1019,7 +1031,7 @@ protected: if (!nold[i].IsNil()) { Node *n = NewKey(nold[i].Pair.Key); - ::new(&n->Pair.Value) VT(nold[i].Pair.Value); + ::new(&n->Pair.Value) VT(std::move(nold[i].Pair.Value)); nold[i].~Node(); } } diff --git a/src/textures/formats/automaptexture.cpp b/src/textures/formats/automaptexture.cpp index 299727d83f..81205291bb 100644 --- a/src/textures/formats/automaptexture.cpp +++ b/src/textures/formats/automaptexture.cpp @@ -52,7 +52,7 @@ class FAutomapTexture : public FImageSource { public: FAutomapTexture(int lumpnum); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; }; @@ -91,7 +91,7 @@ FAutomapTexture::FAutomapTexture (int lumpnum) // //========================================================================== -TArray FAutomapTexture::GetPalettedPixels(int conversion) +TArray FAutomapTexture::CreatePalettedPixels(int conversion) { int x, y; FMemLump data = Wads.ReadLump (SourceLump); diff --git a/src/textures/formats/buildtexture.cpp b/src/textures/formats/buildtexture.cpp index a76e1666e8..1b5be65435 100644 --- a/src/textures/formats/buildtexture.cpp +++ b/src/textures/formats/buildtexture.cpp @@ -57,7 +57,7 @@ class FBuildTexture : public FImageSource { public: FBuildTexture (const FString &pathprefix, int tilenum, const uint8_t *pixels, int translation, int width, int height, int left, int top); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override; protected: @@ -81,7 +81,7 @@ FBuildTexture::FBuildTexture(const FString &pathprefix, int tilenum, const uint8 TopOffset = top; } -TArray FBuildTexture::GetPalettedPixels(int conversion) +TArray FBuildTexture::CreatePalettedPixels(int conversion) { TArray Pixels(Width * Height, true); FRemapTable *Remap = translationtables[TRANSLATION_Standard][Translation]; diff --git a/src/textures/formats/ddstexture.cpp b/src/textures/formats/ddstexture.cpp index 810f91fe2c..f5ae1f0562 100644 --- a/src/textures/formats/ddstexture.cpp +++ b/src/textures/formats/ddstexture.cpp @@ -164,7 +164,7 @@ class FDDSTexture : public FImageSource public: FDDSTexture (FileReader &lump, int lumpnum, void *surfdesc); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; protected: uint32_t Format; @@ -372,7 +372,7 @@ void FDDSTexture::CalcBitShift (uint32_t mask, uint8_t *lshiftp, uint8_t *rshift // //========================================================================== -TArray FDDSTexture::GetPalettedPixels(int conversion) +TArray FDDSTexture::CreatePalettedPixels(int conversion) { auto lump = Wads.OpenLumpReader (SourceLump); diff --git a/src/textures/formats/emptytexture.cpp b/src/textures/formats/emptytexture.cpp index 11710f226e..1073de660c 100644 --- a/src/textures/formats/emptytexture.cpp +++ b/src/textures/formats/emptytexture.cpp @@ -51,7 +51,7 @@ class FEmptyTexture : public FImageSource { public: FEmptyTexture (int lumpnum); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; }; //========================================================================== @@ -91,7 +91,7 @@ FEmptyTexture::FEmptyTexture (int lumpnum) // //========================================================================== -TArray FEmptyTexture::GetPalettedPixels(int conversion) +TArray FEmptyTexture::CreatePalettedPixels(int conversion) { TArray Pixel(1, true); Pixel[0] = 0; diff --git a/src/textures/formats/flattexture.cpp b/src/textures/formats/flattexture.cpp index a0982645fc..7654b2b350 100644 --- a/src/textures/formats/flattexture.cpp +++ b/src/textures/formats/flattexture.cpp @@ -50,7 +50,7 @@ class FFlatTexture : public FImageSource { public: FFlatTexture (int lumpnum); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; }; @@ -104,7 +104,7 @@ FFlatTexture::FFlatTexture (int lumpnum) // //========================================================================== -TArray FFlatTexture::GetPalettedPixels(int conversion) +TArray FFlatTexture::CreatePalettedPixels(int conversion) { auto lump = Wads.OpenLumpReader (SourceLump); TArray Pixels(Width*Height, true); diff --git a/src/textures/formats/fontchars.cpp b/src/textures/formats/fontchars.cpp index 3573ab8928..93f460b3df 100644 --- a/src/textures/formats/fontchars.cpp +++ b/src/textures/formats/fontchars.cpp @@ -68,12 +68,12 @@ FFontChar1::FFontChar1 (FImageSource *sourcelump) // //========================================================================== -TArray FFontChar1::GetPalettedPixels (int) +TArray FFontChar1::CreatePalettedPixels (int) { // Make the texture as normal, then remap it so that all the colors // are at the low end of the palette // Why? It only creates unnecessary work! - auto Pixels = BaseTexture->GetPalettedPixels(normal); + auto Pixels = BaseTexture->CreatePalettedPixels(normal); if (SourceRemap) { @@ -132,7 +132,7 @@ void FFontChar2::SetSourceRemap(const uint8_t *sourceremap) // //========================================================================== -TArray FFontChar2::GetPalettedPixels(int) +TArray FFontChar2::CreatePalettedPixels(int) { auto lump = Wads.OpenLumpReader (SourceLump); int destSize = Width * Height; diff --git a/src/textures/formats/fontchars.h b/src/textures/formats/fontchars.h index 644372b772..96437f3a8d 100644 --- a/src/textures/formats/fontchars.h +++ b/src/textures/formats/fontchars.h @@ -5,7 +5,7 @@ class FFontChar1 : public FImageSource { public: FFontChar1 (FImageSource *sourcelump); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; void SetSourceRemap(const uint8_t *sourceremap); protected: @@ -20,7 +20,7 @@ class FFontChar2 : public FImageSource public: FFontChar2 (int sourcelump, int sourcepos, int width, int height, int leftofs=0, int topofs=0); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; void SetSourceRemap(const uint8_t *sourceremap); protected: diff --git a/src/textures/formats/imgztexture.cpp b/src/textures/formats/imgztexture.cpp index 681f39d97f..2f466c5e2b 100644 --- a/src/textures/formats/imgztexture.cpp +++ b/src/textures/formats/imgztexture.cpp @@ -68,7 +68,7 @@ class FIMGZTexture : public FImageSource public: FIMGZTexture (int lumpnum, uint16_t w, uint16_t h, int16_t l, int16_t t, bool isalpha); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override; }; @@ -120,7 +120,7 @@ FIMGZTexture::FIMGZTexture (int lumpnum, uint16_t w, uint16_t h, int16_t l, int1 // //========================================================================== -TArray FIMGZTexture::GetPalettedPixels(int conversion) +TArray FIMGZTexture::CreatePalettedPixels(int conversion) { FMemLump lump = Wads.ReadLump (SourceLump); const ImageHeader *imgz = (const ImageHeader *)lump.GetMem(); diff --git a/src/textures/formats/jpegtexture.cpp b/src/textures/formats/jpegtexture.cpp index 451f91f153..a441c585b3 100644 --- a/src/textures/formats/jpegtexture.cpp +++ b/src/textures/formats/jpegtexture.cpp @@ -186,7 +186,7 @@ public: FJPEGTexture (int lumpnum, int width, int height); int CopyPixels(FBitmap *bmp, int conversion) override; - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; }; //========================================================================== @@ -260,7 +260,7 @@ FJPEGTexture::FJPEGTexture (int lumpnum, int width, int height) // //========================================================================== -TArray FJPEGTexture::GetPalettedPixels(int conversion) +TArray FJPEGTexture::CreatePalettedPixels(int conversion) { auto lump = Wads.OpenLumpReader (SourceLump); JSAMPLE *buff = NULL; diff --git a/src/textures/formats/multipatchtexture.cpp b/src/textures/formats/multipatchtexture.cpp index cd50d555c6..fe960923b9 100644 --- a/src/textures/formats/multipatchtexture.cpp +++ b/src/textures/formats/multipatchtexture.cpp @@ -53,6 +53,7 @@ #include "image.h" #include "multipatchtexture.h" + //========================================================================== // // FMultiPatchTexture :: FMultiPatchTexture @@ -142,7 +143,8 @@ static uint8_t *GetBlendMap(PalEntry blend, uint8_t *blendwork) void FMultiPatchTexture::CopyToBlock(uint8_t *dest, int dwidth, int dheight, FImageSource *source, int xpos, int ypos, int rotate, const uint8_t *translation, int style) { - auto image = source->GetPalettedPixels(style); // should use composition cache + auto cimage = source->GetCachedPalettedPixels(style); // should use composition cache + auto &image = cimage.Pixels; const uint8_t *pixels = image.Data(); int srcwidth = source->GetWidth(); int srcheight = source->GetHeight(); @@ -188,7 +190,7 @@ void FMultiPatchTexture::CopyToBlock(uint8_t *dest, int dwidth, int dheight, FIm // //========================================================================== -TArray FMultiPatchTexture::GetPalettedPixels(int conversion) +TArray FMultiPatchTexture::CreatePalettedPixels(int conversion) { int numpix = Width * Height; uint8_t blendwork[256]; @@ -324,3 +326,29 @@ int FMultiPatchTexture::CopyPixels(FBitmap *bmp, int conversion) return retv; } +//========================================================================== +// +// +// +//========================================================================== + +void FMultiPatchTexture::CollectForPrecache(PrecacheInfo &info, bool requiretruecolor) +{ + FImageSource::CollectForPrecache(info, requiretruecolor); + + if (!requiretruecolor) + { + requiretruecolor = bComplex; + + if (!requiretruecolor) for (int i = 0; i < NumParts; ++i) + { + if (Parts[i].op != OP_COPY) requiretruecolor = true; + } + } + for (int i = 0; i < NumParts; ++i) + { + Parts[i].Image->CollectForPrecache(info, requiretruecolor); + } +} + + diff --git a/src/textures/formats/multipatchtexture.h b/src/textures/formats/multipatchtexture.h index 28ff8dcc34..bda492b84f 100644 --- a/src/textures/formats/multipatchtexture.h +++ b/src/textures/formats/multipatchtexture.h @@ -39,8 +39,10 @@ protected: // The getters must optionally redirect if it's a simple one-patch texture. int CopyPixels(FBitmap *bmp, int conversion) override; - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; void CopyToBlock(uint8_t *dest, int dwidth, int dheight, FImageSource *source, int xpos, int ypos, int rotate, const uint8_t *translation, int style); + void CollectForPrecache(PrecacheInfo &info, bool requiretruecolor); + }; diff --git a/src/textures/formats/patchtexture.cpp b/src/textures/formats/patchtexture.cpp index 7a5de9c31b..705da984a0 100644 --- a/src/textures/formats/patchtexture.cpp +++ b/src/textures/formats/patchtexture.cpp @@ -64,7 +64,7 @@ class FPatchTexture : public FImageSource bool isalpha = false; public: FPatchTexture (int lumpnum, patch_t *header, bool isalphatex); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override; void DetectBadPatches(); }; @@ -163,7 +163,7 @@ FPatchTexture::FPatchTexture (int lumpnum, patch_t * header, bool isalphatex) // //========================================================================== -TArray FPatchTexture::GetPalettedPixels(int conversion) +TArray FPatchTexture::CreatePalettedPixels(int conversion) { uint8_t *remap, remaptable[256]; int numspans; diff --git a/src/textures/formats/pcxtexture.cpp b/src/textures/formats/pcxtexture.cpp index 708f36d811..ff37ce8aab 100644 --- a/src/textures/formats/pcxtexture.cpp +++ b/src/textures/formats/pcxtexture.cpp @@ -93,7 +93,7 @@ protected: void ReadPCX8bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr); void ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr, int planes); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; }; @@ -358,7 +358,7 @@ void FPCXTexture::ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr // //========================================================================== -TArray FPCXTexture::GetPalettedPixels(int conversion) +TArray FPCXTexture::CreatePalettedPixels(int conversion) { uint8_t PaletteMap[256]; PCXHeader header; diff --git a/src/textures/formats/pngtexture.cpp b/src/textures/formats/pngtexture.cpp index 37272125ef..3cdfc3a6a8 100644 --- a/src/textures/formats/pngtexture.cpp +++ b/src/textures/formats/pngtexture.cpp @@ -55,7 +55,7 @@ public: ~FPNGTexture(); int CopyPixels(FBitmap *bmp, int conversion) override; - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; protected: void ReadAlphaRemap(FileReader *lump, uint8_t *alpharemap); @@ -367,7 +367,7 @@ void FPNGTexture::ReadAlphaRemap(FileReader *lump, uint8_t *alpharemap) // //========================================================================== -TArray FPNGTexture::GetPalettedPixels(int conversion) +TArray FPNGTexture::CreatePalettedPixels(int conversion) { FileReader *lump; FileReader lfr; diff --git a/src/textures/formats/rawpagetexture.cpp b/src/textures/formats/rawpagetexture.cpp index 0edaa94979..3cc136dfce 100644 --- a/src/textures/formats/rawpagetexture.cpp +++ b/src/textures/formats/rawpagetexture.cpp @@ -54,7 +54,7 @@ class FRawPageTexture : public FImageSource int mPaletteLump = -1; public: FRawPageTexture (int lumpnum); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override; }; @@ -173,7 +173,7 @@ FRawPageTexture::FRawPageTexture (int lumpnum) // //========================================================================== -TArray FRawPageTexture::GetPalettedPixels(int conversion) +TArray FRawPageTexture::CreatePalettedPixels(int conversion) { FMemLump lump = Wads.ReadLump (SourceLump); const uint8_t *source = (const uint8_t *)lump.GetMem(); diff --git a/src/textures/formats/shadertexture.cpp b/src/textures/formats/shadertexture.cpp index 0e79ad8c79..8f817d9bc6 100644 --- a/src/textures/formats/shadertexture.cpp +++ b/src/textures/formats/shadertexture.cpp @@ -100,7 +100,7 @@ public: } } - TArray GetPalettedPixels(int conversion) override + TArray CreatePalettedPixels(int conversion) override { TArray Pix(512, true); if (conversion == luminance) diff --git a/src/textures/formats/tgatexture.cpp b/src/textures/formats/tgatexture.cpp index cfb0f48903..1a26f60361 100644 --- a/src/textures/formats/tgatexture.cpp +++ b/src/textures/formats/tgatexture.cpp @@ -85,7 +85,7 @@ public: protected: void ReadCompressed(FileReader &lump, uint8_t * buffer, int bytesperpixel); - TArray GetPalettedPixels(int conversion) override; + TArray CreatePalettedPixels(int conversion) override; }; //========================================================================== @@ -179,7 +179,7 @@ void FTGATexture::ReadCompressed(FileReader &lump, uint8_t * buffer, int bytespe // //========================================================================== -TArray FTGATexture::GetPalettedPixels(int conversion) +TArray FTGATexture::CreatePalettedPixels(int conversion) { uint8_t PaletteMap[256]; auto lump = Wads.OpenLumpReader (SourceLump); diff --git a/src/textures/image.cpp b/src/textures/image.cpp index c5d29ae5f4..8590217708 100644 --- a/src/textures/image.cpp +++ b/src/textures/image.cpp @@ -43,6 +43,24 @@ FMemArena FImageSource::ImageArena(32768); TArrayFImageSource::ImageForLump; int FImageSource::NextID; +static PrecacheInfo precacheInfo; + +struct PrecacheDataPaletted +{ + TArray Pixels; + int RefCount; + int ImageID; +}; + +struct PrecacheDataRgba +{ + FBitmap Pixels; + int ImageID; +}; + +// TMap doesn't handle this kind of data well. std::map neither. The linear search is still faster, even for a few 100 entries because it doesn't have to access the heap as often.. +TArray precacheDataPaletted; +TArray precacheDataRgba; //=========================================================================== // @@ -50,13 +68,97 @@ int FImageSource::NextID; // //=========================================================================== -TArray FImageSource::GetPalettedPixels(int conversion) +TArray FImageSource::CreatePalettedPixels(int conversion) { TArray Pixels(Width * Height, true); memset(Pixels.Data(), 0, Width * Height); return Pixels; } +PalettedPixels FImageSource::GetCachedPalettedPixels(int conversion) +{ + PalettedPixels ret; + + FString name; + Wads.GetLumpName(name, SourceLump); + if (name.CompareNoCase("W_136") == 0) + { + int a = 0; + } + + std::pair *info = nullptr; + auto imageID = ImageID; + + // Do we have this image in the cache? + unsigned index = precacheDataPaletted.FindEx([=](PrecacheDataPaletted &entry) { return entry.ImageID == imageID; }); + if (index < precacheDataPaletted.Size()) + { + auto cache = &precacheDataPaletted[index]; + + if (cache->RefCount > 1) + { + Printf("returning reference to %s, refcount = %d\n", name.GetChars(), cache->RefCount); + ret.Pixels.Set(cache->Pixels.Data(), cache->Pixels.Size()); + cache->RefCount--; + } + else if (cache->Pixels.Size() > 0) + { + Printf("returning contents of %s, refcount = %d\n", name.GetChars(), cache->RefCount); + ret.PixelStore = std::move(cache->Pixels); + ret.Pixels.Set(ret.PixelStore.Data(), ret.PixelStore.Size()); + precacheDataPaletted.Delete(index); + } + else + { + Printf("something bad happened for %s, refcount = %d\n", name.GetChars(), cache->RefCount); + } + } + else + { + // The image wasn't cached. Now there's two possibilities: + auto info = precacheInfo.CheckKey(ImageID); + if (!info || info->second <= 1 || conversion != normal) + { + // This is either the only copy needed or some access outside the caching block. In these cases create a new one and directly return it. + Printf("returning fresh copy of %s\n", name.GetChars()); + ret.PixelStore = CreatePalettedPixels(conversion); + ret.Pixels.Set(ret.PixelStore.Data(), ret.PixelStore.Size()); + } + else + { + Printf("creating cached entry for %s, refcount = %d\n", name.GetChars(), info->second); + // This is the first time it gets accessed and needs to be placed in the cache. + PrecacheDataPaletted *pdp = &precacheDataPaletted[precacheDataPaletted.Reserve(1)]; + + pdp->ImageID = imageID; + pdp->RefCount = info->second - 1; + info->second = 0; + pdp->Pixels = CreatePalettedPixels(normal); + ret.Pixels.Set(pdp->Pixels.Data(), pdp->Pixels.Size()); + } + } + return ret; +} + +TArray FImageSource::GetPalettedPixels(int conversion) +{ + auto pix = GetCachedPalettedPixels(conversion); + if (pix.ownsPixels()) + { + // return the pixel store of the returned data directly if this was the last reference. + auto array = std::move(pix.PixelStore); + return array; + } + else + { + // If there are pending references, make a copy. + TArray array(pix.Pixels.Size(), true); + memcpy(array.Data(), pix.Pixels.Data(), array.Size()); + return array; + } +} + + //=========================================================================== // @@ -75,7 +177,7 @@ int FImageSource::CopyPixels(FBitmap *bmp, int conversion) if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source. PalEntry *palette = screen->GetPalette(); for(int i=1;i<256;i++) palette[i].a = 255; // set proper alpha values - auto ppix = GetPalettedPixels(conversion); // should use composition cache + auto ppix = CreatePalettedPixels(conversion); bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, palette, nullptr); for(int i=1;i<256;i++) palette[i].a = 0; return 0; @@ -83,11 +185,54 @@ int FImageSource::CopyPixels(FBitmap *bmp, int conversion) int FImageSource::CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap) { - auto ppix = GetPalettedPixels(false); // should use composition cache + auto ppix = CreatePalettedPixels(false); bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, remap, nullptr); return 0; } +//========================================================================== +// +// +// +//========================================================================== + +void FImageSource::CollectForPrecache(PrecacheInfo &info, bool requiretruecolor) +{ + auto val = info.CheckKey(ImageID); + bool tc = requiretruecolor || V_IsTrueColor(); + if (val) + { + val->first += tc; + val->second += !tc; + } + else + { + auto pair = std::make_pair(tc, !tc); + info.Insert(ImageID, pair); + } +} + +void FImageSource::BeginPrecaching() +{ + precacheInfo.Clear(); +} + +void FImageSource::EndPrecaching() +{ + precacheDataPaletted.Clear(); + precacheDataRgba.Clear(); +} + +void FImageSource::RegisterForPrecache(FImageSource *img) +{ + img->CollectForPrecache(precacheInfo); +} + +FBitmap FImageSource::GetPixelsWithCache(int conversion) +{ + return FBitmap(); +} + //========================================================================== // diff --git a/src/textures/image.h b/src/textures/image.h index 7101c28162..7c976f4510 100644 --- a/src/textures/image.h +++ b/src/textures/image.h @@ -5,6 +5,21 @@ #include "textures/bitmap.h" #include "memarena.h" +class FImageSource; +using PrecacheInfo = TMap>; + +struct PalettedPixels +{ + friend class FImageSource; + TArrayView Pixels; +private: + TArray PixelStore; + + bool ownsPixels() const + { + return Pixels.Data() == PixelStore.Data(); + } +}; // This represents a naked image. It has no high level logic attached to it. // All it can do is provide raw image data to its users. @@ -41,13 +56,27 @@ public: bool bMasked = true; // Image (might) have holes (Assume true unless proven otherwise!) int8_t bTranslucent = -1; // Image has pixels with a non-0/1 value. (-1 means the user needs to do a real check) - // Returns the whole texture, paletted and true color versions respectively. - virtual TArray GetPalettedPixels(int conversion); // 'noremap0' will only be looked at by FPatchTexture and forwarded by FMultipatchTexture. + + // 'noremap0' will only be looked at by FPatchTexture and forwarded by FMultipatchTexture. + + // Always creates a new pixel buffer for the texture + virtual TArray CreatePalettedPixels(int conversion); + + // Either returns a reference to the cache, or a newly created item. The return of this has to be considered transient. If you need to store the result, use GetPalettedPixels + PalettedPixels GetCachedPalettedPixels(int conversion); + + // tries to get a buffer from the cache. If not available, create a new one. If further references are pending, create a copy. + TArray GetPalettedPixels(int conversion); + virtual int CopyPixels(FBitmap *bmp, int conversion); // This will always ignore 'luminance'. int CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap); static void ClearImages() { ImageArena.FreeAll(); ImageForLump.Clear(); NextID = 0; } static FImageSource * FImageSource::GetImage(int lumpnum, ETextureType usetype); + // These functions either allocate a new buffer or reuse the last one, if its reference count was greater than 1 when last used. + FBitmap GetPixelsWithCache(int conversion); + + // Conversion option enum EType { @@ -59,9 +88,6 @@ public: FImageSource(int sourcelump = -1) : SourceLump(sourcelump) { ImageID = ++NextID; } virtual ~FImageSource() {} - // Creates an image from the given lump. - static FImageSource *CreateImageSource(int lumpnum); - int GetWidth() const { return Width; @@ -91,6 +117,11 @@ public: { return bUseGamePalette; } + + virtual void CollectForPrecache(PrecacheInfo &info, bool requiretruecolor = false); + static void BeginPrecaching(); + static void EndPrecaching(); + static void RegisterForPrecache(FImageSource *img); }; //========================================================================== diff --git a/src/textures/textures.h b/src/textures/textures.h index ef154a9f63..72bef46ea4 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -307,14 +307,6 @@ public: /*virtual*/ FBitmap GetBgraBitmap(PalEntry *remap, int *trans = nullptr); public: - /* - static void FlipSquareBlock (uint8_t *block, int x, int y); - static void FlipSquareBlockBgra (uint32_t *block, int x, int y); - static void FlipSquareBlockRemap (uint8_t *block, int x, int y, const uint8_t *remap); - static void FlipNonSquareBlock (uint8_t *blockto, const uint8_t *blockfrom, int x, int y, int srcpitch); - static void FlipNonSquareBlockBgra (uint32_t *blockto, const uint32_t *blockfrom, int x, int y, int srcpitch); - static void FlipNonSquareBlockRemap (uint8_t *blockto, const uint8_t *blockfrom, int x, int y, int srcpitch, const uint8_t *remap); - */ static bool SmoothEdges(unsigned char * buffer,int w, int h); static PalEntry averageColor(const uint32_t *data, int size, int maxout); @@ -436,7 +428,7 @@ protected: virtual void ResolvePatches() {} - virtual void SetFrontSkyLayer(); + void SetFrontSkyLayer(); static void InitGrayMap(); @@ -645,11 +637,12 @@ private: TArray FirstTextureForFile; TArray > BuildTileData; - TArray mAnimations; TArray mSwitchDefs; TArray mAnimatedDoors; public: + TArray mAnimations; + bool HasGlobalBrightmap; FRemapTable GlobalBrightmap; short sintable[2048]; // for texture warping diff --git a/src/v_video.h b/src/v_video.h index 1cba846f02..918c572e36 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -428,6 +428,7 @@ public: virtual void CleanForRestart() {} virtual void SetTextureFilterMode() {} virtual IHardwareTexture *CreateHardwareTexture(FTexture *tex) { return nullptr; } + virtual bool CheckPrecacheMaterial(FMaterial *mat) { return true; } virtual void PrecacheMaterial(FMaterial *mat, int translation) {} virtual FModelRenderer *CreateModelRenderer(int mli) { return nullptr; } virtual void UnbindTexUnit(int no) {}