diff options
| author | Matthew Parlane <parlane@gmail.com> | 2012-12-29 17:47:52 +1300 |
|---|---|---|
| committer | Matthew Parlane <parlane@gmail.com> | 2012-12-29 17:47:52 +1300 |
| commit | 13bd235c1174f1291fedbf73f6bfd01c4dd81994 (patch) | |
| tree | 497e9a760a94efb6a69bee0a60bb1b8735152e32 /Source/Core | |
| parent | de0798f4f7fb0d4010567351e34f658ab51348ef (diff) | |
| parent | e9d00bfeebb62d8b6c5627b959c9045914348775 (diff) | |
Merge branch 'master' into wii-usb
Diffstat (limited to 'Source/Core')
52 files changed, 3669 insertions, 691 deletions
diff --git a/Source/Core/Common/Src/ChunkFile.h b/Source/Core/Common/Src/ChunkFile.h index 5935b7f53a..be8b49a3a3 100644 --- a/Source/Core/Common/Src/ChunkFile.h +++ b/Source/Core/Common/Src/ChunkFile.h @@ -158,7 +158,7 @@ public: case MODE_READ: x = (wchar_t*)*ptr; break; case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; case MODE_MEASURE: break; - case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break; + case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break; } (*ptr) += stringLen; } diff --git a/Source/Core/Common/Src/Common.h b/Source/Core/Common/Src/Common.h index 960bf75559..884a1e6c16 100644 --- a/Source/Core/Common/Src/Common.h +++ b/Source/Core/Common/Src/Common.h @@ -60,11 +60,6 @@ private: #undef STACKALIGN #define STACKALIGN __attribute__((__force_align_arg_pointer__)) #endif -// We use wxWidgets on OS X only if it is version 2.9+ with Cocoa support. -#ifdef __WXOSX_COCOA__ -#define HAVE_WX 1 -#define USE_WX 1 // Use wxGLCanvas -#endif #elif defined _WIN32 diff --git a/Source/Core/Common/Src/FileUtil.cpp b/Source/Core/Common/Src/FileUtil.cpp index 77aac45418..77290528a6 100644 --- a/Source/Core/Common/Src/FileUtil.cpp +++ b/Source/Core/Common/Src/FileUtil.cpp @@ -649,8 +649,7 @@ std::string &GetUserPath(const unsigned int DirIDX, const std::string &newPath) if (paths[D_USER_IDX].empty()) { #ifdef _WIN32 - // TODO: use GetExeDirectory() here instead of ROOT_DIR so that if the cwd is changed we still have the correct paths? - paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; + paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; #else if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; diff --git a/Source/Core/Common/Src/MathUtil.h b/Source/Core/Common/Src/MathUtil.h index a6290ff602..c5f613ada1 100644 --- a/Source/Core/Common/Src/MathUtil.h +++ b/Source/Core/Common/Src/MathUtil.h @@ -117,6 +117,8 @@ struct Rectangle Rectangle(T theLeft, T theTop, T theRight, T theBottom) : left(theLeft), top(theTop), right(theRight), bottom(theBottom) { } + + bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } T GetWidth() const { return abs(right - left); } T GetHeight() const { return abs(bottom - top); } diff --git a/Source/Core/Common/Src/VideoBackendBase.cpp b/Source/Core/Common/Src/VideoBackendBase.cpp index 40bb0fc4ce..62bb42997f 100644 --- a/Source/Core/Common/Src/VideoBackendBase.cpp +++ b/Source/Core/Common/Src/VideoBackendBase.cpp @@ -22,7 +22,9 @@ #include "../../../Plugins/Plugin_VideoDX9/Src/VideoBackend.h" #include "../../../Plugins/Plugin_VideoDX11/Src/VideoBackend.h" #endif +#ifndef USE_GLES #include "../../../Plugins/Plugin_VideoOGL/Src/VideoBackend.h" +#endif #include "../../../Plugins/Plugin_VideoSoftware/Src/VideoBackend.h" std::vector<VideoBackend*> g_available_video_backends; @@ -52,7 +54,9 @@ void VideoBackend::PopulateList() if (IsGteVista()) g_available_video_backends.push_back(new DX11::VideoBackend); #endif +#ifndef USE_GLES g_available_video_backends.push_back(new OGL::VideoBackend); +#endif g_available_video_backends.push_back(new SW::VideoSoftware); g_video_backend = g_available_video_backends.front(); diff --git a/Source/Core/Common/Src/VideoBackendBase.h b/Source/Core/Common/Src/VideoBackendBase.h index d09288ebc7..311fa66104 100644 --- a/Source/Core/Common/Src/VideoBackendBase.h +++ b/Source/Core/Common/Src/VideoBackendBase.h @@ -137,6 +137,8 @@ public: // the implementation needs not do synchronization logic, because calls to it are surrounded by PauseAndLock now virtual void DoState(PointerWrap &p) = 0; + + virtual void CheckInvalidState() = 0; }; extern std::vector<VideoBackend*> g_available_video_backends; @@ -176,9 +178,15 @@ class VideoBackendHardware : public VideoBackend void PauseAndLock(bool doLock, bool unpauseOnUnlock=true); void DoState(PointerWrap &p); + + bool m_invalid; + +public: + void CheckInvalidState(); protected: void InitializeShared(); + void InvalidState(); }; #endif diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 4f01cfb5d6..20c611544a 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -70,7 +70,8 @@ set(SRCS Src/ActionReplay.cpp Src/HW/CPU.cpp Src/HW/DSP.cpp Src/HW/DSPHLE/UCodes/UCode_AX.cpp - Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp + Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp + Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp Src/HW/DSPHLE/UCodes/UCode_CARD.cpp Src/HW/DSPHLE/UCodes/UCode_InitAudioSystem.cpp Src/HW/DSPHLE/UCodes/UCode_ROM.cpp @@ -192,7 +193,11 @@ set(SRCS Src/ActionReplay.cpp Src/PowerPC/JitCommon/JitCache.cpp Src/PowerPC/JitCommon/Jit_Util.cpp) -set(LIBS bdisasm inputcommon videoogl videosoftware sfml-network) +set(LIBS bdisasm inputcommon videosoftware sfml-network) + +if(NOT USE_GLES) + set(LIBS ${LIBS} videoogl) +endif() if(LIBUSB_FOUND) diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index e1f6db3d18..eff49552f7 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -261,6 +261,7 @@ <ClCompile Include="Src\HW\DSPHLE\UCodes\UCodes.cpp" /> <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_AX.cpp" /> <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.cpp" /> + <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.cpp" /> <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_CARD.cpp" /> <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_GBA.cpp" /> <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_InitAudioSystem.cpp" /> @@ -463,9 +464,11 @@ <ClInclude Include="Src\HW\DSPHLE\MailHandler.h" /> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCodes.h" /> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX.h" /> - <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXStructs.h" /> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Structs.h" /> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.h" /> - <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_ADPCM.h" /> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Structs.h" /> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Voice.h" /> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.h" /> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Voice.h" /> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_CARD.h" /> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_GBA.h" /> @@ -597,4 +600,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 475861b64a..cb4791abad 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -197,6 +197,9 @@ <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.cpp"> <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> </ClCompile> + <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.cpp"> + <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> + </ClCompile> <ClCompile Include="Src\HW\DSPHLE\UCodes\UCode_CARD.cpp"> <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> </ClCompile> @@ -730,18 +733,24 @@ <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX.h"> <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> </ClInclude> - <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_ADPCM.h"> - <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> - </ClInclude> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Voice.h"> <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> </ClInclude> - <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXStructs.h"> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AX_Structs.h"> <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> </ClInclude> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii.h"> <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> </ClInclude> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Structs.h"> + <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> + </ClInclude> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_AXWii_Voice.h"> + <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> + </ClInclude> + <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_NewAXWii.h"> + <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> + </ClInclude> <ClInclude Include="Src\HW\DSPHLE\UCodes\UCode_CARD.h"> <Filter>HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes</Filter> </ClInclude> @@ -1183,4 +1192,4 @@ <UniqueIdentifier>{321a9af5-9b3d-4620-888c-fe9d02e9559e}</UniqueIdentifier> </Filter> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp index 9524882a45..81ef7c6482 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.cpp @@ -12,475 +12,683 @@ // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ -// Official SVN repository and contact information can be found at +// Official Git repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#include "FileUtil.h" // For IsDirectory() -#include "StringUtil.h" // For StringFromFormat() -#include <sstream> - -#include "Mixer.h" -#include "../MailHandler.h" -#include "../../DSP.h" -#include "UCodes.h" -#include "UCode_AXStructs.h" #include "UCode_AX.h" +#include "../../DSP.h" + +#define AX_GC #include "UCode_AX_Voice.h" -CUCode_AX::CUCode_AX(DSPHLE *dsp_hle, u32 l_CRC) - : IUCode(dsp_hle, l_CRC) - , m_addressPBs(0xFFFFFFFF) +CUCode_AX::CUCode_AX(DSPHLE* dsp_hle, u32 crc) + : IUCode(dsp_hle, crc) + , m_cmdlist_size(0) + , m_axthread(&SpawnAXThread, this) { - // we got loaded + WARN_LOG(DSPHLE, "Instantiating CUCode_AX: crc=%08x", crc); m_rMailHandler.PushMail(DSP_INIT); - - templbuffer = new int[1024 * 1024]; - temprbuffer = new int[1024 * 1024]; + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); } CUCode_AX::~CUCode_AX() { + m_cmdlist_size = (u16)-1; // Special value to signal end + NotifyAXThread(); + m_axthread.join(); + m_rMailHandler.Clear(); - delete [] templbuffer; - delete [] temprbuffer; } -// Needs A LOT of love! -static void ProcessUpdates(AXPB &PB) +void CUCode_AX::SpawnAXThread(CUCode_AX* self) { - // Make the updates we are told to do. When there are multiple updates for a block they - // are placed in memory directly following updaddr. They are mostly for initial time - // delays, sometimes for the FIR filter or channel volumes. We do all of them at once here. - // If we get both an on and an off update we chose on. Perhaps that makes the RE1 music - // work better. - int numupd = PB.updates.num_updates[0] - + PB.updates.num_updates[1] - + PB.updates.num_updates[2] - + PB.updates.num_updates[3] - + PB.updates.num_updates[4]; - if (numupd > 64) numupd = 64; // prevent crazy values TODO: LOL WHAT - const u32 updaddr = (u32)(PB.updates.data_hi << 16) | PB.updates.data_lo; - int on = 0, off = 0; - for (int j = 0; j < numupd; j++) + self->AXThread(); +} + +void CUCode_AX::AXThread() +{ + while (true) { - const u16 updpar = HLEMemory_Read_U16(updaddr + j*4); - const u16 upddata = HLEMemory_Read_U16(updaddr + j*4 + 2); - // some safety checks, I hope it's enough - if (updaddr > 0x80000000 && updaddr < 0x817fffff - && updpar < 63 && updpar > 3 // updpar > 3 because we don't want to change - // 0-3, those are important - //&& (upd0 || upd1 || upd2 || upd3 || upd4) // We should use these in some way to I think - // but I don't know how or when - ) { - ((u16*)&PB)[updpar] = upddata; // WTF ABOUNDS! + std::unique_lock<std::mutex> lk(m_cmdlist_mutex); + while (m_cmdlist_size == 0) + m_cmdlist_cv.wait(lk); } - if (updpar == 7 && upddata != 0) on++; - if (updpar == 7 && upddata == 0) off++; + + if (m_cmdlist_size == (u16)-1) // End of thread signal + break; + + m_processing.lock(); + HandleCommandList(); + m_cmdlist_size = 0; + + // Signal end of processing + m_rMailHandler.PushMail(DSP_YIELD); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + m_processing.unlock(); } - // hack: if we get both an on and an off select on rather than off - if (on > 0 && off > 0) PB.running = 1; } -static void VoiceHacks(AXPB &pb) +void CUCode_AX::NotifyAXThread() +{ + std::unique_lock<std::mutex> lk(m_cmdlist_mutex); + m_cmdlist_cv.notify_one(); +} + +void CUCode_AX::HandleCommandList() { - // get necessary values - const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; - const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; - // const u32 updaddr = (u32)(pb.updates.data_hi << 16) | pb.updates.data_lo; - // const u16 updpar = HLEMemory_Read_U16(updaddr); - // const u16 upddata = HLEMemory_Read_U16(updaddr + 2); - - // ======================================================================================= - /* Fix problems introduced with the SSBM fix. Sometimes when a music stream ended sampleEnd - would end up outside of bounds while the block was still playing resulting in noise - a strange noise. This should take care of that. - */ - if ((sampleEnd > (0x017fffff * 2) || loopPos > (0x017fffff * 2))) // ARAM bounds in nibbles + // Temp variables for addresses computation + u16 addr_hi, addr_lo; + u16 addr2_hi, addr2_lo; + u16 size; + + u32 pb_addr = 0; + +#if 0 + WARN_LOG(DSPHLE, "Command list:"); + for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) + WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); + WARN_LOG(DSPHLE, "-------------"); +#endif + + u32 curr_idx = 0; + bool end = false; + while (!end) { - pb.running = 0; + u16 cmd = m_cmdlist[curr_idx++]; - // also reset all values if it makes any difference - pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; - pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; - pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; + switch (cmd) + { + // Some of these commands are unknown, or unused in this AX HLE. + // We still need to skip their arguments using "curr_idx += N". - pb.src.cur_addr_frac = 0; pb.src.ratio_hi = 0; pb.src.ratio_lo = 0; - pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; + case CMD_SETUP: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetupProcessing(HILO_TO_32(addr)); + break; - pb.audio_addr.looping = 0; - pb.adpcm_loop_info.pred_scale = 0; - pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; - } + case CMD_DL_AND_VOL_MIX: + { + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + u16 vol_main = m_cmdlist[curr_idx++]; + u16 vol_auxa = m_cmdlist[curr_idx++]; + u16 vol_auxb = m_cmdlist[curr_idx++]; + DownloadAndMixWithVolume(HILO_TO_32(addr), vol_main, vol_auxa, vol_auxb); + break; + } - /* - // the fact that no settings are reset (except running) after a SSBM type music stream or another - looping block (for example in Battle Stadium DON) has ended could cause loud garbled sound to be - played from one or more blocks. Perhaps it was in conjunction with the old sequenced music fix below, - I'm not sure. This was an attempt to prevent that anyway by resetting all. But I'm not sure if this - is needed anymore. Please try to play SSBM without it and see if it works anyway. - */ - if ( - // detect blocks that have recently been running that we should reset - pb.running == 0 && pb.audio_addr.looping == 1 - //pb.running == 0 && pb.adpcm_loop_info.pred_scale - - // this prevents us from ruining sequenced music blocks, may not be needed - /* - && !(pb.updates.num_updates[0] || pb.updates.num_updates[1] || pb.updates.num_updates[2] - || pb.updates.num_updates[3] || pb.updates.num_updates[4]) - */ - //&& !(updpar || upddata) - - && pb.mixer_control == 0 // only use this in SSBM - ) - { - // reset the detection values - pb.audio_addr.looping = 0; - pb.adpcm_loop_info.pred_scale = 0; - pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; + case CMD_PB_ADDR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + pb_addr = HILO_TO_32(addr); + break; - //pb.audio_addr.cur_addr_hi = 0; pb.audio_addr.cur_addr_lo = 0; - //pb.audio_addr.end_addr_hi = 0; pb.audio_addr.end_addr_lo = 0; - //pb.audio_addr.loop_addr_hi = 0; pb.audio_addr.loop_addr_lo = 0; + case CMD_PROCESS: + ProcessPBList(pb_addr); + break; - //pb.src.cur_addr_frac = 0; PBs[i].src.ratio_hi = 0; PBs[i].src.ratio_lo = 0; - //pb.adpcm.pred_scale = 0; pb.adpcm.yn1 = 0; pb.adpcm.yn2 = 0; - } -} + case CMD_MIX_AUXA: + case CMD_MIX_AUXB: + // These two commands are handled almost the same internally. + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2)); + break; -void CUCode_AX::MixAdd(short* _pBuffer, int _iSize) -{ - if (_iSize > 1024 * 1024) - _iSize = 1024 * 1024; + case CMD_UPLOAD_LRS: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + UploadLRS(HILO_TO_32(addr)); + break; - memset(templbuffer, 0, _iSize * sizeof(int)); - memset(temprbuffer, 0, _iSize * sizeof(int)); + case CMD_SET_LR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetMainLR(HILO_TO_32(addr)); + break; - AXPB PB; + case CMD_UNK_08: curr_idx += 10; break; // TODO: check - for (int x = 0; x < numPBaddr; x++) - { - //u32 blockAddr = m_addressPBs; - u32 blockAddr = PBaddr[x]; + case CMD_MIX_AUXB_NOWRITE: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + MixAUXSamples(false, 0, HILO_TO_32(addr)); + break; - if (!blockAddr) - return; + case CMD_COMPRESSOR_TABLE_ADDR: curr_idx += 2; break; + case CMD_UNK_0B: break; // TODO: check other versions + case CMD_UNK_0C: break; // TODO: check other versions - for (int i = 0; i < NUMBER_OF_PBS; i++) - { - if (!ReadPB(blockAddr, PB)) + case CMD_MORE: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + size = m_cmdlist[curr_idx++]; + + CopyCmdList(HILO_TO_32(addr), size); + curr_idx = 0; break; - if (m_CRC != 0x3389a79e) - VoiceHacks(PB); + case CMD_OUTPUT: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr)); + break; - MixAddVoice(PB, templbuffer, temprbuffer, _iSize); + case CMD_END: + end = true; + break; - if (!WritePB(blockAddr, PB)) + case CMD_MIX_AUXB_LR: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + MixAUXBLR(HILO_TO_32(addr), HILO_TO_32(addr2)); break; - // next PB, or done - blockAddr = (PB.next_pb_hi << 16) | PB.next_pb_lo; - if (!blockAddr) + case CMD_UNK_11: curr_idx += 2; break; + + case CMD_UNK_12: + { + u16 samp_val = m_cmdlist[curr_idx++]; + u16 idx = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + + // TODO + (void)samp_val; + (void)idx; + + break; + } + + // Send the contents of MAIN LRS, AUXA LRS and AUXB S to RAM, and + // mix data to MAIN LR and AUXB LR. + case CMD_SEND_AUX_AND_MIX: + { + // Address for Main + AUXA LRS upload + u16 main_auxa_up_hi = m_cmdlist[curr_idx++]; + u16 main_auxa_up_lo = m_cmdlist[curr_idx++]; + + // Address for AUXB S upload + u16 auxb_s_up_hi = m_cmdlist[curr_idx++]; + u16 auxb_s_up_lo = m_cmdlist[curr_idx++]; + + // Address to read data for Main L + u16 main_l_dl_hi = m_cmdlist[curr_idx++]; + u16 main_l_dl_lo = m_cmdlist[curr_idx++]; + + // Address to read data for Main R + u16 main_r_dl_hi = m_cmdlist[curr_idx++]; + u16 main_r_dl_lo = m_cmdlist[curr_idx++]; + + // Address to read data for AUXB L + u16 auxb_l_dl_hi = m_cmdlist[curr_idx++]; + u16 auxb_l_dl_lo = m_cmdlist[curr_idx++]; + + // Address to read data for AUXB R + u16 auxb_r_dl_hi = m_cmdlist[curr_idx++]; + u16 auxb_r_dl_lo = m_cmdlist[curr_idx++]; + + SendAUXAndMix(HILO_TO_32(main_auxa_up), HILO_TO_32(auxb_s_up), + HILO_TO_32(main_l_dl), HILO_TO_32(main_r_dl), + HILO_TO_32(auxb_l_dl), HILO_TO_32(auxb_r_dl)); + break; + } + + default: + ERROR_LOG(DSPHLE, "Unknown command in AX cmdlist: %04x", cmd); + end = true; break; } } +} + +static void ApplyUpdatesForMs(AXPB& pb, int curr_ms) +{ + u32 start_idx = 0; + for (int i = 0; i < curr_ms; ++i) + start_idx += pb.updates.num_updates[i]; - if (_pBuffer) + u32 update_addr = HILO_TO_32(pb.updates.data); + for (u32 i = start_idx; i < start_idx + pb.updates.num_updates[curr_ms]; ++i) { - for (int i = 0; i < _iSize; i++) - { - // Clamp into 16-bit. Maybe we should add a volume compressor here. - int left = templbuffer[i] + _pBuffer[0]; - int right = temprbuffer[i] + _pBuffer[1]; - if (left < -32767) left = -32767; - if (left > 32767) left = 32767; - if (right < -32767) right = -32767; - if (right > 32767) right = 32767; - *_pBuffer++ = left; - *_pBuffer++ = right; - } + u16 update_off = HLEMemory_Read_U16(update_addr + 4 * i); + u16 update_val = HLEMemory_Read_U16(update_addr + 4 * i + 2); + + ((u16*)&pb)[update_off] = update_val; } } - -// ------------------------------------------------------------------------------ -// Handle incoming mail -void CUCode_AX::HandleMail(u32 _uMail) +AXMixControl CUCode_AX::ConvertMixerControl(u32 mixer_control) { - if (m_UploadSetupInProgress) + u32 ret = 0; + + // TODO: find other UCode versions with different mixer_control values + if (m_CRC == 0x4e8a8b21) { - PrepareBootUCode(_uMail); - return; - } - else { - if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST) - { - // We are expected to get a new CmdBlock - DEBUG_LOG(DSPHLE, "GetNextCmdBlock (%ibytes)", (u16)_uMail); - } - else if (_uMail == 0xCDD10000) // Action 0 - AX_ResumeTask(); + ret |= MIX_L | MIX_R; + if (mixer_control & 0x0001) ret |= MIX_AUXA_L | MIX_AUXA_R; + if (mixer_control & 0x0002) ret |= MIX_AUXB_L | MIX_AUXB_R; + if (mixer_control & 0x0004) { - m_rMailHandler.PushMail(DSP_RESUME); + ret |= MIX_S; + if (ret & MIX_AUXA_L) ret |= MIX_AUXA_S; + if (ret & MIX_AUXB_L) ret |= MIX_AUXB_S; } - else if (_uMail == 0xCDD10001) // Action 1 - new ucode upload ( GC: BayBlade S.T.B,...) + if (mixer_control & 0x0008) { - DEBUG_LOG(DSPHLE,"DSP IROM - New Ucode!"); - // TODO find a better way to protect from HLEMixer? - soundStream->GetMixer()->SetHLEReady(false); - m_UploadSetupInProgress = true; + ret |= MIX_L_RAMP | MIX_R_RAMP; + if (ret & MIX_AUXA_L) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (ret & MIX_AUXB_L) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (ret & MIX_AUXA_S) ret |= MIX_AUXA_S_RAMP; + if (ret & MIX_AUXB_S) ret |= MIX_AUXB_S_RAMP; } - else if (_uMail == 0xCDD10002) // Action 2 - IROM_Reset(); ( GC: NFS Carbon, FF Crystal Chronicles,...) + } + else + { + if (mixer_control & 0x0001) ret |= MIX_L; + if (mixer_control & 0x0002) ret |= MIX_R; + if (mixer_control & 0x0004) ret |= MIX_S; + if (mixer_control & 0x0008) ret |= MIX_L_RAMP | MIX_R_RAMP | MIX_S_RAMP; + if (mixer_control & 0x0010) ret |= MIX_AUXA_L; + if (mixer_control & 0x0020) ret |= MIX_AUXA_R; + if (mixer_control & 0x0040) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (mixer_control & 0x0080) ret |= MIX_AUXA_S; + if (mixer_control & 0x0100) ret |= MIX_AUXA_S_RAMP; + if (mixer_control & 0x0200) ret |= MIX_AUXB_L; + if (mixer_control & 0x0400) ret |= MIX_AUXB_R; + if (mixer_control & 0x0800) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (mixer_control & 0x1000) ret |= MIX_AUXB_S; + if (mixer_control & 0x2000) ret |= MIX_AUXB_S_RAMP; + + // TODO: 0x4000 is used for Dolby Pro 2 sound mixing + } + + return (AXMixControl)ret; +} + +void CUCode_AX::SetupProcessing(u32 init_addr) +{ + u16 init_data[0x20]; + + for (u32 i = 0; i < 0x20; ++i) + init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); + + // List of all buffers we have to initialize + int* buffers[] = { + m_samples_left, + m_samples_right, + m_samples_surround, + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxA_surround, + m_samples_auxB_left, + m_samples_auxB_right, + m_samples_auxB_surround + }; + + u32 init_idx = 0; + for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) + { + s32 init_val = (s32)((init_data[init_idx] << 16) | init_data[init_idx + 1]); + s16 delta = (s16)init_data[init_idx + 2]; + + init_idx += 3; + + if (!init_val) + memset(buffers[i], 0, 5 * 32 * sizeof (int)); + else { - DEBUG_LOG(DSPHLE,"DSP IROM - Reset!"); - m_DSPHLE->SetUCode(UCODE_ROM); - return; + for (u32 j = 0; j < 32 * 5; ++j) + { + buffers[i][j] = init_val; + init_val += delta; + } } - else if (_uMail == 0xCDD10003) // Action 3 - AX_GetNextCmdBlock(); + } +} + +void CUCode_AX::DownloadAndMixWithVolume(u32 addr, u16 vol_main, u16 vol_auxa, u16 vol_auxb) +{ + int* buffers_main[3] = { m_samples_left, m_samples_right, m_samples_surround }; + int* buffers_auxa[3] = { m_samples_auxA_left, m_samples_auxA_right, m_samples_auxA_surround }; + int* buffers_auxb[3] = { m_samples_auxB_left, m_samples_auxB_right, m_samples_auxB_surround }; + int** buffers[3] = { buffers_main, buffers_auxa, buffers_auxb }; + u16 volumes[3] = { vol_main, vol_auxa, vol_auxb }; + + for (u32 i = 0; i < 3; ++i) + { + int* ptr = (int*)HLEMemory_Get_Pointer(addr); + u16 volume = volumes[i]; + for (u32 j = 0; j < 3; ++j) { + int* buffer = buffers[i][j]; + for (u32 k = 0; k < 5 * 32; ++k) + { + s64 sample = (s64)(s32)Common::swap32(*ptr++); + sample *= volume; + buffer[k] += (s32)(sample >> 15); + } } - else + } +} + +void CUCode_AX::ProcessPBList(u32 pb_addr) +{ + // Samples per millisecond. In theory DSP sampling rate can be changed from + // 32KHz to 48KHz, but AX always process at 32KHz. + const u32 spms = 32; + + AXPB pb; + + while (pb_addr) + { + AXBuffers buffers = {{ + m_samples_left, + m_samples_right, + m_samples_surround, + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxA_surround, + m_samples_auxB_left, + m_samples_auxB_right, + m_samples_auxB_surround + }}; + + if (!ReadPB(pb_addr, pb)) + break; + + for (int curr_ms = 0; curr_ms < 5; ++curr_ms) { - DEBUG_LOG(DSPHLE, " >>>> u32 MAIL : AXTask Mail (%08x)", _uMail); - AXTask(_uMail); + ApplyUpdatesForMs(pb, curr_ms); + + Process1ms(pb, buffers, ConvertMixerControl(pb.mixer_control)); + + // Forward the buffers + for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) + buffers.ptrs[i] += spms; } + + WritePB(pb_addr, pb); + pb_addr = HILO_TO_32(pb.next_pb); } } - -// ------------------------------------------------------------------------------ -// Update with DSP Interrupt -void CUCode_AX::Update(int cycles) +void CUCode_AX::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr) { - if (NeedsResumeMail()) + int* buffers[3] = { 0 }; + + switch (aux_id) { - m_rMailHandler.PushMail(DSP_RESUME); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + case 0: + buffers[0] = m_samples_auxA_left; + buffers[1] = m_samples_auxA_right; + buffers[2] = m_samples_auxA_surround; + break; + + case 1: + buffers[0] = m_samples_auxB_left; + buffers[1] = m_samples_auxB_right; + buffers[2] = m_samples_auxB_surround; + break; } - // check if we have to send something - else if (!m_rMailHandler.IsEmpty()) + + // First, we need to send the contents of our AUX buffers to the CPU. + if (write_addr) { - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + int* ptr = (int*)HLEMemory_Get_Pointer(write_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 5 * 32; ++j) + *ptr++ = Common::swap32(buffers[i][j]); } + + // Then, we read the new temp from the CPU and add to our current + // temp. + int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); + for (u32 i = 0; i < 5 * 32; ++i) + m_samples_left[i] += (int)Common::swap32(*ptr++); + for (u32 i = 0; i < 5 * 32; ++i) + m_samples_right[i] += (int)Common::swap32(*ptr++); + for (u32 i = 0; i < 5 * 32; ++i) + m_samples_surround[i] += (int)Common::swap32(*ptr++); } -// ============================================ -// AX seems to bootup one task only and waits for resume-callbacks -// everytime the DSP has "spare time" it sends a resume-mail to the CPU -// and the __DSPHandler calls a AX-Callback which generates a new AXFrame -bool CUCode_AX::AXTask(u32& _uMail) +void CUCode_AX::UploadLRS(u32 dst_addr) { - u32 uAddress = _uMail; - DEBUG_LOG(DSPHLE, "Begin"); - DEBUG_LOG(DSPHLE, "====================================================================="); - DEBUG_LOG(DSPHLE, "%08x : AXTask - AXCommandList-Addr:", uAddress); - - u32 Addr__AXStudio; - u32 Addr__AXOutSBuffer; - u32 Addr__AXOutSBuffer_1; - u32 Addr__AXOutSBuffer_2; - u32 Addr__A; - //u32 Addr__12; - u32 Addr__4_1; - u32 Addr__4_2; - //u32 Addr__4_3; - //u32 Addr__4_4; - u32 Addr__5_1; - u32 Addr__5_2; - u32 Addr__6; - u32 Addr__9; - - bool bExecuteList = true; - - numPBaddr = 0; - - while (bExecuteList) + int buffers[3][5 * 32]; + + for (u32 i = 0; i < 5 * 32; ++i) { - static int last_valid_command = 0; - u16 iCommand = HLEMemory_Read_U16(uAddress); - uAddress += 2; + buffers[0][i] = Common::swap32(m_samples_left[i]); + buffers[1][i] = Common::swap32(m_samples_right[i]); + buffers[2][i] = Common::swap32(m_samples_surround[i]); + } + memcpy(HLEMemory_Get_Pointer(dst_addr), buffers, sizeof (buffers)); +} - switch (iCommand) - { - case AXLIST_STUDIOADDR: //00 - Addr__AXStudio = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST studio address: %08x", uAddress, Addr__AXStudio); - break; +void CUCode_AX::SetMainLR(u32 src_addr) +{ + int* ptr = (int*)HLEMemory_Get_Pointer(src_addr); + for (u32 i = 0; i < 5 * 32; ++i) + { + int samp = (int)Common::swap32(*ptr++); + m_samples_left[i] = samp; + m_samples_right[i] = samp; + m_samples_surround[i] = 0; + } +} - case 0x001: // 2byte x 10 - { - u32 address = HLEMemory_Read_U32(uAddress); - uAddress += 4; - u16 param1 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - u16 param2 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - u16 param3 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 1: %08x, %04x, %04x, %04x", uAddress, address, param1, param2, param3); - } - break; +void CUCode_AX::OutputSamples(u32 lr_addr, u32 surround_addr) +{ + int surround_buffer[5 * 32]; - // - // Somewhere we should be getting a bitmask of AX_SYNC values - // that tells us what has been updated - // Dunno if important - // - case AXLIST_PBADDR: //02 - { - PBaddr[numPBaddr] = HLEMemory_Read_U32(uAddress); - numPBaddr++; - - // HACK: process updates right now instead of waiting until - // Premix is called. Some games using sequenced music (Tales of - // Symphonia for example) thought PBs were unused because we - // were too slow to update them and set them as running. This - // happens because Premix is basically completely desync-ed - // from the emulation core (it's running in the audio thread). - // Fixing this would require rewriting most of the AX HLE. - u32 block_addr = uAddress; - AXPB pb; - for (int i = 0; block_addr && i < NUMBER_OF_PBS; i++) - { - if (!ReadPB(block_addr, pb)) - break; - ProcessUpdates(pb); - WritePB(block_addr, pb); - block_addr = (pb.next_pb_hi << 16) | pb.next_pb_lo; - } - - m_addressPBs = HLEMemory_Read_U32(uAddress); // left in for now - uAddress += 4; - soundStream->GetMixer()->SetHLEReady(true); - DEBUG_LOG(DSPHLE, "%08x : AXLIST PB address: %08x", uAddress, m_addressPBs); - } - break; + for (u32 i = 0; i < 5 * 32; ++i) + surround_buffer[i] = Common::swap32(m_samples_surround[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); - case 0x0003: - DEBUG_LOG(DSPHLE, "%08x : AXLIST command 0x0003 ????", uAddress); - break; + // 32 samples per ms, 5 ms, 2 channels + short buffer[5 * 32 * 2]; - case 0x0004: // AUX? - Addr__4_1 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - Addr__4_2 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 4_1 4_2 addresses: %08x %08x", uAddress, Addr__4_1, Addr__4_2); - break; + // Output samples clamped to 16 bits and interlaced RLRLRLRLRL... + for (u32 i = 0; i < 5 * 32; ++i) + { + int left = m_samples_left[i]; + int right = m_samples_right[i]; - case 0x0005: - Addr__5_1 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - Addr__5_2 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 5_1 5_2 addresses: %08x %08x", uAddress, Addr__5_1, Addr__5_2); - break; + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; - case 0x0006: - Addr__6 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 6 address: %08x", uAddress, Addr__6); - break; + buffer[2 * i] = Common::swap16(right); + buffer[2 * i + 1] = Common::swap16(left); + } - case AXLIST_SBUFFER: - Addr__AXOutSBuffer = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST OutSBuffer address: %08x", uAddress, Addr__AXOutSBuffer); - break; + memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); +} - case 0x0009: - Addr__9 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST 6 address: %08x", uAddress, Addr__9); - break; +void CUCode_AX::MixAUXBLR(u32 ul_addr, u32 dl_addr) +{ + // Upload AUXB L/R + int* ptr = (int*)HLEMemory_Get_Pointer(ul_addr); + for (u32 i = 0; i < 5 * 32; ++i) + *ptr++ = Common::swap32(m_samples_auxB_left[i]); + for (u32 i = 0; i < 5 * 32; ++i) + *ptr++ = Common::swap32(m_samples_auxB_right[i]); + + // Mix AUXB L/R to MAIN L/R, and replace AUXB L/R + ptr = (int*)HLEMemory_Get_Pointer(dl_addr); + for (u32 i = 0; i < 5 * 32; ++i) + { + int samp = Common::swap32(*ptr++); + m_samples_auxB_left[i] = samp; + m_samples_left[i] += samp; + } + for (u32 i = 0; i < 5 * 32; ++i) + { + int samp = Common::swap32(*ptr++); + m_samples_auxB_right[i] = samp; + m_samples_right[i] += samp; + } +} - case AXLIST_COMPRESSORTABLE: // 0xa - Addr__A = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST CompressorTable address: %08x", uAddress, Addr__A); - break; +void CUCode_AX::SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, + u32 main_r_dl, u32 auxb_l_dl, u32 auxb_r_dl) +{ + // Buffers to upload first + int* up_buffers[] = { + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxA_surround + }; + + // Upload AUXA LRS + int* ptr = (int*)HLEMemory_Get_Pointer(main_auxa_up); + for (u32 i = 0; i < sizeof (up_buffers) / sizeof (up_buffers[0]); ++i) + for (u32 j = 0; j < 32 * 5; ++j) + *ptr++ = Common::swap32(up_buffers[i][j]); + + // Upload AUXB S + ptr = (int*)HLEMemory_Get_Pointer(auxb_s_up); + for (u32 i = 0; i < 32 * 5; ++i) + *ptr++ = Common::swap32(m_samples_auxB_surround[i]); + + // Download buffers and addresses + int* dl_buffers[] = { + m_samples_left, + m_samples_right, + m_samples_auxB_left, + m_samples_auxB_right + }; + u32 dl_addrs[] = { + main_l_dl, + main_r_dl, + auxb_l_dl, + auxb_r_dl + }; + + // Download and mix + for (u32 i = 0; i < sizeof (dl_buffers) / sizeof (dl_buffers[0]); ++i) + { + int* dl_src = (int*)HLEMemory_Get_Pointer(dl_addrs[i]); + for (u32 j = 0; j < 32 * 5; ++j) + dl_buffers[i][j] += (int)Common::swap32(*dl_src++); + } +} - case 0x000e: - Addr__AXOutSBuffer_1 = HLEMemory_Read_U32(uAddress); - uAddress += 4; +void CUCode_AX::HandleMail(u32 mail) +{ + // Indicates if the next message is a command list address. + static bool next_is_cmdlist = false; + static u16 cmdlist_size = 0; - // Addr__AXOutSBuffer_2 is the address in RAM that we are supposed to mix to. - // Although we don't, currently. - Addr__AXOutSBuffer_2 = HLEMemory_Read_U32(uAddress); - uAddress += 4; - DEBUG_LOG(DSPHLE, "%08x : AXLIST sbuf2 addresses: %08x %08x", uAddress, Addr__AXOutSBuffer_1, Addr__AXOutSBuffer_2); - break; + bool set_next_is_cmdlist = false; - case AXLIST_END: - bExecuteList = false; - DEBUG_LOG(DSPHLE, "%08x : AXLIST end", uAddress); - break; + // Wait for DSP processing to be done before answering any mail. This is + // safe to do because it matches what the DSP does on real hardware: there + // is no interrupt when a mail from CPU is received. + m_processing.lock(); - case 0x0010: //Super Monkey Ball 2 - DEBUG_LOG(DSPHLE, "%08x : AXLIST 0x0010", uAddress); - //should probably read/skip stuff here - uAddress += 8; - break; + if (next_is_cmdlist) + { + CopyCmdList(mail, cmdlist_size); + NotifyAXThread(); + } + else if (m_UploadSetupInProgress) + { + PrepareBootUCode(mail); + } + else if (mail == MAIL_RESUME) + { + // Acknowledge the resume request + m_rMailHandler.PushMail(DSP_RESUME); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } + else if (mail == MAIL_NEW_UCODE) + { + soundStream->GetMixer()->SetHLEReady(false); + m_UploadSetupInProgress = true; + } + else if (mail == MAIL_RESET) + { + m_DSPHLE->SetUCode(UCODE_ROM); + } + else if (mail == MAIL_CONTINUE) + { + // We don't have to do anything here - the CPU does not wait for a ACK + // and sends a cmdlist mail just after. + } + else if ((mail & MAIL_CMDLIST_MASK) == MAIL_CMDLIST) + { + // A command list address is going to be sent next. + set_next_is_cmdlist = true; + cmdlist_size = (u16)(mail & ~MAIL_CMDLIST_MASK); + } + else + { + ERROR_LOG(DSPHLE, "Unknown mail sent to AX::HandleMail: %08x", mail); + } - case 0x0011: - uAddress += 4; - break; + m_processing.unlock(); + next_is_cmdlist = set_next_is_cmdlist; +} - case 0x0012: - //Addr__12 = HLEMemory_Read_U16(uAddress); - uAddress += 2; - break; +void CUCode_AX::CopyCmdList(u32 addr, u16 size) +{ + if (size >= (sizeof (m_cmdlist) / sizeof (u16))) + { + ERROR_LOG(DSPHLE, "Command list at %08x is too large: size=%d", addr, size); + return; + } - case 0x0013: - uAddress += 6 * 4; // 6 Addresses. - break; + for (u32 i = 0; i < size; ++i, addr += 2) + m_cmdlist[i] = HLEMemory_Read_U16(addr); + m_cmdlist_size = size; +} - default: - { - static bool bFirst = true; - if (bFirst) - { - char szTemp[2048]; - sprintf(szTemp, "Unknown AX-Command 0x%x (address: 0x%08x). Last valid: %02x\n", - iCommand, uAddress - 2, last_valid_command); - int num = -32; - while (num < 64+32) - { - char szTemp2[128] = ""; - sprintf(szTemp2, "%s0x%04x\n", num == 0 ? ">>" : " ", HLEMemory_Read_U16(uAddress + num)); - strcat(szTemp, szTemp2); - num += 2; - } - - PanicAlert("%s", szTemp); - // bFirst = false; - } - - // unknown command so stop the execution of this TaskList - bExecuteList = false; - } - break; - } - if (bExecuteList) - last_valid_command = iCommand; - } - DEBUG_LOG(DSPHLE, "AXTask - done, send resume"); - DEBUG_LOG(DSPHLE, "====================================================================="); - DEBUG_LOG(DSPHLE, "End"); +void CUCode_AX::MixAdd(short* out_buffer, int nsamples) +{ + // Should never be called: we do not set HLE as ready. + // We accurately send samples to RAM instead of directly to the mixer. +} - m_rMailHandler.PushMail(DSP_YIELD); - return true; +void CUCode_AX::Update(int cycles) +{ + // Used for UCode switching. + if (NeedsResumeMail()) + { + m_rMailHandler.PushMail(DSP_RESUME); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } } -void CUCode_AX::DoState(PointerWrap &p) +void CUCode_AX::DoAXState(PointerWrap& p) { - std::lock_guard<std::mutex> lk(m_csMix); + p.Do(m_cmdlist); + p.Do(m_cmdlist_size); + + p.Do(m_samples_left); + p.Do(m_samples_right); + p.Do(m_samples_surround); + p.Do(m_samples_auxA_left); + p.Do(m_samples_auxA_right); + p.Do(m_samples_auxA_surround); + p.Do(m_samples_auxB_left); + p.Do(m_samples_auxB_right); + p.Do(m_samples_auxB_surround); +} - p.Do(numPBaddr); - p.Do(m_addressPBs); - p.Do(PBaddr); +void CUCode_AX::DoState(PointerWrap& p) +{ + std::lock_guard<std::mutex> lk(m_processing); DoStateShared(p); + DoAXState(p); } diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h index edb52a2801..8fd2a2af5f 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX.h @@ -12,52 +12,162 @@ // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ -// Official SVN repository and contact information can be found at +// Official Git repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#ifndef _UCODE_AX -#define _UCODE_AX +// High-level emulation for the AX Gamecube UCode. +// +// TODO: +// * Depop support +// * ITD support +// * Polyphase sample interpolation support (not very useful) +// * Dolby Pro 2 mixing with recent AX versions -#include <iostream> -#include "UCode_AXStructs.h" +#ifndef _UCODE_AX_H +#define _UCODE_AX_H -enum +#include "UCodes.h" +#include "UCode_AX_Structs.h" + +// We can't directly use the mixer_control field from the PB because it does +// not mean the same in all AX versions. The AX UCode converts the +// mixer_control value to an AXMixControl bitfield. +enum AXMixControl { - NUMBER_OF_PBS = 128 + MIX_L = 0x000001, + MIX_L_RAMP = 0x000002, + MIX_R = 0x000004, + MIX_R_RAMP = 0x000008, + MIX_S = 0x000010, + MIX_S_RAMP = 0x000020, + + MIX_AUXA_L = 0x000040, + MIX_AUXA_L_RAMP = 0x000080, + MIX_AUXA_R = 0x000100, + MIX_AUXA_R_RAMP = 0x000200, + MIX_AUXA_S = 0x000400, + MIX_AUXA_S_RAMP = 0x000800, + + MIX_AUXB_L = 0x001000, + MIX_AUXB_L_RAMP = 0x002000, + MIX_AUXB_R = 0x004000, + MIX_AUXB_R_RAMP = 0x008000, + MIX_AUXB_S = 0x010000, + MIX_AUXB_S_RAMP = 0x020000, + + MIX_AUXC_L = 0x040000, + MIX_AUXC_L_RAMP = 0x080000, + MIX_AUXC_R = 0x100000, + MIX_AUXC_R_RAMP = 0x200000, + MIX_AUXC_S = 0x400000, + MIX_AUXC_S_RAMP = 0x800000 }; -class CUCode_AX : public IUCode +class CUCode_AX : public IUCode { public: - CUCode_AX(DSPHLE *dsp_hle, u32 _CRC); + CUCode_AX(DSPHLE* dsp_hle, u32 crc); virtual ~CUCode_AX(); - void HandleMail(u32 _uMail); - void MixAdd(short* _pBuffer, int _iSize); - void Update(int cycles); - void DoState(PointerWrap &p); + virtual void HandleMail(u32 mail); + virtual void MixAdd(short* out_buffer, int nsamples); + virtual void Update(int cycles); + virtual void DoState(PointerWrap& p); - // PBs - u8 numPBaddr; - u32 PBaddr[8]; //2 needed for MP2 - u32 m_addressPBs; + // Needed because StdThread.h std::thread implem does not support member + // pointers. + static void SpawnAXThread(CUCode_AX* self); -private: - enum +protected: + enum MailType { - MAIL_AX_ALIST = 0xBABE0000, - AXLIST_STUDIOADDR = 0x0000, - AXLIST_PBADDR = 0x0002, - AXLIST_SBUFFER = 0x0007, - AXLIST_COMPRESSORTABLE = 0x000A, - AXLIST_END = 0x000F + MAIL_RESUME = 0xCDD10000, + MAIL_NEW_UCODE = 0xCDD10001, + MAIL_RESET = 0xCDD10002, + MAIL_CONTINUE = 0xCDD10003, + + // CPU sends 0xBABE0000 | cmdlist_size to the DSP + MAIL_CMDLIST = 0xBABE0000, + MAIL_CMDLIST_MASK = 0xFFFF0000 }; - int *templbuffer; - int *temprbuffer; + // 32 * 5 because 32 samples per millisecond, for max 5 milliseconds. + int m_samples_left[32 * 5]; + int m_samples_right[32 * 5]; + int m_samples_surround[32 * 5]; + int m_samples_auxA_left[32 * 5]; + int m_samples_auxA_right[32 * 5]; + int m_samples_auxA_surround[32 * 5]; + int m_samples_auxB_left[32 * 5]; + int m_samples_auxB_right[32 * 5]; + int m_samples_auxB_surround[32 * 5]; + + // Volatile because it's set by HandleMail and accessed in + // HandleCommandList, which are running in two different threads. + volatile u16 m_cmdlist[512]; + volatile u32 m_cmdlist_size; + + std::thread m_axthread; + + // Sync objects + std::mutex m_processing; + std::condition_variable m_cmdlist_cv; + std::mutex m_cmdlist_mutex; + + // Copy a command list from memory to our temp buffer + void CopyCmdList(u32 addr, u16 size); - // ax task message handler - bool AXTask(u32& _uMail); + // Convert a mixer_control bitfield to our internal representation for that + // value. Required because that bitfield has a different meaning in some + // versions of AX. + AXMixControl ConvertMixerControl(u32 mixer_control); + + // Send a notification to the AX thread to tell him a new cmdlist addr is + // available for processing. + void NotifyAXThread(); + + void AXThread(); + + virtual void HandleCommandList(); + + void SetupProcessing(u32 init_addr); + void DownloadAndMixWithVolume(u32 addr, u16 vol_main, u16 vol_auxa, u16 vol_auxb); + void ProcessPBList(u32 pb_addr); + void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr); + void UploadLRS(u32 dst_addr); + void SetMainLR(u32 src_addr); + void OutputSamples(u32 out_addr, u32 surround_addr); + void MixAUXBLR(u32 ul_addr, u32 dl_addr); + void SendAUXAndMix(u32 main_auxa_up, u32 auxb_s_up, u32 main_l_dl, + u32 main_r_dl, u32 auxb_l_dl, u32 auxb_r_dl); + + // Handle save states for main AX. + void DoAXState(PointerWrap& p); + +private: + enum CmdType + { + CMD_SETUP = 0x00, + CMD_DL_AND_VOL_MIX = 0x01, + CMD_PB_ADDR = 0x02, + CMD_PROCESS = 0x03, + CMD_MIX_AUXA = 0x04, + CMD_MIX_AUXB = 0x05, + CMD_UPLOAD_LRS = 0x06, + CMD_SET_LR = 0x07, + CMD_UNK_08 = 0x08, + CMD_MIX_AUXB_NOWRITE = 0x09, + CMD_COMPRESSOR_TABLE_ADDR = 0x0A, + CMD_UNK_0B = 0x0B, + CMD_UNK_0C = 0x0C, + CMD_MORE = 0x0D, + CMD_OUTPUT = 0x0E, + CMD_END = 0x0F, + CMD_MIX_AUXB_LR = 0x10, + CMD_UNK_11 = 0x11, + CMD_UNK_12 = 0x12, + CMD_SEND_AUX_AND_MIX = 0x13, + }; }; -#endif // _UCODE_AX +#endif // !_UCODE_AX_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp index 1bbf8a9838..f4effbba6d 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.cpp @@ -21,10 +21,10 @@ #include "Mixer.h" #include "UCodes.h" -#include "UCode_AXStructs.h" +#include "UCode_AXWii_Structs.h" #include "UCode_AX.h" // for some functions in CUCode_AX #include "UCode_AXWii.h" -#include "UCode_AX_Voice.h" +#include "UCode_AXWii_Voice.h" CUCode_AXWii::CUCode_AXWii(DSPHLE *dsp_hle, u32 l_CRC) diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h index 1e6cffcba0..dc07e71a63 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii.h @@ -18,7 +18,7 @@ #ifndef _UCODE_AXWII #define _UCODE_AXWII -#include "UCode_AXStructs.h" +#include "UCode_AXWii_Structs.h" #define NUMBER_OF_PBS 128 diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h index 9130bb9da4..9130bb9da4 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_ADPCM.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_ADPCM.h diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h index 7f082740de..7f082740de 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXStructs.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Structs.h diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h new file mode 100644 index 0000000000..55f7face27 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AXWii_Voice.h @@ -0,0 +1,271 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AXWII_VOICE_H +#define _UCODE_AXWII_VOICE_H + +#include "UCodes.h" +#include "UCode_AXWii_ADPCM.h" +#include "UCode_AX.h" +#include "Mixer.h" +#include "../../AudioInterface.h" + +// MRAM -> ARAM for GC +inline bool ReadPB(u32 addr, AXPB &PB) +{ + const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + u16* PB_in_aram = (u16*)&PB; + + for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) + { + PB_in_aram[p] = Common::swap16(PB_in_mram[p]); + } + + return true; +} + +// MRAM -> ARAM for Wii +inline bool ReadPB(u32 addr, AXPBWii &PB) +{ + const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + u16* PB_in_aram = (u16*)&PB; + + // preswap the mixer_control + PB.mixer_control = ((u32)PB_in_mram[7] << 16) | ((u32)PB_in_mram[6] >> 16); + + for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) + { + PB_in_aram[p] = Common::swap16(PB_in_mram[p]); + } + + return true; +} + +// ARAM -> MRAM for GC +inline bool WritePB(u32 addr, AXPB &PB) +{ + const u16* PB_in_aram = (const u16*)&PB; + u16* PB_in_mram = (u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + + for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) + { + PB_in_mram[p] = Common::swap16(PB_in_aram[p]); + } + + return true; +} + +// ARAM -> MRAM for Wii +inline bool WritePB(u32 addr, AXPBWii &PB) +{ + const u16* PB_in_aram = (const u16*)&PB; + u16* PB_in_mram = (u16*)Memory::GetPointer(addr); + if (PB_in_mram == NULL) + return false; + + // preswap the mixer_control + *(u32*)&PB_in_mram[6] = (PB.mixer_control << 16) | (PB.mixer_control >> 16); + + for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) + { + PB_in_mram[p] = Common::swap16(PB_in_aram[p]); + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// TODO: fix handling of gc/wii PB differences +// TODO: generally fix up the mess - looks crazy and kinda wrong +template<class ParamBlockType> +inline void MixAddVoice(ParamBlockType &pb, + int *templbuffer, int *temprbuffer, + int _iSize) +{ + if (pb.running) + { + const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) + * /*ratioFactor:*/((float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate())); + u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; + u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; + + u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo; + u32 frac = pb.src.cur_addr_frac; + + // ======================================================================================= + // Handle No-SRC streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0 + // and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This + // makes samplePos update in the correct way. I'm unsure how we are actually supposed to + // detect that this setting. Updates did not fix this automatically. + // --------------------------------------------------------------------------------------- + // Stream settings + // src_type = 2 (most other games have src_type = 0) + // Affected games: + // Baten Kaitos - Eternal Wings (2003) + // Baten Kaitos - Origins (2006)? + // Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps + // the sound format plays in to, Baten use ADPCM, SC2 use PCM16 + //if (pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + if (pb.running && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + { + pb.src.ratio_hi = 1; + } + + // ======================================================================================= + // Games that use looping to play non-looping music streams - SSBM has info in all + // pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams + // like any other looping streams the music works. I'm unsure how we are actually supposed to + // detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may + // identify these types of blocks. Updates did not write any looping values. + if ( + (pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2) + && pb.mixer_control == 0 && pb.adpcm_loop_info.pred_scale <= 0x7F + ) + { + pb.audio_addr.looping = 1; + } + + + + // Top Spin 3 Wii + if (pb.audio_addr.sample_format > 25) + pb.audio_addr.sample_format = 0; + + // ======================================================================================= + // Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to + // compensate for that. _iSize can be as low as 100 or as high as 2000 some cases. + for (int s = 0; s < _iSize; s++) + { + int sample = 0; + u32 oldFrac = frac; + frac += ratio; + u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac + + // ======================================================================================= + // Process sample format + switch (pb.audio_addr.sample_format) + { + case AUDIOFORMAT_PCM8: + pb.adpcm.yn2 = ((s8)DSP::ReadARAM(samplePos)) << 8; //current sample + pb.adpcm.yn1 = ((s8)DSP::ReadARAM(samplePos + 1)) << 8; //next sample + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_PCM16: + pb.adpcm.yn2 = (s16)(u16)((DSP::ReadARAM(samplePos * 2) << 8) | (DSP::ReadARAM((samplePos * 2 + 1)))); //current sample + pb.adpcm.yn1 = (s16)(u16)((DSP::ReadARAM((samplePos + 1) * 2) << 8) | (DSP::ReadARAM(((samplePos + 1) * 2 + 1)))); //next sample + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_ADPCM: + ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac); + + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn2; + else // linear interpolation + sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac) + pb.adpcm.yn2) >> 16; //adpcm moves on frac + + break; + + default: + break; + } + + // =================================================================== + // Overall volume control. In addition to this there is also separate volume settings to + // different channels (left, right etc). + frac &= 0xffff; + + int vol = pb.vol_env.cur_volume >> 9; + sample = sample * vol >> 8; + + if (pb.mixer_control & MIXCONTROL_RAMPING) + { + int x = pb.vol_env.cur_volume; + x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game + // that use this? Or how does it work? + if (x < 0) + x = 0; + if (x >= 0x7fff) + x = 0x7fff; + pb.vol_env.cur_volume = x; // maybe not per sample?? :P + } + + int leftmix = pb.mixer.left >> 5; + int rightmix = pb.mixer.right >> 5; + int left = sample * leftmix >> 8; + int right = sample * rightmix >> 8; + // adpcm has to walk from oldSamplePos to samplePos here + templbuffer[s] += left; + temprbuffer[s] += right; + + // Control the behavior when we reach the end of the sample + if (samplePos >= sampleEnd) + { + if (pb.audio_addr.looping == 1) + { + if ((samplePos & ~0x1f) == (sampleEnd & ~0x1f) || (pb.audio_addr.sample_format != AUDIOFORMAT_ADPCM)) + samplePos = loopPos; + if ((!pb.is_stream) && (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM)) + { + pb.adpcm.yn1 = pb.adpcm_loop_info.yn1; + pb.adpcm.yn2 = pb.adpcm_loop_info.yn2; + pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale; + } + } + else + { + pb.running = 0; + samplePos = loopPos; + //samplePos = samplePos - sampleEnd + loopPos; + memset(&pb.dpop, 0, sizeof(pb.dpop)); + memset(pb.src.last_samples, 0, 8); + break; + } + } + } // end of the _iSize loop + + // Update volume + pb.mixer.left = ADPCM_Vol(pb.mixer.left, pb.mixer.left_delta); + pb.mixer.right = ADPCM_Vol(pb.mixer.right, pb.mixer.right_delta); + + pb.src.cur_addr_frac = (u16)frac; + pb.audio_addr.cur_addr_hi = samplePos >> 16; + pb.audio_addr.cur_addr_lo = (u16)samplePos; + + } // if (pb.running) +} + +#endif diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h new file mode 100644 index 0000000000..c92196dd49 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Structs.h @@ -0,0 +1,392 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AX_STRUCTS_H +#define _UCODE_AX_STRUCTS_H + +struct PBMixer +{ + u16 left; + u16 left_delta; + u16 right; + u16 right_delta; + + u16 auxA_left; + u16 auxA_left_delta; + u16 auxA_right; + u16 auxA_right_delta; + + u16 auxB_left; + u16 auxB_left_delta; + u16 auxB_right; + u16 auxB_right_delta; + + u16 auxB_surround; + u16 auxB_surround_delta; + u16 surround; + u16 surround_delta; + u16 auxA_surround; + u16 auxA_surround_delta; +}; + +struct PBMixerWii +{ + // volume mixing values in .15, 0x8000 = ca. 1.0 + u16 left; + u16 left_delta; + u16 right; + u16 right_delta; + + u16 auxA_left; + u16 auxA_left_delta; + u16 auxA_right; + u16 auxA_right_delta; + + u16 auxB_left; + u16 auxB_left_delta; + u16 auxB_right; + u16 auxB_right_delta; + + // Note: the following elements usage changes a little in DPL2 mode + // TODO: implement and comment it in the mixer + u16 auxC_left; + u16 auxC_left_delta; + u16 auxC_right; + u16 auxC_right_delta; + + u16 surround; + u16 surround_delta; + u16 auxA_surround; + u16 auxA_surround_delta; + u16 auxB_surround; + u16 auxB_surround_delta; + u16 auxC_surround; + u16 auxC_surround_delta; +}; + +struct PBMixerWM +{ + u16 main0; + u16 main0_delta; + u16 aux0; + u16 aux0_delta; + + u16 main1; + u16 main1_delta; + u16 aux1; + u16 aux1_delta; + + u16 main2; + u16 main2_delta; + u16 aux2; + u16 aux2_delta; + + u16 main3; + u16 main3_delta; + u16 aux3; + u16 aux3_delta; +}; + +struct PBInitialTimeDelay +{ + u16 on; + u16 addrMemHigh; + u16 addrMemLow; + u16 offsetLeft; + u16 offsetRight; + u16 targetLeft; + u16 targetRight; +}; + +// Update data - read these each 1ms subframe and use them! +// It seems that to provide higher time precisions for MIDI events, some games +// use this thing to update the parameter blocks per 1ms sub-block (a block is 5ms). +// Using this data should fix games that are missing MIDI notes. +struct PBUpdates +{ + u16 num_updates[5]; + u16 data_hi; // These point to main RAM. Not sure about the structure of the data. + u16 data_lo; +}; + +// The DSP stores the final sample values for each voice after every frame of processing. +// The values are then accumulated for all dropped voices, added to the next frame of audio, +// and ramped down on a per-sample basis to provide a gentle "roll off." +struct PBDpop +{ + s16 left; + s16 auxA_left; + s16 auxB_left; + + s16 right; + s16 auxA_right; + s16 auxB_right; + + s16 surround; + s16 auxA_surround; + s16 auxB_surround; +}; + +struct PBDpopWii +{ + s16 left; + s16 auxA_left; + s16 auxB_left; + s16 auxC_left; + + s16 right; + s16 auxA_right; + s16 auxB_right; + s16 auxC_right; + + s16 surround; + s16 auxA_surround; + s16 auxB_surround; + s16 auxC_surround; +}; + +struct PBDpopWM +{ + s16 aMain0; + s16 aMain1; + s16 aMain2; + s16 aMain3; + + s16 aAux0; + s16 aAux1; + s16 aAux2; + s16 aAux3; +}; + +struct PBVolumeEnvelope +{ + u16 cur_volume; // volume at start of frame + s16 cur_volume_delta; // signed per sample delta (96 samples per frame) +}; + +struct PBUnknown2 +{ + u16 unknown_reserved[3]; +}; + +struct PBAudioAddr +{ + u16 looping; + u16 sample_format; + u16 loop_addr_hi; // Start of loop (this will point to a shared "zero" buffer if one-shot mode is active) + u16 loop_addr_lo; + u16 end_addr_hi; // End of sample (and loop), inclusive + u16 end_addr_lo; + u16 cur_addr_hi; + u16 cur_addr_lo; +}; + +struct PBADPCMInfo +{ + s16 coefs[16]; + u16 gain; + u16 pred_scale; + s16 yn1; + s16 yn2; +}; + +struct PBSampleRateConverter +{ + // ratio = (f32)ratio * 0x10000; + // valid range is 1/512 to 4.0000 + u16 ratio_hi; // integer part of sampling ratio + u16 ratio_lo; // fraction part of sampling ratio + u16 cur_addr_frac; + u16 last_samples[4]; +}; + +struct PBSampleRateConverterWM +{ + u16 currentAddressFrac; + u16 last_samples[4]; +}; + +struct PBADPCMLoopInfo +{ + u16 pred_scale; + u16 yn1; + u16 yn2; +}; + +struct PBLowPassFilter +{ + u16 enabled; + u16 yn1; + u16 a0; + u16 b0; +}; + +struct AXPB +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, ?, linear) + u16 coef_select; + u16 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixer mixer; + PBInitialTimeDelay initial_time_delay; + PBUpdates updates; + PBDpop dpop; + PBVolumeEnvelope vol_env; + PBUnknown2 unknown3; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + PBLowPassFilter lpf; + + u16 padding[25]; +}; + +struct PBBiquadFilter +{ + + u16 on; // on = 2, off = 0 + u16 xn1; // History data + u16 xn2; + u16 yn1; + u16 yn2; + u16 b0; // Filter coefficients + u16 b1; + u16 b2; + u16 a1; + u16 a2; + +}; + +union PBInfImpulseResponseWM +{ + PBLowPassFilter lpf; + PBBiquadFilter biquad; +}; + +struct AXPBWii +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, 4-tap, linear) + u16 coef_select; // coef for the 4-tap src + u16 mixer_control_hi; + u16 mixer_control_lo; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixerWii mixer; + PBInitialTimeDelay initial_time_delay; + PBDpopWii dpop; + PBVolumeEnvelope vol_env; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + PBLowPassFilter lpf; + PBBiquadFilter biquad; + + // WIIMOTE :D + u16 remote; + u16 remote_mixer_control; + + PBMixerWM remote_mixer; + PBDpopWM remote_dpop; + PBSampleRateConverterWM remote_src; + PBInfImpulseResponseWM remote_iir; + + u16 pad[12]; // align us, captain! (32B) +}; + +// Seems like nintendo used an early version of AXWii and forgot to remove the update functionality ;p +struct PBUpdatesWiiSports +{ + u16 num_updates[3]; + u16 data_hi; + u16 data_lo; +}; + +struct AXPBWiiSports +{ + u16 next_pb_hi; + u16 next_pb_lo; + u16 this_pb_hi; + u16 this_pb_lo; + + u16 src_type; // Type of sample rate converter (none, 4-tap, linear) + u16 coef_select; // coef for the 4-tap src + u32 mixer_control; + + u16 running; // 1=RUN 0=STOP + u16 is_stream; // 1 = stream, 0 = one shot + + PBMixerWii mixer; + PBInitialTimeDelay initial_time_delay; + PBUpdatesWiiSports updates; + PBDpopWii dpop; + PBVolumeEnvelope vol_env; + PBAudioAddr audio_addr; + PBADPCMInfo adpcm; + PBSampleRateConverter src; + PBADPCMLoopInfo adpcm_loop_info; + PBLowPassFilter lpf; + PBBiquadFilter biquad; + + // WIIMOTE :D + u16 remote; + u16 remote_mixer_control; + + PBMixerWM remote_mixer; + PBDpopWM remote_dpop; + PBSampleRateConverterWM remote_src; + PBInfImpulseResponseWM remote_iir; + + u16 pad[7]; // align us, captain! (32B) +}; + +// TODO: All these enums have changed a lot for wii +enum { + AUDIOFORMAT_ADPCM = 0, + AUDIOFORMAT_PCM8 = 0x19, + AUDIOFORMAT_PCM16 = 0xA, +}; + +enum { + SRCTYPE_POLYPHASE = 0, + SRCTYPE_LINEAR = 1, + SRCTYPE_NEAREST = 2, +}; + +// Both may be used at once +enum { + FILTER_LOWPASS = 1, + FILTER_BIQUAD = 2, +}; + +#endif // _UCODE_AX_STRUCTS_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h index bf5e4d9b6b..349dc7e03b 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_AX_Voice.h @@ -12,260 +12,389 @@ // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ -// Official SVN repository and contact information can be found at +// Official Git repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +// This file is UGLY (full of #ifdef) so that it can be used with both GC and +// Wii version of AX. Maybe it would be better to abstract away the parts that +// can be made common. + #ifndef _UCODE_AX_VOICE_H #define _UCODE_AX_VOICE_H -#include "UCodes.h" -#include "UCode_AX_ADPCM.h" -#include "UCode_AX.h" -#include "Mixer.h" -#include "../../AudioInterface.h" +#if !defined(AX_GC) && !defined(AX_WII) +#error UCode_AX_Voice.h included without specifying version +#endif -// MRAM -> ARAM for GC -inline bool ReadPB(u32 addr, AXPB &PB) -{ - const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); - if (PB_in_mram == NULL) - return false; - u16* PB_in_aram = (u16*)&PB; +#include "Common.h" +#include "UCode_AX_Structs.h" +#include "../../DSP.h" + +#ifdef AX_GC +# define PB_TYPE AXPB +#else +# define PB_TYPE AXPBWii +#endif + +// Put all of that in an anonymous namespace to avoid stupid compilers merging +// functions from AX GC and AX Wii. +namespace { + +// Useful macro to convert xxx_hi + xxx_lo to xxx for 32 bits. +#define HILO_TO_32(name) \ + ((name##_hi << 16) | name##_lo) - for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) +// Used to pass a large amount of buffers to the mixing function. +union AXBuffers +{ + struct { - PB_in_aram[p] = Common::swap16(PB_in_mram[p]); - } + int* left; + int* right; + int* surround; + + int* auxA_left; + int* auxA_right; + int* auxA_surround; + + int* auxB_left; + int* auxB_right; + int* auxB_surround; + +#ifdef AX_WII + int* auxC_left; + int* auxC_right; + int* auxC_surround; +#endif + }; - return true; -} +#ifdef AX_GC + int* ptrs[9]; +#else + int* ptrs[12]; +#endif +}; -// MRAM -> ARAM for Wii -inline bool ReadPB(u32 addr, AXPBWii &PB) +// Read a PB from MRAM/ARAM +bool ReadPB(u32 addr, PB_TYPE& pb) { - const u16* PB_in_mram = (const u16*)Memory::GetPointer(addr); - if (PB_in_mram == NULL) + u16* dst = (u16*)&pb; + const u16* src = (const u16*)Memory::GetPointer(addr); + if (!src) return false; - u16* PB_in_aram = (u16*)&PB; - - // preswap the mixer_control - PB.mixer_control = ((u32)PB_in_mram[7] << 16) | ((u32)PB_in_mram[6] >> 16); - for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) - { - PB_in_aram[p] = Common::swap16(PB_in_mram[p]); - } + for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) + dst[i] = Common::swap16(src[i]); return true; } -// ARAM -> MRAM for GC -inline bool WritePB(u32 addr, AXPB &PB) +// Write a PB back to MRAM/ARAM +bool WritePB(u32 addr, const PB_TYPE& pb) { - const u16* PB_in_aram = (const u16*)&PB; - u16* PB_in_mram = (u16*)Memory::GetPointer(addr); - if (PB_in_mram == NULL) + const u16* src = (const u16*)&pb; + u16* dst = (u16*)Memory::GetPointer(addr); + if (!dst) return false; - for (size_t p = 0; p < (sizeof(AXPB) >> 1); p++) - { - PB_in_mram[p] = Common::swap16(PB_in_aram[p]); - } + for (u32 i = 0; i < sizeof (pb) / sizeof (u16); ++i) + dst[i] = Common::swap16(src[i]); return true; } -// ARAM -> MRAM for Wii -inline bool WritePB(u32 addr, AXPBWii &PB) +// Dump the value of a PB for debugging +#define DUMP_U16(field) WARN_LOG(DSPHLE, " %04x (%s)", pb.field, #field) +#define DUMP_U32(field) WARN_LOG(DSPHLE, " %08x (%s)", HILO_TO_32(pb.field), #field) +void DumpPB(const PB_TYPE& pb) { - const u16* PB_in_aram = (const u16*)&PB; - u16* PB_in_mram = (u16*)Memory::GetPointer(addr); - if (PB_in_mram == NULL) - return false; + DUMP_U32(next_pb); + DUMP_U32(this_pb); + DUMP_U16(src_type); + DUMP_U16(coef_select); +#ifdef AX_GC + DUMP_U16(mixer_control); +#else + DUMP_U32(mixer_control); +#endif + DUMP_U16(running); + DUMP_U16(is_stream); - // preswap the mixer_control - *(u32*)&PB_in_mram[6] = (PB.mixer_control << 16) | (PB.mixer_control >> 16); + // TODO: complete as needed +} - for (size_t p = 0; p < (sizeof(AXPBWii) >> 1); p++) - { - PB_in_mram[p] = Common::swap16(PB_in_aram[p]); - } +// Simulated accelerator state. +static u32 acc_loop_addr, acc_end_addr; +static u32* acc_cur_addr; +static PB_TYPE* acc_pb; - return true; +// Sets up the simulated accelerator. +void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) +{ + acc_pb = pb; + acc_loop_addr = HILO_TO_32(pb->audio_addr.loop_addr); + acc_end_addr = HILO_TO_32(pb->audio_addr.end_addr); + acc_cur_addr = cur_addr; } -////////////////////////////////////////////////////////////////////////// -// TODO: fix handling of gc/wii PB differences -// TODO: generally fix up the mess - looks crazy and kinda wrong -template<class ParamBlockType> -inline void MixAddVoice(ParamBlockType &pb, - int *templbuffer, int *temprbuffer, - int _iSize) +// Reads a sample from the simulated accelerator. Also handles looping and +// disabling streams that reached the end (this is done by an exception raised +// by the accelerator on real hardware). +u16 AcceleratorGetSample() { - if (pb.running) + u16 ret; + + switch (acc_pb->audio_addr.sample_format) { - const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) - * /*ratioFactor:*/((float)AudioInterface::GetAIDSampleRate() / (float)soundStream->GetMixer()->GetSampleRate())); - u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; - u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; - - u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo; - u32 frac = pb.src.cur_addr_frac; - - // ======================================================================================= - // Handle No-SRC streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0 - // and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This - // makes samplePos update in the correct way. I'm unsure how we are actually supposed to - // detect that this setting. Updates did not fix this automatically. - // --------------------------------------------------------------------------------------- - // Stream settings - // src_type = 2 (most other games have src_type = 0) - // Affected games: - // Baten Kaitos - Eternal Wings (2003) - // Baten Kaitos - Origins (2006)? - // Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps - // the sound format plays in to, Baten use ADPCM, SC2 use PCM16 - //if (pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) - if (pb.running && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + case 0x00: // ADPCM { - pb.src.ratio_hi = 1; - } + // ADPCM decoding, not much to explain here. + if ((*acc_cur_addr & 15) == 0) + { + acc_pb->adpcm.pred_scale = DSP::ReadARAM((*acc_cur_addr & ~15) >> 1); + *acc_cur_addr += 2; + } - // ======================================================================================= - // Games that use looping to play non-looping music streams - SSBM has info in all - // pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams - // like any other looping streams the music works. I'm unsure how we are actually supposed to - // detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may - // identify these types of blocks. Updates did not write any looping values. - if ( - (pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2) - && pb.mixer_control == 0 && pb.adpcm_loop_info.pred_scale <= 0x7F - ) - { - pb.audio_addr.looping = 1; - } + int scale = 1 << (acc_pb->adpcm.pred_scale & 0xF); + int coef_idx = (acc_pb->adpcm.pred_scale >> 4) & 0x7; + + s32 coef1 = acc_pb->adpcm.coefs[coef_idx * 2 + 0]; + s32 coef2 = acc_pb->adpcm.coefs[coef_idx * 2 + 1]; + + int temp = (*acc_cur_addr & 1) ? + (DSP::ReadARAM(*acc_cur_addr >> 1) & 0xF) : + (DSP::ReadARAM(*acc_cur_addr >> 1) >> 4); + + if (temp >= 8) + temp -= 16; + int val = (scale * temp) + ((0x400 + coef1 * acc_pb->adpcm.yn1 + coef2 * acc_pb->adpcm.yn2) >> 11); + if (val > 0x7FFF) val = 0x7FFF; + else if (val < -0x7FFF) val = -0x7FFF; - // Top Spin 3 Wii - if (pb.audio_addr.sample_format > 25) - pb.audio_addr.sample_format = 0; + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = val; + *acc_cur_addr += 1; + ret = val; + break; + } + + case 0x0A: // 16-bit PCM audio + ret = (DSP::ReadARAM(*acc_cur_addr * 2) << 8) | DSP::ReadARAM(*acc_cur_addr * 2 + 1); + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = ret; + *acc_cur_addr += 1; + break; + + case 0x19: // 8-bit PCM audio + ret = DSP::ReadARAM(*acc_cur_addr) << 8; + acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; + acc_pb->adpcm.yn1 = ret; + *acc_cur_addr += 1; + break; + + default: + ERROR_LOG(DSPHLE, "Unknown sample format: %d", acc_pb->audio_addr.sample_format); + return 0; + } + + // Have we reached the end address? + // + // On real hardware, this would raise an interrupt that is handled by the + // UCode. We simulate what this interrupt does here. + if (*acc_cur_addr >= acc_end_addr) + { + // If we are really at the end (and we don't simply have cur_addr > + // end_addr all the time), loop back to loop_addr. + if ((*acc_cur_addr & ~0x1F) == (acc_end_addr & ~0x1F)) + *acc_cur_addr = acc_loop_addr; - // ======================================================================================= - // Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to - // compensate for that. _iSize can be as low as 100 or as high as 2000 some cases. - for (int s = 0; s < _iSize; s++) + if (acc_pb->audio_addr.looping) { - int sample = 0; - u32 oldFrac = frac; - frac += ratio; - u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac - - // ======================================================================================= - // Process sample format - switch (pb.audio_addr.sample_format) + // Set the ADPCM infos to continue processing at loop_addr. + // + // For some reason, yn1 and yn2 aren't set if the voice is not of + // stream type. This is what the AX UCode does and I don't really + // know why. + acc_pb->adpcm.pred_scale = acc_pb->adpcm_loop_info.pred_scale; + if (!acc_pb->is_stream) { - case AUDIOFORMAT_PCM8: - pb.adpcm.yn2 = ((s8)DSP::ReadARAM(samplePos)) << 8; //current sample - pb.adpcm.yn1 = ((s8)DSP::ReadARAM(samplePos + 1)) << 8; //next sample - - if (pb.src_type == SRCTYPE_NEAREST) - sample = pb.adpcm.yn2; - else // linear interpolation - sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; - - samplePos = newSamplePos; - break; - - case AUDIOFORMAT_PCM16: - pb.adpcm.yn2 = (s16)(u16)((DSP::ReadARAM(samplePos * 2) << 8) | (DSP::ReadARAM((samplePos * 2 + 1)))); //current sample - pb.adpcm.yn1 = (s16)(u16)((DSP::ReadARAM((samplePos + 1) * 2) << 8) | (DSP::ReadARAM(((samplePos + 1) * 2 + 1)))); //next sample - - if (pb.src_type == SRCTYPE_NEAREST) - sample = pb.adpcm.yn2; - else // linear interpolation - sample = (pb.adpcm.yn1 * (u16)oldFrac + pb.adpcm.yn2 * (u16)(0xFFFF - oldFrac) + pb.adpcm.yn2) >> 16; - - samplePos = newSamplePos; - break; - - case AUDIOFORMAT_ADPCM: - ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac); - - if (pb.src_type == SRCTYPE_NEAREST) - sample = pb.adpcm.yn2; - else // linear interpolation - sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac) + pb.adpcm.yn2) >> 16; //adpcm moves on frac - - break; - - default: - break; + acc_pb->adpcm.yn1 = acc_pb->adpcm_loop_info.yn1; + acc_pb->adpcm.yn2 = acc_pb->adpcm_loop_info.yn2; } + } + else + { + // Non looping voice reached the end -> running = 0. + acc_pb->running = 0; + } + } - // =================================================================== - // Overall volume control. In addition to this there is also separate volume settings to - // different channels (left, right etc). - frac &= 0xffff; + return ret; +} - int vol = pb.vol_env.cur_volume >> 9; - sample = sample * vol >> 8; +// Read 32 input samples from ARAM, decoding and converting rate if required. +void GetInputSamples(PB_TYPE& pb, s16* samples) +{ + u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr); + AcceleratorSetup(&pb, &cur_addr); - if (pb.mixer_control & MIXCONTROL_RAMPING) - { - int x = pb.vol_env.cur_volume; - x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game - // that use this? Or how does it work? - if (x < 0) - x = 0; - if (x >= 0x7fff) - x = 0x7fff; - pb.vol_env.cur_volume = x; // maybe not per sample?? :P - } + // TODO: support polyphase interpolation if coefficients are available. + if (pb.src_type == SRCTYPE_POLYPHASE || pb.src_type == SRCTYPE_LINEAR) + { + // Convert the input to a higher or lower sample rate using a linear + // interpolation algorithm. The input to output ratio is set in + // pb.src.ratio, which is a floating point num stored as a 32b integer: + // * Upper 16 bits of the ratio are the integer part + // * Lower 16 bits are the decimal part + u32 ratio = HILO_TO_32(pb.src.ratio); + + // We start getting samples not from sample 0, but 0.<cur_addr_frac>. + // This avoids discontinuties in the audio stream, especially with very + // low ratios which interpolate a lot of values between two "real" + // samples. + u32 curr_pos = pb.src.cur_addr_frac; + + // These are the two samples between which we interpolate. The initial + // values are stored in the PB, and we update them when resampling the + // input data. + s16 curr0 = pb.src.last_samples[2]; + s16 curr1 = pb.src.last_samples[3]; + + for (u32 i = 0; i < 32; ++i) + { + // Get our current fractional position, used to know how much of + // curr0 and how much of curr1 the output sample should be. + s32 curr_frac_pos = curr_pos & 0xFFFF; - int leftmix = pb.mixer.left >> 5; - int rightmix = pb.mixer.right >> 5; - int left = sample * leftmix >> 8; - int right = sample * rightmix >> 8; - // adpcm has to walk from oldSamplePos to samplePos here - templbuffer[s] += left; - temprbuffer[s] += right; + // Linear interpolation: s1 + (s2 - s1) * pos + s16 sample = curr0 + (s16)(((curr1 - curr0) * (s32)curr_frac_pos) >> 16); + samples[i] = sample; - // Control the behavior when we reach the end of the sample - if (samplePos >= sampleEnd) + curr_pos += ratio; + + // While our current position is >= 1.0, shift to the next 2 + // samples for interpolation. + while ((curr_pos >> 16) != 0) { - if (pb.audio_addr.looping == 1) - { - if ((samplePos & ~0x1f) == (sampleEnd & ~0x1f) || (pb.audio_addr.sample_format != AUDIOFORMAT_ADPCM)) - samplePos = loopPos; - if ((!pb.is_stream) && (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM)) - { - pb.adpcm.yn1 = pb.adpcm_loop_info.yn1; - pb.adpcm.yn2 = pb.adpcm_loop_info.yn2; - pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale; - } - } - else - { - pb.running = 0; - samplePos = loopPos; - //samplePos = samplePos - sampleEnd + loopPos; - memset(&pb.dpop, 0, sizeof(pb.dpop)); - memset(pb.src.last_samples, 0, 8); - break; - } + curr0 = curr1; + curr1 = AcceleratorGetSample(); + curr_pos -= 0x10000; } - } // end of the _iSize loop + } + + // Update the two last_samples values in the PB as well as the current + // position. + pb.src.last_samples[2] = curr0; + pb.src.last_samples[3] = curr1; + pb.src.cur_addr_frac = curr_pos & 0xFFFF; + } + else // SRCTYPE_NEAREST + { + // No sample rate conversion here: simply read 32 samples from the + // accelerator to the output buffer. + for (u32 i = 0; i < 32; ++i) + samples[i] = AcceleratorGetSample(); + + memcpy(pb.src.last_samples, samples + 28, 4 * sizeof (u16)); + } + + // Update current position in the PB. + pb.audio_addr.cur_addr_hi = (u16)(cur_addr >> 16); + pb.audio_addr.cur_addr_lo = (u16)(cur_addr & 0xFFFF); +} + +// Add samples to an output buffer, with optional volume ramping. +void MixAdd(int* out, const s16* input, u16* pvol, s16* dpop, bool ramp) +{ + u16& volume = pvol[0]; + u16 volume_delta = pvol[1]; + + // If volume ramping is disabled, set volume_delta to 0. That way, the + // mixing loop can avoid testing if volume ramping is enabled at each step, + // and just add volume_delta. + if (!ramp) + volume_delta = 0; - // Update volume - pb.mixer.left = ADPCM_Vol(pb.mixer.left, pb.mixer.left_delta); - pb.mixer.right = ADPCM_Vol(pb.mixer.right, pb.mixer.right_delta); + for (u32 i = 0; i < 32; ++i) + { + s64 sample = input[i]; + sample *= volume; + sample >>= 15; - pb.src.cur_addr_frac = (u16)frac; - pb.audio_addr.cur_addr_hi = samplePos >> 16; - pb.audio_addr.cur_addr_lo = (u16)samplePos; - - } // if (pb.running) + out[i] += (s16)sample; + volume += volume_delta; + + *dpop = (s16)sample; + } } +// Process 1ms of audio (32 samples) from a PB and mix it to the buffers. +void Process1ms(PB_TYPE& pb, const AXBuffers& buffers, AXMixControl mctrl) +{ + // If the voice is not running, nothing to do. + if (!pb.running) + return; + + // Read input samples, performing sample rate conversion if needed. + s16 samples[32]; + GetInputSamples(pb, samples); + + // Apply a global volume ramp using the volume envelope parameters. + for (u32 i = 0; i < 32; ++i) + { + s64 sample = 2 * (s16)samples[i] * (s16)pb.vol_env.cur_volume; + samples[i] = (s16)(sample >> 16); + pb.vol_env.cur_volume += pb.vol_env.cur_volume_delta; + } + + // Optionally, execute a low pass filter + if (pb.lpf.enabled) + { + // TODO + } + + // Mix LRS, AUXA and AUXB depending on mixer_control + // TODO: Handle DPL2 on AUXB. + + if (mctrl & MIX_L) + MixAdd(buffers.left, samples, &pb.mixer.left, &pb.dpop.left, mctrl & MIX_L_RAMP); + if (mctrl & MIX_R) + MixAdd(buffers.right, samples, &pb.mixer.right, &pb.dpop.right, mctrl & MIX_R_RAMP); + if (mctrl & MIX_S) + MixAdd(buffers.surround, samples, &pb.mixer.surround, &pb.dpop.surround, mctrl & MIX_S_RAMP); + + if (mctrl & MIX_AUXA_L) + MixAdd(buffers.auxA_left, samples, &pb.mixer.auxA_left, &pb.dpop.auxA_left, mctrl & MIX_AUXA_L_RAMP); + if (mctrl & MIX_AUXA_R) + MixAdd(buffers.auxA_right, samples, &pb.mixer.auxA_right, &pb.dpop.auxA_right, mctrl & MIX_AUXA_R_RAMP); + if (mctrl & MIX_AUXA_S) + MixAdd(buffers.auxA_surround, samples, &pb.mixer.auxA_surround, &pb.dpop.auxA_surround, mctrl & MIX_AUXA_S_RAMP); + + if (mctrl & MIX_AUXB_L) + MixAdd(buffers.auxB_left, samples, &pb.mixer.auxB_left, &pb.dpop.auxB_left, mctrl & MIX_AUXB_L_RAMP); + if (mctrl & MIX_AUXB_R) + MixAdd(buffers.auxB_right, samples, &pb.mixer.auxB_right, &pb.dpop.auxB_right, mctrl & MIX_AUXB_R_RAMP); + if (mctrl & MIX_AUXB_S) + MixAdd(buffers.auxB_surround, samples, &pb.mixer.auxB_surround, &pb.dpop.auxB_surround, mctrl & MIX_AUXB_S_RAMP); + +#ifdef AX_WII + if (mctrl & MIX_AUXC_L) + MixAdd(buffers.auxC_left, samples, &pb.mixer.auxC_left, &pb.dpop.auxC_left, mctrl & MIX_AUXC_L_RAMP); + if (mctrl & MIX_AUXC_R) + MixAdd(buffers.auxC_right, samples, &pb.mixer.auxC_right, &pb.dpop.auxC_right, mctrl & MIX_AUXC_R_RAMP); + if (mctrl & MIX_AUXC_S) + MixAdd(buffers.auxC_surround, samples, &pb.mixer.auxC_surround, &pb.dpop.auxC_surround, mctrl & MIX_AUXC_S_RAMP); #endif + + // Optionally, phase shift left or right channel to simulate 3D sound. + if (pb.initial_time_delay.on) + { + // TODO + } +} + +} // namespace + +#endif // !_UCODE_AX_VOICE_H diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp new file mode 100644 index 0000000000..40ad6e1947 --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.cpp @@ -0,0 +1,383 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "StringUtil.h" + +#include "../MailHandler.h" +#include "Mixer.h" + +#include "UCodes.h" +#include "UCode_AX_Structs.h" +#include "UCode_NewAXWii.h" + +#define AX_WII +#include "UCode_AX_Voice.h" + + +CUCode_NewAXWii::CUCode_NewAXWii(DSPHLE *dsp_hle, u32 l_CRC) + : CUCode_AX(dsp_hle, l_CRC) +{ + WARN_LOG(DSPHLE, "Instantiating CUCode_NewAXWii"); +} + +CUCode_NewAXWii::~CUCode_NewAXWii() +{ +} + +void CUCode_NewAXWii::HandleCommandList() +{ + // Temp variables for addresses computation + u16 addr_hi, addr_lo; + u16 addr2_hi, addr2_lo; + u16 volume; + +// WARN_LOG(DSPHLE, "Command list:"); +// for (u32 i = 0; m_cmdlist[i] != CMD_END; ++i) +// WARN_LOG(DSPHLE, "%04x", m_cmdlist[i]); +// WARN_LOG(DSPHLE, "-------------"); + + u32 curr_idx = 0; + bool end = false; + while (!end) + { + u16 cmd = m_cmdlist[curr_idx++]; + + switch (cmd) + { + // Some of these commands are unknown, or unused in this AX HLE. + // We still need to skip their arguments using "curr_idx += N". + + case CMD_SETUP: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + SetupProcessing(HILO_TO_32(addr)); + break; + + case CMD_UNK_01: curr_idx += 2; break; + case CMD_UNK_02: curr_idx += 2; break; + case CMD_UNK_03: curr_idx += 2; break; + + case CMD_PROCESS: + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + ProcessPBList(HILO_TO_32(addr)); + break; + + case CMD_MIX_AUXA: + case CMD_MIX_AUXB: + case CMD_MIX_AUXC: + volume = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + MixAUXSamples(cmd - CMD_MIX_AUXA, HILO_TO_32(addr), HILO_TO_32(addr2), volume); + break; + + // These two go together and manipulate some AUX buffers. + case CMD_UNK_08: curr_idx += 13; break; + case CMD_UNK_09: curr_idx += 13; break; + + case CMD_UNK_0A: curr_idx += 4; break; + + case CMD_OUTPUT: + volume = m_cmdlist[curr_idx++]; + addr_hi = m_cmdlist[curr_idx++]; + addr_lo = m_cmdlist[curr_idx++]; + addr2_hi = m_cmdlist[curr_idx++]; + addr2_lo = m_cmdlist[curr_idx++]; + OutputSamples(HILO_TO_32(addr2), HILO_TO_32(addr), volume); + break; + + case CMD_UNK_0C: curr_idx += 5; break; + + case CMD_WM_OUTPUT: + { + u32 addresses[4] = { + (u32)(m_cmdlist[curr_idx + 0] << 16) | m_cmdlist[curr_idx + 1], + (u32)(m_cmdlist[curr_idx + 2] << 16) | m_cmdlist[curr_idx + 3], + (u32)(m_cmdlist[curr_idx + 4] << 16) | m_cmdlist[curr_idx + 5], + (u32)(m_cmdlist[curr_idx + 6] << 16) | m_cmdlist[curr_idx + 7], + }; + curr_idx += 8; + OutputWMSamples(addresses); + break; + } + + case CMD_END: + end = true; + break; + } + } +} + +void CUCode_NewAXWii::SetupProcessing(u32 init_addr) +{ + // TODO: should be easily factorizable with AX + s16 init_data[60]; + + for (u32 i = 0; i < 60; ++i) + init_data[i] = HLEMemory_Read_U16(init_addr + 2 * i); + + // List of all buffers we have to initialize + struct { + int* ptr; + u32 samples; + } buffers[] = { + { m_samples_left, 32 }, + { m_samples_right, 32 }, + { m_samples_surround, 32 }, + { m_samples_auxA_left, 32 }, + { m_samples_auxA_right, 32 }, + { m_samples_auxA_surround, 32 }, + { m_samples_auxB_left, 32 }, + { m_samples_auxB_right, 32 }, + { m_samples_auxB_surround, 32 }, + { m_samples_auxC_left, 32 }, + { m_samples_auxC_right, 32 }, + { m_samples_auxC_surround, 32 }, + + { m_samples_wm0, 6 }, + { m_samples_aux0, 6 }, + { m_samples_wm1, 6 }, + { m_samples_aux1, 6 }, + { m_samples_wm2, 6 }, + { m_samples_aux2, 6 }, + { m_samples_wm3, 6 }, + { m_samples_aux3, 6 } + }; + + u32 init_idx = 0; + for (u32 i = 0; i < sizeof (buffers) / sizeof (buffers[0]); ++i) + { + s32 init_val = (s32)((init_data[init_idx] << 16) | init_data[init_idx + 1]); + s16 delta = (s16)init_data[init_idx + 2]; + + init_idx += 3; + + if (!init_val) + memset(buffers[i].ptr, 0, 3 * buffers[i].samples * sizeof (int)); + else + { + for (u32 j = 0; j < 3 * buffers[i].samples; ++j) + { + buffers[i].ptr[j] = init_val; + init_val += delta; + } + } + } +} + +AXMixControl CUCode_NewAXWii::ConvertMixerControl(u32 mixer_control) +{ + u32 ret = 0; + + if (mixer_control & 0x00000001) ret |= MIX_L; + if (mixer_control & 0x00000002) ret |= MIX_R; + if (mixer_control & 0x00000004) ret |= MIX_L_RAMP | MIX_R_RAMP; + if (mixer_control & 0x00000008) ret |= MIX_S; + if (mixer_control & 0x00000010) ret |= MIX_S_RAMP; + if (mixer_control & 0x00010000) ret |= MIX_AUXA_L; + if (mixer_control & 0x00020000) ret |= MIX_AUXA_R; + if (mixer_control & 0x00040000) ret |= MIX_AUXA_L_RAMP | MIX_AUXA_R_RAMP; + if (mixer_control & 0x00080000) ret |= MIX_AUXA_S; + if (mixer_control & 0x00100000) ret |= MIX_AUXA_S_RAMP; + if (mixer_control & 0x00200000) ret |= MIX_AUXB_L; + if (mixer_control & 0x00400000) ret |= MIX_AUXB_R; + if (mixer_control & 0x00800000) ret |= MIX_AUXB_L_RAMP | MIX_AUXB_R_RAMP; + if (mixer_control & 0x01000000) ret |= MIX_AUXB_S; + if (mixer_control & 0x02000000) ret |= MIX_AUXB_S_RAMP; + if (mixer_control & 0x04000000) ret |= MIX_AUXC_L; + if (mixer_control & 0x08000000) ret |= MIX_AUXC_R; + if (mixer_control & 0x10000000) ret |= MIX_AUXC_L_RAMP | MIX_AUXC_R_RAMP; + if (mixer_control & 0x20000000) ret |= MIX_AUXC_S; + if (mixer_control & 0x40000000) ret |= MIX_AUXC_S_RAMP; + + return (AXMixControl)ret; +} + +void CUCode_NewAXWii::ProcessPBList(u32 pb_addr) +{ + const u32 spms = 32; + + AXPBWii pb; + + while (pb_addr) + { + AXBuffers buffers = {{ + m_samples_left, + m_samples_right, + m_samples_surround, + m_samples_auxA_left, + m_samples_auxA_right, + m_samples_auxA_surround, + m_samples_auxB_left, + m_samples_auxB_right, + m_samples_auxB_surround, + m_samples_auxC_left, + m_samples_auxC_right, + m_samples_auxC_surround + }}; + + if (!ReadPB(pb_addr, pb)) + break; + + for (int curr_ms = 0; curr_ms < 3; ++curr_ms) + { + Process1ms(pb, buffers, ConvertMixerControl(HILO_TO_32(pb.mixer_control))); + + // Forward the buffers + for (u32 i = 0; i < sizeof (buffers.ptrs) / sizeof (buffers.ptrs[0]); ++i) + buffers.ptrs[i] += spms; + } + + WritePB(pb_addr, pb); + pb_addr = HILO_TO_32(pb.next_pb); + } +} + +void CUCode_NewAXWii::MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume) +{ + int* buffers[3] = { 0 }; + int* main_buffers[3] = { + m_samples_left, + m_samples_right, + m_samples_surround + }; + + switch (aux_id) + { + case 0: + buffers[0] = m_samples_auxA_left; + buffers[1] = m_samples_auxA_right; + buffers[2] = m_samples_auxA_surround; + break; + + case 1: + buffers[0] = m_samples_auxB_left; + buffers[1] = m_samples_auxB_right; + buffers[2] = m_samples_auxB_surround; + break; + + case 2: + buffers[0] = m_samples_auxC_left; + buffers[1] = m_samples_auxC_right; + buffers[2] = m_samples_auxC_surround; + break; + } + + // Send the content of AUX buffers to the CPU + if (write_addr) + { + int* ptr = (int*)HLEMemory_Get_Pointer(write_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 3 * 32; ++j) + *ptr++ = Common::swap32(buffers[i][j]); + } + + // Then read the buffers from the CPU and add to our main buffers. + int* ptr = (int*)HLEMemory_Get_Pointer(read_addr); + for (u32 i = 0; i < 3; ++i) + for (u32 j = 0; j < 3 * 32; ++j) + { + s64 new_val = main_buffers[i][j] + Common::swap32(*ptr++); + main_buffers[i][j] = (new_val * volume) >> 15; + } +} + +void CUCode_NewAXWii::OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume) +{ + int surround_buffer[3 * 32] = { 0 }; + + for (u32 i = 0; i < 3 * 32; ++i) + surround_buffer[i] = Common::swap32(m_samples_surround[i]); + memcpy(HLEMemory_Get_Pointer(surround_addr), surround_buffer, sizeof (surround_buffer)); + + short buffer[3 * 32 * 2]; + + // Clamp internal buffers to 16 bits. + for (u32 i = 0; i < 3 * 32; ++i) + { + int left = m_samples_left[i]; + int right = m_samples_right[i]; + + // Apply global volume. Cast to s64 to avoid overflow. + left = ((s64)left * volume) >> 15; + right = ((s64)right * volume) >> 15; + + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; + + m_samples_left[i] = left; + m_samples_right[i] = right; + } + + for (u32 i = 0; i < 3 * 32; ++i) + { + buffer[2 * i] = Common::swap16(m_samples_left[i]); + buffer[2 * i + 1] = Common::swap16(m_samples_right[i]); + } + + memcpy(HLEMemory_Get_Pointer(lr_addr), buffer, sizeof (buffer)); +} + +void CUCode_NewAXWii::OutputWMSamples(u32* addresses) +{ + int* buffers[] = { + m_samples_wm0, + m_samples_wm1, + m_samples_wm2, + m_samples_wm3 + }; + + for (u32 i = 0; i < 4; ++i) + { + int* in = buffers[i]; + u16* out = (u16*)HLEMemory_Get_Pointer(addresses[i]); + for (u32 j = 0; j < 3 * 6; ++j) + { + int sample = in[j]; + if (sample < -32767) sample = -32767; + if (sample > 32767) sample = 32767; + out[j] = Common::swap16((u16)sample); + } + } +} + +void CUCode_NewAXWii::DoState(PointerWrap &p) +{ + std::lock_guard<std::mutex> lk(m_processing); + + DoStateShared(p); + DoAXState(p); + + p.Do(m_samples_auxC_left); + p.Do(m_samples_auxC_right); + p.Do(m_samples_auxC_surround); + + p.Do(m_samples_wm0); + p.Do(m_samples_wm1); + p.Do(m_samples_wm2); + p.Do(m_samples_wm3); + + p.Do(m_samples_aux0); + p.Do(m_samples_aux1); + p.Do(m_samples_aux2); + p.Do(m_samples_aux3); +} diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h new file mode 100644 index 0000000000..4c9bc5757c --- /dev/null +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCode_NewAXWii.h @@ -0,0 +1,80 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official Git repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_NEWAXWII_H +#define _UCODE_NEWAXWII_H + +#include "UCode_AX.h" + +class CUCode_NewAXWii : public CUCode_AX +{ +public: + CUCode_NewAXWii(DSPHLE *dsp_hle, u32 _CRC); + virtual ~CUCode_NewAXWii(); + + virtual void DoState(PointerWrap &p); + +protected: + int m_samples_auxC_left[32 * 3]; + int m_samples_auxC_right[32 * 3]; + int m_samples_auxC_surround[32 * 3]; + + // Wiimote buffers + int m_samples_wm0[6 * 3]; + int m_samples_aux0[6 * 3]; + int m_samples_wm1[6 * 3]; + int m_samples_aux1[6 * 3]; + int m_samples_wm2[6 * 3]; + int m_samples_aux2[6 * 3]; + int m_samples_wm3[6 * 3]; + int m_samples_aux3[6 * 3]; + + // Convert a mixer_control bitfield to our internal representation for that + // value. Required because that bitfield has a different meaning in some + // versions of AX. + AXMixControl ConvertMixerControl(u32 mixer_control); + + virtual void HandleCommandList(); + + void SetupProcessing(u32 init_addr); + void ProcessPBList(u32 pb_addr); + void MixAUXSamples(int aux_id, u32 write_addr, u32 read_addr, u16 volume); + void OutputSamples(u32 lr_addr, u32 surround_addr, u16 volume); + void OutputWMSamples(u32* addresses); // 4 addresses + +private: + enum CmdType + { + CMD_SETUP = 0x00, + CMD_UNK_01 = 0x01, + CMD_UNK_02 = 0x02, + CMD_UNK_03 = 0x03, + CMD_PROCESS = 0x04, + CMD_MIX_AUXA = 0x05, + CMD_MIX_AUXB = 0x06, + CMD_MIX_AUXC = 0x07, + CMD_UNK_08 = 0x08, + CMD_UNK_09 = 0x09, + CMD_UNK_0A = 0x0A, + CMD_OUTPUT = 0x0B, + CMD_UNK_0C = 0x0C, + CMD_WM_OUTPUT = 0x0D, + CMD_END = 0x0E + }; +}; + +#endif // _UCODE_AXWII diff --git a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp index c04bf41403..86773ad020 100644 --- a/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/Src/HW/DSPHLE/UCodes/UCodes.cpp @@ -19,6 +19,7 @@ #include "UCode_AX.h" #include "UCode_AXWii.h" +#include "UCode_NewAXWii.h" #include "UCode_Zelda.h" #include "UCode_ROM.h" #include "UCode_CARD.h" @@ -26,6 +27,12 @@ #include "UCode_GBA.h" #include "Hash.h" +#if 0 +# define AXWII CUCode_NewAXWii +#else +# define AXWII CUCode_AXWii +#endif + IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) { switch (_CRC) @@ -90,13 +97,13 @@ IUCode* UCodeFactory(u32 _CRC, DSPHLE *dsp_hle, bool bWii) case 0x4cc52064: // Bleach: Versus Crusade case 0xd9c4bf34: // WiiMenu INFO_LOG(DSPHLE, "CRC %08x: Wii - AXWii chosen", _CRC); - return new CUCode_AXWii(dsp_hle, _CRC); + return new AXWII(dsp_hle, _CRC); default: if (bWii) { PanicAlert("DSPHLE: Unknown ucode (CRC = %08x) - forcing AXWii.\n\nTry LLE emulator if this is homebrew.", _CRC); - return new CUCode_AXWii(dsp_hle, _CRC); + return new AXWII(dsp_hle, _CRC); } else { diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp index 0fb356c2d8..1c9a51bcbd 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp @@ -388,7 +388,7 @@ void ExecuteCommand(u32 _Address) INFO_LOG(WII_IPC_FILEIO, "IOP: Open File (Device=%s, ID=%08x, Mode=%i)", pDevice->GetDeviceName().c_str(), DeviceID, Mode); - if (Memory::Read_U32(_Address + 4) == DeviceID) + if (Memory::Read_U32(_Address + 4) == (u32)DeviceID) { g_FdMap[DeviceID] = pDevice; } diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp index f409c88c83..5e23819d7b 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp @@ -789,8 +789,8 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress) { static CWII_IPC_HLE_Device_usb_oh1_57e_305* s_Usb = GetUsbPointer(); bool* wiiMoteConnected = new bool[s_Usb->m_WiiMotes.size()]; - for(unsigned int i = 0; i < s_Usb->m_WiiMotes.size(); - i++) wiiMoteConnected[i] = s_Usb->m_WiiMotes[i].IsConnected(); + for (unsigned int i = 0; i < s_Usb->m_WiiMotes.size(); i++) + wiiMoteConnected[i] = s_Usb->m_WiiMotes[i].IsConnected(); std::string tContentFile(m_ContentFile.c_str()); WII_IPC_HLE_Interface::Reset(true); diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.h b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.h index 1afb01cf8d..c98338e39d 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.h +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.h @@ -31,7 +31,7 @@ struct NANDStat }; enum { - FS_RESULT_OK = 0,
+ FS_RESULT_OK = 0, FS_INVALID = -4, FS_DIRFILE_NOT_FOUND = -6, FS_RESULT_FATAL = -101, diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb.cpp index af10e98d60..29e3c64a7c 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb.cpp @@ -116,26 +116,26 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::DoState(PointerWrap &p) p.Do(m_EventQueue); m_acl_pool.DoState(p); - for (unsigned int i = 0; i < 4; i++)
- m_WiiMotes[i].DoState(p);
-
- // Reset the connection of real and hybrid wiimotes
- if (p.GetMode() == PointerWrap::MODE_READ && SConfig::GetInstance().m_WiimoteReconnectOnLoad)
- {
- for (unsigned int i = 0; i < 4; i++)
- {
- if (WIIMOTE_SRC_EMU == g_wiimote_sources[i] || WIIMOTE_SRC_NONE == g_wiimote_sources[i])
- continue;
- // TODO: Selectively clear real wiimote messages if possible. Or create a real wiimote channel and reporting mode pre-setup to vacate the need for m_WiimoteReconnectOnLoad.
- m_EventQueue.clear();
- if (!m_WiiMotes[i].IsInactive())
- {
- m_WiiMotes[i].Activate(false);
- m_WiiMotes[i].Activate(true);
- }
- else
- m_WiiMotes[i].Activate(false);
- }
+ for (unsigned int i = 0; i < 4; i++) + m_WiiMotes[i].DoState(p); + + // Reset the connection of real and hybrid wiimotes + if (p.GetMode() == PointerWrap::MODE_READ && SConfig::GetInstance().m_WiimoteReconnectOnLoad) + { + for (unsigned int i = 0; i < 4; i++) + { + if (WIIMOTE_SRC_EMU == g_wiimote_sources[i] || WIIMOTE_SRC_NONE == g_wiimote_sources[i]) + continue; + // TODO: Selectively clear real wiimote messages if possible. Or create a real wiimote channel and reporting mode pre-setup to vacate the need for m_WiimoteReconnectOnLoad. + m_EventQueue.clear(); + if (!m_WiiMotes[i].IsInactive()) + { + m_WiiMotes[i].Activate(false); + m_WiiMotes[i].Activate(true); + } + else + m_WiiMotes[i].Activate(false); + } } } diff --git a/Source/Core/Core/Src/PowerPC/PowerPC.cpp b/Source/Core/Core/Src/PowerPC/PowerPC.cpp index 170044b2e3..8a7b817212 100644 --- a/Source/Core/Core/Src/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/Src/PowerPC/PowerPC.cpp @@ -144,7 +144,7 @@ void Init(int cpu_core) #ifdef _WIN32 _control87(_PC_53, MCW_PC); #else - unsigned short _mode = 0; + unsigned short _mode; asm ("fstcw %0" : : "m" (_mode)); _mode = (_mode & ~FPU_PREC_MASK) | FPU_PREC_53; asm ("fldcw %0" : : "m" (_mode)); diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index d03cf7c6b1..858e05bbfd 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -8,7 +8,6 @@ set(LIBS core z sfml-network ${GTK2_LIBRARIES} - ${OPENGL_LIBRARIES} ${XRANDR_LIBRARIES} ${X11_LIBRARIES}) @@ -31,7 +30,8 @@ if(LIBAV_FOUND) endif() if(wxWidgets_FOUND) - set(SRCS Src/ARCodeAddEdit.cpp + set(SRCS + Src/ARCodeAddEdit.cpp Src/AboutDolphin.cpp Src/CheatsWindow.cpp Src/ConfigMain.cpp @@ -79,7 +79,27 @@ if(wxWidgets_FOUND) set(WXLIBS ${wxWidgets_LIBRARIES}) else() - set(SRCS Src/MainNoGUI.cpp) + set(SRCS + Src/MainNoGUI.cpp) +endif() + +if(USE_EGL) + set(SRCS ${SRCS} Src/GLInterface/EGL.cpp + Src/GLInterface/X11_Util.cpp) +else() + if(WIN32) + set(SRCS ${SRCS} Src/GLInterface/GLW.cpp) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + if(USE_WX) + set(SRCS ${SRCS} Src/GLInterface/WX.cpp) + else() + set(SRCS ${SRCS} Src/GLInterface/AGL.cpp) + endif() + else() + set(SRCS ${SRCS} Src/GLInterface/GLX.cpp + Src/GLInterface/X11_Util.cpp) + + endif() endif() if(WIN32) @@ -196,3 +216,5 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") else() install(TARGETS ${DOLPHIN_EXE} RUNTIME DESTINATION ${bindir}) endif() + +set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} ${DOLPHIN_EXE}) diff --git a/Source/Core/DolphinWX/Dolphin.vcxproj b/Source/Core/DolphinWX/Dolphin.vcxproj index 1556eb5687..0d80b1fa5b 100644 --- a/Source/Core/DolphinWX/Dolphin.vcxproj +++ b/Source/Core/DolphinWX/Dolphin.vcxproj @@ -130,7 +130,7 @@ </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..\..\Externals\GLew\include;..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link /> <PostBuildEvent> @@ -144,7 +144,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..\..\Externals\GLew\include;..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link /> <PostBuildEvent> @@ -158,7 +158,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..\..\Externals\GLew\include;..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <OpenMPSupport> </OpenMPSupport> </ClCompile> @@ -174,7 +174,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\InputUICommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..\..\Externals\GLew\include;..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\InputUICommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link /> <PostBuildEvent> @@ -188,7 +188,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..\..\Externals\GLew\include;..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <OpenMPSupport> </OpenMPSupport> </ClCompile> @@ -206,7 +206,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\InputUICommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..\..\Externals\GLew\include;..\Common\Src;..\VideoCommon\Src;..\AudioCommon\Src;..\Core\Src;..\Core\Src\PowerPC\JitCommon;..\DebuggerWX\Src;..\..\..\Externals\Bochs_disasm;..\InputCommon\Src;..\InputUICommon\Src;..\DiscIO\Src;..\..\..\Externals\SFML\include;..\..\..\Externals\wxWidgets3;..\..\..\Externals\wxWidgets3\include;..\..\..\Externals\CLRun\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link /> <PostBuildEvent> @@ -267,6 +267,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / <ClCompile Include="Src\PHackSettings.cpp" /> <ClCompile Include="Src\Debugger\RegisterView.cpp" /> <ClCompile Include="Src\Debugger\RegisterWindow.cpp" /> + <ClCompile Include="Src\GLInterface\WGL.cpp" /> <ClCompile Include="Src\stdafx.cpp"> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">Create</PrecompiledHeader> @@ -328,6 +329,9 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / <ClInclude Include="Src\WiimoteConfigDiag.h" /> <ClInclude Include="Src\WXInputBase.h" /> <ClInclude Include="Src\WxUtils.h" /> + <ClInclude Include="Src\GLInterface.h" /> + <ClInclude Include="Src\GLInterface\InterfaceBase.h" /> + <ClInclude Include="Src\GLInterface\WGL.h" /> </ItemGroup> <ItemGroup> <None Include="..\..\..\Installer\Dolphin.ico" /> diff --git a/Source/Core/DolphinWX/Src/AboutDolphin.cpp b/Source/Core/DolphinWX/Src/AboutDolphin.cpp index d13c12ee7f..5e1825b6ae 100644 --- a/Source/Core/DolphinWX/Src/AboutDolphin.cpp +++ b/Source/Core/DolphinWX/Src/AboutDolphin.cpp @@ -31,7 +31,7 @@ AboutDolphin::AboutDolphin(wxWindow *parent, wxWindowID id, wxBitmap(iDolphinLogo)); std::string Text = "Dolphin " SCM_DESC_STR "\n" - "Copyright (c) 2003-2011+ Dolphin Team\n" + "Copyright (c) 2003-2013+ Dolphin Team\n" "\n" "Branch: " SCM_BRANCH_STR "\n" "Revision: " SCM_REV_STR "\n" diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index c9408203ea..750696f6c7 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -365,8 +365,6 @@ CFrame::CFrame(wxFrame* parent, // --------------- // Manager - // wxAUI_MGR_LIVE_RESIZE does not exist in the wxWidgets 2.8.9 that comes with Ubuntu 9.04 - // Could just check for wxWidgets version if it becomes a problem. m_Mgr = new wxAuiManager(this, wxAUI_MGR_DEFAULT | wxAUI_MGR_LIVE_RESIZE); m_Mgr->AddPane(m_Panel, wxAuiPaneInfo() diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index fa326adecc..9207144554 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -50,6 +50,7 @@ Core::GetWindowHandle(). #include "LogConfigWindow.h" #include "FifoPlayerDlg.h" #include "WxUtils.h" +#include "Host.h" #include "ConfigManager.h" // Core #include "Core.h" @@ -96,6 +97,8 @@ extern "C" { #include "../resources/KDE.h" }; +bool confirmStop = false; + // Create menu items // --------------------- void CFrame::CreateMenu() @@ -1070,6 +1073,9 @@ void CFrame::DoPause() // Stop the emulation void CFrame::DoStop() { + if (confirmStop) + return; + m_bGameLoading = false; if (Core::GetState() != Core::CORE_UNINITIALIZED || m_RenderParent != NULL) @@ -1082,17 +1088,23 @@ void CFrame::DoStop() // Ask for confirmation in case the user accidentally clicked Stop / Escape if (SConfig::GetInstance().m_LocalCoreStartupParameter.bConfirmStop) { - wxMessageDialog *m_StopDlg = new wxMessageDialog( + Core::EState state = Core::GetState(); + confirmStop = true; + Core::SetState(Core::CORE_PAUSE); + wxMessageDialog m_StopDlg( this, _("Do you want to stop the current emulation?"), _("Please confirm..."), wxYES_NO | wxSTAY_ON_TOP | wxICON_EXCLAMATION, wxDefaultPosition); - int Ret = m_StopDlg->ShowModal(); - m_StopDlg->Destroy(); + int Ret = m_StopDlg.ShowModal(); + confirmStop = false; if (Ret != wxID_YES) + { + Core::SetState(state); return; + } } // TODO: Show the author/description dialog here @@ -1142,6 +1154,9 @@ void CFrame::DoStop() DoFullscreen(false); if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain) m_RenderFrame->Destroy(); + else + // Make sure the window is not longer set to stay on top + m_RenderFrame->SetWindowStyle(m_RenderFrame->GetWindowStyle() & ~wxSTAY_ON_TOP); m_RenderParent = NULL; // Clean framerate indications from the status bar. @@ -1454,6 +1469,7 @@ void CFrame::ConnectWiimote(int wm_idx, bool connect) wxString msg(wxString::Format(wxT("Wiimote %i %s"), wm_idx + 1, connect ? wxT("Connected") : wxT("Disconnected"))); Core::DisplayMessage(msg.ToAscii(), 3000); + Host_UpdateMainFrame(); } } diff --git a/Source/Core/DolphinWX/Src/GLInterface.h b/Source/Core/DolphinWX/Src/GLInterface.h new file mode 100644 index 0000000000..a81f17104c --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface.h @@ -0,0 +1,75 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _GLINTERFACE_H_ +#define _GLINTERFACE_H_ + +#include "Thread.h" + +#if defined(USE_EGL) && USE_EGL +#include "GLInterface/EGL.h" +#elif defined(USE_WX) && USE_WX +#include "GLInterface/WX.h" +#elif defined(__APPLE__) +#include "GLInterface/AGL.h" +#elif defined(_WIN32) +#include "GLInterface/WGL.h" +#elif defined(HAVE_X11) && HAVE_X11 +#include "GLInterface/GLX.h" +#endif + +typedef struct { +#if defined(USE_EGL) && USE_EGL // This is currently a X11/EGL implementation for desktop + int screen; + Display *dpy; + Display *evdpy; + Window win; + Window parent; + EGLSurface egl_surf; + EGLContext egl_ctx; + EGLDisplay egl_dpy; + XVisualInfo *vi; + XSetWindowAttributes attr; + std::thread xEventThread; + int x, y; + unsigned int width, height; +#elif defined(USE_WX) && USE_WX + wxGLCanvas *glCanvas; + wxGLContext *glCtxt; + wxPanel *panel; +#elif defined(__APPLE__) + NSWindow *cocoaWin; + NSOpenGLContext *cocoaCtx; +#elif defined(HAVE_X11) && HAVE_X11 + int screen; + Window win; + Window parent; + // dpy used for glx stuff, evdpy for window events etc. + // evdpy is to be used by XEventThread only + Display *dpy, *evdpy; + XVisualInfo *vi; + GLXContext ctx; + XSetWindowAttributes attr; + std::thread xEventThread; + int x, y; + unsigned int width, height; +#endif +} GLWindow; + +extern cInterfaceBase *GLInterface; +extern GLWindow GLWin; + +#endif diff --git a/Source/Core/DolphinWX/Src/GLInterface/AGL.cpp b/Source/Core/DolphinWX/Src/GLInterface/AGL.cpp new file mode 100644 index 0000000000..30ff4a31cf --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/AGL.cpp @@ -0,0 +1,123 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "VideoConfig.h" +#include "Host.h" +#include "RenderBase.h" + +#include "VertexShaderManager.h" +#include "../GLInterface.h" +#include "AGL.h" + +void cInterfaceAGL::Swap() +{ + [GLWin.cocoaCtx flushBuffer]; +} + +// Show the current FPS +void cInterfaceAGL::UpdateFPSDisplay(const char *text) +{ + [GLWin.cocoaWin setTitle: [NSString stringWithUTF8String: text]]; +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceAGL::Create(void *&window_handle) +{ + int _tx, _ty, _twidth, _theight; + Host_GetRenderWindowSize(_tx, _ty, _twidth, _theight); + + // Control window size and picture scaling + s_backbuffer_width = _twidth; + s_backbuffer_height = _theight; + + NSRect size; + NSUInteger style = NSMiniaturizableWindowMask; + NSOpenGLPixelFormatAttribute attr[2] = { NSOpenGLPFADoubleBuffer, 0 }; + NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] + initWithAttributes: attr]; + if (fmt == nil) { + ERROR_LOG(VIDEO, "failed to create pixel format"); + return NULL; + } + + GLWin.cocoaCtx = [[NSOpenGLContext alloc] + initWithFormat: fmt shareContext: nil]; + [fmt release]; + if (GLWin.cocoaCtx == nil) { + ERROR_LOG(VIDEO, "failed to create context"); + return NULL; + } + + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFullscreen) { + size = [[NSScreen mainScreen] frame]; + style |= NSBorderlessWindowMask; + } else { + size = NSMakeRect(_tx, _ty, _twidth, _theight); + style |= NSResizableWindowMask | NSTitledWindowMask; + } + + GLWin.cocoaWin = [[NSWindow alloc] initWithContentRect: size + styleMask: style backing: NSBackingStoreBuffered defer: NO]; + if (GLWin.cocoaWin == nil) { + ERROR_LOG(VIDEO, "failed to create window"); + return NULL; + } + + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFullscreen) { + CGDisplayCapture(CGMainDisplayID()); + [GLWin.cocoaWin setLevel: CGShieldingWindowLevel()]; + } + + [GLWin.cocoaCtx setView: [GLWin.cocoaWin contentView]]; + [GLWin.cocoaWin makeKeyAndOrderFront: nil]; + + return true; +} + +bool cInterfaceAGL::MakeCurrent() +{ + [GLWin.cocoaCtx makeCurrentContext]; + return true; +} + +// Update window width, size and etc. Called from Render.cpp +void cInterfaceAGL::Update() +{ + int width, height; + + width = [[GLWin.cocoaWin contentView] frame].size.width; + height = [[GLWin.cocoaWin contentView] frame].size.height; + if (width == s_backbuffer_width && height == s_backbuffer_height) + return; + + [GLWin.cocoaCtx setView: [GLWin.cocoaWin contentView]]; + [GLWin.cocoaCtx update]; + [GLWin.cocoaCtx makeCurrentContext]; + s_backbuffer_width = width; + s_backbuffer_height = height; +} + +// Close backend +void cInterfaceAGL::Shutdown() +{ + [GLWin.cocoaWin close]; + [GLWin.cocoaCtx clearDrawable]; + [GLWin.cocoaCtx release]; +} + + diff --git a/Source/Core/DolphinWX/Src/GLInterface/AGL.h b/Source/Core/DolphinWX/Src/GLInterface/AGL.h new file mode 100644 index 0000000000..e03256ab4e --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/AGL.h @@ -0,0 +1,37 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _INTERFACEAGL_H_ +#define _INTERFACEAGL_H_ + +#ifdef __APPLE__ +#include <GL/glew.h> +#import <AppKit/AppKit.h> +#endif + +#include "InterfaceBase.h" + +class cInterfaceAGL : public cInterfaceBase +{ +public: + void Swap(); + void UpdateFPSDisplay(const char *Text); + bool Create(void *&window_handle); + bool MakeCurrent(); + void Shutdown(); +}; +#endif + diff --git a/Source/Core/DolphinWX/Src/GLInterface/EGL.cpp b/Source/Core/DolphinWX/Src/GLInterface/EGL.cpp new file mode 100644 index 0000000000..b8716ab467 --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/EGL.cpp @@ -0,0 +1,190 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Host.h" +#include "RenderBase.h" + +#include "../GLInterface.h" +#include "EGL.h" + +// Show the current FPS +void cInterfaceEGL::UpdateFPSDisplay(const char *text) +{ + XStoreName(GLWin.dpy, GLWin.win, text); +} +void cInterfaceEGL::Swap() +{ + eglSwapBuffers(GLWin.egl_dpy, GLWin.egl_surf); +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceEGL::Create(void *&window_handle) +{ + int _tx, _ty, _twidth, _theight; + Host_GetRenderWindowSize(_tx, _ty, _twidth, _theight); + + // Control window size and picture scaling + s_backbuffer_width = _twidth; + s_backbuffer_height = _theight; + + const char *s; + EGLint egl_major, egl_minor; + + GLWin.dpy = XOpenDisplay(NULL); + + if (!GLWin.dpy) { + printf("Error: couldn't open display\n"); + return false; + } + + GLWin.egl_dpy = eglGetDisplay(GLWin.dpy); + if (!GLWin.egl_dpy) { + printf("Error: eglGetDisplay() failed\n"); + return false; + } + + if (!eglInitialize(GLWin.egl_dpy, &egl_major, &egl_minor)) { + printf("Error: eglInitialize() failed\n"); + return false; + } + + s = eglQueryString(GLWin.egl_dpy, EGL_VERSION); + printf("EGL_VERSION = %s\n", s); + + s = eglQueryString(GLWin.egl_dpy, EGL_VENDOR); + printf("EGL_VENDOR = %s\n", s); + + s = eglQueryString(GLWin.egl_dpy, EGL_EXTENSIONS); + printf("EGL_EXTENSIONS = %s\n", s); + + s = eglQueryString(GLWin.egl_dpy, EGL_CLIENT_APIS); + printf("EGL_CLIENT_APIS = %s\n", s); + + // attributes for a visual in RGBA format with at least + // 8 bits per color and a 24 bit depth buffer + int attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 24, +#ifdef USE_GLES + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, +#endif + EGL_NONE }; + + static const EGLint ctx_attribs[] = { +#ifdef USE_GLES + EGL_CONTEXT_CLIENT_VERSION, 2, +#endif + EGL_NONE + }; + + GLWin.evdpy = XOpenDisplay(NULL); + GLWin.parent = (Window)window_handle; + GLWin.screen = DefaultScreen(GLWin.dpy); + if (GLWin.parent == 0) + GLWin.parent = RootWindow(GLWin.dpy, GLWin.screen); + + XVisualInfo visTemplate; + int num_visuals; + EGLConfig config; + EGLint num_configs; + EGLint vid; + + if (!eglChooseConfig( GLWin.egl_dpy, attribs, &config, 1, &num_configs)) { + printf("Error: couldn't get an EGL visual config\n"); + exit(1); + } + + if (!eglGetConfigAttrib(GLWin.egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { + printf("Error: eglGetConfigAttrib() failed\n"); + exit(1); + } + + /* The X window visual must match the EGL config */ + visTemplate.visualid = vid; + GLWin.vi = XGetVisualInfo(GLWin.dpy, VisualIDMask, &visTemplate, &num_visuals); + if (!GLWin.vi) { + printf("Error: couldn't get X visual\n"); + exit(1); + } + + GLWin.x = _tx; + GLWin.y = _ty; + GLWin.width = _twidth; + GLWin.height = _theight; + + XWindow.CreateXWindow(); +#ifdef USE_GLES + eglBindAPI(EGL_OPENGL_ES_API); +#else + eglBindAPI(EGL_OPENGL_API); +#endif + GLWin.egl_ctx = eglCreateContext(GLWin.egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); + if (!GLWin.egl_ctx) { + printf("Error: eglCreateContext failed\n"); + exit(1); + } + + GLWin.egl_surf = eglCreateWindowSurface(GLWin.egl_dpy, config, GLWin.win, NULL); + if (!GLWin.egl_surf) { + printf("Error: eglCreateWindowSurface failed\n"); + exit(1); + } + + if (!eglMakeCurrent(GLWin.egl_dpy, GLWin.egl_surf, GLWin.egl_surf, GLWin.egl_ctx)) { + + printf("Error: eglMakeCurrent() failed\n"); + return false; + } + + + printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR)); + printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); + printf("GL_VERSION: %s\n", glGetString(GL_VERSION)); + printf("GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS)); + /* Set initial projection/viewing transformation. + * We can't be sure we'll get a ConfigureNotify event when the window + * first appears. + */ + glViewport(0, 0, (GLint) _twidth, (GLint) _theight); + window_handle = (void *)GLWin.win; + return true; +} + +bool cInterfaceEGL::MakeCurrent() +{ + return eglMakeCurrent(GLWin.egl_dpy, GLWin.egl_surf, GLWin.egl_surf, GLWin.egl_ctx); +} +// Close backend +void cInterfaceEGL::Shutdown() +{ + XWindow.DestroyXWindow(); + if (GLWin.egl_ctx && !eglMakeCurrent(GLWin.egl_dpy, GLWin.egl_surf, GLWin.egl_surf, GLWin.egl_ctx)) + NOTICE_LOG(VIDEO, "Could not release drawing context."); + if (GLWin.egl_ctx) + { + eglDestroyContext(GLWin.egl_dpy, GLWin.egl_ctx); + eglDestroySurface(GLWin.egl_dpy, GLWin.egl_surf); + eglTerminate(GLWin.egl_dpy); + GLWin.egl_ctx = NULL; + } +} + diff --git a/Source/Core/DolphinWX/Src/GLInterface/EGL.h b/Source/Core/DolphinWX/Src/GLInterface/EGL.h new file mode 100644 index 0000000000..c4b6a00b6c --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/EGL.h @@ -0,0 +1,44 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _INTERFACEGLX_H_ +#define _INTERFACEGLX_H_ + +#include <EGL/egl.h> +#ifdef USE_GLES +#include <GLES2/gl2.h> +#else +#include <GL/glxew.h> +#include <GL/gl.h> +#endif + +#include "X11_Util.h" +#include "InterfaceBase.h" + +class cInterfaceEGL : public cInterfaceBase +{ +private: + cX11Window XWindow; +public: + friend class cX11Window; + void Swap(); + void UpdateFPSDisplay(const char *Text); + bool Create(void *&window_handle); + bool MakeCurrent(); + void Shutdown(); +}; +#endif + diff --git a/Source/Core/DolphinWX/Src/GLInterface/GLX.cpp b/Source/Core/DolphinWX/Src/GLInterface/GLX.cpp new file mode 100644 index 0000000000..ef60873531 --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/GLX.cpp @@ -0,0 +1,151 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Host.h" +#include "RenderBase.h" +#include "VideoConfig.h" + +#include "../GLInterface.h" +#include "GLX.h" + +// Show the current FPS +void cInterfaceGLX::UpdateFPSDisplay(const char *text) +{ + XStoreName(GLWin.dpy, GLWin.win, text); +} +void cInterfaceGLX::Swap() +{ + glXSwapBuffers(GLWin.dpy, GLWin.win); +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceGLX::Create(void *&window_handle) +{ + int _tx, _ty, _twidth, _theight; + Host_GetRenderWindowSize(_tx, _ty, _twidth, _theight); + + // Control window size and picture scaling + s_backbuffer_width = _twidth; + s_backbuffer_height = _theight; + + int glxMajorVersion, glxMinorVersion; + + // attributes for a single buffered visual in RGBA format with at least + // 8 bits per color and a 24 bit depth buffer + int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + None}; + + // attributes for a double buffered visual in RGBA format with at least + // 8 bits per color and a 24 bit depth buffer + int attrListDbl[] = {GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_SAMPLE_BUFFERS_ARB, g_Config.iMultisampleMode != MULTISAMPLE_OFF?1:0, + GLX_SAMPLES_ARB, g_Config.iMultisampleMode != MULTISAMPLE_OFF?1:0, + None }; + + int attrListDefault[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None }; + + GLWin.dpy = XOpenDisplay(0); + GLWin.evdpy = XOpenDisplay(0); + GLWin.parent = (Window)window_handle; + GLWin.screen = DefaultScreen(GLWin.dpy); + if (GLWin.parent == 0) + GLWin.parent = RootWindow(GLWin.dpy, GLWin.screen); + + glXQueryVersion(GLWin.dpy, &glxMajorVersion, &glxMinorVersion); + NOTICE_LOG(VIDEO, "glX-Version %d.%d", glxMajorVersion, glxMinorVersion); + + // Get an appropriate visual + GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDbl); + if (GLWin.vi == NULL) + { + GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListSgl); + if (GLWin.vi != NULL) + { + ERROR_LOG(VIDEO, "Only single buffered visual!"); + } + else + { + GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDefault); + if (GLWin.vi == NULL) + { + ERROR_LOG(VIDEO, "Could not choose visual (glXChooseVisual)"); + return false; + } + } + } + else + NOTICE_LOG(VIDEO, "Got double buffered visual!"); + + // Create a GLX context. + GLWin.ctx = glXCreateContext(GLWin.dpy, GLWin.vi, 0, GL_TRUE); + if (!GLWin.ctx) + { + PanicAlert("Unable to create GLX context."); + return false; + } + + GLWin.x = _tx; + GLWin.y = _ty; + GLWin.width = _twidth; + GLWin.height = _theight; + + XWindow.CreateXWindow(); + window_handle = (void *)GLWin.win; + return true; +} + +bool cInterfaceGLX::MakeCurrent() +{ + // connect the glx-context to the window + #if defined(HAVE_WX) && (HAVE_WX) + Host_GetRenderWindowSize(GLWin.x, GLWin.y, + (int&)GLWin.width, (int&)GLWin.height); + XMoveResizeWindow(GLWin.dpy, GLWin.win, GLWin.x, GLWin.y, + GLWin.width, GLWin.height); + #endif + return glXMakeCurrent(GLWin.dpy, GLWin.win, GLWin.ctx); +} +// Close backend +void cInterfaceGLX::Shutdown() +{ + XWindow.DestroyXWindow(); + if (GLWin.ctx && !glXMakeCurrent(GLWin.dpy, None, NULL)) + NOTICE_LOG(VIDEO, "Could not release drawing context."); + if (GLWin.ctx) + { + glXDestroyContext(GLWin.dpy, GLWin.ctx); + XCloseDisplay(GLWin.dpy); + XCloseDisplay(GLWin.evdpy); + GLWin.ctx = NULL; + } +} + diff --git a/Source/Core/DolphinWX/Src/GLInterface/GLX.h b/Source/Core/DolphinWX/Src/GLInterface/GLX.h new file mode 100644 index 0000000000..73a6690ec5 --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/GLX.h @@ -0,0 +1,41 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _INTERFACEGLX_H_ +#define _INTERFACEGLX_H_ + +#include <GL/glxew.h> +#include <GL/gl.h> +#include <X11/Xlib.h> +#include <X11/keysym.h> + +#include "X11_Util.h" +#include "InterfaceBase.h" + +class cInterfaceGLX : public cInterfaceBase +{ +private: + cX11Window XWindow; +public: + friend class cX11Window; + void Swap(); + void UpdateFPSDisplay(const char *Text); + bool Create(void *&window_handle); + bool MakeCurrent(); + void Shutdown(); +}; +#endif + diff --git a/Source/Core/DolphinWX/Src/GLInterface/InterfaceBase.h b/Source/Core/DolphinWX/Src/GLInterface/InterfaceBase.h new file mode 100644 index 0000000000..3e4f10fcd4 --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/InterfaceBase.h @@ -0,0 +1,38 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _GLINTERFACEBASE_H_ +#define _GLINTERFACEBASE_H_ +class cInterfaceBase +{ +protected: + // Window dimensions. + u32 s_backbuffer_width; + u32 s_backbuffer_height; +public: + virtual void Swap() = 0; + virtual void UpdateFPSDisplay(const char *Text) = 0; + virtual bool Create(void *&window_handle) = 0; + virtual bool MakeCurrent() = 0; + virtual void Shutdown() = 0; + + virtual u32 GetBackBufferWidth() { return s_backbuffer_width; } + virtual u32 GetBackBufferHeight() { return s_backbuffer_height; } + virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; } + virtual void Update() { } + virtual bool PeekMessages() { return false; } +}; +#endif diff --git a/Source/Core/DolphinWX/Src/GLInterface/WGL.cpp b/Source/Core/DolphinWX/Src/GLInterface/WGL.cpp new file mode 100644 index 0000000000..c97cb48bdf --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/WGL.cpp @@ -0,0 +1,179 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "VideoConfig.h" +#include "Host.h" +#include "RenderBase.h" + +#include "VertexShaderManager.h" +#include "../GLInterface.h" +#include "WGL.h" + +#include "EmuWindow.h" +static HDC hDC = NULL; // Private GDI Device Context +static HGLRC hRC = NULL; // Permanent Rendering Context + +void cInterfaceWGL::Swap() +{ + SwapBuffers(hDC); +} + +// Draw messages on top of the screen +bool cInterfaceWGL::PeekMessages() +{ + // TODO: peekmessage + MSG msg; + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + return FALSE; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return TRUE; +} + +// Show the current FPS +void cInterfaceWGL::UpdateFPSDisplay(const char *text) +{ + TCHAR temp[512]; + swprintf_s(temp, sizeof(temp)/sizeof(TCHAR), _T("%hs"), text); + EmuWindow::SetWindowText(temp); +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceWGL::Create(void *&window_handle) +{ + int _tx, _ty, _twidth, _theight; + Host_GetRenderWindowSize(_tx, _ty, _twidth, _theight); + + // Control window size and picture scaling + s_backbuffer_width = _twidth; + s_backbuffer_height = _theight; + + window_handle = (void*)EmuWindow::Create((HWND)window_handle, GetModuleHandle(0), _T("Please wait...")); + if (window_handle == NULL) + { + Host_SysMessage("failed to create window"); + return false; + } + + // Show the window + EmuWindow::Show(); + + PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be + { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, // Version Number + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, // Must Support Double Buffering + PFD_TYPE_RGBA, // Request An RGBA Format + 32, // Select Our Color Depth + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 0, // 8bit Alpha Buffer + 0, // Shift Bit Ignored + 0, // No Accumulation Buffer + 0, 0, 0, 0, // Accumulation Bits Ignored + 24, // 24Bit Z-Buffer (Depth Buffer) + 8, // 8bit Stencil Buffer + 0, // No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; + + GLuint PixelFormat; // Holds The Results After Searching For A Match + + if (!(hDC=GetDC(EmuWindow::GetWnd()))) { + PanicAlert("(1) Can't create an OpenGL Device context. Fail."); + return false; + } + if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) { + PanicAlert("(2) Can't find a suitable PixelFormat."); + return false; + } + if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { + PanicAlert("(3) Can't set the PixelFormat."); + return false; + } + if (!(hRC = wglCreateContext(hDC))) { + PanicAlert("(4) Can't create an OpenGL rendering context."); + return false; + } + return true; +} + +bool cInterfaceWGL::MakeCurrent() +{ + return wglMakeCurrent(hDC, hRC) ? true : false; +} + +// Update window width, size and etc. Called from Render.cpp +void cInterfaceWGL::Update() +{ + RECT rcWindow; + if (!EmuWindow::GetParentWnd()) + { + // We are not rendering to a child window - use client size. + GetClientRect(EmuWindow::GetWnd(), &rcWindow); + } + else + { + // We are rendering to a child window - use parent size. + GetWindowRect(EmuWindow::GetParentWnd(), &rcWindow); + } + + // Get the new window width and height + // See below for documentation + int width = rcWindow.right - rcWindow.left; + int height = rcWindow.bottom - rcWindow.top; + + // If we are rendering to a child window + if (EmuWindow::GetParentWnd() != 0 && + (s_backbuffer_width != width || s_backbuffer_height != height) && + width >= 4 && height >= 4) + { + ::MoveWindow(EmuWindow::GetWnd(), 0, 0, width, height, FALSE); + s_backbuffer_width = width; + s_backbuffer_height = height; + } +} + +// Close backend +void cInterfaceWGL::Shutdown() +{ + if (hRC) + { + if (!wglMakeCurrent(NULL, NULL)) + NOTICE_LOG(VIDEO, "Could not release drawing context."); + + if (!wglDeleteContext(hRC)) + ERROR_LOG(VIDEO, "Release Rendering Context Failed."); + + hRC = NULL; + } + + if (hDC && !ReleaseDC(EmuWindow::GetWnd(), hDC)) + { + ERROR_LOG(VIDEO, "Release Device Context Failed."); + hDC = NULL; + } +} + + diff --git a/Source/Core/DolphinWX/Src/GLInterface/WGL.h b/Source/Core/DolphinWX/Src/GLInterface/WGL.h new file mode 100644 index 0000000000..184ddd475f --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/WGL.h @@ -0,0 +1,41 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _INTERFACEWGL_H_ +#define _INTERFACEWGL_H_ + +#ifdef _WIN32 +#define GLEW_STATIC +#include <GL/glew.h> +#include <GL/wglew.h> +#endif + +#include "InterfaceBase.h" + +class cInterfaceWGL : public cInterfaceBase +{ +public: + void Swap(); + void UpdateFPSDisplay(const char *Text); + bool Create(void *&window_handle); + bool MakeCurrent(); + void Shutdown(); + + void Update(); + bool PeekMessages(); +}; +#endif + diff --git a/Source/Core/DolphinWX/Src/GLInterface/WX.cpp b/Source/Core/DolphinWX/Src/GLInterface/WX.cpp new file mode 100644 index 0000000000..0db1f8d632 --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/WX.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "VideoConfig.h" +#include "Host.h" +#include "RenderBase.h" + +#include "VertexShaderManager.h" +#include "../GLInterface.h" +#include "WX.h" + +void cInterfaceWX::Swap() +{ + GLWin.glCanvas->SwapBuffers(); +} + +void cInterfaceWX::UpdateFPSDisplay(const char *text) +{ + // Handled by Host_UpdateTitle() +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceWX::Create(void *&window_handle) +{ + int _tx, _ty, _twidth, _theight; + Host_GetRenderWindowSize(_tx, _ty, _twidth, _theight); + + // Control window size and picture scaling + s_backbuffer_width = _twidth; + s_backbuffer_height = _theight; + + GLWin.panel = (wxPanel *)window_handle; + GLWin.glCanvas = new wxGLCanvas(GLWin.panel, wxID_ANY, NULL, + wxPoint(0, 0), wxSize(_twidth, _theight)); + GLWin.glCanvas->Show(true); + if (GLWin.glCtxt == NULL) // XXX dirty hack + GLWin.glCtxt = new wxGLContext(GLWin.glCanvas); +} + +bool cInterfaceWX::MakeCurrent() +{ + return GLWin.glCanvas->SetCurrent(*GLWin.glCtxt); +} + +// Update window width, size and etc. Called from Render.cpp +void cInterfaceWX::Update() +{ + int width, height; + + GLWin.panel->GetSize(&width, &height); + if (width == s_backbuffer_width && height == s_backbuffer_height) + return; + + GLWin.glCanvas->SetFocus(); + GLWin.glCanvas->SetSize(0, 0, width, height); + GLWin.glCtxt->SetCurrent(*GLWin.glCanvas); + s_backbuffer_width = width; + s_backbuffer_height = height; +} + +// Close backend +void cInterfaceWX::Shutdown() +{ + GLWin.glCanvas->Hide(); +} + + diff --git a/Source/Core/DolphinWX/Src/GLInterface/WX.h b/Source/Core/DolphinWX/Src/GLInterface/WX.h new file mode 100644 index 0000000000..7772f864e5 --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/WX.h @@ -0,0 +1,47 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _INTERFACEWX_H_ +#define _INTERFACEWX_H_ + +#if defined HAVE_X11 && HAVE_X11 +#include <GL/glxew.h> +#include <GL/gl.h> +#elif defined __APPLE__ +#include <GL/glew.h> +#import <AppKit/AppKit.h> +#endif + +#if defined USE_WX && USE_WX +#include "wx/wx.h" +#include "wx/glcanvas.h" +#endif + +#include "InterfaceBase.h" + +class cInterfaceWX : public cInterfaceBase +{ +public: + void Swap(); + void UpdateFPSDisplay(const char *Text); + bool Create(void *&window_handle); + bool MakeCurrent(); + void Shutdown(); + + void Update(); +}; +#endif + diff --git a/Source/Core/DolphinWX/Src/GLInterface/X11_Util.cpp b/Source/Core/DolphinWX/Src/GLInterface/X11_Util.cpp new file mode 100644 index 0000000000..9372c4d644 --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/X11_Util.cpp @@ -0,0 +1,213 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Host.h" +#include "RenderBase.h" +#include "VideoConfig.h" +#include "../GLInterface.h" +#include "VertexShaderManager.h" + +void cX11Window::CreateXWindow(void) +{ + Atom wmProtocols[1]; + + // Setup window attributes + GLWin.attr.colormap = XCreateColormap(GLWin.evdpy, + GLWin.parent, GLWin.vi->visual, AllocNone); + GLWin.attr.event_mask = KeyPressMask | StructureNotifyMask | FocusChangeMask; + GLWin.attr.background_pixel = BlackPixel(GLWin.evdpy, GLWin.screen); + GLWin.attr.border_pixel = 0; + + // Create the window + GLWin.win = XCreateWindow(GLWin.evdpy, GLWin.parent, + GLWin.x, GLWin.y, GLWin.width, GLWin.height, 0, + GLWin.vi->depth, InputOutput, GLWin.vi->visual, + CWBorderPixel | CWBackPixel | CWColormap | CWEventMask, &GLWin.attr); + wmProtocols[0] = XInternAtom(GLWin.evdpy, "WM_DELETE_WINDOW", True); + XSetWMProtocols(GLWin.evdpy, GLWin.win, wmProtocols, 1); + XSetStandardProperties(GLWin.evdpy, GLWin.win, "GPU", "GPU", None, NULL, 0, NULL); + XMapRaised(GLWin.evdpy, GLWin.win); + XSync(GLWin.evdpy, True); + + GLWin.xEventThread = std::thread(&cX11Window::XEventThread, this); +} + +void cX11Window::DestroyXWindow(void) +{ + XUnmapWindow(GLWin.dpy, GLWin.win); + GLWin.win = 0; + if (GLWin.xEventThread.joinable()) + GLWin.xEventThread.join(); + XFreeColormap(GLWin.evdpy, GLWin.attr.colormap); +} + +void cX11Window::XEventThread() +{ + // Free look variables + static bool mouseLookEnabled = false; + static bool mouseMoveEnabled = false; + static float lastMouse[2]; + while (GLWin.win) + { + XEvent event; + KeySym key; + for (int num_events = XPending(GLWin.evdpy); num_events > 0; num_events--) + { + XNextEvent(GLWin.evdpy, &event); + switch(event.type) { + case KeyPress: + key = XLookupKeysym((XKeyEvent*)&event, 0); + switch (key) + { + case XK_3: + OSDChoice = 1; + // Toggle native resolution + g_Config.iEFBScale = g_Config.iEFBScale + 1; + if (g_Config.iEFBScale > 7) g_Config.iEFBScale = 0; + break; + case XK_4: + OSDChoice = 2; + // Toggle aspect ratio + g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3; + break; + case XK_5: + OSDChoice = 3; + // Toggle EFB copy + if (!g_Config.bEFBCopyEnable || g_Config.bCopyEFBToTexture) + { + g_Config.bEFBCopyEnable ^= true; + g_Config.bCopyEFBToTexture = false; + } + else + { + g_Config.bCopyEFBToTexture = !g_Config.bCopyEFBToTexture; + } + break; + case XK_6: + OSDChoice = 4; + g_Config.bDisableFog = !g_Config.bDisableFog; + break; + default: + break; + } + if (g_Config.bFreeLook) + { + static float debugSpeed = 1.0f; + switch (key) + { + case XK_parenleft: + debugSpeed /= 2.0f; + break; + case XK_parenright: + debugSpeed *= 2.0f; + break; + case XK_w: + VertexShaderManager::TranslateView(0.0f, debugSpeed); + break; + case XK_s: + VertexShaderManager::TranslateView(0.0f, -debugSpeed); + break; + case XK_a: + VertexShaderManager::TranslateView(debugSpeed, 0.0f); + break; + case XK_d: + VertexShaderManager::TranslateView(-debugSpeed, 0.0f); + break; + case XK_r: + VertexShaderManager::ResetView(); + break; + } + } + break; + case ButtonPress: + if (g_Config.bFreeLook) + { + switch (event.xbutton.button) + { + case 2: // Middle button + lastMouse[0] = event.xbutton.x; + lastMouse[1] = event.xbutton.y; + mouseMoveEnabled = true; + break; + case 3: // Right button + lastMouse[0] = event.xbutton.x; + lastMouse[1] = event.xbutton.y; + mouseLookEnabled = true; + break; + } + } + break; + case ButtonRelease: + if (g_Config.bFreeLook) + { + switch (event.xbutton.button) + { + case 2: // Middle button + mouseMoveEnabled = false; + break; + case 3: // Right button + mouseLookEnabled = false; + break; + } + } + break; + case MotionNotify: + if (g_Config.bFreeLook) + { + if (mouseLookEnabled) + { + VertexShaderManager::RotateView((event.xmotion.x - lastMouse[0]) / 200.0f, + (event.xmotion.y - lastMouse[1]) / 200.0f); + lastMouse[0] = event.xmotion.x; + lastMouse[1] = event.xmotion.y; + } + + if (mouseMoveEnabled) + { + VertexShaderManager::TranslateView((event.xmotion.x - lastMouse[0]) / 50.0f, + (event.xmotion.y - lastMouse[1]) / 50.0f); + lastMouse[0] = event.xmotion.x; + lastMouse[1] = event.xmotion.y; + } + } + break; + case ConfigureNotify: + Window winDummy; + unsigned int borderDummy, depthDummy; + XGetGeometry(GLWin.evdpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y, + &GLWin.width, &GLWin.height, &borderDummy, &depthDummy); + GLInterface->SetBackBufferDimensions(GLWin.width, GLWin.height); + break; + case ClientMessage: + if ((unsigned long) event.xclient.data.l[0] == + XInternAtom(GLWin.evdpy, "WM_DELETE_WINDOW", False)) + Host_Message(WM_USER_STOP); + if ((unsigned long) event.xclient.data.l[0] == + XInternAtom(GLWin.evdpy, "RESIZE", False)) + XMoveResizeWindow(GLWin.evdpy, GLWin.win, + event.xclient.data.l[1], event.xclient.data.l[2], + event.xclient.data.l[3], event.xclient.data.l[4]); + break; + default: + break; + } + } + Common::SleepCurrentThread(20); + } +} + + diff --git a/Source/Core/DolphinWX/Src/GLInterface/X11_Util.h b/Source/Core/DolphinWX/Src/GLInterface/X11_Util.h new file mode 100644 index 0000000000..9e6f6d5e3c --- /dev/null +++ b/Source/Core/DolphinWX/Src/GLInterface/X11_Util.h @@ -0,0 +1,31 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _X11_UTIL_H_ +#define _X11_UTIL_H_ + +#include <X11/Xlib.h> +#include <X11/keysym.h> + +class cX11Window +{ +private: + void XEventThread(); +public: + void CreateXWindow(void); + void DestroyXWindow(void); +}; +#endif diff --git a/Source/Core/DolphinWX/Src/GameListCtrl.cpp b/Source/Core/DolphinWX/Src/GameListCtrl.cpp index 414138d41f..fbefb1c769 100644 --- a/Source/Core/DolphinWX/Src/GameListCtrl.cpp +++ b/Source/Core/DolphinWX/Src/GameListCtrl.cpp @@ -322,9 +322,11 @@ void CGameListCtrl::Update() } // Sort items by Title + if (!sorted) + last_column = 0; + sorted = false; wxListEvent event; event.m_col = SConfig::GetInstance().m_ListSort2; - last_column = 1; OnColumnClick(event); event.m_col = SConfig::GetInstance().m_ListSort; @@ -711,20 +713,25 @@ void CGameListCtrl::OnColumnClick(wxListEvent& event) if(event.GetColumn() != COLUMN_BANNER) { int current_column = event.GetColumn(); - - if (last_column == current_column) + if (sorted) { - last_sort = -last_sort; + if (last_column == current_column) + { + last_sort = -last_sort; + } + else + { + SConfig::GetInstance().m_ListSort2 = last_sort; + last_column = current_column; + last_sort = current_column; + } + SConfig::GetInstance().m_ListSort = last_sort; } else { - if (sorted) - SConfig::GetInstance().m_ListSort2 = last_sort; - last_column = current_column; last_sort = current_column; + last_column = current_column; } - if (sorted) - SConfig::GetInstance().m_ListSort = last_sort; caller = this; SortItems(wxListCompare, last_sort); } @@ -921,7 +928,8 @@ void CGameListCtrl::OnRightClick(wxMouseEvent& event) { if (selected_iso->IsCompressed()) popupMenu->Append(IDM_COMPRESSGCM, _("Decompress ISO...")); - else + else if (selected_iso->GetFileName().substr(selected_iso->GetFileName().find_last_of(".")) != ".ciso" + && selected_iso->GetFileName().substr(selected_iso->GetFileName().find_last_of(".")) != ".wbfs") popupMenu->Append(IDM_COMPRESSGCM, _("Compress ISO...")); } else popupMenu->Append(IDM_LIST_INSTALLWAD, _("Install to Wii Menu")); diff --git a/Source/Core/DolphinWX/Src/Main.cpp b/Source/Core/DolphinWX/Src/Main.cpp index a5b19eca4c..fc3e2c41a7 100644 --- a/Source/Core/DolphinWX/Src/Main.cpp +++ b/Source/Core/DolphinWX/Src/Main.cpp @@ -45,6 +45,15 @@ #include <wx/intl.h> +// Nvidia drivers >= v302 will check if the application exports a global +// variable named NvOptimusEnablement to know if it should run the app in high +// performance graphics mode or using the IGP. +#ifdef WIN32 +extern "C" { + __declspec(dllexport) DWORD NvOptimusEnablement = 1; +} +#endif + // ------------ // Main window diff --git a/Source/Core/VideoCommon/Src/BPFunctions.cpp b/Source/Core/VideoCommon/Src/BPFunctions.cpp index cddeae3000..b9a76ba7b6 100644 --- a/Source/Core/VideoCommon/Src/BPFunctions.cpp +++ b/Source/Core/VideoCommon/Src/BPFunctions.cpp @@ -236,7 +236,7 @@ bool GetConfig(const int &type) case CONFIG_DISABLEFOG: return g_ActiveConfig.bDisableFog; case CONFIG_SHOWEFBREGIONS: - return false; + return g_ActiveConfig.bShowEFBCopyRegions; default: PanicAlert("GetConfig Error: Unknown Config Type!"); return false; diff --git a/Source/Core/VideoCommon/Src/BPStructs.cpp b/Source/Core/VideoCommon/Src/BPStructs.cpp index 179196e412..0bde9cc613 100644 --- a/Source/Core/VideoCommon/Src/BPStructs.cpp +++ b/Source/Core/VideoCommon/Src/BPStructs.cpp @@ -34,9 +34,9 @@ using namespace BPFunctions; -u32 mapTexAddress; -bool mapTexFound; -int numWrites; +static u32 mapTexAddress; +static bool mapTexFound; +static int numWrites; extern volatile bool g_bSkipCurrentFrame; @@ -81,6 +81,9 @@ void BPWritten(const BPCmd& bp) just stuff geometry in them and don't put state changes there ---------------------------------------------------------------------------------------------------------------- */ + + // check for invalid state, else unneeded configuration are built + g_video_backend->CheckInvalidState(); // Debugging only, this lets you skip a bp update //static int times = 0; diff --git a/Source/Core/VideoCommon/Src/MainBase.cpp b/Source/Core/VideoCommon/Src/MainBase.cpp index cb6dc7ae5b..726ef71b38 100644 --- a/Source/Core/VideoCommon/Src/MainBase.cpp +++ b/Source/Core/VideoCommon/Src/MainBase.cpp @@ -180,6 +180,7 @@ void VideoBackendHardware::InitializeShared() memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); memset(&s_accessEFBArgs, 0, sizeof(s_accessEFBArgs)); s_AccessEFBResult = 0; + m_invalid = false; } // Run from the CPU thread @@ -198,16 +199,25 @@ void VideoBackendHardware::DoState(PointerWrap& p) // Refresh state. if (p.GetMode() == PointerWrap::MODE_READ) { - BPReload(); + m_invalid = true; RecomputeCachedArraybases(); // Clear all caches that touch RAM // (? these don't appear to touch any emulation state that gets saved. moved to on load only.) - TextureCache::Invalidate(); VertexLoaderManager::MarkAllDirty(); } } +void VideoBackendHardware::CheckInvalidState() { + if (m_invalid) + { + m_invalid = false; + + BPReload(); + TextureCache::Invalidate(); + } +} + void VideoBackendHardware::PauseAndLock(bool doLock, bool unpauseOnUnlock) { Fifo_PauseAndLock(doLock, unpauseOnUnlock); diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 7c47c35bb6..9fc18ef1bd 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -377,7 +377,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // // TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies? // TODO: Actually, it should be enough if the internal texture format matches... - if ((entry->type == TCET_NORMAL && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) + if ((entry->type == TCET_NORMAL && width == entry->virtual_width && height == entry->virtual_height && full_format == entry->format && entry->num_mipmaps == maxlevel) || (entry->type == TCET_EC_DYNAMIC && entry->native_width == width && entry->native_height == height)) { // reuse the texture @@ -396,14 +396,21 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, pcfmt = LoadCustomTexture(tex_hash, texformat, 0, width, height); if (pcfmt != PC_TEX_FMT_NONE) { - expandedWidth = width; - expandedHeight = height; + if (expandedWidth != width || expandedHeight != height) + { + expandedWidth = width; + expandedHeight = height; + + // If we thought we could reuse the texture before, make sure to delete it now! + delete entry; + entry = NULL; + } using_custom_texture = true; } } - // TODO: RGBA8 textures are stored non-continuously in tmem, that might cause problems when preloading is enabled - if (pcfmt == PC_TEX_FMT_NONE) + // TODO: RGBA8 textures are stored non-continuously in tmem, that might cause problems here when preloading is enabled + if (!using_custom_texture) pcfmt = TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); diff --git a/Source/Core/VideoCommon/Src/VertexManagerBase.cpp b/Source/Core/VideoCommon/Src/VertexManagerBase.cpp index 4118e3dcbd..5be72e4fcf 100644 --- a/Source/Core/VideoCommon/Src/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/Src/VertexManagerBase.cpp @@ -9,6 +9,7 @@ #include "NativeVertexFormat.h" #include "TextureCacheBase.h" #include "RenderBase.h" +#include "BPStructs.h" #include "VertexManagerBase.h" #include "VideoConfig.h" @@ -159,6 +160,9 @@ void VertexManager::AddVertices(int primitive, int numVertices) void VertexManager::Flush() { + // loading a state will invalidate BP, so check for it + g_video_backend->CheckInvalidState(); + g_vertex_manager->vFlush(); } |
