Line data Source code
1 : /* Copyright (C) 2022 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "VertexBufferManager.h"
21 :
22 : #include "ps/CLogger.h"
23 :
24 : #define DUMP_VB_STATS 0 // for debugging
25 :
26 : namespace
27 : {
28 :
29 1 : const char* GetBufferTypeName(
30 : const Renderer::Backend::IBuffer::Type type)
31 : {
32 1 : const char* name = "UnknownBuffer";
33 1 : switch (type)
34 : {
35 0 : case Renderer::Backend::IBuffer::Type::VERTEX:
36 0 : name = "VertexBuffer";
37 0 : break;
38 1 : case Renderer::Backend::IBuffer::Type::INDEX:
39 1 : name = "IndexBuffer";
40 1 : break;
41 0 : default:
42 0 : debug_warn("Unknown buffer type");
43 0 : break;
44 : }
45 1 : return name;
46 : }
47 :
48 1 : const char* GetGroupName(
49 : const CVertexBufferManager::Group group)
50 : {
51 1 : const char* name = "Unknown";
52 1 : switch (group)
53 : {
54 1 : case CVertexBufferManager::Group::DEFAULT:
55 1 : name = "Default";
56 1 : break;
57 0 : case CVertexBufferManager::Group::TERRAIN:
58 0 : name = "Terrain";
59 0 : break;
60 0 : case CVertexBufferManager::Group::WATER:
61 0 : name = "Water";
62 0 : break;
63 0 : default:
64 0 : debug_warn("Unknown buffer group");
65 0 : break;
66 : }
67 1 : return name;
68 : }
69 :
70 : } // anonymous namespace
71 :
72 1 : CVertexBufferManager g_VBMan;
73 :
74 6 : CVertexBufferManager::Handle::Handle(Handle&& other)
75 6 : : m_Chunk(other.m_Chunk)
76 : {
77 6 : other.m_Chunk = nullptr;
78 6 : }
79 :
80 6 : CVertexBufferManager::Handle& CVertexBufferManager::Handle::operator=(Handle&& other)
81 : {
82 6 : if (&other == this)
83 0 : return *this;
84 :
85 6 : if (IsValid())
86 0 : Reset();
87 :
88 12 : Handle tmp(std::move(other));
89 6 : swap(*this, tmp);
90 :
91 6 : return *this;
92 : }
93 :
94 6 : CVertexBufferManager::Handle::Handle(CVertexBuffer::VBChunk* chunk)
95 6 : : m_Chunk(chunk)
96 : {
97 6 : }
98 :
99 108 : void CVertexBufferManager::Handle::Reset()
100 : {
101 108 : if (!IsValid())
102 102 : return;
103 :
104 6 : g_VBMan.Release(m_Chunk);
105 6 : m_Chunk = nullptr;
106 : }
107 :
108 : // Explicit shutdown of the vertex buffer subsystem.
109 : // This avoids the ordering issues that arise when using destructors of
110 : // global instances.
111 0 : void CVertexBufferManager::Shutdown()
112 : {
113 0 : for (int group = static_cast<int>(Group::DEFAULT); group < static_cast<int>(Group::COUNT); ++group)
114 0 : m_Buffers[group].clear();
115 0 : }
116 :
117 : /**
118 : * AllocateChunk: try to allocate a buffer of given number of vertices (each of
119 : * given size), with the given type, and using the given texture - return null
120 : * if no free chunks available
121 : */
122 6 : CVertexBufferManager::Handle CVertexBufferManager::AllocateChunk(
123 : const size_t vertexSize, const size_t numberOfVertices,
124 : const Renderer::Backend::IBuffer::Type type,
125 : const bool dynamic, void* backingStore, Group group)
126 : {
127 6 : ENSURE(vertexSize > 0);
128 6 : ENSURE(numberOfVertices > 0);
129 :
130 6 : CVertexBuffer::VBChunk* result = nullptr;
131 :
132 6 : if (CVertexBuffer::UseStreaming(dynamic))
133 0 : ENSURE(backingStore != NULL);
134 :
135 : // TODO, RC - run some sanity checks on allocation request
136 :
137 6 : std::vector<std::unique_ptr<CVertexBuffer>>& buffers = m_Buffers[static_cast<int>(group)];
138 :
139 : #if DUMP_VB_STATS
140 : debug_printf("\n============================\n# allocate vsize=%zu nverts=%zu\n\n", vertexSize, numVertices);
141 : for (const std::unique_ptr<CVertexBuffer>& buffer : buffers)
142 : {
143 : if (buffer->CompatibleVertexType(vertexSize, type, dynamic))
144 : {
145 : debug_printf("%p\n", buffer.get());
146 : buffer->DumpStatus();
147 : }
148 : }
149 : #endif
150 :
151 : // iterate through all existing buffers testing for one that'll
152 : // satisfy the allocation
153 6 : for (const std::unique_ptr<CVertexBuffer>& buffer : buffers)
154 : {
155 5 : result = buffer->Allocate(vertexSize, numberOfVertices, type, dynamic, backingStore);
156 5 : if (result)
157 5 : return Handle(result);
158 : }
159 :
160 1 : char bufferName[64] = {0};
161 1 : snprintf(
162 : bufferName, std::size(bufferName), "%s (%s, %zu%s)",
163 : GetBufferTypeName(type), GetGroupName(group), vertexSize, (dynamic ? ", dynamic" : ""));
164 :
165 : // got this far; need to allocate a new buffer
166 1 : buffers.emplace_back(
167 : group == Group::DEFAULT
168 2 : ? std::make_unique<CVertexBuffer>(bufferName, vertexSize, type, dynamic)
169 : // Reduces the buffer size for not so frequent buffers.
170 1 : : std::make_unique<CVertexBuffer>(bufferName, vertexSize, type, dynamic, 1024 * 1024));
171 1 : result = buffers.back()->Allocate(vertexSize, numberOfVertices, type, dynamic, backingStore);
172 :
173 1 : if (!result)
174 : {
175 0 : LOGERROR("Failed to create backend buffer (%zu*%zu)", vertexSize, numberOfVertices);
176 0 : return Handle();
177 : }
178 :
179 1 : return Handle(result);
180 : }
181 :
182 6 : void CVertexBufferManager::Release(CVertexBuffer::VBChunk* chunk)
183 : {
184 6 : ENSURE(chunk);
185 : #if DUMP_VB_STATS
186 : debug_printf("\n============================\n# release %p nverts=%zu\n\n", chunk, chunk->m_Count);
187 : #endif
188 6 : chunk->m_Owner->Release(chunk);
189 6 : }
190 :
191 0 : size_t CVertexBufferManager::GetBytesReserved() const
192 : {
193 0 : size_t total = 0;
194 0 : for (int group = static_cast<int>(Group::DEFAULT); group < static_cast<int>(Group::COUNT); ++group)
195 0 : for (const std::unique_ptr<CVertexBuffer>& buffer : m_Buffers[static_cast<int>(group)])
196 0 : total += buffer->GetBytesReserved();
197 0 : return total;
198 : }
199 :
200 0 : size_t CVertexBufferManager::GetBytesAllocated() const
201 : {
202 0 : size_t total = 0;
203 0 : for (int group = static_cast<int>(Group::DEFAULT); group < static_cast<int>(Group::COUNT); ++group)
204 0 : for (const std::unique_ptr<CVertexBuffer>& buffer : m_Buffers[static_cast<int>(group)])
205 0 : total += buffer->GetBytesAllocated();
206 0 : return total;
207 3 : }
|