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. 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]));
}