mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
15 Commits
gs_wrchack
...
v1.7.4060
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26e691ba93 | ||
|
|
c7352d9e10 | ||
|
|
7b8f9a54ec | ||
|
|
28980af858 | ||
|
|
80dce398e0 | ||
|
|
06db8eec48 | ||
|
|
9c720efe46 | ||
|
|
cbf91a8d19 | ||
|
|
f99414708d | ||
|
|
9549a6b16a | ||
|
|
3206094545 | ||
|
|
5cfae80701 | ||
|
|
b4d140c6bb | ||
|
|
c65eb3c3ee | ||
|
|
eec0984dbe |
@@ -36,10 +36,10 @@ _Note: Recommended GPU is based on 3x Internal, ~1080p resolution requirements.
|
||||
|
||||
### Technical Notes
|
||||
|
||||
- You need the [Visual C++ 2019 x64 Redistributables](https://support.microsoft.com/en-us/help/2977003/) to run PCSX2.
|
||||
- You need the [Visual C++ 2019 x64 Redistributables](https://support.microsoft.com/en-us/help/2977003/) to run PCSX2 on Windows.
|
||||
- Windows XP and Direct3D9 support was dropped after stable release 1.4.0.
|
||||
- Windows 7, Windows 8.0, and Windows 8.1 support was dropped after stable release 1.6.0.
|
||||
- 32-bit and wxwidgets support was dropped after stable release 1.6.0, with the wxwidgets code being removed completely on 25th December 2022.
|
||||
- 32-bit and wxWidgets support was dropped after stable release 1.6.0, with the wxWidgets code being removed completely on 25th December 2022.
|
||||
- Make sure to update your operating system and drivers to ensure you have the best experience possible. Having a newer GPU is also recommended so you have the latest supported drivers.
|
||||
- Because of copyright issues, and the complexity of trying to work around it, you need a BIOS dump extracted from a legitimately-owned PS2 console to use the emulator. For more information about the BIOS and how to get it from your console, visit [this page](pcsx2/Docs/PCSX2_FAQ.md#question-13-where-do-i-get-a-ps2-bios).
|
||||
- PCSX2 uses two CPU cores for emulation by default. A third core can be used via the MTVU speed hack, which is compatible with most games. This can be a significant speedup on CPUs with 3+ cores, but it may be a slowdown on GS-limited games (or on CPUs with fewer than 2 cores). Software renderers will then additionally use however many rendering threads it is set to and will need higher core counts to run efficiently.
|
||||
|
||||
@@ -9392,6 +9392,9 @@ SLED-53512:
|
||||
SLED-53537:
|
||||
name: "187 - Ride or Die [Demo]"
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Softens bloom on objects and cars.
|
||||
halfPixelOffset: 2 # Fixes misalgined bloom on objects and cars.
|
||||
SLED-53673:
|
||||
name: "007 - From Russia with Love [Demo]"
|
||||
region: "PAL-E"
|
||||
@@ -10800,6 +10803,9 @@ SLES-50632:
|
||||
SLES-50636:
|
||||
name: "Centre Court - Hard Hitter"
|
||||
region: "PAL-M3"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLES-50637:
|
||||
name: "Pro Rally 2002"
|
||||
region: "PAL-M5"
|
||||
@@ -11749,6 +11755,9 @@ SLES-51056:
|
||||
SLES-51057:
|
||||
name: "Hard Hitter 2"
|
||||
region: "PAL-M3"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLES-51058:
|
||||
name: "Maken Shao - Demon Sword"
|
||||
region: "PAL-E"
|
||||
@@ -14327,6 +14336,9 @@ SLES-52276:
|
||||
name: "187 - Ride or Die"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Softens bloom on objects and cars.
|
||||
halfPixelOffset: 2 # Fixes misalgined bloom on objects and cars.
|
||||
SLES-52277:
|
||||
name: "Riding Spirits 2"
|
||||
region: "PAL-M5"
|
||||
@@ -14567,7 +14579,7 @@ SLES-52372:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLES-52373:
|
||||
name: "Champions of Norrath"
|
||||
@@ -14730,7 +14742,7 @@ SLES-52447:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLES-52448:
|
||||
name: "Knights of the Temple"
|
||||
@@ -14843,7 +14855,7 @@ SLES-52493:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLES-52495:
|
||||
name: "Bujingai - Swordmaster"
|
||||
@@ -15519,7 +15531,7 @@ SLES-52745:
|
||||
region: "PAL-M4"
|
||||
SLES-52746:
|
||||
name: "Hugo - Cannon Cruise"
|
||||
region: "PAL-M4"
|
||||
region: "PAL-SC"
|
||||
SLES-52747:
|
||||
name: "Dukes of Hazzard, The - The Return of General Lee"
|
||||
region: "PAL-E"
|
||||
@@ -15705,7 +15717,7 @@ SLES-52816:
|
||||
cpuCLUTRender: 1 # Fixes duplicated post processing.
|
||||
SLES-52820:
|
||||
name: "Incredibles, The"
|
||||
region: "PAL-M4"
|
||||
region: "PAL-SC"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes offset post processing.
|
||||
cpuCLUTRender: 1 # Fixes duplicated post processing.
|
||||
@@ -18815,7 +18827,14 @@ SLES-54013:
|
||||
region: "PAL-E"
|
||||
SLES-54015:
|
||||
name: "Disney-Pixar's Cars"
|
||||
region: "PAL-M3"
|
||||
region: "PAL-SC"
|
||||
patches:
|
||||
B36778F5:
|
||||
content: |-
|
||||
author=refraction ported by jordanthetoast
|
||||
// COP2 Rearrangement. Fixes broken collisions.
|
||||
patch=1,EE,00203df8,word,484e9000
|
||||
patch=1,EE,00203dfc,word,4bdbd9ff
|
||||
SLES-54016:
|
||||
name: "AND 1 Streetball"
|
||||
region: "PAL-M5"
|
||||
@@ -22231,37 +22250,27 @@ SLES-55349:
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55350:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-F-G"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55351:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-I-S"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55352:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-M4"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55353:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-M6"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55354:
|
||||
name: "Shin Megami Tensei - Persona 3 FES"
|
||||
region: "PAL-E"
|
||||
@@ -23238,6 +23247,9 @@ SLKA-15011:
|
||||
SLKA-15013:
|
||||
name: "Grand Slam 2003 Tennis" # Hard Hitter 2
|
||||
region: "NTSC-K"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLKA-15015:
|
||||
name: "Ichigeki Sacchuu!! HoiHoi-San"
|
||||
region: "NTSC-K"
|
||||
@@ -24709,8 +24721,6 @@ SLPM-55127:
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLPM-55131:
|
||||
name: "World Soccer Winning Eleven 2009"
|
||||
region: "NTSC-J"
|
||||
@@ -24905,8 +24915,6 @@ SLPM-55244:
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLPM-55245:
|
||||
name: "Himawari - Pebble in the Sky"
|
||||
region: "NTSC-J"
|
||||
@@ -29783,6 +29791,14 @@ SLPM-65657:
|
||||
SLPM-65661:
|
||||
name: "Densha de Go! Professional 2 [Taito Best]"
|
||||
region: "NTSC-J"
|
||||
SLPM-65662:
|
||||
name: "Spider-Man 2"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLPM-65663:
|
||||
name: "Zwei!!"
|
||||
region: "NTSC-J"
|
||||
@@ -31436,6 +31452,14 @@ SLPM-66119:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
speedHacks:
|
||||
InstantVU1SpeedHack: 0 # Fixes missing letters in text such as E.
|
||||
SLPM-66121:
|
||||
name: "Spider-Man 2 [Taito Best]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLPM-66122:
|
||||
name: "Kingdom Hearts [Ultimate Hits]"
|
||||
region: "NTSC-J"
|
||||
@@ -35642,6 +35666,9 @@ SLPS-20097:
|
||||
SLPS-20098:
|
||||
name: "Magical Sports - Hard Hitter"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLPS-20099:
|
||||
name: "Dream Audition 3"
|
||||
region: "NTSC-J"
|
||||
@@ -35793,6 +35820,9 @@ SLPS-20171:
|
||||
SLPS-20173:
|
||||
name: "Magical Sports - Hard Hitter 2"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLPS-20174:
|
||||
name: "Typing Renai Hakusho - Boys Be... [with Keyboard]"
|
||||
region: "NTSC-J"
|
||||
@@ -43196,6 +43226,9 @@ SLUS-20567:
|
||||
SLUS-20568:
|
||||
name: "Hard Hitter Tennis"
|
||||
region: "NTSC-U"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLUS-20569:
|
||||
name: "All-Star Baseball 2004"
|
||||
region: "NTSC-U"
|
||||
@@ -44212,7 +44245,7 @@ SLUS-20776:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLUS-20777:
|
||||
name: "Obscure"
|
||||
@@ -45938,6 +45971,9 @@ SLUS-21116:
|
||||
name: "187 - Ride or Die"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Softens bloom on objects and cars.
|
||||
halfPixelOffset: 2 # Fixes misalgined bloom on objects and cars.
|
||||
SLUS-21117:
|
||||
name: "World Soccer - Winning Eleven 8 - International"
|
||||
region: "NTSC-U"
|
||||
|
||||
@@ -445,7 +445,7 @@ ID3D12GraphicsCommandList4* Context::GetInitCommandList()
|
||||
return res.command_lists[0].get();
|
||||
}
|
||||
|
||||
void Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
bool Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
{
|
||||
CommandListResources& res = m_command_lists[m_current_command_list];
|
||||
HRESULT hr;
|
||||
@@ -463,12 +463,21 @@ void Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
if (res.init_command_list_used)
|
||||
{
|
||||
hr = res.command_lists[0]->Close();
|
||||
pxAssertRel(SUCCEEDED(hr), "Close init command list");
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Closing init command list failed with HRESULT %08X", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Close and queue command list.
|
||||
hr = res.command_lists[1]->Close();
|
||||
pxAssertRel(SUCCEEDED(hr), "Close command list");
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Closing main command list failed with HRESULT %08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res.init_command_list_used)
|
||||
{
|
||||
const std::array<ID3D12CommandList*, 2> execute_lists{res.command_lists[0].get(), res.command_lists[1].get()};
|
||||
@@ -487,6 +496,8 @@ void Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
MoveToNextCommandList();
|
||||
if (wait_for_completion != WaitType::None)
|
||||
WaitForFence(res.ready_fence_value, wait_for_completion == WaitType::Spin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Context::InvalidateSamplerGroups()
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace D3D12
|
||||
};
|
||||
|
||||
/// Executes the current command list.
|
||||
void ExecuteCommandList(WaitType wait_for_completion);
|
||||
bool ExecuteCommandList(WaitType wait_for_completion);
|
||||
|
||||
/// Waits for a specific fence.
|
||||
void WaitForFence(u64 fence, bool spin);
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace GL
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
Console.WriteLn("Created a %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
|
||||
Console.WriteLn("Created an %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
|
||||
|
||||
// NOTE: Not thread-safe. But this is okay, since we're not going to be creating more than one context at a time.
|
||||
static Context* context_being_created;
|
||||
|
||||
@@ -1122,9 +1122,13 @@ namespace Vulkan
|
||||
void Context::WaitForCommandBufferCompletion(u32 index)
|
||||
{
|
||||
// Wait for this command buffer to be completed.
|
||||
VkResult res = vkWaitForFences(m_device, 1, &m_frame_resources[index].fence, VK_TRUE, UINT64_MAX);
|
||||
const VkResult res = vkWaitForFences(m_device, 1, &m_frame_resources[index].fence, VK_TRUE, UINT64_MAX);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
|
||||
m_last_submit_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up any resources for command buffers between the last known completed buffer and this
|
||||
// now-completed command buffer. If we use >2 buffers, this may be more than one buffer.
|
||||
@@ -1266,11 +1270,12 @@ namespace Vulkan
|
||||
submit_info.pSignalSemaphores = &m_spin_resources[index].semaphore;
|
||||
}
|
||||
|
||||
VkResult res = vkQueueSubmit(m_graphics_queue, 1, &submit_info, resources.fence);
|
||||
const VkResult res = vkQueueSubmit(m_graphics_queue, 1, &submit_info, resources.fence);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkQueueSubmit failed: ");
|
||||
pxFailRel("Failed to submit command buffer.");
|
||||
m_last_submit_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spin_cycles != 0)
|
||||
@@ -1286,14 +1291,14 @@ namespace Vulkan
|
||||
|
||||
present_swap_chain->ReleaseCurrentImage();
|
||||
|
||||
VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
|
||||
const VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
||||
if (res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR)
|
||||
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
||||
|
||||
m_last_present_failed.store(true);
|
||||
m_last_present_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1460,6 +1465,9 @@ namespace Vulkan
|
||||
|
||||
void Context::ExecuteCommandBuffer(WaitType wait_for_completion)
|
||||
{
|
||||
if (m_last_submit_failed.load(std::memory_order_acquire))
|
||||
return;
|
||||
|
||||
// If we're waiting for completion, don't bother waking the worker thread.
|
||||
const u32 current_frame = m_current_frame;
|
||||
SubmitCommandBuffer();
|
||||
@@ -1481,9 +1489,12 @@ namespace Vulkan
|
||||
|
||||
bool Context::CheckLastPresentFail()
|
||||
{
|
||||
bool res = m_last_present_failed;
|
||||
m_last_present_failed = false;
|
||||
return res;
|
||||
return m_last_present_failed.exchange(false, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
bool Context::CheckLastSubmitFail()
|
||||
{
|
||||
return m_last_submit_failed.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void Context::DeferBufferDestruction(VkBuffer object)
|
||||
@@ -1596,7 +1607,7 @@ namespace Vulkan
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
DebugMessengerCallback, nullptr};
|
||||
|
||||
VkResult res =
|
||||
const VkResult res =
|
||||
vkCreateDebugUtilsMessengerEXT(m_instance, &messenger_info, nullptr, &m_debug_messenger_callback);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
@@ -1688,7 +1699,7 @@ namespace Vulkan
|
||||
subpass_dependency_ptr};
|
||||
|
||||
VkRenderPass pass;
|
||||
VkResult res = vkCreateRenderPass(m_device, &pass_info, nullptr, &pass);
|
||||
const VkResult res = vkCreateRenderPass(m_device, &pass_info, nullptr, &pass);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateRenderPass failed: ");
|
||||
@@ -1894,9 +1905,14 @@ void main()
|
||||
SpinResources& resources = m_spin_resources[index];
|
||||
if (!resources.in_progress)
|
||||
return;
|
||||
VkResult res = vkWaitForFences(m_device, 1, &resources.fence, VK_TRUE, UINT64_MAX);
|
||||
|
||||
const VkResult res = vkWaitForFences(m_device, 1, &resources.fence, VK_TRUE, UINT64_MAX);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
|
||||
m_last_submit_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
SpinCommandCompleted(index);
|
||||
}
|
||||
|
||||
@@ -1906,7 +1922,7 @@ void main()
|
||||
resources.in_progress = false;
|
||||
const u32 timestamp_base = (index + NUM_COMMAND_BUFFERS) * 2;
|
||||
std::array<u64, 2> timestamps;
|
||||
VkResult res = vkGetQueryPoolResults(m_device, m_timestamp_query_pool, timestamp_base, static_cast<u32>(timestamps.size()),
|
||||
const VkResult res = vkGetQueryPoolResults(m_device, m_timestamp_query_pool, timestamp_base, static_cast<u32>(timestamps.size()),
|
||||
sizeof(timestamps), timestamps.data(), sizeof(u64), VK_QUERY_RESULT_64_BIT);
|
||||
if (res == VK_SUCCESS)
|
||||
{
|
||||
@@ -2014,7 +2030,7 @@ void main()
|
||||
constexpr u64 MAX_MAX_DEVIATION = 100000; // 100µs
|
||||
for (int i = 0; i < 4; i++) // 4 tries to get under MAX_MAX_DEVIATION
|
||||
{
|
||||
VkResult res = vkGetCalibratedTimestampsEXT(m_device, std::size(infos), infos, timestamps, &maxDeviation);
|
||||
const VkResult res = vkGetCalibratedTimestampsEXT(m_device, std::size(infos), infos, timestamps, &maxDeviation);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetCalibratedTimestampsEXT failed: ");
|
||||
|
||||
@@ -209,6 +209,7 @@ namespace Vulkan
|
||||
|
||||
// Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
|
||||
bool CheckLastPresentFail();
|
||||
bool CheckLastSubmitFail();
|
||||
|
||||
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
|
||||
// is next re-used, and the GPU has finished working with the specified resource.
|
||||
@@ -373,6 +374,7 @@ namespace Vulkan
|
||||
|
||||
StreamBuffer m_texture_upload_buffer;
|
||||
|
||||
std::atomic_bool m_last_submit_failed{false};
|
||||
std::atomic_bool m_last_present_failed{false};
|
||||
std::atomic_bool m_present_done{true};
|
||||
std::mutex m_present_mutex;
|
||||
|
||||
@@ -280,7 +280,7 @@ void Host::ReleaseHostDisplay(bool clear_state)
|
||||
g_host_display.reset();
|
||||
}
|
||||
|
||||
bool Host::BeginPresentFrame(bool frame_skip)
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
if (s_loop_number == 0)
|
||||
{
|
||||
@@ -291,12 +291,15 @@ bool Host::BeginPresentFrame(bool frame_skip)
|
||||
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
|
||||
GSQueueSnapshot(dump_path);
|
||||
}
|
||||
if (g_host_display->BeginPresent(frame_skip))
|
||||
return true;
|
||||
|
||||
// don't render imgui
|
||||
ImGuiManager::NewFrame();
|
||||
return false;
|
||||
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
|
||||
if (result != HostDisplay::PresentResult::OK)
|
||||
{
|
||||
// don't render imgui
|
||||
ImGuiManager::SkipFrame();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
|
||||
@@ -967,17 +967,17 @@ void Host::ReleaseHostDisplay(bool clear_state)
|
||||
g_emu_thread->releaseHostDisplay(clear_state);
|
||||
}
|
||||
|
||||
bool Host::BeginPresentFrame(bool frame_skip)
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
if (!g_host_display->BeginPresent(frame_skip))
|
||||
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
|
||||
if (result != HostDisplay::PresentResult::OK)
|
||||
{
|
||||
// if we're skipping a frame, we need to reset imgui's state, since
|
||||
// we won't be calling EndPresentFrame().
|
||||
ImGuiManager::NewFrame();
|
||||
return false;
|
||||
ImGuiManager::SkipFrame();
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
|
||||
@@ -641,13 +641,10 @@ bool D3D11HostDisplay::UpdateImGuiFontTexture()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || !m_swap_chain)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
// When using vsync, the time here seems to include the time for the buffer to become available.
|
||||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||
@@ -664,7 +661,7 @@ bool D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||
const CD3D11_RECT scissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||
m_context->RSSetViewports(1, &vp);
|
||||
m_context->RSSetScissorRects(1, &scissor);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::EndPresent()
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
|
||||
@@ -554,13 +554,13 @@ bool D3D12HostDisplay::UpdateImGuiFontTexture()
|
||||
return ImGui_ImplDX12_CreateFontsTexture();
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (m_device_lost)
|
||||
return HostDisplay::PresentResult::DeviceLost;
|
||||
|
||||
if (frame_skip || !m_swap_chain)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
static constexpr std::array<float, 4> clear_color = {};
|
||||
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||
@@ -574,7 +574,7 @@ bool D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||
const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
|
||||
cmdlist->RSSetViewports(1, &vp);
|
||||
cmdlist->RSSetScissorRects(1, &scissor);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::EndPresent()
|
||||
@@ -586,7 +586,11 @@ void D3D12HostDisplay::EndPresent()
|
||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||
|
||||
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
|
||||
if (!g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None))
|
||||
{
|
||||
m_device_lost = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
|
||||
if (!vsync && m_using_allow_tearing)
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
@@ -97,4 +97,5 @@ protected:
|
||||
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_allow_tearing = false;
|
||||
bool m_device_lost = false;
|
||||
};
|
||||
|
||||
@@ -229,6 +229,12 @@ void ImGuiManager::NewFrame()
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::SkipFrame()
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
NewFrame();
|
||||
}
|
||||
|
||||
void ImGuiManager::SetStyle()
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
@@ -40,6 +40,9 @@ namespace ImGuiManager
|
||||
/// Call at the beginning of the frame to set up ImGui state.
|
||||
void NewFrame();
|
||||
|
||||
/// Call when skipping rendering a frame, to update internal state.
|
||||
void SkipFrame();
|
||||
|
||||
/// Renders any on-screen display elements.
|
||||
void RenderOSD();
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
|
||||
void UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
|
||||
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) override;
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ void MetalHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y,
|
||||
|
||||
static bool s_capture_next = false;
|
||||
|
||||
bool MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
{ @autoreleasepool {
|
||||
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
|
||||
if (dev && m_capture_start_frame && dev->FrameNo() == m_capture_start_frame)
|
||||
@@ -273,7 +273,7 @@ bool MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
|
||||
m_current_drawable = MRCRetain([m_layer nextDrawable]);
|
||||
@@ -284,13 +284,13 @@ bool MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
[buf popDebugGroup];
|
||||
dev->FlushEncoders();
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
[m_pass_desc colorAttachments][0].texture = [m_current_drawable texture];
|
||||
id<MTLRenderCommandEncoder> enc = [buf renderCommandEncoderWithDescriptor:m_pass_desc];
|
||||
[enc setLabel:@"Present"];
|
||||
dev->m_current_render.encoder = MRCRetain(enc);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}}
|
||||
|
||||
void MetalHostDisplay::EndPresent()
|
||||
|
||||
@@ -335,13 +335,10 @@ bool OpenGLHostDisplay::UpdateImGuiFontTexture()
|
||||
return ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
@@ -350,7 +347,7 @@ bool OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::EndPresent()
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
|
||||
@@ -339,17 +339,18 @@ bool VulkanHostDisplay::DoneCurrent()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || !m_swap_chain)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
// Previous frame needs to be presented before we can acquire the swap chain.
|
||||
g_vulkan_context->WaitForPresentComplete();
|
||||
|
||||
// Check if the device was lost.
|
||||
if (g_vulkan_context->CheckLastSubmitFail())
|
||||
return PresentResult::DeviceLost;
|
||||
|
||||
VkResult res = m_swap_chain->AcquireNextImage();
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
@@ -367,7 +368,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
Console.Error("Failed to recreate surface after loss");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
res = m_swap_chain->AcquireNextImage();
|
||||
@@ -380,7 +381,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
|
||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +402,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
const VkRect2D scissor{{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
|
||||
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
|
||||
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::EndPresent()
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
|
||||
@@ -209,15 +209,15 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
if (!g_gs_device->Create())
|
||||
{
|
||||
if (!g_gs_device->Create())
|
||||
{
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
}
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_gs_renderer)
|
||||
{
|
||||
if (renderer == GSRendererType::Null)
|
||||
{
|
||||
g_gs_renderer = std::make_unique<GSRendererNull>();
|
||||
@@ -230,55 +230,67 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
||||
{
|
||||
g_gs_renderer = std::unique_ptr<GSRenderer>(MULTI_ISA_SELECT(makeGSRendererSW)(GSConfig.SWExtraThreads));
|
||||
}
|
||||
|
||||
g_gs_renderer->SetRegsMem(basemem);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
else
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("GS", "GS error: Exception caught in GSopen: %s", ex.what());
|
||||
g_gs_renderer.reset();
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
Console.Warning("(DoGSOpen) Using existing renderer.");
|
||||
}
|
||||
|
||||
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_host_display->SetGPUTimingEnabled(true);
|
||||
|
||||
g_gs_renderer->SetRegsMem(basemem);
|
||||
g_perfmon.Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
|
||||
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
|
||||
{
|
||||
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
|
||||
|
||||
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
|
||||
if (recreate_renderer)
|
||||
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
|
||||
|
||||
freezeData fd = {};
|
||||
if (g_gs_renderer->Freeze(&fd, true) != 0)
|
||||
std::unique_ptr<u8[]> fd_data;
|
||||
if (recreate_renderer)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to get GS freeze size");
|
||||
return false;
|
||||
}
|
||||
if (g_gs_renderer->Freeze(&fd, true) != 0)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to get GS freeze size");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size);
|
||||
fd.data = fd_data.get();
|
||||
if (g_gs_renderer->Freeze(&fd, false) != 0)
|
||||
fd_data = std::make_unique<u8[]>(fd.size);
|
||||
fd.data = fd_data.get();
|
||||
if (g_gs_renderer->Freeze(&fd, false) != 0)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to freeze GS");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to freeze GS");
|
||||
return false;
|
||||
// Make sure nothing is left over.
|
||||
g_gs_renderer->PurgePool();
|
||||
g_gs_renderer->PurgeTextureCache();
|
||||
}
|
||||
|
||||
if (recreate_display)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(true))
|
||||
if (Host::BeginPresentFrame(false) == HostDisplay::PresentResult::OK)
|
||||
Host::EndPresentFrame();
|
||||
}
|
||||
|
||||
u8* basemem = g_gs_renderer->GetRegsMem();
|
||||
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
if (recreate_renderer)
|
||||
{
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
}
|
||||
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
|
||||
@@ -327,13 +339,17 @@ bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
|
||||
}
|
||||
}
|
||||
|
||||
if (g_gs_renderer->Defrost(&fd) != 0)
|
||||
if (recreate_renderer)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to defrost");
|
||||
return false;
|
||||
if (g_gs_renderer->Defrost(&fd) != 0)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to defrost");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_gs_renderer->SetGameCRC(gamecrc);
|
||||
}
|
||||
|
||||
g_gs_renderer->SetGameCRC(gamecrc);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -698,7 +714,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||
GSConfig.DisableShaderCache != old_config.DisableShaderCache ||
|
||||
GSConfig.DisableThreadedPresentation != old_config.DisableThreadedPresentation
|
||||
);
|
||||
if (!GSreopen(do_full_restart, old_config))
|
||||
if (!GSreopen(do_full_restart, true, old_config))
|
||||
pxFailRel("Failed to do full GS reopen");
|
||||
return;
|
||||
}
|
||||
@@ -709,7 +725,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||
GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
|
||||
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight)
|
||||
{
|
||||
if (!GSreopen(false, old_config))
|
||||
if (!GSreopen(false, true, old_config))
|
||||
pxFailRel("Failed to do quick GS reopen");
|
||||
|
||||
return;
|
||||
@@ -787,7 +803,7 @@ void GSSwitchRenderer(GSRendererType new_renderer)
|
||||
const bool recreate_display = (!is_software_switch && existing_api != GetAPIForRenderer(new_renderer));
|
||||
const Pcsx2Config::GSOptions old_config(GSConfig);
|
||||
GSConfig.Renderer = new_renderer;
|
||||
if (!GSreopen(recreate_display, old_config))
|
||||
if (!GSreopen(recreate_display, true, old_config))
|
||||
pxFailRel("Failed to reopen GS for renderer switch.");
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
|
||||
int GSinit();
|
||||
void GSshutdown();
|
||||
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
|
||||
bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config);
|
||||
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config);
|
||||
void GSreset(bool hardware_reset);
|
||||
void GSclose();
|
||||
void GSgifSoftReset(u32 mask);
|
||||
|
||||
@@ -24,15 +24,6 @@ const CRC::Game CRC::m_games[] =
|
||||
{
|
||||
// Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games.
|
||||
{0x00000000, NoTitle /* NoRegion */},
|
||||
{0x9AAC5309, FFX2 /* EU */},
|
||||
{0x9AAC530C, FFX2 /* FR */},
|
||||
{0x9AAC530A, FFX2 /* ES */},
|
||||
{0x9AAC530D, FFX2 /* DE */},
|
||||
{0x9AAC530B, FFX2 /* IT */},
|
||||
{0x48FE0C71, FFX2 /* US */},
|
||||
{0x8A6D7F14, FFX2 /* JP */},
|
||||
{0xE1FD9A2D, FFX2 /* JP */}, // int.
|
||||
{0x11624CD6, FFX2 /* KO */},
|
||||
{0x08C1ED4D, HauntingGround /* EU */},
|
||||
{0x2CD5794C, HauntingGround /* EU */},
|
||||
{0x867BB945, HauntingGround /* JP */},
|
||||
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
enum Title : u32
|
||||
{
|
||||
NoTitle,
|
||||
FFX2,
|
||||
GetawayGames,
|
||||
HauntingGround,
|
||||
ICO,
|
||||
|
||||
@@ -50,6 +50,8 @@ std::unique_ptr<GSRenderer> g_gs_renderer;
|
||||
// we might be switching while the other thread reads it.
|
||||
static GSVector4 s_last_draw_rect;
|
||||
|
||||
// Last time we reset the renderer due to a GPU crash, if any.
|
||||
static Common::Timer::Value s_last_gpu_reset_time;
|
||||
|
||||
GSRenderer::GSRenderer()
|
||||
: m_shader_time_start(Common::Timer::GetCurrentValue())
|
||||
@@ -602,6 +604,40 @@ void GSJoinSnapshotThreads()
|
||||
}
|
||||
}
|
||||
|
||||
bool GSRenderer::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
const HostDisplay::PresentResult result = Host::BeginPresentFrame(frame_skip);
|
||||
if (result == HostDisplay::PresentResult::OK)
|
||||
return true;
|
||||
else if (result == HostDisplay::PresentResult::FrameSkipped)
|
||||
return false;
|
||||
|
||||
// If we're constantly crashing on something in particular, we don't want to end up in an
|
||||
// endless reset loop.. that'd probably end up leaking memory and/or crashing us for other
|
||||
// reasons. So just abort in such case.
|
||||
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
||||
if (s_last_gpu_reset_time != 0 &&
|
||||
Common::Timer::ConvertValueToSeconds(current_time - s_last_gpu_reset_time) < 15.0f)
|
||||
{
|
||||
pxFailRel("Host GPU lost too many times, device is probably completely wedged.");
|
||||
}
|
||||
s_last_gpu_reset_time = current_time;
|
||||
|
||||
// Device lost, something went really bad.
|
||||
// Let's just toss out everything, and try to hobble on.
|
||||
if (!GSreopen(true, false, GSConfig))
|
||||
{
|
||||
pxFailRel("Failed to recreate GS device after loss.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First frame after reopening is definitely going to be trash, so skip it.
|
||||
Host::AddIconOSDMessage("GSDeviceLost", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||
"Host GPU device encountered an error and was recovered. This may have broken rendering.",
|
||||
Host::OSD_CRITICAL_ERROR_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
{
|
||||
Flush(GSFlushReason::VSYNC);
|
||||
@@ -647,7 +683,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
if (skip_frame)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(true))
|
||||
if (BeginPresentFrame(true))
|
||||
Host::EndPresentFrame();
|
||||
g_gs_device->RestoreAPIState();
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
|
||||
@@ -694,7 +730,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
}
|
||||
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(false))
|
||||
if (BeginPresentFrame(false))
|
||||
{
|
||||
if (current && !blank_frame)
|
||||
{
|
||||
@@ -910,7 +946,7 @@ void GSRenderer::StopGSDump()
|
||||
void GSRenderer::PresentCurrentFrame()
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(false))
|
||||
if (BeginPresentFrame(false))
|
||||
{
|
||||
GSTexture* current = g_gs_device->GetCurrent();
|
||||
if (current)
|
||||
|
||||
@@ -23,6 +23,7 @@ class GSRenderer : public GSState
|
||||
{
|
||||
private:
|
||||
bool Merge(int field);
|
||||
bool BeginPresentFrame(bool frame_skip);
|
||||
|
||||
u64 m_shader_time_start = 0;
|
||||
|
||||
|
||||
@@ -1486,8 +1486,9 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
}
|
||||
TextureMinMaxResult tmm;
|
||||
const bool process_texture = PRIM->TME && !(PRIM->ABE && m_context->ALPHA.IsBlack() && !m_context->TEX0.TCC);
|
||||
// Disable texture mapping if the blend is black and using alpha from vertex.
|
||||
if (PRIM->TME && !(PRIM->ABE && m_context->ALPHA.IsBlack() && !m_context->TEX0.TCC))
|
||||
if (process_texture)
|
||||
{
|
||||
GIFRegCLAMP MIP_CLAMP = context->CLAMP;
|
||||
GSVector2i hash_lod_range(0, 0);
|
||||
@@ -1607,7 +1608,7 @@ void GSRendererHW::Draw()
|
||||
if (!no_ds)
|
||||
ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite(), 0, false, 0, 0, preload);
|
||||
|
||||
if (PRIM->TME)
|
||||
if (process_texture)
|
||||
{
|
||||
GIFRegCLAMP MIP_CLAMP = context->CLAMP;
|
||||
|
||||
@@ -4025,8 +4026,8 @@ GSRendererHW::CLUTDrawTestResult GSRendererHW::PossibleCLUTDraw()
|
||||
(PRIM->TME && ((m_regs->DISP[0].DISPFB.Block() == m_context->TEX0.TBP0) || (m_regs->DISP[1].DISPFB.Block() == m_context->TEX0.TBP0)) && !(m_mem.m_clut.IsInvalid() & 2)))
|
||||
return CLUTDrawTestResult::NotCLUTDraw;
|
||||
|
||||
// Ignore recursive/shuffle effects, but possible it will recursively draw, but make sure it's staying in page width
|
||||
if (PRIM->TME && m_context->TEX0.TBP0 == m_context->FRAME.Block() && (m_context->FRAME.FBW != 1 && m_context->TEX0.TBW == m_context->FRAME.FBW))
|
||||
// Ignore large render targets, make sure it's staying in page width.
|
||||
if (PRIM->TME && (m_context->FRAME.FBW != 1 && m_context->TEX0.TBW == m_context->FRAME.FBW))
|
||||
return CLUTDrawTestResult::NotCLUTDraw;
|
||||
|
||||
// Hopefully no games draw a CLUT with a CLUT, that would be evil, most likely a channel shuffle.
|
||||
|
||||
@@ -1579,6 +1579,24 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
// Look for an exact match on the targets.
|
||||
GSTextureCache::Target* src = GetExactTarget(SBP, SBW, SPSM);
|
||||
GSTextureCache::Target* dst = GetExactTarget(DBP, DBW, DPSM);
|
||||
|
||||
// Beware of the case where a game might create a larger texture by moving a bunch of chunks around.
|
||||
// We use dx/dy == 0 and the TBW check as a safeguard to make sure these go through to local memory.
|
||||
// Good test case for this is the Xenosaga I cutscene transitions, or Gradius V.
|
||||
if (src && !dst && dx == 0 && dy == 0 && ((static_cast<u32>(w) + 63) / 64) == DBW)
|
||||
{
|
||||
GIFRegTEX0 new_TEX0 = {};
|
||||
new_TEX0.TBP0 = DBP;
|
||||
new_TEX0.TBW = DBW;
|
||||
new_TEX0.PSM = DPSM;
|
||||
|
||||
const int real_height = GetTargetHeight(DBP, DBW, DPSM, h);
|
||||
const GSVector2 scale(src->m_texture->GetScale());
|
||||
dst = LookupTarget(new_TEX0, GSVector2i(static_cast<int>(w * scale.x), static_cast<int>(real_height * scale.y)), src->m_type, true);
|
||||
if (dst)
|
||||
dst->m_texture->SetScale(scale);
|
||||
}
|
||||
|
||||
if (!src || !dst || src->m_texture->GetScale() != dst->m_texture->GetScale())
|
||||
return false;
|
||||
|
||||
@@ -3070,21 +3088,6 @@ void GSTextureCache::Target::Update(bool reset_age)
|
||||
m_dirty.ClearDirty();
|
||||
return;
|
||||
}
|
||||
else if (m_type == DepthStencil && g_gs_renderer->m_game.title == CRC::FFX2)
|
||||
{
|
||||
GL_INS("ERROR: bad invalidation detected, depth buffer will be cleared");
|
||||
// FFX2 menu. Invalidation of the depth is wrongly done and only the first
|
||||
// page is invalidated. Technically a CRC hack will be better but I don't expect
|
||||
// any games to only upload a single page of data for the depth.
|
||||
//
|
||||
// FFX2 menu got another bug. I'm not sure the top-left is properly written or not. It
|
||||
// could be a gs transfer bug too due to unaligned-page transfer.
|
||||
//
|
||||
// So the quick and dirty solution is just to clean the depth buffer.
|
||||
g_gs_device->ClearDepth(m_texture);
|
||||
m_dirty.ClearDirty();
|
||||
return;
|
||||
}
|
||||
|
||||
GIFRegTEXA TEXA = {};
|
||||
|
||||
@@ -3116,7 +3119,7 @@ void GSTextureCache::Target::Update(bool reset_age)
|
||||
}
|
||||
else
|
||||
{
|
||||
int pitch = ((r.width()+3) & ~3) * 4;
|
||||
const int pitch = Common::AlignUpPow2(r.width() * sizeof(u32), 32);
|
||||
g_gs_renderer->m_mem.ReadTexture(off, r, s_unswizzle_buffer, pitch, TEXA);
|
||||
|
||||
t->Update(r, s_unswizzle_buffer, pitch);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "GLLoader.h"
|
||||
#include "GS/GS.h"
|
||||
#include "Host.h"
|
||||
#include "HostSettings.h"
|
||||
|
||||
namespace ReplaceGL
|
||||
{
|
||||
@@ -112,8 +113,8 @@ namespace GLLoader
|
||||
bool vendor_id_amd = false;
|
||||
bool vendor_id_nvidia = false;
|
||||
bool vendor_id_intel = false;
|
||||
bool mesa_driver = false;
|
||||
bool buggy_pbo = false;
|
||||
bool disable_download_pbo = false;
|
||||
|
||||
bool is_gles = false;
|
||||
bool has_dual_source_blend = false;
|
||||
@@ -131,14 +132,8 @@ namespace GLLoader
|
||||
vendor_id_amd = true;
|
||||
else if (strstr(vendor, "NVIDIA Corporation"))
|
||||
vendor_id_nvidia = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
else if (strstr(vendor, "Intel"))
|
||||
vendor_id_intel = true;
|
||||
#else
|
||||
// On linux assumes the free driver if it isn't nvidia or amd pro driver
|
||||
mesa_driver = !vendor_id_nvidia && !vendor_id_amd;
|
||||
#endif
|
||||
|
||||
if (GSConfig.OverrideGeometryShaders != -1)
|
||||
{
|
||||
@@ -225,10 +220,16 @@ namespace GLLoader
|
||||
|
||||
// Don't use PBOs when we don't have ARB_buffer_storage, orphaning buffers probably ends up worse than just
|
||||
// using the normal texture update routines and letting the driver take care of it.
|
||||
GLLoader::buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
|
||||
if (GLLoader::buggy_pbo)
|
||||
buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
|
||||
if (buggy_pbo)
|
||||
Console.Warning("Not using PBOs for texture uploads because buffer_storage is unavailable.");
|
||||
|
||||
// Give the user the option to disable PBO usage for downloads.
|
||||
// Most drivers seem to be faster with PBO.
|
||||
disable_download_pbo = Host::GetBoolSettingValue("EmuCore/GS", "DisableGLDownloadPBO", false);
|
||||
if (disable_download_pbo)
|
||||
Console.Warning("Not using PBOs for texture downloads, this may reduce performance.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,8 @@ namespace GLLoader
|
||||
extern bool vendor_id_amd;
|
||||
extern bool vendor_id_nvidia;
|
||||
extern bool vendor_id_intel;
|
||||
extern bool mesa_driver;
|
||||
extern bool buggy_pbo;
|
||||
extern bool in_replayer;
|
||||
extern bool disable_download_pbo;
|
||||
|
||||
// GL
|
||||
extern bool is_gles;
|
||||
|
||||
@@ -453,9 +453,10 @@ GSDownloadTextureOGL::~GSDownloadTextureOGL()
|
||||
|
||||
std::unique_ptr<GSDownloadTextureOGL> GSDownloadTextureOGL::Create(u32 width, u32 height, GSTexture::Format format)
|
||||
{
|
||||
const u32 buffer_size = GetBufferSize(width, height, format, GSTexture::GetCompressedBytesPerBlock(format));
|
||||
const u32 buffer_size = GetBufferSize(width, height, format, TEXTURE_UPLOAD_PITCH_ALIGNMENT);
|
||||
|
||||
const bool use_buffer_storage = (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage);
|
||||
const bool use_buffer_storage = (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage) &&
|
||||
!GLLoader::disable_download_pbo;
|
||||
if (use_buffer_storage)
|
||||
{
|
||||
GLuint buffer_id;
|
||||
|
||||
@@ -60,6 +60,13 @@ public:
|
||||
RightOrBottom
|
||||
};
|
||||
|
||||
enum class PresentResult
|
||||
{
|
||||
OK,
|
||||
FrameSkipped,
|
||||
DeviceLost
|
||||
};
|
||||
|
||||
struct AdapterAndModeList
|
||||
{
|
||||
std::vector<std::string> adapter_names;
|
||||
@@ -137,7 +144,7 @@ public:
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
virtual bool BeginPresent(bool frame_skip) = 0;
|
||||
virtual PresentResult BeginPresent(bool frame_skip) = 0;
|
||||
|
||||
/// Presents the frame to the display, and renders OSD elements.
|
||||
virtual void EndPresent() = 0;
|
||||
@@ -184,7 +191,7 @@ namespace Host
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
bool BeginPresentFrame(bool frame_skip);
|
||||
HostDisplay::PresentResult BeginPresentFrame(bool frame_skip);
|
||||
|
||||
/// Presents the frame to the display, and renders OSD elements.
|
||||
void EndPresentFrame();
|
||||
|
||||
@@ -114,9 +114,9 @@ void Host::ReleaseHostDisplay(bool clear_state)
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::BeginPresentFrame(bool frame_skip)
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
return false;
|
||||
return HostDisplay::PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
|
||||
Reference in New Issue
Block a user