1 /*
2  * Copyright 2020 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/GrRingBuffer.h"
9 
10 #include "src/gpu/GrDirectContextPriv.h"
11 #include "src/gpu/GrGpu.h"
12 #include "src/gpu/GrResourceProvider.h"
13 
14 // Get offset into buffer that has enough space for size
15 // Returns fTotalSize if no space
getAllocationOffset(size_t size)16 size_t GrRingBuffer::getAllocationOffset(size_t size) {
17     // capture current state locally (because fTail could be overwritten by the completion handler)
18     size_t head, tail;
19     head = fHead;
20     tail = fTail;
21 
22     // The head and tail indices increment without bound, wrapping with overflow,
23     // so we need to mod them down to the actual bounds of the allocation to determine
24     // which blocks are available.
25     size_t modHead = head & (fTotalSize - 1);
26     size_t modTail = tail & (fTotalSize - 1);
27 
28     bool full = (head != tail && modHead == modTail);
29 
30     if (full) {
31         return fTotalSize;
32     }
33 
34     // case 1: free space lies at the beginning and/or the end of the buffer
35     if (modHead >= modTail) {
36         // check for room at the end
37         if (fTotalSize - modHead < size) {
38             // no room at the end, check the beginning
39             if (modTail < size) {
40                 // no room at the beginning
41                 return fTotalSize;
42             }
43             // we are going to allocate from the beginning, adjust head to '0' position
44             head += fTotalSize - modHead;
45             modHead = 0;
46         }
47         // case 2: free space lies in the middle of the buffer, check for room there
48     } else if (modTail - modHead < size) {
49         // no room in the middle
50         return fTotalSize;
51     }
52 
53     fHead = GrAlignTo(head + size, fAlignment);
54     return modHead;
55 }
56 
suballocate(size_t size)57 GrRingBuffer::Slice GrRingBuffer::suballocate(size_t size) {
58     if (fCurrentBuffer) {
59         size_t offset = this->getAllocationOffset(size);
60         if (offset < fTotalSize) {
61             return { fCurrentBuffer.get(), offset };
62         }
63 
64         // Try to grow allocation (old allocation will age out).
65         fTotalSize *= 2;
66     }
67 
68     GrResourceProvider* resourceProvider = fGpu->getContext()->priv().resourceProvider();
69     fCurrentBuffer = resourceProvider->createBuffer(fTotalSize, fType, kDynamic_GrAccessPattern);
70 
71     SkASSERT(fCurrentBuffer);
72     fTrackedBuffers.push_back(fCurrentBuffer);
73     fHead = 0;
74     fTail = 0;
75     fGenID++;
76     size_t offset = this->getAllocationOffset(size);
77     SkASSERT(offset < fTotalSize);
78     return { fCurrentBuffer.get(), offset };
79 }
80 
81 // used when current command buffer/command list is submitted
startSubmit(GrGpu * gpu)82 void GrRingBuffer::startSubmit(GrGpu* gpu) {
83     for (unsigned int i = 0; i < fTrackedBuffers.size(); ++i) {
84         gpu->takeOwnershipOfBuffer(std::move(fTrackedBuffers[i]));
85     }
86     fTrackedBuffers.clear();
87     // add current buffer to be tracked for next submit
88     fTrackedBuffers.push_back(fCurrentBuffer);
89 
90     SubmitData* submitData = new SubmitData();
91     submitData->fOwner = this;
92     submitData->fLastHead = fHead;
93     submitData->fGenID = fGenID;
94     gpu->addFinishedProc(FinishSubmit, submitData);
95 }
96 
97 // used when current command buffer/command list is completed
FinishSubmit(void * finishedContext)98 void GrRingBuffer::FinishSubmit(void* finishedContext) {
99     GrRingBuffer::SubmitData* submitData = (GrRingBuffer::SubmitData*)finishedContext;
100     if (submitData && submitData->fOwner && submitData->fGenID == submitData->fOwner->fGenID) {
101         submitData->fOwner->fTail = submitData->fLastHead;
102         submitData->fOwner = nullptr;
103     }
104     delete submitData;
105 }
106