Files
2026-07-13 13:33:03 +08:00

138 lines
3.9 KiB
Plaintext

layout(set=0, binding=0) buffer outValueBuffer {
FLOAT data[];
} uOutValue;
layout(set=0, binding=1) buffer outIndexBuffer {
int data[];
} uOutIndex;
layout(set=0, binding=2) readonly buffer sourceBuffer {
FLOAT data[];
} uInput;
layout(set=0, binding=3) uniform constBuffer {
int rowSize;
int k;
int numRows;
int pad;
} uConst;
#define THREAD_NUMBER 128
#define LOCAL_K 8
#define CANDIDATE_NUMBER (THREAD_NUMBER * LOCAL_K)
shared FLOAT sharedValue[CANDIDATE_NUMBER];
shared int sharedIndex[CANDIDATE_NUMBER];
layout(local_size_x = THREAD_NUMBER, local_size_y = 1, local_size_z = 1) in;
bool afterAsc(FLOAT aValue, int aIndex, FLOAT bValue, int bIndex) {
if (aValue > bValue) return true;
if (aValue < bValue) return false;
return aIndex > bIndex;
}
bool better(FLOAT aValue, int aIndex, FLOAT bValue, int bIndex) {
if (bIndex < 0) return true;
if (aIndex < 0) return false;
#ifdef SORT_DESC
if (aValue > bValue) return true;
if (aValue < bValue) return false;
#else
if (aValue < bValue) return true;
if (aValue > bValue) return false;
#endif
return aIndex < bIndex;
}
void main() {
int tid = int(gl_LocalInvocationID.x);
int row = int(gl_WorkGroupID.y);
if (row >= uConst.numRows) return;
int rowOffset = row * uConst.rowSize;
// Initialize per-thread top-k
#ifdef SORT_DESC
FLOAT initVal = FLOAT(-1e38);
#else
FLOAT initVal = FLOAT(1e38);
#endif
FLOAT localValue[LOCAL_K];
int localIndex[LOCAL_K];
for (int i = 0; i < LOCAL_K; ++i) {
localValue[i] = initVal;
localIndex[i] = -1;
}
// Read input and build per-thread top-k
for (int c = tid; c < uConst.rowSize; c += THREAD_NUMBER) {
FLOAT value = uInput.data[rowOffset + c];
int idx = c;
if (!better(value, idx, localValue[LOCAL_K - 1], localIndex[LOCAL_K - 1])) continue;
int insertPos = LOCAL_K;
for (int j = 0; j < LOCAL_K; ++j) {
if (better(value, idx, localValue[j], localIndex[j])) {
insertPos = j;
break;
}
}
if (insertPos >= LOCAL_K) continue;
for (int j = LOCAL_K - 1; j > insertPos; --j) {
localValue[j] = localValue[j - 1];
localIndex[j] = localIndex[j - 1];
}
localValue[insertPos] = value;
localIndex[insertPos] = idx;
}
// Scatter to shared memory
int base = tid * LOCAL_K;
for (int i = 0; i < LOCAL_K; ++i) {
sharedValue[base + i] = localValue[i];
sharedIndex[base + i] = localIndex[i];
}
barrier();
// Bitonic sort in shared memory
for (uint size = 2; size <= CANDIDATE_NUMBER; size <<= 1) {
for (uint stride = size >> 1; stride > 0; stride >>= 1) {
for (uint sIdx = uint(tid); sIdx < CANDIDATE_NUMBER; sIdx += THREAD_NUMBER) {
uint ixj = sIdx ^ stride;
if (ixj <= sIdx) continue;
bool up = ((sIdx & size) == 0u);
#ifdef SORT_DESC
up = !up;
#endif
bool after = afterAsc(sharedValue[sIdx], sharedIndex[sIdx],
sharedValue[ixj], sharedIndex[ixj]);
if (up == after) {
FLOAT tValue = sharedValue[sIdx];
sharedValue[sIdx] = sharedValue[ixj];
sharedValue[ixj] = tValue;
int tIndex = sharedIndex[sIdx];
sharedIndex[sIdx] = sharedIndex[ixj];
sharedIndex[ixj] = tIndex;
}
}
barrier();
}
}
// Thread 0 writes output
if (tid == 0) {
int realK = min(uConst.k, uConst.rowSize);
int outOffset = row * uConst.k;
for (int i = 0; i < realK; ++i) {
uOutValue.data[outOffset + i] = sharedValue[i];
uOutIndex.data[outOffset + i] = sharedIndex[i];
}
}
}