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 "../../Graphics/Graphics.h"
27 #include "../../Graphics/GraphicsImpl.h"
28 #include "../../Graphics/IndexBuffer.h"
29 #include "../../IO/Log.h"
30 
31 #include "../../DebugNew.h"
32 
33 namespace Urho3D
34 {
35 
OnDeviceLost()36 void IndexBuffer::OnDeviceLost()
37 {
38     // Dynamic buffers are in the default pool and need to be released on device loss
39     if (dynamic_)
40         Release();
41 }
42 
OnDeviceReset()43 void IndexBuffer::OnDeviceReset()
44 {
45     // Dynamic buffers are in the default pool and need to be recreated after device reset
46     if (dynamic_ || !object_.ptr_)
47     {
48         Create();
49         dataLost_ = !UpdateToGPU();
50     }
51     else if (dataPending_)
52         dataLost_ = !UpdateToGPU();
53 
54     dataPending_ = false;
55 }
56 
Release()57 void IndexBuffer::Release()
58 {
59     Unlock();
60 
61     if (graphics_ && graphics_->GetIndexBuffer() == this)
62         graphics_->SetIndexBuffer(0);
63 
64     URHO3D_SAFE_RELEASE(object_.ptr_);
65 }
66 
SetData(const void * data)67 bool IndexBuffer::SetData(const void* data)
68 {
69     if (!data)
70     {
71         URHO3D_LOGERROR("Null pointer for index buffer data");
72         return false;
73     }
74 
75     if (!indexSize_)
76     {
77         URHO3D_LOGERROR("Index size not defined, can not set index buffer data");
78         return false;
79     }
80 
81     if (shadowData_ && data != shadowData_.Get())
82         memcpy(shadowData_.Get(), data, indexCount_ * indexSize_);
83 
84     if (object_.ptr_)
85     {
86         if (graphics_->IsDeviceLost())
87         {
88             URHO3D_LOGWARNING("Index buffer data assignment while device is lost");
89             dataPending_ = true;
90             return true;
91         }
92 
93         void* hwData = MapBuffer(0, indexCount_, true);
94         if (hwData)
95         {
96             memcpy(hwData, data, indexCount_ * indexSize_);
97             UnmapBuffer();
98         }
99         else
100             return false;
101     }
102 
103     dataLost_ = false;
104     return true;
105 }
106 
SetDataRange(const void * data,unsigned start,unsigned count,bool discard)107 bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
108 {
109     if (start == 0 && count == indexCount_)
110         return SetData(data);
111 
112     if (!data)
113     {
114         URHO3D_LOGERROR("Null pointer for index buffer data");
115         return false;
116     }
117 
118     if (!indexSize_)
119     {
120         URHO3D_LOGERROR("Index size not defined, can not set index buffer data");
121         return false;
122     }
123 
124     if (start + count > indexCount_)
125     {
126         URHO3D_LOGERROR("Illegal range for setting new index buffer data");
127         return false;
128     }
129 
130     if (!count)
131         return true;
132 
133     if (shadowData_ && shadowData_.Get() + start * indexSize_ != data)
134         memcpy(shadowData_.Get() + start * indexSize_, data, count * indexSize_);
135 
136     if (object_.ptr_)
137     {
138         if (graphics_->IsDeviceLost())
139         {
140             URHO3D_LOGWARNING("Index buffer data assignment while device is lost");
141             dataPending_ = true;
142             return true;
143         }
144 
145         void* hwData = MapBuffer(start, count, discard);
146         if (hwData)
147         {
148             memcpy(hwData, data, count * indexSize_);
149             UnmapBuffer();
150         }
151         else
152             return false;
153     }
154 
155     return true;
156 }
157 
Lock(unsigned start,unsigned count,bool discard)158 void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
159 {
160     if (lockState_ != LOCK_NONE)
161     {
162         URHO3D_LOGERROR("Index buffer already locked");
163         return 0;
164     }
165 
166     if (!indexSize_)
167     {
168         URHO3D_LOGERROR("Index size not defined, can not lock index buffer");
169         return 0;
170     }
171 
172     if (start + count > indexCount_)
173     {
174         URHO3D_LOGERROR("Illegal range for locking index buffer");
175         return 0;
176     }
177 
178     if (!count)
179         return 0;
180 
181     lockStart_ = start;
182     lockCount_ = count;
183 
184     // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
185     if (object_.ptr_ && !shadowData_ && !graphics_->IsDeviceLost())
186         return MapBuffer(start, count, discard);
187     else if (shadowData_)
188     {
189         lockState_ = LOCK_SHADOW;
190         return shadowData_.Get() + start * indexSize_;
191     }
192     else if (graphics_)
193     {
194         lockState_ = LOCK_SCRATCH;
195         lockScratchData_ = graphics_->ReserveScratchBuffer(count * indexSize_);
196         return lockScratchData_;
197     }
198     else
199         return 0;
200 }
201 
Unlock()202 void IndexBuffer::Unlock()
203 {
204     switch (lockState_)
205     {
206     case LOCK_HARDWARE:
207         UnmapBuffer();
208         break;
209 
210     case LOCK_SHADOW:
211         SetDataRange(shadowData_.Get() + lockStart_ * indexSize_, lockStart_, lockCount_);
212         lockState_ = LOCK_NONE;
213         break;
214 
215     case LOCK_SCRATCH:
216         SetDataRange(lockScratchData_, lockStart_, lockCount_);
217         if (graphics_)
218             graphics_->FreeScratchBuffer(lockScratchData_);
219         lockScratchData_ = 0;
220         lockState_ = LOCK_NONE;
221         break;
222 
223     default: break;
224     }
225 }
226 
Create()227 bool IndexBuffer::Create()
228 {
229     Release();
230 
231     if (!indexCount_)
232         return true;
233 
234     if (graphics_)
235     {
236         if (graphics_->IsDeviceLost())
237         {
238             URHO3D_LOGWARNING("Index buffer creation while device is lost");
239             return true;
240         }
241 
242         unsigned pool = dynamic_ ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
243         unsigned d3dUsage = dynamic_ ? D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY : 0;
244 
245         IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
246         HRESULT hr = device->CreateIndexBuffer(
247             indexCount_ * indexSize_,
248             d3dUsage,
249             indexSize_ == sizeof(unsigned) ? D3DFMT_INDEX32 : D3DFMT_INDEX16,
250             (D3DPOOL)pool,
251             (IDirect3DIndexBuffer9**)&object_,
252             0);
253         if (FAILED(hr))
254         {
255             URHO3D_SAFE_RELEASE(object_.ptr_)
256             URHO3D_LOGD3DERROR("Could not create index buffer", hr);
257             return false;
258         }
259     }
260 
261     return true;
262 }
263 
UpdateToGPU()264 bool IndexBuffer::UpdateToGPU()
265 {
266     if (object_.ptr_ && shadowData_)
267         return SetData(shadowData_.Get());
268     else
269         return false;
270 }
271 
MapBuffer(unsigned start,unsigned count,bool discard)272 void* IndexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
273 {
274     void* hwData = 0;
275 
276     if (object_.ptr_)
277     {
278         DWORD flags = 0;
279 
280         if (discard && dynamic_)
281             flags = D3DLOCK_DISCARD;
282 
283         HRESULT hr = ((IDirect3DIndexBuffer9*)object_.ptr_)->Lock(start * indexSize_, count * indexSize_, &hwData, flags);
284         if (FAILED(hr))
285             URHO3D_LOGD3DERROR("Could not lock index buffer", hr);
286         else
287             lockState_ = LOCK_HARDWARE;
288     }
289 
290     return hwData;
291 }
292 
UnmapBuffer()293 void IndexBuffer::UnmapBuffer()
294 {
295     if (object_.ptr_ && lockState_ == LOCK_HARDWARE)
296     {
297         ((IDirect3DIndexBuffer9*)object_.ptr_)->Unlock();
298         lockState_ = LOCK_NONE;
299     }
300 }
301 
302 }
303