diff --git a/MobileGL/MG_Test/Buffer/BufferTest.cpp b/MobileGL/MG_Test/Buffer/BufferTest.cpp index 6116fb3e..a6df4ec2 100644 --- a/MobileGL/MG_Test/Buffer/BufferTest.cpp +++ b/MobileGL/MG_Test/Buffer/BufferTest.cpp @@ -62,3 +62,246 @@ TEST_F(BufferTest, PingPong) { ASSERT_EQ(range.start, 0); ASSERT_EQ(range.end, byte_size); } + +TEST_F(BufferTest, GenerateManyNames_NoPrematureCreation) { + const SizeT largeCount = 100000; // generate tons of buffer names + auto names = glContext.GenBufferNames(largeCount); + + std::vector indices = { 0, 600, 5000, 32768, 99999 }; // only create a few buffer objects + for (SizeT idx : indices) { + GLuint name = names[idx]; + auto bufObj = glContext.CreateBufferObject(name); + auto& slot = glContext.GetBufferBindingSlot(BufferTarget::Index); + slot.Bind(bufObj); + + Vector data = { static_cast(idx + 1), static_cast(idx + 2) }; + SizeT byteSize = data.size() * sizeof(Int); + bufObj->Resize(byteSize); + DataPtr ptr{ .data = data.data(), .size = byteSize }; + bufObj->UploadData(ptr, 0); + + Vector actual(data.size()); + void* p = bufObj->AcquireMemory(false, true, false); + memcpy(actual.data(), p, byteSize); + EXPECT_EQ(actual, data); + } +} + +TEST_F(BufferTest, AcquireMemory) { + auto& slot = glContext.GetBufferBindingSlot(BufferTarget::Uniform); + auto bufferNames = glContext.GenBufferNames(1); + auto bufObj = glContext.CreateBufferObject(bufferNames[0]); + slot.Bind(bufObj); + Vector initData{ 10, 20, 30, 40, 50 }; + SizeT byteSize = initData.size() * sizeof(Int); + bufObj->Resize(byteSize); + DataPtr ptr{ .data = initData.data(), .size = byteSize }; + bufObj->UploadData(ptr, 0); + bufObj->ClearDirty(); + Int* mappedPtr = static_cast(bufObj->AcquireMemory(true, true, true)); + mappedPtr[0] = 100; + mappedPtr[1] = 200; + mappedPtr[2] = 300; + bufObj->ReleaseMemory(); + Vector expected{ 100, 200, 300, 40, 50 }; + Vector actual(5); + void* p = bufObj->AcquireMemory(false, true, false); + memcpy(actual.data(), p, byteSize); + ASSERT_EQ(actual, expected); + auto dirty = bufObj->GetDirtyRange(); + + ASSERT_EQ(dirty.start, 0); + ASSERT_EQ(dirty.end, sizeof(Int) * 5); +} + +TEST_F(BufferTest, AcquireMemoryRangeWithoutExplicit) { + auto& slot = glContext.GetBufferBindingSlot(BufferTarget::Uniform); + auto bufferNames = glContext.GenBufferNames(1); + auto bufObj = glContext.CreateBufferObject(bufferNames[0]); + slot.Bind(bufObj); + Vector initData{ 10, 20, 30, 40, 50 }; + SizeT byteSize = initData.size() * sizeof(Int); + bufObj->Resize(byteSize); + DataPtr ptr{ .data = initData.data(), .size = byteSize }; + bufObj->UploadData(ptr, 0); + bufObj->ClearDirty(); + + Range1D mapRange{ + .start = sizeof(Int), + .end = sizeof(Int) * 4 + }; + Int* mappedPtr = static_cast(bufObj->AcquireMemoryRange( + mapRange, + BufferMappingAccessBit::Write + )); + mappedPtr[0] = 200; + mappedPtr[1] = 300; + bufObj->ReleaseMemory(); + Vector expected{ 10, 200, 300, 40, 50 }; + Vector actual(5); + void* p = bufObj->AcquireMemory(false, true, false); + memcpy(actual.data(), p, byteSize); + ASSERT_EQ(actual, expected); + auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(dirty.start, sizeof(Int)); + ASSERT_EQ(dirty.end, sizeof(Int) * 4); +} + +TEST_F(BufferTest, AcquireMemoryRangeWithExplicit) { + auto& slot = glContext.GetBufferBindingSlot(BufferTarget::Uniform); + auto bufferNames = glContext.GenBufferNames(1); + auto bufObj = glContext.CreateBufferObject(bufferNames[0]); + slot.Bind(bufObj); + + Vector initData{ 10, 20, 30, 40, 50 }; + SizeT byteSize = initData.size() * sizeof(Int); + bufObj->Resize(byteSize); + DataPtr ptr{ .data = initData.data(), .size = byteSize }; + bufObj->UploadData(ptr, 0); + + bufObj->ClearDirty(); + + Range1D mapRange{ + .start = sizeof(Int), + .end = sizeof(Int) * 4 + }; + Int* mappedPtr = static_cast(bufObj->AcquireMemoryRange( + mapRange, + BufferMappingAccessBit::Write | BufferMappingAccessBit::FlushExplicit + )); + + mappedPtr[0] = 200; + mappedPtr[1] = 300; + + bufObj->FlushMemoryRange(0, sizeof(Int)); + auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(dirty.start, sizeof(Int)); + ASSERT_EQ(dirty.end, sizeof(Int) * 2); + + bufObj->ReleaseMemory(); + + dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(dirty.start, sizeof(Int)); + ASSERT_EQ(dirty.end, sizeof(Int) * 2); + + Vector expected{ 10, 200, 30, 40, 50 }; + Vector actual(5); + void* p = bufObj->AcquireMemory(false, true, false); + memcpy(actual.data(), p, byteSize); + ASSERT_EQ(actual, expected); + + dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(dirty.start, sizeof(Int)); + ASSERT_EQ(dirty.end, sizeof(Int) * 2); +} + +TEST_F(BufferTest, CopyBufferSubData) { + auto& srcSlot = glContext.GetBufferBindingSlot(BufferTarget::CopyRead); + auto& dstSlot = glContext.GetBufferBindingSlot(BufferTarget::CopyWrite); + + auto srcNames = glContext.GenBufferNames(1); + auto srcObj = glContext.CreateBufferObject(srcNames[0]); + srcSlot.Bind(srcObj); + + Vector srcData{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + SizeT srcSize = srcData.size() * sizeof(Int); + srcObj->Resize(srcSize); + DataPtr srcPtr{ .data = srcData.data(), .size = srcSize }; + srcObj->UploadData(srcPtr, 0); + + auto dstNames = glContext.GenBufferNames(1); + auto dstObj = glContext.CreateBufferObject(dstNames[0]); + dstSlot.Bind(dstObj); + + Vector dstData(15, 0); + SizeT dstSize = dstData.size() * sizeof(Int); + dstObj->Resize(dstSize); + DataPtr dstPtr{ .data = dstData.data(), .size = dstSize }; + dstObj->UploadData(dstPtr, 0); + + srcObj->ClearDirty(); + dstObj->ClearDirty(); + + dstObj->CopyDataFrom(srcObj, + 2 * sizeof(Int), + 5 * sizeof(Int), + 4 * sizeof(Int)); + + Vector expected{ + 0, 0, 0, 0, 0, + 3, 4, 5, 6, + 0, 0, 0, 0, 0, 0 + }; + + Vector actual(15); + void* p = dstObj->AcquireMemory(false, true, false); + memcpy(actual.data(), p, dstSize); + + ASSERT_EQ(actual, expected); + + auto dirty = dstObj->GetDirtyRange(); + ASSERT_EQ(dirty.start, 5 * sizeof(Int)); + ASSERT_EQ(dirty.end, 9 * sizeof(Int)); +} + +TEST_F(BufferTest, WriteWhileMapped) { + auto& slot = glContext.GetBufferBindingSlot(BufferTarget::ShaderStorage); + auto bufferNames = glContext.GenBufferNames(1); + auto bufObj = glContext.CreateBufferObject(bufferNames[0]); + slot.Bind(bufObj); + + Vector initData(10, 0); + SizeT byteSize = initData.size() * sizeof(Int); + bufObj->Resize(byteSize); + + Int* mappedPtr = static_cast(bufObj->AcquireMemory(true, true, true)); + + for (int i = 0; i < 10; i++) { + mappedPtr[i] = i * 10; + } + + bufObj->ReleaseMemory(); + + Vector expected{ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }; + Vector actual(10); + void* p = bufObj->AcquireMemory(false, true, false); + memcpy(actual.data(), p, byteSize); + + ASSERT_EQ(actual, expected); + + auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(dirty.start, 0); + ASSERT_EQ(dirty.end, byteSize); +} + +TEST_F(BufferTest, PartialUpdate) { + auto& slot = glContext.GetBufferBindingSlot(BufferTarget::Vertex); + auto bufferNames = glContext.GenBufferNames(1); + auto bufObj = glContext.CreateBufferObject(bufferNames[0]); + slot.Bind(bufObj); + + Vector initData{ 100, 200, 300, 400, 500 }; + SizeT byteSize = initData.size() * sizeof(Int); + bufObj->Resize(byteSize); + DataPtr ptr{ .data = initData.data(), .size = byteSize }; + bufObj->UploadData(ptr, 0); + bufObj->ClearDirty(); + + Vector update{ 999, 888 }; + bufObj->UploadSubData( + sizeof(Int), + update.size() * sizeof(Int), + update.data() + ); + + Vector expected{ 100, 999, 888, 400, 500 }; + Vector actual(5); + void* p = bufObj->AcquireMemory(false, true, false); + memcpy(actual.data(), p, byteSize); + + ASSERT_EQ(actual, expected); + + auto dirty = bufObj->GetDirtyRange(); + ASSERT_EQ(dirty.start, sizeof(Int)); + ASSERT_EQ(dirty.end, 3 * sizeof(Int)); +}