1 // Copyright 2018 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <gtest/gtest.h>
16
17 #include "dawn_native/null/DeviceNull.h"
18
19 using namespace dawn_native;
20
21 namespace {
22
ValidateValidUploadHandle(const UploadHandle & uploadHandle)23 size_t ValidateValidUploadHandle(const UploadHandle& uploadHandle) {
24 ASSERT(uploadHandle.mappedBuffer != nullptr);
25 return uploadHandle.startOffset;
26 }
27
ValidateInvalidUploadHandle(const UploadHandle & uploadHandle)28 void ValidateInvalidUploadHandle(const UploadHandle& uploadHandle) {
29 ASSERT_EQ(uploadHandle.mappedBuffer, nullptr);
30 }
31 } // namespace
32
33 class RingBufferTests : public testing::Test {
34 protected:
SetUp()35 void SetUp() override {
36 // TODO(bryan.bernhart@intel.com): Create this device through the adapter.
37 mDevice = std::make_unique<null::Device>(/*adapter*/ nullptr, /*deviceDescriptor*/ nullptr);
38 }
39
GetDevice() const40 null::Device* GetDevice() const {
41 return mDevice.get();
42 }
43
CreateRingBuffer(size_t size)44 std::unique_ptr<RingBuffer> CreateRingBuffer(size_t size) {
45 std::unique_ptr<RingBuffer> ringBuffer = std::make_unique<RingBuffer>(mDevice.get(), size);
46 DAWN_UNUSED(ringBuffer->Initialize());
47 return ringBuffer;
48 }
49
50 private:
51 std::unique_ptr<null::Device> mDevice;
52 };
53
54 // Number of basic tests for Ringbuffer
TEST_F(RingBufferTests,BasicTest)55 TEST_F(RingBufferTests, BasicTest) {
56 constexpr size_t sizeInBytes = 64000;
57 std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(sizeInBytes);
58
59 // Ensure no requests exist on empty buffer.
60 EXPECT_TRUE(buffer->Empty());
61
62 ASSERT_EQ(buffer->GetSize(), sizeInBytes);
63
64 // Ensure failure upon sub-allocating an oversized request.
65 ValidateInvalidUploadHandle(buffer->SubAllocate(sizeInBytes + 1));
66
67 // Fill the entire buffer with two requests of equal size.
68 ValidateValidUploadHandle(buffer->SubAllocate(sizeInBytes / 2));
69 ValidateValidUploadHandle(buffer->SubAllocate(sizeInBytes / 2));
70
71 // Ensure the buffer is full.
72 ValidateInvalidUploadHandle(buffer->SubAllocate(1));
73 }
74
75 // Tests that several ringbuffer allocations do not fail.
TEST_F(RingBufferTests,RingBufferManyAlloc)76 TEST_F(RingBufferTests, RingBufferManyAlloc) {
77 constexpr size_t maxNumOfFrames = 64000;
78 constexpr size_t frameSizeInBytes = 4;
79
80 std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(maxNumOfFrames * frameSizeInBytes);
81
82 size_t offset = 0;
83 for (size_t i = 0; i < maxNumOfFrames; ++i) {
84 offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
85 GetDevice()->Tick();
86 ASSERT_EQ(offset, i * frameSizeInBytes);
87 }
88 }
89
90 // Tests ringbuffer sub-allocations of the same serial are correctly tracked.
TEST_F(RingBufferTests,AllocInSameFrame)91 TEST_F(RingBufferTests, AllocInSameFrame) {
92 constexpr size_t maxNumOfFrames = 3;
93 constexpr size_t frameSizeInBytes = 4;
94
95 std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(maxNumOfFrames * frameSizeInBytes);
96
97 // F1
98 // [xxxx|--------]
99
100 ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
101 GetDevice()->Tick();
102
103 // F1 F2
104 // [xxxx|xxxx|----]
105
106 ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
107
108 // F1 F2
109 // [xxxx|xxxxxxxx]
110
111 size_t offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
112
113 ASSERT_EQ(offset, 8u);
114 ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 3);
115
116 buffer->Tick(1);
117
118 // Used size does not change as previous sub-allocations were not tracked.
119 ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 3);
120
121 buffer->Tick(2);
122
123 ASSERT_EQ(buffer->GetUsedSize(), 0u);
124 EXPECT_TRUE(buffer->Empty());
125 }
126
127 // Tests ringbuffer sub-allocation at various offsets.
TEST_F(RingBufferTests,RingBufferSubAlloc)128 TEST_F(RingBufferTests, RingBufferSubAlloc) {
129 constexpr size_t maxNumOfFrames = 10;
130 constexpr size_t frameSizeInBytes = 4;
131
132 std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(maxNumOfFrames * frameSizeInBytes);
133
134 // Sub-alloc the first eight frames.
135 for (size_t i = 0; i < 8; ++i) {
136 ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
137 buffer->Track();
138 GetDevice()->Tick();
139 }
140
141 // Each frame corrresponds to the serial number (for simplicity).
142 //
143 // F1 F2 F3 F4 F5 F6 F7 F8
144 // [xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|--------]
145 //
146
147 // Ensure an oversized allocation fails (only 8 bytes left)
148 ValidateInvalidUploadHandle(buffer->SubAllocate(frameSizeInBytes * 3));
149 ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 8);
150
151 // Reclaim the first 3 frames.
152 buffer->Tick(3);
153
154 // F4 F5 F6 F7 F8
155 // [------------|xxxx|xxxx|xxxx|xxxx|xxxx|--------]
156 //
157 ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 5);
158
159 // Re-try the over-sized allocation.
160 size_t offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes * 3));
161
162 // F9 F4 F5 F6 F7 F8
163 // [xxxxxxxxxxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxxxxxx]
164 // ^^^^^^^^ wasted
165
166 // In this example, Tick(8) could not reclaim the wasted bytes. The wasted bytes
167 // were add to F9's sub-allocation.
168 // TODO(bryan.bernhart@intel.com): Decide if Tick(8) should free these wasted bytes.
169
170 ASSERT_EQ(offset, 0u);
171 ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * maxNumOfFrames);
172
173 // Ensure we are full.
174 ValidateInvalidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
175
176 // Reclaim the next two frames.
177 buffer->Tick(5);
178
179 // F9 F4 F5 F6 F7 F8
180 // [xxxxxxxxxxxx|----|----|xxxx|xxxx|xxxx|xxxxxxxx]
181 //
182 ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 8);
183
184 // Sub-alloc the chunk in the middle.
185 offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes * 2));
186
187 ASSERT_EQ(offset, frameSizeInBytes * 3);
188 ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * maxNumOfFrames);
189
190 // F9 F6 F7 F8
191 // [xxxxxxxxxxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxxxxxx]
192 // ^^^^^^^^^ untracked
193
194 // Ensure we are full.
195 ValidateInvalidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
196
197 // Reclaim all.
198 buffer->Tick(maxNumOfFrames);
199
200 EXPECT_TRUE(buffer->Empty());
201 }
202