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 "../../Graphics/Graphics.h"
26 #include "../../Graphics/GraphicsImpl.h"
27 #include "../../Graphics/VertexBuffer.h"
28 #include "../../IO/Log.h"
29 
30 #include "../../DebugNew.h"
31 
32 namespace Urho3D
33 {
34 
OnDeviceLost()35 void VertexBuffer::OnDeviceLost()
36 {
37     // No-op on Direct3D11
38 }
39 
OnDeviceReset()40 void VertexBuffer::OnDeviceReset()
41 {
42     // No-op on Direct3D11
43 }
44 
Release()45 void VertexBuffer::Release()
46 {
47     Unlock();
48 
49     if (graphics_)
50     {
51         for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
52         {
53             if (graphics_->GetVertexBuffer(i) == this)
54                 graphics_->SetVertexBuffer(0);
55         }
56     }
57 
58     URHO3D_SAFE_RELEASE(object_.ptr_);
59 }
60 
SetData(const void * data)61 bool VertexBuffer::SetData(const void* data)
62 {
63     if (!data)
64     {
65         URHO3D_LOGERROR("Null pointer for vertex buffer data");
66         return false;
67     }
68 
69     if (!vertexSize_)
70     {
71         URHO3D_LOGERROR("Vertex elements not defined, can not set vertex buffer data");
72         return false;
73     }
74 
75     if (shadowData_ && data != shadowData_.Get())
76         memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_);
77 
78     if (object_.ptr_)
79     {
80         if (dynamic_)
81         {
82             void* hwData = MapBuffer(0, vertexCount_, true);
83             if (hwData)
84             {
85                 memcpy(hwData, data, vertexCount_ * vertexSize_);
86                 UnmapBuffer();
87             }
88             else
89                 return false;
90         }
91         else
92         {
93             D3D11_BOX destBox;
94             destBox.left = 0;
95             destBox.right = vertexCount_ * vertexSize_;
96             destBox.top = 0;
97             destBox.bottom = 1;
98             destBox.front = 0;
99             destBox.back = 1;
100 
101             graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_.ptr_, 0, &destBox, data, 0, 0);
102         }
103     }
104 
105     return true;
106 }
107 
SetDataRange(const void * data,unsigned start,unsigned count,bool discard)108 bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
109 {
110     if (start == 0 && count == vertexCount_)
111         return SetData(data);
112 
113     if (!data)
114     {
115         URHO3D_LOGERROR("Null pointer for vertex buffer data");
116         return false;
117     }
118 
119     if (!vertexSize_)
120     {
121         URHO3D_LOGERROR("Vertex elements not defined, can not set vertex buffer data");
122         return false;
123     }
124 
125     if (start + count > vertexCount_)
126     {
127         URHO3D_LOGERROR("Illegal range for setting new vertex buffer data");
128         return false;
129     }
130 
131     if (!count)
132         return true;
133 
134     if (shadowData_ && shadowData_.Get() + start * vertexSize_ != data)
135         memcpy(shadowData_.Get() + start * vertexSize_, data, count * vertexSize_);
136 
137     if (object_.ptr_)
138     {
139         if (dynamic_)
140         {
141             void* hwData = MapBuffer(start, count, discard);
142             if (hwData)
143             {
144                 memcpy(hwData, data, count * vertexSize_);
145                 UnmapBuffer();
146             }
147             else
148                 return false;
149         }
150         else
151         {
152             D3D11_BOX destBox;
153             destBox.left = start * vertexSize_;
154             destBox.right = destBox.left + count * vertexSize_;
155             destBox.top = 0;
156             destBox.bottom = 1;
157             destBox.front = 0;
158             destBox.back = 1;
159 
160             graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_.ptr_, 0, &destBox, data, 0, 0);
161         }
162     }
163 
164     return true;
165 }
166 
Lock(unsigned start,unsigned count,bool discard)167 void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
168 {
169     if (lockState_ != LOCK_NONE)
170     {
171         URHO3D_LOGERROR("Vertex buffer already locked");
172         return 0;
173     }
174 
175     if (!vertexSize_)
176     {
177         URHO3D_LOGERROR("Vertex elements not defined, can not lock vertex buffer");
178         return 0;
179     }
180 
181     if (start + count > vertexCount_)
182     {
183         URHO3D_LOGERROR("Illegal range for locking vertex buffer");
184         return 0;
185     }
186 
187     if (!count)
188         return 0;
189 
190     lockStart_ = start;
191     lockCount_ = count;
192 
193     // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
194     if (object_.ptr_ && !shadowData_ && dynamic_)
195         return MapBuffer(start, count, discard);
196     else if (shadowData_)
197     {
198         lockState_ = LOCK_SHADOW;
199         return shadowData_.Get() + start * vertexSize_;
200     }
201     else if (graphics_)
202     {
203         lockState_ = LOCK_SCRATCH;
204         lockScratchData_ = graphics_->ReserveScratchBuffer(count * vertexSize_);
205         return lockScratchData_;
206     }
207     else
208         return 0;
209 }
210 
Unlock()211 void VertexBuffer::Unlock()
212 {
213     switch (lockState_)
214     {
215     case LOCK_HARDWARE:
216         UnmapBuffer();
217         break;
218 
219     case LOCK_SHADOW:
220         SetDataRange(shadowData_.Get() + lockStart_ * vertexSize_, lockStart_, lockCount_);
221         lockState_ = LOCK_NONE;
222         break;
223 
224     case LOCK_SCRATCH:
225         SetDataRange(lockScratchData_, lockStart_, lockCount_);
226         if (graphics_)
227             graphics_->FreeScratchBuffer(lockScratchData_);
228         lockScratchData_ = 0;
229         lockState_ = LOCK_NONE;
230         break;
231 
232     default: break;
233     }
234 }
235 
Create()236 bool VertexBuffer::Create()
237 {
238     Release();
239 
240     if (!vertexCount_ || !elementMask_)
241         return true;
242 
243     if (graphics_)
244     {
245         D3D11_BUFFER_DESC bufferDesc;
246         memset(&bufferDesc, 0, sizeof bufferDesc);
247         bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
248         bufferDesc.CPUAccessFlags = dynamic_ ? D3D11_CPU_ACCESS_WRITE : 0;
249         bufferDesc.Usage = dynamic_ ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
250         bufferDesc.ByteWidth = (UINT)(vertexCount_ * vertexSize_);
251 
252         HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_.ptr_);
253         if (FAILED(hr))
254         {
255             URHO3D_SAFE_RELEASE(object_.ptr_);
256             URHO3D_LOGD3DERROR("Failed to create vertex buffer", hr);
257             return false;
258         }
259     }
260 
261     return true;
262 }
263 
UpdateToGPU()264 bool VertexBuffer::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* VertexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
273 {
274     void* hwData = 0;
275 
276     if (object_.ptr_)
277     {
278         D3D11_MAPPED_SUBRESOURCE mappedData;
279         mappedData.pData = 0;
280 
281         HRESULT hr = graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Buffer*)object_.ptr_, 0, discard ? D3D11_MAP_WRITE_DISCARD :
282             D3D11_MAP_WRITE, 0, &mappedData);
283         if (FAILED(hr) || !mappedData.pData)
284             URHO3D_LOGD3DERROR("Failed to map vertex buffer", hr);
285         else
286         {
287             hwData = mappedData.pData;
288             lockState_ = LOCK_HARDWARE;
289         }
290     }
291 
292     return hwData;
293 }
294 
UnmapBuffer()295 void VertexBuffer::UnmapBuffer()
296 {
297     if (object_.ptr_ && lockState_ == LOCK_HARDWARE)
298     {
299         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Buffer*)object_.ptr_, 0);
300         lockState_ = LOCK_NONE;
301     }
302 }
303 
304 }
305