Files
oak-editor/app/render/backend/osl/oslworker.cpp
T
2020-02-14 02:13:38 +11:00

354 lines
12 KiB
C++

#include "oslworker.h"
#include <OpenImageIO/imagebuf.h>
#include <QMatrix4x4>
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
#include "common/clamp.h"
#include "common/define.h"
#include "core.h"
#include "node/block/transition/transition.h"
#include "node/node.h"
#include "render/colormanager.h"
#include "render/pixelservice.h"
OSLWorker::OSLWorker(VideoRenderFrameCache* frame_cache,
OSL::ShadingSystem* shading_system,
OSLShaderCache* shader_cache,
ColorProcessorCache* color_cache,
QObject* parent) :
VideoRenderWorker(frame_cache, parent),
shading_system_(shading_system),
shader_cache_(shader_cache),
color_cache_(color_cache)
{
}
void OSLWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table)
{
// Ensure stream is video or image type
if (stream->type() != Stream::kVideo && stream->type() != Stream::kImage) {
return;
}
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
// Set up OCIO context
ColorProcessorPtr color_processor = color_cache_->Get(video_stream->colorspace());
if (!color_processor) {
// FIXME: We match with the colorspace string, but this won't change if the user sets a new config with a colorspace with the same string
color_processor = ColorProcessor::Create(video_stream->footage()->project()->color_manager()->GetConfig(),
video_stream->colorspace(),
OCIO::ROLE_SCENE_LINEAR);
color_cache_->Add(video_stream->colorspace(), color_processor);
}
// Convert frame to float for OCIO
if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) {
frame = PixelService::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F);
}
// If alpha is associated, disassociate for the color transform
if (video_stream->premultiplied_alpha()) {
ColorManager::DisassociateAlpha(frame);
}
// Perform color transform
color_processor->ConvertFrame(frame);
// Associate alpha
if (video_stream->premultiplied_alpha()) {
ColorManager::ReassociateAlpha(frame);
} else {
ColorManager::AssociateAlpha(frame);
}
OIIOImageBufRef buf = std::make_shared<OIIO::ImageBuf>(OIIO::ImageSpec(frame->width(), frame->height(), kRGBAChannels, OIIO::TypeDesc::FLOAT));
memcpy(buf->localpixels(), frame->data(), frame->allocated_size());
table->Push(NodeParam::kTexture, QVariant::fromValue(buf));
}
void OSLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable *output_params)
{
if (!node->IsAccelerated()) {
return;
}
OSL::ShaderGroupRef group = shader_cache_->Get(node->id());
if (!group) {
return;
}
int input_texture_count = 0;
OIIOImageBufRef iteration_buf = nullptr;
int iteration_id = -1;
foreach (NodeParam* param, node->parameters()) {
if (param->type() == NodeParam::kInput) {
// This variable is used in the shader, let's set it to our value
NodeInput* input = static_cast<NodeInput*>(param);
// Get value from database at this input
const NodeValueTable& input_data = input_params[input];
QVariant value = node->InputValueFromTable(input, input_data);
switch (input->data_type()) {
case NodeInput::kInt:
{
int val = value.toInt();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc::INT, &val);
break;
}
case NodeInput::kFloat:
{
double val = value.toDouble();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc::DOUBLE, &val);
break;
}
case NodeInput::kVec2:
{
QVector2D val = value.value<QVector2D>();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc(OIIO::TypeDesc::FLOAT, OIIO::TypeDesc::VEC2), &val);
break;
}
case NodeInput::kVec3:
{
QVector3D val = value.value<QVector3D>();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc(OIIO::TypeDesc::FLOAT, OIIO::TypeDesc::VEC3), &val);
break;
}
case NodeInput::kVec4:
{
QVector4D val = value.value<QVector4D>();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc(OIIO::TypeDesc::FLOAT, OIIO::TypeDesc::VEC4), &val);
break;
}
case NodeInput::kMatrix:
{
QMatrix4x4 val = value.value<QMatrix4x4>();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc(OIIO::TypeDesc::FLOAT, OIIO::TypeDesc::MATRIX44), &val);
break;
}
case NodeInput::kColor:
{
QVector4D val = value.value<QVector4D>();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc(OIIO::TypeDesc::FLOAT, OIIO::TypeDesc::VEC4, OIIO::TypeDesc::COLOR), &val);
break;
}
case NodeInput::kBoolean:
{
bool val = value.toBool();
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc::CHAR, &val);
break;
}
case NodeInput::kFootage:
case NodeInput::kTexture:
case NodeInput::kBuffer:
{
OIIOImageBufRef buf = value.value<OIIOImageBufRef>();
if (buf) {
QString tex_fn = ImageBufToTexture(*buf.get(), input_texture_count);
if (!tex_fn.isEmpty()) {
QByteArray tex_fn_bytes = tex_fn.toUtf8();
const char* tex_fn_c_str = tex_fn_bytes.constData();
if (!shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc::STRING, &tex_fn_c_str)) {
qDebug() << "Failed to set string parameter";
}
}
} else {
shading_system_->ReParameter(*group.get(), "layer1", param->id().toStdString(), OIIO::TypeDesc::STRING, nullptr);
}
if (node->AcceleratedCodeIterativeInput() == input) {
iteration_buf = buf;
iteration_id = input_texture_count;
}
/*
// Set enable flag if shader wants it
int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(input->id()));
if (enable_param_location > -1) {
shader->setUniformValue(enable_param_location,
tex_id > 0);
}
if (tex_id > 0) {
// Set texture resolution if shader wants it
int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(input->id()));
if (res_param_location > -1) {
shader->setUniformValue(res_param_location,
static_cast<GLfloat>(texture->texture()->width()),
static_cast<GLfloat>(texture->texture()->height()));
}
}
OpenGLRenderFunctions::PrepareToDraw(functions_);
*/
input_texture_count++;
break;
}
case NodeInput::kSamples:
case NodeInput::kText:
case NodeInput::kRational:
case NodeInput::kFont:
case NodeInput::kFile:
case NodeInput::kDecimal:
case NodeInput::kWholeNumber:
case NodeInput::kNumber:
case NodeInput::kString:
case NodeInput::kVector:
case NodeInput::kNone:
case NodeInput::kAny:
break;
}
}
}
// Provide some standard args
{
QVector2D resolution(static_cast<float>(video_params().width()), static_cast<float>(video_params().height()));
shading_system_->ReParameter(*group.get(),
"layer1",
"ove_resolution",
OIIO::TypeDesc(OIIO::TypeDesc::FLOAT, OIIO::TypeDesc::VEC2),
&resolution);
}
if (node->IsBlock() && static_cast<const Block*>(node)->type() == Block::kTransition) {
const TransitionBlock* transition_node = static_cast<const TransitionBlock*>(node);
// Provides total transition progress from 0.0 (start) - 1.0 (end)
double p = transition_node->GetTotalProgress(range.in());
shading_system_->ReParameter(*group.get(),
"layer1",
"ove_tprog_all",
OIIO::TypeDesc::DOUBLE,
&p);
// Provides progress of out section from 1.0 (start) - 0.0 (end)
p = transition_node->GetOutProgress(range.in());
shading_system_->ReParameter(*group.get(),
"layer1",
"ove_tprog_out",
OIIO::TypeDesc::DOUBLE,
&p);
// Provides progress of in section from 0.0 (start) - 1.0 (end)
p = transition_node->GetInProgress(range.in());
shading_system_->ReParameter(*group.get(),
"layer1",
"ove_tprog_in",
OIIO::TypeDesc::DOUBLE,
&p);
}
OIIOImageBufRef dest_buf = nullptr;
// Some nodes use multiple iterations for optimization
for (int iteration=0;iteration<node->AcceleratedCodeIterations();iteration++) {
// Set iteration number
shading_system_->ReParameter(*group.get(),
"layer1",
"ove_iteration",
OIIO::TypeDesc::INT,
&iteration);
if (iteration > 0) {
// Convert destination buffer into texture
QString s = ImageBufToTexture(*dest_buf.get(), iteration_id);
QByteArray s_bytes = s.toUtf8();
const char* s_c_str = s_bytes.constData();
// Set texture as iterative input
shading_system_->ReParameter(*group.get(), "layer1", node->AcceleratedCodeIterativeInput()->id().toStdString(), OIIO::TypeDesc::STRING, &s_c_str);
// Swap destination buffer and iteration buffer
std::swap(iteration_buf, dest_buf);
}
// Ensure dest_buf exists
if (!dest_buf) {
dest_buf = std::make_shared<OIIO::ImageBuf>(OIIO::ImageSpec(video_params().width(),
video_params().height(),
kRGBAChannels,
PixelService::GetPixelFormatInfo(video_params().format()).oiio_desc));
}
static OSL::ustring outputs[] = {OSL::ustring("Cout")};
OIIO::ImageBufAlgo::parallel_image_options popt;
#if OPENIMAGEIO_VERSION > 10902
popt.minitems = 4096;
popt.splitdir = OIIO::Split_Tile;
popt.recursive = true;
#endif
shade_image(*shading_system_,
*group.get(),
nullptr,
*dest_buf.get(),
outputs,
OSL::ShadePixelCenters,
OIIO::ROI(),
popt);
}
output_params->Push(NodeParam::kTexture, QVariant::fromValue(dest_buf));
}
void OSLWorker::TextureToBuffer(const QVariant &tex_in, QByteArray &buffer)
{
OIIOImageBufRef frame = tex_in.value<OIIOImageBufRef>();
memcpy(buffer.data(), frame->localpixels(), static_cast<size_t>(buffer.size()));
}
QString OSLWorker::ImageBufToTexture(const OpenImageIO_v2_1::ImageBuf &buf, int tex_no)
{
OIIO::ImageSpec config;
config.attribute("maketx:filtername", "lanczos3");
QString tex_fn = QStringLiteral("C:/Users/Matt/Desktop/temp-%1-%2.tx").arg(QString::number(reinterpret_cast<quintptr>(this)), QString::number(tex_no));
stringstream s;
if (!OIIO::ImageBufAlgo::make_texture(OIIO::ImageBufAlgo::MakeTextureMode::MakeTxTexture,
buf,
tex_fn.toStdString(),
config,
&s)) {
qCritical() << "Failed to make_texture:" << s.str().c_str();
return QString();
}
return tex_fn;
}