Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ngscopeclient/scopehal-apps
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 82fa51264a07
Choose a base ref
...
head repository: ngscopeclient/scopehal-apps
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1bea07858458
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Sep 5, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6acc20f View commit details
  2. Waveform rendering: now memcpy packed bools directly to GPU for digit…

    …al waveforms, rather than doing messy bool to float conversion. Fixes #164.
    azonenberg committed Sep 5, 2020
    Copy the full SHA
    1bea078 View commit details
23 changes: 16 additions & 7 deletions src/glscopeclient/WaveformArea.cpp
Original file line number Diff line number Diff line change
@@ -613,7 +613,8 @@ void WaveformArea::on_unrealize()
void WaveformArea::CleanupGLHandles()
{
//Clean up old shaders
m_waveformComputeProgram.Destroy();
m_digitalWaveformComputeProgram.Destroy();
m_analogWaveformComputeProgram.Destroy();
m_colormapProgram.Destroy();
m_persistProgram.Destroy();
m_eyeProgram.Destroy();
@@ -651,12 +652,20 @@ void WaveformArea::CleanupGLHandles()
void WaveformArea::InitializeWaveformPass()
{
//ProfileBlock pb("Load waveform shaders");
ComputeShader wc;
if(!wc.Load("shaders/waveform-compute.glsl"))
LogFatal("failed to load waveform compute shader, aborting\n");
m_waveformComputeProgram.Add(wc);
if(!m_waveformComputeProgram.Link())
LogFatal("failed to link shader program, aborting\n");

ComputeShader dwc;
if(!dwc.Load("shaders/waveform-compute-digital.glsl"))
LogFatal("failed to load digital waveform compute shader, aborting\n");
m_digitalWaveformComputeProgram.Add(dwc);
if(!m_digitalWaveformComputeProgram.Link())
LogFatal("failed to link digital waveform shader program, aborting\n");

ComputeShader awc;
if(!awc.Load("shaders/waveform-compute-analog.glsl"))
LogFatal("failed to load analog waveform compute shader, aborting\n");
m_analogWaveformComputeProgram.Add(awc);
if(!m_analogWaveformComputeProgram.Link())
LogFatal("failed to link analog waveform shader program, aborting\n");
}

void WaveformArea::InitializeColormapPass()
7 changes: 6 additions & 1 deletion src/glscopeclient/WaveformArea.h
Original file line number Diff line number Diff line change
@@ -90,6 +90,9 @@ class WaveformRenderData
, m_count(0)
{}

bool IsDigital()
{ return m_channel.m_channel->GetType() == OscilloscopeChannel::CHANNEL_TYPE_DIGITAL; }

//The channel of interest
StreamDescriptor m_channel;

@@ -111,6 +114,7 @@ class WaveformRenderData
//OpenGL-mapped buffers for the data
int64_t* m_mappedXBuffer;
float* m_mappedYBuffer;
bool* m_mappedDigitalYBuffer;
uint32_t* m_mappedIndexBuffer;
uint32_t* m_mappedConfigBuffer;
int64_t* m_mappedConfigBuffer64;
@@ -314,7 +318,8 @@ class WaveformArea : public Gtk::GLArea
void RenderTrace(WaveformRenderData* wdata);
void InitializeWaveformPass();
void PrepareGeometry(WaveformRenderData* wdata);
Program m_waveformComputeProgram;
Program m_analogWaveformComputeProgram;
Program m_digitalWaveformComputeProgram;
WaveformRenderData* m_waveformRenderData;
std::map<StreamDescriptor, WaveformRenderData*> m_overlayRenderData;

51 changes: 36 additions & 15 deletions src/glscopeclient/WaveformArea_rendering.cpp
Original file line number Diff line number Diff line change
@@ -67,9 +67,19 @@ void WaveformRenderData::MapBuffers(size_t width)
}

m_mappedXBuffer = (int64_t*)m_waveformXBuffer.Map(m_count*sizeof(int64_t), GL_READ_WRITE);
m_mappedYBuffer = (float*)m_waveformYBuffer.Map(m_count*sizeof(float));
if(IsDigital())
{
//round up to next multiple of 4 since buffer is actually made of int32's
m_mappedDigitalYBuffer = (bool*)m_waveformYBuffer.Map((m_count*sizeof(bool) | 3) + 1);
m_mappedYBuffer = NULL;
}
else
{
m_mappedYBuffer = (float*)m_waveformYBuffer.Map(m_count*sizeof(float));
m_mappedDigitalYBuffer = NULL;
}
m_mappedIndexBuffer = (uint32_t*)m_waveformIndexBuffer.Map(width*sizeof(uint32_t));
m_mappedConfigBuffer = (uint32_t*)m_waveformConfigBuffer.Map(sizeof(float)*12);
m_mappedConfigBuffer = (uint32_t*)m_waveformConfigBuffer.Map(sizeof(float)*11);
m_mappedFloatConfigBuffer = (float*)m_mappedConfigBuffer;
m_mappedConfigBuffer64 = (int64_t*)m_mappedConfigBuffer;
}
@@ -137,10 +147,8 @@ void WaveformArea::PrepareGeometry(WaveformRenderData* wdata)
else
digheight = 20;

//TODO: AVX this conversion
yscale = digheight;
for(size_t j=0; j<wdata->m_count; j++)
wdata->m_mappedYBuffer[j] = digdat->m_samples[j];
memcpy(wdata->m_mappedDigitalYBuffer, &digdat->m_samples[0], wdata->m_count*sizeof(bool));
}
else
{
@@ -184,12 +192,11 @@ void WaveformArea::PrepareGeometry(WaveformRenderData* wdata)
wdata->m_mappedConfigBuffer[3] = m_plotRight; //windowWidth
wdata->m_mappedConfigBuffer[4] = wdata->m_count; //depth
wdata->m_mappedFloatConfigBuffer[5] = alpha_scaled; //alpha
wdata->m_mappedConfigBuffer[6] = digdat ? 1 : 0; //digital
wdata->m_mappedFloatConfigBuffer[7] = pdat->m_triggerPhase * m_group->m_pixelsPerXUnit; //xoff
wdata->m_mappedFloatConfigBuffer[8] = pdat->m_timescale * m_group->m_pixelsPerXUnit; //xscale
wdata->m_mappedFloatConfigBuffer[9] = ybase; //ybase
wdata->m_mappedFloatConfigBuffer[10] = yscale; //yscale
wdata->m_mappedFloatConfigBuffer[11] = channel->GetOffset(); //yoff
wdata->m_mappedFloatConfigBuffer[6] = pdat->m_triggerPhase * m_group->m_pixelsPerXUnit; //xoff
wdata->m_mappedFloatConfigBuffer[7] = pdat->m_timescale * m_group->m_pixelsPerXUnit; //xscale
wdata->m_mappedFloatConfigBuffer[8] = ybase; //ybase
wdata->m_mappedFloatConfigBuffer[9] = yscale; //yscale
wdata->m_mappedFloatConfigBuffer[10] = channel->GetOffset(); //yoff

//Done
wdata->m_geometryOK = true;
@@ -385,7 +392,10 @@ bool WaveformArea::on_render(const Glib::RefPtr<Gdk::GLContext>& /*context*/)
}

//Make sure all compute shaders are done before we composite
m_waveformComputeProgram.MemoryBarrier();
if(IsDigital())
m_digitalWaveformComputeProgram.MemoryBarrier();
else
m_analogWaveformComputeProgram.MemoryBarrier();

//Final compositing of data being drawn to the screen
m_windowFramebuffer.Bind(GL_FRAMEBUFFER);
@@ -544,13 +554,24 @@ void WaveformArea::RenderTrace(WaveformRenderData* data)
}
int numGroups = numCols / localSize;

m_waveformComputeProgram.Bind();
m_waveformComputeProgram.SetImageUniform(data->m_waveformTexture, "outputTex");
if(data->IsDigital())
{
m_digitalWaveformComputeProgram.Bind();
m_digitalWaveformComputeProgram.SetImageUniform(data->m_waveformTexture, "outputTex");
}
else
{
m_analogWaveformComputeProgram.Bind();
m_analogWaveformComputeProgram.SetImageUniform(data->m_waveformTexture, "outputTex");
}
data->m_waveformXBuffer.BindBase(1);
data->m_waveformYBuffer.BindBase(4);
data->m_waveformConfigBuffer.BindBase(2);
data->m_waveformIndexBuffer.BindBase(3);
m_waveformComputeProgram.DispatchCompute(numGroups, 1, 1);
if(data->IsDigital())
m_digitalWaveformComputeProgram.DispatchCompute(numGroups, 1, 1);
else
m_analogWaveformComputeProgram.DispatchCompute(numGroups, 1, 1);
}

void WaveformArea::RenderTraceColorCorrection(WaveformRenderData* data)
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ layout(std430, binding=1) buffer waveform_x

layout(std430, binding=4) buffer waveform_y
{
float voltage[]; //y value of this sample, in pixels
float voltage[]; //y value of the sample, in volts
};

//Global configuration for the run
@@ -22,7 +22,6 @@ layout(std430, binding=2) buffer config
uint windowWidth;
uint memDepth;
float alpha;
uint digital;
float xoff;
float xscale;
float ybase;
@@ -106,32 +105,11 @@ void main()
float endy = right.y;

//Interpolate analog signals if either end is outside our column
if(digital == 0)
{
float slope = (right.y - left.y) / (right.x - left.x);
if(left.x < gl_GlobalInvocationID.x)
starty = InterpolateY(left, right, slope, gl_GlobalInvocationID.x);
if(right.x > gl_GlobalInvocationID.x + 1)
endy = InterpolateY(left, right, slope, gl_GlobalInvocationID.x + 1);
}

//Digital signal - do not interpolate
else
{
//If we are very near the left edge, draw vertical line
if(abs(left.x - gl_GlobalInvocationID.x + 1) <= 2)
{
starty = left.y;
endy = right.y;
}

//otherwise draw a single pixel
else
{
starty = right.y;
endy = right.y;
}
}
float slope = (right.y - left.y) / (right.x - left.x);
if(left.x < gl_GlobalInvocationID.x)
starty = InterpolateY(left, right, slope, gl_GlobalInvocationID.x);
if(right.x > gl_GlobalInvocationID.x + 1)
endy = InterpolateY(left, right, slope, gl_GlobalInvocationID.x + 1);

//Clip to window size
starty = min(starty, MAX_HEIGHT);
148 changes: 148 additions & 0 deletions src/glscopeclient/shaders/waveform-compute-digital.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#version 450
#extension GL_ARB_gpu_shader_int64 : require

//The output texture (for now, only alpha channel is used)
layout(binding=0, rgba32f) uniform image2D outputTex;

layout(std430, binding=1) buffer waveform_x
{
int64_t xpos[]; //x position, in time ticks
};

layout(std430, binding=4) buffer waveform_y
{
int voltage[]; //y value of the sample, boolean 0/1 for 4 samples per int
};

//Global configuration for the run
layout(std430, binding=2) buffer config
{
int64_t innerXoff;
uint windowHeight;
uint windowWidth;
uint memDepth;
float alpha;
float xoff;
float xscale;
float ybase;
float yscale;
float yoff;
};

//Indexes so we know which samples go to which X pixel range
layout(std430, binding=3) buffer index
{
uint xind[];
};

//Maximum height of a single waveform, in pixels.
//This is enough for a nearly fullscreen 4K window so should be plenty.
#define MAX_HEIGHT 2048

//Number of columns of pixels per thread block
#define COLS_PER_BLOCK 2

layout(local_size_x=COLS_PER_BLOCK, local_size_y=1, local_size_z=1) in;

//Interpolate a Y coordinate
float InterpolateY(vec2 left, vec2 right, float slope, float x)
{
return left.y + ( (x - left.x) * slope );
}

/*
NEW IDEA
Multiple threads per X coordinate (say, 32 - 1 warp)
Parallel fetch base[i+z] and atomically increment local memory
Each local has a 2D shared array
Assuming 96 KB shared memory, we can fit a total of 24K float32 temp pixels
Assuming 2K max line height, that's up to 12 pixels of width per local
*/

//Shared buffer for the local working buffer
shared float g_workingBuffer[COLS_PER_BLOCK][MAX_HEIGHT];

int GetBoolean(uint i)
{
int block = voltage[i/4];
return (block >> (8*i) ) & 0xff;
}

void main()
{
//Abort if window height is too big, or if we're off the end of the window
if(windowHeight > MAX_HEIGHT)
return;
if(gl_GlobalInvocationID.x > windowWidth)
return;

//Clear column to blank in the first thread of the block
if(gl_LocalInvocationID.y == 0)
{
for(uint y=0; y<windowHeight; y++)
g_workingBuffer[gl_LocalInvocationID.x][y] = 0;
}
barrier();
memoryBarrierShared();

//Loop over the waveform, starting at the leftmost point that overlaps this column
uint istart = xind[gl_GlobalInvocationID.x];
vec2 left = vec2(float(xpos[istart] + innerXoff) * xscale + xoff, GetBoolean(istart)*yscale + ybase);
vec2 right;
for(uint i=istart; i<(memDepth-1); i++)
{
//Fetch coordinates of the current and upcoming sample
right = vec2(float(xpos[i+1] + innerXoff)*xscale + xoff, GetBoolean(i+1)*yscale + ybase);

//If the current point is right of us, stop
if(left.x > gl_GlobalInvocationID.x + 1)
break;

//If the upcoming point is still left of us, we're not there yet
if(right.x < gl_GlobalInvocationID.x)
{
left = right;
continue;
}

float starty;
float endy;

//If we are very near the left edge, draw vertical line
if(abs(left.x - gl_GlobalInvocationID.x) <= 1)
{
starty = left.y;
endy = right.y;
}

//otherwise draw a single pixel
else
{
starty = right.y;
endy = right.y;
}

//Clip to window size
starty = min(starty, MAX_HEIGHT);
endy = min(endy, MAX_HEIGHT);

//Sort Y coordinates from min to max
int ymin = int(min(starty, endy));
int ymax = int(max(starty, endy));

//Push current point down the pipeline
left = right;

//Fill in the space between min and max for this segment
for(int y=ymin; y <= ymax; y++)
g_workingBuffer[gl_LocalInvocationID.x][y] += alpha;

//TODO: antialiasing
//TODO: decimation at very wide zooms
}

//Copy working buffer to RGB output
for(uint y=0; y<windowHeight; y++)
imageStore(outputTex, ivec2(gl_GlobalInvocationID.x, y), vec4(0, 0, 0, g_workingBuffer[gl_LocalInvocationID.x][y]));
}