Mercurial > hg > orthanc-stone
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 } |