1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../../Precompiled.h"
24 
25 #include "../../Core/Context.h"
26 #include "../../Core/Profiler.h"
27 #include "../../Graphics/Graphics.h"
28 #include "../../Graphics/GraphicsEvents.h"
29 #include "../../Graphics/GraphicsImpl.h"
30 #include "../../Graphics/Renderer.h"
31 #include "../../Graphics/Texture2DArray.h"
32 #include "../../IO/FileSystem.h"
33 #include "../../IO/Log.h"
34 #include "../../Resource/ResourceCache.h"
35 #include "../../Resource/XMLFile.h"
36 
37 #include "../../DebugNew.h"
38 
39 #ifdef _MSC_VER
40 #pragma warning(disable:4355)
41 #endif
42 
43 namespace Urho3D
44 {
45 
OnDeviceLost()46 void Texture2DArray::OnDeviceLost()
47 {
48     if (usage_ > TEXTURE_STATIC)
49         Release();
50 }
51 
OnDeviceReset()52 void Texture2DArray::OnDeviceReset()
53 {
54     if (usage_ > TEXTURE_STATIC || !object_.ptr_ || dataPending_)
55     {
56         // If has a resource file, reload through the resource cache. Otherwise just recreate.
57         ResourceCache* cache = GetSubsystem<ResourceCache>();
58         if (cache->Exists(GetName()))
59             dataLost_ = !cache->ReloadResource(this);
60 
61         if (!object_.ptr_)
62         {
63             Create();
64             dataLost_ = true;
65         }
66     }
67 
68     dataPending_ = false;
69 }
70 
Release()71 void Texture2DArray::Release()
72 {
73     if (graphics_)
74     {
75         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
76         {
77             if (graphics_->GetTexture(i) == this)
78                 graphics_->SetTexture(i, 0);
79         }
80     }
81 
82     if (renderSurface_)
83         renderSurface_->Release();
84 
85     URHO3D_SAFE_RELEASE(object_.ptr_);
86 }
87 
SetData(unsigned layer,unsigned level,int x,int y,int width,int height,const void * data)88 bool Texture2DArray::SetData(unsigned layer, unsigned level, int x, int y, int width, int height, const void* data)
89 {
90     URHO3D_LOGERROR("Texture2DArray not supported on Direct3D9, can not set data");
91     return false;
92 }
93 
SetData(unsigned layer,Deserializer & source)94 bool Texture2DArray::SetData(unsigned layer, Deserializer& source)
95 {
96     SharedPtr<Image> image(new Image(context_));
97     if (!image->Load(source))
98         return false;
99 
100     return SetData(layer, image);
101 }
102 
SetData(unsigned layer,Image * image,bool useAlpha)103 bool Texture2DArray::SetData(unsigned layer, Image* image, bool useAlpha)
104 {
105     if (!image)
106     {
107         URHO3D_LOGERROR("Null image, can not set data");
108         return false;
109     }
110     if (!layers_)
111     {
112         URHO3D_LOGERROR("Number of layers in the array must be set first");
113         return false;
114     }
115     if (layer >= layers_)
116     {
117         URHO3D_LOGERROR("Illegal layer for setting data");
118         return false;
119     }
120 
121     // Use a shared ptr for managing the temporary mip images created during this function
122     SharedPtr<Image> mipImage;
123     unsigned memoryUse = 0;
124     int quality = QUALITY_HIGH;
125     Renderer* renderer = GetSubsystem<Renderer>();
126     if (renderer)
127         quality = renderer->GetTextureQuality();
128 
129     if (!image->IsCompressed())
130     {
131         unsigned char* levelData = image->GetData();
132         int levelWidth = image->GetWidth();
133         int levelHeight = image->GetHeight();
134         unsigned components = image->GetComponents();
135         unsigned format = 0;
136 
137         // Discard unnecessary mip levels
138         for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
139         {
140             mipImage = image->GetNextLevel(); image = mipImage;
141             levelData = image->GetData();
142             levelWidth = image->GetWidth();
143             levelHeight = image->GetHeight();
144         }
145 
146         switch (components)
147         {
148         case 1:
149             format = Graphics::GetAlphaFormat();
150             break;
151 
152         case 4:
153             format = Graphics::GetRGBAFormat();
154             break;
155 
156         default: break;
157         }
158 
159         // Create the texture array when layer 0 is being loaded, check that rest of the layers are same size & format
160         if (!layer)
161         {
162             // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
163             if (IsCompressed() && requestedLevels_ > 1)
164                 requestedLevels_ = 0;
165             // Create the texture array (the number of layers must have been already set)
166             SetSize(0, levelWidth, levelHeight, format);
167         }
168         else
169         {
170             if (!object_.ptr_)
171             {
172                 // Do not spam this error on D3D9
173                 //URHO3D_LOGERROR("Texture array layer 0 must be loaded first");
174                 return false;
175             }
176             if (levelWidth != width_ || levelHeight != height_ || format != format_)
177             {
178                 URHO3D_LOGERROR("Texture array layer does not match size or format of layer 0");
179                 return false;
180             }
181         }
182 
183         for (unsigned i = 0; i < levels_; ++i)
184         {
185             SetData(layer, i, 0, 0, levelWidth, levelHeight, levelData);
186             memoryUse += levelWidth * levelHeight * components;
187 
188             if (i < levels_ - 1)
189             {
190                 mipImage = image->GetNextLevel(); image = mipImage;
191                 levelData = image->GetData();
192                 levelWidth = image->GetWidth();
193                 levelHeight = image->GetHeight();
194             }
195         }
196     }
197     else
198     {
199         int width = image->GetWidth();
200         int height = image->GetHeight();
201         unsigned levels = image->GetNumCompressedLevels();
202         unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
203         bool needDecompress = false;
204 
205         if (!format)
206         {
207             format = Graphics::GetRGBAFormat();
208             needDecompress = true;
209         }
210 
211         unsigned mipsToSkip = mipsToSkip_[quality];
212         if (mipsToSkip >= levels)
213             mipsToSkip = levels - 1;
214         while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
215             --mipsToSkip;
216         width /= (1 << mipsToSkip);
217         height /= (1 << mipsToSkip);
218 
219         // Create the texture array when layer 0 is being loaded, assume rest of the layers are same size & format
220         if (!layer)
221         {
222             SetNumLevels(Max((levels - mipsToSkip), 1U));
223             SetSize(0, width, height, format);
224         }
225         else
226         {
227             if (!object_.ptr_)
228             {
229                 //URHO3D_LOGERROR("Texture array layer 0 must be loaded first");
230                 return false;
231             }
232             if (width != width_ || height != height_ || format != format_)
233             {
234                 URHO3D_LOGERROR("Texture array layer does not match size or format of layer 0");
235                 return false;
236             }
237         }
238 
239         for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
240         {
241             CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
242             if (!needDecompress)
243             {
244                 SetData(layer, i, 0, 0, level.width_, level.height_, level.data_);
245                 memoryUse += level.rows_ * level.rowSize_;
246             }
247             else
248             {
249                 unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * 4];
250                 level.Decompress(rgbaData);
251                 SetData(layer, i, 0, 0, level.width_, level.height_, rgbaData);
252                 memoryUse += level.width_ * level.height_ * 4;
253                 delete[] rgbaData;
254             }
255         }
256     }
257 
258     layerMemoryUse_[layer] = memoryUse;
259     unsigned totalMemoryUse = sizeof(Texture2DArray) + layerMemoryUse_.Capacity() * sizeof(unsigned);
260     for (unsigned i = 0; i < layers_; ++i)
261         totalMemoryUse += layerMemoryUse_[i];
262     SetMemoryUse(totalMemoryUse);
263 
264     return true;
265 }
266 
GetData(unsigned layer,unsigned level,void * dest) const267 bool Texture2DArray::GetData(unsigned layer, unsigned level, void* dest) const
268 {
269     URHO3D_LOGERROR("Texture2DArray not supported on Direct3D9, can not get data");
270     return false;
271 }
272 
Create()273 bool Texture2DArray::Create()
274 {
275     Release();
276 
277     if (!graphics_ || !width_ || !height_ || !layers_)
278         return false;
279 
280     URHO3D_LOGERROR("Texture2DArray not supported on Direct3D9, can not create");
281     return false;
282 }
283 
284 }
285