1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_SortingMaterialIdGenerator.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 22799 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/gfx/gfx_SortingMaterialIdGenerator.h>
19 #include <nw/gfx/gfx_Material.h>
20 
21 namespace nw
22 {
23 namespace gfx
24 {
25 
26 NW_UT_RUNTIME_TYPEINFO_ROOT_DEFINITION(SortingMaterialIdGenerator);
27 
28 //----------------------------------------
29 IMaterialIdGenerator*
Create(os::IAllocator * allocator)30 SortingMaterialIdGenerator::Builder::Create(
31     os::IAllocator* allocator
32 )
33 {
34     NW_NULL_ASSERT(allocator);
35 
36     void* memory = allocator->Alloc(sizeof(SortingMaterialIdGenerator));
37     NW_NULL_ASSERT(memory);
38 
39     MaterialKeyValueArray materials;
40     MaterialKeyValueArray materialsWorkSpace;
41     if (m_Description.isFixedSizeMemory)
42     {
43         materials = MaterialKeyValueArray(m_Description.maxMaterials, allocator);
44         materialsWorkSpace = MaterialKeyValueArray(m_Description.maxMaterials, allocator);
45     }
46     else
47     {
48         materials = MaterialKeyValueArray(allocator);
49         materialsWorkSpace = MaterialKeyValueArray(allocator);
50     }
51 
52     SortingMaterialIdGenerator* generator =
53         new(memory) SortingMaterialIdGenerator(
54         allocator,
55         materials,
56         materialsWorkSpace);
57 
58     return generator;
59 }
60 
61 
62 //---------------------------------------------------------------------------
63 //! @brief        ソート比較用関数オブジェクトです。
64 //---------------------------------------------------------------------------
65 
66 struct MaterialKeyValueCompare
67     : public std::binary_function<internal::MaterialKeyValue, internal::MaterialKeyValue, bool>
68 {
operator ()nw::gfx::MaterialKeyValueCompare69     bool operator() (const internal::MaterialKeyValue& lhs,
70         const internal::MaterialKeyValue& rhs)
71     {
72         return lhs.key < rhs.key;
73     }
74 };
75 
76 struct MaterialSubKeyValueCompare
77     : public std::binary_function<internal::MaterialKeyValue, internal::MaterialKeyValue, bool>
78 {
operator ()nw::gfx::MaterialSubKeyValueCompare79     bool operator() (const internal::MaterialKeyValue& lhs,
80         const internal::MaterialKeyValue& rhs)
81     {
82         return lhs.subKey < rhs.subKey;
83     }
84 };
85 
86 struct MaterialUniqueIdCompare
87     : public std::unary_function<internal::MaterialKeyValue, bool>
88 {
operator ()nw::gfx::MaterialUniqueIdCompare89     bool operator() (const internal::MaterialKeyValue& keyValue, const u32 uniqueId)
90     {
91         return keyValue.uniqueId < uniqueId;
92     }
93 
operator ()nw::gfx::MaterialUniqueIdCompare94     bool operator() (const u32 uniqueId, const internal::MaterialKeyValue& keyValue)
95     {
96         return keyValue.uniqueId > uniqueId;
97     }
98 };
99 
100 //----------------------------------------
101 void
Accept(Material * material)102 SortingMaterialIdGenerator::Accept(Material* material)
103 {
104     internal::MaterialKeyValue materialKeyValue;
105     materialKeyValue.key = reinterpret_cast<u32>(material->GetOriginal().ptr());
106     materialKeyValue.material = material;
107 
108     bool isPushed = m_Materials.push_back(materialKeyValue);
109     NW_ASSERT(isPushed);
110 }
111 
112 //----------------------------------------
113 void
Generate()114 SortingMaterialIdGenerator::Generate()
115 {
116     // ResMaterialData を基にソートします。
117     std::sort( m_Materials.begin(), m_Materials.end(),
118         nw::gfx::MaterialKeyValueCompare() );
119 
120     u32 lastPtrKey = 0x0;
121     u32 uniquePtrId = 0;
122     // ResMaterialData が同一のノードを統合しつつ ResBinaryShaderData を基にキーを作成します。
123     MaterialKeyValueArray::iterator materialPtrEnd = m_Materials.end();
124     for (MaterialKeyValueArray::iterator iter = m_Materials.begin();
125         iter != materialPtrEnd;
126         ++iter)
127     {
128         if (lastPtrKey != (*iter).key)
129         {
130             ++uniquePtrId;
131 
132             ResMaterial resMaterial = (*iter).material->GetOriginal();
133             NW_ASSERT(resMaterial.IsValid());
134             ResShaderProgramDescription resDescription = (*iter).material->GetDescription();
135             NW_ASSERT(resDescription.IsValid());
136             u32 key = reinterpret_cast<u32>(resDescription.GetOwnerShaderData());
137 
138             internal::MaterialKeyValue materialKeyValue;
139             materialKeyValue.uniqueId = uniquePtrId;
140             materialKeyValue.key = key;
141             materialKeyValue.subKey = resMaterial.GetFragmentLightingTableHash();
142             materialKeyValue.material = (*iter).material;
143             m_MaterialsWorkSpace.push_back(materialKeyValue);
144             lastPtrKey = (*iter).key;
145         }
146         (*iter).uniqueId = uniquePtrId;
147     }
148 
149     // ResBinaryShaderData を基にソートします。
150     std::sort( m_MaterialsWorkSpace.begin(), m_MaterialsWorkSpace.end(),
151         nw::gfx::MaterialKeyValueCompare() );
152 
153     // ResBinaryShaderData が同一であるマテリアル列の内部を ResFragmentLightingTable の Hash 値を基にソートします。
154     u32 sortStartIndex = 0;
155     u32 sortEndIndex = 0;
156     u32 lastShaderKey = 0;
157     u32 endIndex = m_MaterialsWorkSpace.size() - 1;
158     for (int index = 0; index < endIndex + 1; ++index)
159     {
160         if (lastShaderKey != m_MaterialsWorkSpace[index].key ||
161             index == endIndex)
162         {
163             sortEndIndex = index;
164             int diff = (sortEndIndex - sortStartIndex);
165             // 同一キーが3ノード以上連続した場合にソートを行います。
166             if (diff >= 3)
167             {
168                 std::sort(m_MaterialsWorkSpace.begin() + sortStartIndex,
169                     m_MaterialsWorkSpace.begin() + sortEndIndex,
170                     nw::gfx::MaterialSubKeyValueCompare() );
171             }
172             sortStartIndex = index;
173         }
174 
175         lastShaderKey = m_MaterialsWorkSpace[index].key;
176     }
177 
178     // 統合したノードを展開しながら MaterialId を 1 から順に付けます。
179     u32 materialId = 1;
180     MaterialKeyValueArray::iterator shaderEnd = m_MaterialsWorkSpace.end();
181     for (MaterialKeyValueArray::iterator shaderIter = m_MaterialsWorkSpace.begin();
182         shaderIter != shaderEnd;
183         ++shaderIter)
184     {
185         ::std::pair<MaterialKeyValueArray::iterator, MaterialKeyValueArray::iterator> range =
186             equal_range(m_Materials.begin(), m_Materials.end(), (*shaderIter).uniqueId,
187             MaterialUniqueIdCompare());
188 
189         for (MaterialKeyValueArray::iterator ptrIter = range.first;
190             ptrIter != range.second;
191             ++ptrIter)
192         {
193             ResMaterial resMaterial = (*ptrIter).material->GetOriginal();
194             resMaterial.SetMaterialId( materialId );
195             ++materialId;
196         }
197     }
198 
199     // 生成後はマテリアルのリストを解放します。
200     m_Materials.clear();
201     m_MaterialsWorkSpace.clear();
202 }
203 
204 } // namespace gfx
205 } // namespace nw
206