refactor(node,render): route oaknode->oakrender calls through the C ABI
- oakrender cache C API: add create_for_node (four cache kinds), get_uuid, request, load/save_state, set_saving_enabled, set_passthrough, get_passthroughs, get_valid_cache_filename, get_timebase, lock/unlock, invalidate_range (rational variant), get_native (C++ only, for the in-module PreviewAutoCacher) - Node's four caches become owned OakRenderCache value handles; all cache access in node.cpp/clip/viewer/traverser/serializers goes through the C ABI - oaknode_node_get_video_frame_cache returns an addref'd OakRenderCache; drop OakNodeFrameCache; oaktask precache/export and oakrender_video_ticket_params take the value handle - color: OCIOBaseNode/OCIOLutNode hold OakColorProcessor handles (dtors added); oakrender gains color_processor_create_transform/ create_lut/create_grading_primary/get_native and LUT library queries, keeping all OCIO transform construction inside oakrender; oaknode gains colormanager_wrap_borrowed/get_native - RenderManager::instance() check -> oakrender_manager_available(); cancel_video_tasks and disk cache path go through manager C API - remaining node->render C++ symbol: only olive::Texture::~Texture via Variant's shared_ptr payload (recorded exception, same class as the UndoCommand inheritance) Tests: new cache/color/manager/colormanager cases; all standalone trees green (common 196, codec 22, audio 40, task 112, render 63, timeline 125, node 98, plugin 102).
This commit is contained in:
@@ -21,13 +21,18 @@
|
||||
#include "../../../include/render/cache.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
#include "alivecount.h"
|
||||
#include "internalhandles.h"
|
||||
|
||||
#include "audioplaybackcache.h"
|
||||
#include "audiowaveformcache.h"
|
||||
#include "framehashcache.h"
|
||||
#include "playbackcache.h"
|
||||
#include "output/viewer/viewer.h"
|
||||
#include "../../node/c_api/nodehandle.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -114,6 +119,231 @@ OakRenderCache oakrender_cache_wrap_borrowed(void *native_cache)
|
||||
native_cache, false, nullptr);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Recover the boxed cache as its PlaybackCache base (NULL-safe).
|
||||
*/
|
||||
olive::PlaybackCache *base(OakRenderCache c)
|
||||
{
|
||||
if (!c.ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<olive::PlaybackCache *>(
|
||||
oakrender_c_api::box_object(c.ctx));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy @p value into the two-stage string buffer.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL). The buffer is
|
||||
* only written to when it is large enough.
|
||||
*/
|
||||
int copy_string(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int needed = static_cast<int>(value.size()) + 1;
|
||||
if (buf && buf_size >= needed) {
|
||||
memcpy(buf, value.c_str(), needed);
|
||||
}
|
||||
return needed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OakRenderCache oakrender_cache_create_for_node(OakNodeNode parent, int kind)
|
||||
{
|
||||
olive::Node *native_parent =
|
||||
oaknode_c_api::to_native<olive::Node>(parent);
|
||||
if (!native_parent) {
|
||||
return OakRenderCache{};
|
||||
}
|
||||
|
||||
olive::PlaybackCache *cache = nullptr;
|
||||
switch (kind) {
|
||||
case OAKRENDER_CACHE_VIDEO_FRAME:
|
||||
cache = new (std::nothrow) olive::FrameHashCache(native_parent);
|
||||
break;
|
||||
case OAKRENDER_CACHE_THUMBNAIL:
|
||||
cache = new (std::nothrow) olive::ThumbnailCache(native_parent);
|
||||
break;
|
||||
case OAKRENDER_CACHE_AUDIO_PLAYBACK:
|
||||
cache = new (std::nothrow) olive::AudioPlaybackCache(native_parent);
|
||||
break;
|
||||
case OAKRENDER_CACHE_AUDIO_WAVEFORM:
|
||||
cache = new (std::nothrow) olive::AudioWaveformCache(native_parent);
|
||||
break;
|
||||
default:
|
||||
return OakRenderCache{};
|
||||
}
|
||||
if (!cache) {
|
||||
return OakRenderCache{};
|
||||
}
|
||||
|
||||
return oakrender_c_api::make_handle<OakRenderCache>(
|
||||
cache, true, &oakrender_c_api::delete_as<olive::PlaybackCache>);
|
||||
}
|
||||
|
||||
int oakrender_cache_get_uuid(OakRenderCache cache, char *buf, int buf_size)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
return copy_string(c->get_uuid(), buf, buf_size);
|
||||
}
|
||||
|
||||
void oakrender_cache_invalidate_range(OakRenderCache cache,
|
||||
int64_t in_num, int64_t in_den,
|
||||
int64_t out_num, int64_t out_den)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c->invalidate(
|
||||
olive::core::TimeRange(olive::Rational(in_num, in_den),
|
||||
olive::Rational(out_num, out_den)));
|
||||
}
|
||||
|
||||
int oakrender_cache_request(OakRenderCache cache, OakNodeNode context,
|
||||
int64_t in_num, int64_t in_den,
|
||||
int64_t out_num, int64_t out_den)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
olive::Node *native_context =
|
||||
oaknode_c_api::to_native<olive::Node>(context);
|
||||
auto *viewer = dynamic_cast<olive::ViewerOutput *>(native_context);
|
||||
if (!c || !viewer) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
c->request(viewer,
|
||||
olive::core::TimeRange(olive::Rational(in_num, in_den),
|
||||
olive::Rational(out_num, out_den)));
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_cache_load_state(OakRenderCache cache)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
try {
|
||||
c->load_state();
|
||||
return OAKRENDER_OK;
|
||||
} catch (...) {
|
||||
return OAKRENDER_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakrender_cache_save_state(OakRenderCache cache)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
try {
|
||||
c->save_state();
|
||||
return OAKRENDER_OK;
|
||||
} catch (...) {
|
||||
return OAKRENDER_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakrender_cache_set_saving_enabled(OakRenderCache cache, int enabled)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
c->set_saving_enabled(enabled != 0);
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_cache_set_passthrough(OakRenderCache cache,
|
||||
OakRenderCache other)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
olive::PlaybackCache *o = base(other);
|
||||
if (!c || !o) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
c->set_passthrough(o);
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_cache_get_valid_cache_filename(OakRenderCache cache,
|
||||
int64_t time_num,
|
||||
int64_t time_den, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
auto *c = dynamic_cast<olive::FrameHashCache *>(base(cache));
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
return copy_string(
|
||||
c->get_valid_cache_filename(olive::Rational(time_num, time_den)),
|
||||
buf, buf_size);
|
||||
}
|
||||
|
||||
int oakrender_cache_get_passthroughs(OakRenderCache cache, int64_t *ranges,
|
||||
int max_ranges)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
|
||||
const std::vector<olive::PlaybackCache::Passthrough> &all =
|
||||
c->get_passthroughs();
|
||||
int count = static_cast<int>(all.size());
|
||||
if (ranges && max_ranges > 0) {
|
||||
int n = count < max_ranges ? count : max_ranges;
|
||||
for (int i = 0; i < n; i++) {
|
||||
ranges[i * 4] = all[i].in().numerator();
|
||||
ranges[i * 4 + 1] = all[i].in().denominator();
|
||||
ranges[i * 4 + 2] = all[i].out().numerator();
|
||||
ranges[i * 4 + 3] = all[i].out().denominator();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int oakrender_cache_get_timebase(OakRenderCache cache, int *num, int *den)
|
||||
{
|
||||
auto *c = dynamic_cast<olive::FrameHashCache *>(base(cache));
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
if (num) {
|
||||
*num = c->get_timebase().numerator();
|
||||
}
|
||||
if (den) {
|
||||
*den = c->get_timebase().denominator();
|
||||
}
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
void oakrender_cache_lock(OakRenderCache cache)
|
||||
{
|
||||
if (olive::PlaybackCache *c = base(cache)) {
|
||||
c->mutex().lock();
|
||||
}
|
||||
}
|
||||
|
||||
void oakrender_cache_unlock(OakRenderCache cache)
|
||||
{
|
||||
if (olive::PlaybackCache *c = base(cache)) {
|
||||
c->mutex().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
olive::PlaybackCache *oakrender_cache_get_native(OakRenderCache cache)
|
||||
{
|
||||
return base(cache);
|
||||
}
|
||||
|
||||
int oakrender_cache_set_timebase(OakRenderCache cache, int num, int den)
|
||||
{
|
||||
if (!cache.ctx || num <= 0 || den <= 0) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "color/colormanager/colormanager.h"
|
||||
#include "filefunctions.h"
|
||||
#include "lutlibrary.h"
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
|
||||
namespace
|
||||
@@ -105,6 +106,129 @@ int oakrender_color_processor_is_valid(OakColorProcessor processor)
|
||||
return p && p->ptr && p->ptr->get_processor() ? 1 : 0;
|
||||
}
|
||||
|
||||
OakColorProcessor oakrender_color_processor_create_transform(
|
||||
OakNodeColorManager manager, const char *input,
|
||||
OakColorTransform dest, int direction)
|
||||
{
|
||||
olive::ColorManager *native_manager =
|
||||
oaknode_colormanager_get_native(manager);
|
||||
const olive::ColorTransform *native_dest =
|
||||
oakcommon_colortransform_get_native(dest);
|
||||
if (!native_manager || !input || !*input || !native_dest) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
if (direction != OAKRENDER_COLOR_DIRECTION_NORMAL &&
|
||||
direction != OAKRENDER_COLOR_DIRECTION_INVERSE) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
try {
|
||||
auto *impl = new OakColorProcessorImpl;
|
||||
impl->ptr = olive::ColorProcessor::create(
|
||||
native_manager, input, *native_dest,
|
||||
direction == OAKRENDER_COLOR_DIRECTION_NORMAL ?
|
||||
olive::ColorProcessor::k_normal :
|
||||
olive::ColorProcessor::k_inverse);
|
||||
return oakrender_c_api::make_handle<OakColorProcessor>(
|
||||
impl, true, &oakrender_c_api::delete_as<OakColorProcessorImpl>);
|
||||
} catch (...) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
}
|
||||
|
||||
OakColorProcessor oakrender_color_processor_create_lut(
|
||||
OakNodeColorManager manager, const char *path, int direction)
|
||||
{
|
||||
olive::ColorManager *native_manager =
|
||||
oaknode_colormanager_get_native(manager);
|
||||
if (!native_manager || !path || !*path) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
if (direction != OAKRENDER_COLOR_DIRECTION_NORMAL &&
|
||||
direction != OAKRENDER_COLOR_DIRECTION_INVERSE) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
try {
|
||||
OCIO_NAMESPACE::FileTransformRcPtr transform =
|
||||
OCIO_NAMESPACE::FileTransform::Create();
|
||||
transform->setSrc(path);
|
||||
transform->setInterpolation(OCIO_NAMESPACE::INTERP_LINEAR);
|
||||
transform->setDirection(
|
||||
direction == OAKRENDER_COLOR_DIRECTION_NORMAL ?
|
||||
OCIO_NAMESPACE::TRANSFORM_DIR_FORWARD :
|
||||
OCIO_NAMESPACE::TRANSFORM_DIR_INVERSE);
|
||||
|
||||
auto *impl = new OakColorProcessorImpl;
|
||||
impl->ptr = olive::ColorProcessor::create(
|
||||
native_manager->get_config()->getProcessor(transform));
|
||||
return oakrender_c_api::make_handle<OakColorProcessor>(
|
||||
impl, true, &oakrender_c_api::delete_as<OakColorProcessorImpl>);
|
||||
} catch (...) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
}
|
||||
|
||||
OakColorProcessor oakrender_color_processor_create_grading_primary(
|
||||
OakNodeColorManager manager, int style)
|
||||
{
|
||||
olive::ColorManager *native_manager =
|
||||
oaknode_colormanager_get_native(manager);
|
||||
if (!native_manager || (style != OAKRENDER_GRADING_PRIMARY_LIN &&
|
||||
style != OAKRENDER_GRADING_PRIMARY_LOG)) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
try {
|
||||
OCIO_NAMESPACE::GradingPrimaryTransformRcPtr gp =
|
||||
OCIO_NAMESPACE::GradingPrimaryTransform::Create(
|
||||
style == OAKRENDER_GRADING_PRIMARY_LIN ?
|
||||
OCIO_NAMESPACE::GRADING_LIN :
|
||||
OCIO_NAMESPACE::GRADING_LOG);
|
||||
gp->makeDynamic();
|
||||
gp->setDirection(
|
||||
OCIO_NAMESPACE::TransformDirection::TRANSFORM_DIR_FORWARD);
|
||||
|
||||
auto *impl = new OakColorProcessorImpl;
|
||||
impl->ptr = olive::ColorProcessor::create(
|
||||
native_manager->get_config()->getProcessor(gp));
|
||||
return oakrender_c_api::make_handle<OakColorProcessor>(
|
||||
impl, true, &oakrender_c_api::delete_as<OakColorProcessorImpl>);
|
||||
} catch (...) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<olive::ColorProcessor> oakrender_color_processor_get_native(
|
||||
OakColorProcessor processor)
|
||||
{
|
||||
OakColorProcessorImpl *p =
|
||||
oakrender_c_api::to_native<OakColorProcessorImpl>(processor);
|
||||
return p ? p->ptr : nullptr;
|
||||
}
|
||||
|
||||
int oakrender_lut_is_supported_extension(const char *extension)
|
||||
{
|
||||
if (!extension) {
|
||||
return 0;
|
||||
}
|
||||
return olive::LUTLibrary::is_supported_extension(extension) ? 1 : 0;
|
||||
}
|
||||
|
||||
int oakrender_lut_supported_extensions_count(void)
|
||||
{
|
||||
return static_cast<int>(
|
||||
olive::LUTLibrary::supported_extensions().size());
|
||||
}
|
||||
|
||||
int oakrender_lut_supported_extension_at(int index, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
const std::vector<std::string> &all =
|
||||
olive::LUTLibrary::supported_extensions();
|
||||
if (index < 0 || index >= static_cast<int>(all.size())) {
|
||||
return OAKRENDER_E_NOT_FOUND;
|
||||
}
|
||||
return write_string(all[size_t(index)], buf, buf_size);
|
||||
}
|
||||
|
||||
int oakrender_color_processor_convert(OakColorProcessor processor,
|
||||
double ir, double ig, double ib,
|
||||
double ia, double *out_r, double *out_g,
|
||||
|
||||
@@ -161,6 +161,22 @@ int oakrender_cancel_request(int64_t request_id)
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_manager_available(void)
|
||||
{
|
||||
return olive::RenderManager::instance() != nullptr ? 1 : 0;
|
||||
}
|
||||
|
||||
void oakrender_cancel_video_tasks(int wait_for_done)
|
||||
{
|
||||
olive::RenderManager *manager = olive::RenderManager::instance();
|
||||
if (!manager) {
|
||||
return;
|
||||
}
|
||||
if (olive::PreviewAutoCacher *cacher = manager->get_cacher()) {
|
||||
cacher->cancel_video_tasks(wait_for_done != 0);
|
||||
}
|
||||
}
|
||||
|
||||
int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL)
|
||||
{
|
||||
olive::RenderManager *manager = olive::RenderManager::instance();
|
||||
|
||||
@@ -149,9 +149,11 @@ OakRenderTicket oakrender_ticket_render_frame(
|
||||
}
|
||||
}
|
||||
|
||||
if (params->cache) {
|
||||
rvp.add_cache(reinterpret_cast<olive::FrameHashCache *>(
|
||||
params->cache));
|
||||
if (params->cache.ctx) {
|
||||
if (auto *fc = dynamic_cast<olive::FrameHashCache *>(
|
||||
oakrender_cache_get_native(params->cache))) {
|
||||
rvp.add_cache(fc);
|
||||
}
|
||||
}
|
||||
|
||||
auto *watcher = new olive::RenderTicketWatcher();
|
||||
|
||||
Reference in New Issue
Block a user