Unlike OpenGL and Metal, this handle is not shared, but rather Cycles has to take ownership of it. This required a fair amount of refactoring to ensure the handle is closed, ownership is properly transferred, and the handle is recreated once when the pixel buffer is modified.
296 lines
7.1 KiB
C++
296 lines
7.1 KiB
C++
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
|
|
* SPDX-FileCopyrightText: 2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#ifdef _WIN32
|
|
// Include first to avoid "NOGDI" definition set in Cycles headers
|
|
# ifndef NOMINMAX
|
|
# define NOMINMAX
|
|
# endif
|
|
# ifndef WIN32_LEAN_AND_MEAN
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# endif
|
|
# include <Windows.h>
|
|
#endif
|
|
|
|
#include "hydra/display_driver.h"
|
|
#include "hydra/render_buffer.h"
|
|
#include "hydra/session.h"
|
|
|
|
#include <epoxy/gl.h>
|
|
#include <pxr/imaging/hgiGL/texture.h>
|
|
|
|
HDCYCLES_NAMESPACE_OPEN_SCOPE
|
|
|
|
HdCyclesDisplayDriver::HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi)
|
|
: _renderParam(renderParam), _hgi(hgi)
|
|
{
|
|
}
|
|
|
|
HdCyclesDisplayDriver::~HdCyclesDisplayDriver()
|
|
{
|
|
if (texture_) {
|
|
_hgi->DestroyTexture(&texture_);
|
|
}
|
|
|
|
if (gl_pbo_id_) {
|
|
glDeleteBuffers(1, &gl_pbo_id_);
|
|
}
|
|
|
|
gl_context_dispose();
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::gl_context_create()
|
|
{
|
|
#ifdef _WIN32
|
|
if (!gl_context_) {
|
|
hdc_ = GetDC(CreateWindowA("STATIC",
|
|
"HdCycles",
|
|
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
|
0,
|
|
0,
|
|
64,
|
|
64,
|
|
nullptr,
|
|
nullptr,
|
|
GetModuleHandle(nullptr),
|
|
nullptr));
|
|
|
|
int pixelFormat = GetPixelFormat(wglGetCurrentDC());
|
|
PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd)};
|
|
DescribePixelFormat((HDC)hdc_, pixelFormat, sizeof(pfd), &pfd);
|
|
SetPixelFormat((HDC)hdc_, pixelFormat, &pfd);
|
|
|
|
TF_VERIFY(gl_context_ = wglCreateContext((HDC)hdc_));
|
|
TF_VERIFY(wglShareLists(wglGetCurrentContext(), (HGLRC)gl_context_));
|
|
}
|
|
if (!gl_context_) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (!gl_pbo_id_) {
|
|
glGenBuffers(1, &gl_pbo_id_);
|
|
graphics_interop_buffer_.clear();
|
|
}
|
|
}
|
|
|
|
bool HdCyclesDisplayDriver::gl_context_enable()
|
|
{
|
|
#ifdef _WIN32
|
|
if (!hdc_ || !gl_context_) {
|
|
return false;
|
|
}
|
|
|
|
mutex_.lock();
|
|
|
|
// Do not change context if this is called in the main thread
|
|
if (wglGetCurrentContext() == nullptr) {
|
|
if (!TF_VERIFY(wglMakeCurrent((HDC)hdc_, (HGLRC)gl_context_))) {
|
|
mutex_.unlock();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::gl_context_disable()
|
|
{
|
|
#ifdef _WIN32
|
|
if (wglGetCurrentContext() == gl_context_) {
|
|
TF_VERIFY(wglMakeCurrent(nullptr, nullptr));
|
|
}
|
|
|
|
mutex_.unlock();
|
|
#endif
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::gl_context_dispose()
|
|
{
|
|
#ifdef _WIN32
|
|
if (gl_context_) {
|
|
TF_VERIFY(wglDeleteContext((HGLRC)gl_context_));
|
|
DestroyWindow(WindowFromDC((HDC)hdc_));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::next_tile_begin() {}
|
|
|
|
bool HdCyclesDisplayDriver::update_begin(const Params ¶ms,
|
|
int /*texture_width*/,
|
|
int /*texture_height*/)
|
|
{
|
|
if (!gl_context_enable()) {
|
|
return false;
|
|
}
|
|
|
|
if (gl_render_sync_) {
|
|
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
|
|
}
|
|
|
|
if (pbo_size_.x != params.full_size.x || pbo_size_.y != params.full_size.y) {
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER,
|
|
sizeof(half4) * params.full_size.x * params.full_size.y,
|
|
nullptr,
|
|
GL_DYNAMIC_DRAW);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
pbo_size_ = params.full_size;
|
|
graphics_interop_buffer_.clear();
|
|
}
|
|
|
|
need_update_ = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::update_end()
|
|
{
|
|
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
glFlush();
|
|
|
|
gl_context_disable();
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::flush()
|
|
{
|
|
gl_context_enable();
|
|
|
|
if (gl_upload_sync_) {
|
|
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
|
|
}
|
|
|
|
if (gl_render_sync_) {
|
|
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
|
|
}
|
|
|
|
gl_context_disable();
|
|
}
|
|
|
|
half4 *HdCyclesDisplayDriver::map_texture_buffer()
|
|
{
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
|
|
|
|
auto *const mapped_rgba_pixels = static_cast<half4 *>(
|
|
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
|
|
|
|
if (need_zero_ && mapped_rgba_pixels) {
|
|
memset(mapped_rgba_pixels, 0, sizeof(half4) * pbo_size_.x * pbo_size_.y);
|
|
need_zero_ = false;
|
|
}
|
|
|
|
return mapped_rgba_pixels;
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::unmap_texture_buffer()
|
|
{
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
}
|
|
|
|
GraphicsInteropDevice HdCyclesDisplayDriver::graphics_interop_get_device()
|
|
{
|
|
GraphicsInteropDevice interop_device;
|
|
interop_device.type = GraphicsInteropDevice::OPENGL;
|
|
return interop_device;
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::graphics_interop_update_buffer()
|
|
{
|
|
if (graphics_interop_buffer_.is_empty()) {
|
|
graphics_interop_buffer_.assign(
|
|
GraphicsInteropDevice::OPENGL, gl_pbo_id_, pbo_size_.x * pbo_size_.y * sizeof(half4));
|
|
}
|
|
|
|
if (need_zero_) {
|
|
graphics_interop_buffer_.zero();
|
|
need_zero_ = false;
|
|
}
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::graphics_interop_activate()
|
|
{
|
|
gl_context_enable();
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::graphics_interop_deactivate()
|
|
{
|
|
gl_context_disable();
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::zero()
|
|
{
|
|
need_zero_ = true;
|
|
}
|
|
|
|
void HdCyclesDisplayDriver::draw(const Params ¶ms)
|
|
{
|
|
auto *const renderBuffer = static_cast<HdCyclesRenderBuffer *>(
|
|
_renderParam->GetDisplayAovBinding().renderBuffer);
|
|
if (!renderBuffer || // Ensure this render buffer matches the texture dimensions
|
|
(renderBuffer->GetWidth() != params.size.x || renderBuffer->GetHeight() != params.size.y))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!renderBuffer->IsResourceUsed()) {
|
|
return;
|
|
}
|
|
|
|
gl_context_create();
|
|
|
|
// Cycles 'DisplayDriver' only supports 'half4' format
|
|
TF_VERIFY(renderBuffer->GetFormat() == HdFormatFloat16Vec4);
|
|
|
|
const thread_scoped_lock lock(mutex_);
|
|
|
|
const GfVec3i dimensions(params.size.x, params.size.y, 1);
|
|
if (!texture_ || texture_->GetDescriptor().dimensions != dimensions) {
|
|
if (texture_) {
|
|
_hgi->DestroyTexture(&texture_);
|
|
}
|
|
|
|
HgiTextureDesc texDesc;
|
|
texDesc.usage = 0;
|
|
texDesc.format = HgiFormatFloat16Vec4;
|
|
texDesc.type = HgiTextureType2D;
|
|
texDesc.dimensions = dimensions;
|
|
texDesc.sampleCount = HgiSampleCount1;
|
|
|
|
texture_ = _hgi->CreateTexture(texDesc);
|
|
|
|
renderBuffer->SetResource(VtValue(texture_));
|
|
}
|
|
|
|
HgiGLTexture *const texture = dynamic_cast<HgiGLTexture *>(texture_.Get());
|
|
if (!texture || !need_update_ || pbo_size_.x != params.size.x || pbo_size_.y != params.size.y) {
|
|
return;
|
|
}
|
|
|
|
if (gl_upload_sync_) {
|
|
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture->GetTextureId());
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
|
|
glTexSubImage2D(
|
|
GL_TEXTURE_2D, 0, 0, 0, pbo_size_.x, pbo_size_.y, GL_RGBA, GL_HALF_FLOAT, nullptr);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
glFlush();
|
|
|
|
need_update_ = false;
|
|
}
|
|
|
|
HDCYCLES_NAMESPACE_CLOSE_SCOPE
|