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/Context.h"
12#include "libGLESv2/renderer/VertexBuffer9.h"
13#include "libGLESv2/renderer/VertexDeclarationCache.h"
14
15namespace rx
16{
17
18VertexDeclarationCache::VertexDeclarationCache() : mMaxLru(0)
19{
20    for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
21    {
22        mVertexDeclCache[i].vertexDeclaration = NULL;
23        mVertexDeclCache[i].lruCount = 0;
24    }
25
26    for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
27    {
28        mAppliedVBs[i].serial = 0;
29    }
30
31    mLastSetVDecl = NULL;
32    mInstancingEnabled = true;
33}
34
35VertexDeclarationCache::~VertexDeclarationCache()
36{
37    for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
38    {
39        if (mVertexDeclCache[i].vertexDeclaration)
40        {
41            mVertexDeclCache[i].vertexDeclaration->Release();
42        }
43    }
44}
45
46GLenum VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device, TranslatedAttribute attributes[], gl::ProgramBinary *programBinary, GLsizei instances, GLsizei *repeatDraw)
47{
48    *repeatDraw = 1;
49
50    int indexedAttribute = gl::MAX_VERTEX_ATTRIBS;
51    int instancedAttribute = gl::MAX_VERTEX_ATTRIBS;
52
53    if (instances > 0)
54    {
55        // Find an indexed attribute to be mapped to D3D stream 0
56        for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
57        {
58            if (attributes[i].active)
59            {
60                if (indexedAttribute == gl::MAX_VERTEX_ATTRIBS && attributes[i].divisor == 0)
61                {
62                    indexedAttribute = i;
63                }
64                else if (instancedAttribute == gl::MAX_VERTEX_ATTRIBS && attributes[i].divisor != 0)
65                {
66                    instancedAttribute = i;
67                }
68                if (indexedAttribute != gl::MAX_VERTEX_ATTRIBS && instancedAttribute != gl::MAX_VERTEX_ATTRIBS)
69                    break;   // Found both an indexed and instanced attribute
70            }
71        }
72
73        if (indexedAttribute == gl::MAX_VERTEX_ATTRIBS)
74        {
75            return GL_INVALID_OPERATION;
76        }
77    }
78
79    D3DVERTEXELEMENT9 elements[gl::MAX_VERTEX_ATTRIBS + 1];
80    D3DVERTEXELEMENT9 *element = &elements[0];
81
82    for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
83    {
84        if (attributes[i].active)
85        {
86            // Directly binding the storage buffer is not supported for d3d9
87            ASSERT(attributes[i].storage == NULL);
88
89            int stream = i;
90
91            if (instances > 0)
92            {
93                // Due to a bug on ATI cards we can't enable instancing when none of the attributes are instanced.
94                if (instancedAttribute == gl::MAX_VERTEX_ATTRIBS)
95                {
96                    *repeatDraw = instances;
97                }
98                else
99                {
100                    if (i == indexedAttribute)
101                    {
102                        stream = 0;
103                    }
104                    else if (i == 0)
105                    {
106                        stream = indexedAttribute;
107                    }
108
109                    UINT frequency = 1;
110
111                    if (attributes[i].divisor == 0)
112                    {
113                        frequency = D3DSTREAMSOURCE_INDEXEDDATA | instances;
114                    }
115                    else
116                    {
117                        frequency = D3DSTREAMSOURCE_INSTANCEDATA | attributes[i].divisor;
118                    }
119
120                    device->SetStreamSourceFreq(stream, frequency);
121                    mInstancingEnabled = true;
122                }
123            }
124
125            VertexBuffer9 *vertexBuffer = VertexBuffer9::makeVertexBuffer9(attributes[i].vertexBuffer);
126
127            if (mAppliedVBs[stream].serial != attributes[i].serial ||
128                mAppliedVBs[stream].stride != attributes[i].stride ||
129                mAppliedVBs[stream].offset != attributes[i].offset)
130            {
131                device->SetStreamSource(stream, vertexBuffer->getBuffer(), attributes[i].offset, attributes[i].stride);
132                mAppliedVBs[stream].serial = attributes[i].serial;
133                mAppliedVBs[stream].stride = attributes[i].stride;
134                mAppliedVBs[stream].offset = attributes[i].offset;
135            }
136
137            element->Stream = stream;
138            element->Offset = 0;
139            element->Type = attributes[i].attribute->mArrayEnabled ? vertexBuffer->getDeclType(*attributes[i].attribute) : D3DDECLTYPE_FLOAT4;
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        lastCache->vertexDeclaration->Release();
192        lastCache->vertexDeclaration = NULL;
193        // mLastSetVDecl is set to the replacement, so we don't have to worry
194        // about it.
195    }
196
197    memcpy(lastCache->cachedElements, elements, (element - elements) * sizeof(D3DVERTEXELEMENT9));
198    device->CreateVertexDeclaration(elements, &lastCache->vertexDeclaration);
199    device->SetVertexDeclaration(lastCache->vertexDeclaration);
200    mLastSetVDecl = lastCache->vertexDeclaration;
201    lastCache->lruCount = ++mMaxLru;
202
203    return GL_NO_ERROR;
204}
205
206void VertexDeclarationCache::markStateDirty()
207{
208    for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
209    {
210        mAppliedVBs[i].serial = 0;
211    }
212
213    mLastSetVDecl = NULL;
214    mInstancingEnabled = true;   // Forces it to be disabled when not used
215}
216
217}
218