Skip to content

Commit

Permalink
Fix texture loading race condition and improve performance by only lo…
Browse files Browse the repository at this point in the history
…cking the load mutex if data hasn't already been updated for this frame
  • Loading branch information
dpjudas authored and madame-rachelle committed Mar 26, 2021
1 parent d1f47af commit 61d49a2
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 111 deletions.
2 changes: 0 additions & 2 deletions src/rendering/swrenderer/line/r_walldraw.cpp
Expand Up @@ -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()))
{
Expand Down
3 changes: 0 additions & 3 deletions src/rendering/swrenderer/plane/r_skyplane.cpp
Expand Up @@ -214,9 +214,6 @@ namespace swrenderer

drawerargs.SetStyle();

Thread->PrepareTexture(frontskytex, DefaultRenderStyle());
Thread->PrepareTexture(backskytex, DefaultRenderStyle());

DrawSky(pl);
}

Expand Down
30 changes: 1 addition & 29 deletions src/rendering/swrenderer/r_renderthread.cpp
Expand Up @@ -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)
{
Expand Down
3 changes: 0 additions & 3 deletions src/rendering/swrenderer/r_renderthread.h
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions src/rendering/swrenderer/scene/r_scene.cpp
Expand Up @@ -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
Expand Down
71 changes: 18 additions & 53 deletions src/rendering/swrenderer/textures/r_swtexture.cpp
Expand Up @@ -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))
{
Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -197,60 +191,31 @@ const uint32_t *FSoftwareTexture::GetPixelsBgra()
//
//==========================================================================

const uint8_t *FSoftwareTexture::GetColumn(int index, unsigned int column, const FSoftwareTextureSpan **spans_out)
{
auto Pixeldata = GetPixels(index);
if ((unsigned)column >= (unsigned)GetPhysicalWidth())
{
if (WidthMask + 1 == GetPhysicalWidth())
{
column &= WidthMask;
}
else
{
column %= GetPhysicalWidth();
}
}
if (spans_out != nullptr)
{
if (Spandata[index] == nullptr)
{
Spandata[index] = CreateSpans(Pixeldata);
}
*spans_out = Spandata[index][column];
}
return Pixeldata + column * GetPhysicalHeight();
}

//==========================================================================
//
//
//
//==========================================================================
int FSoftwareTexture::CurrentUpdate = 0;
namespace swrenderer { extern std::mutex loadmutex; }

const uint32_t *FSoftwareTexture::GetColumnBgra(unsigned int column, const FSoftwareTextureSpan **spans_out)
void FSoftwareTexture::UpdatePixels(int index)
{
auto Pixeldata = GetPixelsBgra();
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();
}
}
if (spans_out != nullptr)
{
if (Spandata[2] == nullptr)
{
Spandata[2] = CreateSpans(Pixeldata);
const uint32_t* Pixeldata = GetPixelsBgraLocked();
if (Spandata[index] == nullptr)
Spandata[index] = CreateSpans(Pixeldata);
Unlockeddata[index].Pixels = Pixeldata;
Unlockeddata[index].LastUpdate = CurrentUpdate;
}
*spans_out = Spandata[2][column];
}
return Pixeldata + column * GetPhysicalHeight();
}

//==========================================================================
Expand Down
80 changes: 72 additions & 8 deletions src/rendering/swrenderer/textures/r_swtexture.h
Expand Up @@ -20,6 +20,11 @@ class FSoftwareTexture : public ISoftwareTexture
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;
Expand Down Expand Up @@ -94,6 +99,7 @@ class FSoftwareTexture : public ISoftwareTexture
{
Pixels.Reset();
PixelsBgra.Reset();
for (auto& d : Unlockeddata) d = {};
}

// Returns true if the next call to GetPixels() will return an image different from the
Expand All @@ -110,16 +116,69 @@ class FSoftwareTexture : public ISoftwareTexture
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)
{
Expand All @@ -139,6 +198,11 @@ class FSoftwareTexture : public ISoftwareTexture
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.
Expand All @@ -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();

Expand All @@ -179,8 +243,8 @@ class FSWCanvasTexture : public FSoftwareTexture
~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);
Expand Down
4 changes: 2 additions & 2 deletions src/rendering/swrenderer/textures/swcanvastexture.cpp
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions src/rendering/swrenderer/textures/warptexture.cpp
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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;
Expand All @@ -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();
Expand Down
1 change: 0 additions & 1 deletion src/rendering/swrenderer/things/r_decal.cpp
Expand Up @@ -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);
}

Expand Down
2 changes: 0 additions & 2 deletions src/rendering/swrenderer/things/r_sprite.cpp
Expand Up @@ -270,8 +270,6 @@ namespace swrenderer
portalfloorclip[x] = mfloorclip[x];
}

thread->PrepareTexture(pic, RenderStyle);

ProjectedWallLight mlight;
mlight.SetSpriteLight();

Expand Down
2 changes: 0 additions & 2 deletions src/rendering/swrenderer/things/r_wallsprite.cpp
Expand Up @@ -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++)
Expand Down
2 changes: 0 additions & 2 deletions src/rendering/swrenderer/viewport/r_spandrawer.cpp
Expand Up @@ -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();
Expand Down

0 comments on commit 61d49a2

Please sign in to comment.