Home | History | Annotate | Download | only in d3d9
      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