Add Vulkan backend placeholder with dynamic fallback

This commit is contained in:
2026-07-13 10:19:29 +08:00
parent 76a98e5a93
commit 38c5afb13d
9 changed files with 280 additions and 2 deletions
+17 -1
View File
@@ -117,6 +117,22 @@ if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
add_library(oakvulkan SHARED
render/vulkan/vulkanbackend_c.cpp
)
target_include_directories(oakvulkan PRIVATE
${CMAKE_SOURCE_DIR}/app
)
target_link_libraries(oakvulkan PRIVATE Qt${QT_VERSION_MAJOR}::Core)
target_compile_definitions(oakvulkan PRIVATE ${OLIVE_DEFINITIONS})
target_compile_options(oakvulkan PRIVATE ${OLIVE_COMPILE_OPTIONS})
set_target_properties(oakvulkan PROPERTIES
OUTPUT_NAME oakvulkan
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
endif()
add_library(oakgl-cabi-check OBJECT
@@ -142,7 +158,7 @@ target_include_directories(olive-editor PUBLIC pluginSupport)
target_link_libraries(olive-editor PUBLIC OfxHost)
add_dependencies(olive-editor oakgl-cabi-check)
if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
add_dependencies(olive-editor oakgl)
add_dependencies(olive-editor oakgl oakvulkan)
endif()
# Add render worker process (olive-render-worker).
+55
View File
@@ -86,11 +86,27 @@ bool DynamicRenderer::Load()
}
handle_ = create_(this->parent());
if (!handle_) {
library_.unload();
return false;
}
if (is_available_ && !is_available_(handle_)) {
qWarning() << "Render backend is not available" << backend_
<< library_.fileName();
if (backend_ == QStringLiteral("vulkan")) {
return FallbackToOpenGL();
}
destroy_(handle_);
handle_ = nullptr;
library_.unload();
return false;
}
return handle_ != nullptr;
}
bool DynamicRenderer::ResolveFunctions()
{
ResetFunctions();
#define RESOLVE(member, type, symbol) \
member = reinterpret_cast<type>(library_.resolve(symbol)); \
if (!member) return false
@@ -126,9 +142,48 @@ bool DynamicRenderer::ResolveFunctions()
RESOLVE(opengl_context_, OakBackendOpenGLContextFn,
"oak_renderer_opengl_context");
#undef RESOLVE
is_available_ = reinterpret_cast<OakBackendIsAvailableFn>(
library_.resolve("oak_renderer_is_available"));
return true;
}
bool DynamicRenderer::FallbackToOpenGL()
{
if (handle_ && destroy_) {
destroy_(handle_);
handle_ = nullptr;
}
if (library_.isLoaded()) {
library_.unload();
}
ResetFunctions();
backend_ = QStringLiteral("opengl");
return Load();
}
void DynamicRenderer::ResetFunctions()
{
create_ = nullptr;
destroy_ = nullptr;
is_available_ = nullptr;
init_ = nullptr;
init_with_context_ = nullptr;
post_init_ = nullptr;
post_destroy_ = nullptr;
destroy_internal_ = nullptr;
clear_destination_ = nullptr;
create_native_texture_ = nullptr;
destroy_native_texture_ = nullptr;
create_native_shader_ = nullptr;
destroy_native_shader_ = nullptr;
upload_to_texture_ = nullptr;
download_from_texture_ = nullptr;
flush_ = nullptr;
get_pixel_from_texture_ = nullptr;
blit_ = nullptr;
opengl_context_ = nullptr;
}
bool DynamicRenderer::Init()
{
return Load() && init_(handle_);
+3
View File
@@ -52,6 +52,8 @@ protected:
private:
bool ResolveFunctions();
bool FallbackToOpenGL();
void ResetFunctions();
QString LibraryFilename() const;
QString backend_;
@@ -60,6 +62,7 @@ private:
OakBackendCreateFn create_ = nullptr;
OakBackendDestroyFn destroy_ = nullptr;
OakBackendIsAvailableFn is_available_ = nullptr;
OakBackendInitFn init_ = nullptr;
OakBackendInitWithContextFn init_with_context_ = nullptr;
OakBackendPostInitFn post_init_ = nullptr;
+1
View File
@@ -17,6 +17,7 @@ typedef void *OakRenderBackendHandle;
typedef OakRenderBackendHandle (*OakBackendCreateFn)(void *parent);
typedef void (*OakBackendDestroyFn)(OakRenderBackendHandle handle);
typedef bool (*OakBackendIsAvailableFn)(OakRenderBackendHandle handle);
typedef bool (*OakBackendInitFn)(OakRenderBackendHandle handle);
typedef void (*OakBackendInitWithContextFn)(OakRenderBackendHandle handle,
void *context);
+6
View File
@@ -43,6 +43,12 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handl
delete Renderer(handle);
}
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
OakRenderBackendHandle handle)
{
return handle != nullptr;
}
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
{
return Renderer(handle)->Init();
+182
View File
@@ -0,0 +1,182 @@
#include "render/backend/renderbackend_c.h"
#include <QDebug>
#include <QVariant>
namespace {
struct VulkanBackend {
bool warned = false;
};
VulkanBackend *Backend(OakRenderBackendHandle handle)
{
return static_cast<VulkanBackend *>(handle);
}
void WarnUnavailable(OakRenderBackendHandle handle, const char *function)
{
auto *backend = Backend(handle);
if (!backend || backend->warned) {
return;
}
backend->warned = true;
qWarning() << "Vulkan render backend is a C ABI placeholder; function"
<< function << "is not implemented yet";
}
} // namespace
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle oak_renderer_create(void *parent)
{
Q_UNUSED(parent)
return new VulkanBackend();
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handle)
{
delete Backend(handle);
}
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
return false;
}
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
{
WarnUnavailable(handle, "init");
return false;
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
OakRenderBackendHandle handle, void *context)
{
Q_UNUSED(context)
WarnUnavailable(handle, "init_with_context");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_init(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_destroy(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_internal(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
OakRenderBackendHandle handle, void *texture, double r, double g, double b,
double a)
{
Q_UNUSED(texture)
Q_UNUSED(r)
Q_UNUSED(g)
Q_UNUSED(b)
Q_UNUSED(a)
WarnUnavailable(handle, "clear_destination");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_texture(
OakRenderBackendHandle handle, int width, int height, int depth, int format,
int channel_count, const void *data, int linesize, void *out_variant)
{
Q_UNUSED(width)
Q_UNUSED(height)
Q_UNUSED(depth)
Q_UNUSED(format)
Q_UNUSED(channel_count)
Q_UNUSED(data)
Q_UNUSED(linesize)
static_cast<QVariant *>(out_variant)->clear();
WarnUnavailable(handle, "create_native_texture");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_texture(
OakRenderBackendHandle handle, const void *variant)
{
Q_UNUSED(variant)
WarnUnavailable(handle, "destroy_native_texture");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
OakRenderBackendHandle handle, const void *shader_code, void *out_variant)
{
Q_UNUSED(shader_code)
static_cast<QVariant *>(out_variant)->clear();
WarnUnavailable(handle, "create_native_shader");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_shader(
OakRenderBackendHandle handle, const void *variant)
{
Q_UNUSED(variant)
WarnUnavailable(handle, "destroy_native_shader");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
const void *data, int linesize)
{
Q_UNUSED(variant)
Q_UNUSED(video_params)
Q_UNUSED(data)
Q_UNUSED(linesize)
WarnUnavailable(handle, "upload_to_texture");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
void *data, int linesize)
{
Q_UNUSED(variant)
Q_UNUSED(video_params)
Q_UNUSED(data)
Q_UNUSED(linesize)
WarnUnavailable(handle, "download_from_texture");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_flush(OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
OakRenderBackendHandle handle, void *texture, const void *point,
void *out_color)
{
Q_UNUSED(texture)
Q_UNUSED(point)
Q_UNUSED(out_color)
WarnUnavailable(handle, "get_pixel_from_texture");
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
OakRenderBackendHandle handle, const void *shader, void *job,
void *destination, const void *destination_params, bool clear_destination)
{
Q_UNUSED(shader)
Q_UNUSED(job)
Q_UNUSED(destination)
Q_UNUSED(destination_params)
Q_UNUSED(clear_destination)
WarnUnavailable(handle, "blit");
}
OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
return nullptr;
}