comparison OrthancStone/Sources/OpenGL/OpenGLTextureArray.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 f5db73779f2d
comparison
equal deleted inserted replaced
2059:f7cbc58ff44d 2060:86e0e92a2e0d
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 "OpenGLTextureArray.h"
25
26 #if defined(__EMSCRIPTEN__)
27 # if !defined(ORTHANC_WEBGL2_HEAP_COMPAT)
28 # error The macro ORTHANC_WEBGL2_HEAP_COMPAT must be defined
29 # endif
30 #endif
31
32 #include "OpenGLFramebuffer.h"
33 #include "OpenGLTexture.h"
34
35 #include <Images/Image.h>
36 #include <OrthancException.h>
37
38 #include <boost/lexical_cast.hpp>
39 #include <cassert>
40
41 namespace OrthancStone
42 {
43 namespace OpenGL
44 {
45 OpenGLTextureArray::OpenGLTextureArray(IOpenGLContext& context) :
46 context_(context),
47 texture_(0),
48 width_(0),
49 height_(0),
50 depth_(0),
51 format_(Orthanc::PixelFormat_Float32),
52 isLinearInterpolation_(false)
53 {
54 if (context.IsContextLost())
55 {
56 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
57 "OpenGL context has been lost");
58 }
59
60 glGenTextures(1, &texture_);
61 ORTHANC_OPENGL_CHECK("glGenTextures()");
62
63 if (texture_ == 0)
64 {
65 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
66 "Cannot create an OpenGL texture array");
67 }
68 }
69
70
71 OpenGLTextureArray::~OpenGLTextureArray()
72 {
73 assert(texture_ != 0);
74 glDeleteTextures(1, &texture_);
75 }
76
77
78 void OpenGLTextureArray::Setup(Orthanc::PixelFormat format,
79 unsigned int width,
80 unsigned int height,
81 unsigned int depth,
82 bool isLinearInterpolation)
83 {
84 glActiveTexture(GL_TEXTURE0);
85 glBindTexture(GL_TEXTURE_2D_ARRAY, texture_);
86 ORTHANC_OPENGL_CHECK("glBindTexture(GL_TEXTURE_2D_ARRAY)");
87
88 GLenum sourceFormat, internalFormat, pixelType;
89 OpenGLTexture::ConvertToOpenGLFormats(sourceFormat, internalFormat, pixelType, format);
90
91 glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internalFormat, width, height, depth,
92 0, sourceFormat, pixelType, NULL);
93 ORTHANC_OPENGL_CHECK("glTexImage3D()");
94
95 #if !defined(__EMSCRIPTEN__)
96 /**
97 * glGetTexLevelParameteriv() was introduced in OpenGL ES 3.1,
98 * but WebGL 2 only supports OpenGL ES 3.0, so it is not
99 * available in WebAssembly:
100 * https://registry.khronos.org/OpenGL-Refpages/es3.1/html/glGetTexLevelParameter.xhtml
101 **/
102 GLint w, h, d;
103 glGetTexLevelParameteriv(GL_TEXTURE_2D_ARRAY, 0, GL_TEXTURE_WIDTH, &w);
104 glGetTexLevelParameteriv(GL_TEXTURE_2D_ARRAY, 0, GL_TEXTURE_HEIGHT, &h);
105 glGetTexLevelParameteriv(GL_TEXTURE_2D_ARRAY, 0, GL_TEXTURE_DEPTH, &d);
106 if (width != static_cast<unsigned int>(w) ||
107 height != static_cast<unsigned int>(h) ||
108 depth != static_cast<unsigned int>(d))
109 {
110 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
111 "Your GPU cannot create an array of textures of size " +
112 boost::lexical_cast<std::string>(width) + " x " +
113 boost::lexical_cast<std::string>(height) + " x " +
114 boost::lexical_cast<std::string>(depth));
115 }
116 #endif
117
118 format_ = format;
119 width_ = width;
120 height_ = height;
121 depth_ = depth;
122 isLinearInterpolation_ = isLinearInterpolation;
123
124 GLint interpolation = (isLinearInterpolation ? GL_LINEAR : GL_NEAREST);
125 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, interpolation);
126 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, interpolation);
127 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
128 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
129 }
130
131
132 void OpenGLTextureArray::SetClampingToZero()
133 {
134 #if defined(__EMSCRIPTEN__)
135 /**
136 * This is because WebGL 2 derives from OpenGL ES 3.0, which
137 * doesn't support GL_CLAMP_TO_BORDER, as can be seen here:
138 * https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glTexParameter.xhtml
139 **/
140 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
141 "OpenGLTextureArray::SetClampingToZero() is not available in WebGL 2");
142 #else
143 ORTHANC_OPENGL_CHECK("Entering OpenGLTextureArray::SetClampingToZero()");
144
145 glBindTexture(GL_TEXTURE_2D_ARRAY, texture_);
146 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
147 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
148
149 GLfloat colorfv[4] = { 0, 0, 0, 0 };
150 glTexParameterfv(texture_, GL_TEXTURE_BORDER_COLOR, colorfv);
151
152 ORTHANC_OPENGL_CHECK("Exiting OpenGLTextureArray::SetClampingToZero()");
153 #endif
154 }
155
156
157 void OpenGLTextureArray::Bind(GLint location) const
158 {
159 glActiveTexture(GL_TEXTURE0);
160 glBindTexture(GL_TEXTURE_2D_ARRAY, texture_);
161 glUniform1i(location, 0 /* texture unit */);
162 }
163
164
165 void OpenGLTextureArray::BindAsTextureUnit(GLint location,
166 unsigned int unit) const
167 {
168 if (unit >= 32)
169 {
170 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
171 }
172
173 assert(GL_TEXTURE0 + 1 == GL_TEXTURE1 &&
174 GL_TEXTURE0 + 31 == GL_TEXTURE31);
175
176 glActiveTexture(GL_TEXTURE0 + unit);
177 glBindTexture(GL_TEXTURE_2D_ARRAY, texture_);
178 glUniform1i(location, unit /* texture unit */);
179 }
180
181
182 void OpenGLTextureArray::Upload(const Orthanc::ImageAccessor& image,
183 unsigned int layer)
184 {
185 if (image.GetWidth() != width_ ||
186 image.GetHeight() != height_)
187 {
188 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
189 }
190
191 if (layer >= depth_)
192 {
193 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
194 }
195
196 if (width_ != 0 &&
197 height_ != 0)
198 {
199 GLenum sourceFormat, internalFormat, pixelType;
200 OpenGLTexture::ConvertToOpenGLFormats(sourceFormat, internalFormat, pixelType, image.GetFormat());
201
202 glBindTexture(GL_TEXTURE_2D_ARRAY, texture_);
203
204 #if defined(__EMSCRIPTEN__) && (ORTHANC_WEBGL2_HEAP_COMPAT == 1)
205 // Check out "OpenGLTexture.cpp" for an explanation
206 EM_ASM({
207 var ptr = emscriptenWebGLGetTexPixelData($5, $4, $2, $3, $0, $1);
208 GLctx.texSubImage3D(GLctx.TEXTURE_2D_ARRAY, 0, 0 /* x offset */, 0 /* y offset */,
209 $6, $2, $3, 1 /* depth */, $4, $5, ptr);
210 },
211 image.GetConstBuffer(), // $0
212 internalFormat, // $1
213 image.GetWidth(), // $2
214 image.GetHeight(), // $3
215 sourceFormat, // $4
216 pixelType, // $5
217 layer); // $6
218 #else
219 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0 /* x offset */, 0 /* y offset */, layer /* z offset */,
220 width_, height_, 1 /* depth */, sourceFormat, pixelType, image.GetConstBuffer());
221 #endif
222 }
223 }
224
225
226 OpenGLTextureArray::DownloadedVolume::DownloadedVolume(const OpenGLTextureArray& texture) :
227 format_(texture.format_),
228 width_(texture.width_),
229 height_(texture.height_),
230 depth_(texture.depth_)
231 {
232 if (width_ != 0 &&
233 height_ != 0 &&
234 depth_ != 0)
235 {
236 buffer_.resize(Orthanc::GetBytesPerPixel(format_) * width_ * height_ * depth_);
237 assert(buffer_.size() > 0);
238
239 #if 1 || defined(__EMSCRIPTEN__)
240 /**
241 * The "glGetTexImage()" function is unavailable in WebGL, it
242 * is necessary to use a framebuffer:
243 * https://stackoverflow.com/a/15064957
244 **/
245 OpenGLFramebuffer framebuffer(texture.context_);
246
247 Orthanc::Image tmp(texture.GetFormat(), texture.GetWidth(), texture.GetHeight(), true);
248
249 for (unsigned int layer = 0; layer < depth_; layer++)
250 {
251 framebuffer.ReadTexture(tmp, texture, layer);
252 memcpy(&buffer_[0] + layer * tmp.GetPitch() * height_,
253 tmp.GetBuffer(), tmp.GetPitch() * height_);
254 }
255
256 #else
257 glBindTexture(GL_TEXTURE_2D_ARRAY, texture.texture_);
258
259 switch (format)
260 {
261 case Orthanc::PixelFormat_Grayscale8:
262 glGetTexImage(GL_TEXTURE_2D_ARRAY, 0 /* base level */, GL_RED, GL_UNSIGNED_BYTE, &buffer_[0]);
263 break;
264
265 case Orthanc::PixelFormat_RGB24:
266 glGetTexImage(GL_TEXTURE_2D_ARRAY, 0 /* base level */, GL_RGB, GL_UNSIGNED_BYTE, &buffer_[0]);
267 break;
268
269 case Orthanc::PixelFormat_RGBA32:
270 glGetTexImage(GL_TEXTURE_2D_ARRAY, 0 /* base level */, GL_RGBA, GL_UNSIGNED_BYTE, &buffer_[0]);
271 break;
272
273 case Orthanc::PixelFormat_Float32:
274 glGetTexImage(GL_TEXTURE_2D_ARRAY, 0 /* base level */, GL_RED, GL_FLOAT, &buffer_[0]);
275 break;
276
277 default:
278 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
279 }
280 #endif
281 }
282 }
283
284
285 Orthanc::ImageAccessor* OpenGLTextureArray::DownloadedVolume::GetLayer(unsigned int layer) const
286 {
287 if (layer >= depth_)
288 {
289 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
290 }
291 else if (width_ == 0 ||
292 height_ == 0 ||
293 depth_ == 0)
294 {
295 return new Orthanc::Image(format_, 0, 0, true);
296 }
297 else
298 {
299 const size_t rowSize = width_ * Orthanc::GetBytesPerPixel(format_);
300
301 std::unique_ptr<Orthanc::ImageAccessor> target(new Orthanc::ImageAccessor);
302 target->AssignReadOnly(format_, width_, height_, rowSize,
303 reinterpret_cast<const uint8_t*>(buffer_.c_str()) + layer * height_ * rowSize);
304 return target.release();
305 }
306 }
307 }
308 }