Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
758e255
viz/core: VK_EXT_debug_utils + VK_EXT_validation_features (debug builds)
farbod-nv May 6, 2026
a342a4b
viz/core: vk.hpp project include — vulkan-hpp + vk::raii conventions
farbod-nv May 6, 2026
3896199
viz/core: migrate VkContext internals to vk::raii
farbod-nv May 6, 2026
ca18b3d
viz/core: restore API-doc comments on PhysicalDeviceInfo + Config
farbod-nv May 6, 2026
36db5a0
viz/core: migrate RenderTarget internals to vk::raii
farbod-nv May 6, 2026
2f1c34d
viz/core: migrate FrameSync internals to vk::raii
farbod-nv May 6, 2026
f4005b0
viz/core: migrate DeviceImage internals to vk::raii
farbod-nv May 6, 2026
63ac90a
viz/core: review fixes — validation features chain, cmd buffer cast, …
farbod-nv May 6, 2026
1c6865c
viz/core: reorder validation pNext chain — ValidationFeaturesEXT first
farbod-nv May 6, 2026
cf77d95
viz/session: migrate Swapchain internals to vk::raii
farbod-nv May 6, 2026
e141180
viz/session: migrate GlfwWindow surface to vk::raii::SurfaceKHR
farbod-nv May 6, 2026
f90cb15
viz/session: migrate OffscreenBackend internals to vk::raii
farbod-nv May 6, 2026
ba0bb1f
viz/session: migrate VizCompositor internals to vk::raii
farbod-nv May 6, 2026
b902d0c
viz/layers: migrate QuadLayer internals to vk::raii
farbod-nv May 6, 2026
ce22649
viz: clang-format pass on migrated files
farbod-nv May 6, 2026
3dd5352
viz/session: don't throw on OUT_OF_DATE in Swapchain::acquire_next_image
farbod-nv May 6, 2026
41fbf04
viz/core: portable debug-callback assignment + gate validation_featur…
farbod-nv May 6, 2026
992b09a
viz/core: portable find_memory_type signature + lift StructureChain a…
farbod-nv May 6, 2026
e127860
fix
farbod-nv May 6, 2026
d9e18f1
viz/session: cast through raw VkXxx for VK_NULL_HANDLE comparisons
farbod-nv May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/viz/core/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ add_library(viz_core STATIC
inc/viz/core/render_target.hpp
inc/viz/core/viz_buffer.hpp
inc/viz/core/viz_types.hpp
inc/viz/core/vk.hpp
inc/viz/core/vk_context.hpp
)

Expand All @@ -50,4 +51,10 @@ target_link_libraries(viz_core
)

# Aliased as viz::core (consumers say viz::core, not viz::viz_core).
# Defined PUBLIC so every consumer of viz_core (and transitively
# every Televiz Vulkan TU) sees vulkan-hpp's structs as aggregates,
# regardless of include order. Defining it only inside vk.hpp would
# silently break if another header included vulkan.hpp first.
target_compile_definitions(viz_core PUBLIC VULKAN_HPP_NO_CONSTRUCTORS)

add_library(viz::core ALIAS viz_core)
370 changes: 157 additions & 213 deletions src/viz/core/cpp/device_image.cpp

Large diffs are not rendered by default.

69 changes: 17 additions & 52 deletions src/viz/core/cpp/frame_sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,10 @@
#include <viz/core/vk_context.hpp>

#include <stdexcept>
#include <string>

namespace viz
{

namespace
{

void check_vk(VkResult result, const char* what)
{
if (result != VK_SUCCESS)
{
throw std::runtime_error(std::string("FrameSync: ") + what + " failed: VkResult=" + std::to_string(result));
}
}

} // namespace

std::unique_ptr<FrameSync> FrameSync::create(const VkContext& ctx)
{
if (!ctx.is_initialized())
Expand All @@ -45,21 +31,17 @@ FrameSync::~FrameSync()

void FrameSync::init()
{
const VkDevice device = ctx_->device();
const auto& device = ctx_->raii_device();

VkFenceCreateInfo fence_info{};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
// Start signaled so the first wait()/reset() pair is a no-op.
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;

VkSemaphoreCreateInfo sem_info{};
sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
const vk::FenceCreateInfo fence_info{ .flags = vk::FenceCreateFlagBits::eSignaled };
const vk::SemaphoreCreateInfo sem_info{};

try
{
check_vk(vkCreateFence(device, &fence_info, nullptr, &in_flight_fence_), "vkCreateFence");
check_vk(vkCreateSemaphore(device, &sem_info, nullptr, &image_available_), "vkCreateSemaphore(image_available)");
check_vk(vkCreateSemaphore(device, &sem_info, nullptr, &render_complete_), "vkCreateSemaphore(render_complete)");
in_flight_fence_ = vk::raii::Fence{ device, fence_info };
image_available_ = vk::raii::Semaphore{ device, sem_info };
render_complete_ = vk::raii::Semaphore{ device, sem_info };
}
catch (...)
{
Expand All @@ -70,48 +52,31 @@ void FrameSync::init()

void FrameSync::destroy()
{
if (ctx_ == nullptr)
{
return;
}
const VkDevice device = ctx_->device();
if (device == VK_NULL_HANDLE)
{
return;
}
if (render_complete_ != VK_NULL_HANDLE)
{
vkDestroySemaphore(device, render_complete_, nullptr);
render_complete_ = VK_NULL_HANDLE;
}
if (image_available_ != VK_NULL_HANDLE)
{
vkDestroySemaphore(device, image_available_, nullptr);
image_available_ = VK_NULL_HANDLE;
}
if (in_flight_fence_ != VK_NULL_HANDLE)
{
vkDestroyFence(device, in_flight_fence_, nullptr);
in_flight_fence_ = VK_NULL_HANDLE;
}
render_complete_ = nullptr;
image_available_ = nullptr;
in_flight_fence_ = nullptr;
}

void FrameSync::wait(uint64_t timeout_ns)
{
if (in_flight_fence_ == VK_NULL_HANDLE)
if (!*in_flight_fence_)
{
throw std::logic_error("FrameSync::wait: not initialized");
}
check_vk(vkWaitForFences(ctx_->device(), 1, &in_flight_fence_, VK_TRUE, timeout_ns), "vkWaitForFences");
const vk::Result r = ctx_->raii_device().waitForFences({ *in_flight_fence_ }, VK_TRUE, timeout_ns);
if (r != vk::Result::eSuccess)
{
throw std::runtime_error("FrameSync: vkWaitForFences returned " + vk::to_string(r));
}
}

void FrameSync::reset()
{
if (in_flight_fence_ == VK_NULL_HANDLE)
if (!*in_flight_fence_)
{
throw std::logic_error("FrameSync::reset: not initialized");
}
check_vk(vkResetFences(ctx_->device(), 1, &in_flight_fence_), "vkResetFences");
ctx_->raii_device().resetFences({ *in_flight_fence_ });
}

} // namespace viz
24 changes: 10 additions & 14 deletions src/viz/core/cpp/inc/viz/core/device_image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#include <viz/core/viz_buffer.hpp> // PixelFormat — used in API signatures
#include <viz/core/viz_types.hpp>
#include <vulkan/vulkan.h>
#include <viz/core/vk.hpp>

#include <atomic>
#include <cstdint>
Expand Down Expand Up @@ -61,11 +61,11 @@ class DeviceImage
// init; transition_to_*() below moves it back and forth.
VkImage vk_image() const noexcept
{
return image_;
return *image_;
}
VkImageView vk_image_view() const noexcept
{
return image_view_;
return *image_view_;
}
VkFormat vk_format() const noexcept
{
Expand All @@ -76,7 +76,7 @@ class DeviceImage
// value returned by cuda_done_writing_value() before sampling.
VkSemaphore cuda_done_writing() const noexcept
{
return cuda_done_writing_;
return *cuda_done_writing_;
}

// Latest value CUDA has signaled successfully. Vulkan uses this
Expand Down Expand Up @@ -132,10 +132,12 @@ class DeviceImage
VkFormat vk_format_ = VK_FORMAT_R8G8B8A8_UNORM;
VkImageLayout current_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;

VkImage image_ = VK_NULL_HANDLE;
VkDeviceMemory memory_ = VK_NULL_HANDLE;
VkImageView image_view_ = VK_NULL_HANDLE;
VkCommandPool command_pool_ = VK_NULL_HANDLE; // For layout transitions only.
// Declared parent-first so reverse-destruction is correct.
vk::raii::DeviceMemory memory_{ nullptr };
vk::raii::Image image_{ nullptr };
vk::raii::ImageView image_view_{ nullptr };
vk::raii::CommandPool command_pool_{ nullptr }; // for layout transitions only
vk::raii::Semaphore cuda_done_writing_{ nullptr };

// CUDA dup's the fd internally on import; we close ours after.
int memory_fd_ = -1;
Expand All @@ -144,12 +146,6 @@ class DeviceImage
cudaMipmappedArray_t cuda_mipmapped_array_ = nullptr;
cudaArray_t cuda_array_ = nullptr; // Level-0 view, non-owning.

// Producer→consumer timeline semaphore exported via
// VK_KHR_external_semaphore_fd and imported into CUDA. Two atomic
// counters (next reservation, last committed) so a failed
// cudaSignal can't leave the public value pointing at something
// that was never signaled.
VkSemaphore cuda_done_writing_ = VK_NULL_HANDLE;
cudaExternalSemaphore_t cuda_cuda_done_writing_ = nullptr;
std::atomic<uint64_t> cuda_done_writing_next_{ 0 };
std::atomic<uint64_t> cuda_done_writing_value_{ 0 };
Expand Down
16 changes: 7 additions & 9 deletions src/viz/core/cpp/inc/viz/core/frame_sync.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#pragma once

#include <vulkan/vulkan.h>
#include <viz/core/vk.hpp>

#include <memory>

Expand Down Expand Up @@ -31,8 +31,6 @@ class VkContext;
class FrameSync
{
public:
// Creates the three sync objects. Throws std::runtime_error on Vulkan
// failure or std::invalid_argument if ctx is not initialized.
static std::unique_ptr<FrameSync> create(const VkContext& ctx);

~FrameSync();
Expand All @@ -53,15 +51,15 @@ class FrameSync

VkFence in_flight_fence() const noexcept
{
return in_flight_fence_;
return *in_flight_fence_;
}
VkSemaphore image_available_semaphore() const noexcept
{
return image_available_;
return *image_available_;
}
VkSemaphore render_complete_semaphore() const noexcept
{
return render_complete_;
return *render_complete_;
}

private:
Expand All @@ -70,9 +68,9 @@ class FrameSync

const VkContext* ctx_ = nullptr;

VkFence in_flight_fence_ = VK_NULL_HANDLE;
VkSemaphore image_available_ = VK_NULL_HANDLE;
VkSemaphore render_complete_ = VK_NULL_HANDLE;
vk::raii::Fence in_flight_fence_{ nullptr };
vk::raii::Semaphore image_available_{ nullptr };
vk::raii::Semaphore render_complete_{ nullptr };
};

} // namespace viz
44 changes: 19 additions & 25 deletions src/viz/core/cpp/inc/viz/core/render_target.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#pragma once

#include <viz/core/viz_types.hpp>
#include <vulkan/vulkan.h>
#include <viz/core/vk.hpp>

#include <memory>

Expand All @@ -26,10 +26,6 @@ class VkContext;
// The render pass clears both attachments at load and stores the color
// attachment (the depth attachment is dontCare on store — we never read it
// back).
//
// The class owns the Vulkan handles (images, image views, memory, render
// pass, framebuffer) and tears them down in destroy() / destructor. It does
// not own the VkContext.
class RenderTarget
{
public:
Expand All @@ -44,33 +40,31 @@ class RenderTarget
// on Vulkan failure or std::invalid_argument if resolution is zero.
static std::unique_ptr<RenderTarget> create(const VkContext& ctx, const Config& config);

// Releases all Vulkan handles. Idempotent.
~RenderTarget();
void destroy();

// Non-copyable, non-movable for now (owns Vulkan handles).
RenderTarget(const RenderTarget&) = delete;
RenderTarget& operator=(const RenderTarget&) = delete;
RenderTarget(RenderTarget&&) = delete;
RenderTarget& operator=(RenderTarget&&) = delete;

// Vulkan handle accessors for the compositor / custom layers.
// Raw-handle accessors for the compositor / custom layers.
VkRenderPass render_pass() const noexcept
{
return render_pass_;
return *render_pass_;
}
VkFramebuffer framebuffer() const noexcept
{
return framebuffer_;
return *framebuffer_;
}

VkImage color_image() const noexcept
{
return color_image_;
return *color_image_;
}
VkImageView color_image_view() const noexcept
{
return color_view_;
return *color_view_;
}
VkFormat color_format() const noexcept
{
Expand All @@ -79,11 +73,11 @@ class RenderTarget

VkImage depth_image() const noexcept
{
return depth_image_;
return *depth_image_;
}
VkImageView depth_image_view() const noexcept
{
return depth_view_;
return *depth_view_;
}
VkFormat depth_format() const noexcept
{
Expand All @@ -109,24 +103,24 @@ class RenderTarget
void create_depth_image(const Config& config);
void create_render_pass();
void create_framebuffer();
void destroy_attachments(); // images + views + memory + framebuffer
void destroy_attachments();

const VkContext* ctx_ = nullptr;

Resolution resolution_{};

VkFormat color_format_ = VK_FORMAT_R8G8B8A8_SRGB;
VkImage color_image_ = VK_NULL_HANDLE;
VkDeviceMemory color_memory_ = VK_NULL_HANDLE;
VkImageView color_view_ = VK_NULL_HANDLE;

VkFormat depth_format_ = VK_FORMAT_D32_SFLOAT;
VkImage depth_image_ = VK_NULL_HANDLE;
VkDeviceMemory depth_memory_ = VK_NULL_HANDLE;
VkImageView depth_view_ = VK_NULL_HANDLE;

VkRenderPass render_pass_ = VK_NULL_HANDLE;
VkFramebuffer framebuffer_ = VK_NULL_HANDLE;
// Declared parent-first so reverse-order destruction tears children
// down before parents (framebuffer → views → images → memory).
vk::raii::DeviceMemory color_memory_{ nullptr };
vk::raii::Image color_image_{ nullptr };
vk::raii::ImageView color_view_{ nullptr };
vk::raii::DeviceMemory depth_memory_{ nullptr };
vk::raii::Image depth_image_{ nullptr };
vk::raii::ImageView depth_view_{ nullptr };
vk::raii::RenderPass render_pass_{ nullptr };
vk::raii::Framebuffer framebuffer_{ nullptr };
};

} // namespace viz
30 changes: 30 additions & 0 deletions src/viz/core/cpp/inc/viz/core/vk.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

#pragma once

// Project-wide vulkan-hpp + vk::raii include header.
//
// Conventions for Televiz Vulkan code:
// * Owned handles use vk::raii::* (Instance, Device, Image, Semaphore, ...)
// * pNext chains use vk::StructureChain<Outer, Inner1, Inner2, ...>
// * Initialize structs with C++20 designated initializers
// (`vk::ImageCreateInfo{.imageType = ..., .format = ..., ...}`)
// * Extract raw handles via *handle_ ONLY at deliberate interop
// boundaries (CUDA external memory FD, XrGraphicsBindingVulkanKHR).
// Mark such sites with a comment so they read as boundary code.
//
// We use the default static dispatch for vulkan-hpp; vk::raii types
// own their dispatcher automatically — no VULKAN_HPP_DEFAULT_DISPATCHER
// initialization needed.
//
// VULKAN_HPP_NO_CONSTRUCTORS is defined as a project-wide compile
// flag in viz_core's CMakeLists (PUBLIC propagation), not here —
// otherwise the macro would only take effect for TUs that happen to
// include this header before vulkan.hpp. The compile flag enforces
// it everywhere, removing vulkan-hpp's hand-written constructors so
// structs are aggregates and C++20 designated initializers work
// (`vk::ImageCreateInfo{.format = ..., ...}`).

#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
Loading
Loading