Files
blender/intern/cycles/hydra/render_delegate.cpp
Brecht Van Lommel 0de0950ad5 Cycles: various Linux build fixes related to Hydra render delegate
* Add missing GLEW and hgiGL libraries for Hydra
* Fix wrong case sensitive include
* Fix link errors by adding external libs to static Hydra lib
* Work around weird Hydra link error with MAX_SAMPLES
* Use Embree by default for Hydra
* Sync external libs code with standalone
* Update version number to match Blender
* Remove unneeded CLEW/GLEW from test executable

None of this should affect Cycles in Blender.

Ref T96731
2022-04-07 19:52:53 +02:00

516 lines
14 KiB
C++

/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/render_delegate.h"
#include "hydra/camera.h"
#include "hydra/curves.h"
#include "hydra/field.h"
#include "hydra/instancer.h"
#include "hydra/light.h"
#include "hydra/material.h"
#include "hydra/mesh.h"
#include "hydra/node_util.h"
#include "hydra/pointcloud.h"
#include "hydra/render_buffer.h"
#include "hydra/render_pass.h"
#include "hydra/session.h"
#include "hydra/volume.h"
#include "scene/integrator.h"
#include "scene/scene.h"
#include "session/session.h"
#include <pxr/base/tf/getenv.h>
#include <pxr/imaging/hd/extComputation.h>
#include <pxr/imaging/hgi/tokens.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(cycles)
(openvdbAsset)
);
TF_DEFINE_PRIVATE_TOKENS(HdCyclesRenderSettingsTokens,
((device, "cycles:device"))
((threads, "cycles:threads"))
((time_limit, "cycles:time_limit"))
((samples, "cycles:samples"))
((sample_offset, "cycles:sample_offset"))
);
// clang-format on
namespace {
const TfTokenVector kSupportedRPrimTypes = {
HdPrimTypeTokens->basisCurves,
HdPrimTypeTokens->mesh,
HdPrimTypeTokens->points,
#ifdef WITH_OPENVDB
HdPrimTypeTokens->volume,
#endif
};
const TfTokenVector kSupportedSPrimTypes = {
HdPrimTypeTokens->camera,
HdPrimTypeTokens->material,
HdPrimTypeTokens->diskLight,
HdPrimTypeTokens->distantLight,
HdPrimTypeTokens->domeLight,
HdPrimTypeTokens->rectLight,
HdPrimTypeTokens->sphereLight,
HdPrimTypeTokens->extComputation,
};
const TfTokenVector kSupportedBPrimTypes = {
HdPrimTypeTokens->renderBuffer,
#ifdef WITH_OPENVDB
_tokens->openvdbAsset,
#endif
};
SessionParams GetSessionParams(const HdRenderSettingsMap &settings)
{
SessionParams params;
params.threads = 0;
params.background = false;
params.use_resolution_divider = false;
HdRenderSettingsMap::const_iterator it;
// Pull all setting that contribute to device creation first
it = settings.find(HdCyclesRenderSettingsTokens->threads);
if (it != settings.end()) {
params.threads = VtValue::Cast<int>(it->second).GetWithDefault(params.threads);
}
// Get the Cycles device from settings or environment, falling back to CPU
std::string deviceType = Device::string_from_type(DEVICE_CPU);
it = settings.find(HdCyclesRenderSettingsTokens->device);
if (it != settings.end()) {
deviceType = VtValue::Cast<std::string>(it->second).GetWithDefault(deviceType);
}
else {
const std::string deviceTypeEnv = TfGetenv("CYCLES_DEVICE");
if (!deviceTypeEnv.empty()) {
deviceType = deviceTypeEnv;
}
}
// Move to all uppercase for Device::type_from_string
std::transform(deviceType.begin(), deviceType.end(), deviceType.begin(), ::toupper);
vector<DeviceInfo> devices = Device::available_devices(
DEVICE_MASK(Device::type_from_string(deviceType.c_str())));
if (devices.empty()) {
devices = Device::available_devices(DEVICE_MASK_CPU);
if (!devices.empty()) {
params.device = devices.front();
}
}
else {
params.device = Device::get_multi_device(devices, params.threads, params.background);
}
return params;
}
} // namespace
HdCyclesDelegate::HdCyclesDelegate(const HdRenderSettingsMap &settingsMap, Session *session_)
: HdRenderDelegate()
{
_renderParam = session_ ? std::make_unique<HdCyclesSession>(session_) :
std::make_unique<HdCyclesSession>(GetSessionParams(settingsMap));
// If the delegate owns the session, pull any remaining settings
if (!session_) {
for (const auto &setting : settingsMap) {
// Skip over the settings known to be used for initialization only
if (setting.first == HdCyclesRenderSettingsTokens->device ||
setting.first == HdCyclesRenderSettingsTokens->threads) {
continue;
}
SetRenderSetting(setting.first, setting.second);
}
}
}
HdCyclesDelegate::~HdCyclesDelegate()
{
}
void HdCyclesDelegate::SetDrivers(const HdDriverVector &drivers)
{
for (HdDriver *hdDriver : drivers) {
if (hdDriver->name == HgiTokens->renderDriver && hdDriver->driver.IsHolding<Hgi *>()) {
_hgi = hdDriver->driver.UncheckedGet<Hgi *>();
break;
}
}
}
bool HdCyclesDelegate::IsDisplaySupported() const
{
#ifdef _WIN32
return _hgi && _hgi->GetAPIName() == HgiTokens->OpenGL;
#else
return false;
#endif
}
const TfTokenVector &HdCyclesDelegate::GetSupportedRprimTypes() const
{
return kSupportedRPrimTypes;
}
const TfTokenVector &HdCyclesDelegate::GetSupportedSprimTypes() const
{
return kSupportedSPrimTypes;
}
const TfTokenVector &HdCyclesDelegate::GetSupportedBprimTypes() const
{
return kSupportedBPrimTypes;
}
HdRenderParam *HdCyclesDelegate::GetRenderParam() const
{
return _renderParam.get();
}
HdResourceRegistrySharedPtr HdCyclesDelegate::GetResourceRegistry() const
{
return HdResourceRegistrySharedPtr();
}
bool HdCyclesDelegate::IsPauseSupported() const
{
return true;
}
bool HdCyclesDelegate::Pause()
{
_renderParam->session->set_pause(true);
return true;
}
bool HdCyclesDelegate::Resume()
{
_renderParam->session->set_pause(false);
return true;
}
HdRenderPassSharedPtr HdCyclesDelegate::CreateRenderPass(HdRenderIndex *index,
const HdRprimCollection &collection)
{
return HdRenderPassSharedPtr(new HdCyclesRenderPass(index, collection, _renderParam.get()));
}
HdInstancer *HdCyclesDelegate::CreateInstancer(HdSceneDelegate *delegate,
const SdfPath &instancerId
#if PXR_VERSION < 2102
,
const SdfPath &parentId
#endif
)
{
return new HdCyclesInstancer(delegate,
instancerId
#if PXR_VERSION < 2102
,
parentId
#endif
);
}
void HdCyclesDelegate::DestroyInstancer(HdInstancer *instancer)
{
delete instancer;
}
HdRprim *HdCyclesDelegate::CreateRprim(const TfToken &typeId,
const SdfPath &rprimId
#if PXR_VERSION < 2102
,
const SdfPath &instancerId
#endif
)
{
if (typeId == HdPrimTypeTokens->mesh) {
return new HdCyclesMesh(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
);
}
if (typeId == HdPrimTypeTokens->basisCurves) {
return new HdCyclesCurves(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
);
}
if (typeId == HdPrimTypeTokens->points) {
return new HdCyclesPoints(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
);
}
#ifdef WITH_OPENVDB
if (typeId == HdPrimTypeTokens->volume) {
return new HdCyclesVolume(rprimId
# if PXR_VERSION < 2102
,
instancerId
# endif
);
}
#endif
TF_CODING_ERROR("Unknown Rprim type %s", typeId.GetText());
return nullptr;
}
void HdCyclesDelegate::DestroyRprim(HdRprim *rPrim)
{
delete rPrim;
}
HdSprim *HdCyclesDelegate::CreateSprim(const TfToken &typeId, const SdfPath &sprimId)
{
if (typeId == HdPrimTypeTokens->camera) {
return new HdCyclesCamera(sprimId);
}
if (typeId == HdPrimTypeTokens->material) {
return new HdCyclesMaterial(sprimId);
}
if (typeId == HdPrimTypeTokens->diskLight || typeId == HdPrimTypeTokens->distantLight ||
typeId == HdPrimTypeTokens->domeLight || typeId == HdPrimTypeTokens->rectLight ||
typeId == HdPrimTypeTokens->sphereLight) {
return new HdCyclesLight(sprimId, typeId);
}
if (typeId == HdPrimTypeTokens->extComputation) {
return new HdExtComputation(sprimId);
}
TF_CODING_ERROR("Unknown Sprim type %s", typeId.GetText());
return nullptr;
}
HdSprim *HdCyclesDelegate::CreateFallbackSprim(const TfToken &typeId)
{
return CreateSprim(typeId, SdfPath::EmptyPath());
}
void HdCyclesDelegate::DestroySprim(HdSprim *sPrim)
{
delete sPrim;
}
HdBprim *HdCyclesDelegate::CreateBprim(const TfToken &typeId, const SdfPath &bprimId)
{
if (typeId == HdPrimTypeTokens->renderBuffer) {
return new HdCyclesRenderBuffer(bprimId);
}
#ifdef WITH_OPENVDB
if (typeId == _tokens->openvdbAsset) {
return new HdCyclesField(bprimId, typeId);
}
#endif
TF_RUNTIME_ERROR("Unknown Bprim type %s", typeId.GetText());
return nullptr;
}
HdBprim *HdCyclesDelegate::CreateFallbackBprim(const TfToken &typeId)
{
return CreateBprim(typeId, SdfPath::EmptyPath());
}
void HdCyclesDelegate::DestroyBprim(HdBprim *bPrim)
{
delete bPrim;
}
void HdCyclesDelegate::CommitResources(HdChangeTracker *tracker)
{
TF_UNUSED(tracker);
const SceneLock lock(_renderParam.get());
_renderParam->UpdateScene();
}
TfToken HdCyclesDelegate::GetMaterialBindingPurpose() const
{
return HdTokens->full;
}
#if HD_API_VERSION < 41
TfToken HdCyclesDelegate::GetMaterialNetworkSelector() const
{
return _tokens->cycles;
}
#else
TfTokenVector HdCyclesDelegate::GetMaterialRenderContexts() const
{
return {_tokens->cycles};
}
#endif
VtDictionary HdCyclesDelegate::GetRenderStats() const
{
const Stats &stats = _renderParam->session->stats;
const Progress &progress = _renderParam->session->progress;
double totalTime, renderTime;
progress.get_time(totalTime, renderTime);
double fractionDone = progress.get_progress();
std::string status, substatus;
progress.get_status(status, substatus);
if (!substatus.empty()) {
status += " | " + substatus;
}
return {{"rendererName", VtValue("Cycles")},
{"rendererVersion", VtValue(GfVec3i(0, 0, 0))},
{"percentDone", VtValue(floor_to_int(fractionDone * 100))},
{"fractionDone", VtValue(fractionDone)},
{"loadClockTime", VtValue(totalTime - renderTime)},
{"peakMemory", VtValue(stats.mem_peak)},
{"totalClockTime", VtValue(totalTime)},
{"totalMemory", VtValue(stats.mem_used)},
{"renderProgressAnnotation", VtValue(status)}};
}
HdAovDescriptor HdCyclesDelegate::GetDefaultAovDescriptor(const TfToken &name) const
{
if (name == HdAovTokens->color) {
HdFormat colorFormat = HdFormatFloat32Vec4;
if (IsDisplaySupported()) {
// Can use Cycles 'DisplayDriver' in OpenGL, but it only supports 'half4' format
colorFormat = HdFormatFloat16Vec4;
}
return HdAovDescriptor(colorFormat, false, VtValue(GfVec4f(0.0f)));
}
if (name == HdAovTokens->depth) {
return HdAovDescriptor(HdFormatFloat32, false, VtValue(1.0f));
}
if (name == HdAovTokens->normal) {
return HdAovDescriptor(HdFormatFloat32Vec3, false, VtValue(GfVec3f(0.0f)));
}
if (name == HdAovTokens->primId || name == HdAovTokens->instanceId ||
name == HdAovTokens->elementId) {
return HdAovDescriptor(HdFormatInt32, false, VtValue(-1));
}
return HdAovDescriptor();
}
HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() const
{
Scene *const scene = _renderParam->session->scene;
HdRenderSettingDescriptorList descriptors;
descriptors.push_back({
"Time Limit",
HdCyclesRenderSettingsTokens->time_limit,
VtValue(0.0),
});
descriptors.push_back({
"Sample Count",
HdCyclesRenderSettingsTokens->samples,
VtValue(1024),
});
descriptors.push_back({
"Sample Offset",
HdCyclesRenderSettingsTokens->sample_offset,
VtValue(0),
});
for (const SocketType &socket : scene->integrator->type->inputs) {
descriptors.push_back({socket.ui_name.string(),
TfToken("cycles:integrator:" + socket.name.string()),
GetNodeValue(scene->integrator, socket)});
}
return descriptors;
}
void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS::VtValue &value)
{
Scene *const scene = _renderParam->session->scene;
Session *const session = _renderParam->session;
if (key == HdCyclesRenderSettingsTokens->time_limit) {
session->set_time_limit(
VtValue::Cast<double>(value).GetWithDefault(session->params.time_limit));
}
else if (key == HdCyclesRenderSettingsTokens->samples) {
static const int max_samples = Integrator::MAX_SAMPLES;
int samples = VtValue::Cast<int>(value).GetWithDefault(session->params.samples);
samples = std::min(std::max(1, samples), max_samples);
session->set_samples(samples);
}
else if (key == HdCyclesRenderSettingsTokens->sample_offset) {
session->params.sample_offset = VtValue::Cast<int>(value).GetWithDefault(
session->params.sample_offset);
++_settingsVersion;
}
else {
const std::string &keyString = key.GetString();
if (keyString.rfind("cycles:integrator:", 0) == 0) {
ustring socketName(keyString, sizeof("cycles:integrator:") - 1);
if (const SocketType *socket = scene->integrator->type->find_input(socketName)) {
SetNodeValue(scene->integrator, *socket, value);
++_settingsVersion;
}
}
}
}
VtValue HdCyclesDelegate::GetRenderSetting(const TfToken &key) const
{
Scene *const scene = _renderParam->session->scene;
Session *const session = _renderParam->session;
if (key == HdCyclesRenderSettingsTokens->device) {
return VtValue(TfToken(Device::string_from_type(session->params.device.type)));
}
else if (key == HdCyclesRenderSettingsTokens->threads) {
return VtValue(session->params.threads);
}
else if (key == HdCyclesRenderSettingsTokens->time_limit) {
return VtValue(session->params.time_limit);
}
else if (key == HdCyclesRenderSettingsTokens->samples) {
return VtValue(session->params.samples);
}
else if (key == HdCyclesRenderSettingsTokens->sample_offset) {
return VtValue(session->params.sample_offset);
}
else {
const std::string &keyString = key.GetString();
if (keyString.rfind("cycles:integrator:", 0) == 0) {
ustring socketName(keyString, sizeof("cycles:integrator:") - 1);
if (const SocketType *socket = scene->integrator->type->find_input(socketName)) {
return GetNodeValue(scene->integrator, *socket);
}
}
}
return VtValue();
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE