blob: faa9202689f0ef52da82312c24f5fda4889c368f [file] [log] [blame]
//
// Copyright 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// VertexDeclarationCache.cpp: Implements a helper class to construct and cache vertex declarations.
#include "libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.h"
#include "libANGLE/Context.h"
#include "libANGLE/VertexAttribute.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/d3d/ProgramD3D.h"
#include "libANGLE/renderer/d3d/d3d9/Context9.h"
#include "libANGLE/renderer/d3d/d3d9/VertexBuffer9.h"
#include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
namespace rx
{
VertexDeclarationCache::VertexDeclarationCache() : mMaxLru(0)
{
for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
{
mVertexDeclCache[i].vertexDeclaration = nullptr;
mVertexDeclCache[i].lruCount = 0;
}
for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
{
mAppliedVBs[i].serial = 0;
}
mLastSetVDecl = nullptr;
mInstancingEnabled = true;
}
VertexDeclarationCache::~VertexDeclarationCache()
{
for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
{
SafeRelease(mVertexDeclCache[i].vertexDeclaration);
}
}
angle::Result VertexDeclarationCache::applyDeclaration(
const gl::Context *context,
IDirect3DDevice9 *device,
const std::vector<TranslatedAttribute> &attributes,
gl::Program *program,
GLint start,
GLsizei instances,
GLsizei *repeatDraw)
{
ASSERT(gl::MAX_VERTEX_ATTRIBS >= attributes.size());
*repeatDraw = 1;
const size_t invalidAttribIndex = attributes.size();
size_t indexedAttribute = invalidAttribIndex;
size_t instancedAttribute = invalidAttribIndex;
if (instances == 0)
{
for (size_t i = 0; i < attributes.size(); ++i)
{
if (attributes[i].divisor != 0)
{
// If a divisor is set, it still applies even if an instanced draw was not used, so
// treat as a single-instance draw.
instances = 1;
break;
}
}
}
if (instances > 0)
{
// Find an indexed attribute to be mapped to D3D stream 0
for (size_t i = 0; i < attributes.size(); i++)
{
if (attributes[i].active)
{
if (indexedAttribute == invalidAttribIndex && attributes[i].divisor == 0)
{
indexedAttribute = i;
}
else if (instancedAttribute == invalidAttribIndex && attributes[i].divisor != 0)
{
instancedAttribute = i;
}
if (indexedAttribute != invalidAttribIndex &&
instancedAttribute != invalidAttribIndex)
break; // Found both an indexed and instanced attribute
}
}
// The validation layer checks that there is at least one active attribute with a zero
// divisor as per the GL_ANGLE_instanced_arrays spec.
ASSERT(indexedAttribute != invalidAttribIndex);
}
D3DCAPS9 caps;
device->GetDeviceCaps(&caps);
D3DVERTEXELEMENT9 elements[gl::MAX_VERTEX_ATTRIBS + 1];
D3DVERTEXELEMENT9 *element = &elements[0];
ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
const auto &semanticIndexes = programD3D->getAttribLocationToD3DSemantics();
for (size_t i = 0; i < attributes.size(); i++)
{
if (attributes[i].active)
{
// Directly binding the storage buffer is not supported for d3d9
ASSERT(attributes[i].storage == nullptr);
int stream = static_cast<int>(i);
if (instances > 0)
{
// Due to a bug on ATI cards we can't enable instancing when none of the attributes
// are instanced.
if (instancedAttribute == invalidAttribIndex)
{
*repeatDraw = instances;
}
else
{
if (i == indexedAttribute)
{
stream = 0;
}
else if (i == 0)
{
stream = static_cast<int>(indexedAttribute);
}
UINT frequency = 1;
if (attributes[i].divisor == 0)
{
frequency = D3DSTREAMSOURCE_INDEXEDDATA | instances;
}
else
{
frequency = D3DSTREAMSOURCE_INSTANCEDATA | attributes[i].divisor;
}
device->SetStreamSourceFreq(stream, frequency);
mInstancingEnabled = true;
}
}
VertexBuffer9 *vertexBuffer = GetAs<VertexBuffer9>(attributes[i].vertexBuffer.get());
unsigned int offset = 0;
ANGLE_TRY(attributes[i].computeOffset(context, start, &offset));
if (mAppliedVBs[stream].serial != attributes[i].serial ||
mAppliedVBs[stream].stride != attributes[i].stride ||
mAppliedVBs[stream].offset != offset)
{
device->SetStreamSource(stream, vertexBuffer->getBuffer(), offset,
attributes[i].stride);
mAppliedVBs[stream].serial = attributes[i].serial;
mAppliedVBs[stream].stride = attributes[i].stride;
mAppliedVBs[stream].offset = offset;
}
angle::FormatID vertexformatID =
gl::GetVertexFormatID(*attributes[i].attribute, gl::VertexAttribType::Float);
const d3d9::VertexFormat &d3d9VertexInfo =
d3d9::GetVertexFormatInfo(caps.DeclTypes, vertexformatID);
element->Stream = static_cast<WORD>(stream);
element->Offset = 0;
element->Type = static_cast<BYTE>(d3d9VertexInfo.nativeFormat);
element->Method = D3DDECLMETHOD_DEFAULT;
element->Usage = D3DDECLUSAGE_TEXCOORD;
element->UsageIndex = static_cast<BYTE>(semanticIndexes[i]);
element++;
}
}
if (instances == 0 || instancedAttribute == invalidAttribIndex)
{
if (mInstancingEnabled)
{
for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
{
device->SetStreamSourceFreq(i, 1);
}
mInstancingEnabled = false;
}
}
static const D3DVERTEXELEMENT9 end = D3DDECL_END();
*(element++) = end;
for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
{
VertexDeclCacheEntry *entry = &mVertexDeclCache[i];
if (memcmp(entry->cachedElements, elements,
(element - elements) * sizeof(D3DVERTEXELEMENT9)) == 0 &&
entry->vertexDeclaration)
{
entry->lruCount = ++mMaxLru;
if (entry->vertexDeclaration != mLastSetVDecl)
{
device->SetVertexDeclaration(entry->vertexDeclaration);
mLastSetVDecl = entry->vertexDeclaration;
}
return angle::Result::Continue;
}
}
VertexDeclCacheEntry *lastCache = mVertexDeclCache;
for (int i = 0; i < NUM_VERTEX_DECL_CACHE_ENTRIES; i++)
{
if (mVertexDeclCache[i].lruCount < lastCache->lruCount)
{
lastCache = &mVertexDeclCache[i];
}
}
if (lastCache->vertexDeclaration != nullptr)
{
SafeRelease(lastCache->vertexDeclaration);
// mLastSetVDecl is set to the replacement, so we don't have to worry
// about it.
}
memcpy(lastCache->cachedElements, elements, (element - elements) * sizeof(D3DVERTEXELEMENT9));
HRESULT result = device->CreateVertexDeclaration(elements, &lastCache->vertexDeclaration);
ANGLE_TRY_HR(GetImplAs<Context9>(context), result,
"Failed to create internal vertex declaration");
device->SetVertexDeclaration(lastCache->vertexDeclaration);
mLastSetVDecl = lastCache->vertexDeclaration;
lastCache->lruCount = ++mMaxLru;
return angle::Result::Continue;
}
void VertexDeclarationCache::markStateDirty()
{
for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++)
{
mAppliedVBs[i].serial = 0;
}
mLastSetVDecl = nullptr;
mInstancingEnabled = true; // Forces it to be disabled when not used
}
} // namespace rx