Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions cmake/defaults/CYCOMMON.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,21 @@ SET(RV_DEPS_GLEW_VERSION_LIB
"2.3.1"
)

# vulkan https://github.com/KhronosGroup/Vulkan-Headers + https://github.com/KhronosGroup/Vulkan-Loader
SET(RV_DEPS_VULKAN_VERSION
"1.4.350.0"
)
SET(RV_DEPS_VULKAN_HEADERS_DOWNLOAD_HASH
"74d68465ca2ef442397dc159edaa3b9c"
)
SET(RV_DEPS_VULKAN_LOADER_DOWNLOAD_HASH
"6ec91c673b48bbdffc923cce9d6a1a85"
)
# libvulkan.so SONAME major (libvulkan.so.1)
SET(RV_DEPS_VULKAN_VERSION_LIB
"1"
)

# imgui https://github.com/pthom/imgui
#
# Note this also depends on the following repositories: https://github.com/pthom/implot.git https://github.com/dpaulat/imgui-backend-qt.git
Expand Down
7 changes: 7 additions & 0 deletions cmake/dependencies/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ IF(RV_TARGET_DARWIN
INCLUDE(gc)
INCLUDE(glew)
ENDIF()
# Vulkan (headers + libvulkan.so loader) is always fetched on Linux.
IF(RV_TARGET_LINUX)
INCLUDE(vulkan)
ENDIF()
INCLUDE(imath)
INCLUDE(zlib)
INCLUDE(ffmpeg)
Expand Down Expand Up @@ -163,6 +167,9 @@ IF(RV_TARGET_DARWIN
MESSAGE(STATUS "Using GC: ${RV_DEPS_GC_VERSION}")
MESSAGE(STATUS "Using GLEW: ${RV_DEPS_GLEW_VERSION}")
ENDIF()
IF(RV_TARGET_LINUX)
MESSAGE(STATUS "Using Vulkan: ${RV_DEPS_VULKAN_VERSION}")
ENDIF()
MESSAGE(STATUS "Using Imath: ${RV_DEPS_IMATH_VERSION}")
MESSAGE(STATUS "Using JpegTurbo: ${RV_DEPS_JPEGTURBO_VERSION}")
MESSAGE(STATUS "Using OpenEXR: ${RV_DEPS_OPENEXR_VERSION}")
Expand Down
96 changes: 96 additions & 0 deletions cmake/dependencies/build/vulkan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#
# Copyright (C) 2026 Autodesk, Inc. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
# Build Vulkan-Headers (header-only install) + Vulkan-Loader (libvulkan.so) from hash-pinned Khronos GitHub tarballs. Both install into the shared
# ${_install_dir}. Included by cmake/dependencies/vulkan.cmake when no system Vulkan package is found.
#
# Expects these variables from the caller (set by RV_CREATE_STANDARD_DEPS_VARIABLES): _target, _version, _install_dir, _include_dir, _lib_dir. And these
# dep-specific variables: _vulkan_lib, _vulkan_lib_name.
#
# The loader ships pre-generated sources and gates its Python codegen behind LOADER_CODEGEN (default OFF), so no Python is required at build time. It does
# require the Vulkan-Headers CMake package (find_package(VulkanHeaders CONFIG)), provided via CMAKE_PREFIX_PATH below.

SET(_headers_url
"https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/vulkan-sdk-${_version}.tar.gz"
)
SET(_loader_url
"https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/vulkan-sdk-${_version}.tar.gz"
)

# --- Vulkan-Headers: installs headers + the VulkanHeaders CMake config + the registry. ---
EXTERNALPROJECT_ADD(
${_target}_headers
URL ${_headers_url}
URL_MD5 ${RV_DEPS_VULKAN_HEADERS_DOWNLOAD_HASH}
DOWNLOAD_NAME vulkan-headers-${_version}.tar.gz
DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR}
SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/headers-src
BINARY_DIR ${RV_DEPS_BASE_DIR}/${_target}/headers-build
INSTALL_DIR ${_install_dir}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${_install_dir} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DVULKAN_HEADERS_ENABLE_MODULE=OFF -DVULKAN_HEADERS_ENABLE_TESTS=OFF
BUILD_ALWAYS FALSE
USES_TERMINAL_BUILD TRUE
)

# --- Vulkan-Loader: builds libvulkan.so against the installed headers. ---
# WSI: enable Xlib/XCB (RV is an X11 client); leave Wayland off to avoid a wayland-client build dependency.
EXTERNALPROJECT_ADD(
${_target}
DEPENDS ${_target}_headers
URL ${_loader_url}
URL_MD5 ${RV_DEPS_VULKAN_LOADER_DOWNLOAD_HASH}
DOWNLOAD_NAME vulkan-loader-${_version}.tar.gz
DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR}
SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/loader-src
BINARY_DIR ${RV_DEPS_BASE_DIR}/${_target}/loader-build
INSTALL_DIR ${_install_dir}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${_install_dir}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_LIBDIR=lib
-DCMAKE_PREFIX_PATH=${_install_dir}
-DVULKAN_HEADERS_INSTALL_DIR=${_install_dir}
-DBUILD_TESTS=OFF
-DBUILD_WSI_XLIB_SUPPORT=ON
-DBUILD_WSI_XCB_SUPPORT=ON
-DBUILD_WSI_WAYLAND_SUPPORT=OFF
-DBUILD_WSI_XLIB_XRANDR_SUPPORT=OFF
BUILD_BYPRODUCTS ${_vulkan_lib}
BUILD_ALWAYS FALSE
USES_TERMINAL_BUILD TRUE
)

IF(TARGET Vulkan::Vulkan)
# Qt's find_package(Qt6 Gui) -> WrapVulkanHeaders ran find_package(Vulkan) before us and created a global Vulkan::Vulkan pointing at the system loader. We
# can't redefine it (ADD_LIBRARY would error), so repoint it at our fetched build instead. This is invisible to Qt: it consumes only the Vulkan headers (via
# WrapVulkanHeaders) and dlopen()s the loader at runtime -- it never links Vulkan::Vulkan's IMPORTED_LOCATION.
MESSAGE(STATUS "Repointing existing Vulkan::Vulkan target at managed Vulkan ${_version}")
ADD_DEPENDENCIES(Vulkan::Vulkan ${_target})
FILE(MAKE_DIRECTORY ${_include_dir})
SET_TARGET_PROPERTIES(
Vulkan::Vulkan
PROPERTIES IMPORTED_LOCATION ${_vulkan_lib}
IMPORTED_SONAME ${_vulkan_lib_name}
INTERFACE_INCLUDE_DIRECTORIES ${_include_dir}
)
SET(RV_DEPS_LIST
${RV_DEPS_LIST} Vulkan::Vulkan
)
ELSE()
RV_ADD_IMPORTED_LIBRARY(
NAME
Vulkan::Vulkan
TYPE
SHARED
LOCATION
${_vulkan_lib}
SONAME
${_vulkan_lib_name}
INCLUDE_DIRS
${_include_dir}
DEPENDS
${_target}
ADD_TO_DEPS_LIST
)
ENDIF()
72 changes: 72 additions & 0 deletions cmake/dependencies/vulkan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#
# Copyright (C) 2026 Autodesk, Inc. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
# Vulkan = Vulkan-Headers + Vulkan-Loader (libvulkan.so), fetched from hash-pinned Khronos GitHub tarballs. RV consumes only the headers (<vulkan/vulkan.h>) and
# the loader (linked via Vulkan::Vulkan; all API calls go through vkGetInstanceProcAddr/vkGetDeviceProcAddr) -- no validation layers or shader tools. Modeled on
# glew.cmake.
#

# FORCE_LIB so _lib_dir is install/lib (we also pass -DCMAKE_INSTALL_LIBDIR=lib to the loader), giving a deterministic location across RHEL (lib64) and
# non-RHEL.
RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_VULKAN" "${RV_DEPS_VULKAN_VERSION}" "" "" FORCE_LIB)

# --- Try to find an installed/system package first (only when RV_DEPS_PREFER_INSTALLED=ON). ---
# Vulkan ships no CMake config; it is located via the built-in FindVulkan module (ALLOW_MODULE) or the `vulkan` pkg-config module. Omit VERSION (FindVulkan
# version reporting is unreliable).
RV_FIND_DEPENDENCY(
TARGET
${_target}
PACKAGE
Vulkan
ALLOW_MODULE
PKG_CONFIG_NAME
vulkan
DEPS_LIST_TARGETS
Vulkan::Vulkan
)

# --- Library naming (shared between find and build paths), à la glew's _glew_lib_name. ---
# We reference the SONAME symlink (libvulkan.so.1); the loader also installs the fully versioned real file (libvulkan.so.<version>) and the libvulkan.so dev
# symlink alongside it.
SET(_vulkan_lib_name
${CMAKE_SHARED_LIBRARY_PREFIX}vulkan${CMAKE_SHARED_LIBRARY_SUFFIX}.${RV_DEPS_VULKAN_VERSION_LIB}
)
SET(_vulkan_lib
${_lib_dir}/${_vulkan_lib_name}
)

IF(${_target}_FOUND)
# Found via RV_FIND_DEPENDENCY (RV_DEPS_PREFER_INSTALLED=ON). FindVulkan/pkg-config usually provides the Vulkan::Vulkan target; create it only if the
# pkg-config path did not.
IF(NOT TARGET Vulkan::Vulkan)
RV_ADD_IMPORTED_LIBRARY(
NAME
Vulkan::Vulkan
TYPE
SHARED
LOCATION
${_vulkan_lib}
SONAME
${_vulkan_lib_name}
INCLUDE_DIRS
${_include_dir}
DEPENDS
${_target}
ADD_TO_DEPS_LIST
)
ENDIF()

# Found path: use TARGET_LIBS to resolve the actual library path at build time.
RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS Vulkan::Vulkan)
ELSE()
# --- Default: fetch headers + loader from Khronos and build from source. ---
# We always build our own hash-pinned Vulkan (like every other managed dependency) unless RV_DEPS_PREFER_INSTALLED=ON. Note that a global Vulkan::Vulkan
# target may already exist here: INCLUDE(qt6) runs before us and Qt's find_package(Qt6 Gui) -> WrapVulkanHeaders runs find_package(Vulkan), which (with
# CMAKE_FIND_PACKAGE_TARGETS_GLOBAL) creates a global Vulkan::Vulkan from a discoverable *system* loader. build/vulkan.cmake takes ownership of that target
# name (repointing it at our fetched build) rather than reusing the system loader.
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/vulkan.cmake)

RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_vulkan_lib_name})
ENDIF()
9 changes: 9 additions & 0 deletions src/bin/apps/rv/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,15 @@ int utf8Main(int argc, char* argv[])
// (RV Preferences/Rendering/Multithread GPU Upload)
QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);

// Put every QOpenGLContext in one resource-sharing group. RV's GL contexts
// (GLView, video devices) already share manually via setShareContext, and
// shared resources like FTGL font-atlas textures rely on that invariant. On
// the Linux Vulkan presentation path there is no GLView to chain from, so
// the offscreen presentation context joins this global group instead (see
// QTVulkanVideoDevice::ensureGLContext). Otherwise, font glyph uploads land
// in a context where the atlas texture has no storage.
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);

TwkUtil::MemPool::initialize();

string altPrefsPath;
Expand Down
25 changes: 25 additions & 0 deletions src/lib/app/RvCommon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ IF(RV_TARGET_LINUX
LIST(APPEND _sources ${_filename})
ENDIF()

IF(RV_TARGET_LINUX)
# Vulkan is always available on Linux (fetched via cmake/dependencies/vulkan.cmake). No VULKAN_SDK environment variable or setup-env.sh is required.
IF(NOT TARGET Vulkan::Vulkan)
MESSAGE(FATAL_ERROR "Vulkan::Vulkan target is missing on Linux; cmake/dependencies/vulkan.cmake should provide it")
ENDIF()
MESSAGE(STATUS "Linux Vulkan: using managed Vulkan ${RV_DEPS_VULKAN_VERSION}")
LIST(
APPEND
_sources
VulkanBuildProbe.cpp
VulkanView.cpp
QTVulkanVideoDevice.cpp
RvCommon/VulkanView.h
RvCommon/QTVulkanVideoDevice.h
)
ENDIF()

FILE(GLOB _ui_sources ui/*.ui)

IF(RV_VFX_PLATFORM STRGREATER_EQUAL CY2024)
Expand Down Expand Up @@ -148,6 +165,9 @@ FILE(
IF(NOT RV_TARGET_DARWIN)
LIST(REMOVE_ITEM _files_to_moc "RvCommon/DisplayLink.h" "RvCommon/CGDesktopVideoDevice.h")
ENDIF()
IF(NOT RV_TARGET_LINUX)
LIST(REMOVE_ITEM _files_to_moc "RvCommon/VulkanView.h" "RvCommon/QTVulkanVideoDevice.h")
ENDIF()

FOREACH(
file_to_moc
Expand Down Expand Up @@ -450,6 +470,11 @@ IF(RV_TARGET_LINUX)
${_target}
PRIVATE X11 ${_rvcommon_link_libraries}
)

TARGET_LINK_LIBRARIES(
${_target}
PRIVATE Vulkan::Vulkan
)
ENDIF()

IF(RV_TARGET_WINDOWS)
Expand Down
70 changes: 70 additions & 0 deletions src/lib/app/RvCommon/GLView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
#include <RvCommon/InitGL.h>
#include <RvCommon/RvDocument.h>
#include <RvApp/Options.h>
#include <IPCore/ImageRenderer.h>
#include <iostream>
#include <TwkApp/Event.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <cstdlib>

#include <QtWidgets/QMenu>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>

namespace Rv
{
Expand All @@ -35,6 +39,23 @@ namespace Rv

namespace
{
static string envOrUnset(const char* name)
{
const char* value = std::getenv(name);
return value ? value : "<unset>";
}

static string formatSummary(const QSurfaceFormat& f)
{
ostringstream out;
out << "rgba " << f.redBufferSize() << " " << f.greenBufferSize() << " " << f.blueBufferSize() << " "
<< (f.alphaBufferSize() <= 0 ? 0 : f.alphaBufferSize());
out << ", depth " << f.depthBufferSize() << ", stencil " << f.stencilBufferSize();
out << ", swapInterval " << f.swapInterval();
out << ", stereo " << (f.stereo() ? "true" : "false");
out << ", major.minor " << f.majorVersion() << "." << f.minorVersion();
return out.str();
}

class SyncBufferThreadData
{
Expand Down Expand Up @@ -229,6 +250,11 @@ namespace Rv

fmt.setSwapInterval(vsync ? 1 : 0);

#ifdef PLATFORM_LINUX
if (ImageRenderer::reportGL())
cout << "INFO: GLView requested QSurfaceFormat: " << formatSummary(fmt) << endl;
#endif

return fmt;
}

Expand Down Expand Up @@ -264,6 +290,50 @@ namespace Rv
// NOTE_QT6: QGLFormat is deprecated. Using QSurfaceFormat now.
QSurfaceFormat f = context()->format();

#ifdef PLATFORM_LINUX
static bool baselineLogged = false;
if (ImageRenderer::reportGL() && !baselineLogged)
{
baselineLogged = true;

QScreen* screen = this->screen();
if (!screen)
screen = QGuiApplication::primaryScreen();

const GLubyte* glVendor = glGetString(GL_VENDOR);
const GLubyte* glRenderer = glGetString(GL_RENDERER);
const GLubyte* glVersion = glGetString(GL_VERSION);
const GLubyte* glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);
const QSurfaceFormat widgetFormat = format();

cout << "INFO: GLView runtime baseline begin" << endl;
cout << "INFO: Qt platform name: " << QGuiApplication::platformName().toStdString() << endl;
cout << "INFO: Qt version: " << qVersion() << endl;
cout << "INFO: Constructor-requested color bits (GLView args): rgba " << m_red << " " << m_green << " " << m_blue << " "
<< m_alpha << endl;
cout << "INFO: QOpenGLWidget::format() (post-negotiation): " << formatSummary(widgetFormat) << endl;
cout << "INFO: Actual QOpenGLContext format: " << formatSummary(f) << endl;
if (screen)
{
cout << "INFO: Screen name: " << screen->name().toStdString() << ", depth: " << screen->depth() << endl;
}
else
{
cout << "INFO: Screen name: <unknown>, depth: <unknown>" << endl;
}

cout << "INFO: GL vendor: " << (glVendor ? reinterpret_cast<const char*>(glVendor) : "<unknown>") << endl;
cout << "INFO: GL renderer: " << (glRenderer ? reinterpret_cast<const char*>(glRenderer) : "<unknown>") << endl;
cout << "INFO: GL version: " << (glVersion ? reinterpret_cast<const char*>(glVersion) : "<unknown>") << endl;
cout << "INFO: GLSL version: " << (glslVersion ? reinterpret_cast<const char*>(glslVersion) : "<unknown>") << endl;
cout << "INFO: Linux display env: XDG_SESSION_TYPE=" << envOrUnset("XDG_SESSION_TYPE")
<< ", WAYLAND_DISPLAY=" << envOrUnset("WAYLAND_DISPLAY") << ", DISPLAY=" << envOrUnset("DISPLAY")
<< ", XDG_CURRENT_DESKTOP=" << envOrUnset("XDG_CURRENT_DESKTOP")
<< ", DESKTOP_SESSION=" << envOrUnset("DESKTOP_SESSION") << endl;
cout << "INFO: GLView runtime baseline end" << endl;
}
#endif

#ifndef PLATFORM_DARWIN
//
// Doesn't work on OS X
Expand Down
Loading
Loading