Skip to content

Commit de54186

Browse files
committed
Fix staging performance
1 parent 32f55c2 commit de54186

14 files changed

Lines changed: 158 additions & 113 deletions

File tree

pathfinder/gpu/command_encoder.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ void CommandEncoder::write_buffer(const std::shared_ptr<Buffer> &buffer,
214214
auto device = device_.lock();
215215
auto allocation = device->allocate_staging(data_size);
216216

217+
if (!used_staging_buffer_) {
218+
used_staging_buffer_ = true;
219+
device->increment_staging_encoder();
220+
}
221+
217222
void *mapped_ptr = device->map_staging(allocation);
218223
memcpy(mapped_ptr, data, data_size);
219224
device->unmap_staging(allocation);
@@ -250,8 +255,13 @@ void CommandEncoder::read_buffer(const std::shared_ptr<Buffer> &buffer,
250255
auto device = device_.lock();
251256
auto allocation = device->allocate_staging(data_size);
252257

258+
if (!used_staging_buffer_) {
259+
used_staging_buffer_ = true;
260+
device->increment_staging_encoder();
261+
}
262+
253263
Command cmd{};
254-
cmd.type = CommandType::ReadBuffer;
264+
cmd.type = CommandType::WriteBuffer;
255265

256266
auto &args = cmd.args.read_buffer;
257267
args.buffer = buffer.get();
@@ -288,6 +298,11 @@ void CommandEncoder::write_texture(const std::shared_ptr<Texture> &texture, Rect
288298
auto device = device_.lock();
289299
auto allocation = device->allocate_staging(data_size);
290300

301+
if (!used_staging_buffer_) {
302+
used_staging_buffer_ = true;
303+
device->increment_staging_encoder();
304+
}
305+
291306
void *mapped_ptr = device->map_staging(allocation);
292307
memcpy(mapped_ptr, src, data_size);
293308
device->unmap_staging(allocation);
@@ -325,6 +340,11 @@ void CommandEncoder::read_texture(const std::shared_ptr<Texture> &texture, RectI
325340
auto device = device_.lock();
326341
auto allocation = device->allocate_staging(data_size);
327342

343+
if (!used_staging_buffer_) {
344+
used_staging_buffer_ = true;
345+
device->increment_staging_encoder();
346+
}
347+
328348
Command cmd{};
329349
cmd.type = CommandType::ReadTexture;
330350

@@ -349,4 +369,19 @@ void CommandEncoder::read_texture(const std::shared_ptr<Texture> &texture, RectI
349369
track_temporary_resource(allocation.buffer);
350370
}
351371

372+
void CommandEncoder::invoke_callbacks() {
373+
for (auto &callback : callbacks_) {
374+
callback();
375+
}
376+
377+
callbacks_.clear();
378+
temp_buffers_.clear();
379+
380+
if (used_staging_buffer_) {
381+
if (auto device = device_.lock()) {
382+
device_.lock()->decrement_staging_encoder();
383+
}
384+
}
385+
}
386+
352387
} // namespace Pathfinder

pathfinder/gpu/command_encoder.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ enum class CommandType {
5555
struct StagingAllocation {
5656
std::shared_ptr<Buffer> buffer;
5757
size_t offset = 0;
58+
size_t data_size = 0;
5859
void *mapped_ptr = nullptr;
5960
};
6061

@@ -236,14 +237,7 @@ class CommandEncoder {
236237
temp_buffers_.push_back(buffer);
237238
}
238239

239-
void invoke_callbacks() {
240-
for (auto &callback : callbacks_) {
241-
callback();
242-
}
243-
244-
callbacks_.clear();
245-
temp_buffers_.clear();
246-
}
240+
void invoke_callbacks();
247241

248242
protected:
249243
CommandEncoder() = default;
@@ -276,6 +270,8 @@ class CommandEncoder {
276270
std::weak_ptr<Device> device_;
277271

278272
std::vector<std::shared_ptr<Framebuffer>> framebuffers_;
273+
274+
bool used_staging_buffer_ = false;
279275
};
280276

281277
} // namespace Pathfinder

pathfinder/gpu/device.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "device.h"
2+
3+
namespace Pathfinder {
4+
5+
StagingAllocation Device::allocate_staging(size_t size) {
6+
if (size > STAGING_BLOCK_SIZE) {
7+
auto buffer = create_staging_buffer(size);
8+
9+
StagingAllocation alloc;
10+
alloc.buffer = buffer;
11+
alloc.offset = 0;
12+
alloc.data_size = size;
13+
alloc.mapped_ptr = nullptr;
14+
return alloc;
15+
}
16+
17+
for (auto &block : staging_blocks_) {
18+
size_t aligned_offset = (block.used_size + 15) & ~15;
19+
20+
if (aligned_offset + size <= STAGING_BLOCK_SIZE) {
21+
StagingAllocation alloc;
22+
alloc.buffer = block.buffer;
23+
alloc.offset = aligned_offset;
24+
alloc.data_size = size;
25+
alloc.mapped_ptr = nullptr;
26+
27+
block.used_size = aligned_offset + size;
28+
return alloc;
29+
}
30+
}
31+
32+
auto buffer = create_staging_buffer(STAGING_BLOCK_SIZE);
33+
34+
StagingBlock new_block;
35+
new_block.buffer = buffer;
36+
new_block.used_size = size;
37+
staging_blocks_.push_back(new_block);
38+
39+
StagingAllocation alloc;
40+
alloc.buffer = buffer;
41+
alloc.offset = 0;
42+
alloc.data_size = size;
43+
alloc.mapped_ptr = nullptr;
44+
45+
return alloc;
46+
}
47+
48+
void Device::reset_staging() {
49+
for (auto &block : staging_blocks_) {
50+
block.used_size = 0;
51+
}
52+
}
53+
54+
} // namespace Pathfinder

pathfinder/gpu/device.h

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,49 @@ class Device : public std::enable_shared_from_this<Device> {
7272

7373
virtual std::shared_ptr<Fence> create_fence(const std::string &label) = 0;
7474

75-
virtual StagingAllocation allocate_staging(size_t size) = 0;
75+
StagingAllocation allocate_staging(size_t size);
7676

77-
virtual void *map_staging(const StagingAllocation &allocation) { return nullptr; }
77+
virtual void *map_staging(const StagingAllocation &allocation) {
78+
return nullptr;
79+
}
7880

7981
virtual void unmap_staging(const StagingAllocation &allocation) {}
8082

81-
virtual void reset_staging() {}
83+
virtual void reset_staging();
8284

8385
virtual size_t get_aligned_uniform_size(size_t original_size) = 0;
8486

8587
BackendType get_backend_type() const {
8688
return backend_type;
8789
}
8890

91+
public:
92+
void increment_staging_encoder() {
93+
pending_staging_encoders_++;
94+
}
95+
96+
void decrement_staging_encoder() {
97+
pending_staging_encoders_--;
98+
if (pending_staging_encoders_ == 0) {
99+
reset_staging();
100+
}
101+
}
102+
89103
protected:
90104
BackendType backend_type = BackendType::Vulkan;
105+
106+
struct StagingBlock {
107+
std::shared_ptr<Buffer> buffer;
108+
size_t used_size = 0;
109+
};
110+
111+
const size_t STAGING_BLOCK_SIZE = 4 * 1024 * 1024;
112+
113+
std::vector<StagingBlock> staging_blocks_;
114+
115+
uint32_t pending_staging_encoders_ = 0;
116+
117+
virtual std::shared_ptr<Buffer> create_staging_buffer(size_t size) = 0;
91118
};
92119

93120
} // namespace Pathfinder

pathfinder/gpu/gl/buffer.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ BufferGl::~BufferGl() {
4949
}
5050

5151
void BufferGl::upload_via_mapping(size_t data_size, size_t offset, const void *data) {
52+
if (mapped_ptr_) {
53+
memcpy((uint8_t *)mapped_ptr_ + offset, data, data_size);
54+
return;
55+
}
56+
5257
int gl_buffer_type = 0;
5358

5459
switch (desc_.type) {
@@ -115,6 +120,10 @@ void BufferGl::set_label(const std::string &label) {
115120
}
116121

117122
void *BufferGl::map() {
123+
if (mapped_ptr_) {
124+
return mapped_ptr_;
125+
}
126+
118127
GLint target = GL_NONE;
119128
switch (desc_.type) {
120129
case BufferType::Uniform:
@@ -135,8 +144,11 @@ void *BufferGl::map() {
135144
} break;
136145
}
137146
glBindBuffer(target, gl_id_);
138-
void *ptr = glMapBufferRange(target, 0, desc_.size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
147+
GLbitfield flags = GL_MAP_WRITE_BIT;
148+
void *ptr = glMapBufferRange(target, 0, desc_.size, flags);
139149
glBindBuffer(target, 0);
150+
151+
mapped_ptr_ = ptr;
140152
return ptr;
141153
}
142154

@@ -163,6 +175,7 @@ void BufferGl::unmap() {
163175
glBindBuffer(target, gl_id_);
164176
glUnmapBuffer(target);
165177
glBindBuffer(target, 0);
178+
mapped_ptr_ = nullptr;
166179
}
167180

168181
} // namespace Pathfinder

pathfinder/gpu/gl/buffer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ class BufferGl : public Buffer {
2626

2727
private:
2828
explicit BufferGl(const BufferDescriptor& desc);
29-
3029
uint32_t gl_id_ = 0;
30+
31+
// Cache mapped pointer to improve performance.
32+
void* mapped_ptr_ = nullptr;
3133
};
3234

3335
} // namespace Pathfinder

pathfinder/gpu/gl/device.cpp

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,26 +137,17 @@ std::shared_ptr<Fence> DeviceGl::create_fence(const std::string &label) {
137137

138138
return fence_gl;
139139
}
140-
141-
StagingAllocation DeviceGl::allocate_staging(size_t size) {
140+
std::shared_ptr<Buffer> DeviceGl::create_staging_buffer(size_t size) {
142141
BufferDescriptor desc;
143142
desc.type = BufferType::Storage;
144143
desc.size = size;
145144
desc.property = MemoryProperty::HostVisibleAndCoherent;
146-
147-
auto buffer = create_buffer(desc, "GL Staging Buffer");
148-
149-
StagingAllocation alloc;
150-
alloc.buffer = buffer;
151-
alloc.offset = 0;
152-
alloc.mapped_ptr = nullptr;
153-
154-
return alloc;
145+
return create_buffer(desc, "GL Staging Buffer");
155146
}
156147

157148
void *DeviceGl::map_staging(const StagingAllocation &allocation) {
158149
auto buffer_gl = (BufferGl *)allocation.buffer.get();
159-
return buffer_gl->map();
150+
return (uint8_t *)buffer_gl->map() + allocation.offset;
160151
}
161152

162153
void DeviceGl::unmap_staging(const StagingAllocation &allocation) {

pathfinder/gpu/gl/device.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ class DeviceGl : public Device {
6262

6363
std::shared_ptr<Fence> create_fence(const std::string &label) override;
6464

65-
StagingAllocation allocate_staging(size_t size) override;
66-
6765
void *map_staging(const StagingAllocation &allocation) override;
6866

6967
void unmap_staging(const StagingAllocation &allocation) override;
7068

69+
std::shared_ptr<Buffer> create_staging_buffer(size_t size) override;
70+
7171
size_t get_aligned_uniform_size(size_t original_size) override;
7272

7373
private:

pathfinder/gpu/mtl/device.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ class DeviceMtl final : public Device {
6666

6767
std::shared_ptr<Fence> create_fence(const std::string &label) override;
6868

69-
StagingAllocation allocate_staging(size_t size) override;
70-
7169
void *map_staging(const StagingAllocation &allocation) override;
7270

7371
void unmap_staging(const StagingAllocation &allocation) override;
7472

73+
std::shared_ptr<Buffer> create_staging_buffer(size_t size) override;
74+
7575
size_t get_aligned_uniform_size(size_t original_size) override;
7676

7777
std::shared_ptr<TextureMtl> wrap_texture(id<MTLTexture> mtl_texture, bool has_resource_ownership);

pathfinder/gpu/mtl/device.mm

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -203,25 +203,17 @@
203203
return std::shared_ptr<Fence>(new Fence());
204204
}
205205

206-
StagingAllocation DeviceMtl::allocate_staging(size_t size) {
206+
std::shared_ptr<Buffer> DeviceMtl::create_staging_buffer(size_t size) {
207207
BufferDescriptor desc;
208208
desc.type = BufferType::Storage;
209209
desc.size = size;
210210
desc.property = MemoryProperty::HostVisibleAndCoherent;
211-
212-
auto buffer = create_buffer(desc, "Metal Staging Buffer");
213-
214-
StagingAllocation alloc;
215-
alloc.buffer = buffer;
216-
alloc.offset = 0;
217-
alloc.mapped_ptr = nullptr;
218-
219-
return alloc;
211+
return create_buffer(desc, "Metal Staging Buffer");
220212
}
221213

222214
void *DeviceMtl::map_staging(const StagingAllocation &allocation) {
223215
auto buffer_mtl = (BufferMtl *)allocation.buffer.get();
224-
return buffer_mtl->contents();
216+
return (uint8_t *)buffer_mtl->contents() + allocation.offset;
225217
}
226218

227219
void DeviceMtl::unmap_staging(const StagingAllocation &allocation) {

0 commit comments

Comments
 (0)