TachyonUI 0.1.0
High-performance UI for building tools.
Loading...
Searching...
No Matches
Rendering the UI into a host image

  • ElementFlags::WindowRenderToImage makes a render-to-image window:
    • it gets no OS window
    • it is laid out and drawn into an image instead
  • Use it for:
    • engine viewports
    • compositing the UI into your own scene
    • off-screen rendering, as the GPU tests do
if (BeginWindow(windowID, "Panel", theme.windowStyleRef, tachyon::LayoutOverride{}.Size(400, 300),
{
// controls as usual
}
EndWindow();
@ WindowRenderToImage
no OS window; see WindowFlags::RenderToImage
Definition gui_element_flags.h:40
Per-element overrides of style layout properties.
Definition gui_constraints.h:150
int2 Size() const
The explicit sizes; kAuto on axes without one.
Definition gui_constraints.h:205

Copying into the host's image

vk->SetWindowImageTarget(windowID, hostImage, VkExtent2D{ 400, 300 });
  • You can set the target before the window exists.
  • Passing VK_NULL_HANDLE stops the copies.
  • The window's pixels are scaled to the extent you give.
vk->SetWindowImageTarget(windowID, VK_NULL_HANDLE, VkExtent2D{}); // no more copies

Synchronization contract, on the queue passed to SetExternalDevice:

  1. The image needs TRANSFER_DST usage. After your fragment-stage reads, the GUI's frame moves it from SHADER_READ_ONLY_OPTIMAL (UNDEFINED the first time) to TRANSFER_DST_OPTIMAL, copies the window in, and leaves it in SHADER_READ_ONLY_OPTIMAL, visible to the fragment stage.
  2. Read it in work submitted after the GUI's frame (after RecordFrame), and leave it in SHADER_READ_ONLY_OPTIMAL.
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // the GUI copies in, you sample
  • Recording the GUI's draw commands into your own command buffer is not supported yet.

Reading the pixels back

  • ReadWindowPixels copies the window's last frame to memory.
    • The format is BGRA8, 4 bytes per pixel, with tightly packed rows.
    • It works on the GUI's own device too.
    • It waits for that frame to finish on the GPU.
const tachyon::IntRect rect = app.windowManager->GetWindowRect(windowID); // pixels
std::vector<uint8_t> pixels(size_t(rect.w) * rect.h * 4);
if (tachyon::Result<void> read = app.ReadWindowPixels(windowID, pixels); !read)
std::fprintf(stderr, "%s\n", read.GetError().message.c_str());
A value, or the Error saying why there is none.
Definition gui_result.h:36
Definition gui_base_types.h:407
  • It returns ErrorCode::NotFound for an unknown window.
  • It returns ErrorCode::Unsupported when:
    • the window is not a render-to-image window
    • no frame has been rendered yet
    • the span is too small

Input for render-to-image windows

  • No OS input reaches a render-to-image window. You inject it with the Inject* calls.
  • Positions are in the window's pixels.
app.InjectMouseMove(windowID, pos);
app.InjectMouseButton(windowID, pos, tachyon::MouseButtons::Left, true); // down
app.InjectMouseButton(windowID, pos, tachyon::MouseButtons::Left, false); // up
app.InjectMouseScroll(windowID, pos, tachyon::float2{ 0.0f, 1.0f });
app.InjectMouseLeave(windowID);
app.InjectKey(windowID, key, tachyon::KeyAction::KeyDown);
app.InjectText(windowID, "text"); // UTF-8
app.InjectFocus(windowID, false);
@ KeyDown
Definition gui_input_enums.h:51
@ Left
Definition gui_input_enums.h:40
Definition gui_base_types.h:121

Previous: Your own loop | Integration | Next: Allocation hooks and headless backends