A host application, such as a game engine, creates its own Vulkan instance and device and hands them to the GUI. Each frame the host renders into its own image, and the GUI shows that image in a window. Full source: examples/host_device/host_device.cpp.
- On this page:
- the host is the application that owns the Vulkan device
- the GUI is TachyonUI, running on that device
- The example creates the device with vk-bootstrap. Any other way works too; what matters is what it enables and what it hands over.
A device the GUI can use
- Enable everything VulkanDeviceContext::GetDeviceRequirements() lists.
- The instance also needs SDL's surface extensions, because SDL creates the GUI's windows.
auto selected = vkb::PhysicalDeviceSelector{ instance }
.set_minimum_version(1, 3)
.set_required_features(requirements.features)
.set_required_features_11(requirements.features11)
.set_required_features_12(requirements.features12)
.set_required_features_13(requirements.features13)
.add_required_extensions(requirements.deviceExtensions)
.defer_surface_initialization()
.select();
static DeviceRequirements GetDeviceRequirements()
What a host's device must enable for the GUI.
The surface extensions come from SDL, so SDL must be running before the instance is created:
SDL_Init(SDL_INIT_VIDEO);
Uint32 count = 0;
if (const char* const* extensions = SDL_Vulkan_GetInstanceExtensions(&count))
for (Uint32 i = 0; i < count; ++i)
builder.enable_extension(extensions[i]);
Handing it over
- Describe the host's handles in an ExternalDevice.
- Host and GUI submit to the same queue, so pass the mutex the host locks around its own submissions in queueMutex.
external.
device = device.device;
A host's Vulkan handles for SetExternalDevice, so the GUI renders on the host's device (an engine hos...
Definition vk_device_context.h:152
VkDevice device
Made with GetDeviceRequirements() enabled.
Definition vk_device_context.h:158
VkInstance instance
Must enable the window backend's surface extensions.
Definition vk_device_context.h:154
VkPhysicalDevice physicalDevice
The GPU device was made on.
Definition vk_device_context.h:156
VkQueue graphicsQueue
Every GUI submit and present goes here; it supports graphics and presenting to the GUI's windows.
Definition vk_device_context.h:161
std::mutex * queueMutex
Optional.
Definition vk_device_context.h:172
uint32_t graphicsQueueFamily
graphicsQueue's family.
Definition vk_device_context.h:163
- Give it to the Vulkan graphics backend before Initialize:
- the graphics backend is the part of TachyonUI that draws, here VulkanGraphicsContext
- SetExternalDevice makes it render on the host's device instead of creating one
- SetGraphicsBackend hands the backend to the app, which owns it from then on
- Keep a plain pointer to the backend for the calls below.
auto backend = std::make_unique<tachyon::vulkan::VulkanGraphicsContext>();
if (this->host.CreateDevice())
backend->SetExternalDevice(this->host.Describe());
else
std::printf("no Vulkan 1.3 device with the features the GUI needs\n");
vk = backend.get();
SetGraphicsBackend(std::move(backend));
Showing the host's image
- After Initialize, register the host's image view and sampler once.
- RegisterExternalImage returns an ImageIndex, the number controls use to show an image.
- The host keeps owning the view and the sampler.
hostImage = vk->RegisterExternalImage(host.view, host.sampler);
- Each frame the host submits its rendering during OnGUI, before the GUI submits its own frame.
- Image then shows the result.
void OnGUI() override
{
const float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - start).count();
host.Render(0.5f + 0.5f * std::sin(t), 0.5f + 0.5f * std::sin(t + 2.1f), 0.5f + 0.5f * std::sin(t + 4.2f));
{
Text("Rendered by the host:");
Image(hostImage,
tachyon::uint2{ Host::kImageSize.width, Host::kImageSize.height });
EndVertical();
}
EndWindow();
}
Per-element overrides of style layout properties.
Definition gui_constraints.h:150
LayoutOverride & FlexGrow(int16_t v) &
Overrides the style's flex-grow / flex-shrink.
Definition gui_constraints.h:317
int2 Size() const
The explicit sizes; kAuto on axes without one.
Definition gui_constraints.h:205
Definition gui_base_types.h:75
- Host::Render follows the GUI's rules for the image:
- before writing it, it waits for the GUI's fragment-stage reads
- when done, it leaves the image in SHADER_READ_ONLY_OPTIMAL
Barrier(firstFrame ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, 0,
VK_PIPELINE_STAGE_2_CLEAR_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);
vkCmdClearColorImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range);
Barrier(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_2_CLEAR_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_SAMPLED_READ_BIT);
Shutting down
- Cleanup destroys only what the GUI created.
- The host destroys its own device afterwards.
Full requirements, ownership and synchronization rules: Using your own Vulkan device.
Previous: Custom theme | Examples | Next: Showcase