Fix texture loading race condition and improve performance by only locking the load mutex if data hasn't already been updated for this frame

This commit is contained in:
Magnus Norddahl 2021-02-17 01:43:22 +01:00 committed by Rachael Alexanderson
parent d1f47afd96
commit 61d49a2007
13 changed files with 99 additions and 112 deletions

View file

@ -81,8 +81,6 @@ namespace swrenderer
mLight.SetColormap(lightsector, curline);
mLight.SetLightLeft(Thread, WallC);
Thread->PrepareTexture(pic, DefaultRenderStyle()); // Get correct render style? Shaded won't get here.
CameraLight* cameraLight = CameraLight::Instance();
if (cameraLight->FixedColormap() || cameraLight->FixedLightLevel() >= 0 || !(lightsector->e && lightsector->e->XFloor.lightlist.Size()))
{

View file

@ -214,9 +214,6 @@ namespace swrenderer
drawerargs.SetStyle();
Thread->PrepareTexture(frontskytex, DefaultRenderStyle());
Thread->PrepareTexture(backskytex, DefaultRenderStyle());
DrawSky(pl);
}

View file

@ -92,35 +92,7 @@ namespace swrenderer
return pal_drawers.get();
}
static std::mutex loadmutex;
void RenderThread::PrepareTexture(FSoftwareTexture *texture, FRenderStyle style)
{
if (texture == nullptr)
return;
// Textures may not have loaded/refreshed yet. The shared code doing
// this is not thread safe. By calling GetPixels in a mutex lock we
// make sure that only one thread is loading a texture at any given
// time.
//
// It is critical that this function is called before any direct
// calls to GetPixels for this to work.
std::unique_lock<std::mutex> lock(loadmutex);
const FSoftwareTextureSpan *spans;
if (Viewport->RenderTarget->IsBgra())
{
texture->GetPixelsBgra();
texture->GetColumnBgra(0, &spans);
}
else
{
bool alpha = !!(style.Flags & STYLEF_RedIsAlpha);
texture->GetPixels(alpha);
texture->GetColumn(alpha, 0, &spans);
}
}
std::mutex loadmutex;
std::pair<PalEntry, PalEntry> RenderThread::GetSkyCapColor(FSoftwareTexture* tex)
{

View file

@ -87,9 +87,6 @@ namespace swrenderer
SWPixelFormatDrawers *Drawers(RenderViewport *viewport);
// Make sure texture can accessed safely
void PrepareTexture(FSoftwareTexture *texture, FRenderStyle style);
// Setup poly object in a threadsafe manner
void PreparePolyObject(subsector_t *sub);

View file

@ -219,6 +219,7 @@ namespace swrenderer
Threads[i]->X2 = viewwidth * (i + 1) / numThreads;
}
run_id++;
FSoftwareTexture::CurrentUpdate = run_id;
start_lock.unlock();
// Notify threads to run

View file

@ -119,7 +119,7 @@ void FSoftwareTexture::CalcBitSize ()
//
//==========================================================================
const uint8_t *FSoftwareTexture::GetPixels(int style)
const uint8_t *FSoftwareTexture::GetPixelsLocked(int style)
{
if (Pixels.Size() == 0 || CheckModified(style))
{
@ -158,13 +158,7 @@ const uint8_t *FSoftwareTexture::GetPixels(int style)
return Pixels.Data();
}
//==========================================================================
//
//
//
//==========================================================================
const uint32_t *FSoftwareTexture::GetPixelsBgra()
const uint32_t *FSoftwareTexture::GetPixelsBgraLocked()
{
if (PixelsBgra.Size() == 0 || CheckModified(2))
{
@ -197,60 +191,31 @@ const uint32_t *FSoftwareTexture::GetPixelsBgra()
//
//==========================================================================
const uint8_t *FSoftwareTexture::GetColumn(int index, unsigned int column, const FSoftwareTextureSpan **spans_out)
int FSoftwareTexture::CurrentUpdate = 0;
namespace swrenderer { extern std::mutex loadmutex; }
void FSoftwareTexture::UpdatePixels(int index)
{
auto Pixeldata = GetPixels(index);
if ((unsigned)column >= (unsigned)GetPhysicalWidth())
std::unique_lock<std::mutex> lock(swrenderer::loadmutex);
if (Unlockeddata[index].LastUpdate != CurrentUpdate)
{
if (WidthMask + 1 == GetPhysicalWidth())
if (index != 2)
{
column &= WidthMask;
const uint8_t* Pixeldata = GetPixelsLocked(index);
if (Spandata[index] == nullptr)
Spandata[index] = CreateSpans(Pixeldata);
Unlockeddata[index].Pixels = Pixeldata;
Unlockeddata[index].LastUpdate = CurrentUpdate;
}
else
{
column %= GetPhysicalWidth();
const uint32_t* Pixeldata = GetPixelsBgraLocked();
if (Spandata[index] == nullptr)
Spandata[index] = CreateSpans(Pixeldata);
Unlockeddata[index].Pixels = Pixeldata;
Unlockeddata[index].LastUpdate = CurrentUpdate;
}
}
if (spans_out != nullptr)
{
if (Spandata[index] == nullptr)
{
Spandata[index] = CreateSpans(Pixeldata);
}
*spans_out = Spandata[index][column];
}
return Pixeldata + column * GetPhysicalHeight();
}
//==========================================================================
//
//
//
//==========================================================================
const uint32_t *FSoftwareTexture::GetColumnBgra(unsigned int column, const FSoftwareTextureSpan **spans_out)
{
auto Pixeldata = GetPixelsBgra();
if ((unsigned)column >= (unsigned)GetPhysicalWidth())
{
if (WidthMask + 1 == GetPhysicalWidth())
{
column &= WidthMask;
}
else
{
column %= GetPhysicalWidth();
}
}
if (spans_out != nullptr)
{
if (Spandata[2] == nullptr)
{
Spandata[2] = CreateSpans(Pixeldata);
}
*spans_out = Spandata[2][column];
}
return Pixeldata + column * GetPhysicalHeight();
}
//==========================================================================

View file

@ -20,6 +20,11 @@ protected:
FTexture *mSource;
TArray<uint8_t> Pixels;
TArray<uint32_t> PixelsBgra;
struct
{
const void* Pixels = nullptr;
int LastUpdate = -1;
} Unlockeddata[3];
FSoftwareTextureSpan **Spandata[3] = { };
DVector2 Scale;
uint8_t WidthBits = 0, HeightBits = 0;
@ -94,6 +99,7 @@ public:
{
Pixels.Reset();
PixelsBgra.Reset();
for (auto& d : Unlockeddata) d = {};
}
// Returns true if the next call to GetPixels() will return an image different from the
@ -110,16 +116,69 @@ public:
virtual bool Mipmapped() { return true; }
// Returns a single column of the texture
virtual const uint8_t *GetColumn(int style, unsigned int column, const FSoftwareTextureSpan **spans_out);
const uint8_t* GetColumn(int style, unsigned int column, const FSoftwareTextureSpan** spans_out)
{
column = WrapColumn(column);
const uint8_t* pixels = GetPixels(style);
if (spans_out)
*spans_out = Spandata[style][column];
return pixels + column * GetPhysicalHeight();
}
// Returns a single column of the texture, in BGRA8 format
virtual const uint32_t *GetColumnBgra(unsigned int column, const FSoftwareTextureSpan **spans_out);
const uint32_t* GetColumnBgra(unsigned int column, const FSoftwareTextureSpan** spans_out)
{
column = WrapColumn(column);
const uint32_t* pixels = GetPixelsBgra();
if (spans_out)
*spans_out = Spandata[2][column];
return pixels + column * GetPhysicalHeight();
}
unsigned int WrapColumn(unsigned int column)
{
if ((unsigned)column >= (unsigned)GetPhysicalWidth())
{
if (WidthMask + 1 == GetPhysicalWidth())
{
column &= WidthMask;
}
else
{
column %= GetPhysicalWidth();
}
}
return column;
}
// Returns the whole texture, stored in column-major order, in BGRA8 format
virtual const uint32_t *GetPixelsBgra();
const uint32_t* GetPixelsBgra()
{
int style = 2;
if (Unlockeddata[2].LastUpdate == CurrentUpdate)
{
return static_cast<const uint32_t*>(Unlockeddata[style].Pixels);
}
else
{
UpdatePixels(style);
return static_cast<const uint32_t*>(Unlockeddata[style].Pixels);
}
}
// Returns the whole texture, stored in column-major order
virtual const uint8_t *GetPixels(int style);
const uint8_t* GetPixels(int style)
{
if (Unlockeddata[style].LastUpdate == CurrentUpdate)
{
return static_cast<const uint8_t*>(Unlockeddata[style].Pixels);
}
else
{
UpdatePixels(style);
return static_cast<const uint8_t*>(Unlockeddata[style].Pixels);
}
}
const uint8_t *GetPixels(FRenderStyle style)
{
@ -139,6 +198,11 @@ public:
return GetColumn(alpha, column, spans_out);
}
static int CurrentUpdate;
void UpdatePixels(int style);
virtual const uint32_t* GetPixelsBgraLocked();
virtual const uint8_t* GetPixelsLocked(int style);
};
// A texture that returns a wiggly version of another texture.
@ -154,8 +218,8 @@ class FWarpTexture : public FSoftwareTexture
public:
FWarpTexture (FGameTexture *source, int warptype);
const uint32_t *GetPixelsBgra() override;
const uint8_t *GetPixels(int style) override;
const uint32_t *GetPixelsBgraLocked() override;
const uint8_t *GetPixelsLocked(int style) override;
bool CheckModified (int which) override;
void GenerateBgraMipmapsFast();
@ -179,8 +243,8 @@ public:
~FSWCanvasTexture();
// Returns the whole texture, stored in column-major order
const uint32_t *GetPixelsBgra() override;
const uint8_t *GetPixels(int style) override;
const uint32_t *GetPixelsBgraLocked() override;
const uint8_t *GetPixelsLocked(int style) override;
virtual void Unload() override;
void UpdatePixels(bool truecolor);

View file

@ -77,7 +77,7 @@ FSWCanvasTexture::~FSWCanvasTexture()
//
//==========================================================================
const uint8_t *FSWCanvasTexture::GetPixels(int style)
const uint8_t *FSWCanvasTexture::GetPixelsLocked(int style)
{
static_cast<FCanvasTexture*>(mSource)->NeedUpdate();
if (Canvas == nullptr)
@ -94,7 +94,7 @@ const uint8_t *FSWCanvasTexture::GetPixels(int style)
//
//==========================================================================
const uint32_t *FSWCanvasTexture::GetPixelsBgra()
const uint32_t *FSWCanvasTexture::GetPixelsBgraLocked()
{
static_cast<FCanvasTexture*>(mSource)->NeedUpdate();
if (CanvasBgra == nullptr)

View file

@ -57,7 +57,7 @@ bool FWarpTexture::CheckModified (int style)
return screen->FrameTime != GenTime[style];
}
const uint32_t *FWarpTexture::GetPixelsBgra()
const uint32_t *FWarpTexture::GetPixelsBgraLocked()
{
uint64_t time = screen->FrameTime;
uint64_t resizeMult = gl_texture_hqresizemult;
@ -67,7 +67,7 @@ const uint32_t *FWarpTexture::GetPixelsBgra()
if (gl_texture_hqresizemode == 0 || gl_texture_hqresizemult < 1 || !(gl_texture_hqresize_targets & 1))
resizeMult = 1;
auto otherpix = FSoftwareTexture::GetPixelsBgra();
auto otherpix = FSoftwareTexture::GetPixelsBgraLocked();
WarpedPixelsRgba.Resize(unsigned(GetWidth() * GetHeight() * resizeMult * resizeMult * 4 / 3 + 1));
WarpBuffer(WarpedPixelsRgba.Data(), otherpix, int(GetWidth() * resizeMult), int(GetHeight() * resizeMult), WidthOffsetMultiplier, HeightOffsetMultiplier, time, mTexture->GetShaderSpeed(), bWarped);
GenerateBgraMipmapsFast();
@ -78,7 +78,7 @@ const uint32_t *FWarpTexture::GetPixelsBgra()
}
const uint8_t *FWarpTexture::GetPixels(int index)
const uint8_t *FWarpTexture::GetPixelsLocked(int index)
{
uint64_t time = screen->FrameTime;
uint64_t resizeMult = gl_texture_hqresizemult;
@ -88,7 +88,7 @@ const uint8_t *FWarpTexture::GetPixels(int index)
if (gl_texture_hqresizemode == 0 || gl_texture_hqresizemult < 1 || !(gl_texture_hqresize_targets & 1))
resizeMult = 1;
const uint8_t *otherpix = FSoftwareTexture::GetPixels(index);
const uint8_t *otherpix = FSoftwareTexture::GetPixelsLocked(index);
WarpedPixels[index].Resize(unsigned(GetWidth() * GetHeight() * resizeMult * resizeMult));
WarpBuffer(WarpedPixels[index].Data(), otherpix, int(GetWidth() * resizeMult), int(GetHeight() * resizeMult), WidthOffsetMultiplier, HeightOffsetMultiplier, time, mTexture->GetShaderSpeed(), bWarped);
FreeAllSpans();

View file

@ -240,7 +240,6 @@ namespace swrenderer
bool visible = drawerargs.SetStyle(thread->Viewport.get(), decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor, cmlight);
if (visible)
{
thread->PrepareTexture(WallSpriteTile, decal->RenderStyle);
drawerargs.DrawMasked(thread, zpos + WallSpriteTile->GetTopOffset(0) * decal->ScaleY, decal->ScaleY, decal->RenderFlags & RF_XFLIP, decal->RenderFlags & RF_YFLIP, WallC, clipper->x1, clipper->x2, light, WallSpriteTile, mfloorclip, mceilingclip, decal->RenderStyle);
}

View file

@ -270,8 +270,6 @@ namespace swrenderer
portalfloorclip[x] = mfloorclip[x];
}
thread->PrepareTexture(pic, RenderStyle);
ProjectedWallLight mlight;
mlight.SetSpriteLight();

View file

@ -176,8 +176,6 @@ namespace swrenderer
// Draw it
auto WallSpriteTile = spr->pic;
thread->PrepareTexture(WallSpriteTile, spr->RenderStyle);
RenderTranslucentPass* translucentPass = thread->TranslucentPass.get();
short floorclip[MAXWIDTH];
for (int x = x1; x < x2; x++)

View file

@ -32,8 +32,6 @@ namespace swrenderer
void SpanDrawerArgs::SetTexture(RenderThread *thread, FSoftwareTexture *tex)
{
thread->PrepareTexture(tex, DefaultRenderStyle());
ds_texwidth = tex->GetPhysicalWidth();
ds_texheight = tex->GetPhysicalHeight();
ds_xbits = tex->GetWidthBits();