StagingBuffer: Large slots should be aligned to alignment
Slots whose stride size is larger than the slot alignment value should have their size rounded up to the alignment size so that the slots that follow them will also be aligned.
This commit is contained in:
@@ -68,10 +68,13 @@ static size_t calculateMaxAlignment(
|
||||
|
||||
void StagingBuffer::computeSlotStrideAndBufferSize()
|
||||
{
|
||||
// Slot stride is the maximum of alignment and padding
|
||||
slotStrideNBytes = std::max(
|
||||
// Slot stride is the maximum of alignment and padding, rounded up to a multiple of alignment
|
||||
size_t minSlotStride = std::max(
|
||||
inputConstraints.slotStartAlignmentByteVal,
|
||||
inputConstraints.slotPadToNBytes);
|
||||
slotStrideNBytes = ((minSlotStride + inputConstraints.slotStartAlignmentByteVal - 1)
|
||||
/ inputConstraints.slotStartAlignmentByteVal)
|
||||
* inputConstraints.slotStartAlignmentByteVal;
|
||||
|
||||
// Calculate maximum alignment needed for first slot (must satisfy both frame and slot alignment)
|
||||
size_t maxAlignment = calculateMaxAlignment(
|
||||
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
+ ": StimulusFrame: failed to create clBuffer");
|
||||
}
|
||||
|
||||
std::cout << __func__ << ": StimulusFrame: created clBuffer with size " << slotDesc.nBytes << " bytes @ " << (const void*)slotDesc.vaddr << std::endl;
|
||||
// std::cout << __func__ << ": StimulusFrame: created clBuffer with size " << slotDesc.nBytes << " bytes @ " << (const void*)slotDesc.vaddr << std::endl;
|
||||
}
|
||||
|
||||
~StimulusFrame() = default;
|
||||
|
||||
@@ -9,76 +9,6 @@
|
||||
namespace smo {
|
||||
namespace stim_buff {
|
||||
|
||||
// Helper function to calculate expected buffer size using the same logic as computeSlotStrideAndBufferSize
|
||||
static size_t calculateExpectedBufferSize(
|
||||
size_t nSlots,
|
||||
const StagingBuffer::IOEngineConstraints& constraints)
|
||||
{
|
||||
// Slot stride is the maximum of alignment and padding
|
||||
size_t slotStrideNBytes = std::max(
|
||||
constraints.slotStartAlignmentByteVal,
|
||||
constraints.slotPadToNBytes);
|
||||
|
||||
// Calculate maximum alignment needed for first slot
|
||||
size_t maxAlignment;
|
||||
if (constraints.frameStartAlignmentByteVal >= constraints.slotStartAlignmentByteVal)
|
||||
{
|
||||
if (constraints.frameStartAlignmentByteVal % constraints.slotStartAlignmentByteVal == 0)
|
||||
{
|
||||
maxAlignment = constraints.frameStartAlignmentByteVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxAlignment = std::max(
|
||||
constraints.frameStartAlignmentByteVal,
|
||||
constraints.slotStartAlignmentByteVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (constraints.slotStartAlignmentByteVal % constraints.frameStartAlignmentByteVal == 0)
|
||||
{
|
||||
maxAlignment = constraints.slotStartAlignmentByteVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxAlignment = std::max(
|
||||
constraints.frameStartAlignmentByteVal,
|
||||
constraints.slotStartAlignmentByteVal);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate minimum buffer size
|
||||
size_t minBufferSize = std::max(
|
||||
constraints.framePadToNBytes,
|
||||
constraints.slotPadToNBytes);
|
||||
|
||||
// Calculate total size needed for nSlots slots
|
||||
size_t slotAreaSize = nSlots * slotStrideNBytes;
|
||||
|
||||
// Add padding space at buffer start for alignment offset (worst case: max alignment - 1)
|
||||
size_t alignmentPadding = maxAlignment - 1;
|
||||
|
||||
// Total size needed: alignment padding + slot area, then ensure minimum is met
|
||||
size_t rawSize = alignmentPadding + slotAreaSize;
|
||||
if (rawSize < minBufferSize)
|
||||
{
|
||||
rawSize = minBufferSize;
|
||||
}
|
||||
|
||||
// Align up to the maximum alignment to ensure we can always find a valid offset
|
||||
return ((rawSize + maxAlignment - 1) / maxAlignment) * maxAlignment;
|
||||
}
|
||||
|
||||
// Helper function to calculate expected slot stride
|
||||
static size_t calculateExpectedSlotStride(
|
||||
const StagingBuffer::IOEngineConstraints& constraints)
|
||||
{
|
||||
return std::max(
|
||||
constraints.slotStartAlignmentByteVal,
|
||||
constraints.slotPadToNBytes);
|
||||
}
|
||||
|
||||
// Helper function to create test constraints
|
||||
static StagingBuffer::IOEngineConstraints createTestConstraints(
|
||||
size_t slotStartAlignment,
|
||||
@@ -117,357 +47,230 @@ protected:
|
||||
size_t pageSize;
|
||||
};
|
||||
|
||||
// Test 1: Single slot, minimal padding, small alignments
|
||||
TEST_F(StagingBufferTest, SingleSlotMinimalAlignment) {
|
||||
size_t nSlots = 1;
|
||||
auto constraints = createTestConstraints(4, 16, 4, 16);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
|
||||
// Verify FrameAssemblyDesc
|
||||
// Helper function to verify all slots are page-aligned and have correct stride
|
||||
static void verifyAllSlotsPageAligned(
|
||||
const StagingBuffer& buffer,
|
||||
size_t expectedSlotStride,
|
||||
size_t pageSize)
|
||||
{
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||
EXPECT_EQ(frameDesc->slots.size(), nSlots);
|
||||
ASSERT_GT(frameDesc->slots.size(), 0u);
|
||||
|
||||
// Verify first slot alignment
|
||||
if (!frameDesc->slots.empty()) {
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, constraints.slotStartAlignmentByteVal,
|
||||
"First slot");
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, constraints.frameStartAlignmentByteVal,
|
||||
"First slot frame alignment");
|
||||
}
|
||||
}
|
||||
// Verify slot stride is a multiple of page size
|
||||
EXPECT_EQ(expectedSlotStride % pageSize, 0u)
|
||||
<< "Slot stride " << expectedSlotStride
|
||||
<< " should be a multiple of page size " << pageSize;
|
||||
|
||||
// Test 2: Multiple slots, no alignment padding needed
|
||||
TEST_F(StagingBufferTest, MultipleSlotsMinimalAlignment) {
|
||||
size_t nSlots = 10;
|
||||
auto constraints = createTestConstraints(4, 16, 4, 16);
|
||||
// Verify first slot is page-aligned
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, pageSize, "First slot");
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
// Verify all subsequent slots are page-aligned and have correct stride
|
||||
for (size_t i = 1; i < frameDesc->slots.size(); ++i) {
|
||||
verifyAlignment(frameDesc->slots[i].vaddr, pageSize,
|
||||
"Slot " + std::to_string(i));
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
|
||||
// Verify all slots are properly spaced
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||
|
||||
for (size_t i = 0; i < frameDesc->slots.size(); ++i) {
|
||||
if (i > 0) {
|
||||
// Verify actual stride matches expected stride
|
||||
size_t actualStride = reinterpret_cast<uintptr_t>(frameDesc->slots[i].vaddr) -
|
||||
reinterpret_cast<uintptr_t>(frameDesc->slots[i-1].vaddr);
|
||||
EXPECT_EQ(actualStride, expectedSlotStride)
|
||||
<< "Slot " << i << " stride mismatch";
|
||||
}
|
||||
<< "Slot " << i << " stride mismatch: expected " << expectedSlotStride
|
||||
<< ", got " << actualStride;
|
||||
}
|
||||
}
|
||||
|
||||
// Test 3: OpenCL Mesh constraints (real-world scenario)
|
||||
TEST_F(StagingBufferTest, OpenClMeshConstraints) {
|
||||
size_t nSlots = 909; // 30000ms / 33ms
|
||||
size_t nDgramsPerFrame = 84;
|
||||
size_t nPointsPerDgram = 96; // SingleFirst/Strongest/Dual return mode
|
||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 3; // 96768
|
||||
|
||||
// Test 1: Small slot stride (< page size) - should round up to page size
|
||||
TEST_F(StagingBufferTest, SmallSlotStrideRoundsUpToPageSize) {
|
||||
size_t nSlots = 10;
|
||||
size_t smallSlotPad = 256; // Much smaller than typical page size (4096)
|
||||
auto constraints = createTestConstraints(
|
||||
sizeof(float), // slotStartAlignment
|
||||
pageSize, // slotStartAlignment (page size)
|
||||
smallSlotPad, // slotPadToNBytes (small)
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
// Slot stride should be rounded up to page size
|
||||
size_t expectedSlotStride = pageSize;
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||
<< "Small slot pad should round up to page size";
|
||||
|
||||
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||
}
|
||||
|
||||
// Test 2: Slot stride equal to page size - should remain page size
|
||||
TEST_F(StagingBufferTest, SlotStrideEqualToPageSize) {
|
||||
size_t nSlots = 20;
|
||||
auto constraints = createTestConstraints(
|
||||
pageSize, // slotStartAlignment
|
||||
pageSize, // slotPadToNBytes (equal to page size)
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = pageSize;
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||
<< "Slot stride equal to page size should remain unchanged";
|
||||
|
||||
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||
}
|
||||
|
||||
// Test 3: Slot stride slightly larger than page size (e.g., 336) - should round up
|
||||
TEST_F(StagingBufferTest, SlotStrideSlightlyLargerThanPageSize) {
|
||||
size_t nSlots = 50;
|
||||
size_t slotPadToNBytes = 336; // Slightly larger than page size (4096)
|
||||
auto constraints = createTestConstraints(
|
||||
pageSize, // slotStartAlignment
|
||||
slotPadToNBytes, // slotPadToNBytes
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
// Should round up to next multiple of page size (4096)
|
||||
size_t expectedSlotStride = pageSize;
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||
<< "Slot stride 336 should round up to page size " << pageSize;
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
||||
<< "Buffer size should be page-aligned";
|
||||
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||
}
|
||||
|
||||
// Verify FrameAssemblyDesc
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
||||
// Test 4: Slot stride much larger than page size (e.g., 32256) - should round up to 32768
|
||||
TEST_F(StagingBufferTest, SlotStrideMuchLargerThanPageSize) {
|
||||
size_t nSlots = 100;
|
||||
size_t slotPadToNBytes = 32256; // Much larger than page size
|
||||
auto constraints = createTestConstraints(
|
||||
pageSize, // slotStartAlignment
|
||||
slotPadToNBytes, // slotPadToNBytes
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
// Verify first slot alignment
|
||||
if (!frameDesc->slots.empty()) {
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, pageSize, "First slot frame alignment");
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
// Should round up to next multiple of page size
|
||||
// 32256 / 4096 = 7.875, so rounds up to 8 pages = 32768
|
||||
size_t expectedSlotStride = ((slotPadToNBytes + pageSize - 1) / pageSize) * pageSize;
|
||||
EXPECT_EQ(expectedSlotStride, 32768u)
|
||||
<< "32256 should round up to 32768 (8 pages)";
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||
<< "Slot stride should be rounded up to " << expectedSlotStride;
|
||||
|
||||
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||
}
|
||||
|
||||
// Test 5: Slot stride already a multiple of page size - should remain unchanged
|
||||
TEST_F(StagingBufferTest, SlotStrideAlreadyMultipleOfPageSize) {
|
||||
size_t nSlots = 30;
|
||||
size_t slotPadToNBytes = pageSize * 3; // Already a multiple (e.g., 12288)
|
||||
auto constraints = createTestConstraints(
|
||||
pageSize, // slotStartAlignment
|
||||
slotPadToNBytes, // slotPadToNBytes (already multiple)
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
// Should remain unchanged
|
||||
size_t expectedSlotStride = slotPadToNBytes;
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||
<< "Slot stride already a multiple of page size should remain unchanged";
|
||||
|
||||
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||
}
|
||||
|
||||
// Test 6: Multiple different slot stride sizes in sequence
|
||||
TEST_F(StagingBufferTest, MultipleDifferentSlotStrideSizes) {
|
||||
// Test with various slot pad sizes
|
||||
struct TestCase {
|
||||
size_t slotPadToNBytes;
|
||||
size_t expectedRoundedStride;
|
||||
};
|
||||
|
||||
std::vector<TestCase> testCases = {
|
||||
{256, pageSize}, // Small: rounds to 1 page
|
||||
{pageSize, pageSize}, // Equal: stays 1 page
|
||||
{pageSize + 1, pageSize * 2}, // Slightly larger: rounds to 2 pages
|
||||
{32256, 32768}, // Much larger: rounds to 8 pages
|
||||
{pageSize * 5, pageSize * 5}, // Already multiple: stays 5 pages
|
||||
{pageSize * 10 + 100, pageSize * 11}, // Large with remainder: rounds to 11 pages
|
||||
};
|
||||
|
||||
for (const auto& testCase : testCases) {
|
||||
size_t nSlots = 10;
|
||||
auto constraints = createTestConstraints(
|
||||
pageSize,
|
||||
testCase.slotPadToNBytes,
|
||||
pageSize,
|
||||
pageSize);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, testCase.expectedRoundedStride)
|
||||
<< "Slot pad " << testCase.slotPadToNBytes
|
||||
<< " should round to " << testCase.expectedRoundedStride;
|
||||
|
||||
verifyAllSlotsPageAligned(buffer, testCase.expectedRoundedStride, pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4: OpenCL Intensity constraints
|
||||
TEST_F(StagingBufferTest, OpenClIntensityConstraints) {
|
||||
size_t nSlots = 909; // 30000ms / 33ms
|
||||
// Test 7: Real-world scenario - PcloudIntensityStimulusBuffer (32256 bytes)
|
||||
TEST_F(StagingBufferTest, RealWorldPcloudIntensityScenario) {
|
||||
size_t nSlots = 909; // histbuffMs=30000 / CONFIG_STIMBUFF_FRAME_PERIOD_MS=33
|
||||
size_t nDgramsPerFrame = 84;
|
||||
size_t nPointsPerDgram = 96;
|
||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 1; // 32256
|
||||
|
||||
auto constraints = createTestConstraints(
|
||||
sizeof(float), // slotStartAlignment
|
||||
pageSize, // slotStartAlignment
|
||||
slotPadToNBytes, // slotPadToNBytes
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
// Should round up to 32768 (8 pages)
|
||||
size_t expectedSlotStride = 32768;
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||
<< "Real-world 32256-byte slots should round to 32768";
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
||||
<< "Buffer size should be page-aligned";
|
||||
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||
|
||||
// Verify FrameAssemblyDesc
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
||||
}
|
||||
|
||||
// Test 5: OpenCL Ambience constraints
|
||||
TEST_F(StagingBufferTest, OpenClAmbienceConstraints) {
|
||||
size_t nSlots = 909; // 30000ms / 33ms
|
||||
// Test 8: Real-world scenario - PcloudAmbienceStimulusBuffer (336 bytes)
|
||||
TEST_F(StagingBufferTest, RealWorldPcloudAmbienceScenario) {
|
||||
size_t nSlots = 909;
|
||||
size_t nDgramsPerFrame = 84;
|
||||
size_t slotPadToNBytes = nDgramsPerFrame * sizeof(float); // 336
|
||||
|
||||
auto constraints = createTestConstraints(
|
||||
sizeof(float), // slotStartAlignment
|
||||
slotPadToNBytes, // slotPadToNBytes
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
||||
<< "Buffer size should be page-aligned";
|
||||
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
||||
}
|
||||
|
||||
// Test 6: io_uring constraints
|
||||
TEST_F(StagingBufferTest, IoUringConstraints) {
|
||||
size_t nSlots = 84;
|
||||
auto constraints = createTestConstraints(
|
||||
pageSize, // slotStartAlignment
|
||||
1472, // slotPadToNBytes
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(expectedSlotStride, pageSize) << "Slot stride should be max(alignment, padding) = pageSize";
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
||||
<< "Buffer size should be page-aligned";
|
||||
}
|
||||
|
||||
// Test 7: Slot alignment larger than padding
|
||||
TEST_F(StagingBufferTest, SlotAlignmentLargerThanPadding) {
|
||||
size_t nSlots = 10;
|
||||
auto constraints = createTestConstraints(64, 16, 64, 64);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
EXPECT_EQ(expectedSlotStride, 64u) << "Slot stride should be max(64, 16) = 64";
|
||||
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
if (!frameDesc->slots.empty()) {
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, 64, "First slot");
|
||||
}
|
||||
}
|
||||
|
||||
// Test 8: Frame alignment larger than slot alignment
|
||||
TEST_F(StagingBufferTest, FrameAlignmentLargerThanSlotAlignment) {
|
||||
size_t nSlots = 10;
|
||||
auto constraints = createTestConstraints(4, 16, pageSize, pageSize);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
||||
<< "Buffer size should be page-aligned";
|
||||
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
if (!frameDesc->slots.empty()) {
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, pageSize, "First slot frame alignment");
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, 4, "First slot slot alignment");
|
||||
}
|
||||
}
|
||||
|
||||
// Test 9: Slot alignment larger than frame alignment
|
||||
TEST_F(StagingBufferTest, SlotAlignmentLargerThanFrameAlignment) {
|
||||
size_t nSlots = 10;
|
||||
size_t largeAlignment = 8192;
|
||||
auto constraints = createTestConstraints(largeAlignment, 16, pageSize, pageSize);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % largeAlignment, 0u)
|
||||
<< "Buffer size should be aligned to max alignment";
|
||||
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
if (!frameDesc->slots.empty()) {
|
||||
verifyAlignment(frameDesc->slots[0].vaddr, largeAlignment, "First slot");
|
||||
}
|
||||
}
|
||||
|
||||
// Test 10: Minimum buffer size larger than calculated size
|
||||
TEST_F(StagingBufferTest, MinimumBufferSizeEnforcement) {
|
||||
size_t nSlots = 1;
|
||||
auto constraints = createTestConstraints(4, 16, 4, 1024);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
size_t actualBufferSize = buffer.getIoUringRegisterIoVec().iov_len;
|
||||
|
||||
EXPECT_GE(actualBufferSize, 1024u) << "Buffer size should be at least framePadToNBytes";
|
||||
EXPECT_EQ(actualBufferSize, expectedBufferSize);
|
||||
}
|
||||
|
||||
// Test 11: Actual PcloudIntensityStimulusBuffer scenario
|
||||
TEST_F(StagingBufferTest, RealWorldPcloudIntensityScenario) {
|
||||
size_t nSlots = 909; // histbuffMs=30000 / CONFIG_STIMBUFF_FRAME_PERIOD_MS=33
|
||||
size_t nDgramsPerFrame = 84;
|
||||
size_t nPointsPerDgram = 96; // SingleFirst/Strongest/Dual
|
||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 1; // 32256
|
||||
|
||||
auto constraints = createTestConstraints(
|
||||
sizeof(float), // slotStartAlignment
|
||||
slotPadToNBytes, // slotPadToNBytes
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
// Should round up to page size
|
||||
size_t expectedSlotStride = pageSize;
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||
<< "Real-world 336-byte slots should round to page size";
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||
|
||||
// Verify the buffer can hold all slots
|
||||
// Verify FrameAssemblyDesc
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||
|
||||
// Verify all slots fit within buffer
|
||||
if (!frameDesc->slots.empty()) {
|
||||
uint8_t* bufferStart = buffer.getIoUringRegisterIoVec().iov_base;
|
||||
size_t bufferSize = buffer.getIoUringRegisterIoVec().iov_len;
|
||||
uint8_t* lastSlotEnd = frameDesc->slots.back().vaddr + frameDesc->slots.back().nBytes;
|
||||
EXPECT_LE(lastSlotEnd - bufferStart, static_cast<ptrdiff_t>(bufferSize))
|
||||
<< "Last slot should fit within buffer";
|
||||
}
|
||||
}
|
||||
|
||||
// Test 12: Different return modes - Triple (90 points)
|
||||
TEST_F(StagingBufferTest, TripleReturnModeMesh) {
|
||||
size_t nSlots = 909;
|
||||
size_t nDgramsPerFrame = 84;
|
||||
size_t nPointsPerDgram = 90; // Triple return mode
|
||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 3; // 90720
|
||||
|
||||
auto constraints = createTestConstraints(
|
||||
sizeof(float), // slotStartAlignment
|
||||
slotPadToNBytes, // slotPadToNBytes
|
||||
pageSize, // frameStartAlignment
|
||||
pageSize); // framePadToNBytes
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
||||
|
||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
||||
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
||||
}
|
||||
|
||||
// Test 13: Verify firstSlotOffsetNBytes is valid
|
||||
TEST_F(StagingBufferTest, FirstSlotOffsetValidation) {
|
||||
size_t nSlots = 100;
|
||||
auto constraints = createTestConstraints(4, 16, pageSize, pageSize);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
EXPECT_LE(buffer.firstSlotOffsetNBytes, buffer.getIoUringRegisterIoVec().iov_len)
|
||||
<< "First slot offset should be within buffer";
|
||||
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
if (!frameDesc->slots.empty()) {
|
||||
uint8_t* bufferStart = buffer.getIoUringRegisterIoVec().iov_base;
|
||||
uint8_t* firstSlot = frameDesc->slots[0].vaddr;
|
||||
size_t calculatedOffset = firstSlot - bufferStart;
|
||||
EXPECT_EQ(calculatedOffset, buffer.firstSlotOffsetNBytes)
|
||||
<< "First slot offset should match calculated value";
|
||||
}
|
||||
}
|
||||
|
||||
// Test 14: Verify all slots are properly aligned
|
||||
TEST_F(StagingBufferTest, AllSlotsProperlyAligned) {
|
||||
size_t nSlots = 50;
|
||||
auto constraints = createTestConstraints(64, 64, 64, 64);
|
||||
|
||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||
|
||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||
ASSERT_NE(frameDesc, nullptr);
|
||||
|
||||
for (size_t i = 0; i < frameDesc->slots.size(); ++i) {
|
||||
verifyAlignment(frameDesc->slots[i].vaddr, constraints.slotStartAlignmentByteVal,
|
||||
"Slot " + std::to_string(i));
|
||||
verifyAlignment(frameDesc->slots[i].vaddr, constraints.frameStartAlignmentByteVal,
|
||||
"Slot " + std::to_string(i) + " frame alignment");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stim_buff
|
||||
} // namespace smo
|
||||
|
||||
|
||||
Reference in New Issue
Block a user