Mercurial > hg > orthanc-stone
diff OrthancStone/Sources/OpenGL/OpenGLFramebuffer.cpp @ 2060:86e0e92a2e0d deep-learning
added OpenGLTextureArray and OpenGLFramebuffer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 03 May 2023 16:15:50 +0200 |
parents | |
children | 4e31d76c7ecd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/OpenGL/OpenGLFramebuffer.cpp Wed May 03 16:15:50 2023 +0200 @@ -0,0 +1,261 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2022 Osimis S.A., Belgium + * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLFramebuffer.h" + +#if defined(__EMSCRIPTEN__) +# if !defined(ORTHANC_WEBGL2_HEAP_COMPAT) +# error The macro ORTHANC_WEBGL2_HEAP_COMPAT must be defined +# endif +#endif + +#include "OpenGLTexture.h" +#include "OpenGLTextureArray.h" + +#include <OrthancException.h> + + +namespace OrthancStone +{ + namespace OpenGL + { + void OpenGLFramebuffer::SetupTextureTarget() + { + GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, drawBuffers); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Incomplete setup of an OpenGL framebuffer"); + } + } + + + void OpenGLFramebuffer::ReadContent(Orthanc::ImageAccessor& target) + { + if (target.GetPitch() != target.GetWidth() * Orthanc::GetBytesPerPixel(target.GetFormat()) || + target.GetBuffer() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Image must have minimal pitch"); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) + { + ORTHANC_OPENGL_CHECK("glCheckFramebufferStatus()"); + + glViewport(0, 0, target.GetWidth(), target.GetHeight()); + + GLenum sourceFormat, internalFormat, pixelType; + OpenGLTexture::ConvertToOpenGLFormats(sourceFormat, internalFormat, pixelType, target.GetFormat()); + +#if defined(__EMSCRIPTEN__) && (ORTHANC_WEBGL2_HEAP_COMPAT == 1) + // Check out "OpenGLTexture.cpp" for an explanation + + int framebufferFormat, framebufferType; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &framebufferFormat); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &framebufferType); + + switch (target.GetFormat()) + { + case Orthanc::PixelFormat_RGBA32: + if (sourceFormat != GL_RGBA || + internalFormat != GL_RGBA || + pixelType != GL_UNSIGNED_BYTE || + framebufferFormat != GL_RGBA || + framebufferType != GL_UNSIGNED_BYTE) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + EM_ASM({ + var ptr = emscriptenWebGLGetTexPixelData(GLctx.UNSIGNED_BYTE, GLctx.RGBA, $1, $2, $0, GLctx.RGBA); + GLctx.readPixels(0, 0, $1, $2, GLctx.RGBA, GLctx.UNSIGNED_BYTE, ptr); + }, + target.GetBuffer(), // $0 + target.GetWidth(), // $1 + target.GetHeight()); // $2 + break; + + case Orthanc::PixelFormat_Float32: + // In Mozilla Firefox, "Float32" is not available as such. We + // have to download an RGBA image in Float32. + if (sourceFormat != GL_RED || + internalFormat != GL_R32F || + pixelType != GL_FLOAT || + framebufferType != GL_FLOAT) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + switch (framebufferFormat) + { + case GL_RGBA: + // This is Mozilla Firefox + EM_ASM({ + var tmp = new Float32Array($1 * $2 * 4); + GLctx.readPixels(0, 0, $1, $2, GLctx.RGBA, GLctx.FLOAT, tmp); + + // From RGBA to RED + var ptr = emscriptenWebGLGetTexPixelData(GLctx.FLOAT, GLctx.RED, $1, $2, $0, GLctx.R32F); + for (var i = 0; i < $1 * $2; i++) { + ptr[i] = tmp[4 * i]; + } + }, + target.GetBuffer(), // $0 + target.GetWidth(), // $1 + target.GetHeight()); // $2 + break; + + case GL_RED: + // This is Chromium + EM_ASM({ + var ptr = emscriptenWebGLGetTexPixelData(GLctx.FLOAT, GLctx.RED, $1, $2, $0, GLctx.R32F); + GLctx.readPixels(0, 0, $1, $2, GLctx.RED, GLctx.FLOAT, ptr); + }, + target.GetBuffer(), // $0 + target.GetWidth(), // $1 + target.GetHeight()); // $2 + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } +#else + glReadPixels(0, 0, target.GetWidth(), target.GetHeight(), sourceFormat, pixelType, target.GetBuffer()); +#endif + + ORTHANC_OPENGL_CHECK("glReadPixels()"); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Incomplete setup of an OpenGL framebuffer"); + } + } + + + OpenGLFramebuffer::OpenGLFramebuffer(IOpenGLContext& context) : + context_(context), + framebuffer_(0) + { + if (context.IsContextLost()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "OpenGL context has been lost"); + } + + glGenFramebuffers(1, &framebuffer_); + if (framebuffer_ == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Cannot create an OpenGL framebuffer"); + } + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); + } + + + OpenGLFramebuffer::~OpenGLFramebuffer() + { + glDeleteFramebuffers(1, &framebuffer_); + } + + + void OpenGLFramebuffer::SetTarget(OpenGLTexture& target) + { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target.GetId(), 0); + SetupTextureTarget(); + glViewport(0, 0, target.GetWidth(), target.GetHeight()); + } + + + void OpenGLFramebuffer::SetTarget(OpenGLTextureArray& target, + unsigned int layer) + { + if (layer >= target.GetDepth()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target.GetId(), 0, layer); + SetupTextureTarget(); + glViewport(0, 0, target.GetWidth(), target.GetHeight()); + } + } + + + void OpenGLFramebuffer::ReadTexture(Orthanc::ImageAccessor& target, + const OpenGLTexture& source) + { + if (target.GetWidth() != source.GetWidth() || + target.GetHeight() != source.GetHeight()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); + } + else if (target.GetFormat() != source.GetFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + else + { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, source.GetId(), 0); + ORTHANC_OPENGL_CHECK("glFramebufferTexture2D()"); + ReadContent(target); + } + } + + + void OpenGLFramebuffer::ReadTexture(Orthanc::ImageAccessor& target, + const OpenGLTextureArray& source, + unsigned int layer) + { + if (target.GetWidth() != source.GetWidth() || + target.GetHeight() != source.GetHeight()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); + } + else if (target.GetFormat() != source.GetFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + else if (layer >= source.GetDepth()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, source.GetId(), 0, layer); + ORTHANC_OPENGL_CHECK("glFramebufferTextureLayer()"); + ReadContent(target); + } + } + } +}