comparison OrthancStone/Sources/OpenGL/OpenGLTextureVolume.cpp @ 2066:cf3d85eb291c deep-learning

added class OpenGLTextureVolume
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 04 May 2023 17:18:14 +0200
parents
children
comparison
equal deleted inserted replaced
2065:15f2e52835a1 2066:cf3d85eb291c
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2022 Osimis S.A., Belgium
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 *
8 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation, either version 3 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 **/
22
23
24 #include "OpenGLTextureVolume.h"
25
26 #include "OpenGLFramebuffer.h"
27 #include "OpenGLTexture.h"
28
29 #include <Images/Image.h>
30 #include <OrthancException.h>
31
32 #include <boost/lexical_cast.hpp>
33 #include <cassert>
34
35
36 namespace OrthancStone
37 {
38 namespace OpenGL
39 {
40 OpenGLTextureVolume::OpenGLTextureVolume(IOpenGLContext& context) :
41 context_(context),
42 texture_(0),
43 width_(0),
44 height_(0),
45 depth_(0),
46 format_(Orthanc::PixelFormat_Float32),
47 isLinearInterpolation_(false)
48 {
49 if (context.IsContextLost())
50 {
51 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
52 "OpenGL context has been lost");
53 }
54
55 glGenTextures(1, &texture_);
56 ORTHANC_OPENGL_CHECK("glGenTextures()");
57
58 if (texture_ == 0)
59 {
60 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
61 "Cannot create an OpenGL texture array");
62 }
63 }
64
65
66 OpenGLTextureVolume::~OpenGLTextureVolume()
67 {
68 assert(texture_ != 0);
69 glDeleteTextures(1, &texture_);
70 }
71
72
73 void OpenGLTextureVolume::Setup(Orthanc::PixelFormat format,
74 unsigned int width,
75 unsigned int height,
76 unsigned int depth,
77 bool isLinearInterpolation)
78 {
79 glActiveTexture(GL_TEXTURE0);
80 glBindTexture(GL_TEXTURE_3D, texture_);
81 ORTHANC_OPENGL_CHECK("glBindTexture(GL_TEXTURE_3D)");
82
83 GLenum sourceFormat, internalFormat, pixelType;
84 OpenGLTexture::ConvertToOpenGLFormats(sourceFormat, internalFormat, pixelType, format);
85
86 glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth,
87 0, sourceFormat, pixelType, NULL);
88 ORTHANC_OPENGL_CHECK("glTexImage3D()");
89
90 #if !defined(__EMSCRIPTEN__)
91 /**
92 * glGetTexLevelParameteriv() was introduced in OpenGL ES 3.1,
93 * but WebGL 2 only supports OpenGL ES 3.0, so it is not
94 * available in WebAssembly:
95 * https://registry.khronos.org/OpenGL-Refpages/es3.1/html/glGetTexLevelParameter.xhtml
96 **/
97 GLint w, h, d;
98 glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &w);
99 glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &h);
100 glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_DEPTH, &d);
101 if (width != static_cast<unsigned int>(w) ||
102 height != static_cast<unsigned int>(h) ||
103 depth != static_cast<unsigned int>(d))
104 {
105 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
106 "Your GPU cannot create a 3D texture of size " +
107 boost::lexical_cast<std::string>(width) + " x " +
108 boost::lexical_cast<std::string>(height) + " x " +
109 boost::lexical_cast<std::string>(depth));
110 }
111 #endif
112
113 format_ = format;
114 width_ = width;
115 height_ = height;
116 depth_ = depth;
117 isLinearInterpolation_ = isLinearInterpolation;
118
119 GLint interpolation = (isLinearInterpolation ? GL_LINEAR : GL_NEAREST);
120 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, interpolation);
121 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, interpolation);
122 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
123 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
124 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
125 }
126
127
128 void OpenGLTextureVolume::SetClampingToZero()
129 {
130 #if defined(__EMSCRIPTEN__)
131 /**
132 * This is because WebGL 2 derives from OpenGL ES 3.0, which
133 * doesn't support GL_CLAMP_TO_BORDER, as can be seen here:
134 * https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glTexParameter.xhtml
135 **/
136 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
137 "OpenGLTextureArray::SetClampingToZero() is not available in WebGL 2");
138 #else
139 ORTHANC_OPENGL_CHECK("Entering OpenGLTextureArray::SetClampingToZero()");
140
141 glBindTexture(GL_TEXTURE_3D, texture_);
142 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
143 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
144
145 GLfloat colorfv[4] = { 0, 0, 0, 0 };
146 glTexParameterfv(texture_, GL_TEXTURE_BORDER_COLOR, colorfv);
147
148 ORTHANC_OPENGL_CHECK("Exiting OpenGLTextureArray::SetClampingToZero()");
149 #endif
150 }
151
152
153 void OpenGLTextureVolume::Bind(GLint location) const
154 {
155 glActiveTexture(GL_TEXTURE0);
156 glBindTexture(GL_TEXTURE_3D, texture_);
157 glUniform1i(location, 0 /* texture unit */);
158 }
159
160
161 void OpenGLTextureVolume::BindAsTextureUnit(GLint location,
162 unsigned int unit) const
163 {
164 if (unit >= 32)
165 {
166 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
167 }
168
169 assert(GL_TEXTURE0 + 1 == GL_TEXTURE1 &&
170 GL_TEXTURE0 + 31 == GL_TEXTURE31);
171
172 glActiveTexture(GL_TEXTURE0 + unit);
173 glBindTexture(GL_TEXTURE_3D, texture_);
174 glUniform1i(location, unit /* texture unit */);
175 }
176
177
178 void OpenGLTextureVolume::Upload(const Orthanc::ImageAccessor& image,
179 unsigned int z)
180 {
181 if (image.GetWidth() != width_ ||
182 image.GetHeight() != height_)
183 {
184 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
185 }
186 else if (z >= depth_)
187 {
188 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
189 }
190 else if (image.GetPitch() != Orthanc::GetBytesPerPixel(format_) * width_)
191 {
192 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Minimal pitch is required for upload");
193 }
194 else if (width_ != 0 &&
195 height_ != 0)
196 {
197 GLenum sourceFormat, internalFormat, pixelType;
198 OpenGLTexture::ConvertToOpenGLFormats(sourceFormat, internalFormat, pixelType, image.GetFormat());
199
200 glBindTexture(GL_TEXTURE_3D, texture_);
201
202 #if defined(__EMSCRIPTEN__) && (ORTHANC_WEBGL2_HEAP_COMPAT == 1)
203 // Check out "OpenGLTexture.cpp" for an explanation
204 EM_ASM({
205 var ptr = emscriptenWebGLGetTexPixelData($5, $4, $2, $3, $0, $1);
206 GLctx.texSubImage3D(GLctx.TEXTURE_3D, 0, 0 /* x offset */, 0 /* y offset */,
207 $6, $2, $3, 1 /* depth */, $4, $5, ptr);
208 },
209 image.GetConstBuffer(), // $0
210 internalFormat, // $1
211 image.GetWidth(), // $2
212 image.GetHeight(), // $3
213 sourceFormat, // $4
214 pixelType, // $5
215 z); // $6
216 #else
217 glTexSubImage3D(GL_TEXTURE_3D, 0, 0 /* x offset */, 0 /* y offset */, z /* z offset */,
218 width_, height_, 1 /* depth */, sourceFormat, pixelType, image.GetConstBuffer());
219 #endif
220 }
221 }
222
223
224 size_t OpenGLTextureVolume::GetMemoryBufferSize() const
225 {
226 return static_cast<size_t>(Orthanc::GetBytesPerPixel(format_)) * width_ * height_ * depth_;
227 }
228
229
230 void OpenGLTextureVolume::Download(void* targetBuffer,
231 size_t targetSize) const
232 {
233 if (targetSize != GetMemoryBufferSize())
234 {
235 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
236 }
237 else if (targetSize == 0)
238 {
239 return;
240 }
241 else if (targetBuffer == NULL)
242 {
243 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
244 }
245 else
246 {
247 #if 1 || defined(__EMSCRIPTEN__)
248 /**
249 * The "glGetTexImage()" function is unavailable in WebGL, it
250 * is necessary to use a framebuffer:
251 * https://stackoverflow.com/a/15064957
252 **/
253 OpenGLFramebuffer framebuffer(context_);
254
255 const size_t sliceSize = targetSize / depth_;
256
257 Orthanc::Image tmp(GetFormat(), GetWidth(), GetHeight(), true);
258 if (sliceSize != tmp.GetPitch() * height_)
259 {
260 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
261 }
262
263 for (unsigned int z = 0; z < depth_; z++)
264 {
265 framebuffer.ReadTexture(tmp, *this, z);
266 memcpy(reinterpret_cast<uint8_t*>(targetBuffer) + z * sliceSize, tmp.GetBuffer(), sliceSize);
267 }
268
269 #else
270 glBindTexture(GL_TEXTURE_3D, texture_);
271
272 switch (format_)
273 {
274 case Orthanc::PixelFormat_Grayscale8:
275 glGetTexImage(GL_TEXTURE_3D, 0 /* base level */, GL_RED, GL_UNSIGNED_BYTE, targetBuffer);
276 break;
277
278 case Orthanc::PixelFormat_RGB24:
279 glGetTexImage(GL_TEXTURE_3D, 0 /* base level */, GL_RGB, GL_UNSIGNED_BYTE, targetBuffer);
280 break;
281
282 case Orthanc::PixelFormat_RGBA32:
283 glGetTexImage(GL_TEXTURE_3D, 0 /* base level */, GL_RGBA, GL_UNSIGNED_BYTE, targetBuffer);
284 break;
285
286 case Orthanc::PixelFormat_Float32:
287 glGetTexImage(GL_TEXTURE_3D, 0 /* base level */, GL_RED, GL_FLOAT, targetBuffer);
288 break;
289
290 default:
291 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
292 }
293 #endif
294 }
295 }
296
297
298 void OpenGLTextureVolume::Download(std::string& target) const
299 {
300 target.resize(GetMemoryBufferSize());
301 Download(target);
302 }
303 }
304 }