oarust 0.8.6

GPU-first semantic computing engine written in Rust
// DrawPlotLine.slang — one-dispatch line plot over a bindless FP32 sample buffer.
//
// Donor: oacpp/source/cpp/lib/oa/ui/shader/compute/drawPlotLine.slang
// Adaptation: exact donor shader body; import path adjusted for Rust tree.
//
// Dispatch: (ceil(dst_w / 64), 1, 1)

import storage;
#include "compositor_common.slang"

struct PushConstants {
	uint  values_idx;
	uint  count;
	uint  dst_idx;
	int   dst_x;
	int   dst_y;
	uint  dst_w;
	uint  dst_h;
	float x_min;
	float x_max;
	float y_min;
	float y_max;
	uint  rgba;
	uint  flags;              // bit 1 = fill under curve, bit 2 = packed explicit X/Y
	uint  antialias_samples;  // admitted values: 1, 4, 8
	float line_width;
	int   clip_x;
	int   clip_y;
	uint  clip_w;
	uint  clip_h;
}

[[vk::push_constant]] PushConstants pc;
[[vk::binding(0, 0)]] RWByteAddressBuffer heap[];
[[vk::binding(1, 0)]] RWTexture2D<float4> images[];

void blendPixel(int2 coord, float4 src) {
	if (coord.x < pc.clip_x || coord.y < pc.clip_y
		|| uint(coord.x - pc.clip_x) >= pc.clip_w
		|| uint(coord.y - pc.clip_y) >= pc.clip_h) return;
	uint imageWidth;
	uint imageHeight;
	images[pc.dst_idx].GetDimensions(imageWidth, imageHeight);
	if (coord.x < 0 || coord.y < 0
		|| coord.x >= int(imageWidth) || coord.y >= int(imageHeight)) return;
	uint2 pixel = uint2(uint(coord.x), uint(coord.y));
	float4 dst = images[pc.dst_idx][pixel];
	images[pc.dst_idx][pixel] = uiSourceOver(src, dst);
}

float sampleAt(float position) {
	if (pc.count <= 1) return oaLoad(heap[pc.values_idx], 0);
	float index = clamp(position, 0.0, 1.0) * float(pc.count - 1);
	uint lo = uint(floor(index));
	uint hi = min(pc.count - 1, lo + 1);
	return lerp(
		oaLoad(heap[pc.values_idx], lo),
		oaLoad(heap[pc.values_idx], hi),
		index - float(lo));
}

float plotY(float value) {
	float normalized = saturate((value - pc.y_min) / (pc.y_max - pc.y_min));
	return (1.0 - normalized) * float(pc.dst_h - 1);
}

float plotX(float value) {
	return saturate((value - pc.x_min) / (pc.x_max - pc.x_min))
		* float(pc.dst_w - 1);
}

float distanceToSegment(float2 point, float2 begin, float2 end) {
	float2 edge = end - begin;
	float lengthSquared = dot(edge, edge);
	if (lengthSquared <= 1.0e-8) return length(point - begin);
	float fraction = saturate(dot(point - begin, edge) / lengthSquared);
	return length(point - (begin + fraction * edge));
}

float2 sampleOffset(uint index, uint count) {
	if (count <= 1U) return float2(0.5, 0.5);
	if (count <= 4U) {
		return float2(
			(index & 1U) == 0U ? 0.25 : 0.75,
			(index & 2U) == 0U ? 0.25 : 0.75);
	}
	switch (index & 7U) {
		case 0U: return float2(0.125, 0.375);
		case 1U: return float2(0.375, 0.875);
		case 2U: return float2(0.625, 0.125);
		case 3U: return float2(0.875, 0.625);
		case 4U: return float2(0.125, 0.875);
		case 5U: return float2(0.375, 0.125);
		case 6U: return float2(0.625, 0.625);
		default: return float2(0.875, 0.375);
	}
}

float lineCoverage(int2 pixel, float2 begin, float2 end) {
	uint count = pc.antialias_samples == 8U
		? 8U : pc.antialias_samples == 1U ? 1U : 4U;
	float covered = 0.0;
	float halfWidth = max(0.25, pc.line_width * 0.5);
	for (uint sample = 0U; sample < 8U; ++sample) {
		if (sample >= count) break;
		float distance = distanceToSegment(
			float2(pixel) + sampleOffset(sample, count), begin, end);
		covered += 1.0 - smoothstep(
			max(0.0, halfWidth - 0.45), halfWidth + 0.45, distance);
	}
	return covered / float(count);
}

void drawSegmentColumn(uint x, float2 begin, float2 end, float4 color) {
	float radius = max(1.0, pc.line_width * 0.5 + 1.0);
	if (float(x) + 1.0 < min(begin.x, end.x) - radius
		|| float(x) > max(begin.x, end.x) + radius) return;
	int minimumY = max(0, int(floor(min(begin.y, end.y) - radius)));
	int maximumY = min(int(pc.dst_h) - 1,
		int(ceil(max(begin.y, end.y) + radius)));
	for (int y = minimumY; y <= maximumY; ++y) {
		float coverage = lineCoverage(int2(int(x), y), begin, end);
		if (coverage <= 0.0) continue;
		blendPixel(
			int2(pc.dst_x + int(x), pc.dst_y + y),
			float4(color.rgb, color.a * coverage));
	}
}

[shader("compute")]
[numthreads(64, 1, 1)]
void main(uint3 tid : SV_DispatchThreadID) {
	uint x = tid.x;
	if (x >= pc.dst_w || pc.count == 0 || pc.dst_h == 0) return;

	float4 color = uiUnpackRgba(pc.rgba);

	if ((pc.flags & 4u) != 0u) {
		if (pc.x_max <= pc.x_min || pc.y_max <= pc.y_min) return;
		if (pc.count == 1) {
			float dataX = oaLoad(heap[pc.values_idx], 0);
			float dataY = oaLoad(heap[pc.values_idx], 1);
			if (isfinite(dataX) && isfinite(dataY)) {
				float2 point = float2(plotX(dataX), plotY(dataY));
				drawSegmentColumn(x, point, point, color);
			}
			return;
		}
		for (uint segment = 0; segment + 1 < pc.count; ++segment) {
			float dataX0 = oaLoad(heap[pc.values_idx], segment);
			float dataX1 = oaLoad(heap[pc.values_idx], segment + 1);
			float dataY0 = oaLoad(heap[pc.values_idx], pc.count + segment);
			float dataY1 = oaLoad(heap[pc.values_idx], pc.count + segment + 1);
			if (!isfinite(dataX0) || !isfinite(dataX1)
				|| !isfinite(dataY0) || !isfinite(dataY1)) continue;
			float screenX0 = plotX(dataX0);
			float screenX1 = plotX(dataX1);
			float minimumX = min(screenX0, screenX1);
			float maximumX = max(screenX0, screenX1);
			if (abs(screenX1 - screenX0) < 0.5) {
				drawSegmentColumn(
					x,
					float2(screenX0, plotY(dataY0)),
					float2(screenX1, plotY(dataY1)),
					color);
				continue;
			}
			if (float(x) + 1.0 < minimumX - pc.line_width
				|| float(x) > maximumX + pc.line_width) continue;
			drawSegmentColumn(
				x,
				float2(screenX0, plotY(dataY0)),
				float2(screenX1, plotY(dataY1)),
				color);
		}
		return;
	}

	float position = pc.dst_w <= 1 ? 0.0 : float(x) / float(pc.dst_w - 1);
	float previousPosition = pc.dst_w <= 1 || x == 0
		? position : float(x - 1) / float(pc.dst_w - 1);
	float y0 = plotY(sampleAt(previousPosition));
	float y1 = plotY(sampleAt(position));

	if ((pc.flags & 2u) != 0u) {
		float4 fill = float4(color.rgb, color.a * 0.16);
		for (int y = int(ceil(max(y0, y1))); y < int(pc.dst_h); ++y) {
			blendPixel(int2(pc.dst_x + int(x), pc.dst_y + y), fill);
		}
	}
	drawSegmentColumn(
		x,
		float2(float(x == 0U ? 0U : x - 1U), y0),
		float2(float(x), y1),
		color);
}