1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 // http://www.nvidia.com/object/General_FAQ.html#t6 !!!!!
6 
7 #include "VideoCommon/PixelEngine.h"
8 
9 #include <mutex>
10 
11 #include "Common/ChunkFile.h"
12 #include "Common/CommonTypes.h"
13 #include "Common/Logging/Log.h"
14 #include "Core/ConfigManager.h"
15 #include "Core/Core.h"
16 #include "Core/CoreTiming.h"
17 #include "Core/HW/MMIO.h"
18 #include "Core/HW/ProcessorInterface.h"
19 #include "VideoCommon/BoundingBox.h"
20 #include "VideoCommon/Fifo.h"
21 #include "VideoCommon/PerfQueryBase.h"
22 #include "VideoCommon/VideoBackendBase.h"
23 
24 namespace PixelEngine
25 {
26 union UPEZConfReg
27 {
28   u16 Hex;
29   struct
30   {
31     u16 ZCompEnable : 1;  // Z Comparator Enable
32     u16 Function : 3;
33     u16 ZUpdEnable : 1;
34     u16 : 11;
35   };
36 };
37 
38 union UPEAlphaConfReg
39 {
40   u16 Hex;
41   struct
42   {
43     u16 BMMath : 1;   // GX_BM_BLEND || GX_BM_SUBSTRACT
44     u16 BMLogic : 1;  // GX_BM_LOGIC
45     u16 Dither : 1;
46     u16 ColorUpdEnable : 1;
47     u16 AlphaUpdEnable : 1;
48     u16 DstFactor : 3;
49     u16 SrcFactor : 3;
50     u16 Substract : 1;  // Additive mode by default
51     u16 BlendOperator : 4;
52   };
53 };
54 
55 union UPEDstAlphaConfReg
56 {
57   u16 Hex;
58   struct
59   {
60     u16 DstAlpha : 8;
61     u16 Enable : 1;
62     u16 : 7;
63   };
64 };
65 
66 union UPEAlphaModeConfReg
67 {
68   u16 Hex;
69   struct
70   {
71     u16 Threshold : 8;
72     u16 CompareMode : 8;
73   };
74 };
75 
76 // fifo Control Register
77 union UPECtrlReg
78 {
79   struct
80   {
81     u16 PETokenEnable : 1;
82     u16 PEFinishEnable : 1;
83     u16 PEToken : 1;   // write only
84     u16 PEFinish : 1;  // write only
85     u16 : 12;
86   };
87   u16 Hex;
UPECtrlReg()88   UPECtrlReg() { Hex = 0; }
UPECtrlReg(u16 _hex)89   UPECtrlReg(u16 _hex) { Hex = _hex; }
90 };
91 
92 // STATE_TO_SAVE
93 static UPEZConfReg m_ZConf;
94 static UPEAlphaConfReg m_AlphaConf;
95 static UPEDstAlphaConfReg m_DstAlphaConf;
96 static UPEAlphaModeConfReg m_AlphaModeConf;
97 static UPEAlphaReadReg m_AlphaRead;
98 static UPECtrlReg m_Control;
99 
100 static std::mutex s_token_finish_mutex;
101 static u16 s_token;
102 static u16 s_token_pending;
103 static bool s_token_interrupt_pending;
104 static bool s_finish_interrupt_pending;
105 static bool s_event_raised;
106 
107 static bool s_signal_token_interrupt;
108 static bool s_signal_finish_interrupt;
109 
110 static CoreTiming::EventType* et_SetTokenFinishOnMainThread;
111 
112 enum
113 {
114   INT_CAUSE_PE_TOKEN = 0x200,   // GP Token
115   INT_CAUSE_PE_FINISH = 0x400,  // GP Finished
116 };
117 
DoState(PointerWrap & p)118 void DoState(PointerWrap& p)
119 {
120   p.Do(m_ZConf);
121   p.Do(m_AlphaConf);
122   p.Do(m_DstAlphaConf);
123   p.Do(m_AlphaModeConf);
124   p.Do(m_AlphaRead);
125   p.DoPOD(m_Control);
126 
127   p.Do(s_token);
128   p.Do(s_token_pending);
129   p.Do(s_token_interrupt_pending);
130   p.Do(s_finish_interrupt_pending);
131   p.Do(s_event_raised);
132 
133   p.Do(s_signal_token_interrupt);
134   p.Do(s_signal_finish_interrupt);
135 }
136 
137 static void UpdateInterrupts();
138 static void SetTokenFinish_OnMainThread(u64 userdata, s64 cyclesLate);
139 
Init()140 void Init()
141 {
142   m_Control.Hex = 0;
143   m_ZConf.Hex = 0;
144   m_AlphaConf.Hex = 0;
145   m_DstAlphaConf.Hex = 0;
146   m_AlphaModeConf.Hex = 0;
147   m_AlphaRead.Hex = 0;
148 
149   s_token = 0;
150   s_token_pending = 0;
151   s_token_interrupt_pending = false;
152   s_finish_interrupt_pending = false;
153   s_event_raised = false;
154 
155   s_signal_token_interrupt = false;
156   s_signal_finish_interrupt = false;
157 
158   et_SetTokenFinishOnMainThread =
159       CoreTiming::RegisterEvent("SetTokenFinish", SetTokenFinish_OnMainThread);
160 }
161 
RegisterMMIO(MMIO::Mapping * mmio,u32 base)162 void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
163 {
164   // Directly mapped registers.
165   struct
166   {
167     u32 addr;
168     u16* ptr;
169   } directly_mapped_vars[] = {
170       {PE_ZCONF, &m_ZConf.Hex},
171       {PE_ALPHACONF, &m_AlphaConf.Hex},
172       {PE_DSTALPHACONF, &m_DstAlphaConf.Hex},
173       {PE_ALPHAMODE, &m_AlphaModeConf.Hex},
174       {PE_ALPHAREAD, &m_AlphaRead.Hex},
175   };
176   for (auto& mapped_var : directly_mapped_vars)
177   {
178     mmio->Register(base | mapped_var.addr, MMIO::DirectRead<u16>(mapped_var.ptr),
179                    MMIO::DirectWrite<u16>(mapped_var.ptr));
180   }
181 
182   // Performance queries registers: read only, need to call the video backend
183   // to get the results.
184   struct
185   {
186     u32 addr;
187     PerfQueryType pqtype;
188   } pq_regs[] = {
189       {PE_PERF_ZCOMP_INPUT_ZCOMPLOC_L, PQ_ZCOMP_INPUT_ZCOMPLOC},
190       {PE_PERF_ZCOMP_OUTPUT_ZCOMPLOC_L, PQ_ZCOMP_OUTPUT_ZCOMPLOC},
191       {PE_PERF_ZCOMP_INPUT_L, PQ_ZCOMP_INPUT},
192       {PE_PERF_ZCOMP_OUTPUT_L, PQ_ZCOMP_OUTPUT},
193       {PE_PERF_BLEND_INPUT_L, PQ_BLEND_INPUT},
194       {PE_PERF_EFB_COPY_CLOCKS_L, PQ_EFB_COPY_CLOCKS},
195   };
196   for (auto& pq_reg : pq_regs)
197   {
198     mmio->Register(base | pq_reg.addr, MMIO::ComplexRead<u16>([pq_reg](u32) {
199                      return g_video_backend->Video_GetQueryResult(pq_reg.pqtype) & 0xFFFF;
200                    }),
201                    MMIO::InvalidWrite<u16>());
202     mmio->Register(base | (pq_reg.addr + 2), MMIO::ComplexRead<u16>([pq_reg](u32) {
203                      return g_video_backend->Video_GetQueryResult(pq_reg.pqtype) >> 16;
204                    }),
205                    MMIO::InvalidWrite<u16>());
206   }
207 
208   // Control register
209   mmio->Register(base | PE_CTRL_REGISTER, MMIO::DirectRead<u16>(&m_Control.Hex),
210                  MMIO::ComplexWrite<u16>([](u32, u16 val) {
211                    UPECtrlReg tmpCtrl(val);
212 
213                    if (tmpCtrl.PEToken)
214                      s_signal_token_interrupt = false;
215 
216                    if (tmpCtrl.PEFinish)
217                      s_signal_finish_interrupt = false;
218 
219                    m_Control.PETokenEnable = tmpCtrl.PETokenEnable;
220                    m_Control.PEFinishEnable = tmpCtrl.PEFinishEnable;
221                    m_Control.PEToken = 0;   // this flag is write only
222                    m_Control.PEFinish = 0;  // this flag is write only
223 
224                    DEBUG_LOG(PIXELENGINE, "(w16) CTRL_REGISTER: 0x%04x", val);
225                    UpdateInterrupts();
226                  }));
227 
228   // Token register, readonly.
229   mmio->Register(base | PE_TOKEN_REG, MMIO::DirectRead<u16>(&s_token), MMIO::InvalidWrite<u16>());
230 
231   // BBOX registers, readonly and need to update a flag.
232   for (int i = 0; i < 4; ++i)
233   {
234     mmio->Register(base | (PE_BBOX_LEFT + 2 * i), MMIO::ComplexRead<u16>([i](u32) {
235                      BoundingBox::Disable();
236                      return g_video_backend->Video_GetBoundingBox(i);
237                    }),
238                    MMIO::InvalidWrite<u16>());
239   }
240 }
241 
UpdateInterrupts()242 static void UpdateInterrupts()
243 {
244   // check if there is a token-interrupt
245   ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN,
246                                    s_signal_token_interrupt && m_Control.PETokenEnable);
247 
248   // check if there is a finish-interrupt
249   ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH,
250                                    s_signal_finish_interrupt && m_Control.PEFinishEnable);
251 }
252 
SetTokenFinish_OnMainThread(u64 userdata,s64 cyclesLate)253 static void SetTokenFinish_OnMainThread(u64 userdata, s64 cyclesLate)
254 {
255   std::unique_lock<std::mutex> lk(s_token_finish_mutex);
256   s_event_raised = false;
257 
258   s_token = s_token_pending;
259 
260   if (s_token_interrupt_pending)
261   {
262     s_token_interrupt_pending = false;
263     s_signal_token_interrupt = true;
264     UpdateInterrupts();
265   }
266 
267   if (s_finish_interrupt_pending)
268   {
269     s_finish_interrupt_pending = false;
270     s_signal_finish_interrupt = true;
271     UpdateInterrupts();
272     lk.unlock();
273     Core::FrameUpdateOnCPUThread();
274   }
275 }
276 
277 // Raise the event handler above on the CPU thread.
278 // s_token_finish_mutex must be locked.
279 // THIS IS EXECUTED FROM VIDEO THREAD
RaiseEvent()280 static void RaiseEvent()
281 {
282   if (s_event_raised)
283     return;
284 
285   s_event_raised = true;
286 
287   CoreTiming::FromThread from = CoreTiming::FromThread::NON_CPU;
288   if (!SConfig::GetInstance().bCPUThread || Fifo::UseDeterministicGPUThread())
289     from = CoreTiming::FromThread::CPU;
290   CoreTiming::ScheduleEvent(0, et_SetTokenFinishOnMainThread, 0, from);
291 }
292 
293 // SetToken
294 // THIS IS EXECUTED FROM VIDEO THREAD
SetToken(const u16 token,const bool interrupt)295 void SetToken(const u16 token, const bool interrupt)
296 {
297   DEBUG_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", token);
298 
299   std::lock_guard<std::mutex> lk(s_token_finish_mutex);
300 
301   s_token_pending = token;
302   s_token_interrupt_pending |= interrupt;
303 
304   RaiseEvent();
305 }
306 
307 // SetFinish
308 // THIS IS EXECUTED FROM VIDEO THREAD (BPStructs.cpp) when a new frame has been drawn
SetFinish()309 void SetFinish()
310 {
311   DEBUG_LOG(PIXELENGINE, "VIDEO Set Finish");
312 
313   std::lock_guard<std::mutex> lk(s_token_finish_mutex);
314 
315   s_finish_interrupt_pending |= true;
316 
317   RaiseEvent();
318 }
319 
GetAlphaReadMode()320 UPEAlphaReadReg GetAlphaReadMode()
321 {
322   return m_AlphaRead;
323 }
324 
325 }  // end of namespace PixelEngine
326