From b62790fd0002d07a1b1a9ac7534089c9c4ed6472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 Nov 2022 23:02:11 +0100 Subject: [PATCH 01/16] Fix translatability of the string "Stereo display shader". See #16459 --- UI/GameSettingsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index c85513f0e4..b2ad14f16e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1793,7 +1793,7 @@ void DeveloperToolsScreen::CreateViews() { list->Add(new CheckBox(&g_Config.bStereoRendering, gr->T("Stereo rendering"))); std::vector stereoShaderNames; - ChoiceWithValueDisplay *stereoShaderChoice = list->Add(new ChoiceWithValueDisplay(&g_Config.sStereoToMonoShader, "Stereo display shader", &PostShaderTranslateName)); + ChoiceWithValueDisplay *stereoShaderChoice = list->Add(new ChoiceWithValueDisplay(&g_Config.sStereoToMonoShader, gr->T("Stereo display shader"), &PostShaderTranslateName)); stereoShaderChoice->SetEnabledFunc(enableStereo); stereoShaderChoice->OnClick.Add([=](EventParams &e) { auto gr = GetI18NCategory("Graphics"); From dc962094f862eca2af2b7f58b36d3316c13650c7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 23:20:39 -0800 Subject: [PATCH 02/16] softgpu: Correctly fix inversions, matching tests. Inversions are allowed just fine, but if clipping results in coordinates outside range, the triangle should be culled. Fixes more wanted inversions. --- GPU/Software/Clipper.cpp | 15 +++++++-------- GPU/Software/TransformUnit.cpp | 25 +++++++++++-------------- GPU/Software/TransformUnit.h | 2 +- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index ccfd8f5a76..2d96b1f24c 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -50,16 +50,15 @@ inline float clip_dotprod(const ClipVertexData &vert, float A, float B, float C, } inline void clip_interpolate(ClipVertexData &dest, float t, const ClipVertexData &a, const ClipVertexData &b) { - if (different_signs(a.clippos.w, b.clippos.w)) { - if (a.clippos.w < -1.0f || b.clippos.w < -1.0f) { - dest.v.screenpos.x = 0x7FFFFFFF; - return; - } - } - + bool outsideRange = false; dest.Lerp(t, a, b); - dest.v.screenpos = TransformUnit::ClipToScreen(dest.clippos); + dest.v.screenpos = TransformUnit::ClipToScreen(dest.clippos, &outsideRange); dest.v.clipw = dest.clippos.w; + + // If the clipped coordinate is outside range, then we throw it away. + // This prevents a lot of inversions that shouldn't be drawn. + if (outsideRange) + dest.v.screenpos.x = 0x7FFFFFFF; } #define CLIP_POLY( PLANE_BIT, A, B, C, D ) \ diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index bc1f8679eb..ac8ea921a8 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -162,7 +162,7 @@ ClipCoords TransformUnit::ViewToClip(const ViewCoords &coords) { return Vec3ByMatrix44(coords, gstate.projMatrix); } -template +template static ScreenCoords ClipToScreenInternal(Vec3f scaled, const ClipCoords &coords, bool *outside_range_flag) { ScreenCoords ret; @@ -173,7 +173,7 @@ static ScreenCoords ClipToScreenInternal(Vec3f scaled, const ClipCoords &coords, // This matches hardware tests - depth is clamped when this flag is on. if (depthClamp) { // Note: if the depth is clipped (z/w <= -1.0), the outside_range_flag should NOT be set, even for x and y. - if (writeOutsideFlag && coords.z > -coords.w && (scaled.x >= SCREEN_BOUND || scaled.y >= SCREEN_BOUND || scaled.x < 0 || scaled.y < 0)) { + if ((alwaysCheckRange || coords.z > -coords.w) && (scaled.x >= SCREEN_BOUND || scaled.y >= SCREEN_BOUND || scaled.x < 0 || scaled.y < 0)) { *outside_range_flag = true; } @@ -181,7 +181,7 @@ static ScreenCoords ClipToScreenInternal(Vec3f scaled, const ClipCoords &coords, scaled.z = 0.f; else if (scaled.z > 65535.0f) scaled.z = 65535.0f; - } else if (writeOutsideFlag && (scaled.x > SCREEN_BOUND || scaled.y >= SCREEN_BOUND || scaled.x < 0 || scaled.y < 0)) { + } else if (scaled.x > SCREEN_BOUND || scaled.y >= SCREEN_BOUND || scaled.x < 0 || scaled.y < 0) { *outside_range_flag = true; } @@ -209,17 +209,13 @@ static inline ScreenCoords ClipToScreenInternal(const ClipCoords &coords, bool * float z = coords.z * zScale / coords.w + zCenter; if (gstate.isDepthClampEnabled()) { - if (outside_range_flag) - return ClipToScreenInternal(Vec3f(x, y, z), coords, outside_range_flag); - return ClipToScreenInternal(Vec3f(x, y, z), coords, outside_range_flag); + return ClipToScreenInternal(Vec3f(x, y, z), coords, outside_range_flag); } - if (outside_range_flag) - return ClipToScreenInternal(Vec3f(x, y, z), coords, outside_range_flag); - return ClipToScreenInternal(Vec3f(x, y, z), coords, outside_range_flag); + return ClipToScreenInternal(Vec3f(x, y, z), coords, outside_range_flag); } -ScreenCoords TransformUnit::ClipToScreen(const ClipCoords &coords) { - return ClipToScreenInternal(coords, nullptr); +ScreenCoords TransformUnit::ClipToScreen(const ClipCoords &coords, bool *outsideRangeFlag) { + return ClipToScreenInternal(coords, outsideRangeFlag); } ScreenCoords TransformUnit::DrawingToScreen(const DrawingCoords &coords, u16 z) { @@ -317,9 +313,9 @@ void ComputeTransformState(TransformState *state, const VertexReader &vreader) { } if (gstate.isDepthClampEnabled()) - state->roundToScreen = &ClipToScreenInternal; + state->roundToScreen = &ClipToScreenInternal; else - state->roundToScreen = &ClipToScreenInternal; + state->roundToScreen = &ClipToScreenInternal; } ClipVertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState &state) { @@ -977,7 +973,8 @@ bool TransformUnit::GetCurrentSimpleVertices(int count, std::vector coord, this is negative and force-scissors. From d763dca024fb0ac4d9ba28fa92efaa7f0332fb17 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Dec 2022 00:17:14 -0800 Subject: [PATCH 03/16] GPU: Correct fetch assert. --- GPU/Common/FragmentShaderGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index eba7b966da..f1e2aeb4cd 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -172,7 +172,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool fetchFramebuffer = needFramebufferRead && id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH); bool readFramebufferTex = needFramebufferRead && !id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH); - if (fetchFramebuffer && (compat.shaderLanguage != GLSL_VULKAN || compat.shaderLanguage != GLSL_3xx)) { + if (fetchFramebuffer && (compat.shaderLanguage != GLSL_VULKAN && compat.shaderLanguage != GLSL_3xx)) { *errorString = "framebuffer fetch requires GLSL: vulkan or 3xx"; return false; } From fc5dcd0c168f8cf6b6aa4c50b56736c63a6ca864 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Dec 2022 00:59:22 -0800 Subject: [PATCH 04/16] GLES: Correct unit tests for framebuffer fetch. --- GPU/Common/FragmentShaderGenerator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index f1e2aeb4cd..93f30f3323 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -172,7 +172,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool fetchFramebuffer = needFramebufferRead && id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH); bool readFramebufferTex = needFramebufferRead && !id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH); - if (fetchFramebuffer && (compat.shaderLanguage != GLSL_VULKAN && compat.shaderLanguage != GLSL_3xx)) { + if (fetchFramebuffer && compat.shaderLanguage != GLSL_VULKAN && (compat.shaderLanguage != GLSL_3xx || !compat.lastFragData)) { *errorString = "framebuffer fetch requires GLSL: vulkan or 3xx"; return false; } @@ -568,7 +568,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (compat.shaderLanguage == GLSL_3xx) { WRITE(p, " lowp vec4 destColor = %s;\n", compat.lastFragData); } else if (compat.shaderLanguage == GLSL_VULKAN) { - WRITE(p, " lowp vec4 destColor = subpassLoad(inputColor);\n", compat.lastFragData); + WRITE(p, " lowp vec4 destColor = subpassLoad(inputColor);\n"); } else { _assert_msg_(false, "Need fetch destColor, but not a compatible language"); } From 9a108a46d462c1ca2e87387de9f6c92b26e48b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Dec 2022 10:17:31 +0100 Subject: [PATCH 05/16] Disable the PowerVR swapchain hack after driver version 1.386.1368. See #15773 --- Common/GPU/Vulkan/VulkanContext.cpp | 20 +++++++++++++++----- UI/DisplayLayoutScreen.cpp | 23 ++++++++++++++--------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 389eb6dbbe..19ecb40588 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -1136,11 +1136,21 @@ bool VulkanContext::InitSwapchain() { INFO_LOG(G3D, "Transform supported: %s current: %s chosen: %s", supportedTransforms.c_str(), currentTransform.c_str(), preTransformStr.c_str()); if (physicalDeviceProperties_[physical_device_].properties.vendorID == VULKAN_VENDOR_IMGTEC) { - INFO_LOG(G3D, "Applying PowerVR hack (rounding off the width!)"); - // Swap chain width hack to avoid issue #11743 (PowerVR driver bug). - // To keep the size consistent even with pretransform, do this after the swap. Should be fine. - // This is fixed in newer PowerVR drivers but I don't know the cutoff. - swapChainExtent_.width &= ~31; + u32 driverVersion = physicalDeviceProperties_[physical_device_].properties.driverVersion; + // Cutoff the hack at driver version 1.386.1368 (0x00582558, see issue #15773). + if (driverVersion < 0x00582558) { + INFO_LOG(G3D, "Applying PowerVR hack (rounding off the width!) driverVersion=%08x", driverVersion); + // Swap chain width hack to avoid issue #11743 (PowerVR driver bug). + // To keep the size consistent even with pretransform, do this after the swap. Should be fine. + // This is fixed in newer PowerVR drivers but I don't know the cutoff. + swapChainExtent_.width &= ~31; + + // TODO: Also modify display_xres/display_yres appropriately for scissors to match. + // This will get a bit messy. Ideally we should remove that logic from app-android.cpp + // and move it here, but the OpenGL code still needs it. + } else { + INFO_LOG(G3D, "PowerVR driver version new enough (%08x), not applying swapchain width hack", driverVersion); + } } VkSwapchainCreateInfoKHR swap_chain_info{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index ed741ec6c9..18d52d8360 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -63,15 +63,14 @@ public: bool Touch(const TouchInput &touch) { int mode = mode_ ? mode_->GetSelection() : 0; - const Bounds &screenBounds = bounds_; if ((touch.flags & TOUCH_MOVE) != 0 && dragging_) { float relativeTouchX = touch.x - startX_; float relativeTouchY = touch.y - startY_; switch (mode) { case MODE_MOVE: - g_Config.fDisplayOffsetX = clamp_value(startDisplayOffsetX_ + relativeTouchX / screenBounds.w, 0.0f, 1.0f); - g_Config.fDisplayOffsetY = clamp_value(startDisplayOffsetY_ + relativeTouchY / screenBounds.h, 0.0f, 1.0f); + g_Config.fDisplayOffsetX = clamp_value(startDisplayOffsetX_ + relativeTouchX / bounds_.w, 0.0f, 1.0f); + g_Config.fDisplayOffsetY = clamp_value(startDisplayOffsetY_ + relativeTouchY / bounds_.h, 0.0f, 1.0f); break; case MODE_RESIZE: { @@ -84,12 +83,18 @@ public: } if ((touch.flags & TOUCH_DOWN) != 0 && !dragging_) { - dragging_ = true; - startX_ = touch.x; - startY_ = touch.y; - startDisplayOffsetX_ = g_Config.fDisplayOffsetX; - startDisplayOffsetY_ = g_Config.fDisplayOffsetY; - startScale_ = g_Config.fDisplayScale; + // Check that we're in the central 80% of the screen. + // If outside, it may be a drag from displaying the back button on phones + // where you have to drag from the side, etc. + if (touch.x >= bounds_.w * 0.1f && touch.x <= bounds_.w * 0.9f && + touch.y >= bounds_.h * 0.1f && touch.y <= bounds_.h * 0.9f) { + dragging_ = true; + startX_ = touch.x; + startY_ = touch.y; + startDisplayOffsetX_ = g_Config.fDisplayOffsetX; + startDisplayOffsetY_ = g_Config.fDisplayOffsetY; + startScale_ = g_Config.fDisplayScale; + } } if ((touch.flags & TOUCH_UP) != 0 && dragging_) { From 2adc18cfba331648d88987e193a106e15be245e4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 17:52:42 -0800 Subject: [PATCH 06/16] softgpu: Correct src/dst overlap in block transfer. If dest is inside src, it seems to consistently copy in blocks of 64 bytes. --- GPU/Software/SoftGpu.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 7a824fe7b0..27601247fd 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -803,7 +803,9 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { DEBUG_LOG(G3D, "Block transfer: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); - if (srcStride == dstStride && (u32)width == srcStride) { + bool srcDstOverlap = src + srcSize > dst && dst + dstSize > src; + + if (srcStride == dstStride && (u32)width == srcStride && !srcDstOverlap) { u32 srcLineStartAddr = srcBasePtr + (srcY * srcStride + srcX) * bpp; u32 dstLineStartAddr = dstBasePtr + (dstY * dstStride + dstX) * bpp; @@ -823,6 +825,20 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); memcpy(dstp, srcp, bytesToCopy); GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); + } else if (srcDstOverlap) { + for (int y = 0; y < height; y++) { + u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; + u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; + const u8 *srcp = Memory::GetPointer(srcLineStartAddr); + u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); + + u32 totalToCopy = width * bpp; + for (u32 i = 0; i < totalToCopy; i += 64) { + u32 chunk = i + 64 > totalToCopy ? totalToCopy - i : 64; + memmove(dstp + i, srcp + i, chunk); + } + GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, totalToCopy); + } } else { for (int y = 0; y < height; y++) { u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; From 68515aaee2ae000ae0ffff07909ae965f998b723 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 18:29:47 -0800 Subject: [PATCH 07/16] softgpu: Account for width!=stride in xfer checks. --- GPU/Software/SoftGpu.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 27601247fd..2c5874d3c7 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -792,10 +792,11 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { int bpp = gstate.getTransferBpp(); + // Use height less one to account for width, which can be greater or less than stride. const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; - const uint32_t srcSize = height * srcStride * bpp; + const uint32_t srcSize = (height - 1) * srcStride * bpp + width * bpp; const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; - const uint32_t dstSize = height * dstStride * bpp; + const uint32_t dstSize = (height - 1) * dstStride * bpp + width * bpp; // Need to flush both source and target, so we overwrite properly. drawEngine_->transformUnit.FlushIfOverlap("blockxfer", false, src, srcStride, width * bpp, height); From 1c5f0d575caa9d7c300a0531851b4c773b4219ac Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 18:40:25 -0800 Subject: [PATCH 08/16] softgpu: Improve meminfo detail on block transfer. Gaps are useful to understand. --- GPU/Software/SoftGpu.cpp | 106 +++++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 2c5874d3c7..8872979b04 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -29,6 +29,7 @@ #include "Core/Core.h" #include "Core/Debugger/MemBlockInfo.h" #include "Core/MemMap.h" +#include "Core/MemMapHelpers.h" #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceGe.h" #include "Core/MIPS/MIPS.h" @@ -792,6 +793,12 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { int bpp = gstate.getTransferBpp(); + // For VRAM, we wrap around when outside valid memory (mirrors still work.) + if ((srcBasePtr & 0x04800000) == 0x04800000) + srcBasePtr &= ~0x00800000; + if ((dstBasePtr & 0x04800000) == 0x04800000) + dstBasePtr &= ~0x00800000; + // Use height less one to account for width, which can be greater or less than stride. const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; const uint32_t srcSize = (height - 1) * srcStride * bpp + width * bpp; @@ -805,68 +812,93 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { DEBUG_LOG(G3D, "Block transfer: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); bool srcDstOverlap = src + srcSize > dst && dst + dstSize > src; + bool srcValid = Memory::IsValidRange(src, srcSize); + bool dstValid = Memory::IsValidRange(dst, dstSize); + bool srcWraps = Memory::IsVRAMAddress(srcBasePtr) && !srcValid; + bool dstWraps = Memory::IsVRAMAddress(dstBasePtr) && !dstValid; - if (srcStride == dstStride && (u32)width == srcStride && !srcDstOverlap) { + // Simple case: just a straight copy, no overlap or wrapping. + if (srcStride == dstStride && (u32)width == srcStride && !srcDstOverlap && srcValid && dstValid) { u32 srcLineStartAddr = srcBasePtr + (srcY * srcStride + srcX) * bpp; u32 dstLineStartAddr = dstBasePtr + (dstY * dstStride + dstX) * bpp; - u32 bytesToCopy = width * height * bpp; - if (!Memory::IsValidRange(srcLineStartAddr, bytesToCopy)) { - // What should we do here? Memset zeroes to the dest instead? - return; - } - if (!Memory::IsValidRange(dstLineStartAddr, bytesToCopy)) { - // What should we do here? Just not do the write, or partial write if - // some part is in-range? - return; - } - const u8 *srcp = Memory::GetPointer(srcLineStartAddr); u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); memcpy(dstp, srcp, bytesToCopy); GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); - } else if (srcDstOverlap) { + + if (MemBlockInfoDetailed(bytesToCopy)) { + const std::string tag = GetMemWriteTagAt("GPUBlockTransfer/", src, bytesToCopy); + NotifyMemInfo(MemBlockFlags::READ, src, bytesToCopy, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, bytesToCopy, tag.c_str(), tag.size()); + } + } else if (srcDstOverlap || srcWraps || dstWraps) { + u32 bytesToCopy = width * bpp; + static std::string tag; + bool notifyDetail = MemBlockInfoDetailed(bytesToCopy); + bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize); + if (notifyDetail || notifyAll) { + tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); + } + + // TODO: Handle wrapping. for (int y = 0; y < height; y++) { u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; const u8 *srcp = Memory::GetPointer(srcLineStartAddr); u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); - u32 totalToCopy = width * bpp; - for (u32 i = 0; i < totalToCopy; i += 64) { - u32 chunk = i + 64 > totalToCopy ? totalToCopy - i : 64; + for (u32 i = 0; i < bytesToCopy; i += 64) { + u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; memmove(dstp + i, srcp + i, chunk); } - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, totalToCopy); + GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); + + // If we're tracking detail, it's useful to have the gaps illustrated properly. + if (notifyDetail) { + NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + } } - } else { + + if (notifyAll) { + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + } + } else if (srcValid && dstValid) { + u32 bytesToCopy = width * bpp; + static std::string tag; + bool notifyDetail = MemBlockInfoDetailed(bytesToCopy); + bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize); + if (notifyDetail || notifyAll) { + tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); + } + for (int y = 0; y < height; y++) { u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; - u32 bytesToCopy = width * bpp; - if (!Memory::IsValidRange(srcLineStartAddr, bytesToCopy)) { - // What should we do here? Due to the y loop, in this case we might have - // performed a partial copy. Probably fine. - break; - } - if (!Memory::IsValidRange(dstLineStartAddr, bytesToCopy)) { - // What should we do here? Due to the y loop, in this case we might have - // performed a partial copy. Probably fine. - break; - } const u8 *srcp = Memory::GetPointer(srcLineStartAddr); u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); - memcpy(dstp, srcp, width * bpp); - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, width * bpp); - } - } + memcpy(dstp, srcp, bytesToCopy); + GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); - if (MemBlockInfoDetailed(srcSize, dstSize)) { - const std::string tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); - NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + // If we're tracking detail, it's useful to have the gaps illustrated properly. + if (notifyDetail) { + NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + } + } + + if (notifyAll) { + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + } + } else { + // This seems to cause the GE to require a break/reset on a PSP. + // TODO: Handle that and figure out which bytes are still copied? + ERROR_LOG_REPORT(G3D, "Block transfer invalid: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); } // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. From 3589c1e82606dbad8758ad4efd97da7c279715d7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 19:04:52 -0800 Subject: [PATCH 09/16] softgpu: Handle block transfer VRAM wrapping. Note: mirrors still act as mirrors, swizzle and all. --- GPU/Software/SoftGpu.cpp | 105 +++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 8872979b04..27c96844a5 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -833,38 +833,111 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { NotifyMemInfo(MemBlockFlags::READ, src, bytesToCopy, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, bytesToCopy, tag.c_str(), tag.size()); } - } else if (srcDstOverlap || srcWraps || dstWraps) { + } else if ((srcDstOverlap || srcWraps || dstWraps) && (srcValid || srcWraps) && (dstValid || dstWraps)) { + // This path means we have either src/dst overlap, OR one or both of src and dst wrap. + // This should be uncommon so it's the slowest path. u32 bytesToCopy = width * bpp; static std::string tag; - bool notifyDetail = MemBlockInfoDetailed(bytesToCopy); + bool notifyDetail = MemBlockInfoDetailed(srcWraps || dstWraps ? 64 : bytesToCopy); bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize); if (notifyDetail || notifyAll) { tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); } - // TODO: Handle wrapping. + auto notifyingMemmove = [&](u32 d, u32 s, u32 sz) { + const u8 *srcp = Memory::GetPointer(s); + u8 *dstp = Memory::GetPointerWrite(d); + memmove(dstp, srcp, sz); + GPURecord::NotifyMemcpy(d, s, sz); + + if (notifyDetail) { + NotifyMemInfo(MemBlockFlags::READ, s, sz, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, d, sz, tag.c_str(), tag.size()); + } + }; + for (int y = 0; y < height; y++) { u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; - const u8 *srcp = Memory::GetPointer(srcLineStartAddr); - u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); + // If we already passed a wrap, we can use the quicker path. + if ((srcLineStartAddr & 0x04800000) == 0x04800000) + srcLineStartAddr &= ~0x00800000; + if ((dstLineStartAddr & 0x04800000) == 0x04800000) + dstLineStartAddr &= ~0x00800000; + // These flags mean there's a wrap inside this line. + bool srcLineWrap = !Memory::IsValidRange(srcLineStartAddr, bytesToCopy); + bool dstLineWrap = !Memory::IsValidRange(dstLineStartAddr, bytesToCopy); - for (u32 i = 0; i < bytesToCopy; i += 64) { - u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; - memmove(dstp + i, srcp + i, chunk); - } - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); + if (!srcLineWrap && !dstLineWrap) { + const u8 *srcp = Memory::GetPointer(srcLineStartAddr); + u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); + for (u32 i = 0; i < bytesToCopy; i += 64) { + u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; + memmove(dstp + i, srcp + i, chunk); + } + GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); - // If we're tracking detail, it's useful to have the gaps illustrated properly. - if (notifyDetail) { - NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + // If we're tracking detail, it's useful to have the gaps illustrated properly. + if (notifyDetail) { + NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + } + } else { + // We can wrap at any point, so along with overlap this gets a bit complicated. + // We're just going to do this the slow and easy way. + u32 srcLinePos = srcLineStartAddr; + u32 dstLinePos = dstLineStartAddr; + for (u32 i = 0; i < bytesToCopy; i += 64) { + u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; + u32 srcValid = Memory::ValidSize(srcLinePos, chunk); + u32 dstValid = Memory::ValidSize(dstLinePos, chunk); + + // First chunk, for which both are valid. + u32 bothSize = std::min(srcValid, dstValid); + if (bothSize != 0) + notifyingMemmove(dstLinePos, srcLinePos, bothSize); + + // Now, whichever side has more valid (or the rest, if only one side must wrap.) + u32 exclusiveSize = std::max(srcValid, dstValid) - bothSize; + if (exclusiveSize != 0 && srcValid >= dstValid) { + notifyingMemmove(PSP_GetVidMemBase(), srcLineStartAddr + bothSize, exclusiveSize); + } else if (exclusiveSize != 0 && srcValid < dstValid) { + notifyingMemmove(dstLineStartAddr + bothSize, PSP_GetVidMemBase(), exclusiveSize); + } + + // Finally, if both src and dst wrapped, that portion. + u32 wrappedSize = chunk - bothSize - exclusiveSize; + if (wrappedSize != 0 && srcValid >= dstValid) { + notifyingMemmove(PSP_GetVidMemBase() + exclusiveSize, PSP_GetVidMemBase(), wrappedSize); + } else if (wrappedSize != 0 && srcValid < dstValid) { + notifyingMemmove(PSP_GetVidMemBase(), PSP_GetVidMemBase() + exclusiveSize, wrappedSize); + } + + srcLinePos += chunk; + dstLinePos += chunk; + if ((srcLinePos & 0x04800000) == 0x04800000) + srcLinePos &= ~0x00800000; + if ((dstLinePos & 0x04800000) == 0x04800000) + dstLinePos &= ~0x00800000; + } } } if (notifyAll) { - NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + if (srcWraps) { + u32 validSize = Memory::ValidSize(src, srcSize); + NotifyMemInfo(MemBlockFlags::READ, src, validSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::READ, PSP_GetVidMemBase(), srcSize - validSize, tag.c_str(), tag.size()); + } else { + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); + } + if (dstWraps) { + u32 validSize = Memory::ValidSize(dst, dstSize); + NotifyMemInfo(MemBlockFlags::WRITE, dst, validSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetVidMemBase(), dstSize - validSize, tag.c_str(), tag.size()); + } else { + NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + } } } else if (srcValid && dstValid) { u32 bytesToCopy = width * bpp; From cbe96d37742f316b0e4e5ffa2d8e8ea9c6458b71 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 19:12:06 -0800 Subject: [PATCH 10/16] GPU: Use common block transfer logic in all cases. --- GPU/GPUCommon.cpp | 220 ++++++++++++++++++++++++++++++--------- GPU/GPUCommon.h | 2 +- GPU/Software/SoftGpu.cpp | 178 +------------------------------ 3 files changed, 176 insertions(+), 224 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1560d3fe49..11f2caf8b6 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -3057,72 +3057,192 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { DEBUG_LOG(G3D, "Block transfer: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); - if (!Memory::IsValidAddress(srcBasePtr)) { - ERROR_LOG_REPORT(G3D, "BlockTransfer: Bad source transfer address %08x!", srcBasePtr); - return; - } + // For VRAM, we wrap around when outside valid memory (mirrors still work.) + if ((srcBasePtr & 0x04800000) == 0x04800000) + srcBasePtr &= ~0x00800000; + if ((dstBasePtr & 0x04800000) == 0x04800000) + dstBasePtr &= ~0x00800000; - if (!Memory::IsValidAddress(dstBasePtr)) { - ERROR_LOG_REPORT(G3D, "BlockTransfer: Bad destination transfer address %08x!", dstBasePtr); - return; - } + // Use height less one to account for width, which can be greater or less than stride. + const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; + const uint32_t srcSize = (height - 1) * srcStride * bpp + width * bpp; + const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; + const uint32_t dstSize = (height - 1) * dstStride * bpp + width * bpp; - // Check that the last address of both source and dest are valid addresses - - u32 srcLastAddr = srcBasePtr + ((srcY + height - 1) * srcStride + (srcX + width - 1)) * bpp; - u32 dstLastAddr = dstBasePtr + ((dstY + height - 1) * dstStride + (dstX + width - 1)) * bpp; - - if (!Memory::IsValidAddress(srcLastAddr)) { - ERROR_LOG_N_TIMES(bad_xfer_src, 5, G3D, "Bottom-right corner of source of %dx%d src=(%d, %d) block transfer from buffer at %08x is at an invalid address: %08x. Skipping.", width, height, srcX, srcY, srcBasePtr, srcLastAddr); - return; - } - if (!Memory::IsValidAddress(dstLastAddr)) { - ERROR_LOG_N_TIMES(bad_xfer_src, 5, G3D, "Bottom-right corner of destination of %dx%d dst=(%d, %d) block transfer to buffer at %08x is at an invalid address: %08x. Skipping.", width, height, dstX, dstY, dstBasePtr, srcLastAddr); - return; - } + bool srcDstOverlap = src + srcSize > dst && dst + dstSize > src; + bool srcValid = Memory::IsValidRange(src, srcSize); + bool dstValid = Memory::IsValidRange(dst, dstSize); + bool srcWraps = Memory::IsVRAMAddress(srcBasePtr) && !srcValid; + bool dstWraps = Memory::IsVRAMAddress(dstBasePtr) && !dstValid; // Tell the framebuffer manager to take action if possible. If it does the entire thing, let's just return. - if (!framebufferManager_->NotifyBlockTransferBefore(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason)) { + if (!framebufferManager_ || !framebufferManager_->NotifyBlockTransferBefore(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason)) { // Do the copy! (Hm, if we detect a drawn video frame (see below) then we could maybe skip this?) // Can use GetPointerUnchecked because we checked the addresses above. We could also avoid them // entirely by walking a couple of pointers... - if (srcStride == dstStride && (u32)width == srcStride) { - // Common case in God of War, let's do it all in one chunk. + + // Simple case: just a straight copy, no overlap or wrapping. + if (srcStride == dstStride && (u32)width == srcStride && !srcDstOverlap && srcValid && dstValid) { u32 srcLineStartAddr = srcBasePtr + (srcY * srcStride + srcX) * bpp; u32 dstLineStartAddr = dstBasePtr + (dstY * dstStride + dstX) * bpp; - const u8 *src = Memory::GetPointerUnchecked(srcLineStartAddr); - u8 *dst = Memory::GetPointerWriteUnchecked(dstLineStartAddr); - memcpy(dst, src, width * height * bpp); - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, width * height * bpp); - } else { + u32 bytesToCopy = width * height * bpp; + + const u8 *srcp = Memory::GetPointer(srcLineStartAddr); + u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); + memcpy(dstp, srcp, bytesToCopy); + GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); + + if (MemBlockInfoDetailed(bytesToCopy)) { + const std::string tag = GetMemWriteTagAt("GPUBlockTransfer/", src, bytesToCopy); + NotifyMemInfo(MemBlockFlags::READ, src, bytesToCopy, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, bytesToCopy, tag.c_str(), tag.size()); + } + } else if ((srcDstOverlap || srcWraps || dstWraps) && (srcValid || srcWraps) && (dstValid || dstWraps)) { + // This path means we have either src/dst overlap, OR one or both of src and dst wrap. + // This should be uncommon so it's the slowest path. + u32 bytesToCopy = width * bpp; + static std::string tag; + bool notifyDetail = MemBlockInfoDetailed(srcWraps || dstWraps ? 64 : bytesToCopy); + bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize); + if (notifyDetail || notifyAll) { + tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); + } + + auto notifyingMemmove = [&](u32 d, u32 s, u32 sz) { + const u8 *srcp = Memory::GetPointer(s); + u8 *dstp = Memory::GetPointerWrite(d); + memmove(dstp, srcp, sz); + GPURecord::NotifyMemcpy(d, s, sz); + + if (notifyDetail) { + NotifyMemInfo(MemBlockFlags::READ, s, sz, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, d, sz, tag.c_str(), tag.size()); + } + }; + + for (int y = 0; y < height; y++) { + u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; + u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; + // If we already passed a wrap, we can use the quicker path. + if ((srcLineStartAddr & 0x04800000) == 0x04800000) + srcLineStartAddr &= ~0x00800000; + if ((dstLineStartAddr & 0x04800000) == 0x04800000) + dstLineStartAddr &= ~0x00800000; + // These flags mean there's a wrap inside this line. + bool srcLineWrap = !Memory::IsValidRange(srcLineStartAddr, bytesToCopy); + bool dstLineWrap = !Memory::IsValidRange(dstLineStartAddr, bytesToCopy); + + if (!srcLineWrap && !dstLineWrap) { + const u8 *srcp = Memory::GetPointer(srcLineStartAddr); + u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); + for (u32 i = 0; i < bytesToCopy; i += 64) { + u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; + memmove(dstp + i, srcp + i, chunk); + } + GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); + + // If we're tracking detail, it's useful to have the gaps illustrated properly. + if (notifyDetail) { + NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + } + } else { + // We can wrap at any point, so along with overlap this gets a bit complicated. + // We're just going to do this the slow and easy way. + u32 srcLinePos = srcLineStartAddr; + u32 dstLinePos = dstLineStartAddr; + for (u32 i = 0; i < bytesToCopy; i += 64) { + u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; + u32 srcValid = Memory::ValidSize(srcLinePos, chunk); + u32 dstValid = Memory::ValidSize(dstLinePos, chunk); + + // First chunk, for which both are valid. + u32 bothSize = std::min(srcValid, dstValid); + if (bothSize != 0) + notifyingMemmove(dstLinePos, srcLinePos, bothSize); + + // Now, whichever side has more valid (or the rest, if only one side must wrap.) + u32 exclusiveSize = std::max(srcValid, dstValid) - bothSize; + if (exclusiveSize != 0 && srcValid >= dstValid) { + notifyingMemmove(PSP_GetVidMemBase(), srcLineStartAddr + bothSize, exclusiveSize); + } else if (exclusiveSize != 0 && srcValid < dstValid) { + notifyingMemmove(dstLineStartAddr + bothSize, PSP_GetVidMemBase(), exclusiveSize); + } + + // Finally, if both src and dst wrapped, that portion. + u32 wrappedSize = chunk - bothSize - exclusiveSize; + if (wrappedSize != 0 && srcValid >= dstValid) { + notifyingMemmove(PSP_GetVidMemBase() + exclusiveSize, PSP_GetVidMemBase(), wrappedSize); + } else if (wrappedSize != 0 && srcValid < dstValid) { + notifyingMemmove(PSP_GetVidMemBase(), PSP_GetVidMemBase() + exclusiveSize, wrappedSize); + } + + srcLinePos += chunk; + dstLinePos += chunk; + if ((srcLinePos & 0x04800000) == 0x04800000) + srcLinePos &= ~0x00800000; + if ((dstLinePos & 0x04800000) == 0x04800000) + dstLinePos &= ~0x00800000; + } + } + } + + if (notifyAll) { + if (srcWraps) { + u32 validSize = Memory::ValidSize(src, srcSize); + NotifyMemInfo(MemBlockFlags::READ, src, validSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::READ, PSP_GetVidMemBase(), srcSize - validSize, tag.c_str(), tag.size()); + } else { + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); + } + if (dstWraps) { + u32 validSize = Memory::ValidSize(dst, dstSize); + NotifyMemInfo(MemBlockFlags::WRITE, dst, validSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetVidMemBase(), dstSize - validSize, tag.c_str(), tag.size()); + } else { + NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + } + } + } else if (srcValid && dstValid) { + u32 bytesToCopy = width * bpp; + static std::string tag; + bool notifyDetail = MemBlockInfoDetailed(bytesToCopy); + bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize); + if (notifyDetail || notifyAll) { + tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); + } + for (int y = 0; y < height; y++) { u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; - const u8 *src = Memory::GetPointerUnchecked(srcLineStartAddr); - u8 *dst = Memory::GetPointerWriteUnchecked(dstLineStartAddr); - memcpy(dst, src, width * bpp); - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, width * bpp); + const u8 *srcp = Memory::GetPointer(srcLineStartAddr); + u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); + memcpy(dstp, srcp, bytesToCopy); + GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); + + // If we're tracking detail, it's useful to have the gaps illustrated properly. + if (notifyDetail) { + NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); + } } + + if (notifyAll) { + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); + NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + } + } else { + // This seems to cause the GE to require a break/reset on a PSP. + // TODO: Handle that and figure out which bytes are still copied? + ERROR_LOG_REPORT_ONCE(invalidtransfer, G3D, "Block transfer invalid: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); } - // Fixes Gran Turismo's funky text issue, since it overwrites the current texture. - textureCache_->Invalidate(dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, GPU_INVALIDATE_HINT); - framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason); - } - - const uint32_t numBytes = width * height * bpp; - const uint32_t srcSize = height * srcStride * bpp; - const uint32_t dstSize = height * dstStride * bpp; - // We do the check here on the number of bytes to avoid marking really tiny images. - // Helps perf in GT menu which does insane amounts of these, one for each text character per frame. - if (MemBlockInfoDetailed(numBytes, numBytes)) { - const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; - const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; - char tag[128]; - size_t tagSize = FormatMemWriteTagAt(tag, sizeof(tag), "GPUBlockTransfer/", src, srcSize); - NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag, tagSize); - NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag, tagSize); + if (framebufferManager_) { + // Fixes Gran Turismo's funky text issue, since it overwrites the current texture. + textureCache_->Invalidate(dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, GPU_INVALIDATE_HINT); + framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason); + } } // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index bfcf47d415..76b14bdc9e 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -301,6 +301,7 @@ protected: void UpdateState(GPURunState state); void FastLoadBoneMatrix(u32 target); void FlushImm(); + void DoBlockTransfer(u32 skipDrawReason); // TODO: Unify this. virtual void FinishDeferred() {} @@ -406,7 +407,6 @@ protected: private: void CheckDepthUsage(VirtualFramebuffer *vfb); - void DoBlockTransfer(u32 skipDrawReason); void DoExecuteCall(u32 target); void PopDLQueue(); void CheckDrawSync(); diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 27c96844a5..d7e6e3346f 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -793,12 +793,6 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { int bpp = gstate.getTransferBpp(); - // For VRAM, we wrap around when outside valid memory (mirrors still work.) - if ((srcBasePtr & 0x04800000) == 0x04800000) - srcBasePtr &= ~0x00800000; - if ((dstBasePtr & 0x04800000) == 0x04800000) - dstBasePtr &= ~0x00800000; - // Use height less one to account for width, which can be greater or less than stride. const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; const uint32_t srcSize = (height - 1) * srcStride * bpp + width * bpp; @@ -806,176 +800,14 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { const uint32_t dstSize = (height - 1) * dstStride * bpp + width * bpp; // Need to flush both source and target, so we overwrite properly. - drawEngine_->transformUnit.FlushIfOverlap("blockxfer", false, src, srcStride, width * bpp, height); - drawEngine_->transformUnit.FlushIfOverlap("blockxfer", true, dst, dstStride, width * bpp, height); - - DEBUG_LOG(G3D, "Block transfer: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); - - bool srcDstOverlap = src + srcSize > dst && dst + dstSize > src; - bool srcValid = Memory::IsValidRange(src, srcSize); - bool dstValid = Memory::IsValidRange(dst, dstSize); - bool srcWraps = Memory::IsVRAMAddress(srcBasePtr) && !srcValid; - bool dstWraps = Memory::IsVRAMAddress(dstBasePtr) && !dstValid; - - // Simple case: just a straight copy, no overlap or wrapping. - if (srcStride == dstStride && (u32)width == srcStride && !srcDstOverlap && srcValid && dstValid) { - u32 srcLineStartAddr = srcBasePtr + (srcY * srcStride + srcX) * bpp; - u32 dstLineStartAddr = dstBasePtr + (dstY * dstStride + dstX) * bpp; - u32 bytesToCopy = width * height * bpp; - - const u8 *srcp = Memory::GetPointer(srcLineStartAddr); - u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); - memcpy(dstp, srcp, bytesToCopy); - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); - - if (MemBlockInfoDetailed(bytesToCopy)) { - const std::string tag = GetMemWriteTagAt("GPUBlockTransfer/", src, bytesToCopy); - NotifyMemInfo(MemBlockFlags::READ, src, bytesToCopy, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dst, bytesToCopy, tag.c_str(), tag.size()); - } - } else if ((srcDstOverlap || srcWraps || dstWraps) && (srcValid || srcWraps) && (dstValid || dstWraps)) { - // This path means we have either src/dst overlap, OR one or both of src and dst wrap. - // This should be uncommon so it's the slowest path. - u32 bytesToCopy = width * bpp; - static std::string tag; - bool notifyDetail = MemBlockInfoDetailed(srcWraps || dstWraps ? 64 : bytesToCopy); - bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize); - if (notifyDetail || notifyAll) { - tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); - } - - auto notifyingMemmove = [&](u32 d, u32 s, u32 sz) { - const u8 *srcp = Memory::GetPointer(s); - u8 *dstp = Memory::GetPointerWrite(d); - memmove(dstp, srcp, sz); - GPURecord::NotifyMemcpy(d, s, sz); - - if (notifyDetail) { - NotifyMemInfo(MemBlockFlags::READ, s, sz, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, d, sz, tag.c_str(), tag.size()); - } - }; - - for (int y = 0; y < height; y++) { - u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; - u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; - // If we already passed a wrap, we can use the quicker path. - if ((srcLineStartAddr & 0x04800000) == 0x04800000) - srcLineStartAddr &= ~0x00800000; - if ((dstLineStartAddr & 0x04800000) == 0x04800000) - dstLineStartAddr &= ~0x00800000; - // These flags mean there's a wrap inside this line. - bool srcLineWrap = !Memory::IsValidRange(srcLineStartAddr, bytesToCopy); - bool dstLineWrap = !Memory::IsValidRange(dstLineStartAddr, bytesToCopy); - - if (!srcLineWrap && !dstLineWrap) { - const u8 *srcp = Memory::GetPointer(srcLineStartAddr); - u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); - for (u32 i = 0; i < bytesToCopy; i += 64) { - u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; - memmove(dstp + i, srcp + i, chunk); - } - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); - - // If we're tracking detail, it's useful to have the gaps illustrated properly. - if (notifyDetail) { - NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); - } - } else { - // We can wrap at any point, so along with overlap this gets a bit complicated. - // We're just going to do this the slow and easy way. - u32 srcLinePos = srcLineStartAddr; - u32 dstLinePos = dstLineStartAddr; - for (u32 i = 0; i < bytesToCopy; i += 64) { - u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; - u32 srcValid = Memory::ValidSize(srcLinePos, chunk); - u32 dstValid = Memory::ValidSize(dstLinePos, chunk); - - // First chunk, for which both are valid. - u32 bothSize = std::min(srcValid, dstValid); - if (bothSize != 0) - notifyingMemmove(dstLinePos, srcLinePos, bothSize); - - // Now, whichever side has more valid (or the rest, if only one side must wrap.) - u32 exclusiveSize = std::max(srcValid, dstValid) - bothSize; - if (exclusiveSize != 0 && srcValid >= dstValid) { - notifyingMemmove(PSP_GetVidMemBase(), srcLineStartAddr + bothSize, exclusiveSize); - } else if (exclusiveSize != 0 && srcValid < dstValid) { - notifyingMemmove(dstLineStartAddr + bothSize, PSP_GetVidMemBase(), exclusiveSize); - } - - // Finally, if both src and dst wrapped, that portion. - u32 wrappedSize = chunk - bothSize - exclusiveSize; - if (wrappedSize != 0 && srcValid >= dstValid) { - notifyingMemmove(PSP_GetVidMemBase() + exclusiveSize, PSP_GetVidMemBase(), wrappedSize); - } else if (wrappedSize != 0 && srcValid < dstValid) { - notifyingMemmove(PSP_GetVidMemBase(), PSP_GetVidMemBase() + exclusiveSize, wrappedSize); - } - - srcLinePos += chunk; - dstLinePos += chunk; - if ((srcLinePos & 0x04800000) == 0x04800000) - srcLinePos &= ~0x00800000; - if ((dstLinePos & 0x04800000) == 0x04800000) - dstLinePos &= ~0x00800000; - } - } - } - - if (notifyAll) { - if (srcWraps) { - u32 validSize = Memory::ValidSize(src, srcSize); - NotifyMemInfo(MemBlockFlags::READ, src, validSize, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::READ, PSP_GetVidMemBase(), srcSize - validSize, tag.c_str(), tag.size()); - } else { - NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); - } - if (dstWraps) { - u32 validSize = Memory::ValidSize(dst, dstSize); - NotifyMemInfo(MemBlockFlags::WRITE, dst, validSize, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetVidMemBase(), dstSize - validSize, tag.c_str(), tag.size()); - } else { - NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); - } - } - } else if (srcValid && dstValid) { - u32 bytesToCopy = width * bpp; - static std::string tag; - bool notifyDetail = MemBlockInfoDetailed(bytesToCopy); - bool notifyAll = !notifyDetail && MemBlockInfoDetailed(srcSize, dstSize); - if (notifyDetail || notifyAll) { - tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); - } - - for (int y = 0; y < height; y++) { - u32 srcLineStartAddr = srcBasePtr + ((y + srcY) * srcStride + srcX) * bpp; - u32 dstLineStartAddr = dstBasePtr + ((y + dstY) * dstStride + dstX) * bpp; - - const u8 *srcp = Memory::GetPointer(srcLineStartAddr); - u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); - memcpy(dstp, srcp, bytesToCopy); - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); - - // If we're tracking detail, it's useful to have the gaps illustrated properly. - if (notifyDetail) { - NotifyMemInfo(MemBlockFlags::READ, srcLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dstLineStartAddr, bytesToCopy, tag.c_str(), tag.size()); - } - } - - if (notifyAll) { - NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); - } + if (Memory::IsValidRange(src, srcSize) && Memory::IsValidRange(dst, dstSize)) { + drawEngine_->transformUnit.FlushIfOverlap("blockxfer", false, src, srcStride, width * bpp, height); + drawEngine_->transformUnit.FlushIfOverlap("blockxfer", true, dst, dstStride, width * bpp, height); } else { - // This seems to cause the GE to require a break/reset on a PSP. - // TODO: Handle that and figure out which bytes are still copied? - ERROR_LOG_REPORT(G3D, "Block transfer invalid: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); + drawEngine_->transformUnit.Flush("blockxfer_wrap"); } - // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. - cyclesExecuted += ((height * width * bpp) * 16) / 10; + DoBlockTransfer(gstate_c.skipDrawReason); // Could theoretically dirty the framebuffer. MarkDirty(dst, dstSize, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY); From 73489349cc0161753f078da47c4dc2ba663df22a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 19:20:52 -0800 Subject: [PATCH 11/16] GPU: Oops, simplify an expression. --- GPU/GPUCommon.cpp | 4 ++-- GPU/Software/SoftGpu.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 11f2caf8b6..0af26587eb 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -3065,9 +3065,9 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { // Use height less one to account for width, which can be greater or less than stride. const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; - const uint32_t srcSize = (height - 1) * srcStride * bpp + width * bpp; + const uint32_t srcSize = (height - 1) * (srcStride + width) * bpp; const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; - const uint32_t dstSize = (height - 1) * dstStride * bpp + width * bpp; + const uint32_t dstSize = (height - 1) * (dstStride + width) * bpp; bool srcDstOverlap = src + srcSize > dst && dst + dstSize > src; bool srcValid = Memory::IsValidRange(src, srcSize); diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index d7e6e3346f..6ed1bb3af1 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -795,9 +795,9 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { // Use height less one to account for width, which can be greater or less than stride. const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; - const uint32_t srcSize = (height - 1) * srcStride * bpp + width * bpp; + const uint32_t srcSize = (height - 1) * (srcStride + width) * bpp; const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; - const uint32_t dstSize = (height - 1) * dstStride * bpp + width * bpp; + const uint32_t dstSize = (height - 1) * (dstStride + width) * bpp; // Need to flush both source and target, so we overwrite properly. if (Memory::IsValidRange(src, srcSize) && Memory::IsValidRange(dst, dstSize)) { From 5bc51cb01d9f32f46502acf40f2e5cedcb20e351 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 30 Nov 2022 22:16:33 -0800 Subject: [PATCH 12/16] GE Debugger: Remove dup block transfer record. We already record it as a block transfer, we don't need to also do it as a memcpy. --- GPU/GPUCommon.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 0af26587eb..cd3aaeb9d7 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -3090,7 +3090,6 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { const u8 *srcp = Memory::GetPointer(srcLineStartAddr); u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); memcpy(dstp, srcp, bytesToCopy); - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); if (MemBlockInfoDetailed(bytesToCopy)) { const std::string tag = GetMemWriteTagAt("GPUBlockTransfer/", src, bytesToCopy); @@ -3112,7 +3111,6 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { const u8 *srcp = Memory::GetPointer(s); u8 *dstp = Memory::GetPointerWrite(d); memmove(dstp, srcp, sz); - GPURecord::NotifyMemcpy(d, s, sz); if (notifyDetail) { NotifyMemInfo(MemBlockFlags::READ, s, sz, tag.c_str(), tag.size()); @@ -3139,7 +3137,6 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { u32 chunk = i + 64 > bytesToCopy ? bytesToCopy - i : 64; memmove(dstp + i, srcp + i, chunk); } - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); // If we're tracking detail, it's useful to have the gaps illustrated properly. if (notifyDetail) { @@ -3219,7 +3216,6 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { const u8 *srcp = Memory::GetPointer(srcLineStartAddr); u8 *dstp = Memory::GetPointerWrite(dstLineStartAddr); memcpy(dstp, srcp, bytesToCopy); - GPURecord::NotifyMemcpy(dstLineStartAddr, srcLineStartAddr, bytesToCopy); // If we're tracking detail, it's useful to have the gaps illustrated properly. if (notifyDetail) { From f18afc5f51051133d5fcbdb3b0ab52f19c0eb525 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Dec 2022 01:09:50 -0800 Subject: [PATCH 13/16] headless: Update tests. --- pspautotests | 2 +- test.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pspautotests b/pspautotests index a886587cc2..4606502750 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit a886587cc29ffadc93f5533a0f8dc329ae355b9a +Subproject commit 46065027500cd781ce7c15c051c8b0c4751ad1fa diff --git a/test.py b/test.py index dbfe4d1ad7..7b55c8a692 100755 --- a/test.py +++ b/test.py @@ -191,6 +191,9 @@ tests_good = [ "gpu/texfunc/replace", "gpu/textures/mipmap", "gpu/textures/rotate", + "gpu/transfer/invalid", + "gpu/transfer/mirrors", + "gpu/transfer/overlap", "gpu/vertices/colors", "gpu/vertices/morph", "gpu/vertices/texcoords", From 2073eed7b76b72bfac2e334cfbfb7cb5f710e411 Mon Sep 17 00:00:00 2001 From: TotalCaesar659 <14265316+TotalCaesar659@users.noreply.github.com> Date: Thu, 1 Dec 2022 15:20:10 +0300 Subject: [PATCH 14/16] Update Russian translation --- assets/lang/ru_RU.ini | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/assets/lang/ru_RU.ini b/assets/lang/ru_RU.ini index aced6ead7b..4537365dd0 100644 --- a/assets/lang/ru_RU.ini +++ b/assets/lang/ru_RU.ini @@ -82,7 +82,7 @@ Portrait = Портретная Portrait Reversed = Портретная (перевернутая) PSP Action Buttons = Кнопки действий PSP Raw input = Прямой ввод -Repeat mode = Repeat mode +Repeat mode = Режим повтора Reset to defaults = По умолчанию Screen aligned to ground = Экран повёрнут к земле Screen at right angle to ground = Экран повёрнут под прямым углом к земле @@ -240,7 +240,7 @@ FPU = FPU Framedump tests = Тест дампов кадров Frame Profiler = Профайлер кадров GPU Driver Test = Проверка драйвера ГП -GPU log profiler = GPU log profiler +GPU log profiler = Профилировщик логов ГП GPU Profile = Профиль ГП Jit Compare = Сравнение с JIT JIT debug tools = Инструменты отладки JIT @@ -456,7 +456,7 @@ Aggressive = Принудительно Alternative Speed = Другая скорость (в %, 0 = без ограничений) Alternative Speed 2 = Другая скорость 2 (в %, 0 = без ограничений) Anisotropic Filtering = Анизотропная фильтрация -Aspect Ratio = Aspect Ratio +Aspect Ratio = Соотношение сторон Auto = Авто Auto (1:1) = Авто (1:1) Auto (same as Rendering) = Авто (как разрешение рендеринга) @@ -488,14 +488,13 @@ Disabled = Отключено Display Layout && Effects = Редактор расположения экрана Display Resolution (HW scaler) = Разрешение экрана (аппаратное) Enable Cardboard VR = Включить Cardboard VR -#Features = Возможности FPS = FPS Frame Rate Control = Управление частотой кадров Frame Skipping = Пропуск кадров Frame Skipping Type = Тип пропуска кадров FullScreen = Полноэкранный режим -Geometry shader culling = Geometry shader culling -GPUReadbackRequired = Warning: This game requires "Skip GPU Readbacks" to be set to Off. +Geometry shader culling = Вызов геометрических шейдеров +GPUReadbackRequired = Внимание: Для данной игры нужно отключить настройку "Пропускать чтение данных ГП". Hack Settings = Параметры хаков (могут вызывать глюки) Hardware Tessellation = Аппаратная тесселяция Hardware Transform = Аппаратное преобразование @@ -542,20 +541,20 @@ Safe = Безопасно Screen Scaling Filter = Фильтр масштабирования экрана Show Debug Statistics = Отображать отладочную информацию Show FPS Counter = Показывать счетчик FPS -Skip GPU Readbacks = Skip GPU Readbacks +Skip GPU Readbacks = Пропускать чтение данных ГП Software Rendering = Программный рендеринг (медленно) Software Skinning = Программная заливка SoftwareSkinning Tip = Объединяет вызовы отрисовки моделей с заливкой на ЦП, быстрее во многих играх Speed = Скорость Speed Hacks = Speed Hacks (могут вызывать глюки) -Stereo display shader = Stereo display shader -Stereo rendering = Stereo rendering +Stereo display shader = Шейдер стереодисплея +Stereo rendering = Стереорендеринг Stretch = Растягивание Texture Filter = Текстурный фильтр Texture Filtering = Фильтрация текстур Texture Scaling = Масштабирование текстур Texture Shader = Текстурный шейдер -Turn off Hardware Tessellation - unsupported = Выключите "аппаратную тесселяцию": не поддерживается +Turn off Hardware Tessellation - unsupported = Выключите настройку "Аппаратная тесселяция": не поддерживается Unlimited = Без ограничений Up to 1 = До 1 Up to 2 = До 2 @@ -581,8 +580,8 @@ Zip archive corrupt = ZIP-архив повреждён Zip file does not contain PSP software = В ZIP-файле отсутсвует ПО для PSP [KeyMapping] -Autoconfigure = Авто конфиг -Autoconfigure for device = Авто конфиг для устройства +Autoconfigure = Автоконфиг +Autoconfigure for device = Автоконфиг для устройства Bind All = Настроить всё Clear All = Очистить все Default All = По умолчанию @@ -722,8 +721,8 @@ Center Left = В центре слева Center Right = В центре справа Change Mac Address = Изменить MAC-адрес Change proAdhocServer Address = Изменить IP-адрес ad-hoc сервера (localhost = множество экземпляров) -ChangeMacSaveConfirm = Generate a new MAC address? -ChangeMacSaveWarning = Some games verify the MAC address when loading savedata, so this may break old saves. +ChangeMacSaveConfirm = Сгенерировать новый MAC-адрес? +ChangeMacSaveWarning = Некоторые игры проверяют MAC-адрес при загрузке сохранений, из-за этого старые сохранения могут быть испорчены. Chat = Чат Chat Button Position = Позиция кнопки чата Chat Here = Чат здесь @@ -747,7 +746,7 @@ Failed to Bind Localhost IP = Не удалось привязать адрес Failed to Bind Port = Не удалось привязать порт Failed to connect to Adhoc Server = Не удалось подключиться к ad-hoc серверу Forced First Connect = Принудительное первое подключение (быстрое подключение) -GM: Data from Unknown Port = GM: Данные от неизвестного порта +GM: Data from Unknown Port = GM: Данные с неизвестного порта Hostname = Имя хоста Invalid IP or hostname = Некорректный IP или имя хоста Minimum Timeout = Минимальный таймаут (задержка в мс, 0 = по умолчанию) @@ -804,9 +803,9 @@ Black border = Черные рамки Bloom = Свечение Brightness = Яркость Cartoon = Мультипликация -CatmullRom = Бикубический (Catmull-Rom) Апскейлер +CatmullRom = Бикубический (Catmull-Rom) апскейлер ColorCorrection = Цветокоррекция -ColorPreservation = Color preservation +ColorPreservation = Сохранение цвета Contrast = Контрастность CRT = ЭЛТ-развертка FXAA = Сглаживание FXAA @@ -815,13 +814,13 @@ Grayscale = Оттенки серого GreenLevel = GreenLevel Intensity = Интенсивность InverseColors = Инвертированные цвета -MitchellNetravali = Бикубический (Mitchell-Netravali) Апскейлер +MitchellNetravali = Бикубический (Mitchell-Netravali) апскейлер Natural = Естественные цвета NaturalA = Естественные цвета (без размытия) Off = Выключена Power = Сила PSPColor = Цвета PSP -RedBlue = Red/Blue glasses +RedBlue = Красно-синие очки Saturation = Насыщенность Scanlines = Строки развертки (ЭЛТ) Sharpen = Резкость @@ -1043,10 +1042,10 @@ CPU Name = Название D3DCompiler Version = Версия D3DCompiler Debug = Отладочная Debugger Present = Отладчик присутствует -#Depth buffer format = Формат буфера глубины +Depth buffer format = Формат буфера глубины Device Info = Устройство Directories = Директории -Display Color Formats = Display Color Formats +Display Color Formats = Форматы цветов дисплея Display Information = Информация о дисплее Driver bugs = Ошибки драйвера Driver Version = Версия драйвера @@ -1096,7 +1095,7 @@ Cache ISO in RAM = Кэшировать ISO в ОЗУ Change CPU Clock = Эмулируемая частота ЦП PSP (нестабильно) Color Saturation = Насыщенность Color Tint = Оттенок цвета -Game crashed = Game crashed +Game crashed = Игра вылетела Language = Язык Memory Stick folder = Изменить папку с картой памяти Memory Stick size = Изменить размер карты памяти (Гб) From 9d9a64be8b3dc64bcf3eb8992206453d1233c41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Dec 2022 13:38:35 +0100 Subject: [PATCH 15/16] Fix Aspect Ratio translation category --- UI/DisplayLayoutScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index 18d52d8360..5152965179 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -213,7 +213,7 @@ void DisplayLayoutScreen::CreateViews() { auto stretch = new CheckBox(&g_Config.bDisplayStretch, gr->T("Stretch")); leftColumn->Add(stretch); - PopupSliderChoiceFloat *aspectRatio = new PopupSliderChoiceFloat(&g_Config.fDisplayAspectRatio, 0.5f, 2.0f, di->T("Aspect Ratio"), screenManager()); + PopupSliderChoiceFloat *aspectRatio = new PopupSliderChoiceFloat(&g_Config.fDisplayAspectRatio, 0.5f, 2.0f, gr->T("Aspect Ratio"), screenManager()); leftColumn->Add(aspectRatio); aspectRatio->SetDisabledPtr(&g_Config.bDisplayStretch); aspectRatio->SetHasDropShadow(false); From 14e75071456ae656b56be9184bd82fad284c815e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Dec 2022 14:11:37 +0100 Subject: [PATCH 16/16] Temporarily disable the UWP github CI job --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4e7c10155..6a21c78623 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,6 +69,7 @@ jobs: path: ppsspp/ build-uwp: + if: ${{false}} # Temporarily disable runs-on: windows-latest steps: - uses: actions/checkout@v3