1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "BsGLRenderTexture.h" |
4 | #include "BsGLPixelFormat.h" |
5 | #include "BsGLPixelBuffer.h" |
6 | #include "RenderAPI/BsTextureView.h" |
7 | |
8 | namespace bs |
9 | { |
10 | #define PROBE_SIZE 16 |
11 | |
12 | static const GLenum depthFormats[] = |
13 | { |
14 | GL_NONE, |
15 | GL_DEPTH_COMPONENT16, |
16 | GL_DEPTH_COMPONENT32, |
17 | GL_DEPTH24_STENCIL8, |
18 | GL_DEPTH32F_STENCIL8 |
19 | }; |
20 | |
21 | #define DEPTHFORMAT_COUNT (sizeof(depthFormats)/sizeof(GLenum)) |
22 | |
23 | GLRenderTexture::GLRenderTexture(const RENDER_TEXTURE_DESC& desc) |
24 | :RenderTexture(desc), mProperties(desc, true) |
25 | { |
26 | |
27 | } |
28 | |
29 | namespace ct |
30 | { |
31 | GLRenderTexture::GLRenderTexture(const RENDER_TEXTURE_DESC& desc, UINT32 deviceIdx) |
32 | :RenderTexture(desc, deviceIdx), mProperties(desc, true), mFB(nullptr) |
33 | { |
34 | assert(deviceIdx == 0 && "Multiple GPUs not supported natively on OpenGL." ); |
35 | } |
36 | |
37 | GLRenderTexture::~GLRenderTexture() |
38 | { |
39 | if (mFB != nullptr) |
40 | bs_delete(mFB); |
41 | } |
42 | |
43 | void GLRenderTexture::initialize() |
44 | { |
45 | RenderTexture::initialize(); |
46 | |
47 | if (mFB != nullptr) |
48 | bs_delete(mFB); |
49 | |
50 | mFB = bs_new<GLFrameBufferObject>(); |
51 | |
52 | for (size_t i = 0; i < BS_MAX_MULTIPLE_RENDER_TARGETS; i++) |
53 | { |
54 | if (mColorSurfaces[i] != nullptr) |
55 | { |
56 | GLTexture* glColorSurface = static_cast<GLTexture*>(mDesc.colorSurfaces[i].texture.get()); |
57 | GLSurfaceDesc surfaceDesc; |
58 | surfaceDesc.numSamples = getProperties().multisampleCount; |
59 | |
60 | if (mColorSurfaces[i]->getNumArraySlices() == 1) // Binding a single texture layer |
61 | { |
62 | surfaceDesc.allLayers = glColorSurface->getProperties().getNumFaces() == 1; |
63 | |
64 | if (glColorSurface->getProperties().getTextureType() != TEX_TYPE_3D) |
65 | { |
66 | surfaceDesc.zoffset = 0; |
67 | surfaceDesc.buffer = glColorSurface->getBuffer(mColorSurfaces[i]->getFirstArraySlice(), |
68 | mColorSurfaces[i]->getMostDetailedMip()); |
69 | } |
70 | else |
71 | { |
72 | surfaceDesc.zoffset = 0; |
73 | surfaceDesc.buffer = glColorSurface->getBuffer(0, mColorSurfaces[i]->getMostDetailedMip()); |
74 | } |
75 | } |
76 | else // Binding an array of textures or a range of 3D texture slices |
77 | { |
78 | surfaceDesc.allLayers = true; |
79 | |
80 | if (glColorSurface->getProperties().getTextureType() != TEX_TYPE_3D) |
81 | { |
82 | if (mColorSurfaces[i]->getNumArraySlices() != glColorSurface->getProperties().getNumFaces()) |
83 | LOGWRN("OpenGL doesn't support binding of arbitrary ranges for array textures. The entire range will be bound instead." ); |
84 | |
85 | surfaceDesc.zoffset = 0; |
86 | surfaceDesc.buffer = glColorSurface->getBuffer(0, mColorSurfaces[i]->getMostDetailedMip()); |
87 | } |
88 | else |
89 | { |
90 | surfaceDesc.zoffset = 0; |
91 | surfaceDesc.buffer = glColorSurface->getBuffer(0, mColorSurfaces[i]->getMostDetailedMip()); |
92 | } |
93 | } |
94 | |
95 | mFB->bindSurface((UINT32)i, surfaceDesc); |
96 | } |
97 | else |
98 | { |
99 | mFB->unbindSurface((UINT32)i); |
100 | } |
101 | } |
102 | |
103 | if (mDepthStencilSurface != nullptr && mDesc.depthStencilSurface.texture != nullptr) |
104 | { |
105 | GLTexture* glDepthStencilTexture = static_cast<GLTexture*>(mDesc.depthStencilSurface.texture.get()); |
106 | SPtr<GLPixelBuffer> depthStencilBuffer = nullptr; |
107 | |
108 | bool allLayers = true; |
109 | if (mDepthStencilSurface->getNumArraySlices() == 1) // Binding a single texture layer |
110 | allLayers = glDepthStencilTexture->getProperties().getNumFaces() == 1; |
111 | |
112 | if (glDepthStencilTexture->getProperties().getTextureType() != TEX_TYPE_3D) |
113 | { |
114 | UINT32 firstSlice = 0; |
115 | if (!allLayers) |
116 | firstSlice = mDepthStencilSurface->getFirstArraySlice(); |
117 | |
118 | depthStencilBuffer = glDepthStencilTexture->getBuffer(firstSlice, |
119 | mDepthStencilSurface->getMostDetailedMip()); |
120 | } |
121 | |
122 | mFB->bindDepthStencil(depthStencilBuffer, allLayers); |
123 | } |
124 | |
125 | mFB->rebuild(); |
126 | } |
127 | |
128 | void GLRenderTexture::getCustomAttribute(const String& name, void* data) const |
129 | { |
130 | if(name=="FBO" ) |
131 | { |
132 | *static_cast<GLFrameBufferObject**>(data) = mFB; |
133 | } |
134 | else if (name == "GL_FBOID" || name == "GL_MULTISAMPLEFBOID" ) |
135 | { |
136 | *static_cast<GLuint*>(data) = mFB->getGLFBOID(); |
137 | } |
138 | } |
139 | |
140 | GLRTTManager::GLRTTManager() |
141 | :mBlitReadFBO(0), mBlitWriteFBO(0) |
142 | { |
143 | detectFBOFormats(); |
144 | |
145 | glGenFramebuffers(1, &mBlitReadFBO); |
146 | BS_CHECK_GL_ERROR(); |
147 | |
148 | glGenFramebuffers(1, &mBlitWriteFBO); |
149 | BS_CHECK_GL_ERROR(); |
150 | } |
151 | |
152 | GLRTTManager::~GLRTTManager() |
153 | { |
154 | glDeleteFramebuffers(1, &mBlitReadFBO); |
155 | BS_CHECK_GL_ERROR(); |
156 | |
157 | glDeleteFramebuffers(1, &mBlitWriteFBO); |
158 | BS_CHECK_GL_ERROR(); |
159 | } |
160 | |
161 | bool GLRTTManager::_tryFormat(GLenum depthFormat, GLenum stencilFormat) |
162 | { |
163 | GLuint status, depthRB = 0, stencilRB = 0; |
164 | bool failed = false; |
165 | |
166 | if (depthFormat != GL_NONE) |
167 | { |
168 | // Generate depth renderbuffer |
169 | glGenRenderbuffers(1, &depthRB); |
170 | BS_CHECK_GL_ERROR(); |
171 | |
172 | // Bind it to FBO |
173 | glBindRenderbuffer(GL_RENDERBUFFER, depthRB); |
174 | BS_CHECK_GL_ERROR(); |
175 | |
176 | // Allocate storage for depth buffer |
177 | glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, PROBE_SIZE, PROBE_SIZE); |
178 | |
179 | if (glGetError() != GL_NO_ERROR) |
180 | failed = true; |
181 | |
182 | // Attach depth |
183 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRB); |
184 | |
185 | if (glGetError() != GL_NO_ERROR) |
186 | failed = true; |
187 | } |
188 | |
189 | if (stencilFormat != GL_NONE) |
190 | { |
191 | // Generate stencil renderbuffer |
192 | glGenRenderbuffers(1, &stencilRB); |
193 | BS_CHECK_GL_ERROR(); |
194 | |
195 | // Bind it to FBO |
196 | glBindRenderbuffer(GL_RENDERBUFFER, stencilRB); |
197 | BS_CHECK_GL_ERROR(); |
198 | |
199 | // Allocate storage for stencil buffer |
200 | glRenderbufferStorage(GL_RENDERBUFFER, stencilFormat, PROBE_SIZE, PROBE_SIZE); |
201 | |
202 | if (glGetError() != GL_NO_ERROR) |
203 | failed = true; |
204 | |
205 | // Attach stencil |
206 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencilRB); |
207 | |
208 | if (glGetError() != GL_NO_ERROR) |
209 | failed = true; |
210 | } |
211 | |
212 | status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
213 | BS_CHECK_GL_ERROR(); |
214 | |
215 | // Detach and destroy |
216 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); |
217 | BS_CHECK_GL_ERROR(); |
218 | |
219 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); |
220 | BS_CHECK_GL_ERROR(); |
221 | |
222 | if (depthRB) |
223 | { |
224 | glDeleteRenderbuffers(1, &depthRB); |
225 | BS_CHECK_GL_ERROR(); |
226 | } |
227 | |
228 | if (stencilRB) |
229 | { |
230 | glDeleteRenderbuffers(1, &stencilRB); |
231 | BS_CHECK_GL_ERROR(); |
232 | } |
233 | |
234 | return status == GL_FRAMEBUFFER_COMPLETE && !failed; |
235 | } |
236 | |
237 | bool GLRTTManager::_tryPackedFormat(GLenum packedFormat) |
238 | { |
239 | GLuint packedRB = 0; |
240 | bool failed = false; // flag on GL errors |
241 | |
242 | // Generate renderbuffer |
243 | glGenRenderbuffers(1, &packedRB); |
244 | BS_CHECK_GL_ERROR(); |
245 | |
246 | // Bind it to FBO |
247 | glBindRenderbuffer(GL_RENDERBUFFER, packedRB); |
248 | BS_CHECK_GL_ERROR(); |
249 | |
250 | // Allocate storage for buffer |
251 | glRenderbufferStorage(GL_RENDERBUFFER, packedFormat, PROBE_SIZE, PROBE_SIZE); |
252 | |
253 | if (glGetError() != GL_NO_ERROR) |
254 | failed = true; |
255 | |
256 | // Attach depth |
257 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
258 | GL_RENDERBUFFER, packedRB); |
259 | |
260 | if (glGetError() != GL_NO_ERROR) |
261 | failed = true; |
262 | |
263 | // Attach stencil |
264 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
265 | GL_RENDERBUFFER, packedRB); |
266 | |
267 | if (glGetError() != GL_NO_ERROR) |
268 | failed = true; |
269 | |
270 | GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
271 | BS_CHECK_GL_ERROR(); |
272 | |
273 | // Detach and destroy |
274 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); |
275 | BS_CHECK_GL_ERROR(); |
276 | |
277 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); |
278 | BS_CHECK_GL_ERROR(); |
279 | |
280 | glDeleteRenderbuffers(1, &packedRB); |
281 | BS_CHECK_GL_ERROR(); |
282 | |
283 | return status == GL_FRAMEBUFFER_COMPLETE && !failed; |
284 | } |
285 | |
286 | void GLRTTManager::detectFBOFormats() |
287 | { |
288 | // Try all formats, and report which ones work as target |
289 | GLuint fb = 0, tid = 0; |
290 | GLint oldDrawbuffer = 0, oldReadbuffer = 0; |
291 | GLenum target = GL_TEXTURE_2D; |
292 | |
293 | glGetIntegerv(GL_DRAW_BUFFER, &oldDrawbuffer); |
294 | BS_CHECK_GL_ERROR(); |
295 | |
296 | glGetIntegerv(GL_READ_BUFFER, &oldReadbuffer); |
297 | BS_CHECK_GL_ERROR(); |
298 | |
299 | for (UINT32 x = 0; x < PF_COUNT; ++x) |
300 | { |
301 | mProps[x].valid = false; |
302 | |
303 | // Fetch GL format token |
304 | GLenum fmt = GLPixelUtil::getGLInternalFormat((PixelFormat)x); |
305 | if (fmt == GL_NONE && x != 0) |
306 | continue; |
307 | |
308 | // No test for compressed formats |
309 | if(PixelUtil::isCompressed((PixelFormat)x)) |
310 | continue; |
311 | |
312 | // No test for unnormalized integer targets |
313 | if (!PixelUtil::isNormalized((PixelFormat)x) && !PixelUtil::isFloatingPoint((PixelFormat)x)) |
314 | continue; |
315 | |
316 | // Create and attach framebuffer |
317 | glGenFramebuffers(1, &fb); |
318 | BS_CHECK_GL_ERROR(); |
319 | |
320 | glBindFramebuffer(GL_FRAMEBUFFER, fb); |
321 | BS_CHECK_GL_ERROR(); |
322 | |
323 | if (fmt != GL_NONE && !PixelUtil::isDepth((PixelFormat)x)) |
324 | { |
325 | // Create and attach texture |
326 | glGenTextures(1, &tid); |
327 | BS_CHECK_GL_ERROR(); |
328 | |
329 | glBindTexture(target, tid); |
330 | BS_CHECK_GL_ERROR(); |
331 | |
332 | glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 0); |
333 | BS_CHECK_GL_ERROR(); |
334 | |
335 | glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
336 | BS_CHECK_GL_ERROR(); |
337 | |
338 | glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
339 | BS_CHECK_GL_ERROR(); |
340 | |
341 | glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
342 | BS_CHECK_GL_ERROR(); |
343 | |
344 | glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
345 | BS_CHECK_GL_ERROR(); |
346 | |
347 | glTexImage2D(target, 0, fmt, PROBE_SIZE, PROBE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
348 | BS_CHECK_GL_ERROR(); |
349 | |
350 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, tid, 0); |
351 | BS_CHECK_GL_ERROR(); |
352 | } |
353 | else |
354 | { |
355 | // Draw to nowhere (stencil/depth only) |
356 | glDrawBuffer(GL_NONE); |
357 | BS_CHECK_GL_ERROR(); |
358 | |
359 | glReadBuffer(GL_NONE); |
360 | BS_CHECK_GL_ERROR(); |
361 | } |
362 | |
363 | // Check status |
364 | GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
365 | BS_CHECK_GL_ERROR(); |
366 | |
367 | // Ignore status in case of fmt==GL_NONE, because no implementation will accept |
368 | // a buffer without *any* attachment. Buffers with only stencil and depth attachment |
369 | // might still be supported, so we must continue probing. |
370 | if (fmt == GL_NONE || status == GL_FRAMEBUFFER_COMPLETE) |
371 | { |
372 | mProps[x].valid = true; |
373 | |
374 | // For each depth/stencil formats |
375 | for (UINT32 depth = 0; depth < DEPTHFORMAT_COUNT; ++depth) |
376 | { |
377 | if (depthFormats[depth] != GL_DEPTH24_STENCIL8 && depthFormats[depth] != GL_DEPTH32F_STENCIL8) |
378 | { |
379 | if (_tryFormat(depthFormats[depth], GL_NONE)) |
380 | { |
381 | /// Add mode to allowed modes |
382 | FormatProperties::Mode mode; |
383 | mode.depth = depth; |
384 | mode.stencil = 0; |
385 | mProps[x].modes.push_back(mode); |
386 | } |
387 | } |
388 | else |
389 | { |
390 | // Packed depth/stencil format |
391 | if (_tryPackedFormat(depthFormats[depth])) |
392 | { |
393 | /// Add mode to allowed modes |
394 | FormatProperties::Mode mode; |
395 | mode.depth = depth; |
396 | mode.stencil = 0; // unuse |
397 | mProps[x].modes.push_back(mode); |
398 | } |
399 | } |
400 | } |
401 | } |
402 | |
403 | // Delete texture and framebuffer |
404 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
405 | BS_CHECK_GL_ERROR(); |
406 | |
407 | glDeleteFramebuffers(1, &fb); |
408 | BS_CHECK_GL_ERROR(); |
409 | |
410 | glFinish(); |
411 | BS_CHECK_GL_ERROR(); |
412 | |
413 | if (fmt != GL_NONE) |
414 | { |
415 | glDeleteTextures(1, &tid); |
416 | BS_CHECK_GL_ERROR(); |
417 | } |
418 | } |
419 | |
420 | glDrawBuffer(oldDrawbuffer); |
421 | BS_CHECK_GL_ERROR(); |
422 | |
423 | glReadBuffer(oldReadbuffer); |
424 | BS_CHECK_GL_ERROR(); |
425 | } |
426 | |
427 | PixelFormat GLRTTManager::getSupportedAlternative(PixelFormat format) |
428 | { |
429 | if (checkFormat(format)) |
430 | return format; |
431 | |
432 | // Find first alternative |
433 | PixelComponentType pct = PixelUtil::getElementType(format); |
434 | switch (pct) |
435 | { |
436 | case PCT_BYTE: format = PF_RGBA8; break; |
437 | case PCT_FLOAT16: format = PF_RGBA16F; break; |
438 | case PCT_FLOAT32: format = PF_RGBA32F; break; |
439 | default: break; |
440 | } |
441 | |
442 | if (checkFormat(format)) |
443 | return format; |
444 | |
445 | // If none at all, return to default |
446 | return PF_RGBA8; |
447 | } |
448 | } |
449 | } |