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 // InputLayoutCache.cpp: Defines InputLayoutCache, a class that builds and caches
9 // D3D11 input layouts.
10
11 #include "libGLESv2/renderer/InputLayoutCache.h"
12 #include "libGLESv2/renderer/VertexBuffer11.h"
13 #include "libGLESv2/renderer/BufferStorage11.h"
14 #include "libGLESv2/renderer/ShaderExecutable11.h"
15 #include "libGLESv2/ProgramBinary.h"
16 #include "libGLESv2/Context.h"
17 #include "libGLESv2/renderer/VertexDataManager.h"
18
19 #include "third_party/murmurhash/MurmurHash3.h"
20
21 namespace rx
22 {
23
24 const unsigned int InputLayoutCache::kMaxInputLayouts = 1024;
25
InputLayoutCache()26 InputLayoutCache::InputLayoutCache() : mInputLayoutMap(kMaxInputLayouts, hashInputLayout, compareInputLayouts)
27 {
28 mCounter = 0;
29 mDevice = NULL;
30 mDeviceContext = NULL;
31 mCurrentIL = NULL;
32 for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
33 {
34 mCurrentBuffers[i] = -1;
35 mCurrentVertexStrides[i] = -1;
36 mCurrentVertexOffsets[i] = -1;
37 }
38 }
39
~InputLayoutCache()40 InputLayoutCache::~InputLayoutCache()
41 {
42 clear();
43 }
44
initialize(ID3D11Device * device,ID3D11DeviceContext * context)45 void InputLayoutCache::initialize(ID3D11Device *device, ID3D11DeviceContext *context)
46 {
47 clear();
48 mDevice = device;
49 mDeviceContext = context;
50 }
51
clear()52 void InputLayoutCache::clear()
53 {
54 for (InputLayoutMap::iterator i = mInputLayoutMap.begin(); i != mInputLayoutMap.end(); i++)
55 {
56 i->second.inputLayout->Release();
57 }
58 mInputLayoutMap.clear();
59 markDirty();
60 }
61
markDirty()62 void InputLayoutCache::markDirty()
63 {
64 mCurrentIL = NULL;
65 for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
66 {
67 mCurrentBuffers[i] = -1;
68 mCurrentVertexStrides[i] = -1;
69 mCurrentVertexOffsets[i] = -1;
70 }
71 }
72
applyVertexBuffers(TranslatedAttribute attributes[gl::MAX_VERTEX_ATTRIBS],gl::ProgramBinary * programBinary)73 GLenum InputLayoutCache::applyVertexBuffers(TranslatedAttribute attributes[gl::MAX_VERTEX_ATTRIBS],
74 gl::ProgramBinary *programBinary)
75 {
76 int sortedSemanticIndices[gl::MAX_VERTEX_ATTRIBS];
77 programBinary->sortAttributesByLayout(attributes, sortedSemanticIndices);
78
79 if (!mDevice || !mDeviceContext)
80 {
81 ERR("InputLayoutCache is not initialized.");
82 return GL_INVALID_OPERATION;
83 }
84
85 InputLayoutKey ilKey = { 0 };
86
87 ID3D11Buffer *vertexBuffers[gl::MAX_VERTEX_ATTRIBS] = { NULL };
88 unsigned int vertexBufferSerials[gl::MAX_VERTEX_ATTRIBS] = { 0 };
89 UINT vertexStrides[gl::MAX_VERTEX_ATTRIBS] = { 0 };
90 UINT vertexOffsets[gl::MAX_VERTEX_ATTRIBS] = { 0 };
91
92 static const char* semanticName = "TEXCOORD";
93
94 for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
95 {
96 if (attributes[i].active)
97 {
98 VertexBuffer11 *vertexBuffer = VertexBuffer11::makeVertexBuffer11(attributes[i].vertexBuffer);
99 BufferStorage11 *bufferStorage = attributes[i].storage ? BufferStorage11::makeBufferStorage11(attributes[i].storage) : NULL;
100
101 D3D11_INPUT_CLASSIFICATION inputClass = attributes[i].divisor > 0 ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
102
103 // Record the type of the associated vertex shader vector in our key
104 // This will prevent mismatched vertex shaders from using the same input layout
105 GLint attributeSize;
106 programBinary->getActiveAttribute(ilKey.elementCount, 0, NULL, &attributeSize, &ilKey.elements[ilKey.elementCount].glslElementType, NULL);
107
108 ilKey.elements[ilKey.elementCount].desc.SemanticName = semanticName;
109 ilKey.elements[ilKey.elementCount].desc.SemanticIndex = sortedSemanticIndices[i];
110 ilKey.elements[ilKey.elementCount].desc.Format = attributes[i].attribute->mArrayEnabled ? vertexBuffer->getDXGIFormat(*attributes[i].attribute) : DXGI_FORMAT_R32G32B32A32_FLOAT;
111 ilKey.elements[ilKey.elementCount].desc.InputSlot = i;
112 ilKey.elements[ilKey.elementCount].desc.AlignedByteOffset = 0;
113 ilKey.elements[ilKey.elementCount].desc.InputSlotClass = inputClass;
114 ilKey.elements[ilKey.elementCount].desc.InstanceDataStepRate = attributes[i].divisor;
115 ilKey.elementCount++;
116
117 vertexBuffers[i] = bufferStorage ? bufferStorage->getBuffer() : vertexBuffer->getBuffer();
118 vertexBufferSerials[i] = bufferStorage ? bufferStorage->getSerial() : vertexBuffer->getSerial();
119 vertexStrides[i] = attributes[i].stride;
120 vertexOffsets[i] = attributes[i].offset;
121 }
122 }
123
124 ID3D11InputLayout *inputLayout = NULL;
125
126 InputLayoutMap::iterator i = mInputLayoutMap.find(ilKey);
127 if (i != mInputLayoutMap.end())
128 {
129 inputLayout = i->second.inputLayout;
130 i->second.lastUsedTime = mCounter++;
131 }
132 else
133 {
134 ShaderExecutable11 *shader = ShaderExecutable11::makeShaderExecutable11(programBinary->getVertexExecutable());
135
136 D3D11_INPUT_ELEMENT_DESC descs[gl::MAX_VERTEX_ATTRIBS];
137 for (unsigned int j = 0; j < ilKey.elementCount; ++j)
138 {
139 descs[j] = ilKey.elements[j].desc;
140 }
141
142 HRESULT result = mDevice->CreateInputLayout(descs, ilKey.elementCount, shader->getFunction(), shader->getLength(), &inputLayout);
143 if (FAILED(result))
144 {
145 ERR("Failed to crate input layout, result: 0x%08x", result);
146 return GL_INVALID_OPERATION;
147 }
148
149 if (mInputLayoutMap.size() >= kMaxInputLayouts)
150 {
151 TRACE("Overflowed the limit of %u input layouts, removing the least recently used "
152 "to make room.", kMaxInputLayouts);
153
154 InputLayoutMap::iterator leastRecentlyUsed = mInputLayoutMap.begin();
155 for (InputLayoutMap::iterator i = mInputLayoutMap.begin(); i != mInputLayoutMap.end(); i++)
156 {
157 if (i->second.lastUsedTime < leastRecentlyUsed->second.lastUsedTime)
158 {
159 leastRecentlyUsed = i;
160 }
161 }
162 leastRecentlyUsed->second.inputLayout->Release();
163 mInputLayoutMap.erase(leastRecentlyUsed);
164 }
165
166 InputLayoutCounterPair inputCounterPair;
167 inputCounterPair.inputLayout = inputLayout;
168 inputCounterPair.lastUsedTime = mCounter++;
169
170 mInputLayoutMap.insert(std::make_pair(ilKey, inputCounterPair));
171 }
172
173 if (inputLayout != mCurrentIL)
174 {
175 mDeviceContext->IASetInputLayout(inputLayout);
176 mCurrentIL = inputLayout;
177 }
178
179 for (unsigned int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
180 {
181 if (vertexBufferSerials[i] != mCurrentBuffers[i] || vertexStrides[i] != mCurrentVertexStrides[i] ||
182 vertexOffsets[i] != mCurrentVertexOffsets[i])
183 {
184 mDeviceContext->IASetVertexBuffers(i, 1, &vertexBuffers[i], &vertexStrides[i], &vertexOffsets[i]);
185 mCurrentBuffers[i] = vertexBufferSerials[i];
186 mCurrentVertexStrides[i] = vertexStrides[i];
187 mCurrentVertexOffsets[i] = vertexOffsets[i];
188 }
189 }
190
191 return GL_NO_ERROR;
192 }
193
hashInputLayout(const InputLayoutKey & inputLayout)194 std::size_t InputLayoutCache::hashInputLayout(const InputLayoutKey &inputLayout)
195 {
196 static const unsigned int seed = 0xDEADBEEF;
197
198 std::size_t hash = 0;
199 MurmurHash3_x86_32(inputLayout.begin(), inputLayout.end() - inputLayout.begin(), seed, &hash);
200 return hash;
201 }
202
compareInputLayouts(const InputLayoutKey & a,const InputLayoutKey & b)203 bool InputLayoutCache::compareInputLayouts(const InputLayoutKey &a, const InputLayoutKey &b)
204 {
205 if (a.elementCount != b.elementCount)
206 {
207 return false;
208 }
209
210 return std::equal(a.begin(), a.end(), b.begin());
211 }
212
213 }
214