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
67namespace 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
0 commit comments