diff --git a/Source/Core/AudioCommon/Src/OpenALStream.cpp b/Source/Core/AudioCommon/Src/OpenALStream.cpp index 6b239a584c..7d1c45175b 100644 --- a/Source/Core/AudioCommon/Src/OpenALStream.cpp +++ b/Source/Core/AudioCommon/Src/OpenALStream.cpp @@ -200,6 +200,7 @@ void OpenALStream::SoundLoop() u64 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond(); unsigned int numSamples = (unsigned int)num_samples_to_render; + unsigned int minSamples = surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION) numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples; numSamples = m_mixer->Mix(realtimeBuffer, numSamples); @@ -236,9 +237,15 @@ void OpenALStream::SoundLoop() // Adjust SETTING_SEQUENCE_MS to balance between lag vs hollow audio soundTouch.setSetting(SETTING_SEQUENCE_MS, (int)(1 / (rate * rate))); soundTouch.setTempo(rate); + if (rate > 10) + { + soundTouch.clear(); + } } - unsigned int nSamples = soundTouch.receiveSamples(sampleBuffer, OAL_MAX_SAMPLES * SIZE_FLOAT * SURROUND_CHANNELS * OAL_MAX_BUFFERS); - if (nSamples > 0) + + unsigned int nSamples = soundTouch.receiveSamples(sampleBuffer, OAL_MAX_SAMPLES * SIZE_FLOAT * OAL_MAX_BUFFERS); + + if (nSamples > minSamples) { // Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer) if (iBuffersFilled == 0) diff --git a/Source/Core/Common/Src/CPUDetect.cpp b/Source/Core/Common/Src/CPUDetect.cpp index 65cd6dbd0b..282929e7b2 100644 --- a/Source/Core/Common/Src/CPUDetect.cpp +++ b/Source/Core/Common/Src/CPUDetect.cpp @@ -205,14 +205,7 @@ void CPUInfo::Detect() // Turn the cpu info into a string we can show std::string CPUInfo::Summarize() { - std::string sum; - if (num_cores == 1) - sum = StringFromFormat("%s, %i core", cpu_string, num_cores); - else - { - sum = StringFromFormat("%s, %i cores", cpu_string, num_cores); - if (HTT) sum += StringFromFormat(" (%i logical threads per physical core)", logical_cpu_count); - } + std::string sum(cpu_string); if (bSSE) sum += ", SSE"; if (bSSE2) sum += ", SSE2"; if (bSSE3) sum += ", SSE3"; diff --git a/Source/Core/Common/Src/FileUtil.cpp b/Source/Core/Common/Src/FileUtil.cpp index d74c99b7a2..d6ab33962a 100644 --- a/Source/Core/Common/Src/FileUtil.cpp +++ b/Source/Core/Common/Src/FileUtil.cpp @@ -42,6 +42,7 @@ #endif #include +#include #include #ifndef S_ISDIR @@ -196,8 +197,9 @@ bool CreateFullPath(const std::string &fullPath) // we're done, yay! if (position == fullPath.npos) return true; - - std::string subPath = fullPath.substr(0, position); + + // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") + std::string const subPath(fullPath.substr(0, position + 1)); if (!File::IsDirectory(subPath)) File::CreateDir(subPath); @@ -773,6 +775,24 @@ IOFile::~IOFile() Close(); } +IOFile::IOFile(IOFile&& other) + : m_file(NULL), m_good(true) +{ + Swap(other); +} + +IOFile& IOFile::operator=(IOFile other) +{ + Swap(other); + return *this; +} + +void IOFile::Swap(IOFile& other) +{ + std::swap(m_file, other.m_file); + std::swap(m_good, other.m_good); +} + bool IOFile::Open(const std::string& filename, const char openmode[]) { Close(); diff --git a/Source/Core/Common/Src/FileUtil.h b/Source/Core/Common/Src/FileUtil.h index 451b1b6929..2c8bf8f64b 100644 --- a/Source/Core/Common/Src/FileUtil.h +++ b/Source/Core/Common/Src/FileUtil.h @@ -150,7 +150,7 @@ bool ReadFileToString(bool text_file, const char *filename, std::string &str); // simple wrapper for cstdlib file functions to // hopefully will make error checking easier // and make forgetting an fclose() harder -class IOFile : NonCopyable +class IOFile { public: IOFile(); @@ -158,6 +158,11 @@ public: IOFile(const std::string& filename, const char openmode[]); ~IOFile(); + + IOFile(IOFile&& other); + IOFile& operator=(IOFile other); + + void Swap(IOFile& other); bool Open(const std::string& filename, const char openmode[]); bool Close(); @@ -212,6 +217,7 @@ public: void Clear() { m_good = true; std::clearerr(m_file); } private: + IOFile(const IOFile&) /*= delete*/; IOFile& operator=(const IOFile&) /*= delete*/; std::FILE* m_file; diff --git a/Source/Core/Core/Src/HW/EXI_DeviceGecko.cpp b/Source/Core/Core/Src/HW/EXI_DeviceGecko.cpp index 93b7cd5d09..26e220d7cf 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceGecko.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceGecko.cpp @@ -98,8 +98,8 @@ bool GeckoSockServer::GetAvailableSock(sf::SocketTCP &sock_to_fill) client_running = false; clientThread.join(); - recv_fifo = std::queue(); - send_fifo = std::queue(); + recv_fifo = std::deque(); + send_fifo = std::deque(); } clientThread = std::thread(std::mem_fun(&GeckoSockServer::ClientThread), this); client_count++; @@ -120,28 +120,39 @@ void GeckoSockServer::ClientThread() while (client_running) { - u8 data; - std::size_t got = 0; + bool did_nothing = true; { std::lock_guard lk(transfer_lock); - if (client.Receive((char*)&data, sizeof(data), got) - == sf::Socket::Disconnected) + // what's an ideal buffer size? + char data[128]; + std::size_t got = 0; + + if (client.Receive(&data[0], ARRAYSIZE(data), got) == sf::Socket::Disconnected) client_running = false; - if (got) - recv_fifo.push(data); - - if (send_fifo.size()) + + if (got != 0) { - if (client.Send((char*)&send_fifo.front(), sizeof(u8)) - == sf::Socket::Disconnected) + did_nothing = false; + + recv_fifo.insert(recv_fifo.end(), &data[0], &data[got]); + } + + if (!send_fifo.empty()) + { + did_nothing = false; + + std::vector packet(send_fifo.begin(), send_fifo.end()); + send_fifo.clear(); + + if (client.Send(&packet[0], packet.size()) == sf::Socket::Disconnected) client_running = false; - send_fifo.pop(); } } // unlock transfer - SLEEP(1); + if (did_nothing) + Common::YieldCPU(); } client.Close(); @@ -180,7 +191,7 @@ void CEXIGecko::ImmReadWrite(u32 &_uData, u32 _uSize) if (!recv_fifo.empty()) { _uData = 0x08000000 | (recv_fifo.front() << 16); - recv_fifo.pop(); + recv_fifo.pop_front(); } break; } @@ -190,7 +201,7 @@ void CEXIGecko::ImmReadWrite(u32 &_uData, u32 _uSize) case CMD_SEND: { std::lock_guard lk(transfer_lock); - send_fifo.push(_uData >> 20); + send_fifo.push_back(_uData >> 20); _uData = 0x04000000; break; } diff --git a/Source/Core/Core/Src/HW/EXI_DeviceGecko.h b/Source/Core/Core/Src/HW/EXI_DeviceGecko.h index 9b427e21c8..da055b3c8a 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceGecko.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceGecko.h @@ -20,6 +20,8 @@ #include "SFML/Network.hpp" #include "Thread.h" + +#include #include class GeckoSockServer @@ -36,8 +38,8 @@ public: std::thread clientThread; std::mutex transfer_lock; - std::queue send_fifo; - std::queue recv_fifo; + std::deque send_fifo; + std::deque recv_fifo; private: static int client_count; 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 1fe6986179..2d5c86f800 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp @@ -353,7 +353,7 @@ void ExecuteCommand(u32 _Address) Memory::GetString(DeviceName, Memory::Read_U32(_Address + 0xC)); - WARN_LOG(WII_IPC_HLE, "Tried to open %s as %d", DeviceName.c_str(), DeviceID); + WARN_LOG(WII_IPC_HLE, "Trying to open %s as %d", DeviceName.c_str(), DeviceID); if (DeviceID >= 0) { if (DeviceName.find("/dev/es") == 0) diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp index 2061a106e0..711d360874 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp @@ -28,17 +28,16 @@ static Common::replace_v replacements; -// This is used by several of the FileIO and /dev/fs functions -std::string HLE_IPC_BuildFilename(const char* _pFilename, int _size) +// This is used by several of the FileIO and /dev/fs functions +std::string HLE_IPC_BuildFilename(std::string path_wii, int _size) { std::string path_full = File::GetUserPath(D_WIIROOT_IDX); - std::string path_wii(_pFilename); if ((path_wii.length() > 0) && (path_wii[1] == '0')) path_full += std::string("/title"); // this looks and feel like a hack... // Replaces chars that FAT32 can't support with strings defined in /sys/replace - for (Common::replace_v::const_iterator i = replacements.begin(); i != replacements.end(); ++i) + for (auto i = replacements.begin(); i != replacements.end(); ++i) { for (size_t j = 0; (j = path_wii.find(i->first, j)) != path_wii.npos; ++j) path_wii.replace(j, 1, i->second); @@ -82,9 +81,8 @@ void HLE_IPC_CreateVirtualFATFilesystem() } } -CWII_IPC_HLE_Device_FileIO::CWII_IPC_HLE_Device_FileIO(u32 _DeviceID, const std::string& _rDeviceName) +CWII_IPC_HLE_Device_FileIO::CWII_IPC_HLE_Device_FileIO(u32 _DeviceID, const std::string& _rDeviceName) : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName, false) // not a real hardware - , m_pFileHandle(NULL) , m_Mode(0) , m_SeekPos(0) { @@ -108,12 +106,9 @@ bool CWII_IPC_HLE_Device_FileIO::Close(u32 _CommandAddress, bool _bForce) } bool CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode) -{ +{ m_Mode = _Mode; u32 ReturnValue = 0; - - // close the file handle if we get a reopen - //m_pFileHandle.Close(); static const char* const Modes[] = { @@ -122,20 +117,19 @@ bool CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode) "Write only", "Read and Write" }; - - m_Filename = std::string(HLE_IPC_BuildFilename(m_Name.c_str(), 64)); + m_filepath = HLE_IPC_BuildFilename(m_Name, 64); // The file must exist before we can open it // It should be created by ISFS_CreateFile, not here - if (File::Exists(m_Filename)) + if (File::Exists(m_filepath)) { INFO_LOG(WII_IPC_FILEIO, "FileIO: Open %s (%s == %08X)", m_Name.c_str(), Modes[_Mode], _Mode); ReturnValue = m_DeviceID; } else { - WARN_LOG(WII_IPC_FILEIO, "FileIO: Open (%s) failed - File doesn't exist %s", Modes[_Mode], m_Filename.c_str()); + WARN_LOG(WII_IPC_FILEIO, "FileIO: Open (%s) failed - File doesn't exist %s", Modes[_Mode], m_filepath.c_str()); ReturnValue = FS_FILE_NOT_EXIST; } @@ -145,55 +139,43 @@ bool CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode) return true; } - - -bool CWII_IPC_HLE_Device_FileIO::OpenFile() +File::IOFile CWII_IPC_HLE_Device_FileIO::OpenFile() { + const char* open_mode = ""; + switch (m_Mode) { case ISFS_OPEN_READ: - { - m_pFileHandle.Open(m_Filename, "rb"); + open_mode = "rb"; break; - } + case ISFS_OPEN_WRITE: - { - m_pFileHandle.Open(m_Filename, "r+b"); - break; - } case ISFS_OPEN_RW: - { - m_pFileHandle.Open(m_Filename, "r+b"); + open_mode = "r+b"; break; - } + default: - { PanicAlertT("FileIO: Unknown open mode : 0x%02x", m_Mode); break; } - } - return m_pFileHandle.IsOpen(); + + return File::IOFile(m_filepath, open_mode); } -void CWII_IPC_HLE_Device_FileIO::CloseFile() -{ - m_pFileHandle.Close(); -} - -bool CWII_IPC_HLE_Device_FileIO::Seek(u32 _CommandAddress) +bool CWII_IPC_HLE_Device_FileIO::Seek(u32 _CommandAddress) { u32 ReturnValue = FS_RESULT_FATAL; const u32 SeekPosition = Memory::Read_U32(_CommandAddress + 0xC); const u32 Mode = Memory::Read_U32(_CommandAddress + 0x10); - - if (OpenFile()) + if (auto file = OpenFile()) { ReturnValue = FS_RESULT_FATAL; - const u64 fileSize = m_pFileHandle.GetSize(); + const u64 fileSize = file.GetSize(); INFO_LOG(WII_IPC_FILEIO, "FileIO: Seek Pos: 0x%08x, Mode: %i (%s, Length=0x%08llx)", SeekPosition, Mode, m_Name.c_str(), fileSize); - switch (Mode){ + switch (Mode) + { case 0: { if (SeekPosition <= fileSize) @@ -230,7 +212,6 @@ bool CWII_IPC_HLE_Device_FileIO::Seek(u32 _CommandAddress) break; } } - CloseFile(); } else { @@ -248,18 +229,18 @@ bool CWII_IPC_HLE_Device_FileIO::Read(u32 _CommandAddress) const u32 Size = Memory::Read_U32(_CommandAddress + 0x10); - if (OpenFile()) + if (auto file = OpenFile()) { - if (m_Mode == ISFS_OPEN_WRITE) + if (m_Mode == ISFS_OPEN_WRITE) { WARN_LOG(WII_IPC_FILEIO, "FileIO: Attempted to read 0x%x bytes to 0x%08x on write-only file %s", Size, Address, m_Name.c_str()); } - else + else { INFO_LOG(WII_IPC_FILEIO, "FileIO: Read 0x%x bytes to 0x%08x from %s", Size, Address, m_Name.c_str()); - m_pFileHandle.Seek(m_SeekPos, SEEK_SET); - ReturnValue = (u32)fread(Memory::GetPointer(Address), 1, Size, m_pFileHandle.GetHandle()); - if (ReturnValue != Size && ferror(m_pFileHandle.GetHandle())) + file.Seek(m_SeekPos, SEEK_SET); + ReturnValue = (u32)fread(Memory::GetPointer(Address), 1, Size, file.GetHandle()); + if (ReturnValue != Size && ferror(file.GetHandle())) { ReturnValue = FS_EACCESS; } @@ -269,7 +250,6 @@ bool CWII_IPC_HLE_Device_FileIO::Read(u32 _CommandAddress) } } - CloseFile(); } else { @@ -288,23 +268,22 @@ bool CWII_IPC_HLE_Device_FileIO::Write(u32 _CommandAddress) const u32 Size = Memory::Read_U32(_CommandAddress + 0x10); - if (OpenFile()) + if (auto file = OpenFile()) { - if (m_Mode == ISFS_OPEN_READ) + if (m_Mode == ISFS_OPEN_READ) { WARN_LOG(WII_IPC_FILEIO, "FileIO: Attempted to write 0x%x bytes from 0x%08x to read-only file %s", Size, Address, m_Name.c_str()); } else { INFO_LOG(WII_IPC_FILEIO, "FileIO: Write 0x%04x bytes from 0x%08x to %s", Size, Address, m_Name.c_str()); - m_pFileHandle.Seek(m_SeekPos, SEEK_SET); - if (m_pFileHandle.WriteBytes(Memory::GetPointer(Address), Size)) + file.Seek(m_SeekPos, SEEK_SET); + if (file.WriteBytes(Memory::GetPointer(Address), Size)) { ReturnValue = Size; m_SeekPos += Size; } } - CloseFile(); } else { @@ -329,9 +308,9 @@ bool CWII_IPC_HLE_Device_FileIO::IOCtl(u32 _CommandAddress) { case ISFS_IOCTL_GETFILESTATS: { - if (OpenFile()) + if (auto file = OpenFile()) { - u32 m_FileLength = (u32)m_pFileHandle.GetSize(); + u32 m_FileLength = (u32)file.GetSize(); const u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); INFO_LOG(WII_IPC_FILEIO, "FileIO: ISFS_IOCTL_GETFILESTATS"); @@ -339,7 +318,6 @@ bool CWII_IPC_HLE_Device_FileIO::IOCtl(u32 _CommandAddress) Memory::Write_U32(m_FileLength, BufferOut); Memory::Write_U32(m_SeekPos, BufferOut+4); - CloseFile(); } else { @@ -365,30 +343,8 @@ void CWII_IPC_HLE_Device_FileIO::DoState(PointerWrap &p) { DoStateShared(p); - bool have_file_handle = (m_pFileHandle != 0); - s32 seek = (have_file_handle) ? (s32)m_pFileHandle.Tell() : 0; - - p.Do(have_file_handle); p.Do(m_Mode); - p.Do(seek); p.Do(m_SeekPos); - p.Do(m_Filename); - - if (p.GetMode() == PointerWrap::MODE_READ) - { - int mode = m_Mode; - bool active = m_Active; - if (have_file_handle) - { - Open(0, m_Mode); - _dbg_assert_msg_(WII_IPC_HLE, m_pFileHandle, "bad filehandle"); - } - else - Close(0, true); - m_Mode = mode; - m_Active = active; - } - - if (have_file_handle) - m_pFileHandle.Seek(seek, SEEK_SET); + + m_filepath = HLE_IPC_BuildFilename(m_Name, 64); } diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h index ff34a43a20..3698d05c9f 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h @@ -21,7 +21,7 @@ #include "WII_IPC_HLE_Device.h" #include "FileUtil.h" -std::string HLE_IPC_BuildFilename(const char* _pFilename, int _size); +std::string HLE_IPC_BuildFilename(std::string _pFilename, int _size); void HLE_IPC_CreateVirtualFATFilesystem(); class CWII_IPC_HLE_Device_FileIO : public IWII_IPC_HLE_Device @@ -39,8 +39,7 @@ public: bool IOCtl(u32 _CommandAddress); void DoState(PointerWrap &p); - bool OpenFile(); - void CloseFile(); + File::IOFile OpenFile(); private: enum @@ -76,12 +75,10 @@ private: ISFS_IOCTL_SHUTDOWN }; - File::IOFile m_pFileHandle; u32 m_Mode; - u32 m_SeekPos; - - std::string m_Filename; + + std::string m_filepath; }; #endif diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp index ecc4be0cb0..38100e5d7a 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp @@ -428,7 +428,7 @@ s32 CWII_IPC_HLE_Device_fs::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _B File::CreateFullPath(FilenameRename); // if there is already a file, delete it - if (File::Exists(FilenameRename)) + if (File::Exists(Filename) && File::Exists(FilenameRename)) { File::Delete(FilenameRename); } diff --git a/Source/Core/Core/Src/State.cpp b/Source/Core/Core/Src/State.cpp index 200642eccb..084a94e5f6 100644 --- a/Source/Core/Core/Src/State.cpp +++ b/Source/Core/Core/Src/State.cpp @@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 12; +static const u32 STATE_VERSION = 13; struct StateHeader { diff --git a/Source/Core/DiscIO/Src/NANDContentLoader.cpp b/Source/Core/DiscIO/Src/NANDContentLoader.cpp index dc1f44739b..3c00d66be5 100644 --- a/Source/Core/DiscIO/Src/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/Src/NANDContentLoader.cpp @@ -215,7 +215,7 @@ bool CNANDContentLoader::Initialize(const std::string& _rName) { std::string TMDFileName(m_Path); - if (File::IsDirectory(TMDFileName)) + if ('/' == *TMDFileName.rbegin()) TMDFileName += "title.tmd"; else m_Path = TMDFileName.substr(0, TMDFileName.find("title.tmd")); diff --git a/Source/Core/DolphinWX/Src/InputConfigDiag.cpp b/Source/Core/DolphinWX/Src/InputConfigDiag.cpp index 8cdd2e16cb..1efcf98af6 100644 --- a/Source/Core/DolphinWX/Src/InputConfigDiag.cpp +++ b/Source/Core/DolphinWX/Src/InputConfigDiag.cpp @@ -519,7 +519,7 @@ wxStaticBoxSizer* ControlDialog::CreateControlChooser(GamepadPage* const parent) button_sizer->Add(add_button, 1, 0, 5); } - range_slider = new wxSlider(this, -1, SLIDER_TICK_COUNT, 0, SLIDER_TICK_COUNT * 5, wxDefaultPosition, wxDefaultSize, wxSL_TOP | wxSL_LABELS /*| wxSL_AUTOTICKS*/); + range_slider = new wxSlider(this, -1, SLIDER_TICK_COUNT, -SLIDER_TICK_COUNT * 5, SLIDER_TICK_COUNT * 5, wxDefaultPosition, wxDefaultSize, wxSL_TOP | wxSL_LABELS /*| wxSL_AUTOTICKS*/); range_slider->SetValue((int)(control_reference->range * SLIDER_TICK_COUNT)); diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index f6e6911c12..fd935de342 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -84,35 +84,34 @@ TextureCache::~TextureCache() void TextureCache::OnConfigChanged(VideoConfig& config) { - if (!g_texture_cache) - goto skip_checks; - - // TODO: Invalidating texcache is really stupid in some of these cases - if (config.iSafeTextureCache_ColorSamples != backup_config.s_colorsamples || - config.bTexFmtOverlayEnable != backup_config.s_texfmt_overlay || - config.bTexFmtOverlayCenter != backup_config.s_texfmt_overlay_center || - config.bHiresTextures != backup_config.s_hires_textures) + if (g_texture_cache) { - g_texture_cache->Invalidate(); + // TODO: Invalidating texcache is really stupid in some of these cases + if (config.iSafeTextureCache_ColorSamples != backup_config.s_colorsamples || + config.bTexFmtOverlayEnable != backup_config.s_texfmt_overlay || + config.bTexFmtOverlayCenter != backup_config.s_texfmt_overlay_center || + config.bHiresTextures != backup_config.s_hires_textures) + { + g_texture_cache->Invalidate(); - if(g_ActiveConfig.bHiresTextures) - HiresTextures::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str()); + if(g_ActiveConfig.bHiresTextures) + HiresTextures::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str()); - SetHash64Function(g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures); - TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); + SetHash64Function(g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures); + TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); + } + + // TODO: Probably shouldn't clear all render targets here, just mark them dirty or something. + if (config.bEFBCopyCacheEnable != backup_config.s_copy_cache_enable || // TODO: not sure if this is needed? + config.bCopyEFBToTexture != backup_config.s_copy_efb_to_texture || + config.bCopyEFBScaled != backup_config.s_copy_efb_scaled || + config.bEFBCopyEnable != backup_config.s_copy_efb || + config.iEFBScale != backup_config.s_efb_scale) + { + g_texture_cache->ClearRenderTargets(); + } } - - // TODO: Probably shouldn't clear all render targets here, just mark them dirty or something. - if (config.bEFBCopyCacheEnable != backup_config.s_copy_cache_enable || // TODO: not sure if this is needed? - config.bCopyEFBToTexture != backup_config.s_copy_efb_to_texture || - config.bCopyEFBScaled != backup_config.s_copy_efb_scaled || - config.bEFBCopyEnable != backup_config.s_copy_efb || - config.iEFBScale != backup_config.s_efb_scale) - { - g_texture_cache->ClearRenderTargets(); - } - -skip_checks: + backup_config.s_colorsamples = config.iSafeTextureCache_ColorSamples; backup_config.s_copy_efb_to_texture = config.bCopyEFBToTexture; backup_config.s_copy_efb_scaled = config.bCopyEFBScaled; @@ -301,14 +300,30 @@ void TextureCache::DumpTexture(TCacheEntryBase* entry, unsigned int level) entry->Save(szTemp, level); } -TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, - u32 address, unsigned int width, unsigned int height, int texformat, - unsigned int tlutaddr, int tlutfmt, bool use_mipmaps, unsigned int maxlevel, bool from_tmem) +static u32 CalculateLevelSize(u32 level_0_size, u32 level) +{ + return (level_0_size + ((1 << level) - 1)) >> level; +} + +// Used by TextureCache::Load +static TextureCache::TCacheEntryBase* ReturnEntry(unsigned int stage, TextureCache::TCacheEntryBase* entry) +{ + entry->frameCount = frameCount; + entry->Bind(stage); + + GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true); + + return entry; +} + +TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage, + u32 const address, unsigned int width, unsigned int height, int const texformat, + unsigned int const tlutaddr, int const tlutfmt, bool const use_mipmaps, unsigned int maxlevel, bool const from_tmem) { if (0 == address) return NULL; - // TexelSizeInNibbles(format)*width*height/16; + // TexelSizeInNibbles(format) * width * height / 16; const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat) - 1; const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat) - 1; @@ -317,11 +332,9 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, const unsigned int nativeW = width; const unsigned int nativeH = height; - bool using_custom_texture = false; - bool using_custom_lods = false; - u32 texID = address; - u64 tex_hash = TEXHASH_INVALID; // Hash assigned to texcache entry (also used to generate filenames used for texture dumping and custom texture lookup) + // Hash assigned to texcache entry (also used to generate filenames used for texture dumping and custom texture lookup) + u64 tex_hash = TEXHASH_INVALID; u64 tlut_hash = TEXHASH_INVALID; u32 full_format = texformat; @@ -332,9 +345,12 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, full_format = texformat | (tlutfmt << 16); const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); - u8* src_data; - if (from_tmem) src_data = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE]; - else src_data = Memory::GetPointer(address); + + const u8* src_data; + if (from_tmem) + src_data = &texMem[bpmem.tex[stage / 4].texImage1[stage % 4].tmem_even * TMEM_LINE_SIZE]; + else + src_data = Memory::GetPointer(address); // TODO: This doesn't hash GB tiles for preloaded RGBA8 textures (instead, it's hashing more data from the low tmem bank than it should) tex_hash = GetHash64(src_data, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); @@ -356,6 +372,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, tex_hash ^= tlut_hash; } + // D3D doesn't like when the specified mipmap count would require more than one 1x1-sized LOD in the mipmap chain + // e.g. 64x64 with 7 LODs would have the mipmap chain 64x64,32x32,16x16,8x8,4x4,2x2,1x1,1x1, so we limit the mipmap count to 6 there + while (g_ActiveConfig.backend_info.bUseMinimalMipCount && max(expandedWidth, expandedHeight) >> maxlevel == 0) + --maxlevel; + TCacheEntryBase *entry = textures[texID]; if (entry) { @@ -369,15 +390,17 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, { entry->type = TCET_EC_VRAM; - // TODO: Print a warning if the format changes! In this case, we could reinterpret the internal texture object data to the new pixel format (similiar to what is already being done in Renderer::ReinterpretPixelFormat()) - goto return_entry; + // TODO: Print a warning if the format changes! In this case, + // we could reinterpret the internal texture object data to the new pixel format + // (similiar to what is already being done in Renderer::ReinterpretPixelFormat()) + return ReturnEntry(stage, entry); } // 2. b) For normal textures, all texture parameters need to match if (address == entry->addr && tex_hash == entry->hash && full_format == entry->format && entry->num_mipmaps > maxlevel && entry->native_width == nativeW && entry->native_height == nativeH) { - goto return_entry; + return ReturnEntry(stage, entry); } // 3. If we reach this line, we'll have to upload the new texture data to VRAM. @@ -385,7 +408,8 @@ 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->virtual_width && height == entry->virtual_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 @@ -398,9 +422,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } } + bool using_custom_texture = false; if (g_ActiveConfig.bHiresTextures) { + // This function may modify width/height. pcfmt = LoadCustomTexture(tex_hash, texformat, 0, width, height); if (pcfmt != PC_TEX_FMT_NONE) { @@ -431,21 +457,25 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } } - unsigned int texLevels; - bool use_native_mips; - texLevels = use_mipmaps ? (maxlevel + 1) : 1; - using_custom_lods = using_custom_texture && CheckForCustomTextureLODs(tex_hash, texformat, texLevels); - use_native_mips = use_mipmaps && !using_custom_lods && (width == nativeW && height == nativeH); // Only load native mips if their dimensions fit to our virtual texture dimensions - texLevels = (use_native_mips || using_custom_lods) ? texLevels : 1; + u32 texLevels = use_mipmaps ? (maxlevel + 1) : 1; + const bool using_custom_lods = using_custom_texture && CheckForCustomTextureLODs(tex_hash, texformat, texLevels); + // Only load native mips if their dimensions fit to our virtual texture dimensions + const bool use_native_mips = use_mipmaps && !using_custom_lods && (width == nativeW && height == nativeH); + texLevels = (use_native_mips || using_custom_lods) ? texLevels : 1; // TODO: Should be forced to 1 for non-pow2 textures (e.g. efb copies with automatically adjusted IR) // create the entry/texture - if (NULL == entry) { + if (NULL == entry) + { textures[texID] = entry = g_texture_cache->CreateTexture(width, height, expandedWidth, texLevels, pcfmt); // Sometimes, we can get around recreating a texture if only the number of mip levels changes // e.g. if our texture cache entry got too many mipmap levels we can limit the number of used levels by setting the appropriate render states // Thus, we don't update this member for every Load, but just whenever the texture gets recreated // TODO: D3D9 doesn't support min_lod. We should add a workaround for that here! + + // TODO: This is the wrong value. We should be storing the number of levels our actual texture has. + // But that will currently make the above "existing entry" tests fail as "texLevels" is not calculated until after. + // Currently, we might try to reuse a texture which appears to have more levels than actual, maybe.. entry->num_mipmaps = maxlevel + 1; entry->type = TCET_NORMAL; @@ -464,78 +494,58 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, if (g_ActiveConfig.bDumpTextures && !using_custom_texture) DumpTexture(entry, 0); + u32 level = 1; // load mips - TODO: Loading mipmaps from tmem is untested! - if (texLevels > 1 && pcfmt != PC_TEX_FMT_NONE && use_native_mips) + if (pcfmt != PC_TEX_FMT_NONE) { - const unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(texformat); - - unsigned int level = 1; - unsigned int mipWidth = (width + 1) >> 1; - unsigned int mipHeight = (height + 1) >> 1; - - u8* ptr_even = NULL, *ptr_odd = NULL; - if (from_tmem) + if (use_native_mips) { - ptr_even = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE + texture_size]; - ptr_odd = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE]; + src_data += texture_size; + + const u8* ptr_even = NULL; + const u8* ptr_odd = NULL; + if (from_tmem) + { + ptr_even = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE + texture_size]; + ptr_odd = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE]; + } + + for (; level != texLevels; ++level) + { + const u32 mip_width = CalculateLevelSize(width, level); + const u32 mip_height = CalculateLevelSize(height, level); + const u32 expanded_mip_width = (mip_width + bsw) & (~bsw); + const u32 expanded_mip_height = (mip_height + bsh) & (~bsh); + + const u8*& mip_src_data = from_tmem + ? ((level % 2) ? ptr_odd : ptr_even) + : src_data; + TexDecoder_Decode(temp, mip_src_data, expanded_mip_width, expanded_mip_height, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); + mip_src_data += TexDecoder_GetTextureSizeInBytes(expanded_mip_width, expanded_mip_height, texformat); + + entry->Load(mip_width, mip_height, expanded_mip_width, level); + + if (g_ActiveConfig.bDumpTextures) + DumpTexture(entry, level); + } } - src_data += texture_size; - - while ((mipHeight || mipWidth) && (level < texLevels)) + else if (using_custom_lods) { - u8** ptr; - if (from_tmem) ptr = (level % 2) ? &ptr_odd : &ptr_even; - else ptr = &src_data; + for (; level != texLevels; ++level) + { + unsigned int mip_width = CalculateLevelSize(width, level); + unsigned int mip_height = CalculateLevelSize(height, level); - const unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1; - const unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1; - - expandedWidth = (currentWidth + bsw) & (~bsw); - expandedHeight = (currentHeight + bsh) & (~bsh); - - TexDecoder_Decode(temp, *ptr, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); - entry->Load(currentWidth, currentHeight, expandedWidth, level); - - if (g_ActiveConfig.bDumpTextures) - DumpTexture(entry, level); - - *ptr += ((std::max(mipWidth, bsw) * std::max(mipHeight, bsh) * bsdepth) >> 1); - mipWidth >>= 1; - mipHeight >>= 1; - ++level; - } - } - else if (texLevels > 1 && pcfmt != PC_TEX_FMT_NONE && using_custom_lods) - { - unsigned int level = 1; - unsigned int mipWidth = (width + 1) >> 1; - unsigned int mipHeight = (height + 1) >> 1; - - while ((mipHeight || mipWidth) && (level < texLevels)) - { - unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1; - unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1; - - LoadCustomTexture(tex_hash, texformat, level, currentWidth, currentHeight); - entry->Load(currentWidth, currentHeight, currentWidth, level); - - mipWidth >>= 1; - mipHeight >>= 1; - ++level; + LoadCustomTexture(tex_hash, texformat, level, mip_width, mip_height); + entry->Load(mip_width, mip_height, mip_width, level); + } } } INCSTAT(stats.numTexturesCreated); SETSTAT(stats.numTexturesAlive, textures.size()); -return_entry: - - entry->frameCount = frameCount; - entry->Bind(stage); - - GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true); - - return entry; + return ReturnEntry(stage, entry); } void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, unsigned int srcFormat, diff --git a/Source/Core/VideoCommon/Src/TextureDecoder.cpp b/Source/Core/VideoCommon/Src/TextureDecoder.cpp index 44efcf8bae..30df817902 100644 --- a/Source/Core/VideoCommon/Src/TextureDecoder.cpp +++ b/Source/Core/VideoCommon/Src/TextureDecoder.cpp @@ -692,7 +692,7 @@ inline void SetOpenMPThreadCount(int width, int height) if (g_ActiveConfig.bOMPDecoder && width > 127 && height > 127) { // don't span to many threads they will kill the rest of the emu :) - omp_set_num_threads((cpu_info.num_cores + 2) / 3); + omp_set_num_threads((omp_get_num_procs() + 2) / 3); } else { diff --git a/Source/Core/VideoCommon/Src/VideoConfig.cpp b/Source/Core/VideoCommon/Src/VideoConfig.cpp index 7418d60741..a76514a10c 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.cpp +++ b/Source/Core/VideoCommon/Src/VideoConfig.cpp @@ -42,6 +42,7 @@ VideoConfig::VideoConfig() // disable all features by default backend_info.APIType = API_NONE; backend_info.bUseRGBATextures = false; + backend_info.bUseMinimalMipCount = false; backend_info.bSupports3DVision = false; } diff --git a/Source/Core/VideoCommon/Src/VideoConfig.h b/Source/Core/VideoCommon/Src/VideoConfig.h index 4364bea6ea..183fcf784c 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.h +++ b/Source/Core/VideoCommon/Src/VideoConfig.h @@ -157,6 +157,7 @@ struct VideoConfig std::vector PPShaders; // post-processing shaders bool bUseRGBATextures; // used for D3D11 in TextureCache + bool bUseMinimalMipCount; bool bSupports3DVision; bool bSupportsDualSourceBlend; // only supported by D3D11 and OpenGL bool bSupportsFormatReinterpretation; diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp index 4bcb4befb8..ab8ed68654 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp @@ -238,7 +238,7 @@ void VertexManager::vFlush() tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, tex.texTlut[i&3].tlut_format, (tex.texMode0[i&3].min_filter & 3), - ceil(tex.texMode1[i&3].max_lod / 16.f), + (tex.texMode1[i&3].max_lod + 0xf) / 0x10, tex.texImage1[i&3].image_type); if (tentry) diff --git a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp index cb348b8684..b7dd9101d3 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp @@ -90,6 +90,7 @@ void InitBackendInfo() g_Config.backend_info.APIType = API_D3D11; g_Config.backend_info.bUseRGBATextures = true; // the GX formats barely match any D3D11 formats + g_Config.backend_info.bUseMinimalMipCount = true; g_Config.backend_info.bSupports3DVision = false; g_Config.backend_info.bSupportsDualSourceBlend = true; g_Config.backend_info.bSupportsFormatReinterpretation = true; diff --git a/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp index d8e2462f51..661cf36e76 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp @@ -334,7 +334,7 @@ void VertexManager::vFlush() tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, tex.texTlut[i&3].tlut_format, (tex.texMode0[i&3].min_filter & 3), - ceil(tex.texMode1[i&3].max_lod / 16.f), + (tex.texMode1[i&3].max_lod + 0xf) / 0x10, tex.texImage1[i&3].image_type); if (tentry) diff --git a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp index 5a80100a08..3c00e1d619 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp @@ -93,6 +93,7 @@ void InitBackendInfo() const int maxConstants = (shaderModel < 3) ? 32 : ((shaderModel < 4) ? 224 : 65536); g_Config.backend_info.APIType = shaderModel < 3 ? API_D3D9_SM20 :API_D3D9_SM30; g_Config.backend_info.bUseRGBATextures = false; + g_Config.backend_info.bUseMinimalMipCount = true; g_Config.backend_info.bSupports3DVision = true; g_Config.backend_info.bSupportsDualSourceBlend = false; g_Config.backend_info.bSupportsFormatReinterpretation = true; diff --git a/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp index 8e9feb2c39..6ea10fc077 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp @@ -165,7 +165,7 @@ void VertexManager::vFlush() tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, tex.texTlut[i&3].tlut_format, (tex.texMode0[i&3].min_filter & 3), - ceil(tex.texMode1[i&3].max_lod / 16.f), + (tex.texMode1[i&3].max_lod + 0xf) / 0x10, tex.texImage1[i&3].image_type); if (tentry) diff --git a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp index e57e3e210a..05f21beed1 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp @@ -130,6 +130,7 @@ void InitBackendInfo() { g_Config.backend_info.APIType = API_OPENGL; g_Config.backend_info.bUseRGBATextures = false; + g_Config.backend_info.bUseMinimalMipCount = false; g_Config.backend_info.bSupports3DVision = false; g_Config.backend_info.bSupportsDualSourceBlend = false; // supported, but broken g_Config.backend_info.bSupportsFormatReinterpretation = false;