xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrRingBuffer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 #include "src/gpu/ganesh/GrRingBuffer.h"
8 
9 #include "include/gpu/ganesh/GrDirectContext.h"
10 #include "include/private/base/SkAlign.h"
11 #include "include/private/gpu/ganesh/GrTypesPriv.h"
12 #include "src/gpu/RefCntedCallback.h"
13 #include "src/gpu/ganesh/GrDirectContextPriv.h"
14 #include "src/gpu/ganesh/GrGpu.h"
15 #include "src/gpu/ganesh/GrResourceProvider.h"
16 
17 #include <utility>
18 
19 // Get offset into buffer that has enough space for size
20 // Returns fTotalSize if no space
getAllocationOffset(size_t size)21 size_t GrRingBuffer::getAllocationOffset(size_t size) {
22     // capture current state locally (because fTail could be overwritten by the completion handler)
23     size_t head, tail;
24     head = fHead;
25     tail = fTail;
26 
27     // The head and tail indices increment without bound, wrapping with overflow,
28     // so we need to mod them down to the actual bounds of the allocation to determine
29     // which blocks are available.
30     size_t modHead = head & (fTotalSize - 1);
31     size_t modTail = tail & (fTotalSize - 1);
32 
33     bool full = (head != tail && modHead == modTail);
34 
35     if (full) {
36         return fTotalSize;
37     }
38 
39     // case 1: free space lies at the beginning and/or the end of the buffer
40     if (modHead >= modTail) {
41         // check for room at the end
42         if (fTotalSize - modHead < size) {
43             // no room at the end, check the beginning
44             if (modTail < size) {
45                 // no room at the beginning
46                 return fTotalSize;
47             }
48             // we are going to allocate from the beginning, adjust head to '0' position
49             head += fTotalSize - modHead;
50             modHead = 0;
51         }
52         // case 2: free space lies in the middle of the buffer, check for room there
53     } else if (modTail - modHead < size) {
54         // no room in the middle
55         return fTotalSize;
56     }
57 
58     fHead = SkAlignTo(head + size, fAlignment);
59     return modHead;
60 }
61 
suballocate(size_t size)62 GrRingBuffer::Slice GrRingBuffer::suballocate(size_t size) {
63     fNewAllocation = true;
64     if (fCurrentBuffer) {
65         size_t offset = this->getAllocationOffset(size);
66         if (offset < fTotalSize) {
67             return { fCurrentBuffer.get(), offset };
68         }
69 
70         // Try to grow allocation (old allocation will age out).
71         fTotalSize *= 2;
72         // Add current buffer to be tracked for next submit.
73         fPreviousBuffers.push_back(std::move(fCurrentBuffer));
74     }
75 
76     GrResourceProvider* resourceProvider = fGpu->getContext()->priv().resourceProvider();
77     fCurrentBuffer = resourceProvider->createBuffer(fTotalSize,
78                                                     fType,
79                                                     kDynamic_GrAccessPattern,
80                                                     GrResourceProvider::ZeroInit::kNo);
81 
82     SkASSERT(fCurrentBuffer);
83     fHead = 0;
84     fTail = 0;
85     fGenID++;
86     size_t offset = this->getAllocationOffset(size);
87     SkASSERT(offset < fTotalSize);
88     return { fCurrentBuffer.get(), offset };
89 }
90 
91 // used when current command buffer/command list is submitted
startSubmit(GrGpu * gpu)92 void GrRingBuffer::startSubmit(GrGpu* gpu) {
93     for (unsigned int i = 0; i < fPreviousBuffers.size(); ++i) {
94         fPreviousBuffers[i]->unmap();
95         gpu->takeOwnershipOfBuffer(std::move(fPreviousBuffers[i]));
96     }
97     fPreviousBuffers.clear();
98 
99     if (fNewAllocation) {
100 #ifdef SK_BUILD_FOR_MAC
101         // Since we're using a Managed buffer on MacOS we need to unmap to write back to GPU
102         // TODO: once we set up persistently mapped UPLOAD buffers on D3D, we can remove the
103         // platform restriction.
104         fCurrentBuffer->unmap();
105 #endif
106         SubmitData* submitData = new SubmitData();
107         submitData->fOwner = this;
108         submitData->fLastHead = fHead;
109         submitData->fGenID = fGenID;
110         gpu->addFinishedCallback(skgpu::AutoCallback(FinishSubmit, submitData));
111         fNewAllocation = false;
112     }
113 }
114 
115 // used when current command buffer/command list is completed
FinishSubmit(void * finishedContext)116 void GrRingBuffer::FinishSubmit(void* finishedContext) {
117     GrRingBuffer::SubmitData* submitData = (GrRingBuffer::SubmitData*)finishedContext;
118     if (submitData && submitData->fOwner && submitData->fGenID == submitData->fOwner->fGenID) {
119         submitData->fOwner->fTail = submitData->fLastHead;
120         submitData->fOwner = nullptr;
121     }
122     delete submitData;
123 }
124