1/*
2 * Copyright 2021 Google Inc.
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 "experimental/graphite/src/DrawBufferManager.h"
9
10#include "experimental/graphite/src/Buffer.h"
11#include "experimental/graphite/src/CommandBuffer.h"
12#include "experimental/graphite/src/ResourceProvider.h"
13
14namespace skgpu {
15
16// TODO: Tune these values on real world data
17static constexpr size_t kVertexBufferSize = 2 << 10;
18static constexpr size_t kIndexBufferSize = 2 << 10;
19static constexpr size_t kUniformBufferSize = 2 << 10;
20
21DrawBufferManager::DrawBufferManager(ResourceProvider* resourceProvider,
22                                     size_t uniformStartAlignment)
23        : fResourceProvider(resourceProvider)
24        , fUniformStartAlignment(uniformStartAlignment) {}
25
26DrawBufferManager::~DrawBufferManager() {}
27
28static bool can_fit(size_t requestedSize,
29                    Buffer* buffer,
30                    size_t currentOffset,
31                    size_t alignment) {
32    size_t startOffset = SkAlignTo(currentOffset, alignment);
33    return requestedSize <= (buffer->size() - startOffset);
34}
35
36std::tuple<VertexWriter, BindBufferInfo> DrawBufferManager::getVertexWriter(size_t requiredBytes) {
37    if (!requiredBytes) {
38        BindBufferInfo bindInfo;
39        bindInfo.fBuffer = nullptr;
40        bindInfo.fOffset = 0;
41        return {VertexWriter(nullptr), bindInfo};
42    }
43    if (fCurrentVertexBuffer &&
44        !can_fit(requiredBytes, fCurrentVertexBuffer.get(), fVertexOffset, /*alignment=*/1)) {
45        fUsedBuffers.push_back(std::move(fCurrentVertexBuffer));
46    }
47
48    if (!fCurrentVertexBuffer) {
49        SkASSERT(requiredBytes <= kVertexBufferSize);
50        fCurrentVertexBuffer = fResourceProvider->findOrCreateBuffer(kVertexBufferSize,
51                                                                     BufferType::kVertex,
52                                                                     PrioritizeGpuReads::kNo);
53        fVertexOffset = 0;
54        if (!fCurrentVertexBuffer) {
55            return {VertexWriter(), BindBufferInfo()};
56        }
57    }
58    BindBufferInfo bindInfo;
59    bindInfo.fBuffer = fCurrentVertexBuffer.get();
60    bindInfo.fOffset = fVertexOffset;
61    fVertexOffset += requiredBytes;
62    return {VertexWriter(fCurrentVertexBuffer->map()), bindInfo};
63}
64
65std::tuple<IndexWriter, BindBufferInfo> DrawBufferManager::getIndexWriter(size_t requiredBytes) {
66    if (!requiredBytes) {
67        BindBufferInfo bindInfo;
68        bindInfo.fBuffer = nullptr;
69        bindInfo.fOffset = 0;
70        return {IndexWriter(nullptr), bindInfo};
71    }
72    if (fCurrentIndexBuffer &&
73        !can_fit(requiredBytes, fCurrentIndexBuffer.get(), fIndexOffset, /*alignment=*/1)) {
74        fUsedBuffers.push_back(std::move(fCurrentIndexBuffer));
75    }
76
77    if (!fCurrentIndexBuffer) {
78        SkASSERT(requiredBytes <= kIndexBufferSize);
79        fCurrentIndexBuffer = fResourceProvider->findOrCreateBuffer(kIndexBufferSize,
80                                                                    BufferType::kIndex,
81                                                                    PrioritizeGpuReads::kNo);
82        fIndexOffset = 0;
83        if (!fCurrentIndexBuffer) {
84            return {IndexWriter(), BindBufferInfo()};
85        }
86    }
87    BindBufferInfo bindInfo;
88    bindInfo.fBuffer = fCurrentIndexBuffer.get();
89    bindInfo.fOffset = fIndexOffset;
90    fIndexOffset += requiredBytes;
91    return {IndexWriter(fCurrentIndexBuffer->map()), bindInfo};
92}
93
94std::tuple<UniformWriter, BindBufferInfo> DrawBufferManager::getUniformWriter(
95        size_t requiredBytes) {
96    if (!requiredBytes) {
97        BindBufferInfo bindInfo;
98        bindInfo.fBuffer = nullptr;
99        bindInfo.fOffset = 0;
100        return {UniformWriter(nullptr), bindInfo};
101    }
102    if (fCurrentUniformBuffer &&
103        !can_fit(requiredBytes,
104                 fCurrentUniformBuffer.get(),
105                 fUniformOffset,
106                 fUniformStartAlignment)) {
107        fUsedBuffers.push_back(std::move(fCurrentUniformBuffer));
108    }
109
110    if (!fCurrentUniformBuffer) {
111        SkASSERT(requiredBytes <= kUniformBufferSize);
112        fCurrentUniformBuffer = fResourceProvider->findOrCreateBuffer(kUniformBufferSize,
113                                                                      BufferType::kUniform,
114                                                                      PrioritizeGpuReads::kNo);
115        fUniformOffset = 0;
116        if (!fCurrentUniformBuffer) {
117            return {UniformWriter(), BindBufferInfo()};
118        }
119    }
120    fUniformOffset = SkAlignTo(fUniformOffset, fUniformStartAlignment);
121    BindBufferInfo bindInfo;
122    bindInfo.fBuffer = fCurrentUniformBuffer.get();
123    bindInfo.fOffset = fUniformOffset;
124    fUniformOffset += requiredBytes;
125    return {UniformWriter(fCurrentUniformBuffer->map()), bindInfo};
126}
127
128void DrawBufferManager::transferToCommandBuffer(CommandBuffer* commandBuffer) {
129    for (auto& buffer : fUsedBuffers) {
130        buffer->unmap();
131        commandBuffer->trackResource(std::move(buffer));
132    }
133    fUsedBuffers.clear();
134}
135
136} // namespace skgpu
137
138