diff --git a/plugins/GSdx/GSLocalMemory.cpp b/plugins/GSdx/GSLocalMemory.cpp index aa2a0c973d..161ebecd44 100644 --- a/plugins/GSdx/GSLocalMemory.cpp +++ b/plugins/GSdx/GSLocalMemory.cpp @@ -84,7 +84,7 @@ GSLocalMemory::psm_t GSLocalMemory::m_psm[64]; GSLocalMemory::GSLocalMemory() : m_clut(this) { - if (theApp.GetConfigB("wrap_gs_mem")) + if (theApp.GetConfigB("UserHacks") && theApp.GetConfigB("wrap_gs_mem")) m_vm8 = (uint8*)fifo_alloc(m_vmsize, 4); else m_vm8 = (uint8*)vmalloc(m_vmsize * 4, false); @@ -472,7 +472,7 @@ GSLocalMemory::GSLocalMemory() GSLocalMemory::~GSLocalMemory() { - if (theApp.GetConfigB("wrap_gs_mem")) + if (theApp.GetConfigB("UserHacks") && theApp.GetConfigB("wrap_gs_mem")) fifo_free(m_vm8, m_vmsize, 4); else vmfree(m_vm8, m_vmsize * 4); diff --git a/plugins/GSdx/GSSetting.cpp b/plugins/GSdx/GSSetting.cpp index a632ad50c5..13b54997cc 100644 --- a/plugins/GSdx/GSSetting.cpp +++ b/plugins/GSdx/GSSetting.cpp @@ -146,6 +146,9 @@ const char* dialog_message(int ID, bool* updateText) { case IDC_UNSCALE_POINT_LINE: return "Increases the width of lines at higher than native resolutions. This ensures that the lines will keep the correct proportions and prevents aliasing. " "Avoids empty lines on the screen in games such as Ridge Racer V, and clears FMVs obscured by a grid in games like the Silent Hill series and Dirge of Cerberus."; + case IDC_MEMORY_WRAPPING: + return "Emulates GS memory wrapping accurately. This fixes issues where part of the image is cut-off by block shaped sections such as the FMVs in Wallace & Gromit: The Curse of the Were-Rabbit and Thrillville.\n" + "Note: This hack can have a small impact on performance."; #ifdef _WIN32 // DX9 only case IDC_FBA: diff --git a/plugins/GSdx/GSSetting.h b/plugins/GSdx/GSSetting.h index e8061d3c95..ae3d4f2793 100644 --- a/plugins/GSdx/GSSetting.h +++ b/plugins/GSdx/GSSetting.h @@ -79,6 +79,7 @@ enum { IDC_LINEAR_PRESENT, IDC_AUTO_FLUSH, IDC_UNSCALE_POINT_LINE, + IDC_MEMORY_WRAPPING, IDC_OSD_LOG, IDC_OSD_MONITOR, IDC_OSD_MAX_LOG, diff --git a/plugins/GSdx/GSSettingsDlg.cpp b/plugins/GSdx/GSSettingsDlg.cpp index 9c11bf6a12..1b100f5c8b 100644 --- a/plugins/GSdx/GSSettingsDlg.cpp +++ b/plugins/GSdx/GSSettingsDlg.cpp @@ -687,6 +687,7 @@ void GSHacksDlg::OnInit() CheckDlgButton(m_hWnd, IDC_FAST_TC_INV, theApp.GetConfigB("UserHacks_DisablePartialInvalidation")); CheckDlgButton(m_hWnd, IDC_AUTO_FLUSH, theApp.GetConfigB("UserHacks_AutoFlush")); CheckDlgButton(m_hWnd, IDC_UNSCALE_POINT_LINE, theApp.GetConfigB("UserHacks_unscale_point_line")); + CheckDlgButton(m_hWnd, IDC_MEMORY_WRAPPING, theApp.GetConfigB("wrap_gs_mem")); std::vector hpo_combobox = theApp.m_gs_offset_hack; if (!ogl) { @@ -739,6 +740,7 @@ void GSHacksDlg::OnInit() AddTooltip(IDC_FAST_TC_INV); AddTooltip(IDC_AUTO_FLUSH); AddTooltip(IDC_UNSCALE_POINT_LINE); + AddTooltip(IDC_MEMORY_WRAPPING); } void GSHacksDlg::UpdateControls() @@ -780,6 +782,7 @@ bool GSHacksDlg::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) theApp.SetConfig("UserHacks_DisablePartialInvalidation", (int)IsDlgButtonChecked(m_hWnd, IDC_FAST_TC_INV)); theApp.SetConfig("UserHacks_AutoFlush", (int)IsDlgButtonChecked(m_hWnd, IDC_AUTO_FLUSH)); theApp.SetConfig("UserHacks_unscale_point_line", (int)IsDlgButtonChecked(m_hWnd, IDC_UNSCALE_POINT_LINE)); + theApp.SetConfig("wrap_gs_mem", (int)IsDlgButtonChecked(m_hWnd, IDC_MEMORY_WRAPPING)); unsigned int TCOFFSET = SendMessage(GetDlgItem(m_hWnd, IDC_TCOFFSETX), UDM_GETPOS, 0, 0) & 0xFFFF; TCOFFSET |= (SendMessage(GetDlgItem(m_hWnd, IDC_TCOFFSETY), UDM_GETPOS, 0, 0) & 0xFFFF) << 16; diff --git a/plugins/GSdx/GSTextureCache.cpp b/plugins/GSdx/GSTextureCache.cpp index c6dbb1d96c..d319892940 100644 --- a/plugins/GSdx/GSTextureCache.cpp +++ b/plugins/GSdx/GSTextureCache.cpp @@ -39,6 +39,7 @@ GSTextureCache::GSTextureCache(GSRenderer* r) m_disable_partial_invalidation = theApp.GetConfigB("UserHacks_DisablePartialInvalidation"); m_can_convert_depth = !theApp.GetConfigB("UserHacks_DisableDepthSupport"); m_texture_inside_rt = theApp.GetConfigB("UserHacks_TextureInsideRt"); + m_wrap_gs_mem = theApp.GetConfigB("wrap_gs_mem"); } else { m_spritehack = 0; UserHacks_HalfPixelOffset = false; @@ -46,10 +47,9 @@ GSTextureCache::GSTextureCache(GSRenderer* r) m_disable_partial_invalidation = false; m_can_convert_depth = true; m_texture_inside_rt = false; + m_wrap_gs_mem = false; } - m_wrap_gs_mem = theApp.GetConfigB("wrap_gs_mem"); - m_paltex = theApp.GetConfigB("paltex"); m_can_convert_depth &= s_IS_OPENGL; // only supported by openGL so far m_crc_hack_level = theApp.GetConfigT("crc_hack_level"); diff --git a/plugins/GSdx/resource.h b/plugins/GSdx/resource.h index f833e2f042..d77cf715ea 100644 --- a/plugins/GSdx/resource.h +++ b/plugins/GSdx/resource.h @@ -122,6 +122,7 @@ #define IDC_OSDBUTTON 2118 #define IDC_OSD_MAX_LOG 2119 #define IDC_OSD_MAX_LOG_EDIT 2120 +#define IDC_MEMORY_WRAPPING 2121 #define IDR_CONVERT_FX 10000 #define IDR_TFX_FX 10001 #define IDR_MERGE_FX 10002 @@ -141,7 +142,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 10013 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 2121 +#define _APS_NEXT_CONTROL_VALUE 2122 #define _APS_NEXT_SYMED_VALUE 5000 #endif #endif diff --git a/plugins/GSdx/stdafx.cpp b/plugins/GSdx/stdafx.cpp index af135133db..ce8fda9dc8 100644 --- a/plugins/GSdx/stdafx.cpp +++ b/plugins/GSdx/stdafx.cpp @@ -72,16 +72,71 @@ void vmfree(void* ptr, size_t size) VirtualFree(ptr, 0, MEM_RELEASE); } +static HANDLE s_fh = NULL; +static uint8* s_Next[8]; + void* fifo_alloc(size_t size, size_t repeat) { - // FIXME check linux code - return vmalloc(size * repeat, false); + ASSERT(s_fh == NULL); + + if (repeat >= countof(s_Next)) { + fprintf(stderr, "Memory mapping overflow (%zu >= %u)\n", repeat, countof(s_Next)); + return vmalloc(size * repeat, false); // Fallback to default vmalloc + } + + s_fh = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, size, nullptr); + DWORD errorID = ::GetLastError(); + if (s_fh == NULL) { + fprintf(stderr, "Failed to reserve memory. WIN API ERROR:%u\n", errorID); + return vmalloc(size * repeat, false); // Fallback to default vmalloc + } + + int mmap_segment_failed = 0; + void* fifo = MapViewOfFile(s_fh, FILE_MAP_ALL_ACCESS, 0, 0, size); + for (size_t i = 1; i < repeat; i++) { + void* base = (uint8*)fifo + size * i; + s_Next[i] = (uint8*)MapViewOfFileEx(s_fh, FILE_MAP_ALL_ACCESS, 0, 0, size, base); + errorID = ::GetLastError(); + if (s_Next[i] != base) { + mmap_segment_failed++; + if (mmap_segment_failed > 4) { + fprintf(stderr, "Memory mapping failed after %d attempts, aborting. WIN API ERROR:%u\n", mmap_segment_failed, errorID); + fifo_free(fifo, size, repeat); + return vmalloc(size * repeat, false); // Fallback to default vmalloc + } + do { + UnmapViewOfFile(s_Next[i]); + s_Next[i] = 0; + } while (--i > 0); + + fifo = MapViewOfFile(s_fh, FILE_MAP_ALL_ACCESS, 0, 0, size); + } + } + + return fifo; } void fifo_free(void* ptr, size_t size, size_t repeat) { - // FIXME check linux code - return vmfree(ptr, size * repeat); + ASSERT(s_fh != NULL); + + if (s_fh == NULL) { + if (ptr != NULL) + vmfree(ptr, size); + return; + } + + UnmapViewOfFile(ptr); + + for (size_t i = 1; i < countof(s_Next); i++) { + if (s_Next[i] != 0) { + UnmapViewOfFile(s_Next[i]); + s_Next[i] = 0; + } + } + + CloseHandle(s_fh); + s_fh = NULL; } #else @@ -125,10 +180,12 @@ void* fifo_alloc(size_t size, size_t repeat) const char* file_name = "/GSDX.mem"; s_shm_fd = shm_open(file_name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (s_shm_fd != -1) + if (s_shm_fd != -1) { shm_unlink(file_name); // file is deleted but descriptor is still open - else + } else { fprintf(stderr, "Failed to open %s due to %s\n", file_name, strerror(errno)); + return nullptr; + } if (ftruncate(s_shm_fd, repeat * size) < 0) fprintf(stderr, "Failed to reserve memory due to %s\n", strerror(errno));