|  | /* | 
|  | * Copyright 2020 Google LLC | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "src/gpu/GrRingBuffer.h" | 
|  |  | 
|  | #include "src/gpu/GrDirectContextPriv.h" | 
|  | #include "src/gpu/GrGpu.h" | 
|  | #include "src/gpu/GrResourceProvider.h" | 
|  |  | 
|  | // Get offset into buffer that has enough space for size | 
|  | // Returns fTotalSize if no space | 
|  | size_t GrRingBuffer::getAllocationOffset(size_t size) { | 
|  | // capture current state locally (because fTail could be overwritten by the completion handler) | 
|  | size_t head, tail; | 
|  | head = fHead; | 
|  | tail = fTail; | 
|  |  | 
|  | // The head and tail indices increment without bound, wrapping with overflow, | 
|  | // so we need to mod them down to the actual bounds of the allocation to determine | 
|  | // which blocks are available. | 
|  | size_t modHead = head & (fTotalSize - 1); | 
|  | size_t modTail = tail & (fTotalSize - 1); | 
|  |  | 
|  | bool full = (head != tail && modHead == modTail); | 
|  |  | 
|  | if (full) { | 
|  | return fTotalSize; | 
|  | } | 
|  |  | 
|  | // case 1: free space lies at the beginning and/or the end of the buffer | 
|  | if (modHead >= modTail) { | 
|  | // check for room at the end | 
|  | if (fTotalSize - modHead < size) { | 
|  | // no room at the end, check the beginning | 
|  | if (modTail < size) { | 
|  | // no room at the beginning | 
|  | return fTotalSize; | 
|  | } | 
|  | // we are going to allocate from the beginning, adjust head to '0' position | 
|  | head += fTotalSize - modHead; | 
|  | modHead = 0; | 
|  | } | 
|  | // case 2: free space lies in the middle of the buffer, check for room there | 
|  | } else if (modTail - modHead < size) { | 
|  | // no room in the middle | 
|  | return fTotalSize; | 
|  | } | 
|  |  | 
|  | fHead = GrAlignTo(head + size, fAlignment); | 
|  | return modHead; | 
|  | } | 
|  |  | 
|  | GrRingBuffer::Slice GrRingBuffer::suballocate(size_t size) { | 
|  | fNewAllocation = true; | 
|  | if (fCurrentBuffer) { | 
|  | size_t offset = this->getAllocationOffset(size); | 
|  | if (offset < fTotalSize) { | 
|  | return { fCurrentBuffer.get(), offset }; | 
|  | } | 
|  |  | 
|  | // Try to grow allocation (old allocation will age out). | 
|  | fTotalSize *= 2; | 
|  | // Add current buffer to be tracked for next submit. | 
|  | fPreviousBuffers.push_back(std::move(fCurrentBuffer)); | 
|  | } | 
|  |  | 
|  | GrResourceProvider* resourceProvider = fGpu->getContext()->priv().resourceProvider(); | 
|  | fCurrentBuffer = resourceProvider->createBuffer(fTotalSize, fType, kDynamic_GrAccessPattern); | 
|  |  | 
|  | SkASSERT(fCurrentBuffer); | 
|  | fHead = 0; | 
|  | fTail = 0; | 
|  | fGenID++; | 
|  | size_t offset = this->getAllocationOffset(size); | 
|  | SkASSERT(offset < fTotalSize); | 
|  | return { fCurrentBuffer.get(), offset }; | 
|  | } | 
|  |  | 
|  | // used when current command buffer/command list is submitted | 
|  | void GrRingBuffer::startSubmit(GrGpu* gpu) { | 
|  | for (unsigned int i = 0; i < fPreviousBuffers.size(); ++i) { | 
|  | fPreviousBuffers[i]->unmap(); | 
|  | gpu->takeOwnershipOfBuffer(std::move(fPreviousBuffers[i])); | 
|  | } | 
|  | fPreviousBuffers.clear(); | 
|  |  | 
|  | if (fNewAllocation) { | 
|  | #ifdef SK_BUILD_FOR_MAC | 
|  | // Since we're using a Managed buffer on MacOS we need to unmap to write back to GPU | 
|  | // TODO: once we set up persistently mapped UPLOAD buffers on D3D, we can remove the | 
|  | // platform restriction. | 
|  | fCurrentBuffer->unmap(); | 
|  | #endif | 
|  | SubmitData* submitData = new SubmitData(); | 
|  | submitData->fOwner = this; | 
|  | submitData->fLastHead = fHead; | 
|  | submitData->fGenID = fGenID; | 
|  | gpu->addFinishedProc(FinishSubmit, submitData); | 
|  | fNewAllocation = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // used when current command buffer/command list is completed | 
|  | void GrRingBuffer::FinishSubmit(void* finishedContext) { | 
|  | GrRingBuffer::SubmitData* submitData = (GrRingBuffer::SubmitData*)finishedContext; | 
|  | if (submitData && submitData->fOwner && submitData->fGenID == submitData->fOwner->fGenID) { | 
|  | submitData->fOwner->fTail = submitData->fLastHead; | 
|  | submitData->fOwner = nullptr; | 
|  | } | 
|  | delete submitData; | 
|  | } |