Files
test2/source/blender/render/hydra/final_engine.cc
Bogdan Nagirniak 04bb5f9995 Render: support USD Hydra render delegates
Hydra is a rendering architecture part of USD, designed to abstract the
host application from the renderer. A renderer implementing a Hydra
render delegate can run in any host application supporting Hydra, which
now includes Blender.

For external renderers this means less code to be written, and improved
performance due to a using a C++ API instead of a Python API.

Add-ons need to subclass bpy.types.HydraRenderEngine. See the example in
the Python API docs for details.

An add-on for Hydra Storm will be included as well. This is USD's
rasterizing renderer, used in other applications like usdview. For users
it can provide a preview of USD file export, and for developers it
serves a reference.

There are still limitations and missing features, especially around
materials. The remaining to do items are tracked in #110765.

This feature was contributed by AMD.

Ref #110765

Co-authored-by: Georgiy Markelov <georgiy.m.markelov@gmail.com>
Co-authored-by: Vasyl-Pidhirskyi <vpidhirskyi@gmail.com>
Co-authored-by: Brian Savery <brian.savery@gmail.com>
Co-authored-by: Brecht Van Lommel <brecht@blender.org>

Pull Request: https://projects.blender.org/blender/blender/pulls/104712
2023-08-04 17:01:09 +02:00

138 lines
4.2 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
#include "final_engine.h"
#include <pxr/imaging/hd/light.h>
#include <pxr/imaging/hd/renderBuffer.h>
#include "DNA_scene_types.h"
#include "BLI_timecode.h"
#include "PIL_time.h"
#include "BKE_lib_id.h"
#include "DEG_depsgraph_query.h"
#include "IMB_imbuf_types.h"
#include "RE_engine.h"
#include "hydra/camera.h"
namespace blender::render::hydra {
void FinalEngine::render()
{
const ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph_);
char scene_name[MAX_ID_FULL_NAME];
BKE_id_full_name_get(scene_name, &scene_->id, 0);
const RenderData &r = scene_->r;
pxr::GfVec4f border(0, 0, 1, 1);
if (r.mode & R_BORDER) {
border.Set(r.border.xmin,
r.border.ymin,
r.border.xmax - r.border.xmin,
r.border.ymax - r.border.ymin);
}
pxr::GfVec2i image_res(r.xsch * r.size / 100, r.ysch * r.size / 100);
int width = image_res[0] * border[2];
int height = image_res[1] * border[3];
pxr::GfCamera camera =
io::hydra::CameraData(scene_->camera, image_res, pxr::GfVec4f(0, 0, 1, 1)).gf_camera(border);
free_camera_delegate_->SetCamera(camera);
render_task_delegate_->set_viewport(pxr::GfVec4d(0, 0, width, height));
if (light_tasks_delegate_) {
light_tasks_delegate_->set_viewport(pxr::GfVec4d(0, 0, width, height));
}
RenderResult *rr = RE_engine_get_result(bl_engine_);
RenderLayer *rlayer = (RenderLayer *)rr->layers.first;
LISTBASE_FOREACH (RenderPass *, rpass, &rlayer->passes) {
pxr::TfToken *aov_token = aov_tokens_.lookup_ptr(rpass->name);
if (!aov_token) {
CLOG_WARN(LOG_HYDRA_RENDER, "Couldn't find AOV token for render pass: %s", rpass->name);
continue;
}
render_task_delegate_->add_aov(*aov_token);
}
if (bl_engine_->type->flag & RE_USE_GPU_CONTEXT) {
/* For GPU context engine color and depth AOVs has to be added anyway */
render_task_delegate_->add_aov(pxr::HdAovTokens->color);
render_task_delegate_->add_aov(pxr::HdAovTokens->depth);
}
render_task_delegate_->bind();
auto t = tasks();
engine_->Execute(render_index_.get(), &t);
char elapsed_time[32];
double time_begin = PIL_check_seconds_timer();
float percent_done = 0.0;
while (true) {
if (RE_engine_test_break(bl_engine_)) {
break;
}
percent_done = renderer_percent_done();
BLI_timecode_string_from_time_simple(
elapsed_time, sizeof(elapsed_time), PIL_check_seconds_timer() - time_begin);
notify_status(percent_done / 100.0,
std::string(scene_name) + ": " + view_layer->name,
std::string("Render Time: ") + elapsed_time +
" | Done: " + std::to_string(int(percent_done)) + "%");
if (render_task_delegate_->is_converged()) {
break;
}
update_render_result(width, height, view_layer->name);
}
update_render_result(width, height, view_layer->name);
render_task_delegate_->unbind();
}
void FinalEngine::set_render_setting(const std::string &key, const pxr::VtValue &val)
{
if (STRPREFIX(key.c_str(), "aovToken:")) {
aov_tokens_.add_overwrite(key.substr(key.find(":") + 1),
pxr::TfToken(val.UncheckedGet<std::string>()));
return;
}
Engine::set_render_setting(key, val);
}
void FinalEngine::notify_status(float progress, const std::string &title, const std::string &info)
{
RE_engine_update_progress(bl_engine_, progress);
RE_engine_update_stats(bl_engine_, title.c_str(), info.c_str());
}
void FinalEngine::update_render_result(int width, int height, const char *layer_name)
{
RenderResult *rr = RE_engine_begin_result(bl_engine_, 0, 0, width, height, layer_name, nullptr);
RenderLayer *rlayer = static_cast<RenderLayer *>(
BLI_findstring(&rr->layers, layer_name, offsetof(RenderLayer, name)));
if (rlayer) {
LISTBASE_FOREACH (RenderPass *, rpass, &rlayer->passes) {
pxr::TfToken *aov_token = aov_tokens_.lookup_ptr(rpass->name);
if (aov_token) {
render_task_delegate_->read_aov(*aov_token, rpass->ibuf->float_buffer.data);
}
}
}
RE_engine_end_result(bl_engine_, rr, false, false, false);
}
} // namespace blender::render::hydra