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