Create ring buffer for managing D3D uniforms.
* Adds a base class for the ring buffer (to be used by Metal as well),
which tracks the current available space. APIs will need to
implement creation of the buffer in the subclass.
* The API implementation will need to store SubmitData on command buffer
submit, and then pass it to finishSubmit when the command buffer
finishes.
Change-Id: I4cc5e4a72d259ee9d15dac0e964819d4562da3d7
Bug: skia:9935
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/291936
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/GrRingBuffer.cpp b/src/gpu/GrRingBuffer.cpp
new file mode 100644
index 0000000..c901428
--- /dev/null
+++ b/src/gpu/GrRingBuffer.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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"
+
+// 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;
+ SkAutoSpinlock lock(fMutex);
+ 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) {
+ size_t offset = this->getAllocationOffset(size);
+ if (offset < fTotalSize) {
+ return { fBuffer, offset };
+ }
+
+ // Try to grow allocation (old allocation will age out).
+ fTotalSize *= 2;
+ fBuffer = this->createBuffer(fTotalSize);
+ SkASSERT(fBuffer);
+ SkAutoSpinlock lock(fMutex);
+ fHead = 0;
+ fTail = 0;
+ fGenID++;
+ offset = this->getAllocationOffset(size);
+ SkASSERT(offset < fTotalSize);
+ return { fBuffer, offset };
+}
+
+// used when current command buffer/command list is submitted
+GrRingBuffer::SubmitData GrRingBuffer::startSubmit() {
+ SubmitData submitData;
+ SkAutoSpinlock lock(fMutex);
+ submitData.fBuffer = fBuffer;
+ submitData.fLastHead = fHead;
+ submitData.fGenID = fGenID;
+ return submitData;
+}
+
+// used when current command buffer/command list is completed
+void GrRingBuffer::finishSubmit(const GrRingBuffer::SubmitData& submitData) {
+ SkAutoSpinlock lock(fMutex);
+ if (submitData.fGenID == fGenID) {
+ fTail = submitData.fLastHead;
+ }
+}