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