Skip to content

Added PerFrameTransferHelper for streaming and async readback.#86

Merged
Mrkol merged 1 commit into
AlexandrShcherbakov:mainfrom
JackOfBlades232:main
Jan 25, 2026
Merged

Added PerFrameTransferHelper for streaming and async readback.#86
Mrkol merged 1 commit into
AlexandrShcherbakov:mainfrom
JackOfBlades232:main

Conversation

@JackOfBlades232

@JackOfBlades232 JackOfBlades232 commented Jan 17, 2026

Copy link
Copy Markdown
Contributor

Added a new class etna::PerFrameTransferHelper for handling upload and readback inside the application loop.

Has a sync API for uploads and an async API for uploads and readbacks. Operates with one staging buffer for both, so through a combination of splitting the interface into multiple "classes" and runtime checks ensures all readback is processed before upload. Handles asynchronous operations by giving the user a state struct and having the user call methods on this struct.

Usage example:

// Somewhere 
etna::PerFrameTransferHelper th{etna::PerFrameTransferHelper::CreateInfo{
  .totalStagingSize = 4096 * 4096 * 4, .wc = &wc}};

// init calls for async operations states can be called any time
auto bufferUpload = th.initUploadBufferAsync(buf1, 0, bufData1);
auto bufferReadback = th.initReadbackBufferAsync(bufStorageDest2, buf2, 0);
auto imageUpload = th.initUploadImageAsync(img1, 0, 0, imgData1);

// actual sync/progress calls must be done every frame inside of processing
// scope, for that the API is split into several classes to enforce order.

if (auto frame = th.beginFrame())
{
  if (auto rb = frame.beginReadback())
  {
    if (!rb.hasSpaceThisFrame())
      ; // Out of staging space, all upload/progress calls will do nothing
    if (rb.progressBufferReadbackAsync(cmd_buf, bufferReadback))
      ; // Readback is done
  }

  if (auto up = frame.beginUpload())
  {
    if (up.progressBufferUploadAsync(cmd_buf, bufferUpload))
      ; // Upload will be completed this frame
    if (up.progressImageUploadAsync(cmd_buf, imageUpload))
      ; // Upload will be completed this frame
    if (up.uploadBufferSync(cmd_buf, buf3, 0, bufData3))
      ; // There was space in staging and upload will be done
    if (up.uploadImageSync(cmd_buf, img2, 0, 0, imgData2))
      ; // There was space in staging and upload will be done

    for (auto &texUpload : texUploads)
    {
      if (!up.hasSpaceThisFrame())
        break;
      if (texUpload.done())
        continue;
      if (up.progressImageUploadAsync(cmd_buf, texUpload))
        ; // Upload will be completed this frame
    }
  }
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 13. Check the log or trigger a new build to see more.

uint32_t offset;
std::span<std::byte const> src;

bool done() const { return transferHelper && src.size() == 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'const PerFrameTransferHelper *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
bool done() const { return transferHelper && src.size() == 0; }
bool done() const { return (transferHelper != nullptr) && src.size() == 0; }

uint32_t offset;
std::span<std::byte const> src;

bool done() const { return transferHelper && src.size() == 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
bool done() const { return transferHelper && src.size() == 0; }
bool done() const { return transferHelper && src.empty(); }
Additional context

/usr/include/c++/13/span:262: method 'span'::empty() defined here

      empty() const noexcept
      ^

size_t nextIssueSlot;
std::span<std::byte> dst;

bool done() const { return transferHelper && dst.size() == 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'const PerFrameTransferHelper *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
bool done() const { return transferHelper && dst.size() == 0; }
bool done() const { return (transferHelper != nullptr) && dst.size() == 0; }

size_t nextIssueSlot;
std::span<std::byte> dst;

bool done() const { return transferHelper && dst.size() == 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
bool done() const { return transferHelper && dst.size() == 0; }
bool done() const { return transferHelper && dst.empty(); }
Additional context

/usr/include/c++/13/span:262: method 'span'::empty() defined here

      empty() const noexcept
      ^

vk::Offset3D offset;
std::span<std::byte const> src;

bool done() const { return transferHelper && src.size() == 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'const PerFrameTransferHelper *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
bool done() const { return transferHelper && src.size() == 0; }
bool done() const { return (transferHelper != nullptr) && src.size() == 0; }

vk::Offset3D offset;
std::span<std::byte const> src;

bool done() const { return transferHelper && src.size() == 0; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
bool done() const { return transferHelper && src.size() == 0; }
bool done() const { return transferHelper && src.empty(); }
Additional context

/usr/include/c++/13/span:262: method 'span'::empty() defined here

      empty() const noexcept
      ^


void finish();

operator bool() const { return self != nullptr; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 'operator bool' must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]

Suggested change
operator bool() const { return self != nullptr; }
explicit operator bool() const { return self != nullptr; }


void finish();

operator bool() const { return self != nullptr; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 'operator bool' must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]

Suggested change
operator bool() const { return self != nullptr; }
explicit operator bool() const { return self != nullptr; }


void finish();

operator bool() const { return self != nullptr; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 'operator bool' must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]

Suggested change
operator bool() const { return self != nullptr; }
explicit operator bool() const { return self != nullptr; }

Comment thread etna/source/PerFrameTransferHelper.cpp Outdated

void PerFrameTransferHelper::UploadProcessor::finish()
{
if (!self)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'PerFrameTransferHelper *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
if (!self)
if (self == nullptr)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread etna/source/PerFrameTransferHelper.cpp Outdated

void PerFrameTransferHelper::FrameProcessor::finish()
{
if (!self)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'PerFrameTransferHelper *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
if (!self)
if (self == nullptr)

Comment thread etna/source/PerFrameTransferHelper.cpp Outdated
}
}

if (state.dst.size() == 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Suggested change
if (state.dst.size() == 0)
if (state.dst.empty())
Additional context

/usr/include/c++/13/span:262: method 'span'::empty() defined here

      empty() const noexcept
      ^

@github-actions

Copy link
Copy Markdown

А я всегда говорил, нормально делай — нормально будет! 👍

@github-actions

Copy link
Copy Markdown

А я всегда говорил, нормально делай — нормально будет! 👍

@github-actions

Copy link
Copy Markdown

А я всегда говорил, нормально делай — нормально будет! 👍

@github-actions

Copy link
Copy Markdown

А я всегда говорил, нормально делай — нормально будет! 👍

Comment thread etna/include/etna/PerFrameTransferHelper.hpp Outdated
Comment thread etna/include/etna/PerFrameTransferHelper.hpp Outdated
Comment thread etna/include/etna/PerFrameTransferHelper.hpp Outdated
@github-actions

Copy link
Copy Markdown

А я всегда говорил, нормально делай — нормально будет! 👍

@Mrkol Mrkol merged commit b3e5075 into AlexandrShcherbakov:main Jan 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants