Skip to content

Commit 37d16ba

Browse files
committed
utils : rework texture download and writing
+ introduce TransferBufferPool to persist transfer buffers + Ur5WithSystems.cpp : update recorder use
1 parent 78efc93 commit 37d16ba

File tree

4 files changed

+160
-102
lines changed

4 files changed

+160
-102
lines changed

examples/Ur5WithSystems.cpp

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,6 @@ void eventLoop(const Renderer &renderer) {
184184
}
185185
}
186186

187-
Renderer createRenderer(Uint32 width, Uint32 height,
188-
SDL_GPUTextureFormat depth_stencil_format) {
189-
return Renderer{Device{auto_detect_shader_format_subset(), true},
190-
Window(__FILE__, int(width), int(height), 0),
191-
depth_stencil_format};
192-
}
193-
194187
int main(int argc, char **argv) {
195188
CLI::App app{"Ur5 example"};
196189
bool performRecording{false};
@@ -206,8 +199,11 @@ int main(int argc, char **argv) {
206199
return 1;
207200

208201
// D16_UNORM works on macOS, D24_UNORM and D32_FLOAT break the depth prepass
209-
Renderer renderer =
210-
createRenderer(wWidth, wHeight, SDL_GPU_TEXTUREFORMAT_D16_UNORM);
202+
Renderer renderer{
203+
Device{auto_detect_shader_format_subset(), true},
204+
Window(__FILE__, wWidth, wHeight, 0),
205+
SDL_GPU_TEXTUREFORMAT_D16_UNORM,
206+
};
211207

212208
entt::registry registry{};
213209

@@ -414,44 +410,43 @@ int main(int argc, char **argv) {
414410

415411
Uint32 frameNo = 0;
416412

417-
srand(42);
413+
std::srand(42);
418414
Eigen::VectorXd q0 = pin::neutral(model);
419415
Eigen::VectorXd q1 = pin::randomConfiguration(model);
420416

421417
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
422418
media::VideoRecorder recorder{NoInit};
419+
media::TransferBufferPool transfer_buffer_pool{renderer.device};
423420
if (performRecording)
424-
recorder = media::VideoRecorder{wWidth, wHeight, "ur5.mp4"};
421+
recorder = media::VideoRecorder{wWidth,
422+
wHeight,
423+
"ur5.mp4",
424+
{
425+
.fps = 50,
426+
}};
425427
#endif
426428

427-
auto record_callback = [&] {
428-
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
429-
auto swapchain_format = renderer.getSwapchainTextureFormat();
430-
media::videoWriteTextureToFrame(renderer.device, recorder,
431-
renderer.swapchain, swapchain_format,
432-
wWidth, wHeight);
433-
#endif
434-
};
435-
436429
AABB &worldSpaceBounds = robot_scene.worldSpaceBounds;
437430
worldSpaceBounds.update({-1.f, -1.f, 0.f}, {+1.f, +1.f, 1.f});
438431

439432
frustumBoundsDebug.addBounds(worldSpaceBounds);
440433
frustumBoundsDebug.addFrustum(shadowPassInfo.cam);
441434

442435
Eigen::VectorXd q = q0;
436+
Eigen::VectorXd qn = q;
437+
Eigen::VectorXd v{model.nv};
443438
const double dt = 1e-2;
444439

445440
while (!quitRequested) {
446441
// logic
447442
eventLoop(renderer);
448443
double alpha = 0.5 * (1. + std::sin(frameNo * dt));
449-
Eigen::VectorXd qn = q;
450-
q = pin::interpolate(model, q0, q1, alpha);
451-
Eigen::VectorXd v = pin::difference(model, q, qn) / dt;
452-
pin::forwardKinematics(model, pin_data, q, v);
444+
pin::interpolate(model, q0, q1, alpha, qn);
445+
v = pin::difference(model, q, qn) / dt;
446+
pin::forwardKinematics(model, pin_data, qn, v);
453447
pin::updateFramePlacements(model, pin_data);
454448
pin::updateGeometryPlacements(model, pin_data, geom_model, geom_data);
449+
q = qn;
455450
debug_scene.update();
456451

457452
// acquire command buffer and swapchain
@@ -494,19 +489,29 @@ int main(int argc, char **argv) {
494489
command_buffer.submit();
495490

496491
if (performRecording) {
497-
record_callback();
492+
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
493+
CommandBuffer command_buffer = renderer.acquireCommandBuffer();
494+
auto swapchain_format = renderer.getSwapchainTextureFormat();
495+
media::videoWriteTextureToFrame(
496+
command_buffer, renderer.device, transfer_buffer_pool, recorder,
497+
renderer.swapchain, swapchain_format, wWidth, wHeight);
498+
#endif
498499
}
499500
frameNo++;
500501
}
501502

502-
SDL_WaitForGPUIdle(renderer.device);
503+
SDL_WaitForGPUIdle();
503504
frustumBoundsDebug.release();
504505
depthPass.release();
505506
shadowDebugPass.release(renderer.device);
506507
depthDebugPass.release(renderer.device);
507508
robot_scene.release();
508509
debug_scene.release();
509510
gui_system.release();
511+
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
512+
recorder.close();
513+
transfer_buffer_pool.release();
514+
#endif
510515
renderer.destroy();
511516
SDL_Quit();
512517
return 0;

src/candlewick/utils/VideoRecorder.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,7 @@ namespace media {
235235

236236
VideoRecorder::VideoRecorder(Uint32 width, Uint32 height,
237237
const std::string &filename)
238-
: VideoRecorder(width, height, filename,
239-
Settings{
240-
.outputWidth = int(width),
241-
.outputHeight = int(height),
242-
}) {}
238+
: VideoRecorder(width, height, filename, Settings{}) {}
243239

244240
Uint32 VideoRecorder::frameCounter() const { return impl_->m_frameCounter; }
245241

src/candlewick/utils/WriteTextureToImage.cpp

Lines changed: 87 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,61 @@
11
#include "WriteTextureToImage.h"
22
#include "../core/Device.h"
3+
#include "../core/CommandBuffer.h"
34
#include "../third-party/stb_image_write.h"
45
#include "../utils/PixelFormatConversion.h"
56

67
namespace candlewick::media {
78

8-
void dumpTextureImgToFile(const Device &device, SDL_GPUTexture *texture,
9-
SDL_GPUTextureFormat format, const Uint32 width,
10-
const Uint32 height, const char *filename) {
9+
static SDL_GPUTransferBuffer *acquireBufferImpl(SDL_GPUDevice *device,
10+
Uint32 requiredSize) {
11+
SDL_GPUTransferBufferCreateInfo createInfo{
12+
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD,
13+
.size = requiredSize,
14+
.props = 0,
15+
};
16+
return SDL_CreateGPUTransferBuffer(device, &createInfo);
17+
}
18+
19+
TransferBufferPool::TransferBufferPool(const Device &device) : _device(device) {
20+
// pre-allocate 4MB
21+
Uint32 size = 1024 * 1024 * 4;
22+
_buffer = acquireBufferImpl(_device, size);
23+
_currentBufSize = size;
24+
}
25+
26+
void TransferBufferPool::release() noexcept {
27+
if (_device) {
28+
if (_buffer)
29+
SDL_ReleaseGPUTransferBuffer(_device, _buffer);
30+
_buffer = nullptr;
31+
}
32+
_device = nullptr;
33+
}
34+
35+
SDL_GPUTransferBuffer *TransferBufferPool::acquireBuffer(Uint32 requiredSize) {
36+
if (_currentBufSize < requiredSize) {
37+
// 20% boost
38+
requiredSize = Uint32(1.2 * requiredSize);
39+
SDL_Log("TransferBufferPool: re-allocating %u bytes (increase from %u)",
40+
requiredSize, _currentBufSize);
41+
_buffer = acquireBufferImpl(_device, requiredSize);
42+
_currentBufSize = requiredSize;
43+
}
44+
return _buffer;
45+
}
46+
47+
DownloadResult downloadTexture(CommandBuffer &command_buffer,
48+
const Device &device, TransferBufferPool &pool,
49+
SDL_GPUTexture *texture,
50+
SDL_GPUTextureFormat format, const Uint16 width,
51+
const Uint16 height) {
52+
53+
// pixel size, in bytes
54+
const Uint32 pixelSize = SDL_GPUTextureFormatTexelBlockSize(format);
55+
const Uint32 requiredSize = width * height * pixelSize;
56+
assert(requiredSize ==
57+
SDL_CalculateGPUTextureFormatSize(format, width, height, 1));
1158

12-
SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device);
1359
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer);
1460

1561
SDL_GPUTextureRegion source{
@@ -19,17 +65,8 @@ void dumpTextureImgToFile(const Device &device, SDL_GPUTexture *texture,
1965
.h = height,
2066
};
2167

22-
// pixel size, in bytes
23-
const Uint32 pixel_size = SDL_GPUTextureFormatTexelBlockSize(format);
24-
25-
SDL_GPUTransferBuffer *download_transfer_buffer;
26-
{
27-
SDL_GPUTransferBufferCreateInfo createInfo{
28-
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD,
29-
.size = width * height * pixel_size,
30-
};
31-
download_transfer_buffer = SDL_CreateGPUTransferBuffer(device, &createInfo);
32-
}
68+
SDL_GPUTransferBuffer *download_transfer_buffer =
69+
pool.acquireBuffer(requiredSize);
3370

3471
SDL_GPUTextureTransferInfo destination{
3572
.transfer_buffer = download_transfer_buffer,
@@ -39,72 +76,57 @@ void dumpTextureImgToFile(const Device &device, SDL_GPUTexture *texture,
3976
SDL_DownloadFromGPUTexture(copy_pass, &source, &destination);
4077
SDL_EndGPUCopyPass(copy_pass);
4178

42-
SDL_GPUFence *fence =
43-
SDL_SubmitGPUCommandBufferAndAcquireFence(command_buffer);
44-
SDL_WaitForGPUFences(device, true, &fence, 1);
45-
SDL_ReleaseGPUFence(device, fence);
79+
command_buffer.submit();
4680

4781
auto *raw_pixels = reinterpret_cast<Uint32 *>(
4882
SDL_MapGPUTransferBuffer(device, download_transfer_buffer, false));
4983

50-
Uint32 img_num_bytes = width * height * pixel_size;
51-
auto *rgba_pixels = (Uint32 *)std::malloc(img_num_bytes);
84+
return {
85+
.data = raw_pixels,
86+
.format = format,
87+
.width = width,
88+
.height = height,
89+
.buffer = download_transfer_buffer,
90+
.payloadSize = requiredSize,
91+
};
92+
}
93+
94+
void writeToFile(CommandBuffer &command_buffer, const Device &device,
95+
TransferBufferPool &pool, SDL_GPUTexture *texture,
96+
SDL_GPUTextureFormat format, Uint16 width, Uint16 height,
97+
const char *filename) {
98+
99+
auto res = downloadTexture(command_buffer, device, pool, texture, format,
100+
width, height);
101+
auto *raw_pixels = res.data;
102+
auto *rgba_pixels = (Uint32 *)SDL_malloc(res.payloadSize);
52103
if (format == SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM) {
53-
bgraToRgbaConvert(raw_pixels, rgba_pixels, width * height);
104+
bgraToRgbaConvert(raw_pixels, rgba_pixels, res.width * res.height);
54105
} else {
55-
SDL_memcpy(rgba_pixels, raw_pixels, img_num_bytes);
106+
SDL_memcpy(rgba_pixels, raw_pixels, res.payloadSize);
56107
}
57108

58-
stbi_write_png(filename, int(width), int(height), 4, rgba_pixels, 0);
109+
stbi_write_png(filename, int(res.width), int(res.height), 4, rgba_pixels, 0);
59110
std::free(rgba_pixels);
111+
112+
SDL_UnmapGPUTransferBuffer(device, res.buffer);
60113
}
61114

62115
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
63-
void videoWriteTextureToFrame(const Device &device,
64-
media::VideoRecorder &recorder,
65-
SDL_GPUTexture *texture,
66-
SDL_GPUTextureFormat format, const Uint32 width,
67-
const Uint32 height) {
116+
void videoWriteTextureToFrame(CommandBuffer &command_buffer,
117+
const Device &device, TransferBufferPool &pool,
118+
VideoRecorder &recorder, SDL_GPUTexture *texture,
119+
SDL_GPUTextureFormat format, const Uint16 width,
120+
const Uint16 height) {
68121
assert(recorder.initialized());
69122

70-
SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device);
71-
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer);
72-
73-
const Uint32 pixel_size = SDL_GPUTextureFormatTexelBlockSize(format);
74-
const Uint32 payload_size = width * height * pixel_size;
75-
76-
SDL_GPUTransferBuffer *download_transfer_buffer;
77-
{
78-
SDL_GPUTransferBufferCreateInfo info{
79-
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD,
80-
.size = payload_size,
81-
.props = 0};
82-
download_transfer_buffer = SDL_CreateGPUTransferBuffer(device, &info);
83-
}
84-
85-
SDL_GPUTextureTransferInfo destination{
86-
.transfer_buffer = download_transfer_buffer,
87-
.offset = 0,
88-
};
123+
auto res = downloadTexture(command_buffer, device, pool, texture, format,
124+
width, height);
89125

90-
SDL_GPUTextureRegion source_region{
91-
.texture = texture,
92-
.layer = 0,
93-
.w = width,
94-
.h = height,
95-
};
96-
97-
SDL_DownloadFromGPUTexture(copy_pass, &source_region, &destination);
98-
SDL_EndGPUCopyPass(copy_pass);
99-
100-
auto *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(command_buffer);
101-
SDL_WaitForGPUFences(device, true, &fence, 1);
102-
SDL_ReleaseGPUFence(device, fence);
103-
104-
Uint8 *raw_data = reinterpret_cast<Uint8 *>(
105-
SDL_MapGPUTransferBuffer(device, download_transfer_buffer, false));
126+
Uint8 *raw_data = reinterpret_cast<Uint8 *>(res.data);
127+
recorder.writeFrame(raw_data, res.payloadSize, format);
106128

107-
recorder.writeFrame(raw_data, payload_size, format);
129+
SDL_UnmapGPUTransferBuffer(device, res.buffer);
108130
}
109131
#endif
110132

src/candlewick/utils/WriteTextureToImage.h

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,50 @@
99
namespace candlewick {
1010
namespace media {
1111

12-
void dumpTextureImgToFile(const Device &device, SDL_GPUTexture *texture,
13-
SDL_GPUTextureFormat format, const Uint32 width,
14-
const Uint32 height, const char *filename);
12+
struct DownloadResult {
13+
Uint32 *data;
14+
SDL_GPUTextureFormat format;
15+
Uint16 width;
16+
Uint16 height;
17+
SDL_GPUTransferBuffer *buffer; // used for unmapping later
18+
Uint32 payloadSize;
19+
};
20+
21+
/// \brief Transfer buffer for the texture downloader.
22+
class TransferBufferPool {
23+
SDL_GPUDevice *_device = nullptr;
24+
SDL_GPUTransferBuffer *_buffer = nullptr;
25+
Uint32 _currentBufSize = 0;
26+
27+
public:
28+
TransferBufferPool(const Device &device);
29+
void release() noexcept;
30+
~TransferBufferPool() noexcept { this->release(); }
31+
32+
SDL_GPUTransferBuffer *acquireBuffer(Uint32 requiredSize);
33+
};
34+
35+
/// \brief Download texture to a mapped buffer.
36+
///
37+
/// \warning The user is expected to unmap the buffer in the result struct.
38+
DownloadResult downloadTexture(CommandBuffer &command_buffer,
39+
const Device &device, TransferBufferPool &pool,
40+
SDL_GPUTexture *texture,
41+
SDL_GPUTextureFormat format,
42+
const Uint16 width, const Uint16 height);
43+
44+
void writeToFile(CommandBuffer &command_buffer, const Device &device,
45+
TransferBufferPool &pool, SDL_GPUTexture *texture,
46+
SDL_GPUTextureFormat format, const Uint16 width,
47+
const Uint16 height, const char *filename);
1548

1649
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
17-
void videoWriteTextureToFrame(const Device &device, VideoRecorder &recorder,
50+
void videoWriteTextureToFrame(CommandBuffer &command_buffer,
51+
const Device &device, TransferBufferPool &pool,
52+
VideoRecorder &recorder,
1853
SDL_GPUTexture *texture,
19-
SDL_GPUTextureFormat format, const Uint32 width,
20-
const Uint32 height);
54+
SDL_GPUTextureFormat format, const Uint16 width,
55+
const Uint16 height);
2156
#endif
2257

2358
} // namespace media

0 commit comments

Comments
 (0)