From a663f71a9f16a2418e087e27d9fcc0af54b8189e Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 12 Mar 2017 22:53:20 +0100 Subject: [PATCH] - Added thread safety to texture loading in the software renderer --- src/swrenderer/line/r_walldraw.cpp | 2 ++ src/swrenderer/plane/r_flatplane.cpp | 2 +- src/swrenderer/plane/r_skyplane.cpp | 4 +-- src/swrenderer/plane/r_slopeplane.cpp | 2 +- src/swrenderer/r_renderthread.cpp | 34 ++++++++++++++++++++++ src/swrenderer/r_renderthread.h | 3 ++ src/swrenderer/r_swcanvas.cpp | 2 +- src/swrenderer/viewport/r_skydrawer.cpp | 10 ++++--- src/swrenderer/viewport/r_skydrawer.h | 4 +-- src/swrenderer/viewport/r_spandrawer.cpp | 6 ++-- src/swrenderer/viewport/r_spandrawer.h | 2 +- src/swrenderer/viewport/r_spritedrawer.cpp | 6 +++- 12 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/swrenderer/line/r_walldraw.cpp b/src/swrenderer/line/r_walldraw.cpp index d5eacce397..06b096ac1a 100644 --- a/src/swrenderer/line/r_walldraw.cpp +++ b/src/swrenderer/line/r_walldraw.cpp @@ -515,6 +515,8 @@ namespace swrenderer this->rw_pic = pic; this->mask = mask; + Thread->PrepareTexture(pic); + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) { ProcessWallNP2(walltop, wallbottom, texturemid, swall, lwall, top, bottom); diff --git a/src/swrenderer/plane/r_flatplane.cpp b/src/swrenderer/plane/r_flatplane.cpp index ed648e6903..159d4c45ad 100644 --- a/src/swrenderer/plane/r_flatplane.cpp +++ b/src/swrenderer/plane/r_flatplane.cpp @@ -59,7 +59,7 @@ namespace swrenderer } drawerargs.SetSolidColor(3); - drawerargs.SetTexture(Thread->Viewport.get(), texture); + drawerargs.SetTexture(Thread, texture); double planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); double xstep, ystep, leftxfrac, leftyfrac, rightxfrac, rightyfrac; diff --git a/src/swrenderer/plane/r_skyplane.cpp b/src/swrenderer/plane/r_skyplane.cpp index 534a46890e..40115a07df 100644 --- a/src/swrenderer/plane/r_skyplane.cpp +++ b/src/swrenderer/plane/r_skyplane.cpp @@ -198,8 +198,8 @@ namespace swrenderer angle1 = (uint32_t)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); angle2 = (uint32_t)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); - drawerargs.SetFrontTexture(viewport, frontskytex, angle1); - drawerargs.SetBackTexture(viewport, backskytex, angle2); + drawerargs.SetFrontTexture(Thread, frontskytex, angle1); + drawerargs.SetBackTexture(Thread, backskytex, angle2); drawerargs.SetTextureVStep(uv_step); drawerargs.SetTextureVPos(uv_pos); drawerargs.SetDest(viewport, start_x, y1); diff --git a/src/swrenderer/plane/r_slopeplane.cpp b/src/swrenderer/plane/r_slopeplane.cpp index 641c57394c..90aaf9cea9 100644 --- a/src/swrenderer/plane/r_slopeplane.cpp +++ b/src/swrenderer/plane/r_slopeplane.cpp @@ -80,7 +80,7 @@ namespace swrenderer auto viewport = Thread->Viewport.get(); drawerargs.SetSolidColor(3); - drawerargs.SetTexture(Thread->Viewport.get(), texture); + drawerargs.SetTexture(Thread, texture); lxscale = _xscale * ifloatpow2[drawerargs.TextureWidthBits()]; lyscale = _yscale * ifloatpow2[drawerargs.TextureHeightBits()]; diff --git a/src/swrenderer/r_renderthread.cpp b/src/swrenderer/r_renderthread.cpp index 73c944a61c..5dc58446e2 100644 --- a/src/swrenderer/r_renderthread.cpp +++ b/src/swrenderer/r_renderthread.cpp @@ -88,4 +88,38 @@ namespace swrenderer else return pal_drawers.get(); } + + void RenderThread::PrepareTexture(FTexture *texture) + { + 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. + + static std::mutex loadmutex; + loadmutex.lock(); + try + { + texture->GetPixels(); + const FTexture::Span *spans; + texture->GetColumn(0, &spans); + if (Viewport->RenderTarget->IsBgra()) + { + texture->GetPixelsBgra(); + texture->GetColumnBgra(0, &spans); + } + loadmutex.unlock(); + } + catch (...) + { + loadmutex.unlock(); + throw; + } + } } diff --git a/src/swrenderer/r_renderthread.h b/src/swrenderer/r_renderthread.h index 4a671b07d3..8c3823c2d8 100644 --- a/src/swrenderer/r_renderthread.h +++ b/src/swrenderer/r_renderthread.h @@ -80,6 +80,9 @@ namespace swrenderer short cliptop[MAXWIDTH]; SWPixelFormatDrawers *Drawers(RenderViewport *viewport); + + // Make sure texture can accessed safely + void PrepareTexture(FTexture *texture); private: std::unique_ptr tc_drawers; diff --git a/src/swrenderer/r_swcanvas.cpp b/src/swrenderer/r_swcanvas.cpp index df17ba5abd..c55c9a7379 100644 --- a/src/swrenderer/r_swcanvas.cpp +++ b/src/swrenderer/r_swcanvas.cpp @@ -274,7 +274,7 @@ void SWCanvas::FillSimplePoly(DCanvas *canvas, FTexture *tex, FVector2 *points, // Setup constant texture mapping parameters. SpanDrawerArgs drawerargs; - drawerargs.SetTexture(viewport, tex); + drawerargs.SetTexture(&thread, tex); if (colormap) drawerargs.SetLight(colormap, 0, clamp(shade >> FRACBITS, 0, NUMCOLORMAPS - 1)); else diff --git a/src/swrenderer/viewport/r_skydrawer.cpp b/src/swrenderer/viewport/r_skydrawer.cpp index df93c6eed7..e2ce020e07 100644 --- a/src/swrenderer/viewport/r_skydrawer.cpp +++ b/src/swrenderer/viewport/r_skydrawer.cpp @@ -34,9 +34,10 @@ namespace swrenderer dc_viewport = viewport; } - void SkyDrawerArgs::SetFrontTexture(RenderViewport *viewport, FTexture *texture, uint32_t column) + void SkyDrawerArgs::SetFrontTexture(RenderThread *thread, FTexture *texture, uint32_t column) { - if (viewport->RenderTarget->IsBgra()) + thread->PrepareTexture(texture); + if (thread->Viewport->RenderTarget->IsBgra()) { dc_source = (const uint8_t *)texture->GetColumnBgra(column, nullptr); dc_sourceheight = texture->GetHeight(); @@ -48,14 +49,15 @@ namespace swrenderer } } - void SkyDrawerArgs::SetBackTexture(RenderViewport *viewport, FTexture *texture, uint32_t column) + void SkyDrawerArgs::SetBackTexture(RenderThread *thread, FTexture *texture, uint32_t column) { + thread->PrepareTexture(texture); if (texture == nullptr) { dc_source2 = nullptr; dc_sourceheight2 = 1; } - else if (viewport->RenderTarget->IsBgra()) + else if (thread->Viewport->RenderTarget->IsBgra()) { dc_source2 = (const uint8_t *)texture->GetColumnBgra(column, nullptr); dc_sourceheight2 = texture->GetHeight(); diff --git a/src/swrenderer/viewport/r_skydrawer.h b/src/swrenderer/viewport/r_skydrawer.h index 2f352e939d..4d7d448396 100644 --- a/src/swrenderer/viewport/r_skydrawer.h +++ b/src/swrenderer/viewport/r_skydrawer.h @@ -15,8 +15,8 @@ namespace swrenderer public: void SetDest(RenderViewport *viewport, int x, int y); void SetCount(int count) { dc_count = count; } - void SetFrontTexture(RenderViewport *viewport, FTexture *texture, uint32_t column); - void SetBackTexture(RenderViewport *viewport, FTexture *texture, uint32_t column); + void SetFrontTexture(RenderThread *thread, FTexture *texture, uint32_t column); + void SetBackTexture(RenderThread *thread, FTexture *texture, uint32_t column); void SetTextureVPos(uint32_t texturefrac) { dc_texturefrac = texturefrac; } void SetTextureVStep(uint32_t iscale) { dc_iscale = iscale; } void SetSolidTop(uint32_t color) { solid_top = color; } diff --git a/src/swrenderer/viewport/r_spandrawer.cpp b/src/swrenderer/viewport/r_spandrawer.cpp index afe1cbc66c..8900dfe25b 100644 --- a/src/swrenderer/viewport/r_spandrawer.cpp +++ b/src/swrenderer/viewport/r_spandrawer.cpp @@ -22,8 +22,10 @@ namespace swrenderer spanfunc = &SWPixelFormatDrawers::DrawSpan; } - void SpanDrawerArgs::SetTexture(RenderViewport *viewport, FTexture *tex) + void SpanDrawerArgs::SetTexture(RenderThread *thread, FTexture *tex) { + thread->PrepareTexture(tex); + tex->GetWidth(); ds_xbits = tex->WidthBits; ds_ybits = tex->HeightBits; @@ -36,7 +38,7 @@ namespace swrenderer ds_ybits--; } - ds_source = viewport->RenderTarget->IsBgra() ? (const uint8_t*)tex->GetPixelsBgra() : tex->GetPixels(); + ds_source = thread->Viewport->RenderTarget->IsBgra() ? (const uint8_t*)tex->GetPixelsBgra() : tex->GetPixels(); ds_source_mipmapped = tex->Mipmapped() && tex->GetWidth() > 1 && tex->GetHeight() > 1; } diff --git a/src/swrenderer/viewport/r_spandrawer.h b/src/swrenderer/viewport/r_spandrawer.h index cba3ab0d77..4cb131b374 100644 --- a/src/swrenderer/viewport/r_spandrawer.h +++ b/src/swrenderer/viewport/r_spandrawer.h @@ -19,7 +19,7 @@ namespace swrenderer void SetDestY(RenderViewport *viewport, int y) { ds_viewport = viewport; ds_y = y; } void SetDestX1(int x) { ds_x1 = x; } void SetDestX2(int x) { ds_x2 = x; } - void SetTexture(RenderViewport *viewport, FTexture *tex); + void SetTexture(RenderThread *thread, FTexture *tex); void SetTextureLOD(double lod) { ds_lod = lod; } void SetTextureUPos(dsfixed_t xfrac) { ds_xfrac = xfrac; } void SetTextureVPos(dsfixed_t yfrac) { ds_yfrac = yfrac; } diff --git a/src/swrenderer/viewport/r_spritedrawer.cpp b/src/swrenderer/viewport/r_spritedrawer.cpp index cd5bc44d7d..6226c9696a 100644 --- a/src/swrenderer/viewport/r_spritedrawer.cpp +++ b/src/swrenderer/viewport/r_spritedrawer.cpp @@ -49,7 +49,7 @@ namespace swrenderer return; auto viewport = thread->Viewport.get(); - + // Handle the linear filtered version in a different function to reduce chances of merge conflicts from zdoom. if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input) // To do: add support to R_DrawColumnHoriz_rgba { @@ -62,6 +62,8 @@ namespace swrenderer dc_iscale = iscale; dc_textureheight = tex->GetHeight(); + thread->PrepareTexture(tex); + const FTexture::Span *span; const uint8_t *column; if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input) @@ -132,6 +134,8 @@ namespace swrenderer dc_x = x; dc_iscale = iscale; + thread->PrepareTexture(tex); + // Normalize to 0-1 range: double uv_stepd = FIXED2DBL(dc_iscale); double v_step = uv_stepd / tex->GetHeight();