Skip to content

Commit 3354a4d

Browse files
committed
Fix data transfer time ordering
1 parent cc134eb commit 3354a4d

30 files changed

Lines changed: 480 additions & 349 deletions

demo/android/app/src/main/cpp/native_engine.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ void NativeEngine::draw_frame() {
3030

3131
auto surface = pf_swapchain->get_surface_texture();
3232

33+
pf_blit->update_uniform(encoder);
34+
3335
// Swap chain render pass.
3436
{
3537
encoder->begin_render_pass(pf_swapchain->get_render_pass(), surface, Pathfinder::ColorF(0.2, 0.2, 0.2, 1.0));

demo/native/main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ int main() {
6565

6666
auto encoder = device->create_command_encoder("main encoder");
6767

68+
blit->update_uniform(encoder);
69+
6870
// Swap chain render pass.
6971
{
7072
auto surface_texture = swap_chain->get_surface_texture();

pathfinder/core/d3d9/renderer.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,12 @@ void RendererD3D9::draw_tiles(uint64_t tile_vertex_buffer_id,
581581
const std::shared_ptr<DescriptorSet> &tile_descriptor_set,
582582
uint64_t tile_uniform_offset) {
583583
std::shared_ptr<Texture> target_texture;
584+
std::shared_ptr<RenderPass> render_pass;
584585

585586
// If no specific RenderTarget is given, we render to the dst framebuffer.
586587
if (render_target_id == nullptr) {
587588
// Check if we should clear the dst framebuffer.
588-
encoder->begin_render_pass(clear_dest_texture ? dest_render_pass_clear : dest_render_pass_load,
589-
dest_texture,
590-
ColorF());
589+
render_pass = clear_dest_texture ? dest_render_pass_clear : dest_render_pass_load;
591590

592591
target_texture = dest_texture;
593592

@@ -596,20 +595,15 @@ void RendererD3D9::draw_tiles(uint64_t tile_vertex_buffer_id,
596595
}
597596
// Otherwise, we render to the given render target.
598597
else {
599-
auto render_target = get_render_target(*render_target_id);
600-
601-
auto texture = render_target.texture;
602-
603598
// We always clear a render target.
604-
encoder->begin_render_pass(dest_render_pass_clear, texture, ColorF());
599+
render_pass = dest_render_pass_clear;
605600

606-
target_texture = texture;
601+
auto render_target = get_render_target(*render_target_id);
602+
target_texture = render_target.texture;
607603
}
608604

609605
Vec2F target_texture_size = target_texture->get_size().to_f32();
610606

611-
encoder->set_viewport({{0, 0}, target_texture_size.to_i32()});
612-
613607
auto z_buffer_texture = allocator->get_texture(z_buffer_texture_id);
614608
auto color_texture = allocator->get_texture(dummy_texture_id);
615609

@@ -670,6 +664,10 @@ void RendererD3D9::draw_tiles(uint64_t tile_vertex_buffer_id,
670664
Descriptor::sampled(4, allocator->get_texture(*mask_storage.texture_id), get_default_sampler()),
671665
});
672666

667+
encoder->begin_render_pass(render_pass, target_texture, ColorF());
668+
669+
encoder->set_viewport({{0, 0}, target_texture_size.to_i32()});
670+
673671
encoder->bind_render_pipeline(tile_pipeline);
674672

675673
encoder->bind_vertex_buffers(

pathfinder/gpu/command_encoder.cpp

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,12 @@ void CommandEncoder::write_buffer(const std::shared_ptr<Buffer> &buffer,
209209
return;
210210
}
211211

212-
// Write buffer by memory mapping.
213-
if (buffer->get_memory_property() == MemoryProperty::HostVisibleAndCoherent) {
214-
buffer->upload_via_mapping(data_size, offset, data);
215-
return;
216-
}
212+
auto device = device_.lock();
213+
auto allocation = device->allocate_staging(data_size);
214+
215+
void *mapped_ptr = device->map_staging(allocation);
216+
memcpy(mapped_ptr, data, data_size);
217+
device->unmap_staging(allocation);
217218

218219
Command cmd{};
219220
cmd.type = CommandType::WriteBuffer;
@@ -222,9 +223,12 @@ void CommandEncoder::write_buffer(const std::shared_ptr<Buffer> &buffer,
222223
args.buffer = buffer.get();
223224
args.offset = offset;
224225
args.data_size = data_size;
225-
args.data = data;
226+
args.staging_buffer = allocation.buffer.get();
227+
args.staging_offset = (uint32_t)allocation.offset;
226228

227229
commands_.push_back(cmd);
230+
231+
track_temporary_resource(allocation.buffer);
228232
}
229233

230234
void CommandEncoder::read_buffer(const std::shared_ptr<Buffer> &buffer,
@@ -238,15 +242,12 @@ void CommandEncoder::read_buffer(const std::shared_ptr<Buffer> &buffer,
238242

239243
if (data_size == 0 || data == nullptr) {
240244
Logger::error("Tried to read invalid data from buffer!");
241-
}
242-
243-
// Read buffer by memory mapping.
244-
if (buffer->get_memory_property() == MemoryProperty::HostVisibleAndCoherent) {
245-
Logger::error("You're trying to read a mappable buffer through a command. It may indicate some bug.");
246-
buffer->download_via_mapping(data_size, offset, data);
247245
return;
248246
}
249247

248+
auto device = device_.lock();
249+
auto allocation = device->allocate_staging(data_size);
250+
250251
Command cmd{};
251252
cmd.type = CommandType::ReadBuffer;
252253

@@ -255,22 +256,40 @@ void CommandEncoder::read_buffer(const std::shared_ptr<Buffer> &buffer,
255256
args.offset = offset;
256257
args.data_size = data_size;
257258
args.data = data;
259+
args.staging_buffer = allocation.buffer.get();
260+
args.staging_offset = (uint32_t)allocation.offset;
258261

259262
commands_.push_back(cmd);
263+
264+
add_callback([allocation, data, data_size, device] {
265+
void *mapped_ptr = device->map_staging(allocation);
266+
memcpy(data, mapped_ptr, data_size);
267+
device->unmap_staging(allocation);
268+
});
269+
270+
track_temporary_resource(allocation.buffer);
260271
}
261272

262273
void CommandEncoder::write_texture(const std::shared_ptr<Texture> &texture, RectI region, const void *src) {
263274
auto whole_region = RectI({0, 0}, {texture->get_size()});
264275

265-
// Invalid region represents the whole texture.
266276
auto effective_region = region.is_valid() ? region : whole_region;
267277

268-
// Check if the region is a subset of the whole texture region.
269278
if (!effective_region.union_rect(whole_region).is_valid() || effective_region.area() == 0) {
270279
Logger::error("Tried to write invalid region of a texture!");
271280
return;
272281
}
273282

283+
auto pixel_size = get_pixel_size(texture->get_format());
284+
size_t data_size = (size_t)effective_region.width() * effective_region.height() * pixel_size;
285+
286+
auto device = device_.lock();
287+
auto allocation = device->allocate_staging(data_size);
288+
289+
void *mapped_ptr = device->map_staging(allocation);
290+
memcpy(mapped_ptr, src, data_size);
291+
device->unmap_staging(allocation);
292+
274293
Command cmd{};
275294
cmd.type = CommandType::WriteTexture;
276295

@@ -280,23 +299,30 @@ void CommandEncoder::write_texture(const std::shared_ptr<Texture> &texture, Rect
280299
args.offset_y = effective_region.top;
281300
args.width = effective_region.width();
282301
args.height = effective_region.height();
283-
args.data = src;
302+
args.staging_buffer = allocation.buffer.get();
303+
args.staging_offset = (uint32_t)allocation.offset;
284304

285305
commands_.push_back(cmd);
306+
307+
track_temporary_resource(allocation.buffer);
286308
}
287309

288310
void CommandEncoder::read_texture(const std::shared_ptr<Texture> &texture, RectI region, void *dst) {
289311
auto whole_region = RectI({0, 0}, {texture->get_size()});
290312

291-
// Invalid region represents the whole texture.
292313
auto effective_region = region.is_valid() ? region : whole_region;
293314

294-
// Check if the region is a subset of the whole texture region.
295315
if (!effective_region.union_rect(whole_region).is_valid() || effective_region.area() == 0) {
296316
Logger::error("Tried to write invalid region of a texture!");
297317
return;
298318
}
299319

320+
auto pixel_size = get_pixel_size(texture->get_format());
321+
size_t data_size = (size_t)effective_region.width() * effective_region.height() * pixel_size;
322+
323+
auto device = device_.lock();
324+
auto allocation = device->allocate_staging(data_size);
325+
300326
Command cmd{};
301327
cmd.type = CommandType::ReadTexture;
302328

@@ -307,8 +333,18 @@ void CommandEncoder::read_texture(const std::shared_ptr<Texture> &texture, RectI
307333
args.width = effective_region.width();
308334
args.height = effective_region.height();
309335
args.data = dst;
336+
args.staging_buffer = allocation.buffer.get();
337+
args.staging_offset = (uint32_t)allocation.offset;
310338

311339
commands_.push_back(cmd);
340+
341+
add_callback([allocation, dst, data_size, device] {
342+
void *mapped_ptr = device->map_staging(allocation);
343+
memcpy(dst, mapped_ptr, data_size);
344+
device->unmap_staging(allocation);
345+
});
346+
347+
track_temporary_resource(allocation.buffer);
312348
}
313349

314350
} // namespace Pathfinder

pathfinder/gpu/command_encoder.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ enum class CommandType {
5252
Max,
5353
};
5454

55+
struct StagingAllocation {
56+
std::shared_ptr<Buffer> buffer;
57+
size_t offset = 0;
58+
void *mapped_ptr = nullptr;
59+
};
60+
5561
struct Command {
5662
CommandType type = CommandType::Max;
5763

@@ -111,12 +117,16 @@ struct Command {
111117
uint32_t offset;
112118
uint32_t data_size;
113119
const void *data;
120+
Buffer *staging_buffer;
121+
uint32_t staging_offset;
114122
} write_buffer;
115123
struct {
116124
Buffer *buffer;
117125
uint32_t offset;
118126
uint32_t data_size;
119127
void *data;
128+
Buffer *staging_buffer;
129+
uint32_t staging_offset;
120130
} read_buffer;
121131
struct {
122132
Texture *texture;
@@ -125,6 +135,8 @@ struct Command {
125135
uint32_t width;
126136
uint32_t height;
127137
const void *data;
138+
Buffer *staging_buffer;
139+
uint32_t staging_offset;
128140
} write_texture;
129141
struct {
130142
Texture *texture;
@@ -133,6 +145,8 @@ struct Command {
133145
uint32_t width;
134146
uint32_t height;
135147
void *data;
148+
Buffer *staging_buffer;
149+
uint32_t staging_offset;
136150
} read_texture;
137151
} args;
138152
};
@@ -194,6 +208,7 @@ class CommandEncoder {
194208

195209
// DATA TRANSFER
196210

211+
/// fixme: should set up a staging buffer every time we need to write to a buffer.
197212
/**
198213
* Upload to buffer.
199214
* @param buffer
@@ -206,6 +221,7 @@ class CommandEncoder {
206221
uint32_t data_size,
207222
const void *data);
208223

224+
/// fixme: should set up a staging buffer every time we need to read from a buffer.
209225
void read_buffer(const std::shared_ptr<Buffer> &buffer, uint32_t offset, uint32_t data_size, void *data);
210226

211227
void write_texture(const std::shared_ptr<Texture> &texture, RectI region, const void *data);
@@ -216,12 +232,17 @@ class CommandEncoder {
216232
callbacks_.push_back(callback);
217233
}
218234

235+
void track_temporary_resource(const std::shared_ptr<Buffer> &buffer) {
236+
temp_buffers_.push_back(buffer);
237+
}
238+
219239
void invoke_callbacks() {
220240
for (auto &callback : callbacks_) {
221241
callback();
222242
}
223243

224244
callbacks_.clear();
245+
temp_buffers_.clear();
225246
}
226247

227248
protected:
@@ -245,6 +266,9 @@ class CommandEncoder {
245266
/// Callbacks after the commands are finished (not only submitted) on GPU.
246267
std::vector<std::function<void()>> callbacks_;
247268

269+
/// Temporary resources that should be kept alive until the command buffer is finished.
270+
std::vector<std::shared_ptr<Buffer>> temp_buffers_;
271+
248272
/// Currently bound pipeline.
249273
RenderPipeline *render_pipeline_{};
250274
ComputePipeline *compute_pipeline_{};

pathfinder/gpu/device.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ 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;
76+
77+
virtual void *map_staging(const StagingAllocation &allocation) { return nullptr; }
78+
79+
virtual void unmap_staging(const StagingAllocation &allocation) {}
80+
81+
virtual void reset_staging() {}
82+
7583
virtual size_t get_aligned_uniform_size(size_t original_size) = 0;
7684

7785
BackendType get_backend_type() const {

0 commit comments

Comments
 (0)