Fix frame image race condition + refactor

- In the connection state, retrieve the FrameImage while owning the data lock.
- Use actual image data pointer as caching key instead of the address of ImageCache which may change during executation (unstable).
- Fixes scale Messages image tooltip scale.
- Free the connection image
This commit is contained in:
Clément Grégoire
2025-12-18 15:44:58 +01:00
parent 8b2019830a
commit 0eb9f4acb6
6 changed files with 40 additions and 68 deletions

View File

@@ -126,7 +126,9 @@ View::~View()
if( m_compare.loadThread.joinable() ) m_compare.loadThread.join();
if( m_saveThread.joinable() ) m_saveThread.join();
if( m_frameTexture ) FreeTexture( m_frameTexture, m_cbMainThread );
if( m_FrameTextureCache.textureId ) FreeTexture( m_FrameTextureCache.textureId, m_cbMainThread );
if( m_FrameTextureCacheConnection.textureId ) FreeTexture( m_FrameTextureCacheConnection.textureId, m_cbMainThread );
if( m_playback.texture ) FreeTexture( m_playback.texture, m_cbMainThread );
}
@@ -1359,6 +1361,24 @@ bool View::DrawImpl()
return keepOpen;
}
void View::DrawFrameImage( FrameImageCache& cache, const FrameImage& fi, float scale )
{
if ( fi.ptr != cache.dataPtr )
{
if( !cache.textureId ) cache.textureId = MakeTexture();
UpdateTexture( cache.textureId, m_worker.UnpackFrameImage( fi ), fi.w, fi.h );
cache.dataPtr = fi.ptr;
}
if( fi.flip )
{
ImGui::Image( cache.textureId, ImVec2( fi.w * scale, fi.h * scale ), ImVec2( 0, 1 ), ImVec2( 1, 0 ) );
}
else
{
ImGui::Image( cache.textureId, ImVec2( fi.w * scale, fi.h * scale ) );
}
}
void View::DrawTextEditor()
{
const auto scale = GetScale();

View File

@@ -106,6 +106,12 @@ class View
int64_t total;
uint16_t threadNum;
};
struct FrameImageCache
{
ImTextureID textureId = 0;
const void* dataPtr = nullptr;
};
public:
struct PlotView
@@ -247,6 +253,7 @@ private:
void Achieve( const char* id );
bool DrawImpl();
void DrawFrameImage( FrameImageCache& cache, const FrameImage& fi, float scale = GetScale() );
void DrawNotificationArea();
bool DrawConnection();
void DrawFrames();
@@ -619,11 +626,8 @@ private:
std::atomic<size_t> m_srcFileBytes { 0 };
std::atomic<size_t> m_dstFileBytes { 0 };
ImTextureID m_frameTexture = 0;
const void* m_frameTexturePtr = nullptr;
ImTextureID m_frameTextureConn = 0;
const void* m_frameTextureConnPtr = nullptr;
FrameImageCache m_FrameTextureCache;
FrameImageCache m_FrameTextureCacheConnection;
std::vector<std::unique_ptr<Annotation>> m_annotations;
UserData m_userData;

View File

@@ -77,6 +77,7 @@ bool View::DrawConnection()
}
}
FrameImage lastFrameImage{};
{
Worker::MainThreadDataLockGuard lock = m_worker.ObtainLockForMainThread();
ImGui::SameLine();
@@ -91,29 +92,16 @@ bool View::DrawConnection()
ImGui::Text( "%6.1f", fps );
ImGui::SameLine();
TextFocused( "Frame time:", TimeToString( dt ) );
}
}
const auto& fis = m_worker.GetFrameImages();
// Keep a copy here since the worker may modify the frame images vector while we do not own the lock
if( !fis.empty() ) lastFrameImage = *fis.back();
}
const auto& fis = m_worker.GetFrameImages();
if( !fis.empty() )
if( lastFrameImage.ptr.get() )
{
const auto fiScale = scale * 0.5f;
const auto& fi = fis.back();
if( fi != m_frameTextureConnPtr )
{
if( !m_frameTextureConn ) m_frameTextureConn = MakeTexture();
UpdateTexture( m_frameTextureConn, m_worker.UnpackFrameImage( *fi ), fi->w, fi->h );
m_frameTextureConnPtr = fi;
}
ImGui::Separator();
if( fi->flip )
{
ImGui::Image( m_frameTextureConn, ImVec2( fi->w * fiScale, fi->h * fiScale ), ImVec2( 0, 1 ), ImVec2( 1, 0 ) );
}
else
{
ImGui::Image( m_frameTextureConn, ImVec2( fi->w * fiScale, fi->h * fiScale ) );
}
DrawFrameImage( m_FrameTextureCacheConnection, lastFrameImage, scale * 0.5f );
}
ImGui::Separator();

View File

@@ -196,21 +196,8 @@ void View::DrawFrames()
auto fi = m_worker.GetFrameImage( *m_frames, sel );
if( fi )
{
if( fi != m_frameTexturePtr )
{
if( !m_frameTexture ) m_frameTexture = MakeTexture();
UpdateTexture( m_frameTexture, m_worker.UnpackFrameImage( *fi ), fi->w, fi->h );
m_frameTexturePtr = fi;
}
ImGui::Separator();
if( fi->flip )
{
ImGui::Image( m_frameTexture, ImVec2( fi->w * scale, fi->h * scale ), ImVec2( 0, 1 ), ImVec2( 1, 0 ) );
}
else
{
ImGui::Image( m_frameTexture, ImVec2( fi->w * scale, fi->h * scale ) );
}
DrawFrameImage( m_FrameTextureCache, *fi );
}
ImGui::EndTooltip();

View File

@@ -153,22 +153,8 @@ void View::DrawTimelineFrames( const FrameData& frames )
auto fi = m_worker.GetFrameImage( frames, i );
if( fi )
{
const auto scale = GetScale();
if( fi != m_frameTexturePtr )
{
if( !m_frameTexture ) m_frameTexture = MakeTexture();
UpdateTexture( m_frameTexture, m_worker.UnpackFrameImage( *fi ), fi->w, fi->h );
m_frameTexturePtr = fi;
}
ImGui::Separator();
if( fi->flip )
{
ImGui::Image( m_frameTexture, ImVec2( fi->w * scale, fi->h * scale ), ImVec2( 0, 1 ), ImVec2( 1, 0 ) );
}
else
{
ImGui::Image( m_frameTexture, ImVec2( fi->w * scale, fi->h * scale ) );
}
DrawFrameImage( m_FrameTextureCache, *fi );
if( ImGui::GetIO().KeyCtrl && IsMouseClicked( 0 ) )
{

View File

@@ -268,20 +268,7 @@ void View::DrawMessageLine( const MessageData& msg, bool hasCallstack, int& idx
if( fi )
{
ImGui::BeginTooltip();
if( fi != m_frameTexturePtr )
{
if( !m_frameTexture ) m_frameTexture = MakeTexture();
UpdateTexture( m_frameTexture, m_worker.UnpackFrameImage( *fi ), fi->w, fi->h );
m_frameTexturePtr = fi;
}
if( fi->flip )
{
ImGui::Image( m_frameTexture, ImVec2( fi->w, fi->h ), ImVec2( 0, 1 ), ImVec2( 1, 0 ) );
}
else
{
ImGui::Image( m_frameTexture, ImVec2( fi->w, fi->h ) );
}
DrawFrameImage( m_FrameTextureCache , *fi );
ImGui::EndTooltip();
}
}