feat(render): dynamic OpenGL/Vulkan backend split and backend-neutral viewer

- Extract libolive-rendercore static library to minimize backend link boundary.
- Add DynamicRenderer adapter with C ABI (oakgl/oakvulkan shared libs).
- Make OAK_ENABLE_DYNAMIC_RENDER_BACKEND default ON with OpenGL fallback.
- Implement VulkanRenderer prototype (textures, shaders, UBO blit, readback).
- Add backend-neutral viewer readback path (offscreen -> QImage -> QPainter).
- Refactor PluginRenderer to be renderer-agnostic; OFX plugins fall back to CPU
  path on non-OpenGL backends while preserving OpenGL render path.
- Add Renderer::AttachOutputTexture/DetachOutputTexture and C ABI forwards.
- Update docs/zh/render-backend-dynamic-plan.md for Phase 3/4/5.
This commit is contained in:
2026-07-13 10:19:29 +08:00
parent 5fc8232671
commit 225f8505c2
42 changed files with 3042 additions and 495 deletions
+2
View File
@@ -30,6 +30,7 @@ set(OLIVE_SOURCES
render/backend/dynamicrenderer.h
render/backend/renderbackend_c.h
render/cancelatom.h
render/colormanagement.cpp
render/colorprocessor.cpp
render/colorprocessor.h
render/colorprocessorcache.h
@@ -39,6 +40,7 @@ set(OLIVE_SOURCES
render/framehashcache.h
render/framemanager.cpp
render/framemanager.h
render/interlacetexture.cpp
render/loopmode.h
render/managedcolor.cpp
render/managedcolor.h
+28
View File
@@ -45,6 +45,8 @@ QString DynamicRenderer::LibraryFilename() const
const QStringList candidates = {
app_dir.filePath(filename),
app_dir.filePath(QDir(QStringLiteral("render_backends")).filePath(filename)),
app_dir.filePath(QDir(QStringLiteral("../lib")).filePath(filename)),
app_dir.filePath(QDir(QStringLiteral("../../lib")).filePath(filename)),
app_dir.filePath(QDir(QStringLiteral("../app")).filePath(filename)),
app_dir.filePath(QDir(QStringLiteral("../../app")).filePath(filename))
};
@@ -139,6 +141,10 @@ bool DynamicRenderer::ResolveFunctions()
RESOLVE(get_pixel_from_texture_, OakBackendGetPixelFromTextureFn,
"oak_renderer_get_pixel_from_texture");
RESOLVE(blit_, OakBackendBlitFn, "oak_renderer_blit");
RESOLVE(attach_output_texture_, OakBackendAttachOutputTextureFn,
"oak_renderer_attach_output_texture");
RESOLVE(detach_output_texture_, OakBackendDetachOutputTextureFn,
"oak_renderer_detach_output_texture");
RESOLVE(opengl_context_, OakBackendOpenGLContextFn,
"oak_renderer_opengl_context");
#undef RESOLVE
@@ -184,6 +190,8 @@ void DynamicRenderer::ResetFunctions()
flush_ = nullptr;
get_pixel_from_texture_ = nullptr;
blit_ = nullptr;
attach_output_texture_ = nullptr;
detach_output_texture_ = nullptr;
opengl_context_ = nullptr;
}
@@ -271,6 +279,11 @@ QOpenGLContext *DynamicRenderer::OpenGLContext() const
: nullptr;
}
bool DynamicRenderer::IsOpenGL() const
{
return backend_ == QStringLiteral("opengl");
}
void DynamicRenderer::Blit(QVariant shader, AcceleratedJob &job,
Texture *destination, VideoParams destination_params,
bool clear_destination)
@@ -301,4 +314,19 @@ void DynamicRenderer::DestroyInternal()
}
}
void DynamicRenderer::AttachOutputTexture(Texture *texture)
{
if (attach_output_texture_ && texture) {
QVariant id = texture->id();
attach_output_texture_(handle_, &id);
}
}
void DynamicRenderer::DetachOutputTexture()
{
if (detach_output_texture_) {
detach_output_texture_(handle_);
}
}
}
+8
View File
@@ -44,6 +44,12 @@ public:
const QPointF &pt) override;
virtual QOpenGLContext *OpenGLContext() const override;
virtual bool IsOpenGL() const override;
virtual void AttachOutputTexture(Texture *texture) override;
virtual void DetachOutputTexture() override;
protected:
virtual void Blit(QVariant shader, AcceleratedJob &job,
Texture *destination, VideoParams destination_params,
@@ -84,6 +90,8 @@ private:
OakBackendFlushFn flush_ = nullptr;
OakBackendGetPixelFromTextureFn get_pixel_from_texture_ = nullptr;
OakBackendBlitFn blit_ = nullptr;
OakBackendAttachOutputTextureFn attach_output_texture_ = nullptr;
OakBackendDetachOutputTextureFn detach_output_texture_ = nullptr;
OakBackendOpenGLContextFn opengl_context_ = nullptr;
};
+6 -1
View File
@@ -27,7 +27,9 @@ enum OakRenderBackendCapability {
OAK_RENDER_BACKEND_CAP_SHADERS = 1ULL << 1,
OAK_RENDER_BACKEND_CAP_BLIT = 1ULL << 2,
OAK_RENDER_BACKEND_CAP_READBACK = 1ULL << 3,
OAK_RENDER_BACKEND_CAP_VIEWER_CONTEXT = 1ULL << 4
OAK_RENDER_BACKEND_CAP_VIEWER_CONTEXT = 1ULL << 4,
OAK_RENDER_BACKEND_CAP_INSTANCE = 1ULL << 5,
OAK_RENDER_BACKEND_CAP_DEVICE = 1ULL << 6
};
struct OakRenderBackendInfo {
@@ -81,6 +83,9 @@ typedef void (*OakBackendBlitFn)(OakRenderBackendHandle handle,
void *destination,
const void *destination_params,
bool clear_destination);
typedef void (*OakBackendAttachOutputTextureFn)(OakRenderBackendHandle handle,
const void *texture_id);
typedef void (*OakBackendDetachOutputTextureFn)(OakRenderBackendHandle handle);
typedef void *(*OakBackendOpenGLContextFn)(OakRenderBackendHandle handle);
#ifdef __cplusplus
+225
View File
@@ -0,0 +1,225 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "renderer.h"
#include "common/filefunctions.h"
#include "node/node.h"
#include "render/colorprocessor.h"
#include "render/job/colortransformjob.h"
#include "render/job/shaderjob.h"
namespace olive
{
bool Renderer::GetColorContext(const ColorTransformJob &color_job,
Renderer::ColorContext *ctx)
{
QMutexLocker locker(&color_cache_mutex_);
ColorContext &color_ctx = *ctx;
QString proc_id = color_job.id();
if (color_cache_.contains(proc_id)) {
color_ctx = color_cache_.value(proc_id);
return true;
} else {
// Create shader description
QString ocio_func_name;
if (color_job.GetFunctionName().isEmpty()) {
ocio_func_name = "OCIODisplay";
} else {
ocio_func_name = color_job.GetFunctionName();
}
auto shader_desc = OCIO::GpuShaderDesc::CreateShaderDesc();
shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0);
shader_desc->setFunctionName(ocio_func_name.toUtf8());
shader_desc->setResourcePrefix("ocio_");
// Generate shader
color_job.GetColorProcessor()
->GetProcessor()
->getDefaultGPUProcessor()
->extractGpuShaderInfo(shader_desc);
ShaderCode code;
if (const Node *shader_src = color_job.CustomShaderSource()) {
// Use shader code from associated node
code = shader_src->GetShaderCode(
{ color_job.CustomShaderID(), shader_desc->getShaderText() });
} else {
// Generate shader code using OCIO stub and our auto-generated name
code = FileFunctions::ReadFileAsString(
QStringLiteral(":/shaders/colormanage.frag"));
code.set_frag_code(
code.frag_code().arg(shader_desc->getShaderText()));
}
// Try to compile shader
color_ctx.compiled_shader = CreateNativeShader(code);
if (color_ctx.compiled_shader.isNull()) {
return false;
}
color_ctx.lut3d_textures.resize(shader_desc->getNum3DTextures());
for (unsigned int i = 0; i < shader_desc->getNum3DTextures(); i++) {
const char *tex_name = nullptr;
const char *sampler_name = nullptr;
unsigned int edge_len = 0;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
shader_desc->get3DTexture(i, tex_name, sampler_name, edge_len,
interpolation);
if (!tex_name || !*tex_name || !sampler_name || !*sampler_name ||
!edge_len) {
qCritical() << "3D LUT texture data is corrupted";
return false;
}
const float *values = nullptr;
shader_desc->get3DTextureValues(i, values);
if (!values) {
qCritical() << "3D LUT texture values are missing";
return false;
}
// Allocate 3D LUT
color_ctx.lut3d_textures[i].texture = CreateTexture(
VideoParams(edge_len, edge_len, edge_len, PixelFormat::F32,
VideoParams::kRGBChannelCount),
values);
color_ctx.lut3d_textures[i].name = sampler_name;
color_ctx.lut3d_textures[i].interpolation =
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
Texture::kLinear;
}
color_ctx.lut1d_textures.resize(shader_desc->getNumTextures());
for (unsigned int i = 0; i < shader_desc->getNumTextures(); i++) {
const char *tex_name = nullptr;
const char *sampler_name = nullptr;
unsigned int width = 0, height = 0;
OCIO::GpuShaderDesc::TextureType channel =
OCIO::GpuShaderDesc::TEXTURE_RGB_CHANNEL;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
#if OCIO_VERSION_MAJOR > 2 || (OCIO_VERSION_MAJOR == 2 && OCIO_VERSION_MINOR >= 3)
OCIO::GpuShaderDesc::TextureDimensions dimensions =
OCIO::GpuShaderDesc::TEXTURE_2D;
shader_desc->getTexture(i, tex_name, sampler_name, width, height,
channel, dimensions, interpolation);
#else
shader_desc->getTexture(i, tex_name, sampler_name, width, height,
channel, interpolation);
#endif
if (!tex_name || !*tex_name || !sampler_name || !*sampler_name ||
!width) {
qCritical() << "1D LUT texture data is corrupted";
return false;
}
const float *values = nullptr;
shader_desc->getTextureValues(i, values);
if (!values) {
qCritical() << "1D LUT texture values are missing";
return false;
}
// Allocate 1D LUT
color_ctx.lut1d_textures[i].texture = CreateTexture(
VideoParams(width, height, PixelFormat::F32,
(channel ==
OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ?
1 :
VideoParams::kRGBChannelCount),
values);
color_ctx.lut1d_textures[i].name = sampler_name;
color_ctx.lut1d_textures[i].interpolation =
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
Texture::kLinear;
}
color_cache_.insert(proc_id, color_ctx);
return true;
}
}
void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
Texture *destination, const VideoParams &params)
{
ColorContext color_ctx;
if (!GetColorContext(color_job, &color_ctx)) {
ShaderJob fallback_job;
fallback_job.Insert(QStringLiteral("ove_maintex"),
color_job.GetInputTexture());
fallback_job.Insert(
QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
if (destination) {
BlitToTexture(GetDefaultShader(), fallback_job, destination,
color_job.IsClearDestinationEnabled());
} else {
Blit(GetDefaultShader(), fallback_job, params,
color_job.IsClearDestinationEnabled());
}
return;
}
ShaderJob job;
job.Insert(QStringLiteral("ove_maintex"), color_job.GetInputTexture());
job.Insert(QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
job.Insert(QStringLiteral("ove_cropmatrix"),
NodeValue(NodeValue::kMatrix,
color_job.GetCropMatrix().inverted()));
job.Insert(QStringLiteral("ove_maintex_alpha"),
NodeValue(NodeValue::kInt,
int(color_job.GetInputAlphaAssociation())));
job.Insert(QStringLiteral("ove_force_opaque"),
NodeValue(NodeValue::kBoolean, color_job.GetForceOpaque()));
job.Insert(color_job.GetValues());
foreach (const ColorContext::LUT &l, color_ctx.lut3d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture,
QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
foreach (const ColorContext::LUT &l, color_ctx.lut1d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture,
QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
if (destination) {
BlitToTexture(color_ctx.compiled_shader, job, destination,
color_job.IsClearDestinationEnabled());
} else {
Blit(color_ctx.compiled_shader, job, params,
color_job.IsClearDestinationEnabled());
}
}
}
+59
View File
@@ -0,0 +1,59 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "renderer.h"
#include "common/filefunctions.h"
#include "node/value.h"
#include "render/job/shaderjob.h"
namespace olive
{
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom,
const VideoParams &params)
{
color_cache_mutex_.lock();
if (interlace_texture_.isNull()) {
interlace_texture_ =
CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(
QStringLiteral(":/shaders/interlace.frag"))));
}
color_cache_mutex_.unlock();
ShaderJob job;
job.Insert(QStringLiteral("top_tex_in"),
NodeValue(NodeValue::kTexture, QVariant::fromValue(top)));
job.Insert(QStringLiteral("bottom_tex_in"),
NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom)));
job.Insert(QStringLiteral("resolution_in"),
NodeValue(NodeValue::kVec2,
QVector2D(params.effective_width(),
params.effective_height())));
TexturePtr output = CreateTexture(params);
BlitToTexture(interlace_texture_, job, output.get());
return output;
}
}
+14
View File
@@ -19,6 +19,8 @@ public:
using olive::OpenGLRenderer::CreateNativeTexture;
using olive::OpenGLRenderer::DestroyInternal;
using olive::OpenGLRenderer::DestroyNativeTexture;
using olive::OpenGLRenderer::AttachTextureAsDestination;
using olive::OpenGLRenderer::DetachTextureAsDestination;
};
BackendOpenGLRenderer *Renderer(OakRenderBackendHandle handle)
@@ -176,3 +178,15 @@ OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
{
return Renderer(handle)->context();
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
OakRenderBackendHandle handle, const void *texture_id)
{
Renderer(handle)->AttachTextureAsDestination(VariantRef(texture_id));
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_detach_output_texture(
OakRenderBackendHandle handle)
{
Renderer(handle)->DetachTextureAsDestination();
}
+14
View File
@@ -27,7 +27,9 @@
#include <QOpenGLExtraFunctions>
#include <QRegularExpression>
#include "common/filefunctions.h"
#include "config/config.h"
#include "render/job/shaderjob.h"
namespace olive
{
@@ -406,6 +408,18 @@ void OpenGLRenderer::Flush()
}
}
void OpenGLRenderer::AttachOutputTexture(olive::Texture *texture)
{
if (texture) {
AttachTextureAsDestination(texture->id());
}
}
void OpenGLRenderer::DetachOutputTexture()
{
DetachTextureAsDestination();
}
Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
{
AttachTextureAsDestination(texture->id());
+9
View File
@@ -82,6 +82,15 @@ public:
return context();
}
virtual bool IsOpenGL() const override
{
return true;
}
virtual void AttachOutputTexture(olive::Texture *texture) override;
virtual void DetachOutputTexture() override;
bool EnsureContextCurrent(const char *caller);
protected:
+9 -7
View File
@@ -1432,9 +1432,10 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
auto *olive_instance =
dynamic_cast<olive::plugin::OlivePluginInstance *>(instance);
const bool use_opengl =
supports_opengl && destination && destination->renderer() &&
destination->id().isValid();
supports_opengl && renderer_ && renderer_->IsOpenGL() && destination &&
destination->renderer() == renderer_ && destination->id().isValid();
if (olive_instance) {
olive_instance->setOpenGLEnabled(use_opengl);
olive_instance->setVideoParam(destination_params);
// Ensure all clip instances inherit the project's params so that
// getAspectRatio/getFrameRate etc. return valid values before
@@ -1798,7 +1799,7 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
renderScale, true, interactive);
return;
}
AVFramePtr converted = ConvertFrameIfNeeded(frame_ptr, destination_params, this);
AVFramePtr converted = ConvertFrameIfNeeded(frame_ptr, destination_params, renderer_);
const AVPixelFormat expected_fmt =
GetDestinationAVPixelFormat(destination_params);
destination->handleFrame(converted);
@@ -1837,15 +1838,16 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
// Purpose: Attach output texture for OFX GL rendering.
void olive::plugin::PluginRenderer::AttachOutputTexture(olive::TexturePtr texture)
{
if (!texture) {
return;
if (renderer_) {
renderer_->AttachOutputTexture(texture.get());
}
AttachTextureAsDestination(texture->id());
}
// 作用:解除 OFX 的 GL 输出绑定。
// Purpose: Detach OFX GL output binding.
void olive::plugin::PluginRenderer::DetachOutputTexture()
{
DetachTextureAsDestination();
if (renderer_) {
renderer_->DetachOutputTexture();
}
}
+19 -11
View File
@@ -23,17 +23,12 @@
#ifndef PLUGINRENDERER_H
#define PLUGINRENDERER_H
#include <QOpenGLExtraFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLShader>
#include <QOpenGLVertexArrayObject>
#include <QThread>
#include <QOffscreenSurface>
#include <QObject>
#include "render/renderer.h"
#include "render/job/pluginjob.h"
#include "render/opengl/openglrenderer.h"
namespace olive
{
namespace plugin{
@@ -44,11 +39,22 @@ int BytesToPixels(int byte_linesize, const olive::VideoParams &params);
}
// 作用:OFX 插件渲染器,负责 CPU/GL 路径下的插件调用和纹理桥接。
// Purpose: OFX plugin renderer that drives CPU/GL render paths and texture bridging.
class PluginRenderer : public olive::OpenGLRenderer{
//
// 不再继承 OpenGLRenderer,而是持有一个通用的 Renderer 指针。这样当主渲染器
// 是 Vulkan 或动态加载的后端时,插件仍可通过 CPU readback/upload 路径工作;
// 仅当底层渲染器真正支持 OpenGL 时才走 OFX OpenGL 渲染路径。
class PluginRenderer : public QObject {
Q_OBJECT
public:
PluginRenderer(QObject *parent=nullptr):OpenGLRenderer(parent){};
virtual ~PluginRenderer() override{};
explicit PluginRenderer(olive::Renderer *renderer, QObject *parent = nullptr)
: QObject(parent), renderer_(renderer) {}
virtual ~PluginRenderer() override {}
olive::Renderer *renderer() const
{
return renderer_;
}
// 作用:将目标纹理绑定为插件输出。
// Purpose: Attach destination texture as OFX output.
void AttachOutputTexture(olive::TexturePtr texture);
@@ -62,6 +68,8 @@ public:
olive::VideoParams destination_params,
bool clear_destination, bool interactive);
private:
olive::Renderer *renderer_;
};
}
}
+1 -221
View File
@@ -116,34 +116,6 @@ void Renderer::DestroyTexture(Texture *texture)
}
}
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom,
const VideoParams &params)
{
color_cache_mutex_.lock();
if (interlace_texture_.isNull()) {
interlace_texture_ =
CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(
QStringLiteral(":/shaders/interlace.frag"))));
}
color_cache_mutex_.unlock();
ShaderJob job;
job.Insert(QStringLiteral("top_tex_in"),
NodeValue(NodeValue::kTexture, QVariant::fromValue(top)));
job.Insert(QStringLiteral("bottom_tex_in"),
NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom)));
job.Insert(QStringLiteral("resolution_in"),
NodeValue(NodeValue::kVec2,
QVector2D(params.effective_width(),
params.effective_height())));
TexturePtr output = CreateTexture(params);
BlitToTexture(interlace_texture_, job, output.get());
return output;
}
QVariant Renderer::GetDefaultShader()
{
QMutexLocker locker(&color_cache_mutex_);
@@ -191,142 +163,6 @@ TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v,
return std::make_shared<Texture>(this, v, params, lifetime_);
}
bool Renderer::GetColorContext(const ColorTransformJob &color_job,
Renderer::ColorContext *ctx)
{
QMutexLocker locker(&color_cache_mutex_);
ColorContext &color_ctx = *ctx;
QString proc_id = color_job.id();
if (color_cache_.contains(proc_id)) {
color_ctx = color_cache_.value(proc_id);
return true;
} else {
// Create shader description
QString ocio_func_name;
if (color_job.GetFunctionName().isEmpty()) {
ocio_func_name = "OCIODisplay";
} else {
ocio_func_name = color_job.GetFunctionName();
}
auto shader_desc = OCIO::GpuShaderDesc::CreateShaderDesc();
shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0);
shader_desc->setFunctionName(ocio_func_name.toUtf8());
shader_desc->setResourcePrefix("ocio_");
// Generate shader
color_job.GetColorProcessor()
->GetProcessor()
->getDefaultGPUProcessor()
->extractGpuShaderInfo(shader_desc);
ShaderCode code;
if (const Node *shader_src = color_job.CustomShaderSource()) {
// Use shader code from associated node
code = shader_src->GetShaderCode(
{ color_job.CustomShaderID(), shader_desc->getShaderText() });
} else {
// Generate shader code using OCIO stub and our auto-generated name
code = FileFunctions::ReadFileAsString(
QStringLiteral(":shaders/colormanage.frag"));
code.set_frag_code(
code.frag_code().arg(shader_desc->getShaderText()));
}
// Try to compile shader
color_ctx.compiled_shader = CreateNativeShader(code);
if (color_ctx.compiled_shader.isNull()) {
return false;
}
color_ctx.lut3d_textures.resize(shader_desc->getNum3DTextures());
for (unsigned int i = 0; i < shader_desc->getNum3DTextures(); i++) {
const char *tex_name = nullptr;
const char *sampler_name = nullptr;
unsigned int edge_len = 0;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
shader_desc->get3DTexture(i, tex_name, sampler_name, edge_len,
interpolation);
if (!tex_name || !*tex_name || !sampler_name || !*sampler_name ||
!edge_len) {
qCritical() << "3D LUT texture data is corrupted";
return false;
}
const float *values = nullptr;
shader_desc->get3DTextureValues(i, values);
if (!values) {
qCritical() << "3D LUT texture values are missing";
return false;
}
// Allocate 3D LUT
color_ctx.lut3d_textures[i].texture = CreateTexture(
VideoParams(edge_len, edge_len, edge_len, PixelFormat::F32,
VideoParams::kRGBChannelCount),
values);
color_ctx.lut3d_textures[i].name = sampler_name;
color_ctx.lut3d_textures[i].interpolation =
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
Texture::kLinear;
}
color_ctx.lut1d_textures.resize(shader_desc->getNumTextures());
for (unsigned int i = 0; i < shader_desc->getNumTextures(); i++) {
const char *tex_name = nullptr;
const char *sampler_name = nullptr;
unsigned int width = 0, height = 0;
OCIO::GpuShaderDesc::TextureType channel =
OCIO::GpuShaderDesc::TEXTURE_RGB_CHANNEL;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
#if OCIO_VERSION_MAJOR > 2 || (OCIO_VERSION_MAJOR == 2 && OCIO_VERSION_MINOR >= 3)
OCIO::GpuShaderDesc::TextureDimensions dimensions =
OCIO::GpuShaderDesc::TEXTURE_2D;
shader_desc->getTexture(i, tex_name, sampler_name, width, height,
channel, dimensions, interpolation);
#else
shader_desc->getTexture(i, tex_name, sampler_name, width, height,
channel, interpolation);
#endif
if (!tex_name || !*tex_name || !sampler_name || !*sampler_name ||
!width) {
qCritical() << "1D LUT texture data is corrupted";
return false;
}
const float *values = nullptr;
shader_desc->getTextureValues(i, values);
if (!values) {
qCritical() << "1D LUT texture values are missing";
return false;
}
// Allocate 1D LUT
color_ctx.lut1d_textures[i].texture = CreateTexture(
VideoParams(width, height, PixelFormat::F32,
(channel ==
OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ?
1 :
VideoParams::kRGBChannelCount),
values);
color_ctx.lut1d_textures[i].name = sampler_name;
color_ctx.lut1d_textures[i].interpolation =
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
Texture::kLinear;
}
color_cache_.insert(proc_id, color_ctx);
return true;
}
}
void Renderer::ClearOldTextures()
{
QMutexLocker locker(&texture_cache_lock_);
@@ -342,60 +178,4 @@ void Renderer::ClearOldTextures()
}
}
void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
Texture *destination, const VideoParams &params)
{
ColorContext color_ctx;
if (!GetColorContext(color_job, &color_ctx)) {
ShaderJob fallback_job;
fallback_job.Insert(QStringLiteral("ove_maintex"),
color_job.GetInputTexture());
fallback_job.Insert(
QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
if (destination) {
BlitToTexture(GetDefaultShader(), fallback_job, destination,
color_job.IsClearDestinationEnabled());
} else {
Blit(GetDefaultShader(), fallback_job, params,
color_job.IsClearDestinationEnabled());
}
return;
}
ShaderJob job;
job.Insert(QStringLiteral("ove_maintex"), color_job.GetInputTexture());
job.Insert(QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
job.Insert(QStringLiteral("ove_cropmatrix"),
NodeValue(NodeValue::kMatrix,
color_job.GetCropMatrix().inverted()));
job.Insert(QStringLiteral("ove_maintex_alpha"),
NodeValue(NodeValue::kInt,
int(color_job.GetInputAlphaAssociation())));
job.Insert(QStringLiteral("ove_force_opaque"),
NodeValue(NodeValue::kBoolean, color_job.GetForceOpaque()));
job.Insert(color_job.GetValues());
foreach (const ColorContext::LUT &l, color_ctx.lut3d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture,
QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
foreach (const ColorContext::LUT &l, color_ctx.lut1d_textures) {
job.Insert(l.name, NodeValue(NodeValue::kTexture,
QVariant::fromValue(l.texture)));
job.SetInterpolation(l.name, l.interpolation);
}
if (destination) {
BlitToTexture(color_ctx.compiled_shader, job, destination,
color_job.IsClearDestinationEnabled());
} else {
Blit(color_ctx.compiled_shader, job, params,
color_job.IsClearDestinationEnabled());
}
}
}
} // namespace olive
+31 -4
View File
@@ -29,12 +29,15 @@
#include <memory>
#include "common/define.h"
#include "node/node.h"
#include "render/colorprocessor.h"
#include "render/job/colortransformjob.h"
#include "render/shadercode.h"
#include "render/videoparams.h"
#include "texture.h"
#include "job/pluginjob.h"
// Forward declarations to keep the render core header lightweight
namespace olive {
class ColorTransformJob;
class Node;
}
namespace olive
{
@@ -117,6 +120,30 @@ public:
return lifetime_;
}
virtual bool IsOpenGL() const
{
return false;
}
/**
* @brief Attach a texture as the current output destination for OFX plugin
* OpenGL rendering.
*
* Default implementation is a no-op. OpenGL-based renderers override this
* to bind the texture as a framebuffer render target.
*/
virtual void AttachOutputTexture(olive::Texture *texture)
{
(void)texture;
}
/**
* @brief Detach the current OFX plugin OpenGL output texture.
*
* Default implementation is a no-op.
*/
virtual void DetachOutputTexture() {}
protected:
virtual void Blit(QVariant shader, olive::AcceleratedJob& job,
olive::Texture *destination,
+18 -8
View File
@@ -78,21 +78,31 @@ QString RenderManager::BackendToString(Backend backend)
}
RenderManager::RenderManager(QObject *parent)
: backend_(kOpenGL)
, requested_backend_(BackendFromString(
: backend_(BackendFromString(
OLIVE_CONFIG("GraphicsBackend").toString()))
, requested_backend_(backend_)
, aggressive_gc_(0)
, worker_pool_(nullptr)
{
if (requested_backend_ == kVulkan) {
qWarning()
<< "Vulkan graphics backend was requested, but the current render "
"pipeline still uses OpenGL. Falling back to OpenGL renderer.";
if (backend_ == kVulkan) {
#ifndef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
qWarning() << "Vulkan backend requested but dynamic render backend is not enabled. Falling back to OpenGL.";
backend_ = kOpenGL;
#endif
}
if (backend_ == kOpenGL) {
if (backend_ == kOpenGL || backend_ == kVulkan) {
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
context_ = new DynamicRenderer(BackendToString(requested_backend_));
auto *dynamic_renderer = new DynamicRenderer(BackendToString(requested_backend_));
if (!dynamic_renderer->Load()) {
qWarning() << "Failed to load dynamic render backend" << BackendToString(requested_backend_)
<< ", falling back to OpenGL";
delete dynamic_renderer;
backend_ = kOpenGL;
context_ = new OpenGLRenderer();
} else {
context_ = dynamic_renderer;
}
#else
context_ = new OpenGLRenderer();
#endif
+3 -23
View File
@@ -33,8 +33,6 @@
#include "node/block/transition/transition.h"
#include "node/project.h"
#include "rendermanager.h"
#include "render/opengl/openglcontextprovider.h"
#include "render/opengl/openglrenderer.h"
#include "render/plugin/pluginrenderer.h"
#include "pluginSupport/OliveClip.h"
#include "pluginSupport/OliveHost.h"
@@ -711,26 +709,8 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
return destination;
}
plugin::PluginRenderer *plugin_renderer = nullptr;
{
static QThreadStorage<std::shared_ptr<plugin::PluginRenderer>>
cached_plugin_renderers;
if (!cached_plugin_renderers.hasLocalData()) {
auto *gl = dynamic_cast<OpenGLContextProvider *>(render_ctx_);
if (gl && gl->OpenGLContext()) {
auto cached_plugin_renderer =
std::make_shared<plugin::PluginRenderer>();
cached_plugin_renderer->Init(gl->OpenGLContext());
cached_plugin_renderer->PostInit();
cached_plugin_renderers.setLocalData(cached_plugin_renderer);
}
}
if (cached_plugin_renderers.hasLocalData()) {
plugin_renderer = cached_plugin_renderers.localData().get();
}
}
if (!plugin_renderer) {
plugin::PluginRenderer plugin_renderer(render_ctx_);
if (!plugin_renderer.renderer()) {
return destination;
}
@@ -782,7 +762,7 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
}
}
plugin_renderer->RenderPlugin(
plugin_renderer.RenderPlugin(
src,
*plugin_job,
destination,
+1
View File
@@ -21,6 +21,7 @@
#include "texture.h"
#include "render/job/acceleratedjob.h"
#include "renderer.h"
namespace olive
+1 -1
View File
@@ -22,7 +22,7 @@
#ifndef RENDERTEXTURE_H
#define RENDERTEXTURE_H
#include "common/ffmpegutils.h"
#include "common/avframeptr.h"
#include <atomic>
#include <memory>
+12
View File
@@ -29,6 +29,7 @@ extern "C" {
#include <QtMath>
#include "core.h"
#include "ofxImageEffect.h"
namespace olive
{
@@ -120,6 +121,17 @@ VideoParams::VideoParams(int width, int height, int depth, PixelFormat format,
set_defaults_for_footage();
}
void VideoParams::set_channel_count(const std::string &ofxComponent)
{
if (ofxComponent == kOfxImageComponentAlpha) {
channel_count_ = 1;
} else if (ofxComponent == kOfxImageComponentRGB) {
channel_count_ = kRGBChannelCount;
} else if (ofxComponent == kOfxImageComponentRGBA) {
channel_count_ = kRGBAChannelCount;
}
}
VideoParams::VideoParams(int width, int height, const rational &time_base,
PixelFormat format, int nb_channels,
const rational &pixel_aspect_ratio,
+1 -13
View File
@@ -22,7 +22,6 @@
#ifndef VIDEOPARAMS_H
#define VIDEOPARAMS_H
#include "ofxImageEffect.h"
#include <olive/core/core.h>
#include <QVector2D>
#include <QXmlStreamReader>
@@ -178,18 +177,7 @@ public:
{
channel_count_ = c;
}
void set_channel_count(std::string ofxComponent)
{
if (ofxComponent == kOfxImageComponentAlpha){
channel_count_ = 1;
}
else if (ofxComponent == kOfxImageComponentRGB){
channel_count_ = kRGBChannelCount;
}
else if(ofxComponent == kOfxImageComponentRGBA){
channel_count_ = kRGBAChannelCount;
}
}
void set_channel_count(const std::string &ofxComponent);
const rational &pixel_aspect_ratio() const
{
return pixel_aspect_ratio_;
+77 -68
View File
@@ -1,41 +1,47 @@
#include "render/backend/renderbackend_c.h"
#include <QDebug>
#include <QOpenGLContext>
#include <QPointF>
#include <QVariant>
#include "render/job/acceleratedjob.h"
#include "render/shadercode.h"
#include "render/texture.h"
#include "render/videoparams.h"
#include "render/vulkan/vulkanrenderer.h"
namespace {
struct VulkanBackend {
bool warned = false;
class BackendVulkanRenderer : public olive::VulkanRenderer {
public:
using olive::VulkanRenderer::VulkanRenderer;
using olive::VulkanRenderer::Blit;
using olive::VulkanRenderer::CreateNativeTexture;
using olive::VulkanRenderer::DestroyInternal;
using olive::VulkanRenderer::DestroyNativeTexture;
};
VulkanBackend *Backend(OakRenderBackendHandle handle)
BackendVulkanRenderer *Renderer(OakRenderBackendHandle handle)
{
return static_cast<VulkanBackend *>(handle);
return static_cast<BackendVulkanRenderer *>(handle);
}
void WarnUnavailable(OakRenderBackendHandle handle, const char *function)
const QVariant &VariantRef(const void *variant)
{
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";
return *static_cast<const QVariant *>(variant);
}
} // namespace
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle oak_renderer_create(void *parent)
{
Q_UNUSED(parent)
return new VulkanBackend();
return new BackendVulkanRenderer(static_cast<QObject *>(parent));
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handle)
{
delete Backend(handle);
delete Renderer(handle);
}
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
@@ -46,146 +52,134 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
}
out_info->abi_version = 1;
out_info->kind = OAK_RENDER_BACKEND_VULKAN;
out_info->capabilities = 0;
out_info->capabilities = OAK_RENDER_BACKEND_CAP_TEXTURES |
OAK_RENDER_BACKEND_CAP_SHADERS | OAK_RENDER_BACKEND_CAP_BLIT |
OAK_RENDER_BACKEND_CAP_READBACK;
out_info->name = "vulkan";
out_info->status = "placeholder-unavailable";
out_info->status = Renderer(handle)->IsAvailable() ? "available" : "unavailable";
return true;
}
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
return false;
auto *r = Renderer(handle);
if (!r || r->IsAvailable()) {
return r && r->IsAvailable();
}
// Try to initialize if not already available
if (r->Init()) {
r->PostInit();
}
return r->IsAvailable();
}
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
{
WarnUnavailable(handle, "init");
return false;
return Renderer(handle)->Init();
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
OakRenderBackendHandle handle, void *context)
{
Q_UNUSED(context)
WarnUnavailable(handle, "init_with_context");
Renderer(handle)->Init();
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_init(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
Renderer(handle)->PostInit();
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_destroy(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
Renderer(handle)->PostDestroy();
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_internal(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
Renderer(handle)->DestroyInternal();
}
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");
Renderer(handle)->ClearDestination(static_cast<olive::Texture *>(texture),
r, g, b, a);
}
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");
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeTexture(
width, height, depth, static_cast<olive::PixelFormat::Format>(format),
channel_count, data, linesize);
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_texture(
OakRenderBackendHandle handle, const void *variant)
{
Q_UNUSED(variant)
WarnUnavailable(handle, "destroy_native_texture");
Renderer(handle)->DestroyNativeTexture(VariantRef(variant));
}
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");
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeShader(
*static_cast<const olive::ShaderCode *>(shader_code));
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_shader(
OakRenderBackendHandle handle, const void *variant)
{
Q_UNUSED(variant)
WarnUnavailable(handle, "destroy_native_shader");
Renderer(handle)->DestroyNativeShader(VariantRef(variant));
}
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");
Renderer(handle)->UploadToTexture(
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
data, linesize);
}
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");
Renderer(handle)->DownloadFromTexture(
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
data, linesize);
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_flush(OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
Renderer(handle)->Flush();
}
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");
*static_cast<olive::Color *>(out_color) = Renderer(handle)->GetPixelFromTexture(
static_cast<olive::Texture *>(texture), *static_cast<const QPointF *>(point));
}
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");
Renderer(handle)->Blit(
VariantRef(shader), *static_cast<olive::AcceleratedJob *>(job),
static_cast<olive::Texture *>(destination),
*static_cast<const olive::VideoParams *>(destination_params),
clear_destination);
}
OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
@@ -194,3 +188,18 @@ OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
Q_UNUSED(handle)
return nullptr;
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
OakRenderBackendHandle handle, const void *texture_id)
{
Q_UNUSED(handle)
Q_UNUSED(texture_id)
// Vulkan does not support OFX OpenGL render output attachment.
}
OAK_RENDER_BACKEND_EXPORT void oak_renderer_detach_output_texture(
OakRenderBackendHandle handle)
{
Q_UNUSED(handle)
// Vulkan does not support OFX OpenGL render output attachment.
}
File diff suppressed because it is too large Load Diff
+159
View File
@@ -0,0 +1,159 @@
/***************************************************************************
Oak Video Editor
Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***************************************************************************/
#ifndef VULKANRENDERER_H
#define VULKANRENDERER_H
#include <vulkan/vulkan.h>
#include <QMutex>
#include <QObject>
#include <QVariant>
#include "render/renderer.h"
namespace olive
{
class VulkanRenderer : public Renderer {
Q_OBJECT
public:
explicit VulkanRenderer(QObject *parent = nullptr);
virtual ~VulkanRenderer() override;
virtual bool Init() override;
virtual void PostInit() override;
virtual void PostDestroy() override;
virtual void ClearDestination(olive::Texture *texture = nullptr,
double r = 0.0, double g = 0.0,
double b = 0.0, double a = 0.0) override;
virtual QVariant CreateNativeShader(olive::ShaderCode code) override;
virtual void DestroyNativeShader(QVariant shader) override;
virtual void UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize) override;
virtual void DownloadFromTexture(const QVariant &handle,
const VideoParams &params, void *data,
int linesize) override;
virtual void Flush() override;
virtual Color GetPixelFromTexture(olive::Texture *texture,
const QPointF &pt) override;
bool IsAvailable() const
{
return device_ != VK_NULL_HANDLE;
}
protected:
virtual void Blit(QVariant shader, olive::AcceleratedJob &job,
olive::Texture *destination, VideoParams destination_params,
bool clear_destination) override;
virtual QVariant CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
const void *data = nullptr,
int linesize = 0) override;
virtual void DestroyNativeTexture(QVariant texture) override;
virtual void DestroyInternal() override;
private:
struct VulkanTexture;
struct VulkanShader;
struct UniformInfo;
bool CreateInstance();
bool CreateDevice();
bool CreateCommandPool();
bool CreateDescriptorPool();
bool CreateVertexBuffer();
bool CreateLinearSampler();
bool CreateStagingBuffer(VkDeviceSize size, VkBuffer *out_buffer,
VkDeviceMemory *out_memory);
void DestroyStagingBuffer(VkBuffer buffer, VkDeviceMemory memory);
VkCommandBuffer BeginOneTimeCommands();
void EndOneTimeCommands(VkCommandBuffer cmd);
void TransitionImageLayout(VkCommandBuffer cmd, VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout);
void CopyBufferToImage(VkCommandBuffer cmd, VkBuffer buffer, VkImage image,
uint32_t width, uint32_t height, uint32_t depth);
void CopyImageToBuffer(VkCommandBuffer cmd, VkImage image, VkBuffer buffer,
uint32_t width, uint32_t height,
uint32_t offset_x = 0, uint32_t offset_y = 0);
VkFormat PixelFormatToVkFormat(PixelFormat format, int channel_count) const;
VkDeviceSize AlignSize(VkDeviceSize size, VkDeviceSize alignment) const;
uint32_t FindMemoryType(uint32_t type_filter,
VkMemoryPropertyFlags properties) const;
bool CompileGlslToSpv(const QString &glsl, VkShaderStageFlagBits stage,
QByteArray *out_spv);
QString ConvertGlslToVulkan(const QString &glsl, VkShaderStageFlagBits stage);
QString ConvertGlslUniformsToUbo(const QString &glsl,
QVector<UniformInfo> *out_uniforms);
VkDeviceSize GetStd140Size(const QString &type) const;
VkDeviceSize GetStd140Alignment(const QString &type) const;
bool CreatePipelineForShader(VulkanShader *shader,
const VideoParams &dest_params,
VkFormat render_pass_format);
VkRenderPass GetOrCreateRenderPass(VkFormat format);
VkInstance instance_ = VK_NULL_HANDLE;
VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
VkDevice device_ = VK_NULL_HANDLE;
VkQueue graphics_queue_ = VK_NULL_HANDLE;
uint32_t graphics_queue_family_ = UINT32_MAX;
VkCommandPool command_pool_ = VK_NULL_HANDLE;
VkDescriptorPool descriptor_pool_ = VK_NULL_HANDLE;
VkSampler linear_sampler_ = VK_NULL_HANDLE;
QHash<VkFormat, VkRenderPass> render_pass_cache_;
VkBuffer vertex_buffer_ = VK_NULL_HANDLE;
VkDeviceMemory vertex_buffer_memory_ = VK_NULL_HANDLE;
VkPhysicalDeviceMemoryProperties mem_properties_;
VkPhysicalDeviceProperties device_properties_;
QMutex mutex_;
// Texture handle counter
quint64 next_texture_id_ = 1;
QHash<quint64, VulkanTexture *> textures_;
// Shader handle counter
quint64 next_shader_id_ = 1;
QHash<quint64, VulkanShader *> shaders_;
static const int kMaxDescriptorSets = 1024;
};
}
#endif // VULKANRENDERER_H
+44 -5
View File
@@ -39,6 +39,9 @@
#include "render/ipc/frameslotpool.h"
#include "render/ipc/ipcmessage.h"
#include "render/ipc/sharedmemoryregion.h"
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
#include "render/backend/dynamicrenderer.h"
#endif
#include "render/opengl/openglrenderer.h"
#include "render/rendermanager.h"
#include "render/renderprocessor.h"
@@ -80,7 +83,7 @@ QJsonObject ErrorMessage(const QString &message, qint64 ticket_id = 0)
class RenderWorker {
public:
RenderWorker(olive::OpenGLRenderer *renderer, QFile *out)
RenderWorker(olive::Renderer *renderer, QFile *out)
: renderer_(renderer)
, out_(out)
{
@@ -114,7 +117,16 @@ public:
hs.input_slot_data_bytes = 0;
QJsonObject handshake = hs.ToJson();
if (QOpenGLContext *ctx = renderer_->context()) {
QOpenGLContext *ctx = nullptr;
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
if (auto *dynamic_renderer = dynamic_cast<olive::DynamicRenderer *>(renderer_)) {
ctx = dynamic_renderer->OpenGLContext();
} else
#endif
{
ctx = static_cast<olive::OpenGLRenderer *>(renderer_)->context();
}
if (ctx) {
const QSurfaceFormat fmt = ctx->format();
handshake["gl_major"] = fmt.majorVersion();
handshake["gl_minor"] = fmt.minorVersion();
@@ -427,7 +439,7 @@ private:
return Write(ready.ToJson());
}
olive::OpenGLRenderer *renderer_;
olive::Renderer *renderer_;
QFile *out_;
bool shutdown_requested_ = false;
std::unique_ptr<olive::Project> project_;
@@ -459,15 +471,42 @@ int main(int argc, char *argv[])
return 1;
}
auto *renderer = new olive::OpenGLRenderer();
olive::Renderer *renderer;
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
auto *dynamic_renderer = new olive::DynamicRenderer(QStringLiteral("opengl"));
if (dynamic_renderer->Init()) {
dynamic_renderer->PostInit();
renderer = dynamic_renderer;
} else {
delete dynamic_renderer;
qWarning() << "Failed to initialize dynamic OpenGL backend, falling back to direct OpenGL renderer";
renderer = new olive::OpenGLRenderer();
if (!renderer->Init()) {
LogError(QStringLiteral("failed to initialize OpenGL renderer"));
delete renderer;
return 1;
}
renderer->PostInit();
}
#else
renderer = new olive::OpenGLRenderer();
if (!renderer->Init()) {
LogError(QStringLiteral("failed to initialize OpenGL renderer"));
delete renderer;
return 1;
}
renderer->PostInit();
#endif
QOpenGLContext *ctx = renderer->context();
QOpenGLContext *ctx = nullptr;
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
if (auto *loaded_renderer = dynamic_cast<olive::DynamicRenderer *>(renderer)) {
ctx = loaded_renderer->OpenGLContext();
} else
#endif
{
ctx = static_cast<olive::OpenGLRenderer *>(renderer)->context();
}
if (!ctx || !ctx->isValid()) {
LogError(QStringLiteral("OpenGL context is not valid after init"));
renderer->Destroy();