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 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/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index eba7b966da..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"); } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1560d3fe49..cd3aaeb9d7 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -3057,72 +3057,188 @@ 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 + width) * bpp; + const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; + const uint32_t dstSize = (height - 1) * (dstStride + 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); + + 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); + + 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); + } + + // 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); + + // 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/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/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 7a824fe7b0..6ed1bb3af1 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,68 +793,21 @@ 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 + 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 + 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); - - if (srcStride == dstStride && (u32)width == srcStride) { - 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); + 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 { - 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); - } + drawEngine_->transformUnit.Flush("blockxfer_wrap"); } - 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()); - } - - // 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); 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. diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index ed741ec6c9..5152965179 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_) { @@ -208,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); 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"); 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 = Изменить размер карты памяти (Гб) 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",