/* SPDX-License-Identifier: Apache-2.0 * Copyright 2022 NVIDIA Corporation * Copyright 2022 Blender Foundation */ #ifdef _WIN32 // Include first to avoid "NOGDI" definition set in Cycles headers # include #endif #include "hydra/display_driver.h" #include "hydra/render_buffer.h" #include "hydra/session.h" #include #include HDCYCLES_NAMESPACE_OPEN_SCOPE HdCyclesDisplayDriver::HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi) : _renderParam(renderParam), _hgi(hgi) { } HdCyclesDisplayDriver::~HdCyclesDisplayDriver() { deinit(); } void HdCyclesDisplayDriver::init() { #ifdef _WIN32 if (!gl_context_) { hdc_ = GetDC(CreateWindowA("STATIC", "HdCycles", WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 64, 64, NULL, NULL, GetModuleHandle(NULL), NULL)); 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_) { if (glewInit() != GLEW_OK) { return; } glGenBuffers(1, &gl_pbo_id_); } } void HdCyclesDisplayDriver::deinit() { if (texture_) { _hgi->DestroyTexture(&texture_); } if (gl_pbo_id_) { glDeleteBuffers(1, &gl_pbo_id_); } #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) { #ifdef _WIN32 if (!hdc_ || !gl_context_) { return false; } #endif graphics_interop_activate(); 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, 0, GL_DYNAMIC_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); pbo_size_ = params.full_size; } need_update_ = true; return true; } void HdCyclesDisplayDriver::update_end() { gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); graphics_interop_deactivate(); } void HdCyclesDisplayDriver::flush() { graphics_interop_activate(); 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); } graphics_interop_deactivate(); } half4 *HdCyclesDisplayDriver::map_texture_buffer() { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_); const auto mapped_rgba_pixels = static_cast( glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); if (need_clear_ && mapped_rgba_pixels) { memset(mapped_rgba_pixels, 0, sizeof(half4) * pbo_size_.x * pbo_size_.y); need_clear_ = false; } return mapped_rgba_pixels; } void HdCyclesDisplayDriver::unmap_texture_buffer() { glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } DisplayDriver::GraphicsInterop HdCyclesDisplayDriver::graphics_interop_get() { GraphicsInterop interop_dst; interop_dst.buffer_width = pbo_size_.x; interop_dst.buffer_height = pbo_size_.y; interop_dst.opengl_pbo_id = gl_pbo_id_; interop_dst.need_clear = need_clear_; need_clear_ = false; return interop_dst; } void HdCyclesDisplayDriver::graphics_interop_activate() { mutex_.lock(); #ifdef _WIN32 // Do not change context if this is called in the main thread if (wglGetCurrentContext() == nullptr) { TF_VERIFY(wglMakeCurrent((HDC)hdc_, (HGLRC)gl_context_)); } #endif } void HdCyclesDisplayDriver::graphics_interop_deactivate() { #ifdef _WIN32 if (wglGetCurrentContext() == gl_context_) { TF_VERIFY(wglMakeCurrent(nullptr, nullptr)); } #endif mutex_.unlock(); } void HdCyclesDisplayDriver::clear() { need_clear_ = true; } void HdCyclesDisplayDriver::draw(const Params ¶ms) { const auto renderBuffer = static_cast( _renderParam->GetDisplayAovBinding().renderBuffer); if (!renderBuffer || // Ensure this render buffer matches the texture dimensions (renderBuffer->GetWidth() != params.size.x || renderBuffer->GetHeight() != params.size.y)) { return; } init(); // 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(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, 0); 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