1 // Copyright 2016 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #pragma once
6 
7 #include <array>
8 #include <boost/serialization/array.hpp>
9 #include <boost/serialization/split_member.hpp>
10 #include "common/bit_field.h"
11 #include "common/common_types.h"
12 #include "common/vector_math.h"
13 #include "core/memory.h"
14 #include "video_core/geometry_pipeline.h"
15 #include "video_core/primitive_assembly.h"
16 #include "video_core/regs.h"
17 #include "video_core/shader/shader.h"
18 #include "video_core/video_core.h"
19 
20 // Boost::serialization doesn't like union types for some reason,
21 // so we need to mark arrays of union values with a special serialization method
22 template <typename Value, size_t Size>
23 struct UnionArray : public std::array<Value, Size> {
24 private:
25     template <class Archive>
serializeUnionArray26     void serialize(Archive& ar, const unsigned int) {
27         static_assert(sizeof(Value) == sizeof(u32));
28         ar&* static_cast<u32(*)[Size]>(static_cast<void*>(this->data()));
29     }
30     friend class boost::serialization::access;
31 };
32 
33 namespace Pica {
34 
35 /// Struct used to describe current Pica state
36 struct State {
37     State();
38     void Reset();
39 
40     /// Pica registers
41     Regs regs;
42 
43     Shader::ShaderSetup vs;
44     Shader::ShaderSetup gs;
45 
46     Shader::AttributeBuffer input_default_attributes;
47 
48     struct ProcTex {
49         union ValueEntry {
50             u32 raw;
51 
52             // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
53             BitField<0, 12, u32> value; // 0.0.12 fixed point
54 
55             // Difference between two entry values. Used for efficient interpolation.
56             // 0.0.12 fixed point with two's complement. The range is [-0.5, 0.5).
57             // Note: the type of this is different from the one of lighting LUT
58             BitField<12, 12, s32> difference;
59 
ToFloat()60             float ToFloat() const {
61                 return static_cast<float>(value) / 4095.f;
62             }
63 
DiffToFloat()64             float DiffToFloat() const {
65                 return static_cast<float>(difference) / 4095.f;
66             }
67         };
68 
69         union ColorEntry {
70             u32 raw;
71             BitField<0, 8, u32> r;
72             BitField<8, 8, u32> g;
73             BitField<16, 8, u32> b;
74             BitField<24, 8, u32> a;
75 
ToVector()76             Common::Vec4<u8> ToVector() const {
77                 return {static_cast<u8>(r), static_cast<u8>(g), static_cast<u8>(b),
78                         static_cast<u8>(a)};
79             }
80         };
81 
82         union ColorDifferenceEntry {
83             u32 raw;
84             BitField<0, 8, s32> r; // half of the difference between two ColorEntry
85             BitField<8, 8, s32> g;
86             BitField<16, 8, s32> b;
87             BitField<24, 8, s32> a;
88 
ToVector()89             Common::Vec4<s32> ToVector() const {
90                 return Common::Vec4<s32>{r, g, b, a} * 2;
91             }
92         };
93 
94         UnionArray<ValueEntry, 128> noise_table;
95         UnionArray<ValueEntry, 128> color_map_table;
96         UnionArray<ValueEntry, 128> alpha_map_table;
97         UnionArray<ColorEntry, 256> color_table;
98         UnionArray<ColorDifferenceEntry, 256> color_diff_table;
99 
100     private:
101         friend class boost::serialization::access;
102         template <class Archive>
serializeState::ProcTex103         void serialize(Archive& ar, const unsigned int file_version) {
104             ar& noise_table;
105             ar& color_map_table;
106             ar& alpha_map_table;
107             ar& color_table;
108             ar& color_diff_table;
109         }
110     } proctex;
111 
112     struct Lighting {
113         union LutEntry {
114             // Used for raw access
115             u32 raw;
116 
117             // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
118             BitField<0, 12, u32> value; // 0.0.12 fixed point
119 
120             // Used for efficient interpolation.
121             BitField<12, 11, u32> difference; // 0.0.11 fixed point
122             BitField<23, 1, u32> neg_difference;
123 
ToFloat()124             float ToFloat() const {
125                 return static_cast<float>(value) / 4095.f;
126             }
127 
DiffToFloat()128             float DiffToFloat() const {
129                 float diff = static_cast<float>(difference) / 2047.f;
130                 return neg_difference ? -diff : diff;
131             }
132 
133             template <class Archive>
serialize(Archive & ar,const unsigned int file_version)134             void serialize(Archive& ar, const unsigned int file_version) {
135                 ar& raw;
136             }
137         };
138 
139         std::array<UnionArray<LutEntry, 256>, 24> luts;
140     } lighting;
141 
142     struct {
143         union LutEntry {
144             // Used for raw access
145             u32 raw;
146 
147             BitField<0, 13, s32> difference; // 1.1.11 fixed point
148             BitField<13, 11, u32> value;     // 0.0.11 fixed point
149 
ToFloat()150             float ToFloat() const {
151                 return static_cast<float>(value) / 2047.0f;
152             }
153 
DiffToFloat()154             float DiffToFloat() const {
155                 return static_cast<float>(difference) / 2047.0f;
156             }
157         };
158 
159         UnionArray<LutEntry, 128> lut;
160     } fog;
161 
162     /// Current Pica command list
163     struct {
164         PAddr addr; // This exists only for serialization
165         const u32* head_ptr;
166         const u32* current_ptr;
167         u32 length;
168     } cmd_list;
169 
170     /// Struct used to describe immediate mode rendering state
171     struct ImmediateModeState {
172         // Used to buffer partial vertices for immediate-mode rendering.
173         Shader::AttributeBuffer input_vertex;
174         // Index of the next attribute to be loaded into `input_vertex`.
175         u32 current_attribute = 0;
176         // Indicates the immediate mode just started and the geometry pipeline needs to reconfigure
177         bool reset_geometry_pipeline = true;
178 
179     private:
180         friend class boost::serialization::access;
181         template <class Archive>
serializeState::ImmediateModeState182         void serialize(Archive& ar, const unsigned int file_version) {
183             ar& input_vertex;
184             ar& current_attribute;
185             ar& reset_geometry_pipeline;
186         }
187 
188     } immediate;
189 
190     // the geometry shader needs to be kept in the global state because some shaders relie on
191     // preserved register value across shader invocation.
192     // TODO: also bring the three vertex shader units here and implement the shader scheduler.
193     Shader::GSUnitState gs_unit;
194 
195     GeometryPipeline geometry_pipeline;
196 
197     // This is constructed with a dummy triangle topology
198     PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
199 
200     int vs_float_regs_counter = 0;
201     std::array<u32, 4> vs_uniform_write_buffer{};
202 
203     int gs_float_regs_counter = 0;
204     std::array<u32, 4> gs_uniform_write_buffer{};
205 
206     int default_attr_counter = 0;
207     std::array<u32, 3> default_attr_write_buffer{};
208 
209 private:
210     friend class boost::serialization::access;
211     template <class Archive>
serializeState212     void serialize(Archive& ar, const unsigned int file_version) {
213         ar& regs.reg_array;
214         ar& vs;
215         ar& gs;
216         ar& input_default_attributes;
217         ar& proctex;
218         ar& lighting.luts;
219         ar& fog.lut;
220         ar& cmd_list.addr;
221         ar& cmd_list.length;
222         ar& immediate;
223         ar& gs_unit;
224         ar& geometry_pipeline;
225         ar& primitive_assembler;
226         ar& vs_float_regs_counter;
227         ar& boost::serialization::make_array(vs_uniform_write_buffer.data(),
228                                              vs_uniform_write_buffer.size());
229         ar& gs_float_regs_counter;
230         ar& boost::serialization::make_array(gs_uniform_write_buffer.data(),
231                                              gs_uniform_write_buffer.size());
232         ar& default_attr_counter;
233         ar& boost::serialization::make_array(default_attr_write_buffer.data(),
234                                              default_attr_write_buffer.size());
235         boost::serialization::split_member(ar, *this, file_version);
236     }
237 
238     template <class Archive>
saveState239     void save(Archive& ar, const unsigned int file_version) const {
240         ar << static_cast<u32>(cmd_list.current_ptr - cmd_list.head_ptr);
241     }
242 
243     template <class Archive>
loadState244     void load(Archive& ar, const unsigned int file_version) {
245         u32 offset{};
246         ar >> offset;
247         cmd_list.head_ptr =
248             reinterpret_cast<u32*>(VideoCore::g_memory->GetPhysicalPointer(cmd_list.addr));
249         cmd_list.current_ptr = cmd_list.head_ptr + offset;
250     }
251 };
252 
253 extern State g_state; ///< Current Pica state
254 
255 } // namespace Pica
256