#include "oslworker.h" #include #include #include #include #include #include "common/clamp.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(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); } // If alpha is associated, disassociate for the color transform if (video_stream->premultiplied_alpha()) { ColorManager::DisassociateAlpha(frame); } // Convert frame to float for OCIO if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) { frame = PixelService::ConvertPixelFormat(frame, frame->format()); } // Perform color transform color_processor->ConvertFrame(frame); // Associate alpha if (video_stream->premultiplied_alpha()) { ColorManager::ReassociateAlpha(frame); } else { ColorManager::AssociateAlpha(frame); } table->Push(NodeParam::kTexture, QVariant::fromValue(frame)); } 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; } // Create the output textures QList dst_refs; dst_refs.append(texture_cache_.Get(ctx_, video_params_)); GLuint iterative_input = 0; // If this node requires multiple iterations, get a texture for it too if (node->AcceleratedCodeIterations() > 1 && node->AcceleratedCodeIterativeInput()) { dst_refs.append(texture_cache_.Get(ctx_, video_params_)); } 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(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(); 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(); 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(); 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(); 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(); 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: { OpenGLTextureCache::ReferencePtr texture = value.value(); functions_->glActiveTexture(GL_TEXTURE0 + input_texture_count); GLuint tex_id = texture ? texture->texture()->texture() : 0; functions_->glBindTexture(GL_TEXTURE_2D, tex_id); // Set value to bound texture shader->setUniformValue(variable_location, 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(texture->texture()->width()), static_cast(texture->texture()->height())); } } // If this texture binding is the iterative input, set it here if (input == node->AcceleratedCodeIterativeInput()) { iterative_input = input_texture_count; } 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(video_params().width()), static_cast(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(node)->type() == Block::kTransition) { const TransitionBlock* transition_node = static_cast(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); } // Some nodes use multiple iterations for optimization OpenGLTextureCache::ReferencePtr output_tex; for (int iteration=0;iterationAcceleratedCodeIterations();iteration++) { // If this is not the first iteration, set the parameter that will receive the last iteration's texture OpenGLTextureCache::ReferencePtr source_tex = dst_refs.at((iteration+1)%dst_refs.size()); OpenGLTextureCache::ReferencePtr destination_tex = dst_refs.at(iteration%dst_refs.size()); // Set iteration number shading_system_->ReParameter(*group.get(), "layer1", "ove_iteration", OIIO::TypeDesc::INT, &iteration); if (iteration > 0) { functions_->glActiveTexture(GL_TEXTURE0 + iterative_input); functions_->glBindTexture(GL_TEXTURE_2D, source_tex->texture()->texture()); } destination_tex->texture(); // DESTINATION TEXTURE static OIIO::array_view 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 OIIO::ImageBuf buffer; shade_image(*shading_system_, *group.get(), nullptr, buffer, outputs, OSL::ShadePixelCenters, OIIO::ROI(), popt); // Update output reference to the last texture we wrote to output_tex = destination_tex; } output_params->Push(NodeParam::kTexture, QVariant::fromValue(output_tex)); } void OSLWorker::TextureToBuffer(const QVariant &tex_in, QByteArray &buffer) { FramePtr frame = tex_in.value(); memcpy(buffer.data(), frame->data(), static_cast(buffer.size())); }