This offers better semantic and safety of the API. Part of #130632 Pull Request: https://projects.blender.org/blender/blender/pulls/142818
321 lines
9.2 KiB
C++
321 lines
9.2 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "render_task_delegate.hh"
|
|
|
|
#include <epoxy/gl.h>
|
|
|
|
#include "GPU_context.hh"
|
|
|
|
#include <pxr/imaging/hd/renderBuffer.h>
|
|
#include <pxr/imaging/hd/renderDelegate.h>
|
|
#include <pxr/imaging/hdx/renderTask.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include <Eigen/Core>
|
|
|
|
#include "engine.hh"
|
|
|
|
namespace blender::render::hydra {
|
|
|
|
RenderTaskDelegate::RenderTaskDelegate(pxr::HdRenderIndex *parent_index,
|
|
pxr::SdfPath const &delegate_id)
|
|
: pxr::HdSceneDelegate(parent_index, delegate_id)
|
|
{
|
|
task_id_ = GetDelegateID().AppendElementString("task");
|
|
GetRenderIndex().InsertTask<pxr::HdxRenderTask>(this, task_id_);
|
|
|
|
task_params_.enableLighting = true;
|
|
task_params_.alphaThreshold = 0.1f;
|
|
|
|
/* Disable this so Metal and OpenGL match in Storm render tests, only
|
|
* the former seems to use multisample. */
|
|
task_params_.useAovMultiSample = false;
|
|
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", task_id_.GetText());
|
|
}
|
|
|
|
pxr::VtValue RenderTaskDelegate::Get(pxr::SdfPath const &id, pxr::TfToken const &key)
|
|
{
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s, %s", id.GetText(), key.GetText());
|
|
|
|
if (key == pxr::HdTokens->params) {
|
|
return pxr::VtValue(task_params_);
|
|
}
|
|
if (key == pxr::HdTokens->collection) {
|
|
return pxr::VtValue(pxr::HdRprimCollection(
|
|
pxr::HdTokens->geometry, pxr::HdReprSelector(pxr::HdReprTokens->smoothHull)));
|
|
}
|
|
return pxr::VtValue();
|
|
}
|
|
|
|
pxr::TfTokenVector RenderTaskDelegate::GetTaskRenderTags(pxr::SdfPath const &id)
|
|
{
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", id.GetText());
|
|
|
|
return {pxr::HdRenderTagTokens->geometry};
|
|
}
|
|
|
|
pxr::HdRenderBufferDescriptor RenderTaskDelegate::GetRenderBufferDescriptor(pxr::SdfPath const &id)
|
|
{
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", id.GetText());
|
|
|
|
return buffer_descriptors_[id];
|
|
}
|
|
|
|
pxr::HdTaskSharedPtr RenderTaskDelegate::task()
|
|
{
|
|
return GetRenderIndex().GetTask(task_id_);
|
|
}
|
|
|
|
void RenderTaskDelegate::set_camera(pxr::SdfPath const &camera_id)
|
|
{
|
|
if (task_params_.camera == camera_id) {
|
|
return;
|
|
}
|
|
task_params_.camera = camera_id;
|
|
GetRenderIndex().GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
|
}
|
|
|
|
bool RenderTaskDelegate::is_converged()
|
|
{
|
|
return static_cast<pxr::HdxRenderTask *>(task().get())->IsConverged();
|
|
}
|
|
|
|
void RenderTaskDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
|
{
|
|
if (task_params_.viewport == viewport) {
|
|
return;
|
|
}
|
|
auto &render_index = GetRenderIndex();
|
|
task_params_.viewport = viewport;
|
|
render_index.GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
|
|
|
int w = viewport[2] - viewport[0];
|
|
int h = viewport[3] - viewport[1];
|
|
for (auto &it : buffer_descriptors_) {
|
|
it.second.dimensions = pxr::GfVec3i(w, h, 1);
|
|
render_index.GetChangeTracker().MarkBprimDirty(it.first,
|
|
pxr::HdRenderBuffer::DirtyDescription);
|
|
}
|
|
}
|
|
|
|
void RenderTaskDelegate::add_aov(pxr::TfToken const &aov_key)
|
|
{
|
|
pxr::SdfPath buf_id = buffer_id(aov_key);
|
|
if (buffer_descriptors_.find(buf_id) != buffer_descriptors_.end()) {
|
|
return;
|
|
}
|
|
auto &render_index = GetRenderIndex();
|
|
pxr::HdAovDescriptor aov_desc = render_index.GetRenderDelegate()->GetDefaultAovDescriptor(
|
|
aov_key);
|
|
|
|
if (aov_desc.format == pxr::HdFormatInvalid) {
|
|
CLOG_ERROR(LOG_HYDRA_RENDER, "Invalid AOV: %s", aov_key.GetText());
|
|
return;
|
|
}
|
|
if (!ELEM(
|
|
pxr::HdGetComponentFormat(aov_desc.format), pxr::HdFormatFloat32, pxr::HdFormatFloat16))
|
|
{
|
|
CLOG_WARN(LOG_HYDRA_RENDER,
|
|
"Unsupported data format %s for AOV %s",
|
|
pxr::TfEnum::GetName(aov_desc.format).c_str(),
|
|
aov_key.GetText());
|
|
return;
|
|
}
|
|
|
|
int w = task_params_.viewport[2] - task_params_.viewport[0];
|
|
int h = task_params_.viewport[3] - task_params_.viewport[1];
|
|
render_index.InsertBprim(pxr::HdPrimTypeTokens->renderBuffer, this, buf_id);
|
|
buffer_descriptors_[buf_id] = pxr::HdRenderBufferDescriptor(
|
|
pxr::GfVec3i(w, h, 1), aov_desc.format, aov_desc.multiSampled);
|
|
|
|
pxr::HdRenderPassAovBinding binding;
|
|
binding.aovName = aov_key;
|
|
binding.renderBufferId = buf_id;
|
|
binding.aovSettings = aov_desc.aovSettings;
|
|
binding.clearValue = aov_desc.clearValue;
|
|
task_params_.aovBindings.push_back(binding);
|
|
render_index.GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
|
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", aov_key.GetText());
|
|
}
|
|
|
|
void RenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, void *data)
|
|
{
|
|
pxr::HdRenderBuffer *buffer = static_cast<pxr::HdRenderBuffer *>(
|
|
GetRenderIndex().GetBprim(pxr::HdPrimTypeTokens->renderBuffer, buffer_id(aov_key)));
|
|
if (!buffer) {
|
|
return;
|
|
}
|
|
|
|
pxr::HdFormat format = buffer->GetFormat();
|
|
size_t len = buffer->GetWidth() * buffer->GetHeight() * pxr::HdGetComponentCount(format);
|
|
if (pxr::HdGetComponentFormat(format) == pxr::HdFormatFloat32) {
|
|
void *buf_data = buffer->Map();
|
|
memcpy(data, buf_data, len * sizeof(float));
|
|
buffer->Unmap();
|
|
}
|
|
else if (pxr::HdGetComponentFormat(format) == pxr::HdFormatFloat16) {
|
|
Eigen::half *buf_data = (Eigen::half *)buffer->Map();
|
|
float *fdata = (float *)data;
|
|
for (size_t i = 0; i < len; ++i) {
|
|
fdata[i] = buf_data[i];
|
|
}
|
|
buffer->Unmap();
|
|
}
|
|
else {
|
|
BLI_assert_unreachable();
|
|
}
|
|
}
|
|
|
|
pxr::HdRenderBuffer *RenderTaskDelegate::get_aov_buffer(pxr::TfToken const &aov_key)
|
|
{
|
|
return (pxr::HdRenderBuffer *)GetRenderIndex().GetBprim(pxr::HdPrimTypeTokens->renderBuffer,
|
|
buffer_id(aov_key));
|
|
}
|
|
|
|
void RenderTaskDelegate::bind() {}
|
|
|
|
void RenderTaskDelegate::unbind() {}
|
|
|
|
pxr::SdfPath RenderTaskDelegate::buffer_id(pxr::TfToken const &aov_key) const
|
|
{
|
|
return GetDelegateID().AppendElementString("aov_" + aov_key.GetString());
|
|
}
|
|
|
|
GPURenderTaskDelegate::~GPURenderTaskDelegate()
|
|
{
|
|
unbind();
|
|
if (tex_color_) {
|
|
GPU_texture_free(tex_color_);
|
|
}
|
|
if (tex_depth_) {
|
|
GPU_texture_free(tex_depth_);
|
|
}
|
|
}
|
|
|
|
void GPURenderTaskDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
|
{
|
|
if (task_params_.viewport == viewport) {
|
|
return;
|
|
}
|
|
auto &render_index = GetRenderIndex();
|
|
task_params_.viewport = viewport;
|
|
render_index.GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
|
|
|
if (tex_color_) {
|
|
GPU_texture_free(tex_color_);
|
|
tex_color_ = nullptr;
|
|
add_aov(pxr::HdAovTokens->color);
|
|
}
|
|
if (tex_depth_) {
|
|
GPU_texture_free(tex_depth_);
|
|
tex_depth_ = nullptr;
|
|
add_aov(pxr::HdAovTokens->depth);
|
|
}
|
|
}
|
|
|
|
void GPURenderTaskDelegate::add_aov(pxr::TfToken const &aov_key)
|
|
{
|
|
blender::gpu::TextureFormat format;
|
|
blender::gpu::Texture **tex;
|
|
if (aov_key == pxr::HdAovTokens->color) {
|
|
format = blender::gpu::TextureFormat::SFLOAT_32_32_32_32;
|
|
tex = &tex_color_;
|
|
}
|
|
else if (aov_key == pxr::HdAovTokens->depth) {
|
|
format = blender::gpu::TextureFormat::SFLOAT_32_DEPTH;
|
|
tex = &tex_depth_;
|
|
}
|
|
else {
|
|
CLOG_ERROR(LOG_HYDRA_RENDER, "Invalid AOV: %s", aov_key.GetText());
|
|
return;
|
|
}
|
|
|
|
if (*tex) {
|
|
return;
|
|
}
|
|
|
|
*tex = GPU_texture_create_2d(("tex_render_hydra_" + aov_key.GetString()).c_str(),
|
|
task_params_.viewport[2] - task_params_.viewport[0],
|
|
task_params_.viewport[3] - task_params_.viewport[1],
|
|
1,
|
|
format,
|
|
GPU_TEXTURE_USAGE_GENERAL,
|
|
nullptr);
|
|
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", aov_key.GetText());
|
|
}
|
|
|
|
void GPURenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, void *data)
|
|
{
|
|
blender::gpu::Texture *tex = nullptr;
|
|
int c;
|
|
if (aov_key == pxr::HdAovTokens->color) {
|
|
tex = tex_color_;
|
|
c = 4;
|
|
}
|
|
else if (aov_key == pxr::HdAovTokens->depth) {
|
|
tex = tex_depth_;
|
|
c = 1;
|
|
}
|
|
if (!tex) {
|
|
return;
|
|
}
|
|
|
|
int w = GPU_texture_width(tex), h = GPU_texture_height(tex);
|
|
void *tex_data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
|
|
memcpy(data, tex_data, sizeof(float) * w * h * c);
|
|
MEM_freeN(tex_data);
|
|
}
|
|
|
|
void GPURenderTaskDelegate::bind()
|
|
{
|
|
if (!framebuffer_) {
|
|
framebuffer_ = GPU_framebuffer_create("fb_render_hydra");
|
|
}
|
|
GPU_framebuffer_ensure_config(
|
|
&framebuffer_, {GPU_ATTACHMENT_TEXTURE(tex_depth_), GPU_ATTACHMENT_TEXTURE(tex_color_)});
|
|
GPU_framebuffer_bind(framebuffer_);
|
|
|
|
float clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
GPU_framebuffer_clear_color_depth(framebuffer_, clear_color, 1.0f);
|
|
|
|
/* Workaround missing/buggy VAOs in hgiGL and hdSt. For OpenGL compatibility
|
|
* profile this is not a problem, but for core profile it is. */
|
|
if (VAO_ == 0 && GPU_backend_get_type() == GPU_BACKEND_OPENGL) {
|
|
glGenVertexArrays(1, &VAO_);
|
|
glBindVertexArray(VAO_);
|
|
}
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "bind");
|
|
}
|
|
|
|
void GPURenderTaskDelegate::unbind()
|
|
{
|
|
if (VAO_) {
|
|
glDeleteVertexArrays(1, &VAO_);
|
|
VAO_ = 0;
|
|
}
|
|
if (framebuffer_) {
|
|
GPU_framebuffer_free(framebuffer_);
|
|
framebuffer_ = nullptr;
|
|
}
|
|
CLOG_DEBUG(LOG_HYDRA_RENDER, "unbind");
|
|
}
|
|
|
|
blender::gpu::Texture *GPURenderTaskDelegate::get_aov_texture(pxr::TfToken const &aov_key)
|
|
{
|
|
if (aov_key == pxr::HdAovTokens->color) {
|
|
return tex_color_;
|
|
}
|
|
if (aov_key == pxr::HdAovTokens->depth) {
|
|
return tex_depth_;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace blender::render::hydra
|