Compare commits

...

15 Commits

Author SHA1 Message Date
JordanTheToaster
26e691ba93 GameDB: Update HPO on Spider Man 2
Changes HPO to HPO Special to fix rainbow garbage on the top and left side of the screen when moving.
2023-02-11 17:43:05 +00:00
Stenzek
c7352d9e10 GS: Attempt to recreate device if GPU crashes 2023-02-11 15:33:55 +00:00
Stenzek
7b8f9a54ec GS/HW: Purge FFX-2 depth clear CRC hack 2023-02-11 15:26:04 +00:00
JordanTheToaster
28980af858 GameDB: Port COP2 patch for Disneys Cars
Ports patch to fix broken collisions to PAL Disney's Cars
2023-02-11 15:24:23 +00:00
Stenzek
80dce398e0 GS/HW: Carefully allow move to create new targets
Xenosaga I does a move from BP 1C00 to E00, then from E00 to 2A00 a few
frames later for its cutscene transitions. 2A00 then gets used as a
texture and blended on top of the later frames. Because there's no
target at 2A00, the move falls back to the CPU, and E00 contains junk
which gets moved and eventually preloaded instead.

Gradius V uses moves for a screen move/wobble-like effect, by moving
chunks of the framebuffer out, then using those as a texture. It's not
broken at the moment, but it does readback (slow), and break upscaling.
2023-02-11 07:16:19 +00:00
TheTechnician27
06db8eec48 Context.cpp: fix minor typo 2023-02-11 06:46:44 +00:00
Stenzek
9c720efe46 GS/OGL: Fix possible crash downloading odd texture sizes 2023-02-11 06:43:20 +00:00
refractionpcsx2
cbf91a8d19 GS-HW: Tighten CLUT detection slightly. 2023-02-11 02:07:01 +00:00
TheTechnician27
f99414708d Readme: Two minor changes to the README (#8105) 2023-02-11 00:00:25 +00:00
refractionpcsx2
9549a6b16a GS: Fix TME processing when Alpha->IsBlack & !TEX0->TCC 2023-02-10 23:48:43 +00:00
lightningterror
3206094545 GameDB: Add full mipmap and trilinear ps2 to Hard Hitter games.
Improves ground texture rendering.
2023-02-10 22:24:33 +01:00
Stenzek
5cfae80701 GL/OpenGL: Add a hidden [EmuCore/GS] DisableGLDownloadPBO option
.. to disable the use of PBOs when reading back.
2023-02-10 14:38:21 +00:00
JordanTheToaster
b4d140c6bb GameDB: Fixes for 187 Ride or Die
Add autoflush to soften bloom and HPO Special Texture to fix misaligned bloom.
2023-02-10 14:15:51 +00:00
Stenzek
c65eb3c3ee GS/HW: Fix crash with AVX2 due to unaligned pitch 2023-02-10 14:15:33 +00:00
Mrlinkwii
eec0984dbe Gamedb: remove skipdraw from Need for Speed - Undercover 2023-02-10 14:47:52 +01:00
34 changed files with 306 additions and 174 deletions

View File

@@ -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.

View File

@@ -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"

View File

@@ -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()

View File

@@ -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);

View File

@@ -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;

View File

@@ -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: ");

View File

@@ -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;

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;
};

View File

@@ -229,6 +229,12 @@ void ImGuiManager::NewFrame()
}
}
void ImGuiManager::SkipFrame()
{
ImGui::EndFrame();
NewFrame();
}
void ImGuiManager::SetStyle()
{
ImGuiStyle& style = ImGui::GetStyle();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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()

View File

@@ -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()

View File

@@ -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;

View File

@@ -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()

View File

@@ -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;

View File

@@ -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.");
}

View File

@@ -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);

View File

@@ -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 */},

View File

@@ -23,7 +23,6 @@ public:
enum Title : u32
{
NoTitle,
FFX2,
GetawayGames,
HauntingGround,
ICO,

View File

@@ -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)

View File

@@ -23,6 +23,7 @@ class GSRenderer : public GSState
{
private:
bool Merge(int field);
bool BeginPresentFrame(bool frame_skip);
u64 m_shader_time_start = 0;

View File

@@ -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.

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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()