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 "Image/BsTexture.h" |
4 | #include "Private/RTTI/BsTextureRTTI.h" |
5 | #include "FileSystem/BsDataStream.h" |
6 | #include "Error/BsException.h" |
7 | #include "Debug/BsDebug.h" |
8 | #include "CoreThread/BsCoreThread.h" |
9 | #include "Threading/BsAsyncOp.h" |
10 | #include "Resources/BsResources.h" |
11 | #include "Image/BsPixelUtil.h" |
12 | |
13 | namespace bs |
14 | { |
15 | TEXTURE_COPY_DESC TEXTURE_COPY_DESC::DEFAULT = TEXTURE_COPY_DESC(); |
16 | |
17 | TextureProperties::TextureProperties(const TEXTURE_DESC& desc) |
18 | :mDesc(desc) |
19 | { |
20 | |
21 | } |
22 | |
23 | bool TextureProperties::hasAlpha() const |
24 | { |
25 | return PixelUtil::hasAlpha(mDesc.format); |
26 | } |
27 | |
28 | UINT32 TextureProperties::getNumFaces() const |
29 | { |
30 | UINT32 facesPerSlice = getTextureType() == TEX_TYPE_CUBE_MAP ? 6 : 1; |
31 | |
32 | return facesPerSlice * mDesc.numArraySlices; |
33 | } |
34 | |
35 | void TextureProperties::mapFromSubresourceIdx(UINT32 subresourceIdx, UINT32& face, UINT32& mip) const |
36 | { |
37 | UINT32 numMipmaps = getNumMipmaps() + 1; |
38 | |
39 | face = Math::floorToInt((subresourceIdx) / (float)numMipmaps); |
40 | mip = subresourceIdx % numMipmaps; |
41 | } |
42 | |
43 | UINT32 TextureProperties::mapToSubresourceIdx(UINT32 face, UINT32 mip) const |
44 | { |
45 | return face * (getNumMipmaps() + 1) + mip; |
46 | } |
47 | |
48 | SPtr<PixelData> TextureProperties::allocBuffer(UINT32 face, UINT32 mipLevel) const |
49 | { |
50 | UINT32 width = getWidth(); |
51 | UINT32 height = getHeight(); |
52 | UINT32 depth = getDepth(); |
53 | |
54 | for (UINT32 j = 0; j < mipLevel; j++) |
55 | { |
56 | if (width != 1) width /= 2; |
57 | if (height != 1) height /= 2; |
58 | if (depth != 1) depth /= 2; |
59 | } |
60 | |
61 | SPtr<PixelData> dst = bs_shared_ptr_new<PixelData>(width, height, depth, getFormat()); |
62 | dst->allocateInternalBuffer(); |
63 | |
64 | return dst; |
65 | } |
66 | |
67 | Texture::Texture(const TEXTURE_DESC& desc) |
68 | :mProperties(desc) |
69 | { |
70 | |
71 | } |
72 | |
73 | Texture::Texture(const TEXTURE_DESC& desc, const SPtr<PixelData>& pixelData) |
74 | : mProperties(desc), mInitData(pixelData) |
75 | { |
76 | if (mInitData != nullptr) |
77 | mInitData->_lock(); |
78 | } |
79 | |
80 | void Texture::initialize() |
81 | { |
82 | mSize = calculateSize(); |
83 | |
84 | // Allocate CPU buffers if needed |
85 | if ((mProperties.getUsage() & TU_CPUCACHED) != 0) |
86 | { |
87 | createCPUBuffers(); |
88 | |
89 | if (mInitData != nullptr) |
90 | updateCPUBuffers(0, *mInitData); |
91 | } |
92 | |
93 | Resource::initialize(); |
94 | } |
95 | |
96 | SPtr<ct::CoreObject> Texture::createCore() const |
97 | { |
98 | const TextureProperties& props = getProperties(); |
99 | |
100 | SPtr<ct::CoreObject> coreObj = ct::TextureManager::instance().createTextureInternal(props.mDesc, mInitData); |
101 | |
102 | if ((mProperties.getUsage() & TU_CPUCACHED) == 0) |
103 | mInitData = nullptr; |
104 | |
105 | return coreObj; |
106 | } |
107 | |
108 | AsyncOp Texture::writeData(const SPtr<PixelData>& data, UINT32 face, UINT32 mipLevel, bool discardEntireBuffer) |
109 | { |
110 | UINT32 subresourceIdx = mProperties.mapToSubresourceIdx(face, mipLevel); |
111 | updateCPUBuffers(subresourceIdx, *data); |
112 | |
113 | data->_lock(); |
114 | |
115 | std::function<void(const SPtr<ct::Texture>&, UINT32, UINT32, const SPtr<PixelData>&, bool, AsyncOp&)> func = |
116 | [&](const SPtr<ct::Texture>& texture, UINT32 _face, UINT32 _mipLevel, const SPtr<PixelData>& _pixData, |
117 | bool _discardEntireBuffer, AsyncOp& asyncOp) |
118 | { |
119 | texture->writeData(*_pixData, _mipLevel, _face, _discardEntireBuffer); |
120 | _pixData->_unlock(); |
121 | asyncOp._completeOperation(); |
122 | |
123 | }; |
124 | |
125 | return gCoreThread().queueReturnCommand(std::bind(func, getCore(), face, mipLevel, |
126 | data, discardEntireBuffer, std::placeholders::_1)); |
127 | } |
128 | |
129 | AsyncOp Texture::readData(const SPtr<PixelData>& data, UINT32 face, UINT32 mipLevel) |
130 | { |
131 | data->_lock(); |
132 | |
133 | std::function<void(const SPtr<ct::Texture>&, UINT32, UINT32, const SPtr<PixelData>&, AsyncOp&)> func = |
134 | [&](const SPtr<ct::Texture>& texture, UINT32 _face, UINT32 _mipLevel, const SPtr<PixelData>& _pixData, |
135 | AsyncOp& asyncOp) |
136 | { |
137 | // Make sure any queued command start executing before reading |
138 | ct::RenderAPI::instance().submitCommandBuffer(nullptr); |
139 | |
140 | texture->readData(*_pixData, _mipLevel, _face); |
141 | _pixData->_unlock(); |
142 | asyncOp._completeOperation(); |
143 | |
144 | }; |
145 | |
146 | return gCoreThread().queueReturnCommand(std::bind(func, getCore(), face, mipLevel, |
147 | data, std::placeholders::_1)); |
148 | } |
149 | |
150 | TAsyncOp<SPtr<PixelData>> Texture::readData(UINT32 face, UINT32 mipLevel) |
151 | { |
152 | TAsyncOp<SPtr<PixelData>> op; |
153 | |
154 | auto func = [texture = getCore(), face, mipLevel, op]() mutable |
155 | { |
156 | // Make sure any queued command start executing before reading |
157 | ct::RenderAPI::instance().submitCommandBuffer(nullptr); |
158 | |
159 | SPtr<PixelData> output = texture->getProperties().allocBuffer(face, mipLevel); |
160 | texture->readData(*output, mipLevel, face); |
161 | |
162 | op._completeOperation(output); |
163 | |
164 | }; |
165 | |
166 | gCoreThread().queueCommand(func); |
167 | return op; |
168 | } |
169 | |
170 | UINT32 Texture::calculateSize() const |
171 | { |
172 | return mProperties.getNumFaces() * PixelUtil::getMemorySize(mProperties.getWidth(), |
173 | mProperties.getHeight(), mProperties.getDepth(), mProperties.getFormat()); |
174 | } |
175 | |
176 | void Texture::updateCPUBuffers(UINT32 subresourceIdx, const PixelData& pixelData) |
177 | { |
178 | if ((mProperties.getUsage() & TU_CPUCACHED) == 0) |
179 | return; |
180 | |
181 | if (subresourceIdx >= (UINT32)mCPUSubresourceData.size()) |
182 | { |
183 | LOGERR("Invalid subresource index: " + toString(subresourceIdx) + ". Supported range: 0 .. " + toString((UINT32)mCPUSubresourceData.size())); |
184 | return; |
185 | } |
186 | |
187 | UINT32 mipLevel; |
188 | UINT32 face; |
189 | mProperties.mapFromSubresourceIdx(subresourceIdx, face, mipLevel); |
190 | |
191 | UINT32 mipWidth, mipHeight, mipDepth; |
192 | PixelUtil::getSizeForMipLevel(mProperties.getWidth(), mProperties.getHeight(), mProperties.getDepth(), |
193 | mipLevel, mipWidth, mipHeight, mipDepth); |
194 | |
195 | if (pixelData.getWidth() != mipWidth || pixelData.getHeight() != mipHeight || |
196 | pixelData.getDepth() != mipDepth || pixelData.getFormat() != mProperties.getFormat()) |
197 | { |
198 | LOGERR("Provided buffer is not of valid dimensions or format in order to update this texture." ); |
199 | return; |
200 | } |
201 | |
202 | if (mCPUSubresourceData[subresourceIdx]->getSize() != pixelData.getSize()) |
203 | BS_EXCEPT(InternalErrorException, "Buffer sizes don't match." ); |
204 | |
205 | UINT8* dest = mCPUSubresourceData[subresourceIdx]->getData(); |
206 | UINT8* src = pixelData.getData(); |
207 | |
208 | memcpy(dest, src, pixelData.getSize()); |
209 | } |
210 | |
211 | void Texture::readCachedData(PixelData& dest, UINT32 face, UINT32 mipLevel) |
212 | { |
213 | if ((mProperties.getUsage() & TU_CPUCACHED) == 0) |
214 | { |
215 | LOGERR("Attempting to read CPU data from a texture that is created without CPU caching." ); |
216 | return; |
217 | } |
218 | |
219 | UINT32 mipWidth, mipHeight, mipDepth; |
220 | PixelUtil::getSizeForMipLevel(mProperties.getWidth(), mProperties.getHeight(), mProperties.getDepth(), |
221 | mipLevel, mipWidth, mipHeight, mipDepth); |
222 | |
223 | if (dest.getWidth() != mipWidth || dest.getHeight() != mipHeight || |
224 | dest.getDepth() != mipDepth || dest.getFormat() != mProperties.getFormat()) |
225 | { |
226 | LOGERR("Provided buffer is not of valid dimensions or format in order to read from this texture." ); |
227 | return; |
228 | } |
229 | |
230 | UINT32 subresourceIdx = mProperties.mapToSubresourceIdx(face, mipLevel); |
231 | if (subresourceIdx >= (UINT32)mCPUSubresourceData.size()) |
232 | { |
233 | LOGERR("Invalid subresource index: " + toString(subresourceIdx) + ". Supported range: 0 .. " + toString((UINT32)mCPUSubresourceData.size())); |
234 | return; |
235 | } |
236 | |
237 | if (mCPUSubresourceData[subresourceIdx]->getSize() != dest.getSize()) |
238 | BS_EXCEPT(InternalErrorException, "Buffer sizes don't match." ); |
239 | |
240 | UINT8* srcPtr = mCPUSubresourceData[subresourceIdx]->getData(); |
241 | UINT8* destPtr = dest.getData(); |
242 | |
243 | memcpy(destPtr, srcPtr, dest.getSize()); |
244 | } |
245 | |
246 | void Texture::createCPUBuffers() |
247 | { |
248 | UINT32 numFaces = mProperties.getNumFaces(); |
249 | UINT32 numMips = mProperties.getNumMipmaps() + 1; |
250 | |
251 | UINT32 numSubresources = numFaces * numMips; |
252 | mCPUSubresourceData.resize(numSubresources); |
253 | |
254 | for (UINT32 i = 0; i < numFaces; i++) |
255 | { |
256 | UINT32 curWidth = mProperties.getWidth(); |
257 | UINT32 curHeight = mProperties.getHeight(); |
258 | UINT32 curDepth = mProperties.getDepth(); |
259 | |
260 | for (UINT32 j = 0; j < numMips; j++) |
261 | { |
262 | UINT32 subresourceIdx = mProperties.mapToSubresourceIdx(i, j); |
263 | |
264 | mCPUSubresourceData[subresourceIdx] = bs_shared_ptr_new<PixelData>(curWidth, curHeight, curDepth, mProperties.getFormat()); |
265 | mCPUSubresourceData[subresourceIdx]->allocateInternalBuffer(); |
266 | |
267 | if (curWidth > 1) |
268 | curWidth = curWidth / 2; |
269 | |
270 | if (curHeight > 1) |
271 | curHeight = curHeight / 2; |
272 | |
273 | if (curDepth > 1) |
274 | curDepth = curDepth / 2; |
275 | } |
276 | } |
277 | } |
278 | |
279 | SPtr<ct::Texture> Texture::getCore() const |
280 | { |
281 | return std::static_pointer_cast<ct::Texture>(mCoreSpecific); |
282 | } |
283 | |
284 | /************************************************************************/ |
285 | /* SERIALIZATION */ |
286 | /************************************************************************/ |
287 | |
288 | RTTITypeBase* Texture::getRTTIStatic() |
289 | { |
290 | return TextureRTTI::instance(); |
291 | } |
292 | |
293 | RTTITypeBase* Texture::getRTTI() const |
294 | { |
295 | return Texture::getRTTIStatic(); |
296 | } |
297 | |
298 | /************************************************************************/ |
299 | /* STATICS */ |
300 | /************************************************************************/ |
301 | HTexture Texture::create(const TEXTURE_DESC& desc) |
302 | { |
303 | SPtr<Texture> texturePtr = _createPtr(desc); |
304 | |
305 | return static_resource_cast<Texture>(gResources()._createResourceHandle(texturePtr)); |
306 | } |
307 | |
308 | HTexture Texture::create(const SPtr<PixelData>& pixelData, int usage, bool hwGammaCorrection) |
309 | { |
310 | SPtr<Texture> texturePtr = _createPtr(pixelData, usage, hwGammaCorrection); |
311 | |
312 | return static_resource_cast<Texture>(gResources()._createResourceHandle(texturePtr)); |
313 | } |
314 | |
315 | SPtr<Texture> Texture::_createPtr(const TEXTURE_DESC& desc) |
316 | { |
317 | return TextureManager::instance().createTexture(desc); |
318 | } |
319 | |
320 | SPtr<Texture> Texture::_createPtr(const SPtr<PixelData>& pixelData, int usage, bool hwGammaCorrection) |
321 | { |
322 | TEXTURE_DESC desc; |
323 | desc.type = pixelData->getDepth() > 1 ? TEX_TYPE_3D : TEX_TYPE_2D; |
324 | desc.width = pixelData->getWidth(); |
325 | desc.height = pixelData->getHeight(); |
326 | desc.depth = pixelData->getDepth(); |
327 | desc.format = pixelData->getFormat(); |
328 | desc.usage = usage; |
329 | desc.hwGamma = hwGammaCorrection; |
330 | |
331 | return TextureManager::instance().createTexture(desc, pixelData); |
332 | } |
333 | |
334 | namespace ct |
335 | { |
336 | SPtr<Texture> Texture::WHITE; |
337 | SPtr<Texture> Texture::BLACK; |
338 | SPtr<Texture> Texture::NORMAL; |
339 | |
340 | Texture::Texture(const TEXTURE_DESC& desc, const SPtr<PixelData>& initData, GpuDeviceFlags deviceMask) |
341 | :mProperties(desc), mInitData(initData) |
342 | { } |
343 | |
344 | void Texture::initialize() |
345 | { |
346 | if (mInitData != nullptr) |
347 | { |
348 | writeData(*mInitData, 0, 0, true); |
349 | mInitData->_unlock(); |
350 | mInitData = nullptr; |
351 | } |
352 | |
353 | CoreObject::initialize(); |
354 | } |
355 | |
356 | void Texture::writeData(const PixelData& src, UINT32 mipLevel, UINT32 face, bool discardEntireBuffer, |
357 | UINT32 queueIdx) |
358 | { |
359 | THROW_IF_NOT_CORE_THREAD; |
360 | |
361 | if(discardEntireBuffer) |
362 | { |
363 | if((mProperties.getUsage() & TU_DYNAMIC) == 0) |
364 | { |
365 | // Buffer discard is enabled but buffer was not created as dynamic. Disabling discard. |
366 | discardEntireBuffer = false; |
367 | } |
368 | } |
369 | |
370 | writeDataImpl(src, mipLevel, face, discardEntireBuffer, queueIdx); |
371 | } |
372 | |
373 | void Texture::readData(PixelData& dest, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx, UINT32 queueIdx) |
374 | { |
375 | THROW_IF_NOT_CORE_THREAD; |
376 | |
377 | PixelData& pixelData = static_cast<PixelData&>(dest); |
378 | |
379 | UINT32 mipWidth, mipHeight, mipDepth; |
380 | PixelUtil::getSizeForMipLevel(mProperties.getWidth(), mProperties.getHeight(), mProperties.getDepth(), |
381 | mipLevel, mipWidth, mipHeight, mipDepth); |
382 | |
383 | if (pixelData.getWidth() != mipWidth || pixelData.getHeight() != mipHeight || |
384 | pixelData.getDepth() != mipDepth || pixelData.getFormat() != mProperties.getFormat()) |
385 | { |
386 | LOGERR("Provided buffer is not of valid dimensions or format in order to read from this texture." ); |
387 | return; |
388 | } |
389 | |
390 | readDataImpl(pixelData, mipLevel, face, deviceIdx, queueIdx); |
391 | } |
392 | |
393 | PixelData Texture::lock(GpuLockOptions options, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx, UINT32 queueIdx) |
394 | { |
395 | THROW_IF_NOT_CORE_THREAD; |
396 | |
397 | if (mipLevel > mProperties.getNumMipmaps()) |
398 | { |
399 | LOGERR("Invalid mip level: " + toString(mipLevel) + ". Min is 0, max is " + toString(mProperties.getNumMipmaps())); |
400 | return PixelData(0, 0, 0, PF_UNKNOWN); |
401 | } |
402 | |
403 | if (face >= mProperties.getNumFaces()) |
404 | { |
405 | LOGERR("Invalid face index: " + toString(face) + ". Min is 0, max is " + toString(mProperties.getNumFaces())); |
406 | return PixelData(0, 0, 0, PF_UNKNOWN); |
407 | } |
408 | |
409 | return lockImpl(options, mipLevel, face, deviceIdx, queueIdx); |
410 | } |
411 | |
412 | void Texture::unlock() |
413 | { |
414 | THROW_IF_NOT_CORE_THREAD; |
415 | |
416 | unlockImpl(); |
417 | } |
418 | |
419 | void Texture::copy(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, const SPtr<CommandBuffer>& commandBuffer) |
420 | { |
421 | THROW_IF_NOT_CORE_THREAD; |
422 | |
423 | if (target->mProperties.getTextureType() != mProperties.getTextureType()) |
424 | { |
425 | LOGERR("Source and destination textures must be of same type." ); |
426 | return; |
427 | } |
428 | |
429 | if (mProperties.getFormat() != target->mProperties.getFormat()) // Note: It might be okay to use different formats of the same size |
430 | { |
431 | LOGERR("Source and destination texture formats must match." ); |
432 | return; |
433 | } |
434 | |
435 | if (target->mProperties.getNumSamples() > 1 && mProperties.getNumSamples() != target->mProperties.getNumSamples()) |
436 | { |
437 | LOGERR("When copying to a multisampled texture, source texture must have the same number of samples." ); |
438 | return; |
439 | } |
440 | |
441 | if (desc.srcFace >= mProperties.getNumFaces()) |
442 | { |
443 | LOGERR("Invalid source face index." ); |
444 | return; |
445 | } |
446 | |
447 | if (desc.dstFace >= target->mProperties.getNumFaces()) |
448 | { |
449 | LOGERR("Invalid destination face index." ); |
450 | return; |
451 | } |
452 | |
453 | if (desc.srcMip > mProperties.getNumMipmaps()) |
454 | { |
455 | LOGERR("Source mip level out of range. Valid range is [0, " + toString(mProperties.getNumMipmaps()) + "]." ); |
456 | return; |
457 | } |
458 | |
459 | if (desc.dstMip > target->mProperties.getNumMipmaps()) |
460 | { |
461 | LOGERR("Destination mip level out of range. Valid range is [0, " + toString(target->mProperties.getNumMipmaps()) + "]." ); |
462 | return; |
463 | } |
464 | |
465 | UINT32 srcWidth, srcHeight, srcDepth; |
466 | PixelUtil::getSizeForMipLevel( |
467 | mProperties.getWidth(), |
468 | mProperties.getHeight(), |
469 | mProperties.getDepth(), |
470 | desc.srcMip, |
471 | srcWidth, |
472 | srcHeight, |
473 | srcDepth); |
474 | |
475 | UINT32 dstWidth, dstHeight, dstDepth; |
476 | PixelUtil::getSizeForMipLevel( |
477 | target->mProperties.getWidth(), |
478 | target->mProperties.getHeight(), |
479 | target->mProperties.getDepth(), |
480 | desc.dstMip, |
481 | dstWidth, |
482 | dstHeight, |
483 | dstDepth); |
484 | |
485 | if(desc.dstPosition.x < 0 || desc.dstPosition.x >= (INT32)dstWidth || |
486 | desc.dstPosition.y < 0 || desc.dstPosition.y >= (INT32)dstHeight || |
487 | desc.dstPosition.z < 0 || desc.dstPosition.z >= (INT32)dstDepth) |
488 | { |
489 | LOGERR("Destination position falls outside the destination texture." ); |
490 | return; |
491 | } |
492 | |
493 | bool entireSurface = desc.srcVolume.getWidth() == 0 || |
494 | desc.srcVolume.getHeight() == 0 || |
495 | desc.srcVolume.getDepth() == 0; |
496 | |
497 | UINT32 dstRight = (UINT32)desc.dstPosition.x; |
498 | UINT32 dstBottom = (UINT32)desc.dstPosition.y; |
499 | UINT32 dstBack = (UINT32)desc.dstPosition.z; |
500 | if(!entireSurface) |
501 | { |
502 | if(desc.srcVolume.left >= srcWidth || desc.srcVolume.right > srcWidth || |
503 | desc.srcVolume.top >= srcHeight || desc.srcVolume.bottom > srcHeight || |
504 | desc.srcVolume.front >= srcDepth || desc.srcVolume.back > srcDepth) |
505 | { |
506 | LOGERR("Source volume falls outside the source texture." ); |
507 | return; |
508 | } |
509 | |
510 | dstRight += desc.srcVolume.getWidth(); |
511 | dstBottom += desc.srcVolume.getHeight(); |
512 | dstBack += desc.srcVolume.getDepth(); |
513 | } |
514 | else |
515 | { |
516 | dstRight += srcWidth; |
517 | dstBottom += srcHeight; |
518 | dstBack += srcDepth; |
519 | } |
520 | |
521 | if(dstRight > dstWidth || dstBottom > dstHeight || dstBack > dstDepth) |
522 | { |
523 | LOGERR("Destination volume falls outside the destination texture." ); |
524 | return; |
525 | } |
526 | |
527 | copyImpl(target, desc, commandBuffer); |
528 | } |
529 | |
530 | void Texture::clear(const Color& value, UINT32 mipLevel, UINT32 face, UINT32 queueIdx) |
531 | { |
532 | THROW_IF_NOT_CORE_THREAD; |
533 | |
534 | if (face >= mProperties.getNumFaces()) |
535 | { |
536 | LOGERR("Invalid face index." ); |
537 | return; |
538 | } |
539 | |
540 | if (mipLevel > mProperties.getNumMipmaps()) |
541 | { |
542 | LOGERR("Mip level out of range. Valid range is [0, " + toString(mProperties.getNumMipmaps()) + "]." ); |
543 | return; |
544 | } |
545 | |
546 | clearImpl(value, mipLevel, face, queueIdx); |
547 | } |
548 | |
549 | void Texture::clearImpl(const Color& value, UINT32 mipLevel, UINT32 face, UINT32 queueIdx) |
550 | { |
551 | SPtr<PixelData> data = mProperties.allocBuffer(face, mipLevel); |
552 | data->setColors(value); |
553 | |
554 | writeData(*data, mipLevel, face, true, queueIdx); |
555 | } |
556 | |
557 | /************************************************************************/ |
558 | /* TEXTURE VIEW */ |
559 | /************************************************************************/ |
560 | |
561 | SPtr<TextureView> Texture::createView(const TEXTURE_VIEW_DESC& desc) |
562 | { |
563 | return bs_shared_ptr<TextureView>(new (bs_alloc<TextureView>()) TextureView(desc)); |
564 | } |
565 | |
566 | void Texture::clearBufferViews() |
567 | { |
568 | mTextureViews.clear(); |
569 | } |
570 | |
571 | SPtr<TextureView> Texture::requestView(UINT32 mostDetailMip, UINT32 numMips, UINT32 firstArraySlice, |
572 | UINT32 numArraySlices, GpuViewUsage usage) |
573 | { |
574 | THROW_IF_NOT_CORE_THREAD; |
575 | |
576 | const TextureProperties& texProps = getProperties(); |
577 | |
578 | TEXTURE_VIEW_DESC key; |
579 | key.mostDetailMip = mostDetailMip; |
580 | key.numMips = numMips == 0 ? (texProps.getNumMipmaps() + 1) : numMips; |
581 | key.firstArraySlice = firstArraySlice; |
582 | key.numArraySlices = numArraySlices == 0 ? texProps.getNumFaces() : numArraySlices; |
583 | key.usage = usage; |
584 | |
585 | auto iterFind = mTextureViews.find(key); |
586 | if (iterFind == mTextureViews.end()) |
587 | { |
588 | mTextureViews[key] = createView(key); |
589 | |
590 | iterFind = mTextureViews.find(key); |
591 | } |
592 | |
593 | return iterFind->second; |
594 | } |
595 | |
596 | /************************************************************************/ |
597 | /* STATICS */ |
598 | /************************************************************************/ |
599 | SPtr<Texture> Texture::create(const TEXTURE_DESC& desc, GpuDeviceFlags deviceMask) |
600 | { |
601 | return TextureManager::instance().createTexture(desc, deviceMask); |
602 | } |
603 | |
604 | SPtr<Texture> Texture::create(const SPtr<PixelData>& pixelData, int usage, bool hwGammaCorrection, |
605 | GpuDeviceFlags deviceMask) |
606 | { |
607 | TEXTURE_DESC desc; |
608 | desc.type = pixelData->getDepth() > 1 ? TEX_TYPE_3D : TEX_TYPE_2D; |
609 | desc.width = pixelData->getWidth(); |
610 | desc.height = pixelData->getHeight(); |
611 | desc.depth = pixelData->getDepth(); |
612 | desc.format = pixelData->getFormat(); |
613 | desc.usage = usage; |
614 | desc.hwGamma = hwGammaCorrection; |
615 | |
616 | SPtr<Texture> newTex = TextureManager::instance().createTextureInternal(desc, pixelData, deviceMask); |
617 | newTex->initialize(); |
618 | |
619 | return newTex; |
620 | } |
621 | } |
622 | } |
623 | |