diff --git a/CMakeLists.txt b/CMakeLists.txt index 919c22a25..72670fb1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,8 @@ find_package(OpenImageIO REQUIRED) find_package(OpenShadingLanguage REQUIRED) +find_package(OpenEXR REQUIRED) + find_package(Qt5 5.6 REQUIRED COMPONENTS Core diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 1cbec6e8c..4a0ad75d5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -78,15 +78,17 @@ if(MSVC) "$<$:/O2>" ) else() - target_compile_options(${OLIVE_TARGET} PRIVATE -O2 -Werror -Wuninitialized -pedantic-errors -Wall -Wextra -Wconversion -Wsign-conversion) + target_compile_options(${OLIVE_TARGET} PRIVATE -O2 -Werror -Wuninitialized -pedantic-errors -Wall -Wextra -Wsign-conversion) endif() target_include_directories( ${OLIVE_TARGET} PRIVATE - ${OPENCOLORIO_INCLUDE_DIR} - ${OIIO_INCLUDE_DIRS} ${FFMPEG_INCLUDE_DIRS} + ${OCIO_INCLUDE_DIRS} + ${OIIO_INCLUDE_DIRS} + ${OSL_INCLUDE_DIRS} + ${OPENEXR_INCLUDE_DIRS} ) target_link_libraries(${OLIVE_TARGET} @@ -105,6 +107,8 @@ target_link_libraries(${OLIVE_TARGET} FFMPEG::swresample ${OCIO_LIBRARIES} ${OIIO_LIBRARIES} + ${OSL_LIBRARIES} + ${OPENEXR_LIBRARIES} ) set(OLIVE_TS_FILES diff --git a/app/render/backend/colorprocessorcache.h b/app/render/backend/colorprocessorcache.h index 9346b810b..f10de50b1 100644 --- a/app/render/backend/colorprocessorcache.h +++ b/app/render/backend/colorprocessorcache.h @@ -5,6 +5,6 @@ #include "render/colorprocessor.h" #include "rendercache.h" -using ColorProcessorCache = RenderCache; +using ColorProcessorCache = ThreadSafeRenderCache; #endif // COLORPROCESSORCACHE_H diff --git a/app/render/backend/osl/CMakeLists.txt b/app/render/backend/osl/CMakeLists.txt index 4d6aaf902..895199781 100644 --- a/app/render/backend/osl/CMakeLists.txt +++ b/app/render/backend/osl/CMakeLists.txt @@ -18,6 +18,9 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} render/backend/osl/oslbackend.h render/backend/osl/oslbackend.cpp + render/backend/osl/oslrenderer.h + render/backend/osl/oslrenderer.cpp + render/backend/osl/oslshadercache.h render/backend/osl/oslworker.h render/backend/osl/oslworker.cpp PARENT_SCOPE diff --git a/app/render/backend/osl/oslbackend.cpp b/app/render/backend/osl/oslbackend.cpp index 4d6d89ee6..58a8afa7d 100644 --- a/app/render/backend/osl/oslbackend.cpp +++ b/app/render/backend/osl/oslbackend.cpp @@ -1,13 +1,15 @@ #include "oslbackend.h" -#include +#include #include #include #include "oslworker.h" OSLBackend::OSLBackend(QObject *parent) : - VideoRenderBackend(parent) + VideoRenderBackend(parent), + shading_system_(nullptr), + renderer_(nullptr) { } @@ -22,10 +24,17 @@ bool OSLBackend::InitInternal() return false; } + renderer_ = new OSL::SimpleRenderer(); + + shading_system_ = new OSL::ShadingSystem(renderer_); + // Initiate one thread per CPU core for (int i=0;iSetParameters(params()); processors_.append(processor); } @@ -33,9 +42,54 @@ bool OSLBackend::InitInternal() return true; } +void OSLBackend::CloseInternal() +{ + VideoRenderBackend::CloseInternal(); + + shader_cache_.Clear(); + + delete shading_system_; + shading_system_ = nullptr; + + delete renderer_; + renderer_= nullptr; +} + bool OSLBackend::CompileInternal() { - //OSL::ShadingSystem; + OSL::ShadingSystem* system = shading_system_; + + OSL::OSLCompiler compiler; + + QList deps = viewer_node()->GetDependencies(); + foreach (Node* n, deps) { + if (!shader_cache_.Has(n->id())) { + if (n->IsAccelerated()) { + // FIXME: Compile here + + std::string oso_buffer; + + if (!compiler.compile_buffer(n->AcceleratedCodeFragment().toStdString(), + oso_buffer, + std::vector(), + "", + n->id().toStdString())) { + qWarning() << "Failed to compile" << n->id(); + return false; + } + + OSL::ShaderGroupRef ref = system->ShaderGroupBegin(n->id().toStdString()); + system->Shader("surface", n->id().toStdString(), "layer1"); + system->ShaderGroupEnd(); + + shader_cache_.Add(n->id(), ref); + + } else { + // Insert null + shader_cache_.Add(n->id(), nullptr); + } + } + } return true; } diff --git a/app/render/backend/osl/oslbackend.h b/app/render/backend/osl/oslbackend.h index 49f43f29f..1df736d84 100644 --- a/app/render/backend/osl/oslbackend.h +++ b/app/render/backend/osl/oslbackend.h @@ -1,7 +1,11 @@ #ifndef OSLBACKEND_H #define OSLBACKEND_H +#include + #include "../videorenderbackend.h" +#include "oslrenderer.h" +#include "oslshadercache.h" class OSLBackend : public VideoRenderBackend { @@ -14,12 +18,23 @@ public: protected: virtual bool InitInternal() override; + virtual void CloseInternal() override; + virtual bool CompileInternal() override; virtual void DecompileInternal() override; //virtual void ParamsChangedEvent() override; +private: + OSL::ShadingSystem* shading_system_; + + OSL::SimpleRenderer* renderer_; + + OSLShaderCache shader_cache_; + + ColorProcessorCache color_cache_; + }; #endif // OSLBACKEND_H diff --git a/app/render/backend/osl/oslrenderer.cpp b/app/render/backend/osl/oslrenderer.cpp new file mode 100644 index 000000000..9da05ff61 --- /dev/null +++ b/app/render/backend/osl/oslrenderer.cpp @@ -0,0 +1,456 @@ +/* +Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of Sony Pictures Imageworks nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "oslrenderer.h" +using namespace OSL; + +OSL_NAMESPACE_ENTER + +static ustring u_camera("camera"), u_screen("screen"); +static ustring u_NDC("NDC"), u_raster("raster"); +static ustring u_perspective("perspective"); +static ustring u_s("s"), u_t("t"); +static TypeDesc TypeFloatArray2 (TypeDesc::FLOAT, 2); +static TypeDesc TypeFloatArray4 (TypeDesc::FLOAT, 4); +static TypeDesc TypeIntArray2 (TypeDesc::INT, 2); + + + + +SimpleRenderer::SimpleRenderer () +{ + Matrix44 M; M.makeIdentity(); + camera_params (M, u_perspective, 90.0f, + 0.1f, 1000.0f, 256, 256); + + // Set up getters + m_attr_getters[ustring("osl:version")] = &SimpleRenderer::get_osl_version; + m_attr_getters[ustring("camera:resolution")] = &SimpleRenderer::get_camera_resolution; + m_attr_getters[ustring("camera:projection")] = &SimpleRenderer::get_camera_projection; + m_attr_getters[ustring("camera:pixelaspect")] = &SimpleRenderer::get_camera_pixelaspect; + m_attr_getters[ustring("camera:screen_window")] = &SimpleRenderer::get_camera_screen_window; + m_attr_getters[ustring("camera:fov")] = &SimpleRenderer::get_camera_fov; + m_attr_getters[ustring("camera:clip")] = &SimpleRenderer::get_camera_clip; + m_attr_getters[ustring("camera:clip_near")] = &SimpleRenderer::get_camera_clip_near; + m_attr_getters[ustring("camera:clip_far")] = &SimpleRenderer::get_camera_clip_far; + m_attr_getters[ustring("camera:shutter")] = &SimpleRenderer::get_camera_shutter; + m_attr_getters[ustring("camera:shutter_open")] = &SimpleRenderer::get_camera_shutter_open; + m_attr_getters[ustring("camera:shutter_close")] = &SimpleRenderer::get_camera_shutter_close; +} + + + +int +SimpleRenderer::supports (string_view) const +{ + return false; +} + + + +void +SimpleRenderer::camera_params (const Matrix44 &world_to_camera, + ustring projection, float hfov, + float hither, float yon, + int xres, int yres) +{ + m_world_to_camera = world_to_camera; + m_projection = projection; + m_fov = hfov; + m_pixelaspect = 1.0f; // hard-coded + m_hither = hither; + m_yon = yon; + m_shutter[0] = 0.0f; m_shutter[1] = 1.0f; // hard-coded + float frame_aspect = float(xres)/float(yres) * m_pixelaspect; + m_screen_window[0] = -frame_aspect; + m_screen_window[1] = -1.0f; + m_screen_window[2] = frame_aspect; + m_screen_window[3] = 1.0f; + m_xres = xres; + m_yres = yres; +} + + + +bool +SimpleRenderer::get_matrix (ShaderGlobals *, Matrix44 &result, + TransformationPtr xform, + float) +{ + // SimpleRenderer doesn't understand motion blur and transformations + // are just simple 4x4 matrices. + result = *reinterpret_cast(xform); + return true; +} + + + +bool +SimpleRenderer::get_matrix (ShaderGlobals *, Matrix44 &result, + ustring from, float) +{ + TransformMap::const_iterator found = m_named_xforms.find (from); + if (found != m_named_xforms.end()) { + result = *(found->second); + return true; + } else { + return false; + } +} + + + +bool +SimpleRenderer::get_matrix (ShaderGlobals *, Matrix44 &result, + TransformationPtr xform) +{ + // SimpleRenderer doesn't understand motion blur and transformations + // are just simple 4x4 matrices. + result = *reinterpret_cast(xform); + return true; +} + + + +bool +SimpleRenderer::get_matrix (ShaderGlobals *, Matrix44 &result, + ustring from) +{ + // SimpleRenderer doesn't understand motion blur, so we never fail + // on account of time-varying transformations. + TransformMap::const_iterator found = m_named_xforms.find (from); + if (found != m_named_xforms.end()) { + result = *(found->second); + return true; + } else { + return false; + } +} + + + +bool +SimpleRenderer::get_inverse_matrix (ShaderGlobals *, Matrix44 &result, + ustring to, float) +{ + if (to == u_camera || to == u_screen || to == u_NDC || to == u_raster) { + Matrix44 M = m_world_to_camera; + if (to == u_screen || to == u_NDC || to == u_raster) { + float depthrange = (double)m_yon-(double)m_hither; + if (m_projection == u_perspective) { + float tanhalffov = tanf (0.5f * m_fov * M_PI/180.0); + Matrix44 camera_to_screen (1/tanhalffov, 0, 0, 0, + 0, 1/tanhalffov, 0, 0, + 0, 0, m_yon/depthrange, 1, + 0, 0, -m_yon*m_hither/depthrange, 0); + M = M * camera_to_screen; + } else { + Matrix44 camera_to_screen (1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1/depthrange, 0, + 0, 0, -m_hither/depthrange, 1); + M = M * camera_to_screen; + } + if (to == u_NDC || to == u_raster) { + float screenleft = -1.0, screenwidth = 2.0; + float screenbottom = -1.0, screenheight = 2.0; + Matrix44 screen_to_ndc (1/screenwidth, 0, 0, 0, + 0, 1/screenheight, 0, 0, + 0, 0, 1, 0, + -screenleft/screenwidth, -screenbottom/screenheight, 0, 1); + M = M * screen_to_ndc; + if (to == u_raster) { + Matrix44 ndc_to_raster (m_xres, 0, 0, 0, + 0, m_yres, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + M = M * ndc_to_raster; + } + } + } + result = M; + return true; + } + + TransformMap::const_iterator found = m_named_xforms.find (to); + if (found != m_named_xforms.end()) { + result = *(found->second); + result.invert(); + return true; + } else { + return false; + } +} + + + +void +SimpleRenderer::name_transform (const char *name, const OSL::Matrix44 &xform) +{ + std::shared_ptr M (new OSL::Matrix44 (xform)); + m_named_xforms[ustring(name)] = M; +} + + + +bool +SimpleRenderer::get_array_attribute (ShaderGlobals *sg, bool derivatives, ustring object, + TypeDesc type, ustring name, + int index, void *val) +{ + AttrGetterMap::const_iterator g = m_attr_getters.find (name); + if (g != m_attr_getters.end()) { + AttrGetter getter = g->second; + return (this->*(getter)) (sg, derivatives, object, type, name, val); + } + + // If no named attribute was found, allow userdata to bind to the + // attribute request. + if (object.empty() && index == -1) + return get_userdata (derivatives, name, type, sg, val); + + return false; +} + + + +bool +SimpleRenderer::get_attribute (ShaderGlobals *sg, bool derivatives, ustring object, + TypeDesc type, ustring name, void *val) +{ + return get_array_attribute (sg, derivatives, object, + type, name, -1, val); +} + + + +bool +SimpleRenderer::get_userdata (bool derivatives, ustring name, TypeDesc type, + ShaderGlobals *sg, void *val) +{ + // Just to illustrate how this works, respect s and t userdata, filled + // in with the uv coordinates. In a real renderer, it would probably + // look up something specific to the primitive, rather than have hard- + // coded names. + + if (name == u_s && type == TypeDesc::TypeFloat) { + ((float *)val)[0] = sg->u; + if (derivatives) { + ((float *)val)[1] = sg->dudx; + ((float *)val)[2] = sg->dudy; + } + return true; + } + if (name == u_t && type == TypeDesc::TypeFloat) { + ((float *)val)[0] = sg->v; + if (derivatives) { + ((float *)val)[1] = sg->dvdx; + ((float *)val)[2] = sg->dvdy; + } + return true; + } + + return false; +} + + +bool +SimpleRenderer::get_osl_version (ShaderGlobals *, bool, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeDesc::TypeInt) { + ((int *)val)[0] = OSL_VERSION; + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_resolution (ShaderGlobals *, bool, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeIntArray2) { + ((int *)val)[0] = m_xres; + ((int *)val)[1] = m_yres; + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_projection (ShaderGlobals *, bool, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeDesc::TypeString) { + ((ustring *)val)[0] = m_projection; + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_fov (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + // N.B. in a real rederer, this may be time-dependent + if (type == TypeDesc::TypeFloat) { + ((float *)val)[0] = m_fov; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_pixelaspect (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeDesc::TypeFloat) { + ((float *)val)[0] = m_pixelaspect; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_clip (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeFloatArray2) { + ((float *)val)[0] = m_hither; + ((float *)val)[1] = m_yon; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_clip_near (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeDesc::TypeFloat) { + ((float *)val)[0] = m_hither; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_clip_far (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeDesc::TypeFloat) { + ((float *)val)[0] = m_yon; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + + +bool +SimpleRenderer::get_camera_shutter (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeFloatArray2) { + ((float *)val)[0] = m_shutter[0]; + ((float *)val)[1] = m_shutter[1]; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_shutter_open (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeDesc::TypeFloat) { + ((float *)val)[0] = m_shutter[0]; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_shutter_close (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + if (type == TypeDesc::TypeFloat) { + ((float *)val)[0] = m_shutter[1]; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + +bool +SimpleRenderer::get_camera_screen_window (ShaderGlobals *, bool derivs, ustring, + TypeDesc type, ustring, void *val) +{ + // N.B. in a real rederer, this may be time-dependent + if (type == TypeFloatArray4) { + ((float *)val)[0] = m_screen_window[0]; + ((float *)val)[1] = m_screen_window[1]; + ((float *)val)[2] = m_screen_window[2]; + ((float *)val)[3] = m_screen_window[3]; + if (derivs) + memset ((char *)val+type.size(), 0, 2*type.size()); + return true; + } + return false; +} + + + + +OSL_NAMESPACE_EXIT diff --git a/app/render/backend/osl/oslrenderer.h b/app/render/backend/osl/oslrenderer.h new file mode 100644 index 000000000..1af5ce9ae --- /dev/null +++ b/app/render/backend/osl/oslrenderer.h @@ -0,0 +1,129 @@ +/* +Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of Sony Pictures Imageworks nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include +#include +#include + +OSL_NAMESPACE_ENTER + +class SimpleRenderer : public RendererServices +{ +public: + // Just use 4x4 matrix for transformations + typedef Matrix44 Transformation; + + SimpleRenderer (); + ~SimpleRenderer () { } + + virtual int supports (string_view feature) const; + virtual bool get_matrix (ShaderGlobals *sg, Matrix44 &result, + TransformationPtr xform, + float time); + virtual bool get_matrix (ShaderGlobals *sg, Matrix44 &result, + ustring from, float time); + virtual bool get_matrix (ShaderGlobals *sg, Matrix44 &result, + TransformationPtr xform); + virtual bool get_matrix (ShaderGlobals *sg, Matrix44 &result, + ustring from); + virtual bool get_inverse_matrix (ShaderGlobals *sg, Matrix44 &result, + ustring to, float time); + + void name_transform (const char *name, const Transformation &xform); + + virtual bool get_array_attribute (ShaderGlobals *sg, bool derivatives, + ustring object, TypeDesc type, ustring name, + int index, void *val ); + virtual bool get_attribute (ShaderGlobals *sg, bool derivatives, ustring object, + TypeDesc type, ustring name, void *val); + virtual bool get_userdata (bool derivatives, ustring name, TypeDesc type, + ShaderGlobals *sg, void *val); + + // Super simple camera and display parameters. Many options not + // available, no motion blur, etc. + void camera_params (const Matrix44 &world_to_camera, ustring projection, + float hfov, float hither, float yon, + int xres, int yres); + +private: + // Camera parameters + Matrix44 m_world_to_camera; + ustring m_projection; + float m_fov, m_pixelaspect, m_hither, m_yon; + float m_shutter[2]; + float m_screen_window[4]; + int m_xres, m_yres; + + // Named transforms + typedef std::map > TransformMap; + TransformMap m_named_xforms; + + // Attribute and userdata retrieval -- for fast dispatch, use a hash + // table to map attribute names to functions that retrieve them. We + // imagine this to be fairly quick, but for a performance-critical + // renderer, we would encourage benchmarking various methods and + // alternate data structures. + typedef bool (SimpleRenderer::*AttrGetter)(ShaderGlobals *sg, bool derivs, + ustring object, TypeDesc type, + ustring name, void *val); + typedef std::unordered_map AttrGetterMap; + AttrGetterMap m_attr_getters; + + // Attribute getters + bool get_osl_version (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_resolution (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_projection (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_fov (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_pixelaspect (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_clip (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_clip_near (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_clip_far (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_shutter (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_shutter_open (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_shutter_close (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + bool get_camera_screen_window (ShaderGlobals *sg, bool derivs, ustring object, + TypeDesc type, ustring name, void *val); + +}; + +OSL_NAMESPACE_EXIT diff --git a/app/render/backend/osl/oslshadercache.h b/app/render/backend/osl/oslshadercache.h new file mode 100644 index 000000000..6c3c5be9f --- /dev/null +++ b/app/render/backend/osl/oslshadercache.h @@ -0,0 +1,11 @@ +#ifndef OSLSHADERMAP_H +#define OSLSHADERMAP_H + +#include +#include + +#include "../rendercache.h" + +using OSLShaderCache = ThreadSafeRenderCache; + +#endif // OSLSHADERMAP_H diff --git a/app/render/backend/osl/oslworker.cpp b/app/render/backend/osl/oslworker.cpp index 86312e482..448830da0 100644 --- a/app/render/backend/osl/oslworker.cpp +++ b/app/render/backend/osl/oslworker.cpp @@ -1,5 +1,11 @@ #include "oslworker.h" +#include +#include +#include +#include +#include + #include "common/clamp.h" #include "core.h" #include "node/block/transition/transition.h" @@ -7,28 +13,292 @@ #include "render/colormanager.h" #include "render/pixelservice.h" -OSLWorker::OSLWorker(VideoRenderFrameCache *frame_cache, QObject *parent) : - VideoRenderWorker(frame_cache, parent) +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) { - Q_UNUSED(stream) - Q_UNUSED(frame) - Q_UNUSED(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) { - Q_UNUSED(node) - Q_UNUSED(range) - Q_UNUSED(input_params) - Q_UNUSED(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) { - Q_UNUSED(tex_in) - Q_UNUSED(buffer) + FramePtr frame = tex_in.value(); + + memcpy(buffer.data(), frame->data(), static_cast(buffer.size())); } diff --git a/app/render/backend/osl/oslworker.h b/app/render/backend/osl/oslworker.h index af13c4187..2edd33be6 100644 --- a/app/render/backend/osl/oslworker.h +++ b/app/render/backend/osl/oslworker.h @@ -1,13 +1,20 @@ #ifndef OSLWORKER_H #define OSLWORKER_H +#include +#include + #include "../videorenderworker.h" +#include "oslshadercache.h" class OSLWorker : public VideoRenderWorker { Q_OBJECT public: OSLWorker(VideoRenderFrameCache* frame_cache, + OSL::ShadingSystem* shading_system, + OSLShaderCache* shader_cache, + ColorProcessorCache* color_cache, QObject* parent = nullptr); protected: @@ -17,6 +24,13 @@ protected: virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) override; +private: + OSL::ShadingSystem* shading_system_; + + OSLShaderCache* shader_cache_; + + ColorProcessorCache* color_cache_; + }; #endif // OSLWORKER_H diff --git a/app/render/backend/rendercache.h b/app/render/backend/rendercache.h index 1a015168b..e567de386 100644 --- a/app/render/backend/rendercache.h +++ b/app/render/backend/rendercache.h @@ -19,7 +19,7 @@ public: bool Has(K key) const {return values_.contains(key);} private: - QMap values_; + QHash values_; }; template @@ -55,7 +55,7 @@ public: } private: - QMap values_; + QHash values_; QMutex lock_; diff --git a/cmake/FindOpenEXR.cmake b/cmake/FindOpenEXR.cmake new file mode 100644 index 000000000..f036c742e --- /dev/null +++ b/cmake/FindOpenEXR.cmake @@ -0,0 +1,94 @@ +include(FindPackageHandleStandardArgs) + +find_path(OpenEXR_INCLUDE_DIRS OpenEXR/OpenEXRConfig.h) +find_path(OPENEXR_INCLUDE_PATHS NAMES ImfRgbaFile.h PATH_SUFFIXES OpenEXR) + +file(STRINGS "${OpenEXR_INCLUDE_DIRS}/OpenEXR/OpenEXRConfig.h" OPENEXR_CONFIG_H) + +string(REGEX REPLACE "^.*define OPENEXR_VERSION_MAJOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MAJOR "${OPENEXR_CONFIG_H}") +string(REGEX REPLACE "^.*define OPENEXR_VERSION_MINOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MINOR "${OPENEXR_CONFIG_H}") +set(OpenEXR_LIB_SUFFIX "${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}") + +include(SelectLibraryConfigurations) + +if(NOT OpenEXR_BASE_LIBRARY) + find_library(OpenEXR_BASE_LIBRARY_RELEASE NAMES IlmImf-${OpenEXR_LIB_SUFFIX}) + find_library(OpenEXR_BASE_LIBRARY_DEBUG NAMES IlmImf-${OpenEXR_LIB_SUFFIX}_d) + select_library_configurations(OpenEXR_BASE) +endif() + +if(NOT OpenEXR_UTIL_LIBRARY) + find_library(OpenEXR_UTIL_LIBRARY_RELEASE NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}) + find_library(OpenEXR_UTIL_LIBRARY_DEBUG NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}_d) + select_library_configurations(OpenEXR_UTIL) +endif() + +if(NOT OpenEXR_HALF_LIBRARY) + find_library(OpenEXR_HALF_LIBRARY_RELEASE NAMES Half-${OpenEXR_LIB_SUFFIX}) + find_library(OpenEXR_HALF_LIBRARY_DEBUG NAMES Half-${OpenEXR_LIB_SUFFIX}_d) + select_library_configurations(OpenEXR_HALF) +endif() + +if(NOT OpenEXR_IEX_LIBRARY) + find_library(OpenEXR_IEX_LIBRARY_RELEASE NAMES Iex-${OpenEXR_LIB_SUFFIX}) + find_library(OpenEXR_IEX_LIBRARY_DEBUG NAMES Iex-${OpenEXR_LIB_SUFFIX}_d) + select_library_configurations(OpenEXR_IEX) +endif() + +if(NOT OpenEXR_MATH_LIBRARY) + find_library(OpenEXR_MATH_LIBRARY_RELEASE NAMES Imath-${OpenEXR_LIB_SUFFIX}) + find_library(OpenEXR_MATH_LIBRARY_DEBUG NAMES Imath-${OpenEXR_LIB_SUFFIX}_d) + select_library_configurations(OpenEXR_MATH) +endif() + +if(NOT OpenEXR_THREAD_LIBRARY) + find_library(OpenEXR_THREAD_LIBRARY_RELEASE NAMES IlmThread-${OpenEXR_LIB_SUFFIX}) + find_library(OpenEXR_THREAD_LIBRARY_DEBUG NAMES IlmThread-${OpenEXR_LIB_SUFFIX}_d) + select_library_configurations(OpenEXR_THREAD) +endif() + +if(NOT OpenEXR_IEXMATH_LIBRARY) + find_library(OpenEXR_IEXMATH_LIBRARY_RELEASE NAMES IexMath-${OpenEXR_LIB_SUFFIX}) + find_library(OpenEXR_IEXMATH_LIBRARY_DEBUG NAMES IexMath-${OpenEXR_LIB_SUFFIX}_d) + select_library_configurations(OpenEXR_IEXMATH) +endif() + +set(OPENEXR_HALF_LIBRARY "${OpenEXR_HALF_LIBRARY}") +set(OPENEXR_Half_LIBRARY "${OpenEXR_HALF_LIBRARY}") +set(OPENEXR_IEX_LIBRARY "${OpenEXR_IEX_LIBRARY}") +set(OPENEXR_Iex_LIBRARY "${OpenEXR_IEX_LIBRARY}") +set(OPENEXR_IMATH_LIBRARY "${OpenEXR_MATH_LIBRARY}") +set(OPENEXR_ILMIMF_LIBRARY "${OpenEXR_BASE_LIBRARY}") +set(OPENEXR_IlmImf_LIBRARY "${OpenEXR_BASE_LIBRARY}") +set(OPENEXR_ILMIMFUTIL_LIBRARY "${OpenEXR_UTIL_LIBRARY}") +set(OPENEXR_ILMTHREAD_LIBRARY "${OpenEXR_THREAD_LIBRARY}") + +set(OpenEXR_LIBRARY "${OpenEXR_BASE_LIBRARY}") + +set(OpenEXR_LIBRARIES + ${OpenEXR_LIBRARY} + ${OpenEXR_MATH_LIBRARY} + ${OpenEXR_IEXMATH_LIBRARY} + ${OpenEXR_UTIL_LIBRARY} + ${OpenEXR_HALF_LIBRARY} + ${OpenEXR_IEX_LIBRARY} + ${OpenEXR_THREAD_LIBRARY} +) + +set(OPENEXR_LIBRARIES + ${OPENEXR_HALF_LIBRARY} + ${OPENEXR_IEX_LIBRARY} + ${OPENEXR_IMATH_LIBRARY} + ${OPENEXR_ILMIMF_LIBRARY} + ${OPENEXR_ILMTHREAD_LIBRARY} +) + +set(OpenEXR_INCLUDE_DIR ${OpenEXR_INCLUDE_DIRS}) +set(OPENEXR_INCLUDE_DIRS ${OpenEXR_INCLUDE_DIRS}) +set(OPENEXR_INCLUDE_DIR ${OPENEXR_INCLUDE_PATHS}) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenEXR REQUIRED_VARS OpenEXR_LIBRARIES OpenEXR_INCLUDE_DIRS) + +if(OpenEXR_FOUND) + set(OPENEXR_FOUND 1) +endif()