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 "BsGLTexture.h"
4#include "BsGLSupport.h"
5#include "BsGLPixelFormat.h"
6#include "BsGLPixelBuffer.h"
7#include "Error/BsException.h"
8#include "Utility/BsBitwise.h"
9#include "CoreThread/BsCoreThread.h"
10#include "Managers/BsTextureManager.h"
11#include "BsGLRenderTexture.h"
12#include "BsGLTextureView.h"
13#include "Profiling/BsRenderStats.h"
14#include "BsGLCommandBuffer.h"
15
16namespace bs { namespace ct
17{
18 GLTexture::GLTexture(GLSupport& support, const TEXTURE_DESC& desc, const SPtr<PixelData>& initialData,
19 GpuDeviceFlags deviceMask)
20 : Texture(desc, initialData, deviceMask), mGLSupport(support)
21 {
22 assert((deviceMask == GDF_DEFAULT || deviceMask == GDF_PRIMARY) && "Multiple GPUs not supported natively on OpenGL.");
23 }
24
25 GLTexture::~GLTexture()
26 {
27 mSurfaceList.clear();
28 glDeleteTextures(1, &mTextureID);
29 BS_CHECK_GL_ERROR();
30
31 clearBufferViews();
32
33 BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_Texture);
34 }
35
36 void GLTexture::initialize()
37 {
38 UINT32 width = mProperties.getWidth();
39 UINT32 height = mProperties.getHeight();
40 UINT32 depth = mProperties.getDepth();
41 TextureType texType = mProperties.getTextureType();
42 int usage = mProperties.getUsage();
43 UINT32 numMips = mProperties.getNumMipmaps();
44 UINT32 numFaces = mProperties.getNumFaces();
45
46 PixelFormat pixFormat = mProperties.getFormat();
47 mInternalFormat = GLPixelUtil::getClosestSupportedPF(pixFormat, texType, usage);
48
49 if (pixFormat != mInternalFormat)
50 {
51 LOGWRN(StringUtil::format("Provided pixel format is not supported by the driver: {0}. Falling back on: {1}.",
52 pixFormat, mInternalFormat));
53 }
54
55 // Check requested number of mipmaps
56 UINT32 maxMips = PixelUtil::getMaxMipmaps(width, height, depth, mProperties.getFormat());
57 if (numMips > maxMips)
58 {
59 LOGERR("Invalid number of mipmaps. Maximum allowed is: " + toString(maxMips));
60 numMips = maxMips;
61 }
62
63 if ((usage & TU_DEPTHSTENCIL) != 0)
64 {
65 if (texType != TEX_TYPE_2D && texType != TEX_TYPE_CUBE_MAP)
66 {
67 LOGERR("Only 2D and cubemap depth stencil textures are supported. Ignoring depth-stencil flag.");
68 usage &= ~TU_DEPTHSTENCIL;
69 }
70 }
71
72 // Include the base mip level
73 numMips += 1;
74
75 // Generate texture handle
76 glGenTextures(1, &mTextureID);
77 BS_CHECK_GL_ERROR();
78
79 // Set texture type
80 glBindTexture(getGLTextureTarget(), mTextureID);
81 BS_CHECK_GL_ERROR();
82
83 if(mProperties.getNumSamples() <= 1)
84 {
85 // This needs to be set otherwise the texture doesn't get rendered
86 glTexParameteri(getGLTextureTarget(), GL_TEXTURE_MAX_LEVEL, numMips - 1);
87 BS_CHECK_GL_ERROR();
88 }
89
90 // Allocate internal buffer so that glTexSubImageXD can be used
91 mGLFormat = GLPixelUtil::getGLInternalFormat(mInternalFormat, mProperties.isHardwareGammaEnabled());
92
93 UINT32 sampleCount = mProperties.getNumSamples();
94 if((usage & (TU_RENDERTARGET | TU_DEPTHSTENCIL)) != 0 && mProperties.getTextureType() == TEX_TYPE_2D && sampleCount > 1)
95 {
96 if (numFaces <= 1)
97 {
98 // Create immutable storage if available, fallback to mutable
99#if BS_OPENGL_4_3 || BS_OPENGLES_3_1
100 glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, sampleCount, mGLFormat, width, height, GL_TRUE);
101 BS_CHECK_GL_ERROR();
102#else
103 glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, sampleCount, mGLFormat, width, height, GL_TRUE);
104 BS_CHECK_GL_ERROR();
105#endif
106 }
107 else
108 {
109 // Create immutable storage if available, fallback to mutable
110#if BS_OPENGL_4_3 || BS_OPENGLES_3_2
111 glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, sampleCount, mGLFormat, width, height, numFaces, GL_TRUE);
112 BS_CHECK_GL_ERROR();
113#else
114 glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, sampleCount, mGLFormat, width, height, numFaces, GL_TRUE);
115 BS_CHECK_GL_ERROR();
116#endif
117 }
118 }
119 else
120 {
121 // Create immutable storage if available, fallback to mutable
122#if BS_OPENGL_4_2 || BS_OPENGLES_3_1
123 switch (texType)
124 {
125 case TEX_TYPE_1D:
126 {
127 if (numFaces <= 1)
128 {
129 glTexStorage1D(GL_TEXTURE_1D, numMips, mGLFormat, width);
130 BS_CHECK_GL_ERROR();
131 }
132 else
133 {
134 glTexStorage2D(GL_TEXTURE_1D_ARRAY, numMips, mGLFormat, width, numFaces);
135 BS_CHECK_GL_ERROR();
136 }
137 }
138 break;
139 case TEX_TYPE_2D:
140 {
141 if (numFaces <= 1)
142 {
143 glTexStorage2D(GL_TEXTURE_2D, numMips, mGLFormat, width, height);
144 BS_CHECK_GL_ERROR();
145 }
146 else
147 {
148 glTexStorage3D(GL_TEXTURE_2D_ARRAY, numMips, mGLFormat, width, height, numFaces);
149 BS_CHECK_GL_ERROR();
150 }
151 }
152 break;
153 case TEX_TYPE_3D:
154 glTexStorage3D(GL_TEXTURE_3D, numMips, mGLFormat, width, height, depth);
155 BS_CHECK_GL_ERROR();
156 break;
157 case TEX_TYPE_CUBE_MAP:
158 {
159 if (numFaces <= 6)
160 {
161 glTexStorage2D(GL_TEXTURE_CUBE_MAP, numMips, mGLFormat, width, height);
162 BS_CHECK_GL_ERROR();
163 }
164 else
165 {
166 glTexStorage3D(GL_TEXTURE_CUBE_MAP_ARRAY, numMips, mGLFormat, width, height, numFaces);
167 BS_CHECK_GL_ERROR();
168 }
169 }
170 break;
171 }
172#else
173 if((usage & TU_DEPTHSTENCIL) != 0)
174 {
175 GLenum depthStencilType = GLPixelUtil::getDepthStencilTypeFromPF(mInternalFormat);
176 GLenum depthStencilFormat = GLPixelUtil::getDepthStencilFormatFromPF(mInternalFormat);
177
178 if(texType == TEX_TYPE_2D)
179 {
180 if (numFaces <= 1)
181 {
182 glTexImage2D(GL_TEXTURE_2D, 0, mGLFormat, width, height, 0,
183 depthStencilFormat, depthStencilType, nullptr);
184 BS_CHECK_GL_ERROR();
185 }
186 else
187 {
188 glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, mGLFormat, width, height, numFaces, 0,
189 depthStencilFormat, depthStencilType, nullptr);
190 BS_CHECK_GL_ERROR();
191 }
192 }
193 else if(texType == TEX_TYPE_CUBE_MAP)
194 {
195 if (numFaces <= 6)
196 {
197 for (UINT32 face = 0; face < 6; face++)
198 {
199 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, mGLFormat,
200 width, height, 0, depthStencilFormat, depthStencilType, nullptr);
201 BS_CHECK_GL_ERROR();
202 }
203 }
204 else
205 {
206 glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, mGLFormat,
207 width, height, numFaces, 0, depthStencilFormat, depthStencilType, nullptr);
208 BS_CHECK_GL_ERROR();
209 }
210 }
211 else
212 {
213 LOGERR("Unsupported texture type for depth-stencil attachment usage.");
214 }
215 }
216 else
217 {
218 GLenum baseFormat = GLPixelUtil::getGLOriginFormat(mInternalFormat);
219 GLenum baseDataType = GLPixelUtil::getGLOriginDataType(mInternalFormat);
220
221 for (UINT32 mip = 0; mip < numMips; mip++)
222 {
223 switch (texType)
224 {
225 case TEX_TYPE_1D:
226 {
227 if (numFaces <= 1)
228 {
229 glTexImage1D(GL_TEXTURE_1D, mip, mGLFormat, width, 0, baseFormat, baseDataType, nullptr);
230 BS_CHECK_GL_ERROR();
231 }
232 else
233 {
234 glTexImage2D(GL_TEXTURE_1D_ARRAY, mip, mGLFormat, width, numFaces, 0, baseFormat, baseDataType, nullptr);
235 BS_CHECK_GL_ERROR();
236 }
237 }
238 break;
239 case TEX_TYPE_2D:
240 {
241 if (numFaces <= 1)
242 {
243 glTexImage2D(GL_TEXTURE_2D, mip, mGLFormat, width, height, 0, baseFormat, baseDataType, nullptr);
244 BS_CHECK_GL_ERROR();
245 }
246 else
247 {
248 glTexImage3D(GL_TEXTURE_2D_ARRAY, mip, mGLFormat, width, height, numFaces, 0, baseFormat, baseDataType, nullptr);
249 BS_CHECK_GL_ERROR();
250 }
251 }
252 break;
253 case TEX_TYPE_3D:
254 glTexImage3D(GL_TEXTURE_3D, mip, mGLFormat, width, height,
255 depth, 0, baseFormat, baseDataType, nullptr);
256 BS_CHECK_GL_ERROR();
257 break;
258 case TEX_TYPE_CUBE_MAP:
259 {
260 if (numFaces <= 6)
261 {
262 for (UINT32 face = 0; face < 6; face++)
263 {
264 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, mGLFormat,
265 width, height, 0, baseFormat, baseDataType, nullptr);
266 BS_CHECK_GL_ERROR();
267 }
268 }
269 else
270 {
271 glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, mGLFormat,
272 width, height, numFaces, 0, baseFormat, baseDataType, nullptr);
273 BS_CHECK_GL_ERROR();
274 }
275 }
276 break;
277 }
278
279 if(width > 1)
280 width = width/2;
281
282 if(height > 1)
283 height = height/2;
284
285 if(depth > 1)
286 depth = depth/2;
287 }
288 }
289#endif
290 }
291
292 createSurfaceList();
293
294 BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Texture);
295 Texture::initialize();
296 }
297
298 GLenum GLTexture::getGLTextureTarget() const
299 {
300 return getGLTextureTarget(mProperties.getTextureType(), mProperties.getNumSamples(), mProperties.getNumFaces());
301 }
302
303 GLuint GLTexture::getGLID() const
304 {
305 THROW_IF_NOT_CORE_THREAD;
306
307 return mTextureID;
308 }
309
310 GLenum GLTexture::getGLTextureTarget(TextureType type, UINT32 numSamples, UINT32 numFaces)
311 {
312 switch (type)
313 {
314 case TEX_TYPE_1D:
315 if (numFaces <= 1)
316 return GL_TEXTURE_1D;
317 else
318 return GL_TEXTURE_1D_ARRAY;
319 case TEX_TYPE_2D:
320 if (numSamples > 1)
321 {
322 if (numFaces <= 1)
323 return GL_TEXTURE_2D_MULTISAMPLE;
324 else
325 return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
326 }
327 else
328 {
329 if (numFaces <= 1)
330 return GL_TEXTURE_2D;
331 else
332 return GL_TEXTURE_2D_ARRAY;
333 }
334 case TEX_TYPE_3D:
335 return GL_TEXTURE_3D;
336 case TEX_TYPE_CUBE_MAP:
337 if (numFaces <= 6)
338 return GL_TEXTURE_CUBE_MAP;
339 else
340 return GL_TEXTURE_CUBE_MAP_ARRAY;
341 default:
342 return 0;
343 };
344 }
345
346 GLenum GLTexture::getGLTextureTarget(GpuParamObjectType type)
347 {
348 switch(type)
349 {
350 case GPOT_TEXTURE1D:
351 return GL_TEXTURE_1D;
352 case GPOT_TEXTURE2D:
353 return GL_TEXTURE_2D;
354 case GPOT_TEXTURE2DMS:
355 return GL_TEXTURE_2D_MULTISAMPLE;
356 case GPOT_TEXTURE3D:
357 return GL_TEXTURE_3D;
358 case GPOT_TEXTURECUBE:
359 return GL_TEXTURE_CUBE_MAP;
360 case GPOT_TEXTURE1DARRAY:
361 return GL_TEXTURE_1D_ARRAY;
362 case GPOT_TEXTURE2DARRAY:
363 return GL_TEXTURE_2D_ARRAY;
364 case GPOT_TEXTURE2DMSARRAY:
365 return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
366 case GPOT_TEXTURECUBEARRAY:
367 return GL_TEXTURE_CUBE_MAP_ARRAY;
368 default:
369 return GL_TEXTURE_2D;
370 }
371 }
372
373 PixelData GLTexture::lockImpl(GpuLockOptions options, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx,
374 UINT32 queueIdx)
375 {
376 if (mProperties.getNumSamples() > 1)
377 BS_EXCEPT(InvalidStateException, "Multisampled textures cannot be accessed from the CPU directly.");
378
379 if(mLockedBuffer != nullptr)
380 BS_EXCEPT(InternalErrorException, "Trying to lock a buffer that's already locked.");
381
382 UINT32 mipWidth = std::max(1u, mProperties.getWidth() >> mipLevel);
383 UINT32 mipHeight = std::max(1u, mProperties.getHeight() >> mipLevel);
384 UINT32 mipDepth = std::max(1u, mProperties.getDepth() >> mipLevel);
385
386 PixelData lockedArea(mipWidth, mipHeight, mipDepth, mProperties.getFormat());
387
388 mLockedBuffer = getBuffer(face, mipLevel);
389 lockedArea.setExternalBuffer((UINT8*)mLockedBuffer->lock(options));
390
391 return lockedArea;
392 }
393
394 void GLTexture::unlockImpl()
395 {
396 if (mLockedBuffer == nullptr)
397 {
398 LOGERR("Trying to unlock a buffer that's not locked.");
399 return;
400 }
401
402 mLockedBuffer->unlock();
403 mLockedBuffer = nullptr;
404 }
405
406 void GLTexture::readDataImpl(PixelData& dest, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx, UINT32 queueIdx)
407 {
408 if (mProperties.getNumSamples() > 1)
409 {
410 LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
411 return;
412 }
413
414 if(dest.getFormat() != mInternalFormat)
415 {
416 PixelData temp(dest.getExtents(), mInternalFormat);
417 temp.allocateInternalBuffer();
418
419 getBuffer(face, mipLevel)->download(temp);
420 PixelUtil::bulkPixelConversion(temp, dest);
421 }
422 else
423 getBuffer(face, mipLevel)->download(dest);
424 }
425
426 void GLTexture::writeDataImpl(const PixelData& src, UINT32 mipLevel, UINT32 face, bool discardWholeBuffer,
427 UINT32 queueIdx)
428 {
429 if (mProperties.getNumSamples() > 1)
430 {
431 LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
432 return;
433 }
434
435 if (src.getFormat() != mInternalFormat)
436 {
437 PixelData temp(src.getExtents(), mInternalFormat);
438 temp.allocateInternalBuffer();
439
440 PixelUtil::bulkPixelConversion(src, temp);
441 getBuffer(face, mipLevel)->upload(temp, temp.getExtents());
442 }
443 else
444 getBuffer(face, mipLevel)->upload(src, src.getExtents());
445 }
446
447 void GLTexture::copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc,
448 const SPtr<CommandBuffer>& commandBuffer)
449 {
450 auto executeRef = [this](const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc)
451 {
452 GLTexture* destTex = static_cast<GLTexture*>(target.get());
453 GLTextureBuffer* dest = static_cast<GLTextureBuffer*>(destTex->getBuffer(desc.dstFace, desc.dstMip).get());
454 GLTextureBuffer* src = static_cast<GLTextureBuffer*>(getBuffer(desc.srcFace, desc.srcMip).get());
455
456 bool copyEntireSurface = desc.srcVolume.getWidth() == 0 ||
457 desc.srcVolume.getHeight() == 0 ||
458 desc.srcVolume.getDepth() == 0;
459
460 PixelVolume srcVolume = desc.srcVolume;
461
462 PixelVolume dstVolume;
463 dstVolume.left = (UINT32)desc.dstPosition.x;
464 dstVolume.top = (UINT32)desc.dstPosition.y;
465 dstVolume.front = (UINT32)desc.dstPosition.z;
466
467 if(copyEntireSurface)
468 {
469 srcVolume.right = srcVolume.left + src->getWidth();
470 srcVolume.bottom = srcVolume.top + src->getHeight();
471 srcVolume.back = srcVolume.front + src->getDepth();
472
473 dstVolume.right = dstVolume.left + src->getWidth();
474 dstVolume.bottom = dstVolume.top + src->getHeight();
475 dstVolume.back = dstVolume.front + src->getDepth();
476 }
477 else
478 {
479 dstVolume.right = dstVolume.left + desc.srcVolume.getWidth();
480 dstVolume.bottom = dstVolume.top + desc.srcVolume.getHeight();
481 dstVolume.back = dstVolume.front + desc.srcVolume.getDepth();
482 }
483
484 dest->blitFromTexture(src, srcVolume, dstVolume);
485 };
486
487 if (commandBuffer == nullptr)
488 executeRef(target, desc);
489 else
490 {
491 auto execute = [=]() { executeRef(target, desc); };
492
493 SPtr<GLCommandBuffer> cb = std::static_pointer_cast<GLCommandBuffer>(commandBuffer);
494 cb->queueCommand(execute);
495 }
496 }
497
498 void GLTexture::createSurfaceList()
499 {
500 mSurfaceList.clear();
501
502 for (UINT32 face = 0; face < mProperties.getNumFaces(); face++)
503 {
504 for (UINT32 mip = 0; mip <= mProperties.getNumMipmaps(); mip++)
505 {
506 GLPixelBuffer *buf = bs_new<GLTextureBuffer>(getGLTextureTarget(), mTextureID, face, mip, mInternalFormat,
507 static_cast<GpuBufferUsage>(mProperties.getUsage()),
508 mProperties.isHardwareGammaEnabled(),
509 mProperties.getNumSamples());
510
511 mSurfaceList.push_back(bs_shared_ptr<GLPixelBuffer>(buf));
512 if(buf->getWidth() == 0 || buf->getHeight() == 0 || buf->getDepth() == 0)
513 {
514 BS_EXCEPT(RenderingAPIException,
515 "Zero sized texture surface on texture face " + toString(face) + " mipmap " + toString(mip)
516 + ". Probably, the GL driver refused to create the texture.");
517 }
518 }
519 }
520 }
521
522 SPtr<GLPixelBuffer> GLTexture::getBuffer(UINT32 face, UINT32 mipmap)
523 {
524 THROW_IF_NOT_CORE_THREAD;
525
526 if(face >= mProperties.getNumFaces())
527 BS_EXCEPT(InvalidParametersException, "Face index out of range");
528
529 if (mipmap > mProperties.getNumMipmaps())
530 BS_EXCEPT(InvalidParametersException, "Mipmap index out of range");
531
532 unsigned int idx = face * (mProperties.getNumMipmaps() + 1) + mipmap;
533 assert(idx < mSurfaceList.size());
534 return mSurfaceList[idx];
535 }
536
537 SPtr<TextureView> GLTexture::createView(const TEXTURE_VIEW_DESC& desc)
538 {
539 return bs_shared_ptr<GLTextureView>(new (bs_alloc<GLTextureView>()) GLTextureView(this, desc));
540 }
541}}
542