Hydra render engine #104712
|
@ -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
|
||||
self._update(depsgraph)
|
||||
|
||||
if not self.engine_ptr:
|
||||
return
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ set(INC
|
|||
|
||||
set(INC_SYS
|
||||
${PYTHON_INCLUDE_DIRS}
|
||||
${Epoxy_INCLUDE_DIRS}
|
||||
${USD_INCLUDE_DIRS}
|
||||
${BOOST_INCLUDE_DIR}
|
||||
${TBB_INCLUDE_DIR}
|
||||
|
|
|
@ -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
Brecht Van Lommel
commented
Seems unnecessary since there is already 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
Brecht Van Lommel
commented
Add 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
Brecht Van Lommel
commented
It looks like there is some opportunity to deduplicate some (but not all) code with 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
Brecht Van Lommel
commented
The convention for framebuffer and texture names seems to be snake_case. So could be 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
|
||||
|
|
|
@ -26,7 +26,7 @@ class FinalEngine : public Engine {
|
|||
pxr::GfVec2i resolution_;
|
||||
};
|
||||
|
||||
class FinalEngineGL : public FinalEngine {
|
||||
class FinalEngineGPU : public FinalEngine {
|
||||
public:
|
||||
using FinalEngine::FinalEngine;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue
materials can have the same name in different libraries, so this temp file name is not unique.