format: reformatting files
This commit is contained in:
+55
-55
@@ -21,62 +21,62 @@ add_subdirectory(ocioconf)
|
||||
add_subdirectory(opengl)
|
||||
add_subdirectory(plugin)
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
render/audioplaybackcache.cpp
|
||||
render/audioplaybackcache.h
|
||||
render/audiowaveformcache.cpp
|
||||
render/audiowaveformcache.h
|
||||
render/backend/dynamicrenderer.cpp
|
||||
render/backend/dynamicrenderer.h
|
||||
render/backend/renderbackend_c.h
|
||||
render/cancelatom.h
|
||||
render/colormanagement.cpp
|
||||
render/colorprocessor.cpp
|
||||
render/colorprocessor.h
|
||||
render/colorprocessorcache.h
|
||||
render/diskmanager.cpp
|
||||
render/diskmanager.h
|
||||
render/framehashcache.cpp
|
||||
render/framehashcache.h
|
||||
render/framemanager.cpp
|
||||
render/framemanager.h
|
||||
render/interlacetexture.cpp
|
||||
render/loopmode.h
|
||||
render/managedcolor.cpp
|
||||
render/managedcolor.h
|
||||
render/playbackcache.cpp
|
||||
render/playbackcache.h
|
||||
render/previewaudiodevice.cpp
|
||||
render/previewaudiodevice.h
|
||||
render/previewautocacher.cpp
|
||||
render/previewautocacher.h
|
||||
render/projectcopier.cpp
|
||||
render/projectcopier.h
|
||||
render/renderer.cpp
|
||||
render/renderer.h
|
||||
render/rendercache.h
|
||||
render/renderjobtracker.cpp
|
||||
render/renderjobtracker.h
|
||||
render/rendermanager.cpp
|
||||
render/rendermanager.h
|
||||
render/renderworkerpool.cpp
|
||||
render/renderworkerpool.h
|
||||
render/rendermodes.h
|
||||
render/renderprocessor.cpp
|
||||
render/renderprocessor.h
|
||||
render/renderticket.cpp
|
||||
render/renderticket.h
|
||||
render/shadercode.h
|
||||
render/subtitleparams.cpp
|
||||
render/subtitleparams.h
|
||||
render/texture.cpp
|
||||
render/texture.h
|
||||
render/videoparams.cpp
|
||||
render/videoparams.h
|
||||
PARENT_SCOPE
|
||||
${OLIVE_SOURCES}
|
||||
render/audioplaybackcache.cpp
|
||||
render/audioplaybackcache.h
|
||||
render/audiowaveformcache.cpp
|
||||
render/audiowaveformcache.h
|
||||
render/backend/dynamicrenderer.cpp
|
||||
render/backend/dynamicrenderer.h
|
||||
render/backend/renderbackend_c.h
|
||||
render/cancelatom.h
|
||||
render/colormanagement.cpp
|
||||
render/colorprocessor.cpp
|
||||
render/colorprocessor.h
|
||||
render/colorprocessorcache.h
|
||||
render/diskmanager.cpp
|
||||
render/diskmanager.h
|
||||
render/framehashcache.cpp
|
||||
render/framehashcache.h
|
||||
render/framemanager.cpp
|
||||
render/framemanager.h
|
||||
render/interlacetexture.cpp
|
||||
render/loopmode.h
|
||||
render/managedcolor.cpp
|
||||
render/managedcolor.h
|
||||
render/playbackcache.cpp
|
||||
render/playbackcache.h
|
||||
render/previewaudiodevice.cpp
|
||||
render/previewaudiodevice.h
|
||||
render/previewautocacher.cpp
|
||||
render/previewautocacher.h
|
||||
render/projectcopier.cpp
|
||||
render/projectcopier.h
|
||||
render/renderer.cpp
|
||||
render/renderer.h
|
||||
render/rendercache.h
|
||||
render/renderjobtracker.cpp
|
||||
render/renderjobtracker.h
|
||||
render/rendermanager.cpp
|
||||
render/rendermanager.h
|
||||
render/renderworkerpool.cpp
|
||||
render/renderworkerpool.h
|
||||
render/rendermodes.h
|
||||
render/renderprocessor.cpp
|
||||
render/renderprocessor.h
|
||||
render/renderticket.cpp
|
||||
render/renderticket.h
|
||||
render/shadercode.h
|
||||
render/subtitleparams.cpp
|
||||
render/subtitleparams.h
|
||||
render/texture.cpp
|
||||
render/texture.h
|
||||
render/videoparams.cpp
|
||||
render/videoparams.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(OLIVE_RESOURCES
|
||||
${OLIVE_RESOURCES}
|
||||
PARENT_SCOPE
|
||||
${OLIVE_RESOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -37,21 +37,24 @@ DynamicRenderer::~DynamicRenderer()
|
||||
// system libGL/libvulkan loader is never mistaken for an Oak render backend.
|
||||
QString DynamicRenderer::LibraryFilename() const
|
||||
{
|
||||
const QString base = backend_ == QStringLiteral("vulkan")
|
||||
? QStringLiteral("oakvulkan")
|
||||
: QStringLiteral("oakgl");
|
||||
const QString base = backend_ == QStringLiteral("vulkan") ?
|
||||
QStringLiteral("oakvulkan") :
|
||||
QStringLiteral("oakgl");
|
||||
#if defined(Q_OS_WIN)
|
||||
const QString filename = base + QStringLiteral(".dll");
|
||||
#elif defined(Q_OS_MAC)
|
||||
const QString filename = QStringLiteral("lib") + base + QStringLiteral(".dylib");
|
||||
const QString filename =
|
||||
QStringLiteral("lib") + base + QStringLiteral(".dylib");
|
||||
#else
|
||||
const QString filename = QStringLiteral("lib") + base + QStringLiteral(".so");
|
||||
const QString filename =
|
||||
QStringLiteral("lib") + base + QStringLiteral(".so");
|
||||
#endif
|
||||
|
||||
const QDir app_dir(QCoreApplication::applicationDirPath());
|
||||
const QStringList candidates = {
|
||||
app_dir.filePath(filename),
|
||||
app_dir.filePath(QDir(QStringLiteral("render_backends")).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)),
|
||||
@@ -77,9 +80,9 @@ bool DynamicRenderer::Load()
|
||||
library_.setFileName(LibraryFilename());
|
||||
if (!library_.load()) {
|
||||
if (backend_ == QStringLiteral("vulkan")) {
|
||||
qWarning() << "Failed to load Vulkan render backend"
|
||||
<< library_.fileName() << library_.errorString()
|
||||
<< "falling back to OpenGL backend";
|
||||
qWarning()
|
||||
<< "Failed to load Vulkan render backend" << library_.fileName()
|
||||
<< library_.errorString() << "falling back to OpenGL backend";
|
||||
backend_ = QStringLiteral("opengl");
|
||||
library_.setFileName(LibraryFilename());
|
||||
}
|
||||
@@ -126,9 +129,10 @@ bool DynamicRenderer::Load()
|
||||
bool DynamicRenderer::ResolveFunctions()
|
||||
{
|
||||
ResetFunctions();
|
||||
#define RESOLVE(member, type, symbol) \
|
||||
#define RESOLVE(member, type, symbol) \
|
||||
member = reinterpret_cast<type>(library_.resolve(symbol)); \
|
||||
if (!member) return false
|
||||
if (!member) \
|
||||
return false
|
||||
|
||||
RESOLVE(create_, OakBackendCreateFn, "oak_renderer_create");
|
||||
RESOLVE(destroy_, OakBackendDestroyFn, "oak_renderer_destroy");
|
||||
@@ -259,7 +263,7 @@ void DynamicRenderer::PostInit()
|
||||
|
||||
// Forwards render target clearing through the C ABI.
|
||||
void DynamicRenderer::ClearDestination(Texture *texture, double r, double g,
|
||||
double b, double a)
|
||||
double b, double a)
|
||||
{
|
||||
clear_destination_(handle_, texture, r, g, b, a);
|
||||
}
|
||||
@@ -281,16 +285,16 @@ void DynamicRenderer::DestroyNativeShader(QVariant shader)
|
||||
|
||||
// Uploads CPU pixel data into a backend texture through the dynamic ABI.
|
||||
void DynamicRenderer::UploadToTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, const void *data,
|
||||
int linesize)
|
||||
const VideoParams ¶ms,
|
||||
const void *data, int linesize)
|
||||
{
|
||||
upload_to_texture_(handle_, &handle, ¶ms, data, linesize);
|
||||
}
|
||||
|
||||
// Downloads backend texture data into a caller-provided CPU buffer.
|
||||
void DynamicRenderer::DownloadFromTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, void *data,
|
||||
int linesize)
|
||||
const VideoParams ¶ms, void *data,
|
||||
int linesize)
|
||||
{
|
||||
download_from_texture_(handle_, &handle, ¶ms, data, linesize);
|
||||
}
|
||||
@@ -313,9 +317,9 @@ Color DynamicRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
|
||||
// null so callers can avoid GL-only paths.
|
||||
QOpenGLContext *DynamicRenderer::OpenGLContext() const
|
||||
{
|
||||
return opengl_context_ && handle_
|
||||
? static_cast<QOpenGLContext *>(opengl_context_(handle_))
|
||||
: nullptr;
|
||||
return opengl_context_ && handle_ ?
|
||||
static_cast<QOpenGLContext *>(opengl_context_(handle_)) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
// Reports the effective backend after any load-time fallback has completed.
|
||||
@@ -331,8 +335,8 @@ bool DynamicRenderer::IsVulkan() const
|
||||
|
||||
// Dispatches a shader blit to the loaded backend.
|
||||
void DynamicRenderer::Blit(QVariant shader, AcceleratedJob &job,
|
||||
Texture *destination, VideoParams destination_params,
|
||||
bool clear_destination)
|
||||
Texture *destination, VideoParams destination_params,
|
||||
bool clear_destination)
|
||||
{
|
||||
blit_(handle_, &shader, &job, destination, &destination_params,
|
||||
clear_destination);
|
||||
@@ -340,12 +344,13 @@ void DynamicRenderer::Blit(QVariant shader, AcceleratedJob &job,
|
||||
|
||||
// Allocates a backend-native texture and wraps its opaque handle in QVariant.
|
||||
QVariant DynamicRenderer::CreateNativeTexture(int width, int height, int depth,
|
||||
PixelFormat format, int channel_count,
|
||||
const void *data, int linesize)
|
||||
PixelFormat format,
|
||||
int channel_count,
|
||||
const void *data, int linesize)
|
||||
{
|
||||
QVariant out;
|
||||
create_native_texture_(handle_, width, height, depth, format, channel_count,
|
||||
data, linesize, &out);
|
||||
data, linesize, &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,26 +42,26 @@ public:
|
||||
// Runs backend post-init setup.
|
||||
virtual void PostInit() override;
|
||||
// Clears either a native texture destination or the backend output target.
|
||||
virtual void ClearDestination(Texture *texture = nullptr,
|
||||
double r = 0.0, double g = 0.0,
|
||||
double b = 0.0, double a = 0.0) override;
|
||||
virtual void ClearDestination(Texture *texture = nullptr, double r = 0.0,
|
||||
double g = 0.0, double b = 0.0,
|
||||
double a = 0.0) override;
|
||||
// Creates a native shader through the dynamic backend.
|
||||
virtual QVariant CreateNativeShader(ShaderCode code) override;
|
||||
// Destroys a native shader through the dynamic backend.
|
||||
virtual void DestroyNativeShader(QVariant shader) override;
|
||||
// Uploads CPU pixels to a backend texture.
|
||||
virtual void UploadToTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, const void *data,
|
||||
int linesize) override;
|
||||
const VideoParams ¶ms, const void *data,
|
||||
int linesize) override;
|
||||
// Downloads backend texture pixels to CPU memory.
|
||||
virtual void DownloadFromTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, void *data,
|
||||
int linesize) override;
|
||||
const VideoParams ¶ms, void *data,
|
||||
int linesize) override;
|
||||
// Waits for backend work to complete.
|
||||
virtual void Flush() override;
|
||||
// Reads one pixel from a backend texture.
|
||||
virtual Color GetPixelFromTexture(Texture *texture,
|
||||
const QPointF &pt) override;
|
||||
const QPointF &pt) override;
|
||||
// Returns the wrapped OpenGL context for OpenGL backends.
|
||||
virtual QOpenGLContext *OpenGLContext() const override;
|
||||
|
||||
@@ -79,13 +79,13 @@ public:
|
||||
protected:
|
||||
// Dispatches a shader blit through the dynamic backend.
|
||||
virtual void Blit(QVariant shader, AcceleratedJob &job,
|
||||
Texture *destination, VideoParams destination_params,
|
||||
bool clear_destination) override;
|
||||
Texture *destination, VideoParams destination_params,
|
||||
bool clear_destination) override;
|
||||
// Allocates a native texture through the dynamic backend.
|
||||
virtual QVariant CreateNativeTexture(int width, int height, int depth,
|
||||
PixelFormat format, int channel_count,
|
||||
const void *data = nullptr,
|
||||
int linesize = 0) override;
|
||||
PixelFormat format, int channel_count,
|
||||
const void *data = nullptr,
|
||||
int linesize = 0) override;
|
||||
// Releases a native texture through the dynamic backend.
|
||||
virtual void DestroyNativeTexture(QVariant texture) override;
|
||||
// Releases backend-owned renderer resources.
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#ifdef _WIN32
|
||||
#define OAK_RENDER_BACKEND_EXPORT extern "C" __declspec(dllexport)
|
||||
#else
|
||||
#define OAK_RENDER_BACKEND_EXPORT extern "C" __attribute__((visibility("default")))
|
||||
#define OAK_RENDER_BACKEND_EXPORT \
|
||||
extern "C" __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -50,14 +51,14 @@ typedef OakRenderBackendHandle (*OakBackendCreateFn)(void *parent);
|
||||
typedef void (*OakBackendDestroyFn)(OakRenderBackendHandle handle);
|
||||
/* Queries backend metadata and capability bits. */
|
||||
typedef bool (*OakBackendGetInfoFn)(OakRenderBackendHandle handle,
|
||||
struct OakRenderBackendInfo *out_info);
|
||||
struct OakRenderBackendInfo *out_info);
|
||||
/* Checks whether the backend can run on the current machine. */
|
||||
typedef bool (*OakBackendIsAvailableFn)(OakRenderBackendHandle handle);
|
||||
/* Initializes backend-owned device/context resources. */
|
||||
typedef bool (*OakBackendInitFn)(OakRenderBackendHandle handle);
|
||||
/* Initializes the backend against a caller-supplied GL context when applicable. */
|
||||
typedef void (*OakBackendInitWithContextFn)(OakRenderBackendHandle handle,
|
||||
void *context);
|
||||
void *context);
|
||||
/* Runs backend post-initialization after the device/context exists. */
|
||||
typedef void (*OakBackendPostInitFn)(OakRenderBackendHandle handle);
|
||||
/* Runs backend post-destroy cleanup before the library unloads. */
|
||||
@@ -66,40 +67,39 @@ typedef void (*OakBackendPostDestroyFn)(OakRenderBackendHandle handle);
|
||||
typedef void (*OakBackendDestroyInternalFn)(OakRenderBackendHandle handle);
|
||||
/* Clears a texture destination or implicit output target. */
|
||||
typedef void (*OakBackendClearDestinationFn)(OakRenderBackendHandle handle,
|
||||
void *texture, double r, double g,
|
||||
double b, double a);
|
||||
void *texture, double r, double g,
|
||||
double b, double a);
|
||||
/* Creates a native texture and writes a QVariant-compatible handle. */
|
||||
typedef void (*OakBackendCreateNativeTextureFn)(OakRenderBackendHandle handle,
|
||||
int width, int height, int depth,
|
||||
int format, int channel_count,
|
||||
const void *data, int linesize,
|
||||
void *out_variant);
|
||||
typedef void (*OakBackendCreateNativeTextureFn)(
|
||||
OakRenderBackendHandle handle, int width, int height, int depth, int format,
|
||||
int channel_count, const void *data, int linesize, void *out_variant);
|
||||
/* Destroys a native texture represented by a QVariant-compatible handle. */
|
||||
typedef void (*OakBackendDestroyNativeTextureFn)(OakRenderBackendHandle handle,
|
||||
const void *variant);
|
||||
const void *variant);
|
||||
/* Creates a native shader and writes a QVariant-compatible handle. */
|
||||
typedef void (*OakBackendCreateNativeShaderFn)(OakRenderBackendHandle handle,
|
||||
const void *shader_code,
|
||||
void *out_variant);
|
||||
const void *shader_code,
|
||||
void *out_variant);
|
||||
/* Destroys a native shader represented by a QVariant-compatible handle. */
|
||||
typedef void (*OakBackendDestroyNativeShaderFn)(OakRenderBackendHandle handle,
|
||||
const void *variant);
|
||||
const void *variant);
|
||||
/* Uploads CPU pixel data to a native texture. */
|
||||
typedef void (*OakBackendUploadToTextureFn)(OakRenderBackendHandle handle,
|
||||
const void *variant,
|
||||
const void *video_params,
|
||||
const void *data, int linesize);
|
||||
const void *variant,
|
||||
const void *video_params,
|
||||
const void *data, int linesize);
|
||||
/* Downloads native texture pixels into caller-owned CPU memory. */
|
||||
typedef void (*OakBackendDownloadFromTextureFn)(OakRenderBackendHandle handle,
|
||||
const void *variant,
|
||||
const void *video_params,
|
||||
void *data, int linesize);
|
||||
const void *variant,
|
||||
const void *video_params,
|
||||
void *data, int linesize);
|
||||
/* Waits for backend work that must be visible to later operations. */
|
||||
typedef void (*OakBackendFlushFn)(OakRenderBackendHandle handle);
|
||||
/* Reads one pixel from a texture. */
|
||||
typedef void (*OakBackendGetPixelFromTextureFn)(OakRenderBackendHandle handle,
|
||||
void *texture, const void *point,
|
||||
void *out_color);
|
||||
void *texture,
|
||||
const void *point,
|
||||
void *out_color);
|
||||
/* Executes a shader blit job. */
|
||||
typedef void (*OakBackendBlitFn)(OakRenderBackendHandle handle,
|
||||
const void *shader, void *job,
|
||||
@@ -108,7 +108,7 @@ typedef void (*OakBackendBlitFn)(OakRenderBackendHandle handle,
|
||||
bool clear_destination);
|
||||
/* Attaches an output texture for OFX OpenGL rendering when supported. */
|
||||
typedef void (*OakBackendAttachOutputTextureFn)(OakRenderBackendHandle handle,
|
||||
const void *texture_id);
|
||||
const void *texture_id);
|
||||
/* Detaches an OFX output texture when supported. */
|
||||
typedef void (*OakBackendDetachOutputTextureFn)(OakRenderBackendHandle handle);
|
||||
/* Returns the backend OpenGL context, or null for non-OpenGL backends. */
|
||||
|
||||
@@ -114,7 +114,7 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job,
|
||||
color_ctx.lut3d_textures[i].name = sampler_name;
|
||||
color_ctx.lut3d_textures[i].interpolation =
|
||||
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
|
||||
Texture::kLinear;
|
||||
Texture::kLinear;
|
||||
}
|
||||
|
||||
color_ctx.lut1d_textures.resize(shader_desc->getNumTextures());
|
||||
@@ -125,7 +125,8 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job,
|
||||
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)
|
||||
#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,
|
||||
@@ -149,16 +150,18 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job,
|
||||
}
|
||||
|
||||
// Allocate 1D LUT
|
||||
int lut_channels = (channel ==
|
||||
OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ?
|
||||
1 :
|
||||
VideoParams::kRGBChannelCount;
|
||||
VideoParams lut_params(width, height, PixelFormat::F32, lut_channels);
|
||||
color_ctx.lut1d_textures[i].texture = CreateTexture(lut_params, values);
|
||||
int lut_channels =
|
||||
(channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ?
|
||||
1 :
|
||||
VideoParams::kRGBChannelCount;
|
||||
VideoParams lut_params(width, height, PixelFormat::F32,
|
||||
lut_channels);
|
||||
color_ctx.lut1d_textures[i].texture =
|
||||
CreateTexture(lut_params, values);
|
||||
color_ctx.lut1d_textures[i].name = sampler_name;
|
||||
color_ctx.lut1d_textures[i].interpolation =
|
||||
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
|
||||
Texture::kLinear;
|
||||
Texture::kLinear;
|
||||
}
|
||||
|
||||
locker.relock();
|
||||
@@ -176,9 +179,9 @@ void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
|
||||
ShaderJob fallback_job;
|
||||
fallback_job.Insert(QStringLiteral("ove_maintex"),
|
||||
color_job.GetInputTexture());
|
||||
fallback_job.Insert(
|
||||
QStringLiteral("ove_mvpmat"),
|
||||
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
|
||||
fallback_job.Insert(QStringLiteral("ove_mvpmat"),
|
||||
NodeValue(NodeValue::kMatrix,
|
||||
color_job.GetTransformMatrix()));
|
||||
|
||||
if (destination) {
|
||||
BlitToTexture(GetDefaultShader(), fallback_job, destination,
|
||||
@@ -206,12 +209,12 @@ void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
|
||||
|
||||
foreach (const ColorContext::LUT &l, color_ctx.lut3d_textures) {
|
||||
job.Insert(l.name, NodeValue(NodeValue::kTexture,
|
||||
QVariant::fromValue(l.texture)));
|
||||
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)));
|
||||
QVariant::fromValue(l.texture)));
|
||||
job.SetInterpolation(l.name, l.interpolation);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ namespace olive
|
||||
{
|
||||
|
||||
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input,
|
||||
const ColorTransform &transform,
|
||||
Direction direction)
|
||||
const ColorTransform &transform,
|
||||
Direction direction)
|
||||
{
|
||||
processor_ = nullptr;
|
||||
cpu_processor_ = nullptr;
|
||||
@@ -59,17 +59,18 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input,
|
||||
display_transform->setDisplay(output.toUtf8());
|
||||
display_transform->setView(view.toUtf8());
|
||||
display_transform->setDirection(direction == kNormal ?
|
||||
OCIO::TRANSFORM_DIR_FORWARD :
|
||||
OCIO::TRANSFORM_DIR_INVERSE);
|
||||
OCIO::TRANSFORM_DIR_FORWARD :
|
||||
OCIO::TRANSFORM_DIR_INVERSE);
|
||||
|
||||
if (transform.look().isEmpty()) {
|
||||
processor_ = ocio_config->getProcessor(display_transform);
|
||||
} else {
|
||||
auto group = OCIO::GroupTransform::Create();
|
||||
|
||||
const char *out_cs = OCIO::LookTransform::GetLooksResultColorSpace(
|
||||
ocio_config, ocio_config->getCurrentContext(),
|
||||
transform.look().toUtf8());
|
||||
const char *out_cs =
|
||||
OCIO::LookTransform::GetLooksResultColorSpace(
|
||||
ocio_config, ocio_config->getCurrentContext(),
|
||||
transform.look().toUtf8());
|
||||
|
||||
auto lt = OCIO::LookTransform::Create();
|
||||
lt->setSrc(resolved_input.toUtf8());
|
||||
@@ -105,7 +106,8 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input,
|
||||
ColorProcessor::ColorProcessor(OCIO::ConstProcessorRcPtr processor)
|
||||
{
|
||||
processor_ = processor;
|
||||
cpu_processor_ = processor_ ? processor_->getDefaultCPUProcessor() : nullptr;
|
||||
cpu_processor_ = processor_ ? processor_->getDefaultCPUProcessor() :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
void ColorProcessor::ConvertFrame(Frame *f)
|
||||
@@ -151,7 +153,7 @@ ColorProcessorPtr ColorProcessor::Create(ColorManager *config,
|
||||
Direction direction)
|
||||
{
|
||||
return std::make_shared<ColorProcessor>(config, input, transform,
|
||||
direction);
|
||||
direction);
|
||||
}
|
||||
|
||||
ColorProcessorPtr ColorProcessor::Create(OCIO::ConstProcessorRcPtr processor)
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
render/ipc/frameslotpool.cpp
|
||||
render/ipc/frameslotpool.h
|
||||
render/ipc/ipcmessage.cpp
|
||||
render/ipc/ipcmessage.h
|
||||
render/ipc/sharedmemoryregion.cpp
|
||||
render/ipc/sharedmemoryregion.h
|
||||
render/ipc/spscringbuffer.h
|
||||
PARENT_SCOPE
|
||||
${OLIVE_SOURCES}
|
||||
render/ipc/frameslotpool.cpp
|
||||
render/ipc/frameslotpool.h
|
||||
render/ipc/ipcmessage.cpp
|
||||
render/ipc/ipcmessage.h
|
||||
render/ipc/sharedmemoryregion.cpp
|
||||
render/ipc/sharedmemoryregion.h
|
||||
render/ipc/spscringbuffer.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -36,18 +36,21 @@ size_t AlignUp(size_t value, size_t align)
|
||||
return (value + (align - 1)) & ~(align - 1);
|
||||
}
|
||||
|
||||
constexpr size_t kAlign = 64; // Cache-line alignment for each sub-region.
|
||||
constexpr size_t kAlign = 64; // Cache-line alignment for each sub-region.
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
size_t FrameSlotPool::BytesNeeded(uint32_t slot_count, size_t slot_data_bytes)
|
||||
{
|
||||
const uint32_t ring_cap = RingCapacity(slot_count);
|
||||
size_t total = AlignUp(sizeof(Header), kAlign);
|
||||
total += AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // free ring
|
||||
total += AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // ready ring
|
||||
total += AlignUp(sizeof(FrameSlotMeta) * slot_count, kAlign); // metadata array
|
||||
total += AlignUp(slot_data_bytes, kAlign) * slot_count; // pixel data blocks
|
||||
total +=
|
||||
AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // free ring
|
||||
total +=
|
||||
AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // ready ring
|
||||
total +=
|
||||
AlignUp(sizeof(FrameSlotMeta) * slot_count, kAlign); // metadata array
|
||||
total += AlignUp(slot_data_bytes, kAlign) * slot_count; // pixel data blocks
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -111,9 +114,12 @@ FrameSlotPool FrameSlotPool::Attach(void *mem)
|
||||
return pool;
|
||||
}
|
||||
|
||||
pool.free_ring_ = SpscRingBuffer::Attach(pool.base_ + pool.header_->free_ring_offset);
|
||||
pool.ready_ring_ = SpscRingBuffer::Attach(pool.base_ + pool.header_->ready_ring_offset);
|
||||
pool.meta_ = reinterpret_cast<FrameSlotMeta *>(pool.base_ + pool.header_->meta_offset);
|
||||
pool.free_ring_ =
|
||||
SpscRingBuffer::Attach(pool.base_ + pool.header_->free_ring_offset);
|
||||
pool.ready_ring_ =
|
||||
SpscRingBuffer::Attach(pool.base_ + pool.header_->ready_ring_offset);
|
||||
pool.meta_ = reinterpret_cast<FrameSlotMeta *>(pool.base_ +
|
||||
pool.header_->meta_offset);
|
||||
pool.data_ = pool.base_ + pool.header_->data_offset;
|
||||
|
||||
return pool;
|
||||
@@ -169,5 +175,5 @@ bool FrameSlotPool::Release(uint32_t index)
|
||||
return free_ring_->Push(index);
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
@@ -40,16 +40,16 @@ namespace ipc
|
||||
* not guaranteed shared-memory-safe).
|
||||
*/
|
||||
struct FrameSlotMeta {
|
||||
int64_t id; ///< Caller-defined tag (e.g. ticket id, or footage stream hash).
|
||||
int64_t time_num; ///< Frame timestamp numerator.
|
||||
int64_t time_den; ///< Frame timestamp denominator.
|
||||
int64_t id; ///< Caller-defined tag (e.g. ticket id, or footage stream hash).
|
||||
int64_t time_num; ///< Frame timestamp numerator.
|
||||
int64_t time_den; ///< Frame timestamp denominator.
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t format; ///< olive::PixelFormat::Format value.
|
||||
int32_t format; ///< olive::PixelFormat::Format value.
|
||||
int32_t channel_count;
|
||||
int32_t linesize; ///< Bytes per scanline (stride).
|
||||
int32_t data_size; ///< Valid bytes written into the slot's data block.
|
||||
char colorspace[128]; ///< Input colorspace name for color-managed footage.
|
||||
int32_t linesize; ///< Bytes per scanline (stride).
|
||||
int32_t data_size; ///< Valid bytes written into the slot's data block.
|
||||
char colorspace[128]; ///< Input colorspace name for color-managed footage.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -90,7 +90,8 @@ public:
|
||||
* Initializes both rings, seeds the free ring with every slot index, and zeroes metadata.
|
||||
* `mem` must provide at least BytesNeeded(slot_count, slot_data_bytes) bytes.
|
||||
*/
|
||||
static FrameSlotPool Create(void *mem, uint32_t slot_count, size_t slot_data_bytes);
|
||||
static FrameSlotPool Create(void *mem, uint32_t slot_count,
|
||||
size_t slot_data_bytes);
|
||||
|
||||
/**
|
||||
* @brief Map an existing, already-initialized pool (peer side).
|
||||
@@ -148,7 +149,6 @@ public:
|
||||
FrameSlotPool() = default;
|
||||
|
||||
private:
|
||||
|
||||
struct Header {
|
||||
uint32_t magic;
|
||||
uint32_t slot_count;
|
||||
@@ -160,7 +160,7 @@ private:
|
||||
uint64_t data_offset;
|
||||
};
|
||||
|
||||
static constexpr uint32_t kMagic = 0x4F4B5350; // 'OKSP'
|
||||
static constexpr uint32_t kMagic = 0x4F4B5350; // 'OKSP'
|
||||
|
||||
// Ring capacity must exceed slot_count by one because a ring can hold at most capacity-1 entries
|
||||
// and we need to be able to enqueue every slot at once.
|
||||
@@ -177,7 +177,7 @@ private:
|
||||
uint8_t *data_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
|
||||
#endif // IPC_FRAMESLOTPOOL_H
|
||||
#endif // IPC_FRAMESLOTPOOL_H
|
||||
@@ -227,5 +227,5 @@ bool LoadGraphMsg::FromJson(const QJsonObject &o, LoadGraphMsg *out)
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
|
||||
+25
-20
@@ -63,7 +63,7 @@ constexpr const char *kCancel = "cancel";
|
||||
constexpr const char *kGraphUpdate = "graph_update";
|
||||
constexpr const char *kShutdown = "shutdown";
|
||||
constexpr const char *kError = "error";
|
||||
} // namespace msgtype
|
||||
} // namespace msgtype
|
||||
|
||||
/**
|
||||
* @brief Write one NDJSON message line to `device`.
|
||||
@@ -92,29 +92,34 @@ bool ReadMessage(QByteArray *buffer, QJsonObject *out, bool *ok = nullptr);
|
||||
|
||||
struct HandshakeMsg {
|
||||
int protocol_version = 0;
|
||||
QString shm_key; ///< Worker->main output shared-memory segment key.
|
||||
QString input_shm_key; ///< Main->worker input shared-memory segment key (optional).
|
||||
int input_slots = 0; ///< Number of main->worker input frame slots.
|
||||
int output_slots = 0; ///< Number of worker->main output frame slots.
|
||||
qint64 slot_data_bytes = 0; ///< Per-output-slot pixel block size.
|
||||
qint64 input_slot_data_bytes = 0; ///< Per-input-slot pixel block size.
|
||||
QString shm_key; ///< Worker->main output shared-memory segment key.
|
||||
QString
|
||||
input_shm_key; ///< Main->worker input shared-memory segment key (optional).
|
||||
int input_slots = 0; ///< Number of main->worker input frame slots.
|
||||
int output_slots = 0; ///< Number of worker->main output frame slots.
|
||||
qint64 slot_data_bytes = 0; ///< Per-output-slot pixel block size.
|
||||
qint64 input_slot_data_bytes = 0; ///< Per-input-slot pixel block size.
|
||||
|
||||
QJsonObject ToJson() const;
|
||||
static bool FromJson(const QJsonObject &o, HandshakeMsg *out);
|
||||
};
|
||||
|
||||
struct RenderFrameMsg {
|
||||
qint64 ticket_id = 0; ///< Correlates this request with the eventual frame_ready.
|
||||
QString node_uuid; ///< Output/viewer node to render, by stable uuid in the loaded graph.
|
||||
qint64 ticket_id =
|
||||
0; ///< Correlates this request with the eventual frame_ready.
|
||||
QString
|
||||
node_uuid; ///< Output/viewer node to render, by stable uuid in the loaded graph.
|
||||
qint64 time_num = 0;
|
||||
qint64 time_den = 1;
|
||||
int width = 0; ///< Forced output size (0 = use graph default).
|
||||
int width = 0; ///< Forced output size (0 = use graph default).
|
||||
int height = 0;
|
||||
int format = -1; ///< Forced PixelFormat::Format (-1 = default/INVALID).
|
||||
int channel_count = 0; ///< 0 = default.
|
||||
int mode = 0; ///< RenderMode::Mode.
|
||||
int input_slot = -1; ///< Optional main->worker decoded input slot for footage nodes.
|
||||
QVector<int> input_slots; ///< Optional ordered decoded input slots for footage nodes.
|
||||
int format = -1; ///< Forced PixelFormat::Format (-1 = default/INVALID).
|
||||
int channel_count = 0; ///< 0 = default.
|
||||
int mode = 0; ///< RenderMode::Mode.
|
||||
int input_slot =
|
||||
-1; ///< Optional main->worker decoded input slot for footage nodes.
|
||||
QVector<int>
|
||||
input_slots; ///< Optional ordered decoded input slots for footage nodes.
|
||||
|
||||
// Output color transform to apply before returning the frame. When empty,
|
||||
// the worker returns the image in the project's reference space.
|
||||
@@ -130,7 +135,7 @@ struct RenderFrameMsg {
|
||||
|
||||
struct FrameReadyMsg {
|
||||
qint64 ticket_id = 0;
|
||||
int output_slot = 0; ///< Index into the worker->main output FrameSlotPool.
|
||||
int output_slot = 0; ///< Index into the worker->main output FrameSlotPool.
|
||||
|
||||
QJsonObject ToJson() const;
|
||||
static bool FromJson(const QJsonObject &o, FrameReadyMsg *out);
|
||||
@@ -144,13 +149,13 @@ struct CancelMsg {
|
||||
};
|
||||
|
||||
struct LoadGraphMsg {
|
||||
QString path; ///< Temporary file holding the serialized node graph.
|
||||
QString path; ///< Temporary file holding the serialized node graph.
|
||||
|
||||
QJsonObject ToJson() const;
|
||||
static bool FromJson(const QJsonObject &o, LoadGraphMsg *out);
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
|
||||
#endif // IPC_IPCMESSAGE_H
|
||||
#endif // IPC_IPCMESSAGE_H
|
||||
|
||||
@@ -75,16 +75,20 @@ bool SharedMemoryRegion::Open(const QString &key, size_t size, Mode mode)
|
||||
const std::wstring wname = mapping_name.toStdWString();
|
||||
|
||||
if (mode == kCreate) {
|
||||
const DWORD size_high = static_cast<DWORD>((quint64(size) >> 32) & 0xFFFFFFFF);
|
||||
const DWORD size_high =
|
||||
static_cast<DWORD>((quint64(size) >> 32) & 0xFFFFFFFF);
|
||||
const DWORD size_low = static_cast<DWORD>(quint64(size) & 0xFFFFFFFF);
|
||||
handle_ = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
|
||||
size_high, size_low, wname.c_str());
|
||||
handle_ = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
|
||||
PAGE_READWRITE, size_high, size_low,
|
||||
wname.c_str());
|
||||
if (!handle_) {
|
||||
error_ = QStringLiteral("CreateFileMapping failed: %1").arg(GetLastError());
|
||||
error_ = QStringLiteral("CreateFileMapping failed: %1")
|
||||
.arg(GetLastError());
|
||||
return false;
|
||||
}
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
error_ = QStringLiteral("Shared memory key already exists: %1").arg(key);
|
||||
error_ =
|
||||
QStringLiteral("Shared memory key already exists: %1").arg(key);
|
||||
CloseHandle(handle_);
|
||||
handle_ = nullptr;
|
||||
return false;
|
||||
@@ -92,7 +96,8 @@ bool SharedMemoryRegion::Open(const QString &key, size_t size, Mode mode)
|
||||
} else {
|
||||
handle_ = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, wname.c_str());
|
||||
if (!handle_) {
|
||||
error_ = QStringLiteral("OpenFileMapping failed: %1").arg(GetLastError());
|
||||
error_ =
|
||||
QStringLiteral("OpenFileMapping failed: %1").arg(GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +129,7 @@ void SharedMemoryRegion::Close()
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
#else // POSIX
|
||||
#else // POSIX
|
||||
|
||||
bool SharedMemoryRegion::Open(const QString &key, size_t size, Mode mode)
|
||||
{
|
||||
@@ -165,7 +170,8 @@ bool SharedMemoryRegion::Open(const QString &key, size_t size, Mode mode)
|
||||
|
||||
data_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
|
||||
if (data_ == MAP_FAILED) {
|
||||
error_ = QStringLiteral("mmap failed: %1").arg(QString::fromUtf8(strerror(errno)));
|
||||
error_ = QStringLiteral("mmap failed: %1")
|
||||
.arg(QString::fromUtf8(strerror(errno)));
|
||||
data_ = nullptr;
|
||||
::close(fd_);
|
||||
fd_ = -1;
|
||||
@@ -201,5 +207,5 @@ void SharedMemoryRegion::Close()
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
@@ -110,14 +110,14 @@ private:
|
||||
QString error_;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
void *handle_; // HANDLE from CreateFileMapping/OpenFileMapping
|
||||
void *handle_; // HANDLE from CreateFileMapping/OpenFileMapping
|
||||
#else
|
||||
int fd_; // file descriptor from shm_open
|
||||
QString shm_name_; // the platform-prefixed name actually passed to shm_open
|
||||
int fd_; // file descriptor from shm_open
|
||||
QString shm_name_; // the platform-prefixed name actually passed to shm_open
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
|
||||
#endif // IPC_SHAREDMEMORYREGION_H
|
||||
#endif // IPC_SHAREDMEMORYREGION_H
|
||||
@@ -174,11 +174,12 @@ private:
|
||||
std::atomic<uint32_t> tail_;
|
||||
uint32_t capacity_;
|
||||
|
||||
static_assert(sizeof(std::atomic<uint32_t>) == sizeof(uint32_t),
|
||||
"atomic<uint32_t> must be lock-free POD-sized for shared memory use");
|
||||
static_assert(
|
||||
sizeof(std::atomic<uint32_t>) == sizeof(uint32_t),
|
||||
"atomic<uint32_t> must be lock-free POD-sized for shared memory use");
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
} // namespace ipc
|
||||
} // namespace olive
|
||||
|
||||
#endif // IPC_SPSCRINGBUFFER_H
|
||||
#endif // IPC_SPSCRINGBUFFER_H
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
render/job/acceleratedjob.cpp
|
||||
render/job/acceleratedjob.h
|
||||
render/job/footagejob.h
|
||||
render/job/generatejob.h
|
||||
render/job/samplejob.h
|
||||
render/job/shaderjob.h
|
||||
render/job/pluginjob.h
|
||||
render/job/pluginjob.cpp
|
||||
${OLIVE_SOURCES}
|
||||
render/job/acceleratedjob.cpp
|
||||
render/job/acceleratedjob.h
|
||||
render/job/footagejob.h
|
||||
render/job/generatejob.h
|
||||
render/job/samplejob.h
|
||||
render/job/shaderjob.h
|
||||
render/job/pluginjob.h
|
||||
render/job/pluginjob.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
|
||||
#include "pluginjob.h"
|
||||
|
||||
namespace olive {
|
||||
namespace plugin {
|
||||
namespace olive
|
||||
{
|
||||
namespace plugin
|
||||
{
|
||||
} // plugin
|
||||
} // olive
|
||||
+19
-14
@@ -26,47 +26,52 @@
|
||||
#include <any>
|
||||
#include <chrono>
|
||||
|
||||
namespace olive {
|
||||
namespace plugin {
|
||||
namespace olive
|
||||
{
|
||||
namespace plugin
|
||||
{
|
||||
|
||||
class PluginJob :public AcceleratedJob{
|
||||
class PluginJob : public AcceleratedJob {
|
||||
public:
|
||||
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
|
||||
const PluginNode* node, NodeValueRow row,
|
||||
explicit PluginJob(const OFX::Host::ImageEffect::Instance *pluginInstance,
|
||||
const PluginNode *node, NodeValueRow row,
|
||||
const olive::core::rational &time)
|
||||
: AcceleratedJob()
|
||||
, time_seconds_(time.toDouble())
|
||||
{
|
||||
this->pluginInstance_ = pluginInstance;
|
||||
this->node_=node;
|
||||
this->node_ = node;
|
||||
Insert(row);
|
||||
}
|
||||
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
|
||||
const PluginNode* node, NodeValueRow row)
|
||||
explicit PluginJob(const OFX::Host::ImageEffect::Instance *pluginInstance,
|
||||
const PluginNode *node, NodeValueRow row)
|
||||
: PluginJob(pluginInstance, node, row, olive::core::rational(0))
|
||||
{
|
||||
}
|
||||
|
||||
PluginNode *node() const {
|
||||
PluginNode *node() const
|
||||
{
|
||||
return const_cast<PluginNode *>(node_);
|
||||
}
|
||||
|
||||
OFX::Host::ImageEffect::Instance* pluginInstance() {
|
||||
return const_cast<OFX::Host::ImageEffect::Instance*>(pluginInstance_);
|
||||
OFX::Host::ImageEffect::Instance *pluginInstance()
|
||||
{
|
||||
return const_cast<OFX::Host::ImageEffect::Instance *>(pluginInstance_);
|
||||
}
|
||||
|
||||
double time_seconds() const {
|
||||
double time_seconds() const
|
||||
{
|
||||
return time_seconds_;
|
||||
}
|
||||
|
||||
private:
|
||||
const OFX::Host::ImageEffect::Instance *pluginInstance_=nullptr;
|
||||
const OFX::Host::ImageEffect::Instance *pluginInstance_ = nullptr;
|
||||
|
||||
QHash<OfxTime, QHash<QString, std::any>> paramsOnTime;
|
||||
|
||||
QHash<QString, std::any> params;
|
||||
|
||||
const PluginNode *node_=nullptr;
|
||||
const PluginNode *node_ = nullptr;
|
||||
double time_seconds_ = 0.0;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
file(GLOB_RECURSE OCIOCONF_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.ocio *.spi3d *.spi1d)
|
||||
set(QRC_BODY "")
|
||||
foreach(OCIOCONF_FILE ${OCIOCONF_RESOURCES})
|
||||
string(APPEND QRC_BODY "<file>${OCIOCONF_FILE}</file>\n")
|
||||
configure_file(${OCIOCONF_FILE} ${OCIOCONF_FILE} COPYONLY)
|
||||
endforeach()
|
||||
foreach (OCIOCONF_FILE ${OCIOCONF_RESOURCES})
|
||||
string(APPEND QRC_BODY "<file>${OCIOCONF_FILE}</file>\n")
|
||||
configure_file(${OCIOCONF_FILE} ${OCIOCONF_FILE} COPYONLY)
|
||||
endforeach ()
|
||||
configure_file(ocioconf.qrc.in ocioconf.qrc @ONLY)
|
||||
|
||||
set(OLIVE_RESOURCES
|
||||
${OLIVE_RESOURCES}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ocioconf.qrc
|
||||
PARENT_SCOPE
|
||||
${OLIVE_RESOURCES}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ocioconf.qrc
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
render/opengl/openglrenderer.cpp
|
||||
render/opengl/openglrenderer.h
|
||||
PARENT_SCOPE
|
||||
${OLIVE_SOURCES}
|
||||
render/opengl/openglrenderer.cpp
|
||||
render/opengl/openglrenderer.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
#include "render/texture.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
|
||||
class BackendOpenGLRenderer : public olive::OpenGLRenderer {
|
||||
public:
|
||||
@@ -39,29 +40,32 @@ const QVariant &VariantRef(const void *variant)
|
||||
} // namespace
|
||||
|
||||
// Creates the backend object and returns it as an opaque C handle.
|
||||
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle oak_renderer_create(void *parent)
|
||||
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle
|
||||
oak_renderer_create(void *parent)
|
||||
{
|
||||
return new BackendOpenGLRenderer(static_cast<QObject *>(parent));
|
||||
}
|
||||
|
||||
// Destroys the opaque backend object created by oak_renderer_create().
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy(OakRenderBackendHandle handle)
|
||||
{
|
||||
delete Renderer(handle);
|
||||
}
|
||||
|
||||
// Reports static OpenGL backend capabilities to the adapter.
|
||||
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
|
||||
OakRenderBackendHandle handle, OakRenderBackendInfo *out_info)
|
||||
OAK_RENDER_BACKEND_EXPORT bool
|
||||
oak_renderer_get_info(OakRenderBackendHandle handle,
|
||||
OakRenderBackendInfo *out_info)
|
||||
{
|
||||
if (!handle || !out_info) {
|
||||
return false;
|
||||
}
|
||||
out_info->abi_version = 1;
|
||||
out_info->kind = OAK_RENDER_BACKEND_OPENGL;
|
||||
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->capabilities =
|
||||
OAK_RENDER_BACKEND_CAP_TEXTURES | OAK_RENDER_BACKEND_CAP_SHADERS |
|
||||
OAK_RENDER_BACKEND_CAP_BLIT | OAK_RENDER_BACKEND_CAP_READBACK |
|
||||
OAK_RENDER_BACKEND_CAP_VIEWER_CONTEXT;
|
||||
out_info->name = "opengl";
|
||||
out_info->status = "available";
|
||||
@@ -70,8 +74,8 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
|
||||
|
||||
// OpenGL availability is context-dependent, so object creation is the minimum
|
||||
// availability signal for this backend.
|
||||
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT bool
|
||||
oak_renderer_is_available(OakRenderBackendHandle handle)
|
||||
{
|
||||
return handle != nullptr;
|
||||
}
|
||||
@@ -83,38 +87,40 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
|
||||
}
|
||||
|
||||
// Initializes the backend against a caller-owned viewer OpenGL context.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
|
||||
OakRenderBackendHandle handle, void *context)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_init_with_context(OakRenderBackendHandle handle, void *context)
|
||||
{
|
||||
Renderer(handle)->Init(static_cast<QOpenGLContext *>(context));
|
||||
}
|
||||
|
||||
// Runs renderer post-initialization once the GL context is available.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_init(OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_post_init(OakRenderBackendHandle handle)
|
||||
{
|
||||
Renderer(handle)->PostInit();
|
||||
}
|
||||
|
||||
// Releases post-init OpenGL surface/context state.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_destroy(OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_post_destroy(OakRenderBackendHandle handle)
|
||||
{
|
||||
Renderer(handle)->PostDestroy();
|
||||
}
|
||||
|
||||
// Releases renderer-owned GL resources before object destruction.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_internal(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy_internal(OakRenderBackendHandle handle)
|
||||
{
|
||||
Renderer(handle)->DestroyInternal();
|
||||
}
|
||||
|
||||
// Clears either the widget framebuffer or a texture destination.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
|
||||
OakRenderBackendHandle handle, void *texture, double r, double g, double b,
|
||||
double a)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_clear_destination(OakRenderBackendHandle handle, void *texture,
|
||||
double r, double g, double b, double a)
|
||||
{
|
||||
Renderer(handle)->ClearDestination(static_cast<olive::Texture *>(texture),
|
||||
r, g, b, a);
|
||||
r, g, b, a);
|
||||
}
|
||||
|
||||
// Creates an OpenGL texture and writes its QVariant handle to out_variant.
|
||||
@@ -122,51 +128,58 @@ 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)
|
||||
{
|
||||
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeTexture(
|
||||
width, height, depth, static_cast<olive::PixelFormat::Format>(format),
|
||||
channel_count, data, linesize);
|
||||
*static_cast<QVariant *>(out_variant) =
|
||||
Renderer(handle)->CreateNativeTexture(
|
||||
width, height, depth,
|
||||
static_cast<olive::PixelFormat::Format>(format), channel_count,
|
||||
data, linesize);
|
||||
}
|
||||
|
||||
// Destroys an OpenGL texture represented by a QVariant handle.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_texture(
|
||||
OakRenderBackendHandle handle, const void *variant)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy_native_texture(OakRenderBackendHandle handle,
|
||||
const void *variant)
|
||||
{
|
||||
Renderer(handle)->DestroyNativeTexture(VariantRef(variant));
|
||||
}
|
||||
|
||||
// Compiles an OpenGL shader program and returns its QVariant handle.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
|
||||
OakRenderBackendHandle handle, const void *shader_code, void *out_variant)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_create_native_shader(OakRenderBackendHandle handle,
|
||||
const void *shader_code, void *out_variant)
|
||||
{
|
||||
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeShader(
|
||||
*static_cast<const olive::ShaderCode *>(shader_code));
|
||||
*static_cast<QVariant *>(out_variant) =
|
||||
Renderer(handle)->CreateNativeShader(
|
||||
*static_cast<const olive::ShaderCode *>(shader_code));
|
||||
}
|
||||
|
||||
// Destroys an OpenGL shader program represented by a QVariant handle.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_shader(
|
||||
OakRenderBackendHandle handle, const void *variant)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy_native_shader(OakRenderBackendHandle handle,
|
||||
const void *variant)
|
||||
{
|
||||
Renderer(handle)->DestroyNativeShader(VariantRef(variant));
|
||||
}
|
||||
|
||||
// Uploads CPU pixel data into an OpenGL texture.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
|
||||
OakRenderBackendHandle handle, const void *variant, const void *video_params,
|
||||
const void *data, int linesize)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_upload_to_texture(OakRenderBackendHandle handle,
|
||||
const void *variant, const void *video_params,
|
||||
const void *data, int linesize)
|
||||
{
|
||||
Renderer(handle)->UploadToTexture(
|
||||
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
|
||||
data, linesize);
|
||||
VariantRef(variant),
|
||||
*static_cast<const olive::VideoParams *>(video_params), data, linesize);
|
||||
}
|
||||
|
||||
// Reads an OpenGL texture back to CPU memory.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
|
||||
OakRenderBackendHandle handle, const void *variant, const void *video_params,
|
||||
void *data, int linesize)
|
||||
OakRenderBackendHandle handle, const void *variant,
|
||||
const void *video_params, void *data, int linesize)
|
||||
{
|
||||
Renderer(handle)->DownloadFromTexture(
|
||||
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
|
||||
data, linesize);
|
||||
VariantRef(variant),
|
||||
*static_cast<const olive::VideoParams *>(video_params), data, linesize);
|
||||
}
|
||||
|
||||
// Flushes/waits for pending OpenGL work as required by the renderer.
|
||||
@@ -176,18 +189,23 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_flush(OakRenderBackendHandle handle)
|
||||
}
|
||||
|
||||
// Reads one pixel from an OpenGL texture.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
|
||||
OakRenderBackendHandle handle, void *texture, const void *point,
|
||||
void *out_color)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_get_pixel_from_texture(OakRenderBackendHandle handle,
|
||||
void *texture, const void *point,
|
||||
void *out_color)
|
||||
{
|
||||
*static_cast<olive::Color *>(out_color) = Renderer(handle)->GetPixelFromTexture(
|
||||
static_cast<olive::Texture *>(texture), *static_cast<const QPointF *>(point));
|
||||
*static_cast<olive::Color *>(out_color) =
|
||||
Renderer(handle)->GetPixelFromTexture(
|
||||
static_cast<olive::Texture *>(texture),
|
||||
*static_cast<const QPointF *>(point));
|
||||
}
|
||||
|
||||
// Executes a shader blit through the wrapped C++ OpenGL renderer.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
|
||||
OakRenderBackendHandle handle, const void *shader, void *job,
|
||||
void *destination, const void *destination_params, bool clear_destination)
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(OakRenderBackendHandle handle,
|
||||
const void *shader, void *job,
|
||||
void *destination,
|
||||
const void *destination_params,
|
||||
bool clear_destination)
|
||||
{
|
||||
Renderer(handle)->Blit(
|
||||
VariantRef(shader), *static_cast<olive::AcceleratedJob *>(job),
|
||||
@@ -197,22 +215,23 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
|
||||
}
|
||||
|
||||
// Exposes the wrapped OpenGL context for GL-specific integrations.
|
||||
OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void *
|
||||
oak_renderer_opengl_context(OakRenderBackendHandle handle)
|
||||
{
|
||||
return Renderer(handle)->context();
|
||||
}
|
||||
|
||||
// Binds an output texture for OFX OpenGL rendering.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
|
||||
OakRenderBackendHandle handle, const void *texture_id)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_attach_output_texture(OakRenderBackendHandle handle,
|
||||
const void *texture_id)
|
||||
{
|
||||
Renderer(handle)->AttachTextureAsDestination(VariantRef(texture_id));
|
||||
}
|
||||
|
||||
// Detaches any OFX OpenGL output texture binding.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_detach_output_texture(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_detach_output_texture(OakRenderBackendHandle handle)
|
||||
{
|
||||
Renderer(handle)->DetachTextureAsDestination();
|
||||
}
|
||||
|
||||
@@ -265,8 +265,8 @@ void OpenGLRenderer::AttachTextureAsDestination(const QVariant &texture)
|
||||
void OpenGLRenderer::DetachTextureAsDestination()
|
||||
{
|
||||
// QOpenGLWidget renders to a non-zero default FBO.
|
||||
const GLuint default_fbo =
|
||||
context_ ? context_->defaultFramebufferObject() : 0;
|
||||
const GLuint default_fbo = context_ ? context_->defaultFramebufferObject() :
|
||||
0;
|
||||
functions_->glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
|
||||
}
|
||||
|
||||
@@ -504,8 +504,8 @@ struct TextureToBind {
|
||||
Texture::Interpolation interpolation;
|
||||
};
|
||||
|
||||
void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destination,
|
||||
VideoParams destination_params,
|
||||
void OpenGLRenderer::Blit(QVariant s, AcceleratedJob &a_job,
|
||||
Texture *destination, VideoParams destination_params,
|
||||
bool clear_destination)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
@@ -519,7 +519,7 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
functions_->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
ShaderJob &s_job=dynamic_cast<ShaderJob &>(a_job);
|
||||
ShaderJob &s_job = dynamic_cast<ShaderJob &>(a_job);
|
||||
ShaderJob job(s_job);
|
||||
// If this node is iterative, we'll pick up which input here
|
||||
QMap<QString, GLuint> texture_index_map;
|
||||
@@ -585,7 +585,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
case NodeValue::kColor: {
|
||||
Color color = value.toColor();
|
||||
functions_->glUniform4f(variable_location, color.red(),
|
||||
color.green(), color.blue(), color.alpha());
|
||||
color.green(), color.blue(),
|
||||
color.alpha());
|
||||
break;
|
||||
}
|
||||
case NodeValue::kBoolean:
|
||||
@@ -595,7 +596,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
TexturePtr texture = value.toTexture();
|
||||
|
||||
// Set value to bound texture
|
||||
functions_->glUniform1i(variable_location, textures_to_bind.size());
|
||||
functions_->glUniform1i(variable_location,
|
||||
textures_to_bind.size());
|
||||
|
||||
texture_index_map.insert(it.key(), textures_to_bind.size());
|
||||
|
||||
@@ -605,8 +607,10 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
// Set enable flag if shader wants it
|
||||
GLuint tex_id = texture ? texture->id().value<GLuint>() : 0;
|
||||
int enable_param_location = functions_->glGetUniformLocation(
|
||||
shader,
|
||||
QStringLiteral("%1_enabled").arg(it.key()).toUtf8().constData());
|
||||
shader, QStringLiteral("%1_enabled")
|
||||
.arg(it.key())
|
||||
.toUtf8()
|
||||
.constData());
|
||||
if (enable_param_location > -1) {
|
||||
functions_->glUniform1i(enable_param_location, tex_id > 0);
|
||||
}
|
||||
@@ -637,8 +641,9 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
|
||||
functions_->glActiveTexture(GL_TEXTURE0 + i);
|
||||
|
||||
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D :
|
||||
GL_TEXTURE_2D;
|
||||
GLenum target = (texture && texture->params().is_3d()) ?
|
||||
GL_TEXTURE_3D :
|
||||
GL_TEXTURE_2D;
|
||||
functions_->glBindTexture(target, tex_id);
|
||||
|
||||
if (tex_id) {
|
||||
@@ -647,12 +652,12 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
if (texture->channel_count() == 1 &&
|
||||
destination_params.channel_count() != 1) {
|
||||
// Interpret this texture as a grayscale texture
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R,
|
||||
GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G,
|
||||
GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B,
|
||||
GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D,
|
||||
GL_TEXTURE_SWIZZLE_R, GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D,
|
||||
GL_TEXTURE_SWIZZLE_G, GL_RED);
|
||||
functions_->glTexParameteri(GL_TEXTURE_2D,
|
||||
GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -683,7 +688,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
if (!job.GetVertexCoordinates().isEmpty()) {
|
||||
Q_ASSERT(job.GetVertexCoordinates().size() == 18);
|
||||
vert_vbo_.allocate(job.GetVertexCoordinates().constData(),
|
||||
job.GetVertexCoordinates().size() * sizeof(float));
|
||||
job.GetVertexCoordinates().size() *
|
||||
sizeof(float));
|
||||
} else {
|
||||
vert_vbo_.allocate(blit_vertices.constData(),
|
||||
blit_vertices.size() * sizeof(GLfloat));
|
||||
@@ -707,12 +713,13 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
vert_vbo_.release();
|
||||
}
|
||||
|
||||
GLint tex_location = functions_->glGetAttribLocation(shader, "a_texcoord");
|
||||
GLint tex_location =
|
||||
functions_->glGetAttribLocation(shader, "a_texcoord");
|
||||
if (tex_location != -1) {
|
||||
frag_vbo_.bind();
|
||||
functions_->glEnableVertexAttribArray(tex_location);
|
||||
functions_->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE,
|
||||
0, nullptr);
|
||||
functions_->glVertexAttribPointer(tex_location, 2, GL_FLOAT,
|
||||
GL_FALSE, 0, nullptr);
|
||||
frag_vbo_.release();
|
||||
}
|
||||
|
||||
@@ -788,9 +795,9 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
// Blit this texture through this shader
|
||||
{
|
||||
PRINT_GL_ERRORS;
|
||||
functions_->glDrawArrays(GL_TRIANGLES, 0, blit_vertices.size() / 3);
|
||||
functions_->glDrawArrays(GL_TRIANGLES, 0,
|
||||
blit_vertices.size() / 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (destination) {
|
||||
@@ -801,8 +808,9 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
// Release any textures we bound before
|
||||
for (int i = textures_to_bind.size() - 1; i >= 0; i--) {
|
||||
TexturePtr texture = textures_to_bind.at(i).texture;
|
||||
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D :
|
||||
GL_TEXTURE_2D;
|
||||
GLenum target = (texture && texture->params().is_3d()) ?
|
||||
GL_TEXTURE_3D :
|
||||
GL_TEXTURE_2D;
|
||||
functions_->glActiveTexture(GL_TEXTURE0 + i);
|
||||
functions_->glBindTexture(target, 0);
|
||||
}
|
||||
@@ -815,9 +823,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
|
||||
vert_vbo_.destroy();
|
||||
vao_.release();
|
||||
vao_.destroy();
|
||||
} catch (std::bad_cast e) {
|
||||
}
|
||||
catch (std::bad_cast e){}
|
||||
|
||||
}
|
||||
|
||||
GLint OpenGLRenderer::GetInternalFormat(PixelFormat format, int channel_layout)
|
||||
@@ -965,12 +972,12 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
|
||||
const int major = context_ ? context_->format().majorVersion() : 0;
|
||||
const int minor = context_ ? context_->format().minorVersion() : 0;
|
||||
const bool is_gles2 = is_gles && (major < 3);
|
||||
const QString gles_preamble = is_gles2
|
||||
? QStringLiteral("#version 100\n\n"
|
||||
"precision highp float;\n\n"
|
||||
"#define frag_color gl_FragColor\n")
|
||||
: QStringLiteral("#version 300 es\n\n"
|
||||
"precision highp float;\n\n");
|
||||
const QString gles_preamble =
|
||||
is_gles2 ? QStringLiteral("#version 100\n\n"
|
||||
"precision highp float;\n\n"
|
||||
"#define frag_color gl_FragColor\n") :
|
||||
QStringLiteral("#version 300 es\n\n"
|
||||
"precision highp float;\n\n");
|
||||
const QString desktop_preamble =
|
||||
// Use appropriate GL 3.2 shader header
|
||||
QStringLiteral("#version 150\n\n"
|
||||
@@ -991,7 +998,8 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
|
||||
|
||||
QString complete_code;
|
||||
if (base_code.startsWith(QStringLiteral("#version"))) {
|
||||
if (is_gles || !desktop_preamble.startsWith(QStringLiteral("#version"))) {
|
||||
if (is_gles ||
|
||||
!desktop_preamble.startsWith(QStringLiteral("#version"))) {
|
||||
int newline = base_code.indexOf('\n');
|
||||
if (newline >= 0) {
|
||||
complete_code = shader_preamble + base_code.mid(newline + 1);
|
||||
@@ -1007,18 +1015,22 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
|
||||
|
||||
if (is_gles2) {
|
||||
if (type == GL_VERTEX_SHADER) {
|
||||
complete_code.replace(QRegularExpression(QStringLiteral("\\bin\\b")),
|
||||
QStringLiteral("attribute"));
|
||||
complete_code.replace(QRegularExpression(QStringLiteral("\\bout\\b")),
|
||||
QStringLiteral("varying"));
|
||||
complete_code.replace(
|
||||
QRegularExpression(QStringLiteral("\\bin\\b")),
|
||||
QStringLiteral("attribute"));
|
||||
complete_code.replace(
|
||||
QRegularExpression(QStringLiteral("\\bout\\b")),
|
||||
QStringLiteral("varying"));
|
||||
} else if (type == GL_FRAGMENT_SHADER) {
|
||||
complete_code.replace(QRegularExpression(QStringLiteral("\\bin\\b")),
|
||||
QStringLiteral("varying"));
|
||||
complete_code.replace(QRegularExpression(
|
||||
QStringLiteral("\\bout\\s+vec4\\s+frag_color\\s*;")),
|
||||
complete_code.replace(
|
||||
QRegularExpression(QStringLiteral("\\bin\\b")),
|
||||
QStringLiteral("varying"));
|
||||
complete_code.replace(QRegularExpression(QStringLiteral(
|
||||
"\\bout\\s+vec4\\s+frag_color\\s*;")),
|
||||
QStringLiteral("// frag_color output"));
|
||||
complete_code.replace(QRegularExpression(QStringLiteral("\\btexture\\b")),
|
||||
QStringLiteral("texture2D"));
|
||||
complete_code.replace(
|
||||
QRegularExpression(QStringLiteral("\\btexture\\b")),
|
||||
QStringLiteral("texture2D"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,7 +1070,8 @@ bool OpenGLRenderer::EnsureContextCurrent(const char *caller)
|
||||
// paint code can receive textures produced by a render-thread OpenGL
|
||||
// renderer, so guard here before makeCurrent() can crash inside Qt/GL.
|
||||
if (context_->thread() != QThread::currentThread()) {
|
||||
qWarning() << caller << "called from the wrong thread for this OpenGL context";
|
||||
qWarning()
|
||||
<< caller << "called from the wrong thread for this OpenGL context";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
bool EnsureContextCurrent(const char *caller);
|
||||
|
||||
protected:
|
||||
virtual void Blit(QVariant shader, olive::AcceleratedJob& job,
|
||||
virtual void Blit(QVariant shader, olive::AcceleratedJob &job,
|
||||
olive::Texture *destination,
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination) override;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,8 +31,10 @@
|
||||
|
||||
namespace olive
|
||||
{
|
||||
namespace plugin{
|
||||
namespace detail {
|
||||
namespace plugin
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
// 作用:将字节行跨度转换为像素跨度,便于纹理读写。
|
||||
// Purpose: Convert byte stride to pixel stride for texture I/O.
|
||||
int BytesToPixels(int byte_linesize, const olive::VideoParams ¶ms);
|
||||
@@ -46,9 +48,15 @@ int BytesToPixels(int byte_linesize, const olive::VideoParams ¶ms);
|
||||
class PluginRenderer : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PluginRenderer(olive::Renderer *renderer, QObject *parent = nullptr)
|
||||
: QObject(parent), renderer_(renderer) {}
|
||||
virtual ~PluginRenderer() override {}
|
||||
explicit PluginRenderer(olive::Renderer *renderer,
|
||||
QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
, renderer_(renderer)
|
||||
{
|
||||
}
|
||||
virtual ~PluginRenderer() override
|
||||
{
|
||||
}
|
||||
|
||||
olive::Renderer *renderer() const
|
||||
{
|
||||
@@ -63,7 +71,7 @@ public:
|
||||
void DetachOutputTexture();
|
||||
// 作用:执行插件渲染流程(参数配置、输入/输出、调用渲染动作)。
|
||||
// Purpose: Execute plugin render flow (params, inputs/outputs, render actions).
|
||||
void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
|
||||
void RenderPlugin(TexturePtr src, olive::plugin::PluginJob &job,
|
||||
olive::TexturePtr destination,
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination, bool interactive);
|
||||
@@ -74,6 +82,4 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif //PLUGINRENDERER_H
|
||||
|
||||
@@ -561,7 +561,8 @@ void PreviewAutoCacher::TryRender()
|
||||
video_immediate_passthroughs_[watcher].append(t);
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Failed to find copied node for SFR ticket, requeueing";
|
||||
qWarning()
|
||||
<< "Failed to find copied node for SFR ticket, requeueing";
|
||||
single_frame_render_ = t;
|
||||
if (!delayed_requeue_timer_.isActive()) {
|
||||
delayed_requeue_timer_.start();
|
||||
@@ -594,7 +595,8 @@ void PreviewAutoCacher::TryRender()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Failed to find node copy for video job, retrying";
|
||||
qWarning()
|
||||
<< "Failed to find node copy for video job, retrying";
|
||||
if (!delayed_requeue_timer_.isActive()) {
|
||||
delayed_requeue_timer_.start();
|
||||
}
|
||||
@@ -636,7 +638,8 @@ void PreviewAutoCacher::TryRender()
|
||||
|
||||
RenderAudio(copy, d.context, use_range, d.cache);
|
||||
} else {
|
||||
qWarning() << "Failed to find node copy for audio job, retrying";
|
||||
qWarning()
|
||||
<< "Failed to find node copy for audio job, retrying";
|
||||
pop = false;
|
||||
if (!delayed_requeue_timer_.isActive()) {
|
||||
delayed_requeue_timer_.start();
|
||||
|
||||
@@ -265,7 +265,9 @@ void ProjectCopier::InsertIntoCopyMap(Node *node, Node *copy)
|
||||
if (Footage *src_footage = dynamic_cast<Footage *>(node)) {
|
||||
if (dynamic_cast<Footage *>(copy)) {
|
||||
connect(src_footage, &Footage::ProxySettingsChanged, this,
|
||||
[this, src_footage]() { SyncFootageProxySettings(src_footage); });
|
||||
[this, src_footage]() {
|
||||
SyncFootageProxySettings(src_footage);
|
||||
});
|
||||
SyncFootageProxySettings(src_footage);
|
||||
}
|
||||
}
|
||||
@@ -279,14 +281,15 @@ void ProjectCopier::SyncFootageProxySettings(Footage *source)
|
||||
Footage *copy = GetCopy(source);
|
||||
if (!copy) {
|
||||
qWarning() << "ProjectCopier::SyncFootageProxySettings: no copy for"
|
||||
<< source->filename();
|
||||
<< source->filename();
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "ProjectCopier::SyncFootageProxySettings:" << source->filename()
|
||||
<< "enabled=" << source->proxy_enabled() << "->"
|
||||
<< copy->proxy_enabled() << "state="
|
||||
<< ProxyManager::ProxyStateToString(source->proxy_state());
|
||||
qDebug()
|
||||
<< "ProjectCopier::SyncFootageProxySettings:" << source->filename()
|
||||
<< "enabled=" << source->proxy_enabled() << "->"
|
||||
<< copy->proxy_enabled()
|
||||
<< "state=" << ProxyManager::ProxyStateToString(source->proxy_state());
|
||||
|
||||
copy->SetProxy(source->proxy_path(), source->proxy_state(),
|
||||
source->proxy_video_stream_index(),
|
||||
|
||||
+12
-10
@@ -34,7 +34,8 @@
|
||||
#include "texture.h"
|
||||
|
||||
// Forward declarations to keep the render core header lightweight
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
class ColorTransformJob;
|
||||
class Node;
|
||||
}
|
||||
@@ -57,17 +58,16 @@ public:
|
||||
|
||||
void DestroyTexture(Texture *texture);
|
||||
|
||||
virtual void BlitToTexture(QVariant shader, olive::AcceleratedJob& job,
|
||||
olive::Texture *destination,
|
||||
bool clear_destination = true)
|
||||
virtual void BlitToTexture(QVariant shader, olive::AcceleratedJob &job,
|
||||
olive::Texture *destination,
|
||||
bool clear_destination = true)
|
||||
{
|
||||
Blit(shader, job, destination, destination->params(),
|
||||
clear_destination);
|
||||
|
||||
}
|
||||
|
||||
void Blit(QVariant shader, olive::AcceleratedJob& job, olive::VideoParams params,
|
||||
bool clear_destination = true)
|
||||
void Blit(QVariant shader, olive::AcceleratedJob &job,
|
||||
olive::VideoParams params, bool clear_destination = true)
|
||||
{
|
||||
Blit(shader, job, nullptr, params, clear_destination);
|
||||
}
|
||||
@@ -147,10 +147,12 @@ public:
|
||||
*
|
||||
* Default implementation is a no-op.
|
||||
*/
|
||||
virtual void DetachOutputTexture() {}
|
||||
virtual void DetachOutputTexture()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Blit(QVariant shader, olive::AcceleratedJob& job,
|
||||
virtual void Blit(QVariant shader, olive::AcceleratedJob &job,
|
||||
olive::Texture *destination,
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination) = 0;
|
||||
@@ -164,7 +166,7 @@ protected:
|
||||
virtual void DestroyInternal() = 0;
|
||||
|
||||
private:
|
||||
std::atomic<bool> destroyed_{false};
|
||||
std::atomic<bool> destroyed_{ false };
|
||||
std::shared_ptr<RendererLifetime> lifetime_;
|
||||
struct ColorContext {
|
||||
struct LUT {
|
||||
|
||||
@@ -78,24 +78,26 @@ QString RenderManager::BackendToString(Backend backend)
|
||||
}
|
||||
|
||||
RenderManager::RenderManager(QObject *parent)
|
||||
: backend_(BackendFromString(
|
||||
OLIVE_CONFIG("GraphicsBackend").toString()))
|
||||
: backend_(BackendFromString(OLIVE_CONFIG("GraphicsBackend").toString()))
|
||||
, requested_backend_(backend_)
|
||||
, aggressive_gc_(0)
|
||||
, worker_pool_(nullptr)
|
||||
{
|
||||
if (backend_ == kVulkan) {
|
||||
#ifndef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
|
||||
qWarning() << "Vulkan backend requested but dynamic render backend is not enabled. Falling back to OpenGL.";
|
||||
qWarning()
|
||||
<< "Vulkan backend requested but dynamic render backend is not enabled. Falling back to OpenGL.";
|
||||
backend_ = kOpenGL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (backend_ == kOpenGL || backend_ == kVulkan) {
|
||||
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
|
||||
auto *dynamic_renderer = 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_)
|
||||
qWarning() << "Failed to load dynamic render backend"
|
||||
<< BackendToString(requested_backend_)
|
||||
<< ", falling back to OpenGL";
|
||||
delete dynamic_renderer;
|
||||
backend_ = kOpenGL;
|
||||
@@ -104,7 +106,8 @@ RenderManager::RenderManager(QObject *parent)
|
||||
context_ = dynamic_renderer;
|
||||
// DynamicRenderer may internally fall back (e.g. Vulkan -> OpenGL).
|
||||
// Synchronize RenderManager's view of the actual runtime backend.
|
||||
Backend actual_backend = BackendFromString(dynamic_renderer->backend_name());
|
||||
Backend actual_backend =
|
||||
BackendFromString(dynamic_renderer->backend_name());
|
||||
if (actual_backend != backend_) {
|
||||
qWarning() << "Dynamic render backend fell back from"
|
||||
<< BackendToString(backend_) << "to"
|
||||
@@ -218,11 +221,13 @@ RenderTicketPtr RenderManager::RenderFrame(const RenderVideoParams ¶ms)
|
||||
|
||||
if (worker_params.return_type == ReturnType::kNull) {
|
||||
dry_run_thread_->AddTicket(ticket);
|
||||
} else if (worker_pool_ && worker_pool_->SubmitFrame(ticket, worker_params)) {
|
||||
} else if (worker_pool_ &&
|
||||
worker_pool_->SubmitFrame(ticket, worker_params)) {
|
||||
return ticket;
|
||||
} else {
|
||||
qWarning() << "RenderManager: worker pool unavailable, finishing ticket "
|
||||
"without result";
|
||||
qWarning()
|
||||
<< "RenderManager: worker pool unavailable, finishing ticket "
|
||||
"without result";
|
||||
ticket->Finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
|
||||
#include "audio/audioprocessor.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
@@ -159,7 +158,6 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture,
|
||||
QString::fromUtf8(output_color_transform->id()));
|
||||
frame->set_video_params(display_params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return frame;
|
||||
@@ -173,7 +171,7 @@ void RenderProcessor::Run()
|
||||
|
||||
SetCancelPointer(ticket_->GetCancelAtom());
|
||||
|
||||
VideoParams params=ticket_->property("vparam").value<VideoParams>();
|
||||
VideoParams params = ticket_->property("vparam").value<VideoParams>();
|
||||
params.set_format(PixelFormat::F32);
|
||||
SetCacheVideoParams(params);
|
||||
SetCacheAudioParams(ticket_->property("aparam").value<AudioParams>());
|
||||
@@ -416,27 +414,29 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
|
||||
}
|
||||
|
||||
if (using_colorspace.isEmpty()) {
|
||||
qWarning() << "RenderProcessor ProcessVideoFootage: no input colorspace available";
|
||||
qWarning()
|
||||
<< "RenderProcessor ProcessVideoFootage: no input colorspace available";
|
||||
}
|
||||
|
||||
auto blit_color_managed = [&](const TexturePtr &unmanaged_texture,
|
||||
const VideoParams &texture_params) {
|
||||
const VideoParams &texture_params) {
|
||||
if (!render_ctx_ || !unmanaged_texture || IsCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We convert to our rendering pixel format, since that will always be float-based which
|
||||
// is necessary for correct color conversion
|
||||
ColorProcessorPtr processor = ColorProcessor::Create(
|
||||
color_manager, using_colorspace,
|
||||
color_manager->GetReferenceColorSpace());
|
||||
ColorProcessorPtr processor =
|
||||
ColorProcessor::Create(color_manager, using_colorspace,
|
||||
color_manager->GetReferenceColorSpace());
|
||||
|
||||
ColorTransformJob job;
|
||||
job.SetColorProcessor(processor);
|
||||
job.SetInputTexture(unmanaged_texture);
|
||||
|
||||
if (texture_params.channel_count() != VideoParams::kRGBAChannelCount ||
|
||||
texture_params.colorspace() == color_manager->GetReferenceColorSpace()) {
|
||||
texture_params.colorspace() ==
|
||||
color_manager->GetReferenceColorSpace()) {
|
||||
job.SetInputAlphaAssociation(kAlphaNone);
|
||||
} else if (texture_params.premultiplied_alpha()) {
|
||||
job.SetInputAlphaAssociation(kAlphaAssociated);
|
||||
@@ -450,12 +450,14 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
|
||||
render_ctx_->Flush();
|
||||
};
|
||||
|
||||
auto *input_pool =
|
||||
QtUtils::ValueToPtr<ipc::FrameSlotPool>(ticket_->property("ipc_input_pool"));
|
||||
auto *input_pool = QtUtils::ValueToPtr<ipc::FrameSlotPool>(
|
||||
ticket_->property("ipc_input_pool"));
|
||||
int input_slot = -1;
|
||||
const QVariantList input_slots = ticket_->property("ipc_input_slots").toList();
|
||||
const QVariantList input_slots =
|
||||
ticket_->property("ipc_input_slots").toList();
|
||||
if (!input_slots.isEmpty()) {
|
||||
const QVariant cursor_value = ticket_->property("ipc_input_slot_cursor");
|
||||
const QVariant cursor_value =
|
||||
ticket_->property("ipc_input_slot_cursor");
|
||||
const int cursor = cursor_value.isValid() ? cursor_value.toInt() : 0;
|
||||
if (cursor >= 0 && cursor < input_slots.size()) {
|
||||
input_slot = input_slots.at(cursor).toInt();
|
||||
@@ -467,25 +469,26 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
|
||||
}
|
||||
if (render_ctx_ && input_pool && input_slot >= 0) {
|
||||
if (input_slot >= int(input_pool->slot_count())) {
|
||||
qWarning() << "RenderProcessor received out-of-range IPC input frame slot"
|
||||
<< input_slot;
|
||||
qWarning()
|
||||
<< "RenderProcessor received out-of-range IPC input frame slot"
|
||||
<< input_slot;
|
||||
return;
|
||||
}
|
||||
|
||||
const ipc::FrameSlotMeta *meta = input_pool->Meta(uint32_t(input_slot));
|
||||
if (meta && meta->width > 0 && meta->height > 0 && meta->data_size > 0 &&
|
||||
if (meta && meta->width > 0 && meta->height > 0 &&
|
||||
meta->data_size > 0 &&
|
||||
meta->data_size <= int(input_pool->slot_data_bytes())) {
|
||||
VideoParams input_params = stream_data;
|
||||
input_params.set_width(meta->width);
|
||||
input_params.set_height(meta->height);
|
||||
input_params.set_format(PixelFormat::Format(meta->format));
|
||||
input_params.set_channel_count(meta->channel_count);
|
||||
// The decoder may leave depth at 0 for 2D frames, but the renderer
|
||||
// needs depth >= 1 to compute image size and upload the texture.
|
||||
if (input_params.depth() <= 0) {
|
||||
input_params.set_depth(1);
|
||||
}
|
||||
|
||||
// The decoder may leave depth at 0 for 2D frames, but the renderer
|
||||
// needs depth >= 1 to compute image size and upload the texture.
|
||||
if (input_params.depth() <= 0) {
|
||||
input_params.set_depth(1);
|
||||
}
|
||||
|
||||
// Prefer the colorspace that the main process used when decoding this
|
||||
// frame. The FootageJob reconstructed in the worker may have stale or
|
||||
@@ -498,9 +501,9 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
|
||||
}
|
||||
|
||||
const int bytes_per_pixel = input_params.GetBytesPerPixel();
|
||||
const int linesize_pixels = bytes_per_pixel > 0
|
||||
? meta->linesize / bytes_per_pixel
|
||||
: input_params.effective_width();
|
||||
const int linesize_pixels = bytes_per_pixel > 0 ?
|
||||
meta->linesize / bytes_per_pixel :
|
||||
input_params.effective_width();
|
||||
|
||||
const void *slot_data = input_pool->SlotData(uint32_t(input_slot));
|
||||
TexturePtr unmanaged_texture = render_ctx_->CreateTexture(
|
||||
@@ -509,13 +512,15 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
|
||||
blit_color_managed(unmanaged_texture, input_params);
|
||||
return;
|
||||
}
|
||||
qWarning() << "RenderProcessor received invalid IPC input frame slot" << input_slot;
|
||||
qWarning() << "RenderProcessor received invalid IPC input frame slot"
|
||||
<< input_slot;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decoder_cache_) {
|
||||
qWarning() << "RenderProcessor has no decoder cache or IPC input frame for"
|
||||
<< stream->filename();
|
||||
qWarning()
|
||||
<< "RenderProcessor has no decoder cache or IPC input frame for"
|
||||
<< stream->filename();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -523,15 +528,15 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
|
||||
static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()) ==
|
||||
RenderMode::kOffline &&
|
||||
stream->has_proxy() && QFileInfo::exists(stream->proxy_filename());
|
||||
const QString decode_filename =
|
||||
use_proxy ? stream->proxy_filename() : stream->filename();
|
||||
const QString decoder_id =
|
||||
use_proxy ? stream->proxy_decoder() : stream->decoder();
|
||||
const int stream_index =
|
||||
use_proxy ? stream->proxy_stream_index() : stream_data.stream_index();
|
||||
const QString decode_filename = use_proxy ? stream->proxy_filename() :
|
||||
stream->filename();
|
||||
const QString decoder_id = use_proxy ? stream->proxy_decoder() :
|
||||
stream->decoder();
|
||||
const int stream_index = use_proxy ? stream->proxy_stream_index() :
|
||||
stream_data.stream_index();
|
||||
|
||||
Decoder::CodecStream default_codec_stream(
|
||||
decode_filename, stream_index, GetCurrentBlock());
|
||||
Decoder::CodecStream default_codec_stream(decode_filename, stream_index,
|
||||
GetCurrentBlock());
|
||||
|
||||
DecoderPtr decoder = nullptr;
|
||||
|
||||
@@ -553,8 +558,8 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
|
||||
decode_filename, frame_number);
|
||||
|
||||
// Decoder will close automatically since it's a stream_ptr
|
||||
decoder->Open(Decoder::CodecStream(
|
||||
frame_filename, stream_index, GetCurrentBlock()));
|
||||
decoder->Open(Decoder::CodecStream(frame_filename, stream_index,
|
||||
GetCurrentBlock()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -643,7 +648,8 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node,
|
||||
locker.unlock();
|
||||
|
||||
// Run shader
|
||||
render_ctx_->BlitToTexture(shader, const_cast<ShaderJob&>(*job), destination.get());
|
||||
render_ctx_->BlitToTexture(shader, const_cast<ShaderJob &>(*job),
|
||||
destination.get());
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessSamples(SampleBuffer &destination,
|
||||
@@ -720,8 +726,7 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
|
||||
return destination;
|
||||
}
|
||||
|
||||
auto *plugin_job =
|
||||
dynamic_cast<plugin::PluginJob *>(texture->job());
|
||||
auto *plugin_job = dynamic_cast<plugin::PluginJob *>(texture->job());
|
||||
if (!plugin_job) {
|
||||
return destination;
|
||||
}
|
||||
@@ -779,13 +784,8 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
|
||||
}
|
||||
}
|
||||
|
||||
plugin_renderer.RenderPlugin(
|
||||
src,
|
||||
*plugin_job,
|
||||
destination,
|
||||
destination->params(),
|
||||
true,
|
||||
false);
|
||||
plugin_renderer.RenderPlugin(src, *plugin_job, destination,
|
||||
destination->params(), true, false);
|
||||
|
||||
return destination;
|
||||
}
|
||||
@@ -797,7 +797,8 @@ TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob *val)
|
||||
// Auto-detect and discard black/empty cached frames (macOS TBDR artifact)
|
||||
bool all_black = true;
|
||||
if (frame->data() && frame->allocated_size() > 0) {
|
||||
const uint8_t *pixels = reinterpret_cast<const uint8_t *>(frame->data());
|
||||
const uint8_t *pixels =
|
||||
reinterpret_cast<const uint8_t *>(frame->data());
|
||||
size_t alloc_size = static_cast<size_t>(frame->allocated_size());
|
||||
size_t check_bytes = std::min(alloc_size, size_t(4096));
|
||||
for (size_t i = 0; i < check_bytes; ++i) {
|
||||
@@ -808,7 +809,8 @@ TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob *val)
|
||||
}
|
||||
}
|
||||
if (all_black) {
|
||||
qWarning() << "[CACHE] Discarding black cached frame:" << val->GetFilename()
|
||||
qWarning() << "[CACHE] Discarding black cached frame:"
|
||||
<< val->GetFilename()
|
||||
<< "time=" << frame->timestamp().toDouble()
|
||||
<< "size=" << frame->allocated_size();
|
||||
QFile::remove(val->GetFilename());
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
namespace olive
|
||||
{
|
||||
|
||||
namespace plugin {
|
||||
namespace plugin
|
||||
{
|
||||
class PluginRenderer;
|
||||
}
|
||||
|
||||
|
||||
+152
-142
@@ -62,8 +62,8 @@ struct FootageInput {
|
||||
|
||||
class FootageInputCollector : public NodeTraverser {
|
||||
public:
|
||||
QVector<FootageInput> Collect(const RenderManager::RenderVideoParams ¶ms,
|
||||
CancelAtom *cancel)
|
||||
QVector<FootageInput>
|
||||
Collect(const RenderManager::RenderVideoParams ¶ms, CancelAtom *cancel)
|
||||
{
|
||||
SetCancelPointer(cancel);
|
||||
VideoParams cache_params = params.video_params;
|
||||
@@ -75,17 +75,15 @@ public:
|
||||
if (cache_params.interlacing() != VideoParams::kInterlaceNone) {
|
||||
frame_length /= 2;
|
||||
}
|
||||
NodeValueTable table = GenerateTable(params.node,
|
||||
TimeRange(params.time,
|
||||
params.time + frame_length));
|
||||
NodeValueTable table = GenerateTable(
|
||||
params.node, TimeRange(params.time, params.time + frame_length));
|
||||
NodeValue texture = table.Get(NodeValue::kTexture);
|
||||
ResolveJobs(texture);
|
||||
|
||||
if (cache_params.interlacing() != VideoParams::kInterlaceNone) {
|
||||
NodeValueTable second_table =
|
||||
GenerateTable(params.node,
|
||||
TimeRange(params.time + frame_length,
|
||||
params.time + frame_length * 2));
|
||||
NodeValueTable second_table = GenerateTable(
|
||||
params.node, TimeRange(params.time + frame_length,
|
||||
params.time + frame_length * 2));
|
||||
NodeValue second_texture = second_table.Get(NodeValue::kTexture);
|
||||
ResolveJobs(second_texture);
|
||||
}
|
||||
@@ -94,13 +92,12 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
void ProcessVideoFootage(TexturePtr destination,
|
||||
const FootageJob *stream,
|
||||
void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream,
|
||||
const rational &input_time) override
|
||||
{
|
||||
Q_UNUSED(destination)
|
||||
if (stream) {
|
||||
inputs_.append({*stream, input_time});
|
||||
inputs_.append({ *stream, input_time });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +137,7 @@ DecoderPtr ResolveDecoderFromCache(DecoderCache *decoder_cache,
|
||||
}
|
||||
|
||||
FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
|
||||
const FootageInput &input,
|
||||
CancelAtom *cancel)
|
||||
const FootageInput &input, CancelAtom *cancel)
|
||||
{
|
||||
VideoParams stream_data = input.job.video_params();
|
||||
QString filename = input.job.filename();
|
||||
@@ -162,19 +158,17 @@ FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
|
||||
case VideoParams::kVideoTypeVideo:
|
||||
case VideoParams::kVideoTypeStill:
|
||||
decoder = ResolveDecoderFromCache(
|
||||
decoder_cache,
|
||||
decoder_id,
|
||||
decoder_cache, decoder_id,
|
||||
Decoder::CodecStream(filename, stream_index, nullptr));
|
||||
break;
|
||||
case VideoParams::kVideoTypeImageSequence: {
|
||||
const int64_t frame_number =
|
||||
stream_data.get_time_in_timebase_units(input.time);
|
||||
filename = Decoder::TransformImageSequenceFileName(filename, frame_number);
|
||||
filename =
|
||||
Decoder::TransformImageSequenceFileName(filename, frame_number);
|
||||
decoder = Decoder::CreateFromID(decoder_id);
|
||||
if (decoder &&
|
||||
!decoder->Open(Decoder::CodecStream(filename,
|
||||
stream_index,
|
||||
nullptr))) {
|
||||
if (decoder && !decoder->Open(Decoder::CodecStream(
|
||||
filename, stream_index, nullptr))) {
|
||||
decoder = nullptr;
|
||||
}
|
||||
break;
|
||||
@@ -188,9 +182,9 @@ FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
|
||||
Decoder::RetrieveVideoParams retrieve;
|
||||
retrieve.divider = stream_data.divider();
|
||||
retrieve.maximum_format = PixelFormat::U16;
|
||||
retrieve.time = stream_data.video_type() == VideoParams::kVideoTypeVideo
|
||||
? input.time
|
||||
: Decoder::kAnyTimecode;
|
||||
retrieve.time = stream_data.video_type() == VideoParams::kVideoTypeVideo ?
|
||||
input.time :
|
||||
Decoder::kAnyTimecode;
|
||||
retrieve.cancelled = cancel;
|
||||
retrieve.force_range = stream_data.color_range();
|
||||
retrieve.src_interlacing = stream_data.interlacing();
|
||||
@@ -207,15 +201,13 @@ FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
|
||||
frame_params.set_colorspace(stream_data.colorspace());
|
||||
frame->set_video_params(frame_params);
|
||||
}
|
||||
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool DecodeInputFrames(DecoderCache *decoder_cache,
|
||||
const RenderManager::RenderVideoParams ¶ms,
|
||||
CancelAtom *cancel,
|
||||
QVector<FramePtr> *frames)
|
||||
CancelAtom *cancel, QVector<FramePtr> *frames)
|
||||
{
|
||||
frames->clear();
|
||||
|
||||
@@ -271,7 +263,8 @@ bool WriteControlMessage(QProcess *process, const QJsonObject &obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray line = QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
|
||||
const QByteArray line =
|
||||
QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
|
||||
const qint64 written = process->write(line);
|
||||
if (written != line.size()) {
|
||||
return false;
|
||||
@@ -285,7 +278,8 @@ void TryWriteControlMessage(QProcess *process, const QJsonObject &obj)
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray line = QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
|
||||
const QByteArray line =
|
||||
QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
|
||||
process->write(line);
|
||||
}
|
||||
|
||||
@@ -315,12 +309,14 @@ bool IsProcessAlive(qint64 process_id)
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, DWORD(process_id));
|
||||
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
|
||||
DWORD(process_id));
|
||||
if (!handle) {
|
||||
return false;
|
||||
}
|
||||
DWORD exit_code = 0;
|
||||
const bool alive = GetExitCodeProcess(handle, &exit_code) && exit_code == STILL_ACTIVE;
|
||||
const bool alive = GetExitCodeProcess(handle, &exit_code) &&
|
||||
exit_code == STILL_ACTIVE;
|
||||
CloseHandle(handle);
|
||||
return alive;
|
||||
#else
|
||||
@@ -334,11 +330,11 @@ QString WorkerProcessDetails(const QProcess *process)
|
||||
return QStringLiteral("worker process unavailable");
|
||||
}
|
||||
|
||||
const QString exit_status =
|
||||
process->exitStatus() == QProcess::CrashExit
|
||||
? QStringLiteral("crash")
|
||||
: QStringLiteral("normal");
|
||||
return QStringLiteral("state=%1 exit_status=%2 exit_code=%3 process_error=%4 error=\"%5\"")
|
||||
const QString exit_status = process->exitStatus() == QProcess::CrashExit ?
|
||||
QStringLiteral("crash") :
|
||||
QStringLiteral("normal");
|
||||
return QStringLiteral(
|
||||
"state=%1 exit_status=%2 exit_code=%3 process_error=%4 error=\"%5\"")
|
||||
.arg(int(process->state()))
|
||||
.arg(exit_status)
|
||||
.arg(process->exitCode())
|
||||
@@ -355,8 +351,9 @@ bool ReadControlMessage(QProcess *process, QJsonObject *out, QString *error,
|
||||
*error = QStringLiteral("worker exited before response: %1")
|
||||
.arg(WorkerProcessDetails(process));
|
||||
} else {
|
||||
*error = QStringLiteral("timeout waiting for worker response: %1")
|
||||
.arg(WorkerProcessDetails(process));
|
||||
*error =
|
||||
QStringLiteral("timeout waiting for worker response: %1")
|
||||
.arg(WorkerProcessDetails(process));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -372,7 +369,8 @@ bool ReadControlMessage(QProcess *process, QJsonObject *out, QString *error,
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(line, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||
if (error) {
|
||||
*error = QStringLiteral("worker emitted malformed control JSON");
|
||||
*error =
|
||||
QStringLiteral("worker emitted malformed control JSON");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -394,11 +392,10 @@ bool ReadControlMessage(QProcess *process, QJsonObject *out, QString *error,
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
RenderWorkerPool::RenderWorkerPool(DecoderCache *decoder_cache,
|
||||
const QString &gpu_backend,
|
||||
QObject *parent)
|
||||
const QString &gpu_backend, QObject *parent)
|
||||
: QThread(parent)
|
||||
, decoder_cache_(decoder_cache)
|
||||
, gpu_backend_(gpu_backend)
|
||||
@@ -410,8 +407,8 @@ RenderWorkerPool::~RenderWorkerPool()
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool RenderWorkerPool::SubmitFrame(RenderTicketPtr ticket,
|
||||
const RenderManager::RenderVideoParams ¶ms)
|
||||
bool RenderWorkerPool::SubmitFrame(
|
||||
RenderTicketPtr ticket, const RenderManager::RenderVideoParams ¶ms)
|
||||
{
|
||||
Job job(ticket, params);
|
||||
if (!PrepareJob(ticket, params, &job)) {
|
||||
@@ -505,13 +502,13 @@ void RenderWorkerPool::run()
|
||||
active_jobs_.resize(worker_count);
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::unique_ptr<PooledWorker>>> local_pools(worker_count);
|
||||
std::vector<std::vector<std::unique_ptr<PooledWorker>>> local_pools(
|
||||
worker_count);
|
||||
std::vector<std::thread> workers;
|
||||
workers.reserve(size_t(worker_count));
|
||||
for (int i = 0; i < worker_count; i++) {
|
||||
workers.emplace_back([this, i, &local_pools]() {
|
||||
WorkerLoop(i, &local_pools[i]);
|
||||
});
|
||||
workers.emplace_back(
|
||||
[this, i, &local_pools]() { WorkerLoop(i, &local_pools[i]); });
|
||||
}
|
||||
|
||||
for (std::thread &worker : workers) {
|
||||
@@ -529,8 +526,7 @@ void RenderWorkerPool::run()
|
||||
}
|
||||
|
||||
void RenderWorkerPool::WorkerLoop(
|
||||
int worker_index,
|
||||
std::vector<std::unique_ptr<PooledWorker>> *local_pool)
|
||||
int worker_index, std::vector<std::unique_ptr<PooledWorker>> *local_pool)
|
||||
{
|
||||
while (true) {
|
||||
mutex_.lock();
|
||||
@@ -561,7 +557,8 @@ bool RenderWorkerPool::PrepareJob(RenderTicketPtr ticket,
|
||||
|
||||
Project *project = Project::GetProjectFromObject(params.node);
|
||||
if (!project) {
|
||||
qWarning() << "RenderWorkerPool could not resolve project for render node";
|
||||
qWarning()
|
||||
<< "RenderWorkerPool could not resolve project for render node";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -582,12 +579,14 @@ bool RenderWorkerPool::PrepareJob(RenderTicketPtr ticket,
|
||||
if (it != graph_cache_.end() && !project->is_modified()) {
|
||||
graph_path = it->path;
|
||||
AddGraphPathRefLocked(graph_path);
|
||||
qDebug() << "RenderWorkerPool::PrepareJob: using cached graph snapshot"
|
||||
<< graph_path;
|
||||
qDebug()
|
||||
<< "RenderWorkerPool::PrepareJob: using cached graph snapshot"
|
||||
<< graph_path;
|
||||
} else {
|
||||
if (it != graph_cache_.end()) {
|
||||
qDebug() << "RenderWorkerPool::PrepareJob: graph stale, rewriting"
|
||||
<< project->is_modified();
|
||||
qDebug()
|
||||
<< "RenderWorkerPool::PrepareJob: graph stale, rewriting"
|
||||
<< project->is_modified();
|
||||
SetGraphPathCachedLocked(it->path, false);
|
||||
graph_cache_.erase(it);
|
||||
}
|
||||
@@ -603,7 +602,7 @@ bool RenderWorkerPool::PrepareJob(RenderTicketPtr ticket,
|
||||
project->set_modified(false);
|
||||
}
|
||||
locker.relock();
|
||||
graph_cache_.insert(project_uuid, {graph_path});
|
||||
graph_cache_.insert(project_uuid, { graph_path });
|
||||
SetGraphPathCachedLocked(graph_path, true);
|
||||
AddGraphPathRefLocked(graph_path);
|
||||
}
|
||||
@@ -625,34 +624,39 @@ bool RenderWorkerPool::WriteGraphSnapshot(Project *project, QString *path)
|
||||
// still referenced them.
|
||||
const QString graph_dir = QDir::tempPath();
|
||||
|
||||
QTemporaryFile file(QDir(graph_dir).filePath(QStringLiteral("oak-render-graph-XXXXXX.ove")));
|
||||
QTemporaryFile file(QDir(graph_dir).filePath(
|
||||
QStringLiteral("oak-render-graph-XXXXXX.ove")));
|
||||
file.setAutoRemove(false);
|
||||
if (!file.open()) {
|
||||
qWarning() << "RenderWorkerPool failed to create graph snapshot temp file"
|
||||
<< file.errorString();
|
||||
qWarning()
|
||||
<< "RenderWorkerPool failed to create graph snapshot temp file"
|
||||
<< file.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamWriter writer(&file);
|
||||
ProjectSerializer::SaveData data(ProjectSerializer::kProject, project, file.fileName());
|
||||
const ProjectSerializer::Result result = ProjectSerializer::Save(&writer, data);
|
||||
ProjectSerializer::SaveData data(ProjectSerializer::kProject, project,
|
||||
file.fileName());
|
||||
const ProjectSerializer::Result result =
|
||||
ProjectSerializer::Save(&writer, data);
|
||||
file.close();
|
||||
|
||||
if (result.code() != ProjectSerializer::kSuccess || writer.hasError()) {
|
||||
qWarning() << "RenderWorkerPool failed to serialize graph snapshot"
|
||||
<< result.GetDetails();
|
||||
<< result.GetDetails();
|
||||
QFile::remove(file.fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "RenderWorkerPool wrote graph snapshot" << file.fileName()
|
||||
<< "size" << QFileInfo(file.fileName()).size();
|
||||
<< "size" << QFileInfo(file.fileName()).size();
|
||||
|
||||
*path = file.fileName();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderWorkerPool::IsSupported(const RenderManager::RenderVideoParams ¶ms) const
|
||||
bool RenderWorkerPool::IsSupported(
|
||||
const RenderManager::RenderVideoParams ¶ms) const
|
||||
{
|
||||
return params.node && params.return_type == RenderManager::kFrame &&
|
||||
params.video_params.is_valid();
|
||||
@@ -662,7 +666,8 @@ void RenderWorkerPool::ProcessJob(
|
||||
const Job &job, int worker_index,
|
||||
std::vector<std::unique_ptr<PooledWorker>> *local_pool)
|
||||
{
|
||||
const qint64 ticket_id = qint64(reinterpret_cast<quintptr>(job.ticket.get()));
|
||||
const qint64 ticket_id =
|
||||
qint64(reinterpret_cast<quintptr>(job.ticket.get()));
|
||||
SetActiveWorker(worker_index, job.ticket, nullptr, ticket_id);
|
||||
|
||||
job.ticket->Start();
|
||||
@@ -672,7 +677,8 @@ void RenderWorkerPool::ProcessJob(
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<PooledWorker> worker = AcquireWorker(local_pool, job.graph_path);
|
||||
std::unique_ptr<PooledWorker> worker =
|
||||
AcquireWorker(local_pool, job.graph_path);
|
||||
if (!worker) {
|
||||
qWarning() << "RenderWorkerPool failed to acquire worker for ticket"
|
||||
<< ticket_id;
|
||||
@@ -685,22 +691,24 @@ void RenderWorkerPool::ProcessJob(
|
||||
if (attempt > 0) {
|
||||
worker = AcquireWorker(local_pool, job.graph_path);
|
||||
if (!worker) {
|
||||
qWarning() << "RenderWorkerPool failed to acquire worker for retry"
|
||||
<< ticket_id;
|
||||
qWarning()
|
||||
<< "RenderWorkerPool failed to acquire worker for retry"
|
||||
<< ticket_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const JobResult result = ProcessJobAttempt(job, worker_index, attempt,
|
||||
worker.get());
|
||||
const qint64 worker_pid = worker && worker->process
|
||||
? worker->process->processId()
|
||||
: 0;
|
||||
const JobResult result =
|
||||
ProcessJobAttempt(job, worker_index, attempt, worker.get());
|
||||
const qint64 worker_pid =
|
||||
worker && worker->process ? worker->process->processId() : 0;
|
||||
const bool process_state_running = worker && worker->process &&
|
||||
worker->process->state() == QProcess::Running;
|
||||
worker->process->state() ==
|
||||
QProcess::Running;
|
||||
const bool os_alive = worker_pid > 0 && IsProcessAlive(worker_pid);
|
||||
const bool worker_healthy = process_state_running || os_alive;
|
||||
const bool keep_alive = (result == JobResult::kFinished) && worker_healthy;
|
||||
const bool keep_alive = (result == JobResult::kFinished) &&
|
||||
worker_healthy;
|
||||
|
||||
ReturnWorker(local_pool, std::move(worker), keep_alive);
|
||||
worker.reset();
|
||||
@@ -733,11 +741,12 @@ void RenderWorkerPool::ProcessJob(
|
||||
ClearActiveWorker(worker_index, 0);
|
||||
}
|
||||
|
||||
RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
const Job &job, int worker_index, int attempt_index,
|
||||
PooledWorker *worker)
|
||||
RenderWorkerPool::JobResult
|
||||
RenderWorkerPool::ProcessJobAttempt(const Job &job, int worker_index,
|
||||
int attempt_index, PooledWorker *worker)
|
||||
{
|
||||
const qint64 ticket_id = qint64(reinterpret_cast<quintptr>(job.ticket.get()));
|
||||
const qint64 ticket_id =
|
||||
qint64(reinterpret_cast<quintptr>(job.ticket.get()));
|
||||
if (job.ticket->IsCancelled()) {
|
||||
return JobResult::kCancelled;
|
||||
}
|
||||
@@ -748,27 +757,25 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
|
||||
const qint64 worker_process_id = worker->process->processId();
|
||||
|
||||
const int output_width = job.params.force_size.width() > 0
|
||||
? job.params.force_size.width()
|
||||
: job.params.video_params.effective_width();
|
||||
const int output_height = job.params.force_size.height() > 0
|
||||
? job.params.force_size.height()
|
||||
: job.params.video_params.effective_height();
|
||||
const int output_width = job.params.force_size.width() > 0 ?
|
||||
job.params.force_size.width() :
|
||||
job.params.video_params.effective_width();
|
||||
const int output_height = job.params.force_size.height() > 0 ?
|
||||
job.params.force_size.height() :
|
||||
job.params.video_params.effective_height();
|
||||
const PixelFormat::Format output_format =
|
||||
job.params.force_format != PixelFormat::INVALID
|
||||
? PixelFormat::Format(job.params.force_format)
|
||||
: PixelFormat::F32;
|
||||
const int output_channels = job.params.force_channel_count > 0
|
||||
? job.params.force_channel_count
|
||||
: VideoParams::kRGBAChannelCount;
|
||||
const int output_linesize =
|
||||
Frame::generate_linesize_bytes(output_width, output_format,
|
||||
output_channels);
|
||||
job.params.force_format != PixelFormat::INVALID ?
|
||||
PixelFormat::Format(job.params.force_format) :
|
||||
PixelFormat::F32;
|
||||
const int output_channels = job.params.force_channel_count > 0 ?
|
||||
job.params.force_channel_count :
|
||||
VideoParams::kRGBAChannelCount;
|
||||
const int output_linesize = Frame::generate_linesize_bytes(
|
||||
output_width, output_format, output_channels);
|
||||
const size_t estimated_output_slot_bytes =
|
||||
size_t(output_linesize) * size_t(output_height);
|
||||
const int f32_rgba_linesize =
|
||||
Frame::generate_linesize_bytes(output_width, PixelFormat::F32,
|
||||
VideoParams::kRGBAChannelCount);
|
||||
const int f32_rgba_linesize = Frame::generate_linesize_bytes(
|
||||
output_width, PixelFormat::F32, VideoParams::kRGBAChannelCount);
|
||||
const size_t f32_rgba_slot_bytes =
|
||||
size_t(f32_rgba_linesize) * size_t(output_height);
|
||||
const size_t output_slot_bytes =
|
||||
@@ -795,10 +802,11 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
QStringLiteral("-out");
|
||||
}
|
||||
if (!worker->output_region.Open(worker->output_shm_key,
|
||||
output_region_bytes,
|
||||
ipc::SharedMemoryRegion::kCreate)) {
|
||||
qWarning() << "RenderWorkerPool failed to create output shared memory"
|
||||
<< worker->output_region.error();
|
||||
output_region_bytes,
|
||||
ipc::SharedMemoryRegion::kCreate)) {
|
||||
qWarning()
|
||||
<< "RenderWorkerPool failed to create output shared memory"
|
||||
<< worker->output_region.error();
|
||||
return JobResult::kFatalFailure;
|
||||
}
|
||||
worker->output_pool = ipc::FrameSlotPool::Create(
|
||||
@@ -823,17 +831,19 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
ipc::SharedMemoryRegion::MakeKey(worker_process_id, 1) +
|
||||
QStringLiteral("-in");
|
||||
}
|
||||
const size_t input_region_bytes =
|
||||
ipc::FrameSlotPool::BytesNeeded(input_slot_count, input_slot_bytes);
|
||||
const size_t input_region_bytes = ipc::FrameSlotPool::BytesNeeded(
|
||||
input_slot_count, input_slot_bytes);
|
||||
if (!worker->input_region.Open(worker->input_shm_key,
|
||||
input_region_bytes,
|
||||
ipc::SharedMemoryRegion::kCreate)) {
|
||||
qWarning() << "RenderWorkerPool failed to create input shared memory"
|
||||
<< worker->input_region.error();
|
||||
input_region_bytes,
|
||||
ipc::SharedMemoryRegion::kCreate)) {
|
||||
qWarning()
|
||||
<< "RenderWorkerPool failed to create input shared memory"
|
||||
<< worker->input_region.error();
|
||||
return JobResult::kFatalFailure;
|
||||
}
|
||||
worker->input_pool = ipc::FrameSlotPool::Create(
|
||||
worker->input_region.data(), input_slot_count, input_slot_bytes);
|
||||
worker->input_pool =
|
||||
ipc::FrameSlotPool::Create(worker->input_region.data(),
|
||||
input_slot_count, input_slot_bytes);
|
||||
worker->input_slot_bytes = input_slot_bytes;
|
||||
}
|
||||
}
|
||||
@@ -843,7 +853,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
if (input_slot_count > 0) {
|
||||
for (const FramePtr &frame : job.input_frames) {
|
||||
if (frame->allocated_size() > int(worker->input_slot_bytes)) {
|
||||
qWarning() << "RenderWorkerPool decoded input frame exceeds slot size";
|
||||
qWarning()
|
||||
<< "RenderWorkerPool decoded input frame exceeds slot size";
|
||||
return JobResult::kFatalFailure;
|
||||
}
|
||||
|
||||
@@ -869,9 +880,9 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
const QString cs = frame->video_params().colorspace();
|
||||
if (!cs.isEmpty()) {
|
||||
const QByteArray cs_utf8 = cs.toUtf8();
|
||||
const size_t copy_len = qMin(
|
||||
static_cast<size_t>(cs_utf8.size()),
|
||||
sizeof(meta->colorspace) - 1);
|
||||
const size_t copy_len =
|
||||
qMin(static_cast<size_t>(cs_utf8.size()),
|
||||
sizeof(meta->colorspace) - 1);
|
||||
memcpy(meta->colorspace, cs_utf8.constData(), copy_len);
|
||||
meta->colorspace[copy_len] = '\0';
|
||||
}
|
||||
@@ -905,16 +916,16 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
handshake.input_slots = input_slots.size();
|
||||
handshake.output_slots = int(kOutputSlots);
|
||||
handshake.slot_data_bytes = qint64(output_slot_bytes);
|
||||
handshake.input_slot_data_bytes = input_slots.isEmpty()
|
||||
? 0
|
||||
: qint64(input_slot_bytes);
|
||||
handshake.input_slot_data_bytes =
|
||||
input_slots.isEmpty() ? 0 : qint64(input_slot_bytes);
|
||||
if (!WriteControlMessage(worker->process, handshake.ToJson())) {
|
||||
if (!job.ticket->IsCancelled()) {
|
||||
qWarning() << "RenderWorkerPool failed to send shared-memory handshake";
|
||||
qWarning()
|
||||
<< "RenderWorkerPool failed to send shared-memory handshake";
|
||||
}
|
||||
ClearActiveWorker(worker_index, worker_process_id);
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled
|
||||
: JobResult::kRetryableFailure;
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled :
|
||||
JobResult::kRetryableFailure;
|
||||
}
|
||||
|
||||
if (worker->loaded_graph_path != job.graph_path) {
|
||||
@@ -929,12 +940,11 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
<< error << worker->process->readAllStandardError();
|
||||
}
|
||||
ClearActiveWorker(worker_index, worker_process_id);
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled
|
||||
: JobResult::kRetryableFailure;
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled :
|
||||
JobResult::kRetryableFailure;
|
||||
}
|
||||
worker->loaded_graph_path = job.graph_path;
|
||||
|
||||
}
|
||||
}
|
||||
ipc::RenderFrameMsg render;
|
||||
render.ticket_id = ticket_id;
|
||||
render.node_uuid = job.node_token;
|
||||
@@ -960,8 +970,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
qWarning() << "RenderWorkerPool failed to send render_frame";
|
||||
}
|
||||
ClearActiveWorker(worker_index, worker_process_id);
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled
|
||||
: JobResult::kRetryableFailure;
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled :
|
||||
JobResult::kRetryableFailure;
|
||||
}
|
||||
|
||||
QString error;
|
||||
@@ -974,8 +984,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
|
||||
<< error << worker->process->readAllStandardError();
|
||||
}
|
||||
ClearActiveWorker(worker_index, worker_process_id);
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled
|
||||
: JobResult::kRetryableFailure;
|
||||
return job.ticket->IsCancelled() ? JobResult::kCancelled :
|
||||
JobResult::kRetryableFailure;
|
||||
}
|
||||
|
||||
if (ipc::FrameReadyMsg::FromJson(response, &ready)) {
|
||||
@@ -1068,8 +1078,8 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
|
||||
local_pool->erase(local_pool->begin() + i);
|
||||
continue;
|
||||
}
|
||||
const bool candidate_state_running =
|
||||
candidate->process->state() == QProcess::Running;
|
||||
const bool candidate_state_running = candidate->process->state() ==
|
||||
QProcess::Running;
|
||||
const bool candidate_os_alive =
|
||||
IsProcessAlive(candidate->process->processId());
|
||||
if (!candidate_state_running && !candidate_os_alive) {
|
||||
@@ -1085,7 +1095,8 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
|
||||
if (best_index < 0 ||
|
||||
(!candidate->loaded_graph_path.isEmpty() &&
|
||||
candidate->loaded_graph_path == graph_path &&
|
||||
((*local_pool)[size_t(best_index)]->loaded_graph_path != graph_path))) {
|
||||
((*local_pool)[size_t(best_index)]->loaded_graph_path !=
|
||||
graph_path))) {
|
||||
best_index = int(i);
|
||||
}
|
||||
++i;
|
||||
@@ -1100,16 +1111,16 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
|
||||
return worker;
|
||||
}
|
||||
|
||||
|
||||
// No idle worker available: start a new one.
|
||||
auto *process = new QProcess();
|
||||
process->setProgram(WorkerProgramPath());
|
||||
process->setArguments({QStringLiteral("--backend"), gpu_backend_});
|
||||
process->setArguments({ QStringLiteral("--backend"), gpu_backend_ });
|
||||
|
||||
const QString worker_stderr_path = QDir(QDir::tempPath()).filePath(
|
||||
QStringLiteral("oak-render-worker-%1-%2.stderr.log")
|
||||
.arg(QCoreApplication::applicationPid())
|
||||
.arg(QDateTime::currentMSecsSinceEpoch()));
|
||||
const QString worker_stderr_path =
|
||||
QDir(QDir::tempPath())
|
||||
.filePath(QStringLiteral("oak-render-worker-%1-%2.stderr.log")
|
||||
.arg(QCoreApplication::applicationPid())
|
||||
.arg(QDateTime::currentMSecsSinceEpoch()));
|
||||
process->setStandardErrorFile(worker_stderr_path);
|
||||
|
||||
process->start();
|
||||
@@ -1140,8 +1151,7 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
|
||||
|
||||
void RenderWorkerPool::ReturnWorker(
|
||||
std::vector<std::unique_ptr<PooledWorker>> *local_pool,
|
||||
std::unique_ptr<PooledWorker> worker,
|
||||
bool keep_alive)
|
||||
std::unique_ptr<PooledWorker> worker, bool keep_alive)
|
||||
{
|
||||
if (!worker || !worker->process) {
|
||||
return;
|
||||
@@ -1216,8 +1226,7 @@ void RenderWorkerPool::FinishWithFrame(RenderTicketPtr ticket,
|
||||
}
|
||||
|
||||
VideoParams params(meta->width, meta->height,
|
||||
PixelFormat::Format(meta->format),
|
||||
meta->channel_count);
|
||||
PixelFormat::Format(meta->format), meta->channel_count);
|
||||
FramePtr frame = Frame::Create();
|
||||
frame->set_timestamp(rational(int(meta->time_num), int(meta->time_den)));
|
||||
frame->set_video_params(params);
|
||||
@@ -1281,7 +1290,8 @@ void RenderWorkerPool::SetGraphPathCached(const QString &path, bool cached)
|
||||
SetGraphPathCachedLocked(path, cached);
|
||||
}
|
||||
|
||||
void RenderWorkerPool::SetGraphPathCachedLocked(const QString &path, bool cached)
|
||||
void RenderWorkerPool::SetGraphPathCachedLocked(const QString &path,
|
||||
bool cached)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
@@ -1296,4 +1306,4 @@ void RenderWorkerPool::SetGraphPathCachedLocked(const QString &path, bool cached
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
} // namespace olive
|
||||
|
||||
@@ -114,8 +114,7 @@ private:
|
||||
};
|
||||
|
||||
bool PrepareJob(RenderTicketPtr ticket,
|
||||
const RenderManager::RenderVideoParams ¶ms,
|
||||
Job *job);
|
||||
const RenderManager::RenderVideoParams ¶ms, Job *job);
|
||||
bool WriteGraphSnapshot(Project *project, QString *path);
|
||||
bool IsSupported(const RenderManager::RenderVideoParams ¶ms) const;
|
||||
|
||||
@@ -124,8 +123,7 @@ private:
|
||||
void ProcessJob(const Job &job, int worker_index,
|
||||
std::vector<std::unique_ptr<PooledWorker>> *local_pool);
|
||||
JobResult ProcessJobAttempt(const Job &job, int worker_index,
|
||||
int attempt_index,
|
||||
PooledWorker *worker);
|
||||
int attempt_index, PooledWorker *worker);
|
||||
void FinishWithFrame(RenderTicketPtr ticket, const ipc::FrameSlotPool &pool,
|
||||
uint32_t slot);
|
||||
void CleanupGraphFile(const QString &path);
|
||||
@@ -141,13 +139,14 @@ private:
|
||||
void ClearActiveWorker(int worker_index, qint64 process_id);
|
||||
int WorkerCount() const;
|
||||
|
||||
std::unique_ptr<PooledWorker> AcquireWorker(
|
||||
std::vector<std::unique_ptr<PooledWorker>> *local_pool,
|
||||
const QString &graph_path);
|
||||
std::unique_ptr<PooledWorker>
|
||||
AcquireWorker(std::vector<std::unique_ptr<PooledWorker>> *local_pool,
|
||||
const QString &graph_path);
|
||||
void ReturnWorker(std::vector<std::unique_ptr<PooledWorker>> *local_pool,
|
||||
std::unique_ptr<PooledWorker> worker, bool keep_alive);
|
||||
void ShutdownWorker(PooledWorker *worker);
|
||||
void ShutdownLocalPool(std::vector<std::unique_ptr<PooledWorker>> *local_pool);
|
||||
void
|
||||
ShutdownLocalPool(std::vector<std::unique_ptr<PooledWorker>> *local_pool);
|
||||
void ClearGraphCache();
|
||||
|
||||
DecoderCache *decoder_cache_;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace olive
|
||||
class AcceleratedJob;
|
||||
class Renderer;
|
||||
struct RendererLifetime {
|
||||
std::atomic<bool> alive{true};
|
||||
std::atomic<bool> alive{ true };
|
||||
};
|
||||
|
||||
class Texture;
|
||||
@@ -162,16 +162,18 @@ public:
|
||||
}
|
||||
void handleFrame(AVFramePtr ptr)
|
||||
{
|
||||
frame_=ptr;
|
||||
frame_ = ptr;
|
||||
}
|
||||
AVFramePtr frame(){
|
||||
AVFramePtr frame()
|
||||
{
|
||||
return frame_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsRendererAlive() const
|
||||
{
|
||||
return renderer_ &&
|
||||
(!renderer_lifetime_ || renderer_lifetime_->alive.load());
|
||||
(!renderer_lifetime_ || renderer_lifetime_->alive.load());
|
||||
}
|
||||
|
||||
Renderer *renderer_;
|
||||
|
||||
@@ -259,8 +259,8 @@ QString VideoParams::GetFormatName(PixelFormat format)
|
||||
break;
|
||||
}
|
||||
|
||||
return QCoreApplication::translate("VideoParams", "Unknown (0x%1)")
|
||||
.arg(static_cast<int>(format), 0, 16);
|
||||
return QCoreApplication::translate("VideoParams", "Unknown (0x%1)")
|
||||
.arg(static_cast<int>(format), 0, 16);
|
||||
}
|
||||
|
||||
int VideoParams::GetDividerForTargetResolution(int src_width, int src_height,
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
#include "render/videoparams.h"
|
||||
#include "render/vulkan/vulkanrenderer.h"
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
|
||||
class BackendVulkanRenderer : public olive::VulkanRenderer {
|
||||
public:
|
||||
@@ -38,38 +39,42 @@ const QVariant &VariantRef(const void *variant)
|
||||
} // namespace
|
||||
|
||||
// Creates the Vulkan backend object and returns it as an opaque C handle.
|
||||
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle oak_renderer_create(void *parent)
|
||||
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle
|
||||
oak_renderer_create(void *parent)
|
||||
{
|
||||
return new BackendVulkanRenderer(static_cast<QObject *>(parent));
|
||||
}
|
||||
|
||||
// Destroys the opaque backend object created by oak_renderer_create().
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy(OakRenderBackendHandle handle)
|
||||
{
|
||||
delete Renderer(handle);
|
||||
}
|
||||
|
||||
// Reports Vulkan backend capabilities and runtime availability status.
|
||||
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
|
||||
OakRenderBackendHandle handle, OakRenderBackendInfo *out_info)
|
||||
OAK_RENDER_BACKEND_EXPORT bool
|
||||
oak_renderer_get_info(OakRenderBackendHandle handle,
|
||||
OakRenderBackendInfo *out_info)
|
||||
{
|
||||
if (!handle || !out_info) {
|
||||
return false;
|
||||
}
|
||||
out_info->abi_version = 1;
|
||||
out_info->kind = OAK_RENDER_BACKEND_VULKAN;
|
||||
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->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 = Renderer(handle)->IsAvailable() ? "available" : "unavailable";
|
||||
out_info->status = Renderer(handle)->IsAvailable() ? "available" :
|
||||
"unavailable";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Probes runtime availability by trying Init() once; this lets missing ICDs or
|
||||
// unusable drivers fall back before normal rendering starts.
|
||||
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT bool
|
||||
oak_renderer_is_available(OakRenderBackendHandle handle)
|
||||
{
|
||||
auto *r = Renderer(handle);
|
||||
if (!r || r->IsAvailable()) {
|
||||
@@ -89,41 +94,41 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
|
||||
}
|
||||
|
||||
// Vulkan does not use a QOpenGLContext; the argument is accepted for ABI parity.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
|
||||
OakRenderBackendHandle handle, void *context)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_init_with_context(OakRenderBackendHandle handle, void *context)
|
||||
{
|
||||
Q_UNUSED(context)
|
||||
Renderer(handle)->Init();
|
||||
}
|
||||
|
||||
// Creates reusable Vulkan resources after device initialization.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_init(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_post_init(OakRenderBackendHandle handle)
|
||||
{
|
||||
Renderer(handle)->PostInit();
|
||||
}
|
||||
|
||||
// Reserved for API symmetry; Vulkan cleanup is handled by destroy_internal.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_destroy(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_post_destroy(OakRenderBackendHandle handle)
|
||||
{
|
||||
Renderer(handle)->PostDestroy();
|
||||
}
|
||||
|
||||
// Releases all Vulkan resources owned by the renderer.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_internal(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy_internal(OakRenderBackendHandle handle)
|
||||
{
|
||||
Renderer(handle)->DestroyInternal();
|
||||
}
|
||||
|
||||
// Clears a Vulkan texture destination.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
|
||||
OakRenderBackendHandle handle, void *texture, double r, double g, double b,
|
||||
double a)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_clear_destination(OakRenderBackendHandle handle, void *texture,
|
||||
double r, double g, double b, double a)
|
||||
{
|
||||
Renderer(handle)->ClearDestination(static_cast<olive::Texture *>(texture),
|
||||
r, g, b, a);
|
||||
r, g, b, a);
|
||||
}
|
||||
|
||||
// Creates a Vulkan texture and writes its QVariant handle to out_variant.
|
||||
@@ -131,51 +136,58 @@ 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)
|
||||
{
|
||||
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeTexture(
|
||||
width, height, depth, static_cast<olive::PixelFormat::Format>(format),
|
||||
channel_count, data, linesize);
|
||||
*static_cast<QVariant *>(out_variant) =
|
||||
Renderer(handle)->CreateNativeTexture(
|
||||
width, height, depth,
|
||||
static_cast<olive::PixelFormat::Format>(format), channel_count,
|
||||
data, linesize);
|
||||
}
|
||||
|
||||
// Destroys a Vulkan texture represented by a QVariant handle.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_texture(
|
||||
OakRenderBackendHandle handle, const void *variant)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy_native_texture(OakRenderBackendHandle handle,
|
||||
const void *variant)
|
||||
{
|
||||
Renderer(handle)->DestroyNativeTexture(VariantRef(variant));
|
||||
}
|
||||
|
||||
// Compiles a Vulkan shader and returns its QVariant handle.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
|
||||
OakRenderBackendHandle handle, const void *shader_code, void *out_variant)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_create_native_shader(OakRenderBackendHandle handle,
|
||||
const void *shader_code, void *out_variant)
|
||||
{
|
||||
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeShader(
|
||||
*static_cast<const olive::ShaderCode *>(shader_code));
|
||||
*static_cast<QVariant *>(out_variant) =
|
||||
Renderer(handle)->CreateNativeShader(
|
||||
*static_cast<const olive::ShaderCode *>(shader_code));
|
||||
}
|
||||
|
||||
// Destroys a Vulkan shader represented by a QVariant handle.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_shader(
|
||||
OakRenderBackendHandle handle, const void *variant)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_destroy_native_shader(OakRenderBackendHandle handle,
|
||||
const void *variant)
|
||||
{
|
||||
Renderer(handle)->DestroyNativeShader(VariantRef(variant));
|
||||
}
|
||||
|
||||
// Uploads CPU pixel data into a Vulkan texture.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
|
||||
OakRenderBackendHandle handle, const void *variant, const void *video_params,
|
||||
const void *data, int linesize)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_upload_to_texture(OakRenderBackendHandle handle,
|
||||
const void *variant, const void *video_params,
|
||||
const void *data, int linesize)
|
||||
{
|
||||
Renderer(handle)->UploadToTexture(
|
||||
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
|
||||
data, linesize);
|
||||
VariantRef(variant),
|
||||
*static_cast<const olive::VideoParams *>(video_params), data, linesize);
|
||||
}
|
||||
|
||||
// Downloads a Vulkan texture to CPU memory.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
|
||||
OakRenderBackendHandle handle, const void *variant, const void *video_params,
|
||||
void *data, int linesize)
|
||||
OakRenderBackendHandle handle, const void *variant,
|
||||
const void *video_params, void *data, int linesize)
|
||||
{
|
||||
Renderer(handle)->DownloadFromTexture(
|
||||
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
|
||||
data, linesize);
|
||||
VariantRef(variant),
|
||||
*static_cast<const olive::VideoParams *>(video_params), data, linesize);
|
||||
}
|
||||
|
||||
// Waits for all queued Vulkan work to finish.
|
||||
@@ -185,18 +197,23 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_flush(OakRenderBackendHandle handle)
|
||||
}
|
||||
|
||||
// Reads one pixel from a Vulkan texture.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
|
||||
OakRenderBackendHandle handle, void *texture, const void *point,
|
||||
void *out_color)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_get_pixel_from_texture(OakRenderBackendHandle handle,
|
||||
void *texture, const void *point,
|
||||
void *out_color)
|
||||
{
|
||||
*static_cast<olive::Color *>(out_color) = Renderer(handle)->GetPixelFromTexture(
|
||||
static_cast<olive::Texture *>(texture), *static_cast<const QPointF *>(point));
|
||||
*static_cast<olive::Color *>(out_color) =
|
||||
Renderer(handle)->GetPixelFromTexture(
|
||||
static_cast<olive::Texture *>(texture),
|
||||
*static_cast<const QPointF *>(point));
|
||||
}
|
||||
|
||||
// Executes a shader blit through the wrapped Vulkan renderer.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
|
||||
OakRenderBackendHandle handle, const void *shader, void *job,
|
||||
void *destination, const void *destination_params, bool clear_destination)
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(OakRenderBackendHandle handle,
|
||||
const void *shader, void *job,
|
||||
void *destination,
|
||||
const void *destination_params,
|
||||
bool clear_destination)
|
||||
{
|
||||
Renderer(handle)->Blit(
|
||||
VariantRef(shader), *static_cast<olive::AcceleratedJob *>(job),
|
||||
@@ -206,16 +223,17 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
|
||||
}
|
||||
|
||||
// Vulkan has no OpenGL context; return null so callers avoid GL-only paths.
|
||||
OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
|
||||
OakRenderBackendHandle handle)
|
||||
OAK_RENDER_BACKEND_EXPORT void *
|
||||
oak_renderer_opengl_context(OakRenderBackendHandle handle)
|
||||
{
|
||||
Q_UNUSED(handle)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// OFX OpenGL output attachment is unsupported in Vulkan and intentionally no-op.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
|
||||
OakRenderBackendHandle handle, const void *texture_id)
|
||||
OAK_RENDER_BACKEND_EXPORT void
|
||||
oak_renderer_attach_output_texture(OakRenderBackendHandle handle,
|
||||
const void *texture_id)
|
||||
{
|
||||
Q_UNUSED(handle)
|
||||
Q_UNUSED(texture_id)
|
||||
@@ -223,8 +241,8 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
|
||||
}
|
||||
|
||||
// OFX OpenGL output detachment is unsupported in Vulkan and intentionally no-op.
|
||||
OAK_RENDER_BACKEND_EXPORT void oak_renderer_detach_output_texture(
|
||||
OakRenderBackendHandle handle)
|
||||
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
@@ -51,8 +51,8 @@ public:
|
||||
|
||||
// Clears either a texture render target or the currently bound output target.
|
||||
virtual void ClearDestination(olive::Texture *texture = nullptr,
|
||||
double r = 0.0, double g = 0.0,
|
||||
double b = 0.0, double a = 0.0) override;
|
||||
double r = 0.0, double g = 0.0,
|
||||
double b = 0.0, double a = 0.0) override;
|
||||
|
||||
// Compiles GLSL to SPIR-V, creates shader modules, and prepares descriptor
|
||||
// metadata for later blits.
|
||||
@@ -63,12 +63,12 @@ public:
|
||||
|
||||
// Uploads CPU pixel data to a Vulkan image via a staging buffer.
|
||||
virtual void UploadToTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, const void *data,
|
||||
int linesize) override;
|
||||
const VideoParams ¶ms, const void *data,
|
||||
int linesize) override;
|
||||
// Downloads a Vulkan image to CPU memory via a staging buffer.
|
||||
virtual void DownloadFromTexture(const QVariant &handle,
|
||||
const VideoParams ¶ms, void *data,
|
||||
int linesize) override;
|
||||
const VideoParams ¶ms, void *data,
|
||||
int linesize) override;
|
||||
|
||||
// Waits for outstanding device work to complete.
|
||||
virtual void Flush() override;
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
|
||||
// Reads a single texture pixel using a one-pixel transfer readback.
|
||||
virtual Color GetPixelFromTexture(olive::Texture *texture,
|
||||
const QPointF &pt) override;
|
||||
const QPointF &pt) override;
|
||||
|
||||
bool IsAvailable() const
|
||||
{
|
||||
@@ -90,14 +90,15 @@ public:
|
||||
protected:
|
||||
// Runs one or more fullscreen shader passes into the destination texture.
|
||||
virtual void Blit(QVariant shader, olive::AcceleratedJob &job,
|
||||
olive::Texture *destination, VideoParams destination_params,
|
||||
bool clear_destination) override;
|
||||
olive::Texture *destination,
|
||||
VideoParams destination_params,
|
||||
bool clear_destination) override;
|
||||
// Creates a Vulkan image/view/memory bundle and optionally uploads initial
|
||||
// pixel data.
|
||||
virtual QVariant CreateNativeTexture(int width, int height, int depth,
|
||||
PixelFormat format, int channel_count,
|
||||
const void *data = nullptr,
|
||||
int linesize = 0) override;
|
||||
PixelFormat format, int channel_count,
|
||||
const void *data = nullptr,
|
||||
int linesize = 0) override;
|
||||
// Releases a Vulkan texture bundle.
|
||||
virtual void DestroyNativeTexture(QVariant texture) override;
|
||||
// Releases all Vulkan device resources owned by this renderer.
|
||||
@@ -172,10 +173,10 @@ private:
|
||||
float GetFormatMaxAlpha(PixelFormat format) const;
|
||||
// Repackages tightly packed pixels when the requested CPU channel count
|
||||
// differs from the selected GPU format channel count.
|
||||
void CopyPixelsWithChannelConversion(const void *src, void *dst,
|
||||
int width, int height, int depth,
|
||||
int src_channels, int dst_channels,
|
||||
PixelFormat format) const;
|
||||
void CopyPixelsWithChannelConversion(const void *src, void *dst, int width,
|
||||
int height, int depth,
|
||||
int src_channels, int dst_channels,
|
||||
PixelFormat format) const;
|
||||
// Rounds a size up to the requested alignment.
|
||||
VkDeviceSize AlignSize(VkDeviceSize size, VkDeviceSize alignment) const;
|
||||
|
||||
@@ -187,11 +188,13 @@ private:
|
||||
bool CompileGlslToSpv(const QString &glsl, VkShaderStageFlagBits stage,
|
||||
QByteArray *out_spv);
|
||||
// Rewrites an Oak GLSL shader into Vulkan-compatible GLSL.
|
||||
QString ConvertGlslToVulkan(const QString &glsl, VkShaderStageFlagBits stage);
|
||||
QString ConvertGlslToVulkan(const QString &glsl,
|
||||
VkShaderStageFlagBits stage);
|
||||
// Ensures a shader declares a Vulkan-compatible GLSL version.
|
||||
QString EnsureGlslVersion450(const QString &glsl) const;
|
||||
// Extracts uniforms and sampler names from GLSL declarations.
|
||||
void ExtractUniforms(const QString &glsl, QVector<UniformInfo> *out_uniforms,
|
||||
void ExtractUniforms(const QString &glsl,
|
||||
QVector<UniformInfo> *out_uniforms,
|
||||
QVector<QString> *out_samplers) const;
|
||||
// Computes std140 offsets and total UBO size for extracted uniforms.
|
||||
void ComputeUniformLayout(QVector<UniformInfo> *uniforms) const;
|
||||
@@ -199,9 +202,10 @@ private:
|
||||
QString BuildUboBlock(const QVector<UniformInfo> &uniforms) const;
|
||||
// Rewrites standalone uniforms and samplers into explicit UBO/sampler
|
||||
// bindings accepted by Vulkan GLSL.
|
||||
QString RewriteShaderWithUbo(const QString &glsl,
|
||||
const QVector<UniformInfo> &all_uniforms,
|
||||
const QHash<QString, int> &sampler_bindings) const;
|
||||
QString
|
||||
RewriteShaderWithUbo(const QString &glsl,
|
||||
const QVector<UniformInfo> &all_uniforms,
|
||||
const QHash<QString, int> &sampler_bindings) const;
|
||||
// Returns std140 storage size for a supported GLSL type.
|
||||
VkDeviceSize GetStd140Size(const QString &type) const;
|
||||
// Returns std140 alignment for a supported GLSL type.
|
||||
@@ -225,8 +229,8 @@ private:
|
||||
void BlitPass(VulkanShader *shader, VulkanTexture *dest_tex,
|
||||
const QVector<TextureBinding> &bindings,
|
||||
const QByteArray &ubo_data,
|
||||
const VideoParams &destination_params,
|
||||
bool clear_destination, int iteration);
|
||||
const VideoParams &destination_params, bool clear_destination,
|
||||
int iteration);
|
||||
|
||||
VkInstance instance_ = VK_NULL_HANDLE;
|
||||
VkDebugUtilsMessengerEXT debug_messenger_ = VK_NULL_HANDLE;
|
||||
|
||||
@@ -110,7 +110,6 @@ public:
|
||||
|
||||
bool InitializeRuntime()
|
||||
{
|
||||
|
||||
// Create a minimal Core instance so that code paths calling Core::instance()
|
||||
// (e.g. ViewerOutput::data for timecode display) do not dereference null.
|
||||
// The worker is short-lived; leaking this on exit is harmless.
|
||||
@@ -141,7 +140,8 @@ public:
|
||||
QJsonObject handshake = hs.ToJson();
|
||||
QOpenGLContext *ctx = nullptr;
|
||||
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
|
||||
if (auto *dynamic_renderer = dynamic_cast<olive::DynamicRenderer *>(renderer_)) {
|
||||
if (auto *dynamic_renderer =
|
||||
dynamic_cast<olive::DynamicRenderer *>(renderer_)) {
|
||||
ctx = dynamic_renderer->OpenGLContext();
|
||||
} else
|
||||
#endif
|
||||
@@ -164,7 +164,8 @@ public:
|
||||
if (type == QLatin1String(olive::ipc::msgtype::kHandshake)) {
|
||||
olive::ipc::HandshakeMsg hs;
|
||||
if (!olive::ipc::HandshakeMsg::FromJson(message, &hs)) {
|
||||
return Write(ErrorMessage(QStringLiteral("invalid handshake message")));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("invalid handshake message")));
|
||||
}
|
||||
return AttachOutputPool(hs);
|
||||
}
|
||||
@@ -172,7 +173,8 @@ public:
|
||||
if (type == QLatin1String(olive::ipc::msgtype::kLoadGraph)) {
|
||||
olive::ipc::LoadGraphMsg load;
|
||||
if (!olive::ipc::LoadGraphMsg::FromJson(message, &load)) {
|
||||
return Write(ErrorMessage(QStringLiteral("invalid load_graph message")));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("invalid load_graph message")));
|
||||
}
|
||||
return LoadGraph(load.path);
|
||||
}
|
||||
@@ -180,7 +182,8 @@ public:
|
||||
if (type == QLatin1String(olive::ipc::msgtype::kRenderFrame)) {
|
||||
olive::ipc::RenderFrameMsg render;
|
||||
if (!olive::ipc::RenderFrameMsg::FromJson(message, &render)) {
|
||||
return Write(ErrorMessage(QStringLiteral("invalid render_frame message")));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("invalid render_frame message")));
|
||||
}
|
||||
return RenderFrame(render);
|
||||
}
|
||||
@@ -195,7 +198,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
return Write(ErrorMessage(QStringLiteral("unknown message type: %1").arg(type)));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("unknown message type: %1").arg(type)));
|
||||
}
|
||||
|
||||
bool shutdown_requested() const
|
||||
@@ -214,48 +218,58 @@ private:
|
||||
bool AttachOutputPool(const olive::ipc::HandshakeMsg &hs)
|
||||
{
|
||||
if (hs.protocol_version != kProtocolVersion) {
|
||||
return Write(ErrorMessage(QStringLiteral("unsupported protocol version %1")
|
||||
.arg(hs.protocol_version)));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("unsupported protocol version %1")
|
||||
.arg(hs.protocol_version)));
|
||||
}
|
||||
|
||||
if (hs.shm_key.isEmpty() || hs.output_slots <= 0 || hs.slot_data_bytes <= 0) {
|
||||
return Write(ErrorMessage(QStringLiteral("handshake missing output shared-memory geometry")));
|
||||
if (hs.shm_key.isEmpty() || hs.output_slots <= 0 ||
|
||||
hs.slot_data_bytes <= 0) {
|
||||
return Write(ErrorMessage(QStringLiteral(
|
||||
"handshake missing output shared-memory geometry")));
|
||||
}
|
||||
|
||||
const size_t bytes = olive::ipc::FrameSlotPool::BytesNeeded(
|
||||
uint32_t(hs.output_slots), size_t(hs.slot_data_bytes));
|
||||
if (!output_region_.Open(hs.shm_key, bytes, olive::ipc::SharedMemoryRegion::kAttach)) {
|
||||
return Write(ErrorMessage(QStringLiteral("failed to attach shared memory: %1")
|
||||
.arg(output_region_.error())));
|
||||
if (!output_region_.Open(hs.shm_key, bytes,
|
||||
olive::ipc::SharedMemoryRegion::kAttach)) {
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("failed to attach shared memory: %1")
|
||||
.arg(output_region_.error())));
|
||||
}
|
||||
|
||||
output_pool_ = olive::ipc::FrameSlotPool::Attach(output_region_.data());
|
||||
if (!output_pool_->IsValid()) {
|
||||
output_region_.Close();
|
||||
output_pool_.reset();
|
||||
return Write(ErrorMessage(QStringLiteral("shared memory does not contain a frame slot pool")));
|
||||
return Write(ErrorMessage(QStringLiteral(
|
||||
"shared memory does not contain a frame slot pool")));
|
||||
}
|
||||
|
||||
input_pool_.reset();
|
||||
input_region_.Close();
|
||||
if (hs.input_slots > 0) {
|
||||
if (hs.input_shm_key.isEmpty() || hs.input_slot_data_bytes <= 0) {
|
||||
return Write(ErrorMessage(QStringLiteral("handshake missing input shared-memory geometry")));
|
||||
return Write(ErrorMessage(QStringLiteral(
|
||||
"handshake missing input shared-memory geometry")));
|
||||
}
|
||||
|
||||
const size_t input_bytes = olive::ipc::FrameSlotPool::BytesNeeded(
|
||||
uint32_t(hs.input_slots), size_t(hs.input_slot_data_bytes));
|
||||
if (!input_region_.Open(hs.input_shm_key, input_bytes,
|
||||
olive::ipc::SharedMemoryRegion::kAttach)) {
|
||||
return Write(ErrorMessage(QStringLiteral("failed to attach input shared memory: %1")
|
||||
.arg(input_region_.error())));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("failed to attach input shared memory: %1")
|
||||
.arg(input_region_.error())));
|
||||
}
|
||||
|
||||
input_pool_ = olive::ipc::FrameSlotPool::Attach(input_region_.data());
|
||||
input_pool_ =
|
||||
olive::ipc::FrameSlotPool::Attach(input_region_.data());
|
||||
if (!input_pool_->IsValid()) {
|
||||
input_region_.Close();
|
||||
input_pool_.reset();
|
||||
return Write(ErrorMessage(QStringLiteral("input shared memory does not contain a frame slot pool")));
|
||||
return Write(ErrorMessage(QStringLiteral(
|
||||
"input shared memory does not contain a frame slot pool")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,17 +281,23 @@ private:
|
||||
{
|
||||
QFileInfo fi(path);
|
||||
if (!fi.exists()) {
|
||||
LogError(QStringLiteral("LoadGraph: graph file does not exist: %1").arg(path));
|
||||
return Write(ErrorMessage(QStringLiteral("graph file does not exist: %1").arg(path)));
|
||||
LogError(
|
||||
QStringLiteral("LoadGraph: graph file does not exist: %1")
|
||||
.arg(path));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("graph file does not exist: %1").arg(path)));
|
||||
}
|
||||
if (fi.size() == 0) {
|
||||
LogError(QStringLiteral("LoadGraph: graph file is empty: %1").arg(path));
|
||||
return Write(ErrorMessage(QStringLiteral("graph file is empty: %1").arg(path)));
|
||||
LogError(QStringLiteral("LoadGraph: graph file is empty: %1")
|
||||
.arg(path));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("graph file is empty: %1").arg(path)));
|
||||
}
|
||||
LogError(QStringLiteral("LoadGraph: loading %1 (%2 bytes, readable=%3)")
|
||||
.arg(path)
|
||||
.arg(fi.size())
|
||||
.arg(fi.isReadable()));
|
||||
LogError(
|
||||
QStringLiteral("LoadGraph: loading %1 (%2 bytes, readable=%3)")
|
||||
.arg(path)
|
||||
.arg(fi.size())
|
||||
.arg(fi.isReadable()));
|
||||
}
|
||||
|
||||
auto loaded = std::make_unique<olive::Project>();
|
||||
@@ -286,10 +306,12 @@ private:
|
||||
// Initialize() first triggers Q_ASSERT(!root_) in Project::Load.
|
||||
|
||||
olive::ProjectSerializer::Result result =
|
||||
olive::ProjectSerializer::Load(loaded.get(), path, olive::ProjectSerializer::kProject);
|
||||
olive::ProjectSerializer::Load(loaded.get(), path,
|
||||
olive::ProjectSerializer::kProject);
|
||||
if (result != olive::ProjectSerializer::kSuccess) {
|
||||
return Write(ErrorMessage(QStringLiteral("failed to load graph %1: %2")
|
||||
.arg(path, result.GetDetails())));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("failed to load graph %1: %2")
|
||||
.arg(path, result.GetDetails())));
|
||||
}
|
||||
|
||||
project_ = std::move(loaded);
|
||||
@@ -297,12 +319,15 @@ private:
|
||||
color_processor_cache_.clear();
|
||||
|
||||
const auto &data = result.GetLoadData();
|
||||
for (auto it = data.node_ptrs.cbegin(); it != data.node_ptrs.cend(); ++it) {
|
||||
for (auto it = data.node_ptrs.cbegin(); it != data.node_ptrs.cend();
|
||||
++it) {
|
||||
node_by_token_.insert(QString::number(it.key()), it.value());
|
||||
}
|
||||
for (auto it = data.node_uuids.cbegin(); it != data.node_uuids.cend(); ++it) {
|
||||
for (auto it = data.node_uuids.cbegin(); it != data.node_uuids.cend();
|
||||
++it) {
|
||||
node_by_token_.insert(it.value().toString(), it.key());
|
||||
node_by_token_.insert(it.value().toString(QUuid::WithoutBraces), it.key());
|
||||
node_by_token_.insert(it.value().toString(QUuid::WithoutBraces),
|
||||
it.key());
|
||||
}
|
||||
|
||||
QJsonObject ack;
|
||||
@@ -329,29 +354,36 @@ private:
|
||||
bool RenderFrame(const olive::ipc::RenderFrameMsg &message)
|
||||
{
|
||||
if (!project_) {
|
||||
return Write(ErrorMessage(QStringLiteral("render_frame received before load_graph"),
|
||||
message.ticket_id));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("render_frame received before load_graph"),
|
||||
message.ticket_id));
|
||||
}
|
||||
if (!output_pool_ || !output_pool_->IsValid()) {
|
||||
return Write(ErrorMessage(QStringLiteral("render_frame received before output shm handshake"),
|
||||
message.ticket_id));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral(
|
||||
"render_frame received before output shm handshake"),
|
||||
message.ticket_id));
|
||||
}
|
||||
|
||||
olive::Node *node = FindNode(message.node_uuid);
|
||||
if (!node) {
|
||||
return Write(ErrorMessage(QStringLiteral("render node not found: %1").arg(message.node_uuid),
|
||||
message.ticket_id));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("render node not found: %1")
|
||||
.arg(message.node_uuid),
|
||||
message.ticket_id));
|
||||
}
|
||||
|
||||
QVector<int> input_slots;
|
||||
const QVector<int> requested_input_slots =
|
||||
message.input_slots.isEmpty() && message.input_slot >= 0
|
||||
? QVector<int>{message.input_slot}
|
||||
: message.input_slots;
|
||||
message.input_slots.isEmpty() && message.input_slot >= 0 ?
|
||||
QVector<int>{ message.input_slot } :
|
||||
message.input_slots;
|
||||
if (!requested_input_slots.isEmpty()) {
|
||||
if (!input_pool_ || !input_pool_->IsValid()) {
|
||||
return Write(ErrorMessage(QStringLiteral("render_frame referenced input slot without input pool"),
|
||||
message.ticket_id));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral(
|
||||
"render_frame referenced input slot without input pool"),
|
||||
message.ticket_id));
|
||||
}
|
||||
|
||||
for (int requested_slot : requested_input_slots) {
|
||||
@@ -360,8 +392,9 @@ private:
|
||||
for (int slot : input_slots) {
|
||||
input_pool_->Release(uint32_t(slot));
|
||||
}
|
||||
return Write(ErrorMessage(QStringLiteral("input slot index out of range"),
|
||||
message.ticket_id));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("input slot index out of range"),
|
||||
message.ticket_id));
|
||||
}
|
||||
|
||||
uint32_t consumed_slot = 0;
|
||||
@@ -369,16 +402,18 @@ private:
|
||||
for (int slot : input_slots) {
|
||||
input_pool_->Release(uint32_t(slot));
|
||||
}
|
||||
return Write(ErrorMessage(QStringLiteral("input slot was not ready"),
|
||||
message.ticket_id));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("input slot was not ready"),
|
||||
message.ticket_id));
|
||||
}
|
||||
if (int(consumed_slot) != requested_slot) {
|
||||
input_pool_->Release(consumed_slot);
|
||||
for (int slot : input_slots) {
|
||||
input_pool_->Release(uint32_t(slot));
|
||||
}
|
||||
return Write(ErrorMessage(QStringLiteral("input slot order mismatch"),
|
||||
message.ticket_id));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("input slot order mismatch"),
|
||||
message.ticket_id));
|
||||
}
|
||||
input_slots.append(int(consumed_slot));
|
||||
|
||||
@@ -389,41 +424,41 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
olive::VideoParams vparams(message.width > 0 ? message.width : kDefaultWidth,
|
||||
message.height > 0 ? message.height : kDefaultHeight,
|
||||
olive::rational(1, kDefaultFrameRate),
|
||||
message.format >= 0
|
||||
? olive::PixelFormat::Format(message.format)
|
||||
: olive::PixelFormat::F32,
|
||||
message.channel_count > 0
|
||||
? message.channel_count
|
||||
: olive::VideoParams::kRGBAChannelCount);
|
||||
olive::VideoParams vparams(
|
||||
message.width > 0 ? message.width : kDefaultWidth,
|
||||
message.height > 0 ? message.height : kDefaultHeight,
|
||||
olive::rational(1, kDefaultFrameRate),
|
||||
message.format >= 0 ? olive::PixelFormat::Format(message.format) :
|
||||
olive::PixelFormat::F32,
|
||||
message.channel_count > 0 ? message.channel_count :
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
olive::RenderTicketPtr ticket = std::make_shared<olive::RenderTicket>();
|
||||
ticket->setProperty("node", olive::QtUtils::PtrToValue(node));
|
||||
ticket->setProperty("time", QVariant::fromValue(
|
||||
olive::rational(int(message.time_num), int(message.time_den))));
|
||||
ticket->setProperty("time",
|
||||
QVariant::fromValue(olive::rational(
|
||||
int(message.time_num), int(message.time_den))));
|
||||
ticket->setProperty("size", QSize(message.width, message.height));
|
||||
ticket->setProperty("matrix", QMatrix4x4());
|
||||
ticket->setProperty("format",
|
||||
message.format >= 0
|
||||
? olive::PixelFormat::Format(message.format)
|
||||
: olive::PixelFormat::INVALID);
|
||||
message.format >= 0 ?
|
||||
olive::PixelFormat::Format(message.format) :
|
||||
olive::PixelFormat::INVALID);
|
||||
ticket->setProperty("usecache", false);
|
||||
ticket->setProperty("channelcount", message.channel_count);
|
||||
ticket->setProperty("mode", olive::RenderMode::Mode(message.mode));
|
||||
ticket->setProperty("type", olive::RenderManager::kTypeVideo);
|
||||
ticket->setProperty("colormanager", olive::QtUtils::PtrToValue(project_->color_manager()));
|
||||
ticket->setProperty("colormanager", olive::QtUtils::PtrToValue(
|
||||
project_->color_manager()));
|
||||
|
||||
{
|
||||
olive::ColorProcessorPtr color_output;
|
||||
if (message.has_color_transform) {
|
||||
QString cache_key =
|
||||
QStringLiteral("%1|%2|%3|%4")
|
||||
.arg(message.color_is_display ? 1 : 0)
|
||||
.arg(message.color_output,
|
||||
message.color_view,
|
||||
message.color_look);
|
||||
QString cache_key = QStringLiteral("%1|%2|%3|%4")
|
||||
.arg(message.color_is_display ? 1 : 0)
|
||||
.arg(message.color_output,
|
||||
message.color_view,
|
||||
message.color_look);
|
||||
auto it = color_processor_cache_.find(cache_key);
|
||||
if (it != color_processor_cache_.end()) {
|
||||
color_output = it.value();
|
||||
@@ -431,8 +466,8 @@ private:
|
||||
olive::ColorTransform transform;
|
||||
if (message.color_is_display) {
|
||||
transform = olive::ColorTransform(message.color_output,
|
||||
message.color_view,
|
||||
message.color_look);
|
||||
message.color_view,
|
||||
message.color_look);
|
||||
} else {
|
||||
transform = olive::ColorTransform(message.color_output);
|
||||
}
|
||||
@@ -446,19 +481,23 @@ private:
|
||||
}
|
||||
}
|
||||
ticket->setProperty("coloroutput",
|
||||
QVariant::fromValue(color_output));
|
||||
QVariant::fromValue(color_output));
|
||||
}
|
||||
ticket->setProperty("vparam", QVariant::fromValue(vparams));
|
||||
ticket->setProperty("aparam", QVariant::fromValue(olive::AudioParams()));
|
||||
ticket->setProperty("aparam",
|
||||
QVariant::fromValue(olive::AudioParams()));
|
||||
ticket->setProperty("return", olive::RenderManager::kFrame);
|
||||
ticket->setProperty("cache", QString());
|
||||
ticket->setProperty("cachetimebase", QVariant::fromValue(olive::rational(1)));
|
||||
ticket->setProperty("cachetimebase",
|
||||
QVariant::fromValue(olive::rational(1)));
|
||||
ticket->setProperty("cacheid", QVariant::fromValue(QUuid()));
|
||||
ticket->setProperty("multicam", olive::QtUtils::PtrToValue(static_cast<void *>(nullptr)));
|
||||
ticket->setProperty("ipc_input_pool",
|
||||
olive::QtUtils::PtrToValue(
|
||||
input_pool_ ? static_cast<void *>(&*input_pool_)
|
||||
: static_cast<void *>(nullptr)));
|
||||
ticket->setProperty("multicam", olive::QtUtils::PtrToValue(
|
||||
static_cast<void *>(nullptr)));
|
||||
ticket->setProperty(
|
||||
"ipc_input_pool",
|
||||
olive::QtUtils::PtrToValue(input_pool_ ?
|
||||
static_cast<void *>(&*input_pool_) :
|
||||
static_cast<void *>(nullptr)));
|
||||
QVariantList input_slot_values;
|
||||
for (int slot : input_slots) {
|
||||
input_slot_values.append(slot);
|
||||
@@ -469,34 +508,42 @@ private:
|
||||
input_slots.isEmpty() ? -1 : input_slots.front());
|
||||
|
||||
ticket->Start();
|
||||
olive::RenderProcessor::Process(ticket, renderer_, nullptr, &shader_cache_);
|
||||
olive::RenderProcessor::Process(ticket, renderer_, nullptr,
|
||||
&shader_cache_);
|
||||
for (int slot : input_slots) {
|
||||
input_pool_->Release(uint32_t(slot));
|
||||
}
|
||||
if (!ticket->HasResult()) {
|
||||
return Write(ErrorMessage(QStringLiteral("render produced no frame"), message.ticket_id));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("render produced no frame"), message.ticket_id));
|
||||
}
|
||||
|
||||
olive::FramePtr frame = ticket->Get().value<olive::FramePtr>();
|
||||
if (!frame || !frame->is_allocated()) {
|
||||
return Write(ErrorMessage(QStringLiteral("render result was empty"), message.ticket_id));
|
||||
return Write(ErrorMessage(QStringLiteral("render result was empty"),
|
||||
message.ticket_id));
|
||||
}
|
||||
|
||||
uint32_t slot = 0;
|
||||
if (!output_pool_->Acquire(&slot)) {
|
||||
return Write(ErrorMessage(QStringLiteral("no free output frame slot"), message.ticket_id));
|
||||
return Write(
|
||||
ErrorMessage(QStringLiteral("no free output frame slot"),
|
||||
message.ticket_id));
|
||||
}
|
||||
|
||||
const int data_size = frame->linesize_bytes()*frame->height();
|
||||
const int data_size = frame->linesize_bytes() * frame->height();
|
||||
if (data_size > int(output_pool_->slot_data_bytes())) {
|
||||
output_pool_->Release(slot);
|
||||
LogError(QString("Output frame size")+QString::number(data_size));
|
||||
LogError(QString("Slot size")+QString::number(output_pool_->slot_data_bytes()));
|
||||
return Write(ErrorMessage(QStringLiteral("rendered frame does not fit output slot "),
|
||||
message.ticket_id));
|
||||
LogError(QString("Output frame size") + QString::number(data_size));
|
||||
LogError(QString("Slot size") +
|
||||
QString::number(output_pool_->slot_data_bytes()));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("rendered frame does not fit output slot "),
|
||||
message.ticket_id));
|
||||
}
|
||||
|
||||
std::memcpy(output_pool_->SlotData(slot), frame->const_data(), size_t(data_size));
|
||||
std::memcpy(output_pool_->SlotData(slot), frame->const_data(),
|
||||
size_t(data_size));
|
||||
olive::ipc::FrameSlotMeta *meta = output_pool_->Meta(slot);
|
||||
meta->id = message.ticket_id;
|
||||
meta->time_num = frame->timestamp().numerator();
|
||||
@@ -510,8 +557,9 @@ private:
|
||||
|
||||
if (!output_pool_->Publish(slot)) {
|
||||
output_pool_->Release(slot);
|
||||
return Write(ErrorMessage(QStringLiteral("failed to publish output frame slot"),
|
||||
message.ticket_id));
|
||||
return Write(ErrorMessage(
|
||||
QStringLiteral("failed to publish output frame slot"),
|
||||
message.ticket_id));
|
||||
}
|
||||
olive::ipc::FrameReadyMsg ready;
|
||||
ready.ticket_id = message.ticket_id;
|
||||
@@ -532,7 +580,7 @@ private:
|
||||
QHash<QString, olive::ColorProcessorPtr> color_processor_cache_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@@ -600,7 +648,8 @@ int main(int argc, char *argv[])
|
||||
QOpenGLContext *ctx = nullptr;
|
||||
if (backend == QStringLiteral("opengl")) {
|
||||
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
|
||||
if (auto *loaded_renderer = dynamic_cast<olive::DynamicRenderer *>(renderer)) {
|
||||
if (auto *loaded_renderer =
|
||||
dynamic_cast<olive::DynamicRenderer *>(renderer)) {
|
||||
ctx = loaded_renderer->OpenGLContext();
|
||||
} else
|
||||
#endif
|
||||
@@ -639,7 +688,8 @@ int main(int argc, char *argv[])
|
||||
if (!olive::ipc::ReadMessage(&buffer, &message, &ok)) {
|
||||
if (!ok) {
|
||||
olive::ipc::WriteMessage(
|
||||
&out, ErrorMessage(QStringLiteral("malformed control message")));
|
||||
&out, ErrorMessage(QStringLiteral(
|
||||
"malformed control message")));
|
||||
out.flush();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -22,5 +22,5 @@
|
||||
|
||||
void HideWorkerDockIcon()
|
||||
{
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user