VertexDeclarationCache.cpp (8621B)
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 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 (VBData &vb : mAppliedVBs) 31 { 32 vb.serial = 0; 33 } 34 35 mLastSetVDecl = nullptr; 36 mInstancingEnabled = true; 37 } 38 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 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 251 void VertexDeclarationCache::markStateDirty() 252 { 253 for (VBData &vb : mAppliedVBs) 254 { 255 vb.serial = 0; 256 } 257 258 mLastSetVDecl = nullptr; 259 mInstancingEnabled = true; // Forces it to be disabled when not used 260 } 261 262 } // namespace rx