1#include "precompiled.h"
2//
3// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7
8// VertexDeclarationCache.cpp: Implements a helper class to construct and cache vertex declarations.
9
10#include "libGLESv2/ProgramBinary.h"
11#include "libGLESv2/VertexAttribute.h"
12#include "libGLESv2/renderer/d3d9/VertexBuffer9.h"
13#include "libGLESv2/renderer/d3d9/VertexDeclarationCache.h"
14#include "libGLESv2/renderer/d3d9/formatutils9.h"
15
16namespace rx
17{
18
19VertexDeclarationCache::VertexDeclarationCache() : mMaxLru(0)
20{
21    for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
22    {
23        mVertexDeclCache[i].vertexDeclaration = NULL;
24        mVertexDeclCache[i].lruCount = 0;
25    }
26
27    for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
28    {
29        mAppliedVBs[i].serial = 0;
30    }
31
32    mLastSetVDecl = NULL;
33    mInstancingEnabled = true;
34}
35
36VertexDeclarationCache::~VertexDeclarationCache()
37{
38    for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
39    {
40        SafeRelease(mVertexDeclCache[i].vertexDeclaration);
41    }
42}
43
44GLenum VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device, TranslatedAttribute attributes[], gl::ProgramBinary *programBinary, GLsizei instances, GLsizei *repeatDraw)
45{
46    *repeatDraw = 1;
47
48    int indexedAttribute = gl::MAX_VERTEX_ATTRIBS;
49    int instancedAttribute = gl::MAX_VERTEX_ATTRIBS;
50
51    if (instances > 0)
52    {
53        // Find an indexed attribute to be mapped to D3D stream 0
54        for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
55        {
56            if (attributes[i].active)
57            {
58                if (indexedAttribute == gl::MAX_VERTEX_ATTRIBS && attributes[i].divisor == 0)
59                {
60                    indexedAttribute = i;
61                }
62                else if (instancedAttribute == gl::MAX_VERTEX_ATTRIBS && attributes[i].divisor != 0)
63                {
64                    instancedAttribute = i;
65                }
66                if (indexedAttribute != gl::MAX_VERTEX_ATTRIBS && instancedAttribute != gl::MAX_VERTEX_ATTRIBS)
67                    break;   // Found both an indexed and instanced attribute
68            }
69        }
70
71        if (indexedAttribute == gl::MAX_VERTEX_ATTRIBS)
72        {
73            return GL_INVALID_OPERATION;
74        }
75    }
76
77    D3DVERTEXELEMENT9 elements[gl::MAX_VERTEX_ATTRIBS + 1];
78    D3DVERTEXELEMENT9 *element = &elements[0];
79
80    for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
81    {
82        if (attributes[i].active)
83        {
84            // Directly binding the storage buffer is not supported for d3d9
85            ASSERT(attributes[i].storage == NULL);
86
87            int stream = i;
88
89            if (instances > 0)
90            {
91                // Due to a bug on ATI cards we can't enable instancing when none of the attributes are instanced.
92                if (instancedAttribute == gl::MAX_VERTEX_ATTRIBS)
93                {
94                    *repeatDraw = instances;
95                }
96                else
97                {
98                    if (i == indexedAttribute)
99                    {
100                        stream = 0;
101                    }
102                    else if (i == 0)
103                    {
104                        stream = indexedAttribute;
105                    }
106
107                    UINT frequency = 1;
108
109                    if (attributes[i].divisor == 0)
110                    {
111                        frequency = D3DSTREAMSOURCE_INDEXEDDATA | instances;
112                    }
113                    else
114                    {
115                        frequency = D3DSTREAMSOURCE_INSTANCEDATA | attributes[i].divisor;
116                    }
117
118                    device->SetStreamSourceFreq(stream, frequency);
119                    mInstancingEnabled = true;
120                }
121            }
122
123            VertexBuffer9 *vertexBuffer = VertexBuffer9::makeVertexBuffer9(attributes[i].vertexBuffer);
124
125            if (mAppliedVBs[stream].serial != attributes[i].serial ||
126                mAppliedVBs[stream].stride != attributes[i].stride ||
127                mAppliedVBs[stream].offset != attributes[i].offset)
128            {
129                device->SetStreamSource(stream, vertexBuffer->getBuffer(), attributes[i].offset, attributes[i].stride);
130                mAppliedVBs[stream].serial = attributes[i].serial;
131                mAppliedVBs[stream].stride = attributes[i].stride;
132                mAppliedVBs[stream].offset = attributes[i].offset;
133            }
134
135            gl::VertexFormat vertexFormat(*attributes[i].attribute, GL_FLOAT);
136
137            element->Stream = stream;
138            element->Offset = 0;
139            element->Type = d3d9::GetNativeVertexFormat(vertexFormat);
140            element->Method = D3DDECLMETHOD_DEFAULT;
141            element->Usage = D3DDECLUSAGE_TEXCOORD;
142            element->UsageIndex = programBinary->getSemanticIndex(i);
143            element++;
144        }
145    }
146
147    if (instances == 0 || instancedAttribute == gl::MAX_VERTEX_ATTRIBS)
148    {
149        if (mInstancingEnabled)
150        {
151            for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
152            {
153                device->SetStreamSourceFreq(i, 1);
154            }
155
156            mInstancingEnabled = false;
157        }
158    }
159
160    static const D3DVERTEXELEMENT9 end = D3DDECL_END();
161    *(element++) = end;
162
163    for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
164    {
165        VertexDeclCacheEntry *entry = &mVertexDeclCache[i];
166        if (memcmp(entry->cachedElements, elements, (element - elements) * sizeof(D3DVERTEXELEMENT9)) == 0 && entry->vertexDeclaration)
167        {
168            entry->lruCount = ++mMaxLru;
169            if(entry->vertexDeclaration != mLastSetVDecl)
170            {
171                device->SetVertexDeclaration(entry->vertexDeclaration);
172                mLastSetVDecl = entry->vertexDeclaration;
173            }
174
175            return GL_NO_ERROR;
176        }
177    }
178
179    VertexDeclCacheEntry *lastCache = mVertexDeclCache;
180
181    for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
182    {
183        if (mVertexDeclCache[i].lruCount < lastCache->lruCount)
184        {
185            lastCache = &mVertexDeclCache[i];
186        }
187    }
188
189    if (lastCache->vertexDeclaration != NULL)
190    {
191        SafeRelease(lastCache->vertexDeclaration);
192        // mLastSetVDecl is set to the replacement, so we don't have to worry
193        // about it.
194    }
195
196    memcpy(lastCache->cachedElements, elements, (element - elements) * sizeof(D3DVERTEXELEMENT9));
197    device->CreateVertexDeclaration(elements, &lastCache->vertexDeclaration);
198    device->SetVertexDeclaration(lastCache->vertexDeclaration);
199    mLastSetVDecl = lastCache->vertexDeclaration;
200    lastCache->lruCount = ++mMaxLru;
201
202    return GL_NO_ERROR;
203}
204
205void VertexDeclarationCache::markStateDirty()
206{
207    for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
208    {
209        mAppliedVBs[i].serial = 0;
210    }
211
212    mLastSetVDecl = NULL;
213    mInstancingEnabled = true;   // Forces it to be disabled when not used
214}
215
216}
217