Hydra render engine #104712

Closed
Bogdan Nagirniak wants to merge 142 commits from BogdanNagirniak/blender:hydra-render into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
11 changed files with 99 additions and 51 deletions
Showing only changes of commit bd3f747044 - Show all commits

View File

@ -91,7 +91,8 @@ class HydraRenderEngine(bpy.types.RenderEngine):
return {}
# final render
def update(self, data, depsgraph):
def _update(self, depsgraph):
"""This function is preferable to override in child classes instead of update()"""
engine_type = 'PREVIEW' if self.is_preview else 'FINAL'
self.engine_ptr = _bpy_hydra.engine_create(self.as_pointer(), engine_type, self.delegate_id)
if not self.engine_ptr:
@ -102,7 +103,15 @@ class HydraRenderEngine(bpy.types.RenderEngine):
_bpy_hydra.engine_sync(self.engine_ptr, depsgraph.as_pointer(), bpy.context.as_pointer())
def update(self, data, depsgraph):
# If bl_use_gpu_context is true, this function is ignored and render() is used
if not self.bl_use_gpu_context:
self._update(depsgraph)
def render(self, depsgraph):
if self.bl_use_gpu_context:
BogdanNagirniak marked this conversation as resolved Outdated

materials can have the same name in different libraries, so this temp file name is not unique.

materials can have the same name in different libraries, so this temp file name is not unique.
self._update(depsgraph)
if not self.engine_ptr:
return

View File

@ -39,6 +39,7 @@ set(INC
set(INC_SYS
${PYTHON_INCLUDE_DIRS}
${Epoxy_INCLUDE_DIRS}
${USD_INCLUDE_DIRS}
${BOOST_INCLUDE_DIR}
${TBB_INCLUDE_DIR}

View File

@ -1,6 +1,10 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#include <epoxy/gl.h>
#include <pxr/imaging/hd/light.h>
#include "BKE_lib_id.h"
#include "BLI_timecode.h"
#include "DEG_depsgraph_query.h"
@ -18,14 +22,11 @@ namespace blender::render::hydra {
void FinalEngine::render(Depsgraph *depsgraph)
{
prepare_for_render(depsgraph);
BogdanNagirniak marked this conversation as resolved Outdated

Seems unnecessary since there is already Py_BEGIN_ALLOW_THREADS in engine_render_func.

Seems unnecessary since there is already `Py_BEGIN_ALLOW_THREADS` in `engine_render_func`.
render_task_delegate_->set_renderer_aov(pxr::HdAovTokens->color);
engine_->Execute(render_index_.get(), &tasks_);
std::vector<float> &pixels = render_images_["Combined"];
{
/* Release the GIL before calling into hydra, in case any hydra plugins call into python. */
engine_->Execute(render_index_.get(), &tasks_);
}
char elapsed_time[32];
double time_begin = PIL_check_seconds_timer();
float percent_done = 0.0;
@ -36,10 +37,8 @@ void FinalEngine::render(Depsgraph *depsgraph)
}
percent_done = renderer_percent_done();
BogdanNagirniak marked this conversation as resolved Outdated

Add const

Add `const`
BLI_timecode_string_from_time_simple(
elapsed_time, sizeof(elapsed_time), PIL_check_seconds_timer() - time_begin);
notify_status(percent_done / 100.0,
scene_name_ + ": " + layer_name_,
std::string("Render Time: ") + elapsed_time +
@ -107,7 +106,6 @@ void FinalEngine::prepare_for_render(Depsgraph *depsgraph)
free_camera_delegate_->SetCamera(camera);
render_task_delegate_->set_camera_and_viewport(
free_camera_delegate_->GetCameraId(), pxr::GfVec4d(0, 0, resolution_[0], resolution_[1]));
render_task_delegate_->set_renderer_aov(pxr::HdAovTokens->color);
if (simple_light_task_delegate_) {
simple_light_task_delegate_->set_camera_path(free_camera_delegate_->GetCameraId());
@ -120,33 +118,55 @@ void FinalEngine::prepare_for_render(Depsgraph *depsgraph)
std::vector<float>(resolution_[0] * resolution_[1] * 4)); /* 4 - number of channels. */
}
void FinalEngineGL::render(Depsgraph *depsgraph)
void FinalEngineGPU::render(Depsgraph *depsgraph)
{
prepare_for_render(depsgraph);
std::vector<float> &pixels = render_images_["Combined"];
GPUFrameBuffer *framebuffer = GPU_framebuffer_create("fb_render_hydra");
GPUTexture *tex_color = GPU_texture_create_2d("tex_render_hydra_color",
resolution_[0],
resolution_[1],
1,
GPU_RGBA32F,
GPU_TEXTURE_USAGE_GENERAL,
nullptr);
GPUTexture *tex_depth = GPU_texture_create_2d("tex_render_hydra_depth",
resolution_[0],
resolution_[1],
1,
GPU_DEPTH32F_STENCIL8,
GPU_TEXTURE_USAGE_GENERAL,
nullptr);
GPU_texture_filter_mode(tex_color, true);
GPU_texture_mipmap_mode(tex_color, true, true);
GPU_texture_filter_mode(tex_depth, true);
GPU_texture_mipmap_mode(tex_depth, true, true);
GPUFrameBuffer *framebuffer = GPU_framebuffer_create("fb_hdyra_render_final");
GPUTexture *texture = GPU_texture_create_2d("tex_hydra_render_final",
resolution_[0],
resolution_[1],
1,
GPU_RGBA32F,
GPU_TEXTURE_USAGE_GENERAL,
nullptr);
GPU_texture_filter_mode(texture, true);
GPU_texture_mipmap_mode(texture, true, true);
GPU_framebuffer_texture_attach(framebuffer, texture, 0, 0);
GPU_framebuffer_ensure_config(
&framebuffer, {GPU_ATTACHMENT_TEXTURE(tex_depth), GPU_ATTACHMENT_TEXTURE(tex_color)});
GPU_framebuffer_bind(framebuffer);
float clear_color[4] = {0.0, 0.0, 0.0, 0.0};
GPU_framebuffer_clear_color_depth(framebuffer, clear_color, 1.0);
{
/* Release the GIL before calling into hydra, in case any hydra plugins call into python. */
engine_->Execute(render_index_.get(), &tasks_);
float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
BogdanNagirniak marked this conversation as resolved Outdated

It looks like there is some opportunity to deduplicate some (but not all) code with FinalEngine::render here?

It looks like there is some opportunity to deduplicate some (but not all) code with `FinalEngine::render` here?
pxr::VtValue world_color = scene_delegate_->GetLightParamValue(
scene_delegate_->GetDelegateID().AppendElementString("World"), pxr::HdLightTokens->color);
if (!world_color.IsEmpty()) {
auto &c = world_color.Get<pxr::GfVec3f>();
clear_color[0] = c[0];
clear_color[1] = c[1];
clear_color[2] = c[2];
}
GPU_framebuffer_clear_color_depth(framebuffer, clear_color, 1.0f);
/* Important: we have to create and bind at least one Vertex Array Object (VAO) before render
execution: More info at https://open.gl/drawing */
GLuint VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
engine_->Execute(render_index_.get(), &tasks_);
std::vector<float> &pixels = render_images_["Combined"];
char elapsed_time[32];
double time_begin = PIL_check_seconds_timer();
float percent_done = 0.0;
@ -170,19 +190,21 @@ void FinalEngineGL::render(Depsgraph *depsgraph)
break;
}
void *data = GPU_texture_read(texture, GPU_DATA_FLOAT, 0);
void *data = GPU_texture_read(tex_color, GPU_DATA_FLOAT, 0);
memcpy(pixels.data(), data, pixels.size() * sizeof(float));
MEM_freeN(data);
update_render_result();
}
void *data = GPU_texture_read(texture, GPU_DATA_FLOAT, 0);
void *data = GPU_texture_read(tex_color, GPU_DATA_FLOAT, 0);
BogdanNagirniak marked this conversation as resolved Outdated

The convention for framebuffer and texture names seems to be snake_case. So could be fb_hdyra_render_final and tex_hydra_render_final.

The convention for framebuffer and texture names seems to be snake_case. So could be `fb_hdyra_render_final` and `tex_hydra_render_final`.
memcpy(pixels.data(), data, pixels.size() * sizeof(float));
MEM_freeN(data);
update_render_result();
glDeleteVertexArrays(1, &VAO);
GPU_framebuffer_free(framebuffer);
GPU_texture_free(texture);
GPU_texture_free(tex_color);
GPU_texture_free(tex_depth);
}
} // namespace blender::render::hydra

View File

@ -26,7 +26,7 @@ class FinalEngine : public Engine {
pxr::GfVec2i resolution_;
};
class FinalEngineGL : public FinalEngine {
class FinalEngineGPU : public FinalEngine {
public:
using FinalEngine::FinalEngine;

View File

@ -75,7 +75,7 @@ static PyObject *engine_create_func(PyObject * /*self*/, PyObject *args)
}
else {
if (bl_engine->type->flag & RE_USE_GPU_CONTEXT) {
engine = new FinalEngineGL(bl_engine, render_delegate_id);
engine = new FinalEngineGPU(bl_engine, render_delegate_id);
}
else {
engine = new FinalEngine(bl_engine, render_delegate_id);
@ -117,9 +117,9 @@ static PyObject *engine_sync_func(PyObject * /*self*/, PyObject *args)
Depsgraph *depsgraph = (Depsgraph *)PyLong_AsVoidPtr(pydepsgraph);
bContext *context = (bContext *)PyLong_AsVoidPtr(pycontext);
CLOG_INFO(LOG_RENDER_HYDRA, 2, "Engine %016llx", engine);
engine->sync(depsgraph, context);
CLOG_INFO(LOG_RENDER_HYDRA, 2, "Engine %016llx", engine);
Py_RETURN_NONE;
}
@ -135,9 +135,9 @@ static PyObject *engine_sync_usd_func(PyObject * /*self*/, PyObject *args)
boost::python::extract<pxr::UsdStageRefPtr> extract(pystage);
pxr::UsdStagePtr stage = extract();
CLOG_INFO(LOG_RENDER_HYDRA, 2, "Engine %016llx", engine);
engine->sync_usd(stage);
CLOG_INFO(LOG_RENDER_HYDRA, 2, "Engine %016llx", engine);
Py_RETURN_NONE;
}
@ -152,12 +152,13 @@ static PyObject *engine_render_func(PyObject * /*self*/, PyObject *args)
Engine *engine = (Engine *)PyLong_AsVoidPtr(pyengine);
Depsgraph *depsgraph = (Depsgraph *)PyLong_AsVoidPtr(pydepsgraph);
CLOG_INFO(LOG_RENDER_HYDRA, 2, "Engine %016llx", engine);
/* Allow Blender to execute other Python scripts. */
Py_BEGIN_ALLOW_THREADS;
engine->render(depsgraph);
Py_END_ALLOW_THREADS;
CLOG_INFO(LOG_RENDER_HYDRA, 3, "Engine %016llx", engine);
Py_RETURN_NONE;
}
@ -172,12 +173,13 @@ static PyObject *engine_view_draw_func(PyObject * /*self*/, PyObject *args)
Depsgraph *depsgraph = (Depsgraph *)PyLong_AsVoidPtr(pydepsgraph);
bContext *context = (bContext *)PyLong_AsVoidPtr(pycontext);
CLOG_INFO(LOG_RENDER_HYDRA, 3, "Engine %016llx", engine);
/* Allow Blender to execute other Python scripts. */
Py_BEGIN_ALLOW_THREADS;
engine->render(depsgraph, context);
Py_END_ALLOW_THREADS;
CLOG_INFO(LOG_RENDER_HYDRA, 3, "Engine %016llx", engine);
Py_RETURN_NONE;
}
@ -208,9 +210,10 @@ static PyObject *engine_set_sync_setting_func(PyObject * /*self*/, PyObject *arg
}
Engine *engine = (Engine *)PyLong_AsVoidPtr(pyengine);
engine->set_sync_setting(key, get_setting_val(pyval));
CLOG_INFO(LOG_RENDER_HYDRA, 3, "Engine %016llx: %s", engine, key);
engine->set_sync_setting(key, get_setting_val(pyval));
Py_RETURN_NONE;
}
@ -223,9 +226,10 @@ static PyObject *engine_set_render_setting_func(PyObject * /*self*/, PyObject *a
}
Engine *engine = (Engine *)PyLong_AsVoidPtr(pyengine);
engine->set_render_setting(key, get_setting_val(pyval));
CLOG_INFO(LOG_RENDER_HYDRA, 3, "Engine %016llx: %s", engine, key);
engine->set_render_setting(key, get_setting_val(pyval));
Py_RETURN_NONE;
}

View File

@ -178,7 +178,7 @@ bool BlenderSceneDelegate::GetDoubleSided(pxr::SdfPath const &id)
pxr::HdCullStyle BlenderSceneDelegate::GetCullStyle(pxr::SdfPath const &id)
{
CLOG_INFO(LOG_RENDER_HYDRA_SCENE, 3, "%s", id.GetText());
return mesh_data(id)->cull_style;
return mesh_data(id)->cull_style(id);
}
pxr::SdfPath BlenderSceneDelegate::GetInstancerId(pxr::SdfPath const &prim_id)

View File

@ -87,6 +87,11 @@ pxr::VtValue MaterialData::get_material_resource() const
return material_network_map_;
}
pxr::HdCullStyle MaterialData::cull_style() const
{
return double_sided ? pxr::HdCullStyle::HdCullStyleNothing : pxr::HdCullStyle::HdCullStyleBack;
}
void MaterialData::export_mtlx()
{
/* Call of python function hydra.export_mtlx() */

View File

@ -3,7 +3,7 @@
#pragma once
#include "pxr/base/tf/hashmap.h"
#include <pxr/imaging/hd/enums.h>
#include <pxr/usd/sdf/assetPath.h>
#include <pxr/usd/sdf/path.h>
@ -27,6 +27,7 @@ class MaterialData : public IdData {
pxr::VtValue get_data(pxr::TfToken const &key) const override;
pxr::VtValue get_material_resource() const;
pxr::HdCullStyle cull_style() const;
bool double_sided = true;

View File

@ -148,6 +148,15 @@ pxr::SdfPath MeshData::material_id(pxr::SdfPath const &id) const
return sm.mat_data->prim_id;
}
pxr::HdCullStyle MeshData::cull_style(pxr::SdfPath const &id) const
{
const SubMesh &sm = submesh(id);
if (sm.mat_data) {
return sm.mat_data->cull_style();
}
return pxr::HdCullStyle::HdCullStyleNothing;
}
bool MeshData::double_sided(pxr::SdfPath const &id) const
{
const SubMesh &sm = submesh(id);
@ -162,7 +171,8 @@ void MeshData::update_double_sided(MaterialData *mat_data)
for (int i = 0; i < submeshes_.size(); ++i) {
if (submeshes_[i].mat_data == mat_data) {
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(
submesh_prim_id(i), pxr::HdChangeTracker::DirtyDoubleSided);
submesh_prim_id(i),
pxr::HdChangeTracker::DirtyDoubleSided | pxr::HdChangeTracker::DirtyCullStyle);
ID_LOG(1, "%d", i);
}
}

View File

@ -38,13 +38,12 @@ class MeshData : public ObjectData {
pxr::HdMeshTopology mesh_topology(pxr::SdfPath const &id) const;
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const;
pxr::SdfPath material_id(pxr::SdfPath const &id) const;
pxr::HdCullStyle cull_style(pxr::SdfPath const &id) const;
bool double_sided(pxr::SdfPath const &id) const;
void update_double_sided(MaterialData *mat_data);
void available_materials(Set<pxr::SdfPath> &paths) const;
pxr::SdfPathVector submesh_paths() const;
pxr::HdCullStyle cull_style = pxr::HdCullStyleBackUnlessDoubleSided;
private:
pxr::SdfPath submesh_prim_id(int index) const;
const SubMesh &submesh(pxr::SdfPath const &id) const;

View File

@ -253,14 +253,11 @@ void ViewportEngine::render(Depsgraph *depsgraph, bContext *context)
}
tasks.push_back(render_task_delegate_->get_task());
{
/* Release the GIL before calling into hydra, in case any hydra plugins call into python. */
engine_->Execute(render_index_.get(), &tasks);
engine_->Execute(render_index_.get(), &tasks);
if ((bl_engine_->type->flag & RE_USE_GPU_CONTEXT) == 0) {
draw_texture_.set_buffer(render_task_delegate_->get_renderer_aov(pxr::HdAovTokens->color));
draw_texture_.draw(shader, view_settings.border[0], view_settings.border[1]);
}
if ((bl_engine_->type->flag & RE_USE_GPU_CONTEXT) == 0) {
draw_texture_.set_buffer(render_task_delegate_->get_renderer_aov(pxr::HdAovTokens->color));
draw_texture_.draw(shader, view_settings.border[0], view_settings.border[1]);
}
GPU_shader_unbind();