1 // 2 // Copyright (c) 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 "libGLESv2/renderer/d3d/d3d9/VertexDeclarationCache.h" 10 #include "libGLESv2/renderer/d3d/d3d9/VertexBuffer9.h" 11 #include "libGLESv2/renderer/d3d/d3d9/formatutils9.h" 12 #include "libGLESv2/ProgramBinary.h" 13 #include "libGLESv2/VertexAttribute.h" 14 15 namespace rx 16 { 17 18 VertexDeclarationCache::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 35 VertexDeclarationCache::~VertexDeclarationCache() 36 { 37 for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++) 38 { 39 SafeRelease(mVertexDeclCache[i].vertexDeclaration); 40 } 41 } 42 43 gl::Error VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device, TranslatedAttribute attributes[], gl::ProgramBinary *programBinary, GLsizei instances, GLsizei *repeatDraw) 44 { 45 *repeatDraw = 1; 46 47 int indexedAttribute = gl::MAX_VERTEX_ATTRIBS; 48 int instancedAttribute = gl::MAX_VERTEX_ATTRIBS; 49 50 if (instances == 0) 51 { 52 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; ++i) 53 { 54 if (attributes[i].divisor != 0) 55 { 56 // If a divisor is set, it still applies even if an instanced draw was not used, so treat 57 // as a single-instance draw. 58 instances = 1; 59 break; 60 } 61 } 62 } 63 64 if (instances > 0) 65 { 66 // Find an indexed attribute to be mapped to D3D stream 0 67 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) 68 { 69 if (attributes[i].active) 70 { 71 if (indexedAttribute == gl::MAX_VERTEX_ATTRIBS && attributes[i].divisor == 0) 72 { 73 indexedAttribute = i; 74 } 75 else if (instancedAttribute == gl::MAX_VERTEX_ATTRIBS && attributes[i].divisor != 0) 76 { 77 instancedAttribute = i; 78 } 79 if (indexedAttribute != gl::MAX_VERTEX_ATTRIBS && instancedAttribute != gl::MAX_VERTEX_ATTRIBS) 80 break; // Found both an indexed and instanced attribute 81 } 82 } 83 84 // The validation layer checks that there is at least one active attribute with a zero divisor as per 85 // the GL_ANGLE_instanced_arrays spec. 86 ASSERT(indexedAttribute != gl::MAX_VERTEX_ATTRIBS); 87 } 88 89 D3DCAPS9 caps; 90 device->GetDeviceCaps(&caps); 91 92 D3DVERTEXELEMENT9 elements[gl::MAX_VERTEX_ATTRIBS + 1]; 93 D3DVERTEXELEMENT9 *element = &elements[0]; 94 95 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) 96 { 97 if (attributes[i].active) 98 { 99 // Directly binding the storage buffer is not supported for d3d9 100 ASSERT(attributes[i].storage == NULL); 101 102 int stream = i; 103 104 if (instances > 0) 105 { 106 // Due to a bug on ATI cards we can't enable instancing when none of the attributes are instanced. 107 if (instancedAttribute == gl::MAX_VERTEX_ATTRIBS) 108 { 109 *repeatDraw = instances; 110 } 111 else 112 { 113 if (i == indexedAttribute) 114 { 115 stream = 0; 116 } 117 else if (i == 0) 118 { 119 stream = indexedAttribute; 120 } 121 122 UINT frequency = 1; 123 124 if (attributes[i].divisor == 0) 125 { 126 frequency = D3DSTREAMSOURCE_INDEXEDDATA | instances; 127 } 128 else 129 { 130 frequency = D3DSTREAMSOURCE_INSTANCEDATA | attributes[i].divisor; 131 } 132 133 device->SetStreamSourceFreq(stream, frequency); 134 mInstancingEnabled = true; 135 } 136 } 137 138 VertexBuffer9 *vertexBuffer = VertexBuffer9::makeVertexBuffer9(attributes[i].vertexBuffer); 139 140 if (mAppliedVBs[stream].serial != attributes[i].serial || 141 mAppliedVBs[stream].stride != attributes[i].stride || 142 mAppliedVBs[stream].offset != attributes[i].offset) 143 { 144 device->SetStreamSource(stream, vertexBuffer->getBuffer(), attributes[i].offset, attributes[i].stride); 145 mAppliedVBs[stream].serial = attributes[i].serial; 146 mAppliedVBs[stream].stride = attributes[i].stride; 147 mAppliedVBs[stream].offset = attributes[i].offset; 148 } 149 150 gl::VertexFormat vertexFormat(*attributes[i].attribute, GL_FLOAT); 151 const d3d9::VertexFormat &d3d9VertexInfo = d3d9::GetVertexFormatInfo(caps.DeclTypes, vertexFormat); 152 153 element->Stream = stream; 154 element->Offset = 0; 155 element->Type = d3d9VertexInfo.nativeFormat; 156 element->Method = D3DDECLMETHOD_DEFAULT; 157 element->Usage = D3DDECLUSAGE_TEXCOORD; 158 element->UsageIndex = programBinary->getSemanticIndex(i); 159 element++; 160 } 161 } 162 163 if (instances == 0 || instancedAttribute == gl::MAX_VERTEX_ATTRIBS) 164 { 165 if (mInstancingEnabled) 166 { 167 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) 168 { 169 device->SetStreamSourceFreq(i, 1); 170 } 171 172 mInstancingEnabled = false; 173 } 174 } 175 176 static const D3DVERTEXELEMENT9 end = D3DDECL_END(); 177 *(element++) = end; 178 179 for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++) 180 { 181 VertexDeclCacheEntry *entry = &mVertexDeclCache[i]; 182 if (memcmp(entry->cachedElements, elements, (element - elements) * sizeof(D3DVERTEXELEMENT9)) == 0 && entry->vertexDeclaration) 183 { 184 entry->lruCount = ++mMaxLru; 185 if(entry->vertexDeclaration != mLastSetVDecl) 186 { 187 device->SetVertexDeclaration(entry->vertexDeclaration); 188 mLastSetVDecl = entry->vertexDeclaration; 189 } 190 191 return gl::Error(GL_NO_ERROR); 192 } 193 } 194 195 VertexDeclCacheEntry *lastCache = mVertexDeclCache; 196 197 for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++) 198 { 199 if (mVertexDeclCache[i].lruCount < lastCache->lruCount) 200 { 201 lastCache = &mVertexDeclCache[i]; 202 } 203 } 204 205 if (lastCache->vertexDeclaration != NULL) 206 { 207 SafeRelease(lastCache->vertexDeclaration); 208 // mLastSetVDecl is set to the replacement, so we don't have to worry 209 // about it. 210 } 211 212 memcpy(lastCache->cachedElements, elements, (element - elements) * sizeof(D3DVERTEXELEMENT9)); 213 HRESULT result = device->CreateVertexDeclaration(elements, &lastCache->vertexDeclaration); 214 if (FAILED(result)) 215 { 216 return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal vertex declaration, result: 0x%X.", result); 217 } 218 219 device->SetVertexDeclaration(lastCache->vertexDeclaration); 220 mLastSetVDecl = lastCache->vertexDeclaration; 221 lastCache->lruCount = ++mMaxLru; 222 223 return gl::Error(GL_NO_ERROR); 224 } 225 226 void VertexDeclarationCache::markStateDirty() 227 { 228 for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) 229 { 230 mAppliedVBs[i].serial = 0; 231 } 232 233 mLastSetVDecl = NULL; 234 mInstancingEnabled = true; // Forces it to be disabled when not used 235 } 236 237 } 238