/[pcsx2_0.9.7]/trunk/pcsx2/MTGS.cpp
ViewVC logotype

Contents of /trunk/pcsx2/MTGS.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 62 - (show annotations) (download)
Tue Sep 7 11:08:22 2010 UTC (9 years, 11 months ago) by william
File size: 23033 byte(s)
Auto Commited Import of: pcsx2-0.9.7-r3738-debug in ./trunk
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "Common.h"
18
19 #include <list>
20 #include <wx/datetime.h>
21
22 #include "GS.h"
23 #include "Elfheader.h"
24 #include "SamplProf.h"
25
26
27 // Uncomment this to enable profiling of the GS RingBufferCopy function.
28 //#define PCSX2_GSRING_SAMPLING_STATS
29
30 using namespace Threading;
31
32 #if 0 //PCSX2_DEBUG
33 # define MTGS_LOG Console.WriteLn
34 #else
35 # define MTGS_LOG 0&&
36 #endif
37
38 // forces the compiler to treat a non-volatile value as volatile.
39 // This allows us to declare the vars as non-volatile and only use
40 // them as volatile when appropriate (more optimized).
41
42 #define volatize(x) (*reinterpret_cast<volatile uint*>(&(x)))
43
44
45 // =====================================================================================================
46 // MTGS Threaded Class Implementation
47 // =====================================================================================================
48
49 __aligned(32) MTGS_BufferedData RingBuffer;
50 extern bool renderswitch;
51
52
53 #ifdef RINGBUF_DEBUG_STACK
54 #include <list>
55 std::list<uint> ringposStack;
56 #endif
57
58 SysMtgsThread::SysMtgsThread() :
59 SysThreadBase()
60 #ifdef RINGBUF_DEBUG_STACK
61 , m_lock_Stack()
62 #endif
63 {
64 m_name = L"MTGS";
65
66 // All other state vars are initialized by OnStart().
67 }
68
69 void SysMtgsThread::OnStart()
70 {
71 m_PluginOpened = false;
72
73 m_ReadPos = 0;
74 m_WritePos = 0;
75 m_RingBufferIsBusy = false;
76 m_packet_size = 0;
77 m_packet_writepos = 0;
78
79 m_QueuedFrameCount = 0;
80 m_VsyncSignalListener = false;
81 m_SignalRingEnable = 0;
82 m_SignalRingPosition= 0;
83
84 m_CopyDataTally = 0;
85
86 _parent::OnStart();
87 }
88
89 SysMtgsThread::~SysMtgsThread() throw()
90 {
91 _parent::Cancel();
92 }
93
94 void SysMtgsThread::OnResumeReady()
95 {
96 m_sem_OpenDone.Reset();
97 }
98
99 void SysMtgsThread::ResetGS()
100 {
101 pxAssertDev( !IsOpen() || (m_ReadPos == m_WritePos), "Must close or terminate the GS thread prior to gsReset." );
102
103 // MTGS Reset process:
104 // * clear the ringbuffer.
105 // * Signal a reset.
106 // * clear the path and byRegs structs (used by GIFtagDummy)
107
108 m_ReadPos = m_WritePos;
109 m_QueuedFrameCount = 0;
110 m_VsyncSignalListener = false;
111
112 MTGS_LOG( "MTGS: Sending Reset..." );
113 SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
114 SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 );
115 SetEvent();
116
117 GIFPath_Reset();
118 }
119
120 struct RingCmdPacket_Vsync
121 {
122 u8 regset1[0x0f0];
123 u32 csr;
124 u32 imr;
125 GSRegSIGBLID siglblid;
126 };
127
128 void SysMtgsThread::PostVsyncEnd()
129 {
130 // Optimization note: Typically regset1 isn't needed. The regs in that area are typically
131 // changed infrequently, usually during video mode changes. However, on modern systems the
132 // 256-byte copy is only a few dozen cycles -- executed 60 times a second -- so probably
133 // not worth the effort or overhead of trying to selectively avoid it.
134
135 uint packsize = sizeof(RingCmdPacket_Vsync) / 16;
136 PrepDataPacket(GS_RINGTYPE_VSYNC, packsize);
137 MemCopy_WrappedDest( (u128*)PS2MEM_GS, RingBuffer.m_Ring, m_packet_writepos, RingBufferSize, 0xf );
138
139 u32* remainder = (u32*)GetDataPacketPtr();
140 remainder[0] = GSCSRr;
141 remainder[1] = GSIMR;
142 (GSRegSIGBLID&)remainder[2] = GSSIGLBLID;
143 m_packet_writepos = (m_packet_writepos + 1) & RingBufferMask;
144
145 SendDataPacket();
146
147 // Vsyncs should always start the GS thread, regardless of how little has actually be queued.
148 if (m_CopyDataTally != 0) SetEvent();
149
150 // If the MTGS is allowed to queue a lot of frames in advance, it creates input lag.
151 // Use the Queued FrameCount to stall the EE if another vsync (or two) are already queued
152 // in the ringbuffer. The queue limit is disabled when both FrameLimiting and Vsync are
153 // disabled, since the queue can have perverse effects on framerate benchmarking.
154
155 if ((AtomicIncrement(m_QueuedFrameCount) < EmuConfig.GS.VsyncQueueSize) || (!EmuConfig.GS.VsyncEnable && !EmuConfig.GS.FrameLimitEnable)) return;
156
157 m_VsyncSignalListener = true;
158 //Console.WriteLn( Color_Blue, "(EEcore Sleep) Vsync\t\tringpos=0x%06x, writepos=0x%06x", volatize(m_ReadPos), m_WritePos );
159 m_sem_Vsync.WaitNoCancel();
160 }
161
162 struct PacketTagType
163 {
164 u32 command;
165 u32 data[3];
166 };
167
168 static void dummyIrqCallback()
169 {
170 // dummy, because MTGS doesn't need this mess!
171 // (and zerogs does >_<)
172 }
173
174 void SysMtgsThread::OpenPlugin()
175 {
176 if( m_PluginOpened ) return;
177
178 memcpy_aligned( RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS) );
179 GSsetBaseMem( RingBuffer.Regs );
180 GSirqCallback( dummyIrqCallback );
181
182 int result;
183
184 if( GSopen2 != NULL )
185 result = GSopen2( (void*)&pDsp, 1 | (renderswitch ? 4 : 0) );
186 else
187 result = GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
188
189 // Vsync on / off ?
190 if( renderswitch )
191 {
192 Console.Indent(2).WriteLn( "Forced software switch enabled." );
193 if (EmuConfig.GS.VsyncEnable)
194 {
195 // Better turn Vsync off now, as in most cases sw rendering is not fast enough to support a steady 60fps.
196 // Having Vsync still enabled then means a big cut in speed and sloppy rendering.
197 // It's possible though that some users have very fast machines, and rather kept Vsync enabled,
198 // but let's assume this is the minority. At least for now ;)
199 GSsetVsync( false );
200 Console.Indent(2).WriteLn( "Vsync temporarily disabled" );
201 }
202 }
203 else
204 {
205 GSsetVsync( EmuConfig.GS.FrameLimitEnable && EmuConfig.GS.VsyncEnable );
206 }
207
208 if( result != 0 )
209 {
210 DevCon.WriteLn( "GSopen Failed: return code: 0x%x", result );
211 throw Exception::PluginOpenError( PluginId_GS );
212 }
213
214 // This is the preferred place to implement DXGI fullscreen overrides, using LoadLibrary.
215 // But I hate COM, I don't know to make this work, and I don't have DX10, so I give up
216 // and enjoy my working DX9 alt-enter instead. Someone else can fix this mess. --air
217
218 // Also: Prolly needs some DX10 header includes? Which ones? Too many, I gave up.
219
220 #if 0 // defined(__WXMSW__) && defined(_MSC_VER)
221 wxDynamicLibrary dynlib( L"dxgi.dll" );
222 SomeFuncTypeIDunno isThisEvenTheRightFunctionNameIDunno = dynlib.GetSymbol("CreateDXGIFactory");
223 if( isThisEvenTheRightFunctionNameIDunno )
224 {
225 // Is this how LoadLibrary for COM works? I dunno. I dont care.
226
227 IDXGIFactory* pFactory;
228 hr = isThisEvenTheRightFunctionNameIDunno(__uuidof(IDXGIFactory), (void**)(&pFactory) );
229 pFactory->MakeWindowAssociation((HWND)&pDsp, DXGI_MWA_NO_WINDOW_CHANGES);
230 pFactory->Release();
231 }
232 #endif
233
234 m_PluginOpened = true;
235 m_sem_OpenDone.Post();
236
237 GSsetGameCRC( ElfCRC, 0 );
238 }
239
240 class RingBufferLock : public ScopedLock
241 {
242 typedef ScopedLock _parent;
243
244 protected:
245 SysMtgsThread& m_mtgs;
246
247 public:
248 RingBufferLock( SysMtgsThread& mtgs )
249 : ScopedLock( mtgs.m_mtx_RingBufferBusy )
250 , m_mtgs( mtgs )
251 {
252 m_mtgs.m_RingBufferIsBusy = true;
253 }
254
255 virtual ~RingBufferLock() throw()
256 {
257 m_mtgs.m_RingBufferIsBusy = false;
258 }
259
260 void Acquire()
261 {
262 _parent::Acquire();
263 m_mtgs.m_RingBufferIsBusy = true;
264 }
265
266 void Release()
267 {
268 m_mtgs.m_RingBufferIsBusy = false;
269 _parent::Release();
270 }
271 };
272
273 void SysMtgsThread::ExecuteTaskInThread()
274 {
275 #ifdef RINGBUF_DEBUG_STACK
276 PacketTagType prevCmd;
277 #endif
278
279 RingBufferLock busy( *this );
280
281 while( true )
282 {
283 busy.Release();
284
285 // Performance note: Both of these perform cancellation tests, but pthread_testcancel
286 // is very optimized (only 1 instruction test in most cases), so no point in trying
287 // to avoid it.
288
289 m_sem_event.WaitWithoutYield();
290 StateCheckInThread();
291 busy.Acquire();
292
293 // note: m_ReadPos is intentionally not volatile, because it should only
294 // ever be modified by this thread.
295 while( m_ReadPos != volatize(m_WritePos))
296 {
297 if( EmuConfig.GS.DisableOutput )
298 {
299 m_ReadPos = m_WritePos;
300 continue;
301 }
302
303 pxAssert( m_ReadPos < RingBufferSize );
304
305 const PacketTagType& tag = (PacketTagType&)RingBuffer[m_ReadPos];
306 u32 ringposinc = 1;
307
308 #ifdef RINGBUF_DEBUG_STACK
309 // pop a ringpos off the stack. It should match this one!
310
311 m_lock_Stack.Lock();
312 uptr stackpos = ringposStack.back();
313 if( stackpos != m_ReadPos )
314 {
315 Console.Error( "MTGS Ringbuffer Critical Failure ---> %x to %x (prevCmd: %x)\n", stackpos, m_ReadPos, prevCmd.command );
316 }
317 pxAssert( stackpos == m_ReadPos );
318 prevCmd = tag;
319 ringposStack.pop_back();
320 m_lock_Stack.Release();
321 #endif
322
323 switch( tag.command )
324 {
325 case GS_RINGTYPE_P1:
326 {
327 uint datapos = (m_ReadPos+1) & RingBufferMask;
328 const int qsize = tag.data[0];
329 const u128* data = &RingBuffer[datapos];
330
331 MTGS_LOG( "(MTGS Packet Read) ringtype=P1, qwc=%u", qsize );
332
333 uint endpos = datapos + qsize;
334 if( endpos >= RingBufferSize )
335 {
336 uint firstcopylen = RingBufferSize - datapos;
337 GSgifTransfer( (u32*)data, firstcopylen );
338 datapos = endpos & RingBufferMask;
339 GSgifTransfer( (u32*)RingBuffer.m_Ring, datapos );
340 }
341 else
342 {
343 GSgifTransfer( (u32*)data, qsize );
344 }
345
346 ringposinc += qsize;
347 }
348 break;
349
350 case GS_RINGTYPE_P2:
351 {
352 uint datapos = (m_ReadPos+1) & RingBufferMask;
353 const int qsize = tag.data[0];
354 const u128* data = &RingBuffer[datapos];
355
356 MTGS_LOG( "(MTGS Packet Read) ringtype=P2, qwc=%u", qsize );
357
358 uint endpos = datapos + qsize;
359 if( endpos >= RingBufferSize )
360 {
361 uint firstcopylen = RingBufferSize - datapos;
362 GSgifTransfer2( (u32*)data, firstcopylen );
363 datapos = endpos & RingBufferMask;
364 GSgifTransfer2( (u32*)RingBuffer.m_Ring, datapos );
365 }
366 else
367 {
368 GSgifTransfer2( (u32*)data, qsize );
369 }
370
371 ringposinc += qsize;
372 }
373 break;
374
375 case GS_RINGTYPE_P3:
376 {
377 uint datapos = (m_ReadPos+1) & RingBufferMask;
378 const int qsize = tag.data[0];
379 const u128* data = &RingBuffer[datapos];
380
381 MTGS_LOG( "(MTGS Packet Read) ringtype=P3, qwc=%u", qsize );
382
383 uint endpos = datapos + qsize;
384 if( endpos >= RingBufferSize )
385 {
386 uint firstcopylen = RingBufferSize - datapos;
387 GSgifTransfer3( (u32*)data, firstcopylen );
388 datapos = endpos & RingBufferMask;
389 GSgifTransfer3( (u32*)RingBuffer.m_Ring, datapos );
390 }
391 else
392 {
393 GSgifTransfer3( (u32*)data, qsize );
394 }
395
396 ringposinc += qsize;
397 }
398 break;
399
400 default:
401 {
402 switch( tag.command )
403 {
404 case GS_RINGTYPE_VSYNC:
405 {
406 const int qsize = tag.data[0];
407 ringposinc += qsize;
408
409 MTGS_LOG( "(MTGS Packet Read) ringtype=Vsync, field=%u, skip=%s", !!(((u32&)RingBuffer.Regs[0x1000]) & 0x2000) ? 0 : 1, tag.data[1] ? "true" : "false" );
410
411 // Mail in the important GS registers.
412 // This seemingly obtuse system is needed in order to handle cases where the vsync data wraps
413 // around the edge of the ringbuffer. If not for that I'd just use a struct. >_<
414
415 uint datapos = (m_ReadPos+1) & RingBufferMask;
416 MemCopy_WrappedSrc( RingBuffer.m_Ring, datapos, RingBufferSize, (u128*)RingBuffer.Regs, 0xf );
417
418 u32* remainder = (u32*)&RingBuffer[datapos];
419 ((u32&)RingBuffer.Regs[0x1000]) = remainder[0];
420 ((u32&)RingBuffer.Regs[0x1010]) = remainder[1];
421 ((GSRegSIGBLID&)RingBuffer.Regs[0x1080]) = (GSRegSIGBLID&)remainder[2];
422
423 // CSR & 0x2000; is the pageflip id.
424 GSvsync(((u32&)RingBuffer.Regs[0x1000]) & 0x2000);
425 gsFrameSkip();
426
427 // if we're not using GSOpen2, then the GS window is on this thread (MTGS thread),
428 // so we need to call PADupdate from here.
429 if( (GSopen2 == NULL) && (PADupdate != NULL) )
430 PADupdate(0);
431
432 AtomicDecrement( m_QueuedFrameCount );
433 if (!!AtomicExchange(m_VsyncSignalListener, false))
434 m_sem_Vsync.Post();
435
436 busy.Release();
437 StateCheckInThread();
438 busy.Acquire();
439 }
440 break;
441
442 case GS_RINGTYPE_FRAMESKIP:
443 MTGS_LOG( "(MTGS Packet Read) ringtype=Frameskip" );
444 _gs_ResetFrameskip();
445 break;
446
447 case GS_RINGTYPE_FREEZE:
448 {
449 MTGS_FreezeData* data = (MTGS_FreezeData*)(*(uptr*)&tag.data[1]);
450 int mode = tag.data[0];
451 data->retval = GetCorePlugins().DoFreeze( PluginId_GS, mode, data->fdata );
452 }
453 break;
454
455 case GS_RINGTYPE_RESET:
456 MTGS_LOG( "(MTGS Packet Read) ringtype=Reset" );
457 if( GSreset != NULL ) GSreset();
458 break;
459
460 case GS_RINGTYPE_SOFTRESET:
461 {
462 int mask = tag.data[0];
463 MTGS_LOG( "(MTGS Packet Read) ringtype=SoftReset" );
464 GSgifSoftReset( mask );
465 }
466 break;
467
468 case GS_RINGTYPE_MODECHANGE:
469 // [TODO] some frameskip sync logic might be needed here!
470 break;
471
472 case GS_RINGTYPE_CRC:
473 GSsetGameCRC( tag.data[0], 0 );
474 break;
475
476 #ifdef PCSX2_DEVBUILD
477 default:
478 Console.Error("GSThreadProc, bad packet (%x) at m_ReadPos: %x, m_WritePos: %x", tag.command, m_ReadPos, m_WritePos);
479 pxFail( "Bad packet encountered in the MTGS Ringbuffer." );
480 m_ReadPos = m_WritePos;
481 continue;
482 #else
483 // Optimized performance in non-Dev builds.
484 jNO_DEFAULT;
485 #endif
486 }
487 }
488 }
489
490 uint newringpos = (m_ReadPos + ringposinc) & RingBufferMask;
491
492 if( EmuConfig.GS.SynchronousMTGS )
493 {
494 pxAssert( m_WritePos == newringpos );
495 }
496
497 m_ReadPos = newringpos;
498
499 if( m_SignalRingEnable != 0 )
500 {
501 // The EEcore has requested a signal after some amount of processed data.
502 if( AtomicExchangeSub( m_SignalRingPosition, ringposinc ) <= 0 )
503 {
504 // Make sure to post the signal after the m_ReadPos has been updated...
505 AtomicExchange( m_SignalRingEnable, 0 );
506 m_sem_OnRingReset.Post();
507 continue;
508 }
509 }
510 }
511
512 busy.Release();
513
514 // Safety valve in case standard signals fail for some reason -- this ensures the EEcore
515 // won't sleep the eternity, even if SignalRingPosition didn't reach 0 for some reason.
516 // Important: Need to unlock the MTGS busy signal PRIOR, so that EEcore SetEvent() calls
517 // parallel to this handler aren't accidentally blocked.
518 if( AtomicExchange( m_SignalRingEnable, 0 ) != 0 )
519 {
520 //Console.Warning( "(MTGS Thread) Dangling RingSignal on empty buffer! signalpos=0x%06x", AtomicExchange( m_SignalRingPosition, 0 ) );
521 AtomicExchange( m_SignalRingPosition, 0 );
522 m_sem_OnRingReset.Post();
523 }
524
525 if (!!AtomicExchange(m_VsyncSignalListener, false))
526 m_sem_Vsync.Post();
527
528 //Console.Warning( "(MTGS Thread) Nothing to do! ringpos=0x%06x", m_ReadPos );
529 }
530 }
531
532 void SysMtgsThread::ClosePlugin()
533 {
534 if( !m_PluginOpened ) return;
535 m_PluginOpened = false;
536 GetCorePlugins().Close( PluginId_GS );
537 }
538
539 void SysMtgsThread::OnSuspendInThread()
540 {
541 ClosePlugin();
542 _parent::OnSuspendInThread();
543 }
544
545 void SysMtgsThread::OnResumeInThread( bool isSuspended )
546 {
547 if( isSuspended )
548 OpenPlugin();
549
550 _parent::OnResumeInThread( isSuspended );
551 }
552
553 void SysMtgsThread::OnCleanupInThread()
554 {
555 ClosePlugin();
556 _parent::OnCleanupInThread();
557 }
558
559 // Waits for the GS to empty out the entire ring buffer contents.
560 // Used primarily for plugin startup/shutdown.
561 void SysMtgsThread::WaitGS()
562 {
563 pxAssertDev( !IsSelf(), "This method is only allowed from threads *not* named MTGS." );
564
565 if( m_ExecMode == ExecMode_NoThreadYet || !IsRunning() ) return;
566 if( !pxAssertDev( IsOpen(), "MTGS Warning! WaitGS issued on a closed thread." ) ) return;
567
568 if( volatize(m_ReadPos) != m_WritePos )
569 {
570 SetEvent();
571 RethrowException();
572
573 do {
574 m_mtx_RingBufferBusy.Wait();
575 RethrowException();
576 } while( volatize(m_ReadPos) != m_WritePos );
577 }
578
579 // Completely synchronize GS and MTGS register states.
580 memcpy_fast( RingBuffer.Regs, PS2MEM_GS, sizeof(RingBuffer.Regs) );
581 }
582
583 // Sets the gsEvent flag and releases a timeslice.
584 // For use in loops that wait on the GS thread to do certain things.
585 void SysMtgsThread::SetEvent()
586 {
587 if( !m_RingBufferIsBusy )
588 m_sem_event.Post();
589
590 m_CopyDataTally = 0;
591 }
592
593 u8* SysMtgsThread::GetDataPacketPtr() const
594 {
595 return (u8*)&RingBuffer[m_packet_writepos & RingBufferMask];
596 }
597
598 // Closes the data packet send command, and initiates the gs thread (if needed).
599 void SysMtgsThread::SendDataPacket()
600 {
601 // make sure a previous copy block has been started somewhere.
602 pxAssert( m_packet_size != 0 );
603
604 uint actualSize = ((m_packet_writepos - m_packet_startpos) & RingBufferMask)-1;
605 pxAssert( actualSize <= m_packet_size );
606 pxAssert( m_packet_writepos < RingBufferSize );
607
608 PacketTagType& tag = (PacketTagType&)RingBuffer[m_packet_startpos];
609 tag.data[0] = actualSize;
610
611 m_WritePos = m_packet_writepos;
612
613 if( EmuConfig.GS.SynchronousMTGS )
614 {
615 WaitGS();
616 }
617 else if( !m_RingBufferIsBusy )
618 {
619 m_CopyDataTally += m_packet_size;
620 if( m_CopyDataTally > 0x2000 ) SetEvent();
621 }
622
623 m_packet_size = 0;
624
625 //m_PacketLocker.Release();
626 }
627
628 void SysMtgsThread::GenericStall( uint size )
629 {
630 // Note on volatiles: m_WritePos is not modified by the GS thread, so there's no need
631 // to use volatile reads here. We do cache it though, since we know it never changes,
632 // except for calls to RingbufferRestert() -- handled below.
633 const uint writepos = m_WritePos;
634
635 // Sanity checks! (within the confines of our ringbuffer please!)
636 pxAssert( size < RingBufferSize );
637 pxAssert( writepos < RingBufferSize );
638
639 // generic gs wait/stall.
640 // if the writepos is past the readpos then we're safe.
641 // But if not then we need to make sure the readpos is outside the scope of
642 // the block about to be written (writepos + size)
643
644 uint readpos = volatize(m_ReadPos);
645 uint freeroom;
646
647 if (writepos < readpos)
648 freeroom = readpos - writepos;
649 else
650 freeroom = RingBufferSize - (writepos - readpos);
651
652 if (freeroom <= size)
653 {
654 // writepos will overlap readpos if we commit the data, so we need to wait until
655 // readpos is out past the end of the future write pos, or until it wraps around
656 // (in which case writepos will be >= readpos).
657
658 // Ideally though we want to wait longer, because if we just toss in this packet
659 // the next packet will likely stall up too. So lets set a condition for the MTGS
660 // thread to wake up the EE once there's a sizable chunk of the ringbuffer emptied.
661
662 uint somedone = (RingBufferSize - freeroom) / 4;
663 if( somedone < size+1 ) somedone = size + 1;
664
665 // FMV Optimization: FMVs typically send *very* little data to the GS, in some cases
666 // every other frame is nothing more than a page swap. Sleeping the EEcore is a
667 // waste of time, and we get better results using a spinwait.
668
669 if( somedone > 0x80 )
670 {
671 pxAssertDev( m_SignalRingEnable == 0, "MTGS Thread Synchronization Error" );
672 m_SignalRingPosition = somedone;
673
674 //Console.WriteLn( Color_Blue, "(EEcore Sleep) PrepDataPacker \tringpos=0x%06x, writepos=0x%06x, signalpos=0x%06x", readpos, writepos, m_SignalRingPosition );
675
676 while(true) {
677 AtomicExchange( m_SignalRingEnable, 1 );
678 SetEvent();
679 m_sem_OnRingReset.WaitWithoutYield();
680 readpos = volatize(m_ReadPos);
681 //Console.WriteLn( Color_Blue, "(EEcore Awake) Report!\tringpos=0x%06x", readpos );
682
683 if (writepos < readpos)
684 freeroom = readpos - writepos;
685 else
686 freeroom = RingBufferSize - (writepos - readpos);
687
688 if (freeroom > size) break;
689 }
690
691 pxAssertDev( m_SignalRingPosition <= 0, "MTGS Thread Synchronization Error" );
692 }
693 else
694 {
695 //Console.WriteLn( Color_StrongGray, "(EEcore Spin) PrepDataPacket!" );
696 SetEvent();
697 while(true) {
698 SpinWait();
699 readpos = volatize(m_ReadPos);
700
701 if (writepos < readpos)
702 freeroom = readpos - writepos;
703 else
704 freeroom = RingBufferSize - (writepos - readpos);
705
706 if (freeroom > size) break;
707 }
708 }
709 }
710 }
711
712 void SysMtgsThread::PrepDataPacket( MTGS_RingCommand cmd, u32 size )
713 {
714 m_packet_size = size;
715 ++size; // takes into account our RingCommand QWC.
716 GenericStall(size);
717
718 // Command qword: Low word is the command, and the high word is the packet
719 // length in SIMDs (128 bits).
720
721 PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
722 tag.command = cmd;
723 tag.data[0] = m_packet_size;
724 m_packet_startpos = m_WritePos;
725 m_packet_writepos = (m_WritePos + 1) & RingBufferMask;
726 }
727
728 // Returns the amount of giftag data processed (in simd128 values).
729 // Return value is used by VU1's XGKICK instruction to wrap the data
730 // around VU memory instead of having buffer overflow...
731 // Parameters:
732 // size - size of the packet data, in smd128's
733 void SysMtgsThread::PrepDataPacket( GIF_PATH pathidx, u32 size )
734 {
735 //m_PacketLocker.Acquire();
736
737 PrepDataPacket( (MTGS_RingCommand)pathidx, size );
738 }
739
740 __fi void SysMtgsThread::_FinishSimplePacket()
741 {
742 uint future_writepos = (m_WritePos+1) & RingBufferMask;
743 pxAssert( future_writepos != volatize(m_ReadPos) );
744 m_WritePos = future_writepos;
745
746 if( EmuConfig.GS.SynchronousMTGS )
747 WaitGS();
748 else
749 ++m_CopyDataTally;
750 }
751
752 void SysMtgsThread::SendSimplePacket( MTGS_RingCommand type, int data0, int data1, int data2 )
753 {
754 //ScopedLock locker( m_PacketLocker );
755
756 GenericStall(1);
757 PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
758
759 tag.command = type;
760 tag.data[0] = data0;
761 tag.data[1] = data1;
762 tag.data[2] = data2;
763
764 _FinishSimplePacket();
765 }
766
767 void SysMtgsThread::SendPointerPacket( MTGS_RingCommand type, u32 data0, void* data1 )
768 {
769 //ScopedLock locker( m_PacketLocker );
770
771 GenericStall(1);
772 PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
773
774 tag.command = type;
775 tag.data[0] = data0;
776 *(uptr*)&tag.data[1] = (uptr)data1;
777
778 _FinishSimplePacket();
779 }
780
781 void SysMtgsThread::SendGameCRC( u32 crc )
782 {
783 SendSimplePacket( GS_RINGTYPE_CRC, crc, 0, 0 );
784 }
785
786 void SysMtgsThread::WaitForOpen()
787 {
788 if( m_PluginOpened ) return;
789 Resume();
790
791 // Two-phase timeout on MTGS opening, so that possible errors are handled
792 // in a timely fashion. We check for errors after 2 seconds, and then give it
793 // another 12 seconds if no errors occurred (this might seem long, but sometimes a
794 // GS plugin can be very stubborned, especially in debug mode builds).
795
796 if( !m_sem_OpenDone.Wait( wxTimeSpan(0, 0, 2, 0) ) )
797 {
798 RethrowException();
799
800 if( !m_sem_OpenDone.Wait( wxTimeSpan(0, 0, 12, 0) ) )
801 {
802 RethrowException();
803
804 // Not opened yet, and no exceptions. Weird? You decide!
805 // [TODO] : implement a user confirmation to cancel the action and exit the
806 // emulator forcefully, or to continue waiting on the GS.
807
808 throw Exception::PluginOpenError( PluginId_GS )
809 .SetBothMsgs(wxLt("The MTGS thread has become unresponsive while waiting for the GS plugin to open."));
810 }
811 }
812
813 RethrowException();
814 }
815
816 void SysMtgsThread::Freeze( int mode, MTGS_FreezeData& data )
817 {
818 GetCorePlugins().Open( PluginId_GS );
819 SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
820 Resume();
821 WaitGS();
822 }

  ViewVC Help
Powered by ViewVC 1.1.22