• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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