1 //
2 // Copyright 2012 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // VertexDeclarationCache.cpp: Implements a helper class to construct and cache vertex declarations.
8
9 #include "libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.h"
10
11 #include "libANGLE/Context.h"
12 #include "libANGLE/VertexAttribute.h"
13 #include "libANGLE/formatutils.h"
14 #include "libANGLE/renderer/d3d/ProgramD3D.h"
15 #include "libANGLE/renderer/d3d/d3d9/Context9.h"
16 #include "libANGLE/renderer/d3d/d3d9/VertexBuffer9.h"
17 #include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
18
19 namespace rx
20 {
21
VertexDeclarationCache()22 VertexDeclarationCache::VertexDeclarationCache() : mMaxLru(0)
23 {
24 for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
25 {
26 mVertexDeclCache[i].vertexDeclaration = nullptr;
27 mVertexDeclCache[i].lruCount = 0;
28 }
29
30 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
31 {
32 mAppliedVBs[i].serial = 0;
33 }
34
35 mLastSetVDecl = nullptr;
36 mInstancingEnabled = true;
37 }
38
~VertexDeclarationCache()39 VertexDeclarationCache::~VertexDeclarationCache()
40 {
41 for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
42 {
43 SafeRelease(mVertexDeclCache[i].vertexDeclaration);
44 }
45 }
46
applyDeclaration(const gl::Context * context,IDirect3DDevice9 * device,const std::vector<TranslatedAttribute> & attributes,gl::Program * program,GLint start,GLsizei instances,GLsizei * repeatDraw)47 angle::Result VertexDeclarationCache::applyDeclaration(
48 const gl::Context *context,
49 IDirect3DDevice9 *device,
50 const std::vector<TranslatedAttribute> &attributes,
51 gl::Program *program,
52 GLint start,
53 GLsizei instances,
54 GLsizei *repeatDraw)
55 {
56 ASSERT(gl::MAX_VERTEX_ATTRIBS >= attributes.size());
57
58 *repeatDraw = 1;
59
60 const size_t invalidAttribIndex = attributes.size();
61 size_t indexedAttribute = invalidAttribIndex;
62 size_t instancedAttribute = invalidAttribIndex;
63
64 if (instances == 0)
65 {
66 for (size_t i = 0; i < attributes.size(); ++i)
67 {
68 if (attributes[i].divisor != 0)
69 {
70 // If a divisor is set, it still applies even if an instanced draw was not used, so
71 // treat as a single-instance draw.
72 instances = 1;
73 break;
74 }
75 }
76 }
77
78 if (instances > 0)
79 {
80 // Find an indexed attribute to be mapped to D3D stream 0
81 for (size_t i = 0; i < attributes.size(); i++)
82 {
83 if (attributes[i].active)
84 {
85 if (indexedAttribute == invalidAttribIndex && attributes[i].divisor == 0)
86 {
87 indexedAttribute = i;
88 }
89 else if (instancedAttribute == invalidAttribIndex && attributes[i].divisor != 0)
90 {
91 instancedAttribute = i;
92 }
93 if (indexedAttribute != invalidAttribIndex &&
94 instancedAttribute != invalidAttribIndex)
95 break; // Found both an indexed and instanced attribute
96 }
97 }
98
99 // The validation layer checks that there is at least one active attribute with a zero
100 // divisor as per the GL_ANGLE_instanced_arrays spec.
101 ASSERT(indexedAttribute != invalidAttribIndex);
102 }
103
104 D3DCAPS9 caps;
105 device->GetDeviceCaps(&caps);
106
107 D3DVERTEXELEMENT9 elements[gl::MAX_VERTEX_ATTRIBS + 1];
108 D3DVERTEXELEMENT9 *element = &elements[0];
109
110 ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
111 const auto &semanticIndexes = programD3D->getAttribLocationToD3DSemantics();
112
113 for (size_t i = 0; i < attributes.size(); i++)
114 {
115 if (attributes[i].active)
116 {
117 // Directly binding the storage buffer is not supported for d3d9
118 ASSERT(attributes[i].storage == nullptr);
119
120 int stream = static_cast<int>(i);
121
122 if (instances > 0)
123 {
124 // Due to a bug on ATI cards we can't enable instancing when none of the attributes
125 // are instanced.
126 if (instancedAttribute == invalidAttribIndex)
127 {
128 *repeatDraw = instances;
129 }
130 else
131 {
132 if (i == indexedAttribute)
133 {
134 stream = 0;
135 }
136 else if (i == 0)
137 {
138 stream = static_cast<int>(indexedAttribute);
139 }
140
141 UINT frequency = 1;
142
143 if (attributes[i].divisor == 0)
144 {
145 frequency = D3DSTREAMSOURCE_INDEXEDDATA | instances;
146 }
147 else
148 {
149 frequency = D3DSTREAMSOURCE_INSTANCEDATA | attributes[i].divisor;
150 }
151
152 device->SetStreamSourceFreq(stream, frequency);
153 mInstancingEnabled = true;
154 }
155 }
156
157 VertexBuffer9 *vertexBuffer = GetAs<VertexBuffer9>(attributes[i].vertexBuffer.get());
158
159 unsigned int offset = 0;
160 ANGLE_TRY(attributes[i].computeOffset(context, start, &offset));
161
162 if (mAppliedVBs[stream].serial != attributes[i].serial ||
163 mAppliedVBs[stream].stride != attributes[i].stride ||
164 mAppliedVBs[stream].offset != offset)
165 {
166 device->SetStreamSource(stream, vertexBuffer->getBuffer(), offset,
167 attributes[i].stride);
168 mAppliedVBs[stream].serial = attributes[i].serial;
169 mAppliedVBs[stream].stride = attributes[i].stride;
170 mAppliedVBs[stream].offset = offset;
171 }
172
173 angle::FormatID vertexformatID =
174 gl::GetVertexFormatID(*attributes[i].attribute, gl::VertexAttribType::Float);
175 const d3d9::VertexFormat &d3d9VertexInfo =
176 d3d9::GetVertexFormatInfo(caps.DeclTypes, vertexformatID);
177
178 element->Stream = static_cast<WORD>(stream);
179 element->Offset = 0;
180 element->Type = static_cast<BYTE>(d3d9VertexInfo.nativeFormat);
181 element->Method = D3DDECLMETHOD_DEFAULT;
182 element->Usage = D3DDECLUSAGE_TEXCOORD;
183 element->UsageIndex = static_cast<BYTE>(semanticIndexes[i]);
184 element++;
185 }
186 }
187
188 if (instances == 0 || instancedAttribute == invalidAttribIndex)
189 {
190 if (mInstancingEnabled)
191 {
192 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
193 {
194 device->SetStreamSourceFreq(i, 1);
195 }
196
197 mInstancingEnabled = false;
198 }
199 }
200
201 static const D3DVERTEXELEMENT9 end = D3DDECL_END();
202 *(element++) = end;
203
204 for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
205 {
206 VertexDeclCacheEntry *entry = &mVertexDeclCache[i];
207 if (memcmp(entry->cachedElements, elements,
208 (element - elements) * sizeof(D3DVERTEXELEMENT9)) == 0 &&
209 entry->vertexDeclaration)
210 {
211 entry->lruCount = ++mMaxLru;
212 if (entry->vertexDeclaration != mLastSetVDecl)
213 {
214 device->SetVertexDeclaration(entry->vertexDeclaration);
215 mLastSetVDecl = entry->vertexDeclaration;
216 }
217
218 return angle::Result::Continue;
219 }
220 }
221
222 VertexDeclCacheEntry *lastCache = mVertexDeclCache;
223
224 for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
225 {
226 if (mVertexDeclCache[i].lruCount < lastCache->lruCount)
227 {
228 lastCache = &mVertexDeclCache[i];
229 }
230 }
231
232 if (lastCache->vertexDeclaration != nullptr)
233 {
234 SafeRelease(lastCache->vertexDeclaration);
235 // mLastSetVDecl is set to the replacement, so we don't have to worry
236 // about it.
237 }
238
239 memcpy(lastCache->cachedElements, elements, (element - elements) * sizeof(D3DVERTEXELEMENT9));
240 HRESULT result = device->CreateVertexDeclaration(elements, &lastCache->vertexDeclaration);
241 ANGLE_TRY_HR(GetImplAs<Context9>(context), result,
242 "Failed to create internal vertex declaration");
243
244 device->SetVertexDeclaration(lastCache->vertexDeclaration);
245 mLastSetVDecl = lastCache->vertexDeclaration;
246 lastCache->lruCount = ++mMaxLru;
247
248 return angle::Result::Continue;
249 }
250
markStateDirty()251 void VertexDeclarationCache::markStateDirty()
252 {
253 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
254 {
255 mAppliedVBs[i].serial = 0;
256 }
257
258 mLastSetVDecl = nullptr;
259 mInstancingEnabled = true; // Forces it to be disabled when not used
260 }
261
262 } // namespace rx
263