Skip to content

Commit c438a88

Browse files
committed
Added various functions to waveforms and buffers to enable freeing of GPU memory in OOM conditions
1 parent 81a0e05 commit c438a88

File tree

9 files changed

+120
-9
lines changed

9 files changed

+120
-9
lines changed

scopehal/AcceleratorBuffer.h

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ class AcceleratorBuffer
344344
~AcceleratorBuffer()
345345
{
346346
FreeCpuBuffer();
347-
FreeGpuBuffer();
347+
FreeGpuBuffer(true);
348348
}
349349

350350
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1073,15 +1073,25 @@ class AcceleratorBuffer
10731073
}
10741074
}
10751075

1076+
public:
10761077
/**
10771078
@brief Free the GPU-side buffer and underlying physical memory
1079+
1080+
@param dataLossOK True if we do not intend to use the contents of this buffer again
1081+
(and thus it's OK to remove the only copy of the data)
10781082
*/
1079-
void FreeGpuBuffer()
1083+
void FreeGpuBuffer(bool dataLossOK = false)
10801084
{
10811085
//Early out if buffer is already null
10821086
if(m_gpuPhysMem == nullptr)
10831087
return;
10841088

1089+
//If we do NOT have a CPU-side buffer, we're deleting all of our data! Warn for now
1090+
if( (m_cpuMemoryType == MEM_TYPE_NULL) && m_gpuPhysMemIsStale && !empty() && !dataLossOK)
1091+
{
1092+
LogWarning("Freeing a GPU buffer without any CPU backing, may cause data loss\n");
1093+
}
1094+
10851095
//If we have a CPU-side buffer, and it's stale, move our about-to-be-deleted content over before we free it
10861096
if( (m_cpuMemoryType != MEM_TYPE_NULL) && m_cpuPhysMemIsStale && !empty() )
10871097
CopyToCpu();
@@ -1325,12 +1335,34 @@ class AcceleratorBuffer
13251335
}
13261336
}
13271337

1338+
//Retry one more time.
1339+
//If we OOM simultaneously in two threads, it's possible to have the second OnMemoryPressure call
1340+
//return false because the first one already freed all it could. But we might have enough free to continue.
1341+
if(!ok)
1342+
{
1343+
LogDebug("Final retry\n");
1344+
try
1345+
{
1346+
m_gpuPhysMem = std::make_unique<vk::raii::DeviceMemory>(*g_vkComputeDevice, info);
1347+
ok = true;
1348+
}
1349+
catch(vk::OutOfDeviceMemoryError& ex2)
1350+
{
1351+
LogDebug("Allocation failed again\n");
1352+
}
1353+
}
1354+
13281355
//If we get here, we couldn't allocate no matter what
1329-
LogError(
1330-
"Failed to allocate %s of GPU memory despite our best efforts to reclaim space\n"
1331-
"This is unrecoverable (for now).\n",
1332-
Unit(Unit::UNIT_BYTES).PrettyPrint(req.size, 4).c_str());
1333-
exit(1);
1356+
//TODO: Fall back to a CPU-side allocation
1357+
if(!ok)
1358+
{
1359+
LogError(
1360+
"Failed to allocate %s of GPU memory despite our best efforts to reclaim space\n"
1361+
"This is unrecoverable (for now).\n",
1362+
Unit(Unit::UNIT_BYTES).PrettyPrint(req.size, 4).c_str());
1363+
1364+
std::abort();
1365+
}
13341366
}
13351367
m_gpuMemoryType = MEM_TYPE_GPU_ONLY;
13361368

scopehal/ConstellationWaveform.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ class ConstellationWaveform : public DensityFunctionWaveform
8585
*/
8686
float m_saturationLevel;
8787

88+
virtual void FreeGpuMemory() override
89+
{}
90+
91+
virtual bool HasGpuBuffer() override
92+
{ return false; }
93+
8894
protected:
8995

9096
/**

scopehal/EyeWaveform.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* libscopehal *
44
* *
5-
* Copyright (c) 2012-2023 Andrew D. Zonenberg and contributors *
5+
* Copyright (c) 2012-2024 Andrew D. Zonenberg and contributors *
66
* All rights reserved. *
77
* *
88
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
@@ -86,6 +86,12 @@ class EyeWaveform : public DensityFunctionWaveform
8686
EyeType GetType()
8787
{ return m_type; }
8888

89+
virtual void FreeGpuMemory() override
90+
{}
91+
92+
virtual bool HasGpuBuffer() override
93+
{ return false; }
94+
8995
protected:
9096
int64_t* m_accumdata;
9197

scopehal/Oscilloscope.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,15 @@ class Oscilloscope : public virtual Instrument
959959
}
960960

961961
public:
962+
963+
/**
964+
@brief Free all waveforms in our pool to reclaim memory
965+
966+
@return True if memory was freed, false if pools were already empty
967+
*/
968+
bool FreeWaveformPools()
969+
{ return m_analogWaveformPool.clear() || m_digitalWaveformPool.clear(); }
970+
962971
void AddWaveformToAnalogPool(WaveformBase* w)
963972
{ m_analogWaveformPool.Add(w); }
964973

scopehal/Waveform.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ class WaveformBase
234234

235235
virtual void CacheColors();
236236

237+
///@brief Free GPU-side memory if we are short on VRAM or do not anticipate using this waveform for a while
238+
virtual void FreeGpuMemory() =0;
239+
240+
///@brief Returns true if we have at least one buffer resident on the GPU
241+
virtual bool HasGpuBuffer() =0;
242+
237243
protected:
238244

239245
///@brief Cache of packed RGBA32 data with colors for each protocol decode event. Empty for non-protocol waveforms.
@@ -403,6 +409,12 @@ class UniformWaveform : public UniformWaveformBase
403409
///@brief Sample data
404410
AcceleratorBuffer<S> m_samples;
405411

412+
virtual void FreeGpuMemory() override
413+
{ m_samples.FreeGpuBuffer(); }
414+
415+
virtual bool HasGpuBuffer()
416+
{ return m_samples.HasGpuBuffer(); }
417+
406418
virtual void Resize(size_t size) override
407419
{ m_samples.resize(size); }
408420

@@ -511,6 +523,16 @@ class SparseWaveform : public SparseWaveformBase
511523
///@brief Sample data
512524
AcceleratorBuffer<S> m_samples;
513525

526+
virtual void FreeGpuMemory() override
527+
{
528+
m_offsets.FreeGpuBuffer();
529+
m_durations.FreeGpuBuffer();
530+
m_samples.FreeGpuBuffer();
531+
}
532+
533+
virtual bool HasGpuBuffer()
534+
{ return m_samples.HasGpuBuffer() || m_offsets.HasGpuBuffer() || m_durations.HasGpuBuffer(); }
535+
514536
virtual void Resize(size_t size) override
515537
{
516538
m_offsets.resize(size);

scopehal/WaveformPool.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,25 @@ class WaveformPool
101101
return ret;
102102
}
103103

104+
/**
105+
@brief Free all waveforms in the pool to reclaim memory
106+
107+
@return True if memory was freed, false if pool was empty to begin with
108+
*/
109+
bool clear()
110+
{
111+
std::lock_guard<std::mutex> lock(m_mutex);
112+
113+
if(m_waveforms.empty())
114+
return false;
115+
116+
for(auto w : m_waveforms)
117+
delete w;
118+
m_waveforms.clear();
119+
120+
return true;
121+
}
122+
104123
protected:
105124

106125
///@brief Maximum number of waveforms to store in the pool

scopehal/scopehal.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,11 @@ const char* ScopehalGetVersion()
10031003
*/
10041004
bool OnMemoryPressure(MemoryPressureLevel level, MemoryPressureType type, size_t requestedSize)
10051005
{
1006+
//Only allow one OnMemoryPressure() to execute at a time
1007+
//Anyone else who simultaneously OOM'd has to wait
1008+
static mutex memoryPressureMutex;
1009+
lock_guard<mutex> lock(memoryPressureMutex);
1010+
10061011
LogWarning("OnMemoryPressure: %s memory exhaustion on %s (tried to allocate %s)\n",
10071012
(level == MemoryPressureLevel::Hard) ? "Hard" : "Soft",
10081013
(type == MemoryPressureType::Host) ? "host" : "device",

scopeprotocols/SpectrogramFilter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ class SpectrogramWaveform : public DensityFunctionWaveform
6666
double GetBottomEdgeFrequency()
6767
{ return m_bottomEdgeFrequency; }
6868

69+
virtual void FreeGpuMemory() override
70+
{}
71+
72+
virtual bool HasGpuBuffer() override
73+
{ return false; }
74+
6975
protected:
7076
double m_binsize;
7177
double m_bottomEdgeFrequency;

scopeprotocols/Waterfall.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* *
33
* libscopeprotocols *
44
* *
5-
* Copyright (c) 2012-2023 Andrew D. Zonenberg and contributors *
5+
* Copyright (c) 2012-2024 Andrew D. Zonenberg and contributors *
66
* All rights reserved. *
77
* *
88
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
@@ -57,6 +57,12 @@ class WaterfallWaveform : public DensityFunctionWaveform
5757
WaterfallWaveform(const WaterfallWaveform&) =delete;
5858
WaterfallWaveform& operator=(const WaterfallWaveform&) =delete;
5959

60+
virtual void FreeGpuMemory() override
61+
{}
62+
63+
virtual bool HasGpuBuffer() override
64+
{ return false; }
65+
6066
AcceleratorBuffer<float> m_tempBuf;
6167
};
6268

0 commit comments

Comments
 (0)