From 670c31e7465c1f65e4a542d4d5bf286fff16cb45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 29 Aug 2022 23:59:43 +0200 Subject: [PATCH 01/99] Add support for reading depth buffers to the PackFramebufferSync function. Also adds D16 conversion code (though, could probably get away with just creating a 16-bit depth buffer on the GPU and blit to it, where depth blit is available). Not yet used, just getting it in separately. --- Common/GPU/DataFormat.h | 1 + Common/GPU/Vulkan/VulkanQueueRunner.cpp | 2 + Common/GPU/Vulkan/VulkanRenderManager.cpp | 1 + Common/GPU/thin3d.cpp | 40 +++++++++++ GPU/Common/FramebufferManagerCommon.cpp | 87 +++++++++++------------ GPU/Common/FramebufferManagerCommon.h | 13 ++-- GPU/Directx9/FramebufferManagerDX9.cpp | 12 ++-- GPU/Directx9/FramebufferManagerDX9.h | 2 +- 8 files changed, 99 insertions(+), 59 deletions(-) diff --git a/Common/GPU/DataFormat.h b/Common/GPU/DataFormat.h index f1e1687630..b07d0a2f34 100644 --- a/Common/GPU/DataFormat.h +++ b/Common/GPU/DataFormat.h @@ -77,5 +77,6 @@ inline bool DataFormatIsColor(DataFormat fmt) { void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); +void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); } // namespace diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 880d18bd2b..6f84cf4b49 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -1793,6 +1793,8 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataForm } } else if (destFormat == Draw::DataFormat::D32F) { ConvertToD32F(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat); + } else if (destFormat == Draw::DataFormat::D16) { + ConvertToD16(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat); } else { // TODO: Maybe a depth conversion or something? ERROR_LOG(G3D, "CopyReadbackBuffer: Unknown format"); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 8e17483ae9..56bf3ee253 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -836,6 +836,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma } else { _assert_(false); } + // Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM. queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels); return true; diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index 9dee5898fb..04326a24d1 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -623,6 +623,46 @@ void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_ } } +void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { + if (format == Draw::DataFormat::D32F) { + const float *src32 = (const float *)src; + uint16_t *dst16 = (uint16_t *)dst; + if (src == dst) { + return; + } else { + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + dst16[x] = (uint16_t)(src32[x] * 65535.0f); + } + src32 += srcStride; + dst16 += dstStride; + } + } + } else if (format == Draw::DataFormat::D16) { + _assert_(src != dst); + const uint16_t *src16 = (const uint16_t *)src; + uint16_t *dst16 = (uint16_t *)dst; + for (uint32_t y = 0; y < height; ++y) { + memcpy(dst16, src16, width * 2); + src16 += srcStride; + dst16 += dstStride; + } + } else if (format == Draw::DataFormat::D24_S8) { + _assert_(src != dst); + const uint32_t *src32 = (const uint32_t *)src; + uint16_t *dst16 = (uint16_t *)dst; + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + dst16[x] = (src32[x] & 0x00FFFFFF) >> 8; + } + src32 += srcStride; + dst16 += dstStride; + } + } else { + assert(false); + } +} + const char *Bugs::GetBugName(uint32_t bug) { switch (bug) { case NO_DEPTH_CANNOT_DISCARD_STENCIL: return "NO_DEPTH_CANNOT_DISCARD_STENCIL"; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index c9c49da6bb..2a560716fd 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -167,6 +167,7 @@ u32 FramebufferManagerCommon::ColorBufferByteSize(const VirtualFramebuffer *vfb) } bool FramebufferManagerCommon::ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const { + // Dangan Ronpa hack return PSP_CoreParameter().compat.flags().Force04154000Download && vfb->fb_address == 0x04154000; } @@ -898,7 +899,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) { if (ShouldDownloadFramebuffer(vfb) && !vfb->memoryUpdated) { - ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height); + ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR); vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR; } else { DownloadFramebufferOnSwitch(prevVfb); @@ -1106,7 +1107,7 @@ void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer } if (x < src->drawnWidth && y < src->drawnHeight && w > 0 && h > 0) { - BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, "Blit_CopyFramebufferForColorTexture"); + BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, RASTER_COLOR, "Blit_CopyFramebufferForColorTexture"); } } @@ -1216,7 +1217,7 @@ void FramebufferManagerCommon::DownloadFramebufferOnSwitch(VirtualFramebuffer *v // To support this, we save the first frame to memory when we have a safe w/h. // Saving each frame would be slow. if (!g_Config.bDisableSlowFramebufEffects && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) { - ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight); + ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR); vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR; vfb->safeWidth = 0; vfb->safeHeight = 0; @@ -1369,7 +1370,7 @@ void FramebufferManagerCommon::DecimateFBOs() { int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used); if (ShouldDownloadFramebuffer(vfb) && age == 0 && !vfb->memoryUpdated) { - ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height); + ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR); vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR; } @@ -1502,9 +1503,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, if (vfb->fbo) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO"); if (!skipCopy) { - // TODO: In this case, it'll nearly always be better to draw the old framebuffer to the new one than to do an actual blit. - // Usually hardly a performance issue though. - BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, "Blit_ResizeFramebufFBO"); + BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "Blit_ResizeFramebufFBO"); + // Depth copying is handled by deferred copies later. } } fbosToDelete_.push_back(old.fbo); @@ -1608,7 +1608,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } else { WARN_LOG_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy %08x -> %08x (size: %x)", src, dst, size); // Just do the blit! - BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, "Blit_InterBufferMemcpy"); + BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, RASTER_COLOR, "Blit_InterBufferMemcpy"); SetColorUpdated(dstBuffer, skipDrawReason); RebindFramebuffer("RebindFramebuffer - Inter-buffer memcpy"); } @@ -1631,7 +1631,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) { WARN_LOG_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight); } else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated && !PSP_CoreParameter().compat.flags().DisableReadbacks) { - ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH); + ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, RASTER_COLOR); srcBuffer->usageFlags = (srcBuffer->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR; } return false; @@ -1798,7 +1798,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd } // 1:1 pixel sides buffers, we resize buffers to these before we read them back. -VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb) { +VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel) { // For now we'll keep these on the same struct as the ones that can get displayed // (and blatantly copy work already done above while at it). VirtualFramebuffer *nvfb = nullptr; @@ -1837,12 +1837,13 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram char name[64]; snprintf(name, sizeof(name), "download_temp"); - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, name }); + // TODO: We don't have a way to create a depth-only framebuffer yet. + // Also on many + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, channel == RASTER_DEPTH ? true : false, name }); if (!nvfb->fbo) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight); return nullptr; } - bvfbs_.push_back(nvfb); } else { UpdateDownloadTempBuffer(nvfb); @@ -2012,7 +2013,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst dstBasePtr, dstRect.x_bytes / bpp, dstRect.y, dstStride); FlushBeforeCopy(); // Some backends can handle blitting within a framebuffer. Others will just have to deal with it or ignore it, apparently. - BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, "Blit_IntraBufferBlockTransfer"); + BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, RASTER_COLOR, "Blit_IntraBufferBlockTransfer"); RebindFramebuffer("rebind after intra block transfer"); SetColorUpdated(dstRect.vfb, skipDrawReason); return true; // Skip the memory copy. @@ -2033,7 +2034,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst bpp = buffer_bpp; } FlushBeforeCopy(); - BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, "Blit_InterBufferBlockTransfer"); + BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, RASTER_COLOR, "Blit_InterBufferBlockTransfer"); RebindFramebuffer("RebindFramebuffer - Inter-buffer block transfer"); SetColorUpdated(dstRect.vfb, skipDrawReason); return true; @@ -2068,7 +2069,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst if (tooTall) { WARN_LOG_ONCE(btdheight, G3D, "Block transfer download %08x -> %08x dangerous, %d+%d is taller than %d", srcBasePtr, dstBasePtr, srcRect.y, srcRect.h, srcRect.vfb->bufferHeight); } - ReadFramebufferToMemory(srcRect.vfb, static_cast(srcX * srcXFactor), srcY, static_cast(srcRect.w_bytes * srcXFactor), srcRect.h); + ReadFramebufferToMemory(srcRect.vfb, static_cast(srcX * srcXFactor), srcY, static_cast(srcRect.w_bytes * srcXFactor), srcRect.h, RASTER_COLOR); srcRect.vfb->usageFlags = (srcRect.vfb->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR; } } @@ -2248,7 +2249,7 @@ void FramebufferManagerCommon::ShowScreenResolution() { // We might also want to implement an asynchronous callback-style version of this. Would probably // only be possible to implement optimally on Vulkan, but on GL and D3D11 we could do pixel buffers -// and read on the next frame, then call the callback. PackFramebufferAsync_ on OpenGL already does something similar. +// and read on the next frame, then call the callback. // // The main use cases for this are: // * GE debugging(in practice async will not matter because it will stall anyway.) @@ -2286,7 +2287,7 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB tempVfb.renderWidth = w; tempVfb.renderHeight = h; tempVfb.renderScaleFactor = maxScaleFactor; - BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, "Blit_GetFramebuffer"); + BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, RASTER_COLOR, "Blit_GetFramebuffer"); bound = tempFBO; } else { @@ -2395,33 +2396,31 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return retval; } -// This function takes an already correctly-sized framebuffer and packs it into RAM. +// This function takes an already correctly-sized framebuffer and reads it into emulated PSP VRAM. // Does not need to account for scaling. +// // Color conversion is currently done on CPU but should theoretically be done on GPU. // (Except using the GPU might cause problems because of various implementations' // dithering behavior and games that expect exact colors like Danganronpa, so we // can't entirely be rid of the CPU path.) -- unknown -void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); - return; - } - +void FramebufferManagerCommon::PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) { if (w <= 0 || h <= 0) { - ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync_: %d %d %d %d", x, y, w, h); + ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync: %d %d %d %d", x, y, w, h); return; } const u32 fb_address = vfb->fb_address & 0x3FFFFFFF; - Draw::DataFormat destFormat = GEFormatToThin3D(vfb->fb_format); + Draw::DataFormat destFormat = channel == RASTER_COLOR ? GEFormatToThin3D(vfb->fb_format) : GEFormatToThin3D(GE_FORMAT_DEPTH16); const int dstBpp = (int)DataFormatSizeInBytes(destFormat); - const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; - const int dstSize = (h * vfb->fb_stride + w - 1) * dstBpp; + int stride = channel == RASTER_COLOR ? vfb->fb_stride : vfb->z_stride; + + const int dstByteOffset = (y * stride + x) * dstBpp; + const int dstSize = (h * stride + w - 1) * dstBpp; // TODO: What's that "w - 1" doing here? if (!Memory::IsValidRange(fb_address + dstByteOffset, dstSize)) { - ERROR_LOG_REPORT(G3D, "PackFramebufferSync_ would write outside of memory, ignoring"); + ERROR_LOG_REPORT(G3D, "PackFramebufferSync would write outside of memory, ignoring"); return; } @@ -2432,18 +2431,18 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x, ptr=%p", fb_address, destPtr); if (destPtr) { - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync_"); + draw_->CopyFramebufferToMemorySync(vfb->fbo, channel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync"); char tag[128]; size_t len = snprintf(tag, sizeof(tag), "FramebufferPack/%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->fb_format)); NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, tag, len); } else { - ERROR_LOG(G3D, "PackFramebufferSync_: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride); + ERROR_LOG(G3D, "PackFramebufferSync: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride); } gpuStats.numReadbacks++; } -void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h) { +void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) { // Clamp to bufferWidth. Sometimes block transfers can cause this to hit. if (x + w >= vfb->bufferWidth) { w = vfb->bufferWidth - x; @@ -2481,13 +2480,13 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, } if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) { - // No need to blit - PackFramebufferSync_(vfb, x, y, w, h); + // No need to stretch-blit + PackFramebufferSync(vfb, x, y, w, h, channel); } else { - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, channel); if (nvfb) { - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_ReadFramebufferToMemory"); - PackFramebufferSync_(nvfb, x, y, w, h); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, channel, "Blit_ReadFramebufferToMemory"); + PackFramebufferSync(nvfb, x, y, w, h, channel); } } @@ -2536,10 +2535,10 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo vfb->clutUpdatedBytes = loadBytes; // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, RASTER_COLOR); if (nvfb) { - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_DownloadFramebufferForClut"); - PackFramebufferSync_(nvfb, x, y, w, h); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, RASTER_COLOR, "Blit_DownloadFramebufferForClut"); + PackFramebufferSync(nvfb, x, y, w, h, RASTER_COLOR); } textureCache_->ForgetLastTexture(); @@ -2664,9 +2663,7 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); } -void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) { - RasterChannel channel = RASTER_COLOR; - +void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag) { if (!dst->fbo || !src->fbo || !useBufferedRendering_) { // This can happen if they recently switched from non-buffered. if (useBufferedRendering_) { @@ -2697,8 +2694,8 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX return; } - bool useBlit = draw_->GetDeviceCaps().framebufferBlitSupported; - bool useCopy = draw_->GetDeviceCaps().framebufferCopySupported; + bool useBlit = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferBlitSupported : false; + bool useCopy = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferCopySupported : false; if (dst == currentRenderVfb_) { // If already bound, using either a blit or a copy is unlikely to be an optimization. // So we're gonna use a raster draw instead. diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 84bcd99b91..795af08774 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -206,7 +206,7 @@ enum class TempFBO { STENCIL, }; -inline Draw::DataFormat GEFormatToThin3D(int geFormat) { +inline Draw::DataFormat GEFormatToThin3D(GEBufferFormat geFormat) { switch (geFormat) { case GE_FORMAT_4444: return Draw::DataFormat::A4R4G4B4_UNORM_PACK16; @@ -216,7 +216,10 @@ inline Draw::DataFormat GEFormatToThin3D(int geFormat) { return Draw::DataFormat::R5G6B5_UNORM_PACK16; case GE_FORMAT_8888: return Draw::DataFormat::R8G8B8A8_UNORM; + case GE_FORMAT_DEPTH16: + return Draw::DataFormat::D16; default: + // TODO: Assert? return Draw::DataFormat::UNDEFINED; } } @@ -320,7 +323,7 @@ public: void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel); void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes); void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat); @@ -420,7 +423,7 @@ public: VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat); protected: - virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + virtual void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel); void SetViewport2D(int x, int y, int w, int h); Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags); @@ -436,7 +439,7 @@ protected: virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra // Used by ReadFramebufferToMemory and later framebuffer block copies - void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag); + void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag); void BlitUsingRaster( Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, @@ -461,7 +464,7 @@ protected: bool FindTransferFramebuffer(u32 basePtr, int stride, int x, int y, int w, int h, int bpp, bool destination, BlockTransferRect *rect); - VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb); + VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel); virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {} VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format); diff --git a/GPU/Directx9/FramebufferManagerDX9.cpp b/GPU/Directx9/FramebufferManagerDX9.cpp index 8de6f438a9..f828336edd 100644 --- a/GPU/Directx9/FramebufferManagerDX9.cpp +++ b/GPU/Directx9/FramebufferManagerDX9.cpp @@ -133,9 +133,10 @@ } } - void FramebufferManagerDX9::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferDirectx9_: vfb->fbo == 0"); + void FramebufferManagerDX9::PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) { + if (channel != RASTER_COLOR) { + // Unsupported + WARN_LOG_ONCE(d3ddepthreadback, G3D, "Not yet supporting depth readbacks on DX9"); return; } @@ -175,11 +176,6 @@ } void FramebufferManagerDX9::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0"); - return; - } - // We always read the depth buffer in 24_8 format. const u32 z_address = vfb->z_address; diff --git a/GPU/Directx9/FramebufferManagerDX9.h b/GPU/Directx9/FramebufferManagerDX9.h index 4835ded4d1..0f4f7d7ce1 100644 --- a/GPU/Directx9/FramebufferManagerDX9.h +++ b/GPU/Directx9/FramebufferManagerDX9.h @@ -51,7 +51,7 @@ protected: void DecimateFBOs() override; private: - void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; + void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer); From 44d822be3b9497c25ed79be521f847eb5b00c646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 06:41:37 +0200 Subject: [PATCH 02/99] Address feedback --- Common/GPU/thin3d.cpp | 2 + GPU/Common/FramebufferManagerCommon.cpp | 66 ++++++++++++++----------- GPU/Common/FramebufferManagerCommon.h | 2 +- GPU/GPUCommon.cpp | 2 +- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index 04326a24d1..d712195c7b 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -623,6 +623,8 @@ void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_ } } +// TODO: This is missing the conversion to the quarter-range we use if depth clamp is not available. +// That conversion doesn't necessarily belong here in thin3d, though. void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { if (format == Draw::DataFormat::D32F) { const float *src32 = (const float *)src; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 2a560716fd..8df26d7d1c 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -126,13 +126,19 @@ VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const { } VirtualFramebuffer *FramebufferManagerCommon::GetExactVFB(u32 addr, int stride, GEBufferFormat format) const { + VirtualFramebuffer *newest = nullptr; for (auto vfb : vfbs_) { if (vfb->fb_address == addr && vfb->fb_stride == stride && vfb->fb_format == format) { - // There'll only be one exact match, we don't allow duplicates with these conditions. - return vfb; + if (newest) { + if (vfb->colorBindSeq > newest->colorBindSeq) { + newest = vfb; + } + } else { + newest = vfb; + } } } - return nullptr; + return newest; } VirtualFramebuffer *FramebufferManagerCommon::ResolveVFB(u32 addr, int stride, GEBufferFormat format) { @@ -660,7 +666,7 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra // This will result in reinterpret later, if both formats are 16-bit. sources.push_back(CopySource{ src, RASTER_COLOR, 0, 0 }); } else { - // Happens in Prince of Persia - Revelations. Ignoring. + // Likely irrelevant or old, if the game is changing color depth for example. } } else if (src->fb_stride == dst->fb_stride && src->fb_format == dst->fb_format) { u32 bytesPerPixel = BufferFormatBytesPerPixel(src->fb_format); @@ -960,41 +966,41 @@ void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int stride, } } -void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size, bool safe) { +void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { // Take off the uncached flag from the address. Not to be confused with the start of VRAM. addr &= 0x3FFFFFFF; // TODO: Could go through all FBOs, but probably not important? // TODO: Could also check for inner changes, but video is most important. // TODO: This shouldn't care if it's a display framebuf or not, should work exactly the same. bool isDisplayBuf = addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr(); - if (isDisplayBuf || safe) { - // TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help. - if (!Memory::IsValidAddress(displayFramebufPtr_)) - return; + // TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help. + if (!Memory::IsValidAddress(displayFramebufPtr_)) + return; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - if (vfb->fb_address == addr) { - FlushBeforeCopy(); + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + if (vfb->fb_address == addr) { + FlushBeforeCopy(); - if (useBufferedRendering_ && vfb->fbo) { - GEBufferFormat fmt = vfb->fb_format; - if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) { - // If we're not rendering to it, format may be wrong. Use displayFormat_ instead. - fmt = displayFormat_; - } - DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height); - SetColorUpdated(vfb, gstate_c.skipDrawReason); - } else { - INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->fb_format); - DestroyFramebuf(vfb); - vfbs_.erase(vfbs_.begin() + i--); + if (useBufferedRendering_ && vfb->fbo) { + GEBufferFormat fmt = vfb->fb_format; + if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) { + // If we're not rendering to it, format may be wrong. Use displayFormat_ instead. + // TODO: This doesn't seem quite right anymore. + fmt = displayFormat_; } + DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height); + SetColorUpdated(vfb, gstate_c.skipDrawReason); + } else { + INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format)); + DestroyFramebuf(vfb); + vfbs_.erase(vfbs_.begin() + i--); } } - - RebindFramebuffer("RebindFramebuffer - UpdateFromMemory"); } + + RebindFramebuffer("RebindFramebuffer - UpdateFromMemory"); + // TODO: Necessary? gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } @@ -1838,7 +1844,7 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram char name[64]; snprintf(name, sizeof(name), "download_temp"); // TODO: We don't have a way to create a depth-only framebuffer yet. - // Also on many + // Also, at least on Vulkan we always create both depth and color, need to rework how we handle renderpasses. nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, channel == RASTER_DEPTH ? true : false, name }); if (!nvfb->fbo) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight); @@ -2417,7 +2423,9 @@ void FramebufferManagerCommon::PackFramebufferSync(VirtualFramebuffer *vfb, int int stride = channel == RASTER_COLOR ? vfb->fb_stride : vfb->z_stride; const int dstByteOffset = (y * stride + x) * dstBpp; - const int dstSize = (h * stride + w - 1) * dstBpp; // TODO: What's that "w - 1" doing here? + // Leave the gap between the end of the last line and the full stride. + // This is only used for the NotifyMemInfo range. + const int dstSize = (h * stride + w - 1) * dstBpp; if (!Memory::IsValidRange(fb_address + dstByteOffset, dstSize)) { ERROR_LOG_REPORT(G3D, "PackFramebufferSync would write outside of memory, ignoring"); diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 795af08774..f84f0c41a7 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -308,7 +308,7 @@ public: bool NotifyFramebufferCopy(u32 src, u32 dest, int size, bool isMemset, u32 skipDrawReason); void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt); - void UpdateFromMemory(u32 addr, int size, bool safe); + void UpdateFromMemory(u32 addr, int size); void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor); bool PerformStencilUpload(u32 addr, int size, StencilUpload flags); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 18f1cab9d4..f3dac1eabd 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2938,7 +2938,7 @@ void GPUCommon::InvalidateCache(u32 addr, int size, GPUInvalidationType type) { // Vempire invalidates (with writeback) after drawing, but before blitting. // TODO: Investigate whether we can get this to work some other way. if (type == GPU_INVALIDATE_SAFE) { - framebufferManager_->UpdateFromMemory(addr, size, type == GPU_INVALIDATE_SAFE); + framebufferManager_->UpdateFromMemory(addr, size); } } } From a569921716d4a63ba38d8ebc529b6f1aed7be7bb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 29 Aug 2022 22:37:17 -0700 Subject: [PATCH 03/99] GE Debugger: Improve display list disasm. This makes many registers clearer, and corrects some that were totally wrong like minz/maxz. --- GPU/GeDisasm.cpp | 427 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 297 insertions(+), 130 deletions(-) diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp index ef04d7c606..4f4bdb0efd 100644 --- a/GPU/GeDisasm.cpp +++ b/GPU/GeDisasm.cpp @@ -16,11 +16,12 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include - #include "Core/MemMap.h" #include "GPU/ge_constants.h" #include "GPU/GPU.h" +#include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +#include "GPU/GeDisasm.h" void GeDescribeVertexType(u32 op, char *buffer, int len) { bool through = (op & GE_VTYPE_THROUGH_MASK) == GE_VTYPE_THROUGH; @@ -113,7 +114,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_BASE: - snprintf(buffer, bufsize, "BASE: %06x", data); + if ((data & 0x000F0000) == data) + snprintf(buffer, bufsize, "BASE: high=%02x", data >> 16); + else + snprintf(buffer, bufsize, "BASE: high=%02x (extra %06x)", data >> 16, data & ~0x000F0000); break; case GE_CMD_VADDR: @@ -171,16 +175,16 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_JUMP: { - u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; - snprintf(buffer, bufsize, "JUMP: %08x to %08x", pc, target); + u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); + snprintf(buffer, bufsize, "JUMP: %08x to %08x (%06x)", pc, target, data); } break; case GE_CMD_CALL: { u32 retval = pc + 4; - u32 target = gstate_c.getRelativeAddress(op & 0xFFFFFF); - snprintf(buffer, bufsize, "CALL: %08x to %08x, ret=%08x", pc, target, retval); + u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); + snprintf(buffer, bufsize, "CALL: %08x to %08x (%06x), ret=%08x", pc, target, data, retval); } break; @@ -210,28 +214,45 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { int signal = prev & 0xFFFF; int enddata = data & 0xFFFFFF; // We should probably defer to sceGe here, no sense in implementing this stuff in every GPU + u32 target = (((signal << 16) | (enddata & 0xFFFF)) & 0xFFFFFFFC); switch (behaviour) { - case 1: + case PSP_GE_SIGNAL_HANDLER_SUSPEND: snprintf(buffer, bufsize, "Signal with wait. signal/end: %04x %04x", signal, enddata); break; - case 2: + case PSP_GE_SIGNAL_HANDLER_CONTINUE: snprintf(buffer, bufsize, "Signal without wait. signal/end: %04x %04x", signal, enddata); break; - case 3: + case PSP_GE_SIGNAL_HANDLER_PAUSE: snprintf(buffer, bufsize, "Signal with pause. signal/end: %04x %04x", signal, enddata); break; - case 8: + case PSP_GE_SIGNAL_SYNC: snprintf(buffer, bufsize, "Signal with sync. signal/end: %04x %04x", signal, enddata); break; - case 0x10: - snprintf(buffer, bufsize, "Signal with jump. signal/end: %04x %04x", signal, enddata); + case PSP_GE_SIGNAL_JUMP: + snprintf(buffer, bufsize, "Signal with jump. signal/end: %04x %04x, target: %08x", signal, enddata, target); break; - case 0x11: - snprintf(buffer, bufsize, "Signal with call. signal/end: %04x %04x", signal, enddata); + case PSP_GE_SIGNAL_CALL: + snprintf(buffer, bufsize, "Signal with call. signal/end: %04x %04x, target: %08x", signal, enddata, target); break; - case 0x12: + case PSP_GE_SIGNAL_RET: snprintf(buffer, bufsize, "Signal with return. signal/end: %04x %04x", signal, enddata); break; + case PSP_GE_SIGNAL_RJUMP: + target += pc; + snprintf(buffer, bufsize, "Signal with jump (relative.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; + case PSP_GE_SIGNAL_RCALL: + target += pc; + snprintf(buffer, bufsize, "Signal with call (relative.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; + case PSP_GE_SIGNAL_OJUMP: + target = gstate_c.getRelativeAddress(target); + snprintf(buffer, bufsize, "Signal with jump (offset.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; + case PSP_GE_SIGNAL_OCALL: + target = gstate_c.getRelativeAddress(target); + snprintf(buffer, bufsize, "Signal with call (offset.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; default: snprintf(buffer, bufsize, "UNKNOWN Signal UNIMPLEMENTED %i! signal/end: %04x %04x", behaviour, signal, enddata); break; @@ -262,7 +283,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_ORIGIN: - snprintf(buffer, bufsize, "ORIGIN: %06x", data); + if (data) + snprintf(buffer, bufsize, "ORIGIN offset=%08x (extra %06x)", pc, data); + else + snprintf(buffer, bufsize, "ORIGIN offset=%08x", pc); break; case GE_CMD_VERTEXTYPE: @@ -273,24 +297,24 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_OFFSETADDR: - snprintf(buffer, bufsize, "OffsetAddr: %06x", data); + snprintf(buffer, bufsize, "OffsetAddr: %06x (offset=%08x)", data, data << 8); break; case GE_CMD_REGION1: { int x1 = data & 0x3ff; - int y1 = data >> 10; + int y1 = (data >> 10) & 0x3ff; if (data & 0xF00000) - snprintf(buffer, bufsize, "Region TL: %d %d (extra %x)", x1, y1, data >> 20); + snprintf(buffer, bufsize, "Region Rate: %d %d (extra %x)", x1, y1, data >> 20); else - snprintf(buffer, bufsize, "Region TL: %d %d", x1, y1); + snprintf(buffer, bufsize, "Region Rate: %d %d", x1, y1); } break; case GE_CMD_REGION2: { int x2 = data & 0x3ff; - int y2 = data >> 10; + int y2 = (data >> 10) & 0x3ff; if (data & 0xF00000) snprintf(buffer, bufsize, "Region BR: %d %d (extra %x)", x2, y2, data >> 20); else @@ -349,7 +373,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_SCISSOR1: { int x1 = data & 0x3ff; - int y1 = data >> 10; + int y1 = (data >> 10) & 0x3ff; if (data & 0xF00000) snprintf(buffer, bufsize, "Scissor TL: %i, %i (extra %x)", x1, y1, data >> 20); else @@ -359,7 +383,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_SCISSOR2: { int x2 = data & 0x3ff; - int y2 = data >> 10; + int y2 = (data >> 10) & 0x3ff; if (data & 0xF00000) snprintf(buffer, bufsize, "Scissor BR: %i, %i (extra %x)", x2, y2, data >> 20); else @@ -368,33 +392,37 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_MINZ: - { - float zMin = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "MinZ: %f", zMin); - } + if (data & 0xFF0000) + snprintf(buffer, bufsize, "MinZ: 0x%04x / %f (extra %02x)", data & 0xFFFF, (float)(data & 0xFFFF) / 65535.0f, data >> 16); + else + snprintf(buffer, bufsize, "MinZ: 0x%04x / %f", data, (float)data / 65535.0f); break; case GE_CMD_MAXZ: - { - float zMax = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "MaxZ: %f", zMax); - } + if (data & 0xFF0000) + snprintf(buffer, bufsize, "MaxZ: 0x%04x / %f (extra %02x)", data & 0xFFFF, (float)(data & 0xFFFF) / 65535.0f, data >> 16); + else + snprintf(buffer, bufsize, "MaxZ: 0x%04x / %f", data, (float)data / 65535.0f); break; case GE_CMD_FRAMEBUFPTR: { - snprintf(buffer, bufsize, "FramebufPtr: %08x", data); + snprintf(buffer, bufsize, "Framebuf ptr: 0x04%06x", data); } break; case GE_CMD_FRAMEBUFWIDTH: - { - snprintf(buffer, bufsize, "FramebufWidth: %x, address high %02x", data & 0xFFFF, data >> 16); - } + if (data & ~0x07FC) + snprintf(buffer, bufsize, "Framebuf stride: 0x%x (extra %06x)", data & 0x07FC, data & ~0x07FC); + else + snprintf(buffer, bufsize, "Framebuf stride: %04x", data); break; case GE_CMD_FRAMEBUFPIXFORMAT: - snprintf(buffer, bufsize, "FramebufPixelFormat: %i", data); + if (data <= 3) + snprintf(buffer, bufsize, "Framebuf PixelFormat: %s", GeBufferFormatToString((GEBufferFormat)data)); + else + snprintf(buffer, bufsize, "Framebuf PixelFormat: invalid %x", data); break; case GE_CMD_TEXADDR0: @@ -405,7 +433,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TEXADDR5: case GE_CMD_TEXADDR6: case GE_CMD_TEXADDR7: - snprintf(buffer, bufsize, "Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data); + snprintf(buffer, bufsize, "Texture address %d: low=%06x", cmd - GE_CMD_TEXADDR0, data); break; case GE_CMD_TEXBUFWIDTH0: @@ -416,31 +444,53 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TEXBUFWIDTH5: case GE_CMD_TEXBUFWIDTH6: case GE_CMD_TEXBUFWIDTH7: - snprintf(buffer, bufsize, "Texture BUFWIDTH %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data); + snprintf(buffer, bufsize, "Texture stride %d: 0x%04x, address high=%02x", cmd - GE_CMD_TEXBUFWIDTH0, data & 0xFFFF, data >> 16); break; case GE_CMD_CLUTADDR: - snprintf(buffer, bufsize, "CLUT base addr: %06x", data); + snprintf(buffer, bufsize, "CLUT addr: low=%06x", data); break; case GE_CMD_CLUTADDRUPPER: - snprintf(buffer, bufsize, "CLUT addr upper %08x", data); + if ((data & 0x000F0000) == data) + snprintf(buffer, bufsize, "CLUT addr: high=%02x", data >> 16); + else + snprintf(buffer, bufsize, "CLUT addr: high=%02x (extra %06x)", data >> 16, data & ~0x000F0000); break; case GE_CMD_LOADCLUT: - // This could be used to "dirty" textures with clut. - if (data) - snprintf(buffer, bufsize, "Clut load: %08x, %d bytes, %06x", gstate.getClutAddress(), (data & 0x3F) << 5, data & 0xFFFFC0); + if ((data & 0xFFFFC0) != 0) + snprintf(buffer, bufsize, "Clut load: %08x, %d bytes (extra %06x)", gstate.getClutAddress(), (data & 0x3F) << 5, data & 0xFFFFC0); else - snprintf(buffer, bufsize, "Clut load"); + snprintf(buffer, bufsize, "Clut load: %08x, %d bytes", gstate.getClutAddress(), (data & 0x3F) << 5); break; case GE_CMD_TEXMAPMODE: - snprintf(buffer, bufsize, "Tex map mode: %06x", data); + { + static const char *uvgen[] = { + "texcoords", + "texgen matrix", + "env map", + "invalid" + }; + static const char *uvproj[] = { + "pos", + "uv", + "normalized normal", + "normal", + }; + if ((data & 0x000303) == data) + snprintf(buffer, bufsize, "Tex map mode: uvgen=%s, uvproj=%s", uvgen[data & 3], uvproj[(data >> 8) & 3]); + else + snprintf(buffer, bufsize, "Tex map mode: uvgen=%s, uvproj=%s (extra %06x)", uvgen[data & 3], uvproj[(data >> 8) & 3], data & ~0x000303); + } break; case GE_CMD_TEXSHADELS: - snprintf(buffer, bufsize, "Tex shade light sources: %06x", data); + if ((data & 0x000303) == data) + snprintf(buffer, bufsize, "Tex shade light sources: %d, %d", data & 3, (data >> 8) & 3); + else + snprintf(buffer, bufsize, "Tex shade light sources: %d, %d (extra %06x)", data & 3, (data >> 8) & 3, data & ~0x000303); break; case GE_CMD_CLUTFORMAT: @@ -458,9 +508,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TRANSFERSRC: { if (data & 0xF) - snprintf(buffer, bufsize, "Block transfer src: %06x (extra: %x)", data & ~0xF, data & 0xF); + snprintf(buffer, bufsize, "Block transfer src: low=%06x (extra: %x)", data & ~0xF, data & 0xF); else - snprintf(buffer, bufsize, "Block transfer src: %06x", data); + snprintf(buffer, bufsize, "Block transfer src: low=%06x", data); // Nothing to do, the next one prints } break; @@ -470,9 +520,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { u32 xferSrc = (gstate.transfersrc & 0x00FFFFFF) | ((data & 0xFF0000) << 8); u32 xferSrcW = data & 0x3FF; if (data & ~0xFF03FF) - snprintf(buffer, bufsize, "Block transfer src: %08x W: %i (extra %x)", xferSrc, xferSrcW, data); + snprintf(buffer, bufsize, "Block transfer src: high=%02x, w=%d (addr %08x, extra %x)", data >> 16, xferSrcW, xferSrc, data & ~0xFF03FF); else - snprintf(buffer, bufsize, "Block transfer src: %08x W: %i", xferSrc, xferSrcW); + snprintf(buffer, bufsize, "Block transfer src: high=%02x, w=%d (addr %08x)", data >> 16, xferSrcW, xferSrc); break; } @@ -480,9 +530,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { { // Nothing to do, the next one prints if (data & 0xF) - snprintf(buffer, bufsize, "Block transfer dst: %06x (extra: %x)", data & ~0xF, data & 0xF); + snprintf(buffer, bufsize, "Block transfer dst: low=%06x (extra: %x)", data & ~0xF, data & 0xF); else - snprintf(buffer, bufsize, "Block transfer dst: %06x", data); + snprintf(buffer, bufsize, "Block transfer dst: low=%06x", data); } break; @@ -491,9 +541,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { u32 xferDst = (gstate.transferdst & 0x00FFFFFF) | ((data & 0xFF0000) << 8); u32 xferDstW = data & 0x3FF; if (data & ~0xFF03FF) - snprintf(buffer, bufsize, "Block transfer dest: %08x W: %i (extra %x)", xferDst, xferDstW, data); + snprintf(buffer, bufsize, "Block transfer dst: high=%02x, w=%d (addr %08x, extra %x)", data >> 16, xferDstW, xferDst, data & ~0xFF03FF); else - snprintf(buffer, bufsize, "Block transfer dest: %08x W: %i", xferDst, xferDstW); + snprintf(buffer, bufsize, "Block transfer dst: high=%02x, w=%d (addr %08x)", data >> 16, xferDstW, xferDst); break; } @@ -513,9 +563,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { u32 x = (data & 1023); u32 y = ((data>>10) & 1023); if (data & 0xF00000) - snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i (extra %x)", x, y, data >> 20); + snprintf(buffer, bufsize, "Block transfer dst rect TL: %d, %d (extra %x)", x, y, data >> 20); else - snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i", x, y); + snprintf(buffer, bufsize, "Block transfer dst rect TL: %d, %d", x, y); break; } @@ -532,9 +582,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TRANSFERSTART: if (data & ~1) - snprintf(buffer, bufsize, "Block transfer start: %d (extra %x)", data & 1, data & ~1); + snprintf(buffer, bufsize, "Block transfer start: bpp=%d (extra %x)", (data & 1) ? 4 : 2, data & ~1); else - snprintf(buffer, bufsize, "Block transfer start: %d", data); + snprintf(buffer, bufsize, "Block transfer start: bpp=%d", (data & 1) ? 4 : 2); break; case GE_CMD_TEXSIZE0: @@ -548,7 +598,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { { int w = 1 << (data & 0xf); int h = 1 << ((data>>8) & 0xf); - snprintf(buffer, bufsize, "Texture size %i: %06x, width : %d, height : %d", cmd - GE_CMD_TEXSIZE0, data, w, h); + if ((data & ~0x0F0F) && w <= 512 && h <= 512) + snprintf(buffer, bufsize, "Texture size %d: %dx%d", cmd - GE_CMD_TEXSIZE0, w, h); + else + snprintf(buffer, bufsize, "Texture size %d: %dx%d (extra %06x)", cmd - GE_CMD_TEXSIZE0, w, h, data); } break; @@ -559,7 +612,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_ZBUFWIDTH: - snprintf(buffer, bufsize, "Zbuf width: %06x", data); + if (data & ~0x07FC) + snprintf(buffer, bufsize, "Zbuf stride: 0x%x (extra %06x)", data & 0x07FC, data & ~0x07FC); + else + snprintf(buffer, bufsize, "Zbuf stride: %04x", data); break; case GE_CMD_AMBIENTCOLOR: @@ -567,7 +623,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_AMBIENTALPHA: - snprintf(buffer, bufsize, "Ambient alpha: %06x", data); + if (data & ~0xFF) + snprintf(buffer, bufsize, "Ambient alpha: %02x (extra %04x)", data & 0xFF, data >> 8); + else + snprintf(buffer, bufsize, "Ambient alpha: %02x", data); break; case GE_CMD_MATERIALAMBIENT: @@ -587,7 +646,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_MATERIALALPHA: - snprintf(buffer, bufsize, "Material alpha color: %06x", data); + if (data & ~0xFF) + snprintf(buffer, bufsize, "Material alpha: %02x (extra %04x)", data & 0xFF, data >> 8); + else + snprintf(buffer, bufsize, "Material alpha: %02x", data); break; case GE_CMD_MATERIALSPECULARCOEF: @@ -596,23 +658,43 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_SHADEMODE: if (data & ~1) - snprintf(buffer, bufsize, "Shade: %06x (%s, extra %x)", data, data ? "gouraud" : "flat", data); + snprintf(buffer, bufsize, "Shade: %d (%s, extra %x)", data & 1, (data & 1) ? "gouraud" : "flat", data & ~1); else - snprintf(buffer, bufsize, "Shade: %06x (%s)", data, data ? "gouraud" : "flat"); + snprintf(buffer, bufsize, "Shade: %d (%s)", data & 1, (data & 1) ? "gouraud" : "flat"); break; case GE_CMD_LIGHTMODE: if (data & ~1) - snprintf(buffer, bufsize, "Lightmode: %06x (%s, extra %x)", data, data ? "separate spec" : "single color", data); + snprintf(buffer, bufsize, "Lightmode: %d (%s, extra %x)", data & 1, (data & 1) ? "separate spec" : "single color", data & ~1); else - snprintf(buffer, bufsize, "Lightmode: %06x (%s)", data, data ? "separate spec" : "single color"); + snprintf(buffer, bufsize, "Lightmode: %d (%s)", data & 1, (data & 1) ? "separate spec" : "single color"); break; case GE_CMD_LIGHTTYPE0: case GE_CMD_LIGHTTYPE1: case GE_CMD_LIGHTTYPE2: case GE_CMD_LIGHTTYPE3: - snprintf(buffer, bufsize, "Light %i type: %06x", cmd-GE_CMD_LIGHTTYPE0, data); + { + static const char *lightComputations[] = { + "diffuse", + "diffuse + spec", + "pow(diffuse)", + "unknown (diffuse?)", + }; + static const char *lightTypes[] = { + "directional", + "point", + "spot", + "unknown (directional?)", + }; + const int comp = (data & 0x0003) >> 0; + const int type = (data & 0x0300) >> 8; + + if (data & ~0x0303) + snprintf(buffer, bufsize, "Light %d type: %s, comp: %s (extra %06x)", cmd - GE_CMD_LIGHTTYPE0, lightTypes[type], lightComputations[comp], data & ~0x0303); + else + snprintf(buffer, bufsize, "Light %d type: %s, comp: %s", cmd - GE_CMD_LIGHTTYPE0, lightTypes[type], lightComputations[comp]); + } break; case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0: @@ -669,22 +751,22 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_VIEWPORTXSCALE: + snprintf(buffer, bufsize, "Viewport X scale: %f", getFloat24(data)); + break; case GE_CMD_VIEWPORTYSCALE: - case GE_CMD_VIEWPORTXCENTER: - case GE_CMD_VIEWPORTYCENTER: - snprintf(buffer, bufsize, "Viewport param %i: %f", cmd-GE_CMD_VIEWPORTXSCALE, getFloat24(data)); + snprintf(buffer, bufsize, "Viewport Y scale: %f", getFloat24(data)); break; case GE_CMD_VIEWPORTZSCALE: - { - float zScale = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "Viewport Z scale: %f", zScale); - } + snprintf(buffer, bufsize, "Viewport Z scale: %f", getFloat24(data)); + break; + case GE_CMD_VIEWPORTXCENTER: + snprintf(buffer, bufsize, "Viewport X center: %f", getFloat24(data)); + break; + case GE_CMD_VIEWPORTYCENTER: + snprintf(buffer, bufsize, "Viewport Y center: %f", getFloat24(data)); break; case GE_CMD_VIEWPORTZCENTER: - { - float zOff = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "Viewport Z pos: %f", zOff); - } + snprintf(buffer, bufsize, "Viewport Z center: %f", getFloat24(data)); break; case GE_CMD_LIGHTENABLE0: @@ -695,34 +777,69 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_CULL: - snprintf(buffer, bufsize, "Cull: %06x", data); + if (data & ~1) + snprintf(buffer, bufsize, "Cull: %s (extra %06x)", (data & 1) ? "back (CCW)" : "front (CW)", data & ~1); + else + snprintf(buffer, bufsize, "Cull: %s", (data & 1) ? "back (CCW)" : "front (CW)"); break; case GE_CMD_PATCHDIVISION: { - int patch_div_s = data & 0xFF; - int patch_div_t = (data >> 8) & 0xFF; - if (data & 0xFF0000) - snprintf(buffer, bufsize, "Patch subdivision: %i x %i (extra %x)", patch_div_s, patch_div_t, data & 0xFF0000); + int patch_div_s = data & 0x7F; + int patch_div_t = (data >> 8) & 0x7F; + if (data & 0xFF8080) + snprintf(buffer, bufsize, "Patch subdivision: %d x %d (extra %x)", patch_div_s, patch_div_t, data & 0xFF8080); else - snprintf(buffer, bufsize, "Patch subdivision: %i x %i", patch_div_s, patch_div_t); + snprintf(buffer, bufsize, "Patch subdivision: %d x %d", patch_div_s, patch_div_t); } break; case GE_CMD_PATCHPRIMITIVE: - snprintf(buffer, bufsize, "Patch Primitive: %d", data); + { + static const char *patchPrims[] = { + "triangles", + "lines", + "points", + "unknown/points", + }; + if (data & ~3) + snprintf(buffer, bufsize, "Patch Primitive: %s (extra %06x)", patchPrims[data & 3], data & ~3); + else + snprintf(buffer, bufsize, "Patch Primitive: %s", patchPrims[data & 3]); + } break; case GE_CMD_PATCHFACING: - snprintf(buffer, bufsize, "Patch Facing: %d", data); + if (data & ~1) + snprintf(buffer, bufsize, "Patch Facing: %s (extra %06x)", (data & 1) ? "reversed normals" : "standard normals", data & ~1); + else + snprintf(buffer, bufsize, "Patch Facing: %s", (data & 1) ? "reversed normals" : "standard normals"); break; case GE_CMD_REVERSENORMAL: - snprintf(buffer, bufsize, "Reverse normal: %d", data); + if (data & ~1) + snprintf(buffer, bufsize, "Reverse normal: %s (extra %06x)", (data & 1) ? "reversed" : "standard", data & ~1); + else + snprintf(buffer, bufsize, "Reverse normal: %s", (data & 1) ? "reversed" : "standard"); break; case GE_CMD_MATERIALUPDATE: - snprintf(buffer, bufsize, "Material update: %d", data); + { + static const char *materialTypes[] = { + "none", + "ambient", + "diffuse", + "ambient, diffuse", + "specular", + "ambient, specular", + "diffuse, specular", + "ambient, diffuse, specular", + }; + if (data & ~7) + snprintf(buffer, bufsize, "Material update: %s (extra %06x)", materialTypes[data & 7], data & ~7); + else + snprintf(buffer, bufsize, "Material update: %s", materialTypes[data & 7]); + } break; @@ -747,7 +864,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { mode = clearModes[(data >> 8) & 7]; else mode = "off"; - snprintf(buffer, bufsize, "Clear mode: %06x (%s)", data, mode); + if (data & ~0x0701) + snprintf(buffer, bufsize, "Clear mode: %s (extra %06x)", mode, data & ~0x0701); + else + snprintf(buffer, bufsize, "Clear mode: %s", mode); } break; @@ -832,7 +952,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_ALPHATEST: { const char *alphaTestFuncs[] = { " NEVER ", " ALWAYS ", " == ", " != ", " < ", " <= ", " > ", " >= " }; - snprintf(buffer, bufsize, "Alpha test settings: %06x ((c & %02x)%s%02x)", data, (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF); + if (data & ~0xFFFF07) + snprintf(buffer, bufsize, "Alpha test: (src.a & %02x)%s%02x (extra %06x)", (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF, data & ~0xFFFF07); + else + snprintf(buffer, bufsize, "Alpha test: (src.a & %02x)%s%02x", (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF); } break; @@ -860,25 +983,35 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { "blend", "replace", "add", - "unsupported1", - "unsupported2", - "unsupported3", + "add2", + "add3", + "add4", }; if (data & ~0x10107) - snprintf(buffer, bufsize, "TexFunc %i %s %s%s (extra %x)", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : "", data); + snprintf(buffer, bufsize, "TexFunc %d %s %s%s (extra %x)", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? ", color double" : "", data & ~0x10107); else - snprintf(buffer, bufsize, "TexFunc %i %s %s%s", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : ""); + snprintf(buffer, bufsize, "TexFunc %d %s %s%s", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? ", color double" : ""); } break; case GE_CMD_TEXFILTER: { + static const char *textureFilters[] = { + "nearest", + "linear", + "nearest, invalid", + "linear, invalid", + "nearest, mipmap nearest", + "linear, mipmap nearest", + "nearest, mipmap linear", + "linear, mipmap linear", + }; int min = data & 7; int mag = (data >> 8) & 1; if (data & ~0x107) - snprintf(buffer, bufsize, "TexFilter min: %i mag: %i (extra %x)", min, mag, data); + snprintf(buffer, bufsize, "TexFilter min: %s, mag: %s (extra %x)", textureFilters[min], textureFilters[mag], data & ~0x107); else - snprintf(buffer, bufsize, "TexFilter min: %i mag: %i", min, mag); + snprintf(buffer, bufsize, "TexFilter min: %s, mag: %s", textureFilters[min], textureFilters[mag]); } break; @@ -887,12 +1020,15 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_TEXMODE: - snprintf(buffer, bufsize, "TexMode %06x (%s, %d levels, %s)", data, data & 1 ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut"); + if (data & ~0x070101) + snprintf(buffer, bufsize, "TexMode %s, %d levels, %s (extra %06x)", (data & 1) ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut", data & ~0x070101); + else + snprintf(buffer, bufsize, "TexMode %s, %d levels, %s", (data & 1) ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut"); break; case GE_CMD_TEXFORMAT: { - const char *texformats[] = { + static const char *texformats[] = { "5650", "5551", "4444", @@ -910,7 +1046,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { "unsupported4", "unsupported5", }; - snprintf(buffer, bufsize, "TexFormat %06x (%s)", data, texformats[data & 0xF]); + if (data & ~0xF) + snprintf(buffer, bufsize, "TexFormat %s (extra %06x)", texformats[data & 0xF], data & ~0xF); + else + snprintf(buffer, bufsize, "TexFormat %s", texformats[data & 0xF]); } break; @@ -930,24 +1069,34 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TEXWRAP: if (data & ~0x0101) - snprintf(buffer, bufsize, "TexWrap %s s, %s t (extra %x)", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap", data); + snprintf(buffer, bufsize, "TexWrap %s s, %s t (extra %x)", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap", data & ~0x0101); else snprintf(buffer, bufsize, "TexWrap %s s, %s t", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap"); break; case GE_CMD_TEXLEVEL: - if (data & ~0xFF0003) - snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i (extra %x)", data&3, data >> 16, data); - else - snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i", data&3, data >> 16); + { + static const char *mipLevelModes[] = { + "auto + bias", + "bias", + "slope + bias", + "invalid + bias", + }; + const int biasFixed = (s8)(data >> 16); + const float bias = (float)biasFixed / 16.0f; + if (data & ~0xFF0003) + snprintf(buffer, bufsize, "TexLevel mode: %s Offset: %f / %d (extra %x)", mipLevelModes[data & 3], bias, biasFixed, data & ~0xFF0003); + else + snprintf(buffer, bufsize, "TexLevel mode: %s Offset: %f / %d", mipLevelModes[data & 3], bias, biasFixed); + } break; case GE_CMD_FOG1: - snprintf(buffer, bufsize, "Fog1 %f", getFloat24(data)); + snprintf(buffer, bufsize, "Fog end %f", getFloat24(data)); break; case GE_CMD_FOG2: - snprintf(buffer, bufsize, "Fog2 %f", getFloat24(data)); + snprintf(buffer, bufsize, "Fog slope %f", getFloat24(data)); break; case GE_CMD_FOGCOLOR: @@ -955,7 +1104,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_TEXLODSLOPE: - snprintf(buffer, bufsize, "TexLodSlope %06x", data); + snprintf(buffer, bufsize, "TexLodSlope %f", getFloat24(data)); break; ////////////////////////////////////////////////////////////////// @@ -971,15 +1120,18 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_STENCILOP: { - const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT", "unsupported1", "unsupported2" }; + static const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT", "unsupported1", "unsupported2" }; snprintf(buffer, bufsize, "Stencil op: fail=%s, pass/depthfail=%s, pass=%s", stencilOps[data & 7], stencilOps[(data >> 8) & 7], stencilOps[(data >> 16) & 7]); } break; case GE_CMD_STENCILTEST: { - const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " }; - snprintf(buffer, bufsize, "Stencil test: %06x (%02x %s (c & %02x))", data, (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF); + static const char *zTestFuncs[] = { " NEVER ", " ALWAYS ", " == ", " != ", " < ", " <= ", " > ", " >= " }; + if (data & ~0xFFFF07) + snprintf(buffer, bufsize, "Stencil test: %02x%s(dst.a & %02x) (extra %06x)", (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF, data & ~0xFFFF07); + else + snprintf(buffer, bufsize, "Stencil test: %02x%s(dst.a & %02x)", (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF); } break; @@ -989,8 +1141,11 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_ZTEST: { - const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " }; - snprintf(buffer, bufsize, "Z test mode: %i (%s)", data, zTestFuncs[data & 7]); + static const char *zTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" }; + if (data & ~7) + snprintf(buffer, bufsize, "Z test mode: %s (extra %06x)", zTestFuncs[data & 7], data & ~7); + else + snprintf(buffer, bufsize, "Z test mode: %s", zTestFuncs[data & 7]); } break; @@ -1036,18 +1191,27 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { "negated and", "set", }; - snprintf(buffer, bufsize, "LogicOp: %06x (%s)", data, logicOps[data & 0xF]); + if (data & ~0xF) + snprintf(buffer, bufsize, "LogicOp: %s (%06x)", logicOps[data & 0xF], data & ~0xF); + else + snprintf(buffer, bufsize, "LogicOp: %s", logicOps[data & 0xF]); } break; case GE_CMD_ZWRITEDISABLE: - snprintf(buffer, bufsize, "ZMask: %06x", data); + if (data & ~1) + snprintf(buffer, bufsize, "ZMask: %s (extra %06x)", data & 1 ? "disable write" : "allow write", data & ~1); + else + snprintf(buffer, bufsize, "ZMask: %s", data & 1 ? "disable write" : "allow write"); break; case GE_CMD_COLORTEST: { - const char *colorTests[] = {"NEVER", "ALWAYS", " == ", " != "}; - snprintf(buffer, bufsize, "ColorTest: %06x (ref%s(c & cmask))", data, colorTests[data & 3]); + const char *colorTests[] = {" NEVER ", " ALWAYS ", " == ", " != "}; + if (data & ~3) + snprintf(buffer, bufsize, "ColorTest: (src.rgb & cmask)%s(dst.rgb & cmask) (extra %06x)", colorTests[data & 3], data & ~3); + else + snprintf(buffer, bufsize, "ColorTest: (src.rgb & cmask)%s(dst.rgb & cmask)", colorTests[data & 3]); } break; @@ -1060,18 +1224,21 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_MASKRGB: - snprintf(buffer, bufsize, "MaskRGB: %06x", data); + snprintf(buffer, bufsize, "MaskRGB: %06x (bits not to write)", data); break; case GE_CMD_MASKALPHA: - snprintf(buffer, bufsize, "MaskAlpha: %06x", data); + if (data & ~0xFF) + snprintf(buffer, bufsize, "MaskAlpha: %02x (bits not to write) (extra %04x)", data & 0xFF, data >> 8); + else + snprintf(buffer, bufsize, "MaskAlpha: %02x (bits not to write)", data & 0xFF); break; case GE_CMD_WORLDMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "World # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "World # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "World # %i", data & 0xF); + snprintf(buffer, bufsize, "World # %d", data & 0xF); break; case GE_CMD_WORLDMATRIXDATA: @@ -1080,9 +1247,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_VIEWMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "VIEW # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "VIEW # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "VIEW # %i", data & 0xF); + snprintf(buffer, bufsize, "VIEW # %d", data & 0xF); break; case GE_CMD_VIEWMATRIXDATA: @@ -1091,9 +1258,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_PROJMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "PROJECTION # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "PROJECTION # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "PROJECTION # %i", data & 0xF); + snprintf(buffer, bufsize, "PROJECTION # %d", data & 0xF); break; case GE_CMD_PROJMATRIXDATA: @@ -1102,9 +1269,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TGENMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "TGEN # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "TGEN # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "TGEN # %i", data & 0xF); + snprintf(buffer, bufsize, "TGEN # %d", data & 0xF); break; case GE_CMD_TGENMATRIXDATA: @@ -1113,13 +1280,13 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_BONEMATRIXNUMBER: if (data & ~0x7F) - snprintf(buffer, bufsize, "BONE #%i (extra %x)", data & 0x7F, data); + snprintf(buffer, bufsize, "BONE #%d (extra %x)", data & 0x7F, data & ~0x7F); else - snprintf(buffer, bufsize, "BONE #%i", data & 0x7F); + snprintf(buffer, bufsize, "BONE #%d", data & 0x7F); break; case GE_CMD_BONEMATRIXDATA: - snprintf(buffer, bufsize, "BONE data #%i %f", gstate.boneMatrixNumber & 0x7f, getFloat24(data)); + snprintf(buffer, bufsize, "BONE data # %f", getFloat24(data)); break; default: From 6f2966f5028900a8e53ce9a9fb1ecdf65d789f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 11:56:14 +0200 Subject: [PATCH 04/99] Clean up mip size calculations Calculating the same thing in multiple places is a recipe for bugs, so let's not. --- GPU/Common/TextureCacheCommon.cpp | 31 +++++++++++++------ GPU/Common/TextureCacheCommon.h | 17 +++++++++++ GPU/D3D11/TextureCacheD3D11.cpp | 51 +++++++++++++++---------------- GPU/Directx9/TextureCacheDX9.cpp | 19 +++++++----- GPU/GLES/TextureCacheGLES.cpp | 27 ++++++++-------- GPU/Vulkan/TextureCacheVulkan.cpp | 27 +++++++++------- 6 files changed, 103 insertions(+), 69 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index b2ce179863..104ce100eb 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2319,16 +2319,10 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.w = gstate.getTextureWidth(0); plan.h = gstate.getTextureHeight(0); - plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); - if (plan.replaced->Valid()) { - // We're replacing, so we won't scale. - plan.scaleFactor = 1; - plan.levelsToLoad = plan.replaced->NumLevels(); - plan.badMipSizes = false; - } + bool isPPGETexture = entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd(); // Don't scale the PPGe texture. - if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd()) { + if (isPPGETexture) { plan.scaleFactor = 1; } @@ -2364,6 +2358,25 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } } + if (plan.isVideo || isPPGETexture) { + plan.replaced = &replacer_.FindNone(); + } else { + plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); + } + + if (plan.replaced->Valid()) { + // We're replacing, so we won't scale. + plan.scaleFactor = 1; + plan.levelsToLoad = plan.replaced->NumLevels(); + plan.levelsToCreate = std::min(plan.levelsToLoad, plan.levelsToCreate); + plan.badMipSizes = false; + // But, we still need to create the texture at a larger size. + plan.replaced->GetSize(0, plan.createW, plan.createH); + } else { + plan.createW = plan.w * plan.scaleFactor; + plan.createH = plan.h * plan.scaleFactor; + } + // Always load base level texture here plan.baseLevelSrc = 0; if (IsFakeMipmapChange()) { @@ -2376,7 +2389,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt if (plan.isVideo || plan.depth != 1) { plan.maxPossibleLevels = 1; } else { - plan.maxPossibleLevels = log2i(std::min(plan.w * plan.scaleFactor, plan.h * plan.scaleFactor)) + 1; + plan.maxPossibleLevels = log2i(std::min(plan.createW, plan.createH)) + 1; } if (plan.levelsToCreate == 1) { diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 534a8dbbdb..4a1bd4fc9d 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -264,11 +264,28 @@ struct BuildTexturePlan { int w; int h; + // Scaled (or replaced) size of the 0-mip of the final texture. + int createW; + int createH; + // Used for 3D textures only. If not a 3D texture, will be 1. int depth; // The replacement for the texture. ReplacedTexture *replaced; + + void GetMipSize(int level, int *w, int *h) const { + if (replaced->Valid()) { + replaced->GetSize(level, *w, *h); + } else if (depth == 1) { + *w = createW >> level; + *h = createH >> level; + } else { + // 3D texture, we look for layers instead of levels. + *w = createW; + *h = createH; + } + } }; class TextureCacheCommon { diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 7fee999b6f..f27f3fcc61 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -276,26 +276,27 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { return; } - int tw = plan.w; - int th = plan.h; - DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) { + if (plan.replaced->Valid()) { dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc)); } else if (plan.scaleFactor > 1) { - tw *= plan.scaleFactor; - th *= plan.scaleFactor; dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; } - // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. - int levels;; + int levels; ID3D11ShaderResourceView *view; ID3D11Resource *texture = DxTex(entry); _assert_(texture == nullptr); + int tw; + int th; + plan.GetMipSize(0, &tw, &th); + if (plan.depth == 1) { + // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. + levels = std::min(plan.levelsToCreate, plan.levelsToLoad); + ID3D11Texture2D *tex; D3D11_TEXTURE2D_DESC desc{}; desc.CPUAccessFlags = 0; @@ -305,13 +306,11 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { desc.Width = tw; desc.Height = th; desc.Format = dstFmt; - desc.MipLevels = plan.levelsToCreate; + desc.MipLevels = levels; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &tex)); texture = tex; - - levels = std::min(plan.levelsToCreate, plan.levelsToLoad); } else { ID3D11Texture3D *tex; D3D11_TEXTURE3D_DESC desc{}; @@ -338,45 +337,43 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { for (int i = 0; i < levels; i++) { int srcLevel = (i == 0) ? plan.baseLevelSrc : i; - int w = gstate.getTextureWidth(srcLevel); - int h = gstate.getTextureHeight(srcLevel); + int mipWidth; + int mipHeight; + plan.GetMipSize(i, &mipWidth, &mipHeight); u8 *data = nullptr; int stride = 0; + int bpp = 0; // For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :( // NOTE: Could reuse it between levels or textures! - if (plan.replaced->GetSize(srcLevel, w, h)) { - int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); - stride = w * bpp; - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + if (plan.replaced->Valid()) { + bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); } else { if (plan.scaleFactor > 1) { - data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16); - stride = w * plan.scaleFactor * 4; + bpp = 4; } else { - int bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2; - - stride = std::max(w * bpp, 16); - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2; } } + stride = std::max(mipWidth * bpp, 16); + data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16); + if (!data) { - ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h); + ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight); return; } LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, false); - if (plan.depth == 1) { context_->UpdateSubresource(texture, i, nullptr, data, stride, 0); } else { D3D11_BOX box{}; box.front = i; box.back = i + 1; - box.right = w * plan.scaleFactor; - box.bottom = h * plan.scaleFactor; + box.right = mipWidth; + box.bottom = mipHeight; context_->UpdateSubresource(texture, 0, &box, data, stride, 0); } FreeAlignedMemory(data); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 9de670732d..43b33cc1c1 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -238,27 +238,28 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { return; } - int tw = plan.w; - int th = plan.h; - D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) { + if (plan.replaced->Valid()) { dstFmt = ToD3D9Format(plan.replaced->Format(plan.baseLevelSrc)); } else if (plan.scaleFactor > 1) { - tw *= plan.scaleFactor; - th *= plan.scaleFactor; dstFmt = D3DFMT_A8R8G8B8; } - // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. - int levels = std::min(plan.levelsToCreate, plan.levelsToLoad); + int levels; LPDIRECT3DBASETEXTURE9 &texture = DxTex(entry); D3DPOOL pool = D3DPOOL_DEFAULT; int usage = D3DUSAGE_DYNAMIC; + int tw; + int th; + plan.GetMipSize(0, &tw, &th); + HRESULT hr; if (plan.depth == 1) { + // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. + levels = std::min(plan.levelsToCreate, plan.levelsToLoad); + LPDIRECT3DTEXTURE9 tex; hr = device_->CreateTexture(tw, th, levels, usage, dstFmt, pool, &tex, nullptr); texture = tex; @@ -266,6 +267,8 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { LPDIRECT3DVOLUMETEXTURE9 tex; hr = device_->CreateVolumeTexture(tw, th, plan.depth, 1, usage, dstFmt, pool, &tex, nullptr); texture = tex; + + levels = 1; } if (FAILED(hr)) { diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index b24b48283a..537bfaddaa 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -301,37 +301,36 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { for (int i = 0; i < plan.levelsToLoad; i++) { int srcLevel = i == 0 ? plan.baseLevelSrc : i; - int w = gstate.getTextureWidth(srcLevel); - int h = gstate.getTextureHeight(srcLevel); + int mipWidth; + int mipHeight; + plan.GetMipSize(i, &mipWidth, &mipHeight); u8 *data = nullptr; int stride = 0; + int bpp; - if (plan.replaced->GetSize(srcLevel, w, h)) { - int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); - stride = w * bpp; - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + if (plan.replaced->Valid()) { + bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); } else { if (plan.scaleFactor > 1) { - data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16); - stride = w * plan.scaleFactor * 4; + bpp = 4; } else { - int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; - - stride = std::max(w * bpp, 4); - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; } } + stride = mipWidth * bpp; + data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16); + if (!data) { - ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h); + ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight); return; } LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true); // NOTE: TextureImage takes ownership of data, so we don't free it afterwards. - render_->TextureImage(entry->textureName, i, w * plan.scaleFactor, h * plan.scaleFactor, 1, dstFmt, data, GLRAllocType::ALIGNED); + render_->TextureImage(entry->textureName, i, mipWidth, mipHeight, 1, dstFmt, data, GLRAllocType::ALIGNED); } bool genMips = plan.levelsToCreate > plan.levelsToLoad; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 76d2fbe71f..34547647fa 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -503,7 +503,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); image->SetTag(texName); - bool allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); + bool allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; @@ -519,10 +519,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f); } + // Turn off texture replacement for this texture. + plan.replaced = &replacer_.FindNone(); + + plan.createW /= plan.scaleFactor; + plan.createH /= plan.scaleFactor; plan.scaleFactor = 1; actualFmt = dstFmt; - allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); } if (!allocSuccess) { @@ -563,15 +568,14 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { int mipUnscaledWidth = gstate.getTextureWidth(i); int mipUnscaledHeight = gstate.getTextureHeight(i); - int mipWidth = mipUnscaledWidth * plan.scaleFactor; - int mipHeight = mipUnscaledHeight * plan.scaleFactor; - if (plan.replaced->Valid()) { - plan.replaced->GetSize(plan.baseLevelSrc + i, mipWidth, mipHeight); - } + int mipWidth; + int mipHeight; + plan.GetMipSize(i, &mipWidth, &mipHeight); int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp int stride = (mipWidth * bpp + 15) & ~15; // output stride - int size = stride * mipHeight; + int uploadSize = stride * mipHeight; + uint32_t bufferOffset; VkBuffer texBuf; // NVIDIA reports a min alignment of 1 but that can't be healthy... let's align by 16 as a minimum. @@ -594,7 +598,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { bool dataScaled = true; if (plan.replaced->Valid()) { // Directly load the replaced image. - data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment); + data = drawEngine_->GetPushBufferForTextureData()->PushAligned(uploadSize, &bufferOffset, &texBuf, pushAlignment); double replaceStart = time_now_d(); plan.replaced->Load(plan.baseLevelSrc + i, data, stride); // if it fails, it'll just be garbage data... OK for now. replacementTimeThisFrame_ += time_now_d() - replaceStart; @@ -604,7 +608,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT); } else { if (plan.depth != 1) { - loadLevel(size, i, stride, plan.scaleFactor); + // 3D texturing. + loadLevel(uploadSize, i, stride, plan.scaleFactor); entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, stride / bpp); } else if (computeUpload) { int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2; @@ -626,7 +631,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); vulkan->Delete().QueueDeleteImageView(view); } else { - loadLevel(size, i == 0 ? plan.baseLevelSrc : i, stride, plan.scaleFactor); + loadLevel(uploadSize, i == 0 ? plan.baseLevelSrc : i, stride, plan.scaleFactor); VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT, "Copy Upload: %dx%d", mipWidth, mipHeight); entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, stride / bpp); From 4da2ca093595fe9d4b47efb07c58f5fb14d38d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 11:14:55 +0200 Subject: [PATCH 05/99] Implement shader blending for D3D9 This was easy, dunno why I never got around to it before.. I guess I didn't know about VPOS. This does raise our minimum shader model requirement to ps_3_0. --- Common/GPU/D3D9/D3D9ShaderCompiler.cpp | 4 ++-- Common/GPU/D3D9/thin3d_d3d9.cpp | 6 +----- GPU/Common/FragmentShaderGenerator.cpp | 23 +++++++++++++---------- GPU/Directx9/StateMappingDX9.cpp | 3 +-- unittest/TestShaderGenerators.cpp | 2 +- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Common/GPU/D3D9/D3D9ShaderCompiler.cpp b/Common/GPU/D3D9/D3D9ShaderCompiler.cpp index e8585f15f3..f41ea3911d 100644 --- a/Common/GPU/D3D9/D3D9ShaderCompiler.cpp +++ b/Common/GPU/D3D9/D3D9ShaderCompiler.cpp @@ -54,7 +54,7 @@ LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std: } bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, std::string *errorMessage) { - LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_2_0", errorMessage); + LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_3_0", errorMessage); if (pShaderCode) { // Create pixel shader. device->CreatePixelShader((DWORD*)pShaderCode->GetBufferPointer(), pShader); @@ -66,7 +66,7 @@ bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT } bool CompileVertexShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, std::string *errorMessage) { - LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_2_0", errorMessage); + LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_3_0", errorMessage); if (pShaderCode) { // Create vertex shader. device->CreateVertexShader((DWORD*)pShaderCode->GetBufferPointer(), pShader); diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index cf73de79a3..dc72ed898d 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -1069,11 +1069,7 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si auto compile = [&](const char *profile) -> HRESULT { return dyn_D3DCompile(source, (UINT)strlen(source), nullptr, defines, includes, "main", profile, 0, 0, &codeBuffer, &errorBuffer); }; - HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_2_0" : "vs_2_0"); - if (FAILED(hr) && hr == D3DXERR_INVALIDDATA) { - // Might be a post shader. Let's try using shader model 3. - hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0"); - } + HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0"); if (FAILED(hr)) { const char *error = errorBuffer ? (const char *)errorBuffer->GetBufferPointer() : "(no errorbuffer returned)"; if (hr == ERROR_MOD_NOT_FOUND) { diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 77c8042435..8cf705cce5 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -132,11 +132,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu return false; } - if (readFramebuffer && compat.shaderLanguage == HLSL_D3D9) { - *errorString = "Framebuffer read not yet supported in HLSL D3D9"; - return false; - } - if (compat.shaderLanguage == ShaderLanguage::GLSL_VULKAN) { if (useDiscardStencilBugWorkaround && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) { WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n"); @@ -227,7 +222,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } if (readFramebufferTex) { // No sampler required, we Load - WRITE(p, "Texture2D fboTex : register(t1);\n"); + WRITE(p, "Texture2D fbotex : register(t1);\n"); } WRITE(p, "cbuffer base : register(b0) {\n%s};\n", ub_baseStr); @@ -264,8 +259,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (enableFog) { WRITE(p, " float v_fogdepth: TEXCOORD1;\n"); } - if (compat.shaderLanguage == HLSL_D3D11 && needFragCoord) { - WRITE(p, " vec4 pixelPos : SV_POSITION;\n"); + if (needFragCoord) { + if (compat.shaderLanguage == HLSL_D3D11) { + WRITE(p, " vec4 pixelPos : SV_POSITION;\n"); + } else if (compat.shaderLanguage == HLSL_D3D9) { + WRITE(p, " vec4 pixelPos : VPOS;\n"); // VPOS is only supported for Shader Model 3.0, but we can probably forget about D3D9 SM2.0 at this point... + } } WRITE(p, "};\n"); @@ -457,7 +456,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } else if (compat.shaderLanguage == HLSL_D3D9) { WRITE(p, "PS_OUT main( PS_IN In ) {\n"); WRITE(p, " PS_OUT outfragment;\n"); - WRITE(p, " vec4 target;\n"); + if (needFragCoord) { + WRITE(p, " vec4 gl_FragCoord = In.pixelPos;\n"); + } } else { WRITE(p, "void main() {\n"); } @@ -477,7 +478,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu // Two things read from the old framebuffer - shader replacement blending and bit-level masking. if (readFramebuffer) { if (compat.shaderLanguage == HLSL_D3D11) { - WRITE(p, " vec4 destColor = fboTex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n"); + WRITE(p, " vec4 destColor = fbotex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n"); + } else if (compat.shaderLanguage == HLSL_D3D9) { + WRITE(p, " vec4 destColor = tex2D(fbotex, gl_FragCoord.xy * u_fbotexSize.xy);\n", compat.texture); } else if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) { // If we have EXT_shader_framebuffer_fetch / ARM_shader_framebuffer_fetch, we skip the blit. // We can just read the prev value more directly. diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 1e4d1d348c..3ebeb71fca 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -119,8 +119,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - // Unfortunately, this isn't implemented on DX9 yet. - gstate_c.SetAllowFramebufferRead(false); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); if (gstate.isModeClear()) { dxstate.blend.disable(); // Color Mask diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp index 28ab16e2c4..d90d893ff3 100644 --- a/unittest/TestShaderGenerators.cpp +++ b/unittest/TestShaderGenerators.cpp @@ -102,7 +102,7 @@ bool TestCompileShader(const char *buffer, ShaderLanguage lang, ShaderStage stag } case ShaderLanguage::HLSL_D3D9: { - LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_2_0" : "ps_2_0", errorMessage); + LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_3_0" : "ps_3_0", errorMessage); if (blob) { blob->Release(); return true; From 08eaf7665c5ec6a7b9c4546ba4520759861128c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 12:52:03 +0200 Subject: [PATCH 06/99] Fix race condition in replacement texture loading The texture could become valid during the load, causing an inconsistent state within the texture loading. So can only check for valid-ness once. --- GPU/Common/TextureCacheCommon.cpp | 4 +++- GPU/Common/TextureCacheCommon.h | 4 +++- GPU/D3D11/TextureCacheD3D11.cpp | 6 +++--- GPU/Directx9/TextureCacheDX9.cpp | 4 ++-- GPU/GLES/TextureCacheGLES.cpp | 4 ++-- GPU/Vulkan/TextureCacheVulkan.cpp | 9 +++++---- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 104ce100eb..96ac9ca8b4 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2360,11 +2360,13 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt if (plan.isVideo || isPPGETexture) { plan.replaced = &replacer_.FindNone(); + plan.replaceValid = false; } else { plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); + plan.replaceValid = plan.replaced->Valid(); } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { // We're replacing, so we won't scale. plan.scaleFactor = 1; plan.levelsToLoad = plan.replaced->NumLevels(); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 4a1bd4fc9d..57b7ceb421 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -273,9 +273,11 @@ struct BuildTexturePlan { // The replacement for the texture. ReplacedTexture *replaced; + // Need to only check once since it can change during the load! + bool replaceValid; void GetMipSize(int level, int *w, int *h) const { - if (replaced->Valid()) { + if (replaceValid) { replaced->GetSize(level, *w, *h); } else if (depth == 1) { *w = createW >> level; diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index f27f3fcc61..baa8ee5e04 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -277,7 +277,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { } DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaced->Valid()) { + if (plan.replaceValid) { dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc)); } else if (plan.scaleFactor > 1) { dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; @@ -347,7 +347,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { // For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :( // NOTE: Could reuse it between levels or textures! - if (plan.replaced->Valid()) { + if (plan.replaceValid) { bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); } else { if (plan.scaleFactor > 1) { @@ -390,7 +390,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { entry->status &= ~TexCacheEntry::STATUS_NO_MIPS; } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 43b33cc1c1..0ef616914d 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -239,7 +239,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { } D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaced->Valid()) { + if (plan.replaceValid) { dstFmt = ToD3D9Format(plan.replaced->Format(plan.baseLevelSrc)); } else if (plan.scaleFactor > 1) { dstFmt = D3DFMT_A8R8G8B8; @@ -325,7 +325,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { entry->status |= TexCacheEntry::STATUS_3D; } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 537bfaddaa..cf9a8e13af 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -309,7 +309,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { int stride = 0; int bpp; - if (plan.replaced->Valid()) { + if (plan.replaceValid) { bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); } else { if (plan.scaleFactor > 1) { @@ -358,7 +358,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { render_->FinalizeTexture(entry->textureName, 1, false); } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 34547647fa..2f82eae187 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -464,7 +464,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { // Any texture scaling is gonna move away from the original 16-bit format, if any. VkFormat actualFmt = plan.scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt; - if (plan.replaced->Valid()) { + if (plan.replaceValid) { actualFmt = ToVulkanFormat(plan.replaced->Format(plan.baseLevelSrc)); } @@ -542,7 +542,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { ReplacedTextureDecodeInfo replacedInfo; bool willSaveTex = false; - if (replacer_.Enabled() && plan.replaced->IsInvalid() && plan.depth == 1) { + if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { + // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? replacedInfo.cachekey = entry->CacheKey(); replacedInfo.hash = entry->fullhash; replacedInfo.addr = entry->addr; @@ -596,7 +597,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { }; bool dataScaled = true; - if (plan.replaced->Valid()) { + if (plan.replaceValid) { // Directly load the replaced image. data = drawEngine_->GetPushBufferForTextureData()->PushAligned(uploadSize, &bufferOffset, &texBuf, pushAlignment); double replaceStart = time_now_d(); @@ -668,7 +669,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { entry->status |= TexCacheEntry::STATUS_3D; } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } From 340c75a2d9c62136fa2cbfea8bc491272f772183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 10:18:14 +0200 Subject: [PATCH 07/99] Remove "DisableReadbacks" compat flag. MotoGP no longer needs it after BlockTransferAllowCreateFB was added. --- Core/Compatibility.cpp | 1 - Core/Compatibility.h | 1 - GPU/Common/FramebufferManagerCommon.cpp | 2 +- assets/compat.ini | 7 ------- 4 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 84d42df284..6e2ee2db10 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -67,7 +67,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "RequireBufferedRendering", &flags_.RequireBufferedRendering); CheckSetting(iniFile, gameID, "RequireBlockTransfer", &flags_.RequireBlockTransfer); CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock); - CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks); CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth); CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack); CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack); diff --git a/Core/Compatibility.h b/Core/Compatibility.h index dc22c45e0f..134b545cac 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -57,7 +57,6 @@ struct CompatFlags { bool RequireBufferedRendering; bool RequireBlockTransfer; bool RequireDefaultCPUClock; - bool DisableReadbacks; bool DisableAccurateDepth; bool MGS2AcidHack; bool SonicRivalsHack; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 8df26d7d1c..36a7ff23c2 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1636,7 +1636,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, FlushBeforeCopy(); if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) { WARN_LOG_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight); - } else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated && !PSP_CoreParameter().compat.flags().DisableReadbacks) { + } else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated) { ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, RASTER_COLOR); srcBuffer->usageFlags = (srcBuffer->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR; } diff --git a/assets/compat.ini b/assets/compat.ini index 27b8fd571b..6ebe9f5704 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -287,13 +287,6 @@ UCKS45022 = true ULJS19009 = true NPJH50141 = true -[DisableReadbacks] -# MotoGP copies the framebuffer to RAM every frame. We have a hack to display it directly, -# which means we don't also need a readback. -ULJS00078 = true -ULUS10153 = true -UCES00373 = true - [DrawSyncEatCycles] # This replaced Crash Tag Team Racing hack to also fix Gundam games # It makes sceGeDrawSync eat a lot of cycles which can affect timing in lots of games, From 75390be14219839233e0d90deaa589d971f0ba0b Mon Sep 17 00:00:00 2001 From: sum2012 Date: Tue, 30 Aug 2022 20:46:43 +0800 Subject: [PATCH 08/99] Fix sceKernelUnlockMutex timing Fix https://github.com/hrydgard/ppsspp/issues/6557 --- Core/HLE/sceKernelMutex.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 899230142b..915baa9e81 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -657,8 +657,10 @@ int sceKernelUnlockMutex(SceUID id, int count) if (mutex->nm.lockLevel == 0) { - if (__KernelUnlockMutex(mutex, error)) + if (__KernelUnlockMutex(mutex, error)) { hleReSchedule("mutex unlocked"); + return hleDelayResult(0, "unlock", 150); + } } return 0; From daa62beb39592a64ea2513459b6f07c008e41a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 16:14:28 +0200 Subject: [PATCH 09/99] Small tweak in texture matching. Fixes #15927 --- GPU/Common/TextureCacheCommon.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 96ac9ca8b4..5aa097ecf2 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -667,6 +667,14 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector const AttachCandidate &candidate = candidates[i]; int relevancy = candidate.seqCount; + // Add a small negative penalty if the texture is currently bound as a framebuffer, and offset is not zero. + // Should avoid problems when pingponging two nearby buffers, like in Wipeout Pure in #15927 + if (candidate.channel == RASTER_COLOR && + (candidate.match.yOffset != 0 || candidate.match.xOffset != 0) && + (candidate.fb->fb_address & 0x1FFFFF) == (gstate.getFrameBufAddress() & 0x1FFFFF)) { + relevancy -= 2; + } + if (relevancy > bestRelevancy) { bestRelevancy = relevancy; bestIndex = i; From 987bfc79efb8cbe95451f01a9109bf345318bdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 19:36:08 +0200 Subject: [PATCH 10/99] Split the framebuffer in Killzone, with a compatibility flag. Greatly improves performance. See issue #6207 --- Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + GPU/Common/FramebufferManagerCommon.cpp | 17 ++++++++++-- GPU/Common/FramebufferManagerCommon.h | 2 +- GPU/Common/TextureCacheCommon.cpp | 37 ++++++++++++++++--------- GPU/Common/TextureCacheCommon.h | 4 +-- GPU/GPUState.h | 6 ++-- assets/compat.ini | 9 ++++++ 8 files changed, 56 insertions(+), 21 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 6e2ee2db10..0740e41711 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -96,6 +96,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "AllowLargeFBTextureOffsets", &flags_.AllowLargeFBTextureOffsets); CheckSetting(iniFile, gameID, "AtracLoopHack", &flags_.AtracLoopHack); CheckSetting(iniFile, gameID, "DeswizzleDepth", &flags_.DeswizzleDepth); + CheckSetting(iniFile, gameID, "SplitFramebufferMargin", &flags_.SplitFramebufferMargin); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 134b545cac..97f2d48075 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -86,6 +86,7 @@ struct CompatFlags { bool AllowLargeFBTextureOffsets; bool AtracLoopHack; bool DeswizzleDepth; + bool SplitFramebufferMargin; }; class IniFile; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 36a7ff23c2..6aff9382d4 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -341,7 +341,7 @@ void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPU } } -VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason) { +VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason) { gstate_c.Clean(DIRTY_FRAMEBUF); // Collect all parameters. This whole function has really become a cesspool of heuristics... @@ -360,6 +360,19 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame WARN_LOG_ONCE(color_equal_z, G3D, "Framebuffer bound with color addr == z addr, likely will not use Z in this pass: %08x", params.fb_address); } + if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin && params.fb_format == GE_FORMAT_8888) { + // Detect whether we're rendering to the margin. + if (params.scissorWidth == 32 || params.scissorWidth == 512) { + gstate_c.SetCurRTOffset(-480, 0); + // Modify the fb_address and z_address too to avoid matching below. + params.fb_address += 480 * 4; + params.z_address += 480 * 2; + drawing_width = 32; + } else if (params.scissorWidth == 480) { + drawing_width = 480; + } + } + // Find a matching framebuffer. VirtualFramebuffer *vfb = nullptr; for (auto v : vfbs_) { @@ -385,7 +398,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame vfb->height = drawing_height; } break; - } else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format) { + } else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format && !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { u32 v_fb_first_line_end_ptr = v->fb_address + v->fb_stride * bpp; u32 v_fb_end_ptr = v->fb_address + v->fb_stride * v->height * bpp; diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index f84f0c41a7..d7e5a866f0 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -279,7 +279,7 @@ public: void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format); void DestroyFramebuf(VirtualFramebuffer *v); - VirtualFramebuffer *DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason); + VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason); VirtualFramebuffer *SetRenderFrameBuffer(bool framebufChanged, int skipDrawReason) { // Inlining this part since it's so frequent. if (!framebufChanged && currentRenderVfb_) { diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 5aa097ecf2..3fe20676da 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -636,16 +636,18 @@ std::vector TextureCacheCommon::GetFramebufferCandidates(const } if (candidates.size() > 1) { - std::string cands; - for (auto &candidate : candidates) { - cands += candidate.ToString() + "\n"; - } + if (Reporting::ShouldLogNTimes("multifbcandidate", 5)) { + std::string cands; + for (auto &candidate : candidates) { + cands += candidate.ToString() + "\n"; + } - WARN_LOG_REPORT_ONCE(multifbcandidate, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", - (int)candidates.size(), - entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format), - cands.c_str() - ); + WARN_LOG_N_TIMES(multifbcandidate, 5, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", + (int)candidates.size(), + entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format), + cands.c_str() + ); + } } return candidates; @@ -675,6 +677,10 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector relevancy -= 2; } + if (candidate.fb == framebufferManager_->GetCurrentRenderVFB()) { + continue; + } + if (relevancy > bestRelevancy) { bestRelevancy = relevancy; bestIndex = i; @@ -928,12 +934,17 @@ bool TextureCacheCommon::MatchFramebuffer( (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT32) || (fb_format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16); - const u32 bitOffset = (texaddr - addr) * 8; + const int bitOffset = (texaddr - addr) * 8; if (bitOffset != 0) { - const u32 pixelOffset = bitOffset / std::max(1U, (u32)textureBitsPerPixel[entry.format]); + const int pixelOffset = bitOffset / (int)std::max(1U, (u32)textureBitsPerPixel[entry.format]); - matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / entry.bufw; - matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % entry.bufw; + if (pixelOffset > 0) { + matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / (int)entry.bufw; + matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % (int)entry.bufw; + } else if (pixelOffset < 0) { + matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / (int)entry.bufw; + matchInfo->xOffset = entry.bufw == 0 ? 0 : -(-pixelOffset % (int)entry.bufw); + } } if (matchInfo->yOffset + minSubareaHeight >= framebuffer->height) { diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 57b7ceb421..9259333b1e 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -209,8 +209,8 @@ typedef std::map> TexCache; #endif struct FramebufferMatchInfo { - u32 xOffset; - u32 yOffset; + int xOffset; + int yOffset; bool reinterpret; GEBufferFormat reinterpretTo; }; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index f75a09fdad..67facad239 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -621,15 +621,15 @@ struct GPUStateCache { u32 curRTRenderWidth; u32 curRTRenderHeight; - void SetCurRTOffset(u32 xoff, u32 yoff) { + void SetCurRTOffset(int xoff, int yoff) { if (xoff != curRTOffsetX || yoff != curRTOffsetY) { curRTOffsetX = xoff; curRTOffsetY = yoff; Dirty(DIRTY_VIEWPORTSCISSOR_STATE); } } - u32 curRTOffsetX; - u32 curRTOffsetY; + int curRTOffsetX; + int curRTOffsetY; // Set if we are doing hardware bezier/spline. SubmitType submitType; diff --git a/assets/compat.ini b/assets/compat.ini index 6ebe9f5704..f928684a28 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1256,3 +1256,12 @@ UCKS45048 = true UCJS18030 = true UCJS18047 = true NPJG00015 = true + +[SplitFramebufferMargin] +# Killzone: Liberation (see issue #6207) +UCES00279 = true +UCKS45041 = true +UCUS98646 = true +UCET00278 = true +UCUS98670 = true +UCUS98646 = true From 1b3b1acefacd0652fc52aa2c6dde619b855deb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Aug 2022 20:10:44 +0200 Subject: [PATCH 11/99] Tweak ForceMax60Fps to work for Killzone --- Core/HLE/sceDisplay.cpp | 5 ++++- GPU/Common/FramebufferManagerCommon.cpp | 3 ++- GPU/Common/TextureCacheCommon.cpp | 7 ++++++- assets/compat.ini | 8 ++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 7db5998ff7..d0577d46f2 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -823,7 +823,10 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) s64 delayCycles = 0; // Don't count transitions between display off and display on. - if (topaddr != 0 && topaddr != framebuf.topaddr && framebuf.topaddr != 0 && PSP_CoreParameter().compat.flags().ForceMax60FPS) { + if (topaddr != 0 && + (topaddr != framebuf.topaddr || PSP_CoreParameter().compat.flags().SplitFramebufferMargin) && + framebuf.topaddr != 0 && + PSP_CoreParameter().compat.flags().ForceMax60FPS) { // sceDisplaySetFramebuf() isn't supposed to delay threads at all. This is a hack. // So let's only delay when it's more than 1ms. const s64 FLIP_DELAY_CYCLES_MIN = usToCycles(1000); diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 6aff9382d4..d376f2c91e 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -679,7 +679,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra // This will result in reinterpret later, if both formats are 16-bit. sources.push_back(CopySource{ src, RASTER_COLOR, 0, 0 }); } else { - // Likely irrelevant or old, if the game is changing color depth for example. + // This shouldn't happen anymore. I think when it happened last, we still had + // lax stride checking when video was incoming, and a resize happened causing a duplicate. } } else if (src->fb_stride == dst->fb_stride && src->fb_format == dst->fb_format) { u32 bytesPerPixel = BufferFormatBytesPerPixel(src->fb_format); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 3fe20676da..8f4e08a12f 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -664,6 +664,8 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector int bestRelevancy = -1; int bestIndex = -1; + bool kzCompat = PSP_CoreParameter().compat.flags().SplitFramebufferMargin; + // We simply use the sequence counter as relevancy nowadays. for (int i = 0; i < (int)candidates.size(); i++) { const AttachCandidate &candidate = candidates[i]; @@ -677,7 +679,10 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector relevancy -= 2; } - if (candidate.fb == framebufferManager_->GetCurrentRenderVFB()) { + // Avoid binding as texture the framebuffer we're rendering to. + // In Killzone, we split the framebuffer but the matching algorithm can still pick the wrong one, + // which this avoids completely. + if (kzCompat && candidate.fb == framebufferManager_->GetCurrentRenderVFB()) { continue; } diff --git a/assets/compat.ini b/assets/compat.ini index f928684a28..4b2fb0545d 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1004,6 +1004,14 @@ ULUS10317 = true ULUS10598 = true ULES01578 = true +# Killzone: Liberation (see issue #6207) +UCES00279 = true +UCKS45041 = true +UCUS98646 = true +UCET00278 = true +UCUS98670 = true +UCUS98646 = true + [JitInvalidationHack] # This is an absolutely awful hack that somehow prevents issues when clearing the JIT, # if the game has copied code with EmuHack opcodes or something. Hopefully will be able From 70f7f74a05eeac82fa013f1ea215a22029449254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 01:09:23 +0200 Subject: [PATCH 12/99] Check X coordinate of through-mode drawcalls to figure out which of the splits to render to --- GPU/Common/FramebufferManagerCommon.cpp | 36 ++++++++++++++++++++++--- GPU/Common/TextureCacheCommon.cpp | 7 ++++- GPU/GPUCommon.cpp | 5 ++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index d376f2c91e..d4c5a9828d 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -352,7 +352,6 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer int drawing_width, drawing_height; EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height); - gstate_c.SetCurRTOffset(0, 0); if (params.fb_address == params.z_address) { // Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure) @@ -362,15 +361,46 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin && params.fb_format == GE_FORMAT_8888) { // Detect whether we're rendering to the margin. - if (params.scissorWidth == 32 || params.scissorWidth == 512) { + bool margin; + if (params.scissorWidth == 32) { + // title screen + margin = true; + } else if (params.scissorWidth == 480) { + margin = false; + } else { + // Go deep, look at the vertices. + margin = false; + if ((gstate.vertType & 0xFFFFFF) == 0x00800102) { // through, u16, s16 + u16 *vdata = (u16 *)Memory::GetPointerUnchecked(gstate_c.vertexAddr); + int v0PosU = vdata[0]; + int v0PosV = vdata[1]; + int v0PosX = vdata[2]; + int v0PosY = vdata[3]; + int v1PosU = vdata[5]; + int v1PosV = vdata[6]; + int v1PosX = vdata[7]; + int v1PosY = vdata[8]; + //INFO_LOG(G3D, "v0: %d, %d (uv = %d, %d)", v0PosX, v0PosY, v0PosU, v0PosV); + //INFO_LOG(G3D, "v1: %d, %d (uv = %d, %d)", v1PosX, v1PosY, v1PosU, v1PosV); + // Texturing from surface to margin + if (v0PosX >= 480 && v0PosU < 480) { + margin = true; + } + } + } + + if (margin) { gstate_c.SetCurRTOffset(-480, 0); // Modify the fb_address and z_address too to avoid matching below. params.fb_address += 480 * 4; params.z_address += 480 * 2; drawing_width = 32; - } else if (params.scissorWidth == 480) { + } else { + gstate_c.SetCurRTOffset(0, 0); drawing_width = 480; } + } else { + gstate_c.SetCurRTOffset(0, 0); } // Find a matching framebuffer. diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 8f4e08a12f..c707bb549f 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -952,7 +952,12 @@ bool TextureCacheCommon::MatchFramebuffer( } } - if (matchInfo->yOffset + minSubareaHeight >= framebuffer->height) { + if (matchInfo->yOffset > 0 && matchInfo->yOffset + minSubareaHeight >= framebuffer->height) { + // Can't be inside the framebuffer. + return false; + } + + if (matchInfo->yOffset < 0 && matchInfo->yOffset - minSubareaHeight <= 0) { // Can't be inside the framebuffer. return false; } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index f3dac1eabd..476368e1f7 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1686,6 +1686,11 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { } } + if ((gstate.vertType & 0xFFFFFF) == 0x00800102 && PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { + // Need to re-check the framebuffer every draw. + gstate_c.Dirty(DIRTY_FRAMEBUF); + } + // This also makes skipping drawing very effective. VirtualFramebuffer *vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); if (blueToAlpha) { From 7186fc2c17f46022d55f7c04761efa31b6685c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 01:40:53 +0200 Subject: [PATCH 13/99] Offset the draws properly. Now just some texel clamping left to fix --- GPU/Common/GPUStateUtils.cpp | 8 ++++++-- GPU/Common/ShaderUniforms.cpp | 6 ++++++ GPU/GPUState.h | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 1272a0d7c0..534f117bad 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -581,8 +581,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo renderHeightFactor = renderHeight / 272.0f; } - renderX = gstate_c.curRTOffsetX; - renderY = gstate_c.curRTOffsetY; + // negative offsets we take care of in the projection matrix. + renderX = std::max(gstate_c.curRTOffsetX, 0); + renderY = std::max(gstate_c.curRTOffsetY, 0); // Scissor int scissorX1 = gstate.getScissorX1(); @@ -609,6 +610,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo float offsetY = gstate.getOffsetY(); if (out.throughMode) { + // If renderX/renderY are offset to compensate for a split framebuffer, + // applying the offset to the viewport isn't enough, since the viewport clips. + // We need to apply either directly to the vertices, or to the "through" projection matrix. out.viewportX = renderX * renderWidthFactor + displayOffsetX; out.viewportY = renderY * renderHeightFactor + displayOffsetY; out.viewportW = curRTWidth * renderWidthFactor; diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 0a8888321b..3e5424713d 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -152,6 +152,12 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView if (!useBufferedRendering && g_display_rotation != DisplayRotation::ROTATE_0) { proj_through = proj_through * g_display_rot_matrix; } + if (gstate_c.curRTOffsetX < 0 || gstate_c.curRTOffsetY < 0) { + Matrix4x4 xlate; + xlate.setTranslation(Lin::Vec3(2.0f * gstate_c.curRTOffsetX / (int)gstate_c.curRTWidth, 2.0f * gstate_c.curRTOffsetY / (int)gstate_c.curRTHeight, 0.0f)); + proj_through = proj_through * xlate; + } + CopyMatrix4x4(ub->proj_through, proj_through.getReadPtr()); ub->rotation = useBufferedRendering ? 0 : (float)g_display_rotation; } diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 67facad239..57c678734d 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -625,7 +625,7 @@ struct GPUStateCache { if (xoff != curRTOffsetX || yoff != curRTOffsetY) { curRTOffsetX = xoff; curRTOffsetY = yoff; - Dirty(DIRTY_VIEWPORTSCISSOR_STATE); + Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_PROJTHROUGHMATRIX); } } int curRTOffsetX; From a71a10bb6eee0267fc46dbbd0994121fd07f85f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 01:46:58 +0200 Subject: [PATCH 14/99] Allow signed texture offsets on framebuffers --- GPU/GPUState.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 57c678734d..9f7f98df48 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -592,8 +592,8 @@ struct GPUStateCache { u32 curTextureHeight; u32 actualTextureHeight; // Only applied when needShaderTexClamp = true. - u32 curTextureXOffset; - u32 curTextureYOffset; + int curTextureXOffset; + int curTextureYOffset; bool curTextureIs3D; float vpWidth; From 95b299766eeb4b7de84d2ed0587fef729b5ce5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 01:53:05 +0200 Subject: [PATCH 15/99] Cleanup (more to do, though) --- GPU/Common/FramebufferManagerCommon.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index d4c5a9828d..548501f667 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -352,38 +352,30 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer int drawing_width, drawing_height; EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height); - if (params.fb_address == params.z_address) { // Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure) // We probably don't need to do anything about that, but let's log it. WARN_LOG_ONCE(color_equal_z, G3D, "Framebuffer bound with color addr == z addr, likely will not use Z in this pass: %08x", params.fb_address); } + // Compatibility hack for Killzone, see issue #6207. if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin && params.fb_format == GE_FORMAT_8888) { // Detect whether we're rendering to the margin. bool margin; if (params.scissorWidth == 32) { - // title screen + // Title screen has this easy case. margin = true; } else if (params.scissorWidth == 480) { margin = false; } else { - // Go deep, look at the vertices. + // Go deep, look at the vertices. Killzone-specific, of course. margin = false; if ((gstate.vertType & 0xFFFFFF) == 0x00800102) { // through, u16, s16 u16 *vdata = (u16 *)Memory::GetPointerUnchecked(gstate_c.vertexAddr); int v0PosU = vdata[0]; - int v0PosV = vdata[1]; int v0PosX = vdata[2]; - int v0PosY = vdata[3]; - int v1PosU = vdata[5]; - int v1PosV = vdata[6]; - int v1PosX = vdata[7]; - int v1PosY = vdata[8]; - //INFO_LOG(G3D, "v0: %d, %d (uv = %d, %d)", v0PosX, v0PosY, v0PosU, v0PosV); - //INFO_LOG(G3D, "v1: %d, %d (uv = %d, %d)", v1PosX, v1PosY, v1PosU, v1PosV); - // Texturing from surface to margin if (v0PosX >= 480 && v0PosU < 480) { + // Texturing from surface, writing to margin margin = true; } } From d99e4b67145bc63d080474895db27998a17e45eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 08:44:15 +0200 Subject: [PATCH 16/99] Quicker way to update the through projection matrix with the split framebuffer offset --- GPU/Common/GPUStateUtils.cpp | 4 +++- GPU/Common/ShaderUniforms.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 534f117bad..a9f99f793f 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -581,7 +581,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo renderHeightFactor = renderHeight / 272.0f; } - // negative offsets we take care of in the projection matrix. + // We take care negative offsets of in the projection matrix. + // These come from split framebuffers (Killzone). + // TODO: Might be safe to do get rid of this here and do the same for positive offsets? renderX = std::max(gstate_c.curRTOffsetX, 0); renderY = std::max(gstate_c.curRTOffsetY, 0); diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 3e5424713d..c3480e6fef 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -152,10 +152,11 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView if (!useBufferedRendering && g_display_rotation != DisplayRotation::ROTATE_0) { proj_through = proj_through * g_display_rot_matrix; } + + // Negative RT offsets come from split framebuffers (Killzone) if (gstate_c.curRTOffsetX < 0 || gstate_c.curRTOffsetY < 0) { - Matrix4x4 xlate; - xlate.setTranslation(Lin::Vec3(2.0f * gstate_c.curRTOffsetX / (int)gstate_c.curRTWidth, 2.0f * gstate_c.curRTOffsetY / (int)gstate_c.curRTHeight, 0.0f)); - proj_through = proj_through * xlate; + proj_through.wx += 2.0f * (float)gstate_c.curRTOffsetX / (float)gstate_c.curRTWidth; + proj_through.wy += 2.0f * (float)gstate_c.curRTOffsetY / (float)gstate_c.curRTHeight; } CopyMatrix4x4(ub->proj_through, proj_through.getReadPtr()); From 0928d9d28541cc34a98347f1d5665762b44afc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 09:12:52 +0200 Subject: [PATCH 17/99] Extract the split code to a function --- GPU/Common/FramebufferManagerCommon.cpp | 68 ++++++++++++++----------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 548501f667..94ebdaf410 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -341,6 +341,8 @@ void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPU } } +static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width); + VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason) { gstate_c.Clean(DIRTY_FRAMEBUF); @@ -360,37 +362,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer // Compatibility hack for Killzone, see issue #6207. if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin && params.fb_format == GE_FORMAT_8888) { - // Detect whether we're rendering to the margin. - bool margin; - if (params.scissorWidth == 32) { - // Title screen has this easy case. - margin = true; - } else if (params.scissorWidth == 480) { - margin = false; - } else { - // Go deep, look at the vertices. Killzone-specific, of course. - margin = false; - if ((gstate.vertType & 0xFFFFFF) == 0x00800102) { // through, u16, s16 - u16 *vdata = (u16 *)Memory::GetPointerUnchecked(gstate_c.vertexAddr); - int v0PosU = vdata[0]; - int v0PosX = vdata[2]; - if (v0PosX >= 480 && v0PosU < 480) { - // Texturing from surface, writing to margin - margin = true; - } - } - } - - if (margin) { - gstate_c.SetCurRTOffset(-480, 0); - // Modify the fb_address and z_address too to avoid matching below. - params.fb_address += 480 * 4; - params.z_address += 480 * 2; - drawing_width = 32; - } else { - gstate_c.SetCurRTOffset(0, 0); - drawing_width = 480; - } + ApplyKillzoneFramebufferSplit(¶ms, &drawing_width); } else { gstate_c.SetCurRTOffset(0, 0); } @@ -2900,3 +2872,37 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi vfb->colorBindSeq = GetBindSeqCount(); return vfb; } + +static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width) { + // Detect whether we're rendering to the margin. + bool margin; + if (params->scissorWidth == 32) { + // Title screen has this easy case. + margin = true; + } else if (params->scissorWidth == 480) { + margin = false; + } else { + // Go deep, look at the vertices. Killzone-specific, of course. + margin = false; + if ((gstate.vertType & 0xFFFFFF) == 0x00800102) { // through, u16, s16 + u16 *vdata = (u16 *)Memory::GetPointerUnchecked(gstate_c.vertexAddr); + int v0PosU = vdata[0]; + int v0PosX = vdata[2]; + if (v0PosX >= 480 && v0PosU < 480) { + // Texturing from surface, writing to margin + margin = true; + } + } + } + + if (margin) { + gstate_c.SetCurRTOffset(-480, 0); + // Modify the fb_address and z_address too to avoid matching below. + params->fb_address += 480 * 4; + params->z_address += 480 * 2; + *drawing_width = 32; + } else { + gstate_c.SetCurRTOffset(0, 0); + *drawing_width = 480; + } +} From 437d6d30a0dd3cc1d31fc4763e76a8edff4562b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 11:40:10 +0200 Subject: [PATCH 18/99] KillZone: Fix margin detection on the title screen, fixing some glitches. Need more fixes though to fix the shimmer effect. See #6207 comments --- GPU/Common/FramebufferManagerCommon.cpp | 15 +++++++++------ GPU/Common/FramebufferManagerCommon.h | 10 ++++++---- GPU/GPUCommon.cpp | 11 ++++++++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 94ebdaf410..3079a6b5a2 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -333,8 +333,11 @@ void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPU params->viewportHeight = (int)(fabsf(vpy) * 2.0f); params->regionWidth = gstate.getRegionX2() + 1; params->regionHeight = gstate.getRegionY2() + 1; - params->scissorWidth = gstate.getScissorX2() + 1; - params->scissorHeight = gstate.getScissorY2() + 1; + + params->scissorLeft = gstate.getScissorX1(); + params->scissorTop = gstate.getScissorY1(); + params->scissorRight = gstate.getScissorX2() + 1; + params->scissorBottom = gstate.getScissorY2() + 1; if (gstate.getRegionRateX() != 0x100 || gstate.getRegionRateY() != 0x100) { WARN_LOG_REPORT_ONCE(regionRate, G3D, "Drawing region rate add non-zero: %04x, %04x of %04x, %04x", gstate.getRegionRateX(), gstate.getRegionRateY(), gstate.getRegionX2(), gstate.getRegionY2()); @@ -352,7 +355,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer // As there are no clear "framebuffer width" and "framebuffer height" registers, // we need to infer the size of the current framebuffer somehow. int drawing_width, drawing_height; - EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height); + EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorRight, params.scissorBottom, drawing_width, drawing_height); if (params.fb_address == params.z_address) { // Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure) @@ -2876,10 +2879,10 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width) { // Detect whether we're rendering to the margin. bool margin; - if (params->scissorWidth == 32) { - // Title screen has this easy case. + if ((params->scissorRight - params->scissorLeft) == 32) { + // Title screen has this easy case. It also uses non-through verts, so lucky for us that we have this. margin = true; - } else if (params->scissorWidth == 480) { + } else if (params->scissorRight == 480) { margin = false; } else { // Go deep, look at the vertices. Killzone-specific, of course. diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index d7e5a866f0..dad0a887f1 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -165,10 +165,12 @@ struct FramebufferHeuristicParams { bool isBlending; int viewportWidth; int viewportHeight; - int regionWidth; - int regionHeight; - int scissorWidth; - int scissorHeight; + int16_t regionWidth; + int16_t regionHeight; + int16_t scissorLeft; + int16_t scissorTop; + int16_t scissorRight; + int16_t scissorBottom; }; struct GPUgstate; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 476368e1f7..22b323c713 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1686,9 +1686,14 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { } } - if ((gstate.vertType & 0xFFFFFF) == 0x00800102 && PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { - // Need to re-check the framebuffer every draw. - gstate_c.Dirty(DIRTY_FRAMEBUF); + if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { + switch (gstate.vertType & 0xFFFFFF) { + case 0x00800102: // through, u16 uv, u16 pos (used for the framebuffer effect in-game) + case 0x0080011c: // through, 8888 color, s16 pos (used for clearing in the margin of the title screen) + case 0x00000183: // float uv, float pos (used for drawing in the margin of the title screen) + // Need to re-check the framebuffer every one of these draws, to update the split if needed. + gstate_c.Dirty(DIRTY_FRAMEBUF); + } } // This also makes skipping drawing very effective. From 67d6549afdc77aff8a2190583bc8b20a2300aef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 13:48:53 +0200 Subject: [PATCH 19/99] Rename Slow Framebuf effects to Shader Blending (might remove). Reassign readbacks to BlockTransferGPU. --- Core/Config.cpp | 2 +- Core/Config.h | 2 +- GPU/Common/FramebufferManagerCommon.cpp | 4 ++-- GPU/Common/TextureCacheCommon.cpp | 4 ++-- GPU/D3D11/StateMappingD3D11.cpp | 2 +- GPU/Directx9/StateMappingDX9.cpp | 2 +- GPU/GLES/StateMappingGLES.cpp | 2 +- GPU/Vulkan/StateMappingVulkan.cpp | 2 +- UI/GameSettingsScreen.cpp | 2 +- assets/compat.ini | 2 +- libretro/libretro.cpp | 6 +++--- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 29d4dfab04..512e463a9b 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -934,7 +934,7 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("ShaderChainRequires60FPS", &g_Config.bShaderChainRequires60FPS, false, true, true), ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true), - ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false, true, true), + ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableShaderBlending, false, true, true), ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true), ConfigSetting("GfxDebugOutput", &g_Config.bGfxDebugOutput, false, false, false), diff --git a/Core/Config.h b/Core/Config.h index 4edd2147e0..ea1d7e0783 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -232,7 +232,7 @@ public: float fGameListScrollPosition; int iBloomHack; //0 = off, 1 = safe, 2 = balanced, 3 = aggressive bool bBlockTransferGPU; - bool bDisableSlowFramebufEffects; + bool bDisableShaderBlending; bool bFragmentTestCache; int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High bool bHardwareTessellation; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 3079a6b5a2..5f111793ca 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -494,7 +494,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer currentRenderVfb_ = vfb; // Assume that if we're clearing right when switching to a new framebuffer, we don't need to upload. - if (useBufferedRendering_ && !g_Config.bDisableSlowFramebufEffects && params.isDrawing) { + if (useBufferedRendering_ && params.isDrawing) { gpu->PerformMemoryUpload(params.fb_address, byteSize); // Alpha was already done by PerformMemoryUpload. PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA); @@ -1233,7 +1233,7 @@ void FramebufferManagerCommon::DownloadFramebufferOnSwitch(VirtualFramebuffer *v // Some games will draw to some memory once, and use it as a render-to-texture later. // To support this, we save the first frame to memory when we have a safe w/h. // Saving each frame would be slow. - if (!g_Config.bDisableSlowFramebufEffects && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) { + if (g_Config.bBlockTransferGPU && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) { ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR); vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR; vfb->safeWidth = 0; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index c707bb549f..d6c1ce7fe2 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1193,7 +1193,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { // It's possible for a game to (successfully) access outside valid memory. u32 bytes = Memory::ValidSize(clutAddr, loadBytes); - if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bDisableSlowFramebufEffects) { + if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bBlockTransferGPU) { framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes); Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes); if (bytes < loadBytes) { @@ -1932,7 +1932,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer bool smoothedDepal = false; u32 depthUpperBits = 0; - if (need_depalettize && !g_Config.bDisableSlowFramebufEffects) { + if (need_depalettize) { clutTexture = textureShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBufRaw_); smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->fb_format, clutTexture.rampLength); diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 561ea414b3..239d8def45 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -142,7 +142,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); // Blend if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { keys_.blend.value = 0; // full wipe keys_.blend.blendEnable = false; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 3ebeb71fca..bcc2d23b72 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -119,7 +119,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { dxstate.blend.disable(); // Color Mask diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 5f9fe9d135..53fd0711b4 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -145,7 +145,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { // Color Test diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index ac51debc6a..96c145e6f7 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -136,7 +136,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { key.logicOpEnable = false; key.logicOp = VK_LOGIC_OP_CLEAR; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5cd442f7b9..def6924c12 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -507,7 +507,7 @@ void GameSettingsScreen::CreateViews() { }); texSecondary_->SetDisabledPtr(&g_Config.bSoftwareRendering); - CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableSlowFramebufEffects, gr->T("Disable slower effects (speedup)"))); + CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableShaderBlending, gr->T("Disable slower effects (speedup)"))); framebufferSlowEffects->SetDisabledPtr(&g_Config.bSoftwareRendering); // Seems solid, so we hide the setting. diff --git a/assets/compat.ini b/assets/compat.ini index 4b2fb0545d..bbe058a707 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -528,7 +528,7 @@ ULJM08033 = true NPJH50373 = true NPUH10191 = true NPUH10197 = true -# Grand Knights History need it to fix blackboxes on characters and flickering texture . See issues #2135 , #6099 +# Grand Knights History need it to fix blackboxes on characters and flickering texture . See issues #2135, #6099 ULJS00394 = true ULJS19068 = true NPJH50518 = true diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index eacbca25e5..636da101d9 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -563,7 +563,7 @@ static RetroOption ppsspp_lazy_texture_caching("ppsspp_lazy_texture_cachin static RetroOption ppsspp_retain_changed_textures("ppsspp_retain_changed_textures", "Retain changed textures (Speedup, mem hog)", false); static RetroOption ppsspp_force_lag_sync("ppsspp_force_lag_sync", "Force real clock sync (Slower, less lag)", false); static RetroOption ppsspp_spline_quality("ppsspp_spline_quality", "Spline/Bezier curves quality", { {"Low", 0}, {"Medium", 1}, {"High", 2} }); -static RetroOption ppsspp_disable_slow_framebuffer_effects("ppsspp_disable_slow_framebuffer_effects", "Disable slower effects (Speedup)", false); +static RetroOption ppsspp_disable_shader_blending("ppsspp_disable_slow_framebuffer_effects", "Disable shader blending (speedup)", false); static RetroOption ppsspp_enable_wlan("ppsspp_enable_wlan", "Enable Networking/WLAN (beta, may break games)", false); static RetroOption ppsspp_change_mac_address[] = { {"ppsspp_change_mac_address01", "MAC address Pt 1: X-:--:--:--:--:--", MAC_INITIALIZER_LIST}, @@ -699,7 +699,7 @@ void retro_set_environment(retro_environment_t cb) vars.push_back(ppsspp_lazy_texture_caching.GetOptions()); vars.push_back(ppsspp_retain_changed_textures.GetOptions()); vars.push_back(ppsspp_force_lag_sync.GetOptions()); - vars.push_back(ppsspp_disable_slow_framebuffer_effects.GetOptions()); + vars.push_back(ppsspp_disable_shader_blending.GetOptions()); vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions()); vars.push_back(ppsspp_texture_scaling_level.GetOptions()); vars.push_back(ppsspp_texture_scaling_type.GetOptions()); @@ -831,7 +831,7 @@ static void check_variables(CoreParameter &coreParam) ppsspp_retain_changed_textures.Update(&g_Config.bTextureSecondaryCache); ppsspp_force_lag_sync.Update(&g_Config.bForceLagSync); ppsspp_spline_quality.Update(&g_Config.iSplineBezierQuality); - ppsspp_disable_slow_framebuffer_effects.Update(&g_Config.bDisableSlowFramebufEffects); + ppsspp_disable_shader_blending.Update(&g_Config.bDisableShaderBlending); ppsspp_inflight_frames.Update(&g_Config.iInflightFrames); const bool do_scaling_type_update = ppsspp_texture_scaling_type.Update(&g_Config.iTexScalingType); const bool do_scaling_level_update = ppsspp_texture_scaling_level.Update(&g_Config.iTexScalingLevel); From a8a9fb42063e0a057fa3eaf55aacbd09268c26f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Aug 2022 14:23:56 +0200 Subject: [PATCH 20/99] Much faster framebuffer matching (no more ToString every time there's more than 1 candidate, etc). --- Common/Data/Collections/TinySet.h | 42 ++++++++++++++++++++++++- GPU/Common/FramebufferManagerCommon.cpp | 24 +++++++++----- GPU/Common/FramebufferManagerCommon.h | 4 --- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/Common/Data/Collections/TinySet.h b/Common/Data/Collections/TinySet.h index 8a7cf89fa6..219a26d3cd 100644 --- a/Common/Data/Collections/TinySet.h +++ b/Common/Data/Collections/TinySet.h @@ -3,10 +3,12 @@ #include // Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded. +// Can also be used as a small vector, then use push_back (or push_in_place) instead of insert. +// Duplicates are thus allowed if you use that, but not if you exclusively use insert. template struct TinySet { ~TinySet() { delete slowLookup_; } - inline void insert(T t) { + inline void insert(const T &t) { // Fast linear scan. for (int i = 0; i < fastCount; i++) { if (fastLookup_[i] == t) @@ -20,6 +22,27 @@ struct TinySet { // Fall back to slow path. insertSlow(t); } + inline void push_back(const T &t) { + if (fastCount < MaxFastSize) { + fastLookup_[fastCount++] = t; + return; + } + if (!slowLookup_) { + slowLookup_ = new std::vector(); + } + slowLookup_->push_back(t); + } + inline T *add_back() { + if (fastCount < MaxFastSize) { + return &fastLookup_[fastCount++]; + } + if (!slowLookup_) { + slowLookup_ = new std::vector(); + } + T t; + slowLookup_->push_back(t); + return slowLookup_->back(); + } bool contains(T t) const { for (int i = 0; i < fastCount; i++) { if (fastLookup_[i] == t) @@ -52,6 +75,23 @@ struct TinySet { slowLookup_ = nullptr; fastCount = 0; } + bool empty() const { + return fastCount == 0; + } + size_t size() const { + if (fastCount <= MaxFastSize) { + return fastCount; + } else { + return slowLookup_->size() + MaxFastSize; + } + } + const T &operator[] (size_t index) const { + if (index < MaxFastSize) { + return fastLookup_[index]; + } else { + return (*slowLookup_)[index - MaxFastSize]; + } + } private: void insertSlow(T t) { diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 5f111793ca..95535d5317 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -21,6 +21,7 @@ #include "Common/GPU/thin3d.h" #include "Common/GPU/OpenGL/GLFeatures.h" +#include "Common/Data/Collections/TinySet.h" #include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Text/I18n.h" #include "Common/Math/lin/matrix4x4.h" @@ -1677,7 +1678,7 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p int x_bytes = x_pixels * bpp; int w_bytes = w_pixels * bpp; - std::vector candidates; + TinySet candidates; // We work entirely in bytes when we do the matching, because games don't consistently use bpps that match // that of their buffers. Then after matching we try to map the copy to the simplest operation that does @@ -1752,20 +1753,27 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p candidates.push_back(candidate); } + const BlockTransferRect *best = nullptr; // Sort candidates by just recency for now, we might add other. - std::sort(candidates.begin(), candidates.end()); + for (size_t i = 0; i < candidates.size(); i++) { + const BlockTransferRect *candidate = &candidates[i]; + if (!best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq) { + best = candidate; + } + } if (candidates.size() > 1) { - std::string log; - for (auto &candidate : candidates) { - log += " - " + candidate.ToString() + "\n"; + if (Reporting::ShouldLogNTimes("mulblock", 5)) { + std::string log; + for (size_t i = 0; i < candidates.size(); i++) { + log += " - " + candidates[i].ToString() + "\n"; + } + WARN_LOG(G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str()); } - WARN_LOG_N_TIMES(mulblock, 5, G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str()); } if (!candidates.empty()) { - // Pick the last candidate. - *rect = candidates.back(); + *rect = *best; return true; } else { if (Memory::IsVRAMAddress(basePtr) && destination && h >= 128) { diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index dad0a887f1..f78e52d13e 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -245,10 +245,6 @@ struct BlockTransferRect { int x_pixels() const { return x_bytes / BufferFormatBytesPerPixel(vfb->fb_format); } - - bool operator < (const BlockTransferRect &other) const { - return vfb->colorBindSeq < other.vfb->colorBindSeq; - } }; namespace Draw { From 470efac7d8334a189b858fa161312db67176d4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 00:35:43 +0200 Subject: [PATCH 21/99] Merge GetFramebufferCandidates and GetBestCandidateIndex into one function. --- GPU/Common/TextureCacheCommon.cpp | 82 ++++++++++++++----------------- GPU/Common/TextureCacheCommon.h | 3 +- 2 files changed, 39 insertions(+), 46 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index d6c1ce7fe2..6dbafba440 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -532,21 +532,17 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { def.format = texFormat; def.bufw = bufw; - std::vector candidates = GetFramebufferCandidates(def, 0); - if (candidates.size() > 0) { - int index = GetBestCandidateIndex(candidates); - if (index != -1) { - // If we had a texture entry here, let's get rid of it. - if (entryIter != cache_.end()) { - DeleteTexture(entryIter); - } - - const AttachCandidate &candidate = candidates[index]; - nextTexture_ = nullptr; - nextNeedsRebuild_ = false; - SetTextureFramebuffer(candidate); // sets curTexture3D - return nullptr; + AttachCandidate bestCandidate; + if (GetBestFramebufferCandidate(def, 0, &bestCandidate)) { + // If we had a texture entry here, let's get rid of it. + if (entryIter != cache_.end()) { + DeleteTexture(entryIter); } + + nextTexture_ = nullptr; + nextNeedsRebuild_ = false; + SetTextureFramebuffer(bestCandidate); // sets curTexture3D + return nullptr; } // Didn't match a framebuffer, keep going. @@ -617,7 +613,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { return entry; } -std::vector TextureCacheCommon::GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset) { +bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const { gpuStats.numFramebufferEvaluations++; std::vector candidates; @@ -635,29 +631,24 @@ std::vector TextureCacheCommon::GetFramebufferCandidates(const } } - if (candidates.size() > 1) { - if (Reporting::ShouldLogNTimes("multifbcandidate", 5)) { - std::string cands; - for (auto &candidate : candidates) { - cands += candidate.ToString() + "\n"; - } - - WARN_LOG_N_TIMES(multifbcandidate, 5, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", - (int)candidates.size(), - entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format), - cands.c_str() - ); - } + if (candidates.size() == 0) { + return false; + } else if (candidates.size() == 1) { + *bestCandidate = candidates[0]; + return true; } - return candidates; -} + if (Reporting::ShouldLogNTimes("multifbcandidate", 5)) { + std::string cands; + for (auto &candidate : candidates) { + cands += candidate.ToString() + "\n"; + } -int TextureCacheCommon::GetBestCandidateIndex(const std::vector &candidates) { - _dbg_assert_(!candidates.empty()); - - if (candidates.size() == 1) { - return 0; + WARN_LOG_N_TIMES(multifbcandidate, 5, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", + (int)candidates.size(), + entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format), + cands.c_str() + ); } // OK, multiple possible candidates. Will need to figure out which one is the most relevant. @@ -692,7 +683,12 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector } } - return bestIndex; + if (bestIndex != -1) { + *bestCandidate = candidates[bestIndex]; + return true; + } else { + return false; + } } // Removes old textures. @@ -1111,15 +1107,13 @@ bool TextureCacheCommon::SetOffsetTexture(u32 yOffset) { def.bufw = GetTextureBufw(0, texaddr, fmt); def.dim = gstate.getTextureDimension(0); - std::vector candidates = GetFramebufferCandidates(def, texaddrOffset); - if (candidates.size() > 0) { - int index = GetBestCandidateIndex(candidates); - if (index != -1) { - SetTextureFramebuffer(candidates[index]); - return true; - } + AttachCandidate bestCandidate; + if (GetBestFramebufferCandidate(def, texaddrOffset, &bestCandidate)) { + SetTextureFramebuffer(bestCandidate); + return true; + } else { + return false; } - return false; } void TextureCacheCommon::NotifyConfigChanged() { diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 9259333b1e..547f04bb64 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -381,8 +381,7 @@ protected: bool MatchFramebuffer(const TextureDefinition &entry, VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel, FramebufferMatchInfo *matchInfo) const; - std::vector GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset); - int GetBestCandidateIndex(const std::vector &candidates); + bool GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const; void SetTextureFramebuffer(const AttachCandidate &candidate); From 1c9ec36fd86fdedbeb68abdd42a28981cf847161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 08:26:23 +0200 Subject: [PATCH 22/99] Stricter checks for negative framebuffer offsets. Fixes #15937 --- GPU/Common/TextureCacheCommon.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index c707bb549f..ad7145e1d3 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -947,7 +947,10 @@ bool TextureCacheCommon::MatchFramebuffer( matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / (int)entry.bufw; matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % (int)entry.bufw; } else if (pixelOffset < 0) { - matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / (int)entry.bufw; + // We don't support negative Y offsets, and negative X offsets are only for the Killzone workaround. + if (pixelOffset < -(int)entry.bufw || !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { + return false; + } matchInfo->xOffset = entry.bufw == 0 ? 0 : -(-pixelOffset % (int)entry.bufw); } } @@ -957,11 +960,6 @@ bool TextureCacheCommon::MatchFramebuffer( return false; } - if (matchInfo->yOffset < 0 && matchInfo->yOffset - minSubareaHeight <= 0) { - // Can't be inside the framebuffer. - return false; - } - if (fb_stride != entry.bufw) { if (noOffset) { WARN_LOG_ONCE(diffStrides2, G3D, "Matching framebuffer(matching_clut = %s) different strides %d != %d", matchingClutFormat ? "yes" : "no", entry.bufw, fb_stride); From a854fbbe76c2994c7aef99ed2ab7ec162785c81e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 10:24:52 +0200 Subject: [PATCH 23/99] fbtex matching: Shrink the matching struct, use TinySet instead of vector to avoid an allocation. --- GPU/Common/FramebufferManagerCommon.cpp | 2 +- GPU/Common/TextureCacheCommon.cpp | 27 +++++++++++++------------ GPU/Common/TextureCacheCommon.h | 13 +++++------- GPU/ge_constants.h | 2 +- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 95535d5317..00f5df9b07 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -905,7 +905,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer } void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb) { - // ugly... if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) { gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); } @@ -922,6 +921,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe } else { DownloadFramebufferOnSwitch(prevVfb); } + textureCache_->ForgetLastTexture(); shaderManager_->DirtyLastShader(); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 98f5d560cf..faa03ad261 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -21,6 +21,7 @@ #include "Common/Common.h" #include "Common/Data/Convert/ColorConv.h" +#include "Common/Data/Collections/TinySet.h" #include "Common/Profiler/Profiler.h" #include "Common/MemoryUtil.h" #include "Common/StringUtils.h" @@ -616,18 +617,18 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const { gpuStats.numFramebufferEvaluations++; - std::vector candidates; + TinySet candidates; const std::vector &framebuffers = framebufferManager_->Framebuffers(); for (VirtualFramebuffer *framebuffer : framebuffers) { FramebufferMatchInfo match{}; if (MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_COLOR, &match)) { - candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_COLOR, framebuffer->colorBindSeq }); + candidates.push_back(AttachCandidate{ framebuffer, match, RASTER_COLOR }); } match = {}; if (MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_DEPTH, &match)) { - candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_DEPTH, framebuffer->depthBindSeq }); + candidates.push_back(AttachCandidate{ framebuffer, match, RASTER_DEPTH }); } } @@ -640,11 +641,11 @@ bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &en if (Reporting::ShouldLogNTimes("multifbcandidate", 5)) { std::string cands; - for (auto &candidate : candidates) { - cands += candidate.ToString() + "\n"; - } + for (size_t i = 0; i < candidates.size(); i++) { + cands += candidates[i].ToString() + "\n"; + } - WARN_LOG_N_TIMES(multifbcandidate, 5, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", + WARN_LOG(G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", (int)candidates.size(), entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format), cands.c_str() @@ -653,17 +654,17 @@ bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &en // OK, multiple possible candidates. Will need to figure out which one is the most relevant. int bestRelevancy = -1; - int bestIndex = -1; + size_t bestIndex = -1; bool kzCompat = PSP_CoreParameter().compat.flags().SplitFramebufferMargin; // We simply use the sequence counter as relevancy nowadays. - for (int i = 0; i < (int)candidates.size(); i++) { + for (size_t i = 0; i < candidates.size(); i++) { const AttachCandidate &candidate = candidates[i]; - int relevancy = candidate.seqCount; + int relevancy = candidate.channel == RASTER_COLOR ? candidate.fb->colorBindSeq : candidate.fb->depthBindSeq; // Add a small negative penalty if the texture is currently bound as a framebuffer, and offset is not zero. - // Should avoid problems when pingponging two nearby buffers, like in Wipeout Pure in #15927 + // Should avoid problems when pingponging two nearby buffers, like in Wipeout Pure in #15927. if (candidate.channel == RASTER_COLOR && (candidate.match.yOffset != 0 || candidate.match.xOffset != 0) && (candidate.fb->fb_address & 0x1FFFFF) == (gstate.getFrameBufAddress() & 0x1FFFFF)) { @@ -865,7 +866,7 @@ bool TextureCacheCommon::MatchFramebuffer( uint32_t fb_stride = channel == RASTER_DEPTH ? framebuffer->z_stride : framebuffer->fb_stride; GEBufferFormat fb_format = channel == RASTER_DEPTH ? GE_FORMAT_DEPTH16 : framebuffer->fb_format; - if (channel == RASTER_DEPTH && framebuffer->z_address == framebuffer->fb_address) { + if (channel == RASTER_DEPTH && (framebuffer->z_address == framebuffer->fb_address || framebuffer->z_address == 0)) { // Try to avoid silly matches to somewhat malformed buffers. return false; } @@ -2248,7 +2249,7 @@ void TextureCacheCommon::ClearNextFrame() { std::string AttachCandidate::ToString() const { return StringFromFormat("[%s seq:%d C:%08x/%d(%s) Z:%08x/%d X:%d Y:%d reint: %s]", this->channel == RASTER_COLOR ? "COLOR" : "DEPTH", - this->seqCount, + this->channel == RASTER_COLOR ? this->fb->colorBindSeq : this->fb->depthBindSeq, this->fb->fb_address, this->fb->fb_stride, GeBufferFormatToString(this->fb->fb_format), this->fb->z_address, this->fb->z_stride, this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false"); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 547f04bb64..e394923090 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -93,12 +93,11 @@ class VulkanTexture; // Enough information about a texture to match it to framebuffers. struct TextureDefinition { u32 addr; + u16 bufw; + u16 dim; GETextureFormat format; - u32 dim; - u32 bufw; }; - // TODO: Shrink this struct. There is some fluff. // NOTE: These only handle textures loaded directly from PSP memory contents. @@ -209,18 +208,16 @@ typedef std::map> TexCache; #endif struct FramebufferMatchInfo { - int xOffset; - int yOffset; + int16_t xOffset; + int16_t yOffset; bool reinterpret; GEBufferFormat reinterpretTo; }; struct AttachCandidate { - FramebufferMatchInfo match; - TextureDefinition entry; VirtualFramebuffer *fb; + FramebufferMatchInfo match; RasterChannel channel; - int seqCount; std::string ToString() const; }; diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index 8cd455ba79..3559282c57 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -405,7 +405,7 @@ enum GELightComputation GE_LIGHTCOMP_ONLYPOWDIFFUSE = 2, }; -enum GETextureFormat +enum GETextureFormat : uint8_t { GE_TFMT_5650 = 0, GE_TFMT_5551 = 1, From bd71d8044baa1773eb39e3731a8ffd0a37b90ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 10:46:47 +0200 Subject: [PATCH 24/99] TinySet bugfix, add "append" --- Common/Data/Collections/TinySet.h | 19 ++++++++++++++- unittest/UnitTest.cpp | 40 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Common/Data/Collections/TinySet.h b/Common/Data/Collections/TinySet.h index 219a26d3cd..5c8c8e78e6 100644 --- a/Common/Data/Collections/TinySet.h +++ b/Common/Data/Collections/TinySet.h @@ -43,6 +43,20 @@ struct TinySet { slowLookup_->push_back(t); return slowLookup_->back(); } + void append(const TinySet &other) { + size_t otherSize = other.size(); + if (size() + otherSize <= MaxFastSize) { + // Fast case + for (int i = 0; i < otherSize; i++) { + fastLookup_[fastCount + i] = other.fastLookup_[i]; + } + fastCount += other.fastCount; + } else { + for (int i = 0; i < otherSize; i++) { + push_back(other[i]); + } + } + } bool contains(T t) const { for (int i = 0; i < fastCount; i++) { if (fastLookup_[i] == t) @@ -79,7 +93,7 @@ struct TinySet { return fastCount == 0; } size_t size() const { - if (fastCount <= MaxFastSize) { + if (!slowLookup_) { return fastCount; } else { return slowLookup_->size() + MaxFastSize; @@ -92,6 +106,9 @@ struct TinySet { return (*slowLookup_)[index - MaxFastSize]; } } + const T &back() const { + return (*this)[size() - 1]; + } private: void insertSlow(T t) { diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 78a08515d6..4f8239a11b 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -40,6 +40,7 @@ #include #endif +#include "Common/Data/Collections/TinySet.h" #include "Common/Data/Text/Parsers.h" #include "Common/Data/Text/WrapText.h" #include "Common/Data/Encoding/Utf8.h" @@ -308,6 +309,44 @@ bool TestParsers() { return true; } +bool TestTinySet() { + TinySet a; + EXPECT_EQ_INT((int)a.size(), 0); + a.push_back(1); + EXPECT_EQ_INT((int)a.size(), 1); + a.push_back(2); + EXPECT_EQ_INT((int)a.size(), 2); + TinySet b; + b.push_back(8); + b.push_back(9); + b.push_back(10); + EXPECT_EQ_INT((int)b.size(), 3); + + a.append(b); + EXPECT_EQ_INT((int)a.size(), 5); + EXPECT_EQ_INT((int)b.size(), 3); + + b.append(b); + EXPECT_EQ_INT((int)b.size(), 6); + + EXPECT_EQ_INT(a[0], 1); + EXPECT_EQ_INT(a[1], 2); + EXPECT_EQ_INT(a[2], 8); + EXPECT_EQ_INT(a[3], 9); + EXPECT_EQ_INT(a[4], 10); + a.append(a); + EXPECT_EQ_INT(a.size(), 10); + EXPECT_EQ_INT(a[9], 10); + + b.push_back(11); + EXPECT_EQ_INT((int)b.size(), 7); + b.push_back(12); + EXPECT_EQ_INT((int)b.size(), 8); + b.push_back(13); + EXPECT_EQ_INT(b.size(), 9); + return true; +} + bool TestVFPUSinCos() { float sine, cosine; InitVFPUSinCos(); @@ -792,6 +831,7 @@ TestItem availableTests[] = { TEST_ITEM(AndroidContentURI), TEST_ITEM(ThreadManager), TEST_ITEM(WrapText), + TEST_ITEM(TinySet), }; int main(int argc, const char *argv[]) { From 24d674d2bca7836b0bb8055fb04e1aca191758e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 10:52:35 +0200 Subject: [PATCH 25/99] Shrink the TransitionRequest struct, use TinySet --- Common/GPU/Vulkan/VulkanQueueRunner.cpp | 5 +++-- Common/GPU/Vulkan/VulkanQueueRunner.h | 4 ++-- Common/GPU/Vulkan/VulkanRenderManager.cpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 6f84cf4b49..2acf0ff1c0 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -685,7 +685,7 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { auto mergeRenderSteps = [](VKRStep *dst, VKRStep *src) { // OK. Now, if it's a render, slurp up all the commands and kill the step. // Also slurp up any pretransitions. - dst->preTransitions.insert(dst->preTransitions.end(), src->preTransitions.begin(), src->preTransitions.end()); + dst->preTransitions.append(src->preTransitions); dst->commands.insert(dst->commands.end(), src->commands.begin(), src->commands.end()); MergeRenderAreaRectInto(&dst->render.renderArea, src->render.renderArea); // So we don't consider it for other things, maybe doesn't matter. @@ -1060,7 +1060,8 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou } void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { - for (const auto &iter : step.preTransitions) { + for (size_t i = 0; i < step.preTransitions.size(); i++) { + const TransitionRequest &iter = step.preTransitions[i]; if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) { recordBarrier_.TransitionImageAuto( iter.fb->color.image, diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index f80a1ba340..ba79634702 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -134,8 +134,8 @@ enum class VKRRenderPassStoreAction : uint8_t { }; struct TransitionRequest { - VkImageAspectFlags aspect; // COLOR or DEPTH VKRFramebuffer *fb; + VkImageAspectFlags aspect; // COLOR or DEPTH VkImageLayout targetLayout; }; @@ -153,7 +153,7 @@ struct VKRStep { VKRStepType stepType; std::vector commands; - std::vector preTransitions; + TinySet preTransitions; TinySet dependencies; const char *tag; union { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 56bf3ee253..ba2c64f758 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -1189,7 +1189,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in // We're done. return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView; } else { - curRenderStep_->preTransitions.push_back({ aspectBit, fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); + curRenderStep_->preTransitions.push_back({ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView; } } From c9048c3748f8b0c7a3eb158e601a795b8846f863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 11:07:30 +0200 Subject: [PATCH 26/99] Shrink the DeferredDrawcall struct, because why not. Assorted cleanup --- Core/Dialog/PSPDialog.h | 2 +- Core/Dialog/SavedataParam.h | 3 --- GPU/Common/DrawEngineCommon.cpp | 5 ++--- GPU/Common/DrawEngineCommon.h | 2 +- GPU/Common/VertexDecoderCommon.cpp | 3 --- GPU/Common/VertexDecoderCommon.h | 20 ++++++++++---------- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/Core/Dialog/PSPDialog.h b/Core/Dialog/PSPDialog.h index 636fc20334..673bd54f9b 100644 --- a/Core/Dialog/PSPDialog.h +++ b/Core/Dialog/PSPDialog.h @@ -129,6 +129,6 @@ protected: private: DialogStatus status = SCE_UTILITY_STATUS_NONE; - UtilityDialogType dialogType_; + UtilityDialogType dialogType_ = UtilityDialogType::NONE; bool volatileLocked_ = false; }; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index e7bdf9ad46..8599a1428d 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -285,9 +285,6 @@ struct SaveFileInfo PPGeImage *texture = nullptr; - SaveFileInfo() { - } - void DoState(PointerWrap &p); }; diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index cbbf4621be..6bc9d7437f 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -701,12 +701,11 @@ void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimiti DeferredDrawCall &dc = drawCalls[numDrawCalls]; dc.verts = verts; dc.inds = inds; + dc.vertexCount = vertexCount; dc.indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT; dc.prim = prim; - dc.vertexCount = vertexCount; - dc.uvScale = gstate_c.uv; dc.cullMode = cullMode; - + dc.uvScale = gstate_c.uv; if (inds) { GetIndexBounds(inds, vertexCount, vertTypeID, &dc.indexLowerBound, &dc.indexUpperBound); } else { diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index fb1218224b..b55bb85cbd 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -165,10 +165,10 @@ protected: u32 vertexCount; u8 indexType; s8 prim; + u8 cullMode; u16 indexLowerBound; u16 indexUpperBound; UVScale uvScale; - int cullMode; }; enum { MAX_DEFERRED_DRAW_CALLS = 128 }; diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index fd4365d34e..c6a4631636 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -176,9 +176,6 @@ void PrintDecodedVertex(VertexReader &vtx) { printf("P: %f %f %f\n", pos[0], pos[1], pos[2]); } -VertexDecoder::VertexDecoder() : decoded_(nullptr), ptr_(nullptr), jitted_(0), jittedSize_(0) { -} - void VertexDecoder::Step_WeightsU8() const { u8 *wt = (u8 *)(decoded_ + decFmt.w0off); diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 33725facb3..b80c18c2dc 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -125,10 +125,13 @@ public: case DEC_FLOAT_3: { const float *f = (const float *)(data_ + decFmt_.posoff); - memcpy(pos, f, 12); - if (isThrough()) { + pos[0] = f[0]; + pos[1] = f[1]; + if (!isThrough()) { + pos[2] = f[2]; + } else { // Integer value passed in a float. Clamped to 0, 65535. - const float z = (int)pos[2] * (1.0f / 65535.0f); + const float z = (int)f[2] * (1.0f / 65535.0f); pos[2] = z > 1.0f ? 1.0f : (z < 0.0f ? 0.0f : z); } } @@ -443,8 +446,6 @@ struct VertexDecoderOptions { class VertexDecoder { public: - VertexDecoder(); - // A jit cache is not mandatory. void SetVertexType(u32 vtype, const VertexDecoderOptions &options, VertexDecoderJitCache *jitCache = nullptr); @@ -549,11 +550,10 @@ public: int ToString(char *output) const; // Mutable decoder state - mutable u8 *decoded_; - mutable const u8 *ptr_; - - JittedVertexDecoder jitted_; - int32_t jittedSize_; + mutable u8 *decoded_ = nullptr; + mutable const u8 *ptr_ = nullptr; + JittedVertexDecoder jitted_ = 0; + int32_t jittedSize_ = 0; // "Immutable" state, set at startup From 7bced814cec0a3585ef46c6487a2df3c31f17031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 11:54:48 +0200 Subject: [PATCH 27/99] Remove a per-audio-frame allocation in sceAudio --- Core/HLE/__sceAudio.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 9632d90e0b..3176d26130 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -64,8 +64,8 @@ int srcFrequency = 0; const int hwSampleRate = 44100; -int hwBlockSize = 64; -int hostAttemptBlockSize = 512; +const int hwBlockSize = 64; +const int hostAttemptBlockSize = 512; static int audioIntervalCycles; static int audioHostIntervalCycles; @@ -110,8 +110,6 @@ void __AudioInit() { chanQueueMaxSizeFactor = 2; chanQueueMinSizeFactor = 1; - hwBlockSize = 64; - hostAttemptBlockSize = 512; __AudioCPUMHzChange(); @@ -338,7 +336,8 @@ void __AudioUpdate(bool resetRecording) { // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio // if the buffer somehow gets full. bool firstChannel = true; - std::vector srcBuffer; + const int16_t srcBufferSize = hwBlockSize * 2; + int16_t srcBuffer[srcBufferSize]; for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) { if (!chans[i].reserved) @@ -351,7 +350,7 @@ void __AudioUpdate(bool resetRecording) { } bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency; - size_t sz = needsResample ? (hwBlockSize * 2 * srcFrequency) / mixFrequency : hwBlockSize * 2; + size_t sz = needsResample ? (srcBufferSize * srcFrequency) / mixFrequency : srcBufferSize; if (sz > chanSampleQueues[i].size()) { ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chanSampleQueues[i].size() / 2, (int)sz / 2); } @@ -372,13 +371,11 @@ void __AudioUpdate(bool resetRecording) { return buf1[sz1 - 1]; }; - srcBuffer.resize(hwBlockSize * 2); - // TODO: This is terrible, since it's doing it by small chunk and discarding frac. const uint32_t ratio = (uint32_t)(65536.0 * srcFrequency / (double)mixFrequency); uint32_t frac = 0; size_t readIndex = 0; - for (size_t outIndex = 0; readIndex < sz && outIndex < srcBuffer.size(); outIndex += 2) { + for (size_t outIndex = 0; readIndex < sz && outIndex < srcBufferSize; outIndex += 2) { size_t readIndex2 = readIndex + 2; int16_t l1 = read(readIndex); int16_t r1 = read(readIndex + 1); @@ -393,8 +390,8 @@ void __AudioUpdate(bool resetRecording) { frac &= 0xffff; } - buf1 = srcBuffer.data(); - sz1 = srcBuffer.size(); + buf1 = srcBuffer; + sz1 = srcBufferSize; buf2 = nullptr; sz2 = 0; } From ecb84987efb49224ac335300652e9511a0be8d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 11:55:14 +0200 Subject: [PATCH 28/99] Remove allocation from the heaviest MemBlockInfo path (BlockTransfer). Required some refactoring. --- Common/StringUtils.cpp | 7 +++-- Common/StringUtils.h | 6 ++-- Core/Debugger/MemBlockInfo.cpp | 54 +++++++++++++++++++++------------ Core/Debugger/MemBlockInfo.h | 4 ++- Core/HLE/ReplaceTables.cpp | 10 +++--- Core/HLE/sceAtrac.cpp | 2 +- Core/HLE/sceDeflt.cpp | 2 +- Core/HLE/sceDmac.cpp | 2 +- Core/HLE/sceKernelInterrupt.cpp | 6 ++-- Core/MemMapHelpers.h | 5 +-- GPU/GPUCommon.cpp | 11 ++++--- GPU/Software/SoftGpu.cpp | 2 +- 12 files changed, 65 insertions(+), 46 deletions(-) diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index c4b88c4c3a..f88f6194ab 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -44,15 +44,16 @@ #include "Common/Buffer.h" #include "Common/StringUtils.h" -void truncate_cpy(char *dest, size_t destSize, const char *src) { +size_t truncate_cpy(char *dest, size_t destSize, const char *src) { size_t len = strlen(src); if (len >= destSize - 1) { memcpy(dest, src, destSize - 1); - dest[destSize - 1] = '\0'; + len = destSize - 1; } else { memcpy(dest, src, len); - dest[len] = '\0'; } + dest[len] = '\0'; + return len; } const char* safe_string(const char* s) { diff --git a/Common/StringUtils.h b/Common/StringUtils.h index 989fe8b9fd..16bfaa18e5 100644 --- a/Common/StringUtils.h +++ b/Common/StringUtils.h @@ -78,10 +78,10 @@ std::string ReplaceAll(std::string input, const std::string& src, const std::str void SkipSpace(const char **ptr); -void truncate_cpy(char *dest, size_t destSize, const char *src); +size_t truncate_cpy(char *dest, size_t destSize, const char *src); template -inline void truncate_cpy(char(&out)[Count], const char *src) { - truncate_cpy(out, Count, src); +inline size_t truncate_cpy(char(&out)[Count], const char *src) { + return truncate_cpy(out, Count, src); } const char* safe_string(const char* s); diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index f0be93ea60..e38b6861b7 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -37,7 +37,8 @@ public: bool Mark(uint32_t addr, uint32_t size, uint64_t ticks, uint32_t pc, bool allocated, const char *tag); bool Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::vector &results); - bool FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size, std::string &result); + // Note that the returned pointer gets invalidated as soon as Mark is called. + const char *FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size); void Reset(); void DoState(PointerWrap &p); @@ -151,17 +152,16 @@ bool MemSlabMap::Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::ve return found; } -bool MemSlabMap::FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size, std::string &result) { +const char *MemSlabMap::FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size) { uint32_t end = addr + size; Slab *slab = FindSlab(addr); while (slab != nullptr && slab->start < end) { if (slab->pc != 0 || slab->tag[0] != '\0') { - result = slab->tag; - return true; + return slab->tag; } slab = slab->next; } - return false; + return nullptr; } void MemSlabMap::Reset() { @@ -485,7 +485,7 @@ std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, return results; } -static std::string FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) { +static const char *FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) { start &= ~0xC0000000; if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start) @@ -493,37 +493,51 @@ static std::string FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint3 if (pendingNotifyMinAddr2 < start + size && pendingNotifyMaxAddr2 >= start) FlushPendingMemInfo(); - std::string tag; if (flags & MemBlockFlags::ALLOC) { - if (allocMap.FastFindWriteTag(MemBlockFlags::ALLOC, start, size, tag)) + const char *tag = allocMap.FastFindWriteTag(MemBlockFlags::ALLOC, start, size); + if (tag) return tag; } if (flags & MemBlockFlags::SUB_ALLOC) { - if (suballocMap.FastFindWriteTag(MemBlockFlags::SUB_ALLOC, start, size, tag)) + const char *tag = suballocMap.FastFindWriteTag(MemBlockFlags::SUB_ALLOC, start, size); + if (tag) return tag; } if (flags & MemBlockFlags::WRITE) { - if (writeMap.FastFindWriteTag(MemBlockFlags::WRITE, start, size, tag)) + const char *tag = writeMap.FastFindWriteTag(MemBlockFlags::WRITE, start, size); + if (tag) return tag; } if (flags & MemBlockFlags::TEXTURE) { - if (textureMap.FastFindWriteTag(MemBlockFlags::TEXTURE, start, size, tag)) + const char *tag = textureMap.FastFindWriteTag(MemBlockFlags::TEXTURE, start, size); + if (tag) return tag; } - return ""; + return nullptr; } -std::string GetMemWriteTagAt(uint32_t start, uint32_t size) { - std::string tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size); - if (!tag.empty() && tag != "MemInit") - return tag; - +std::string GetMemWriteTagAt(const char *prefix, uint32_t start, uint32_t size) { + const char *tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size); + if (tag && strcmp(tag, "MemInit") != 0) + return std::string(prefix) + tag; // Fall back to alloc and texture, especially for VRAM. We prefer write above. tag = FindWriteTagByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size); - if (!tag.empty()) - return tag; + if (tag) + return std::string(prefix) + tag; + return StringFromFormat("%s%08x_size_%08x", prefix, start, size); +} - return StringFromFormat("%08x_size_%08x", start, size); +size_t FormatMemWriteTagAt(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size) { + const char *tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size); + if (tag && strcmp(tag, "MemInit") != 0) { + return snprintf(buf, sz, "%s%s", prefix, tag); + } + // Fall back to alloc and texture, especially for VRAM. We prefer write above. + tag = FindWriteTagByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size); + if (tag) { + return snprintf(buf, sz, "%s%s", prefix, tag); + } + return snprintf(buf, sz, "%s%08x_size_%08x", prefix, start, size); } void MemBlockInfoInit() { diff --git a/Core/Debugger/MemBlockInfo.h b/Core/Debugger/MemBlockInfo.h index 50e3bfae53..839249f3a2 100644 --- a/Core/Debugger/MemBlockInfo.h +++ b/Core/Debugger/MemBlockInfo.h @@ -68,7 +68,9 @@ inline void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, co std::vector FindMemInfo(uint32_t start, uint32_t size); std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, uint32_t size); -std::string GetMemWriteTagAt(uint32_t start, uint32_t size); +std::string GetMemWriteTagAt(const char *prefix, uint32_t start, uint32_t size); +// Same as above but allocation-free. +size_t FormatMemWriteTagAt(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size); void MemBlockInfoInit(); void MemBlockInfoShutdown(); diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 57603ccf2c..462bad0a7e 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -159,7 +159,7 @@ static int Replace_memcpy() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpy/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); @@ -211,7 +211,7 @@ static int Replace_memcpy_jak() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpy/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); @@ -249,7 +249,7 @@ static int Replace_memcpy16() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemcpy16/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpy16/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); } @@ -290,7 +290,7 @@ static int Replace_memcpy_swizzled() { RETURN(0); if (MemBlockInfoDetailed(pitch * h)) { - const std::string tag = "ReplaceMemcpySwizzle/" + GetMemWriteTagAt(srcPtr, pitch * h); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpySwizzle/", srcPtr, pitch * h); NotifyMemInfo(MemBlockFlags::READ, srcPtr, pitch * h, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, pitch * h, tag.c_str(), tag.size()); } @@ -321,7 +321,7 @@ static int Replace_memmove() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemmove/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemmove/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); } diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 6ee643fe5a..b22c439bc1 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -1268,7 +1268,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 if (outbufPtr != 0) { u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16); if (packetAddr != 0 && MemBlockInfoDetailed()) { - const std::string tag = "AtracDecode/" + GetMemWriteTagAt(packetAddr, packetSize); + const std::string tag = GetMemWriteTagAt("AtracDecode/", packetAddr, packetSize); NotifyMemInfo(MemBlockFlags::READ, packetAddr, packetSize, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tag.c_str(), tag.size()); } else { diff --git a/Core/HLE/sceDeflt.cpp b/Core/HLE/sceDeflt.cpp index d5580dcc40..985b89d543 100644 --- a/Core/HLE/sceDeflt.cpp +++ b/Core/HLE/sceDeflt.cpp @@ -59,7 +59,7 @@ static int CommonDecompress(int windowBits, u32 OutBuffer, int OutBufferLength, } if (MemBlockInfoDetailed(stream.total_in, stream.total_out)) { - const std::string tag = "sceDeflt/" + GetMemWriteTagAt(InBuffer, stream.total_in); + const std::string tag = GetMemWriteTagAt("sceDeflt/", InBuffer, stream.total_in); NotifyMemInfo(MemBlockFlags::READ, InBuffer, stream.total_in, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, OutBuffer, stream.total_out, tag.c_str(), tag.size()); } diff --git a/Core/HLE/sceDmac.cpp b/Core/HLE/sceDmac.cpp index 86c8e60a1e..5fba8f9b94 100644 --- a/Core/HLE/sceDmac.cpp +++ b/Core/HLE/sceDmac.cpp @@ -52,7 +52,7 @@ static int __DmacMemcpy(u32 dst, u32 src, u32 size) { if (!skip) { currentMIPS->InvalidateICache(src, size); if (MemBlockInfoDetailed(size)) { - const std::string tag = "DmacMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("DmacMemcpy/", src, size); Memory::Memcpy(dst, src, size, tag.c_str(), tag.size()); } else { Memory::Memcpy(dst, src, size, "DmacMemcpy"); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index a86deee9fe..cffedaeb46 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -656,7 +656,7 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) } if (MemBlockInfoDetailed(size)) { - const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); } @@ -691,7 +691,7 @@ static u32 sysclib_memcpy(u32 dst, u32 src, u32 size) { memcpy(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size); } if (MemBlockInfoDetailed(size)) { - const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); } @@ -794,7 +794,7 @@ static u32 sysclib_memmove(u32 dst, u32 src, u32 size) { memmove(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size); } if (MemBlockInfoDetailed(size)) { - const std::string tag = "KernelMemmove/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("KernelMemmove/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); } diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index 848bfa73b6..e623d9e768 100644 --- a/Core/MemMapHelpers.h +++ b/Core/MemMapHelpers.h @@ -20,6 +20,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/StringUtils.h" #include "Core/Debugger/MemBlockInfo.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" @@ -70,9 +71,9 @@ inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len, if (MemBlockInfoDetailed(len)) { char tagData[128]; if (!tag) { - const std::string srcTag = GetMemWriteTagAt(from_address, len); + const std::string srcTag = GetMemWriteTagAt("Memcpy/", from_address, len); tag = tagData; - tagLen = snprintf(tagData, sizeof(tagData), "Memcpy/%s", srcTag.c_str()); + tagLen = truncate_cpy(tagData, srcTag.c_str()); } NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag, tagLen); NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag, tagLen); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 22b323c713..60383b6c86 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2864,9 +2864,10 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { if (MemBlockInfoDetailed(numBytes, numBytes)) { const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; - const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize); - NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + 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); } // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. @@ -2881,7 +2882,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { // Since they're identical we don't need to copy. if (!Memory::IsVRAMAddress(dest) || (dest ^ 0x00400000) != src) { if (MemBlockInfoDetailed(size)) { - const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("GPUMemcpy/", src, size); Memory::Memcpy(dest, src, size, tag.c_str(), tag.size()); } else { Memory::Memcpy(dest, src, size, "GPUMemcpy"); @@ -2893,7 +2894,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { } if (MemBlockInfoDetailed(size)) { - const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("GPUMemcpy/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dest, size, tag.c_str(), tag.size()); } diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index dd4b71b166..8c26eac9b1 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -813,7 +813,7 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { } if (MemBlockInfoDetailed(srcSize, dstSize)) { - const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize); + 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()); } From 7c5d36eca76c905a122c191858938191724edab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 13:33:46 +0200 Subject: [PATCH 29/99] libretro buildfix --- libretro/libretro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 636da101d9..a2adf4c4fa 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -379,7 +379,7 @@ class LibretroHost : public Host void InitSound() override {} void UpdateSound() override { - extern int hostAttemptBlockSize; + extern const int hostAttemptBlockSize; const int blockSizeMax = 512; static int16_t audio[blockSizeMax * 2]; assert(hostAttemptBlockSize <= blockSizeMax); From 1cd34f9d2bef6c92ed457c6aa85bf4d9b5111e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 14:07:14 +0200 Subject: [PATCH 30/99] Another buildfix attempt --- Core/HLE/__sceAudio.cpp | 5 +++++ Core/HLE/__sceAudio.h | 2 ++ libretro/libretro.cpp | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 3176d26130..f05ad9dfca 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -82,6 +82,11 @@ static bool m_logAudio; static int chanQueueMaxSizeFactor; static int chanQueueMinSizeFactor; +// Accessor for libretro +int __AudioGetHostAttemptBlockSize() { + return hostAttemptBlockSize; +} + static void hleAudioUpdate(u64 userdata, int cyclesLate) { // Schedule the next cycle first. __AudioUpdate() may consume cycles. CoreTiming::ScheduleEvent(audioIntervalCycles - cyclesLate, eventAudioUpdate, 0); diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index ae9ca35126..f26ae0dc77 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -50,6 +50,8 @@ int __AudioMix(short *outstereo, int numSamples, int sampleRate); void __AudioGetDebugStats(char *buf, size_t bufSize); void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu! +int __AudioGetHostAttemptBlockSize(); + // Audio Dumping stuff void __StartLogAudio(const Path &filename); void __StopLogAudio(); diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index a2adf4c4fa..a5a720b7a7 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -379,7 +379,7 @@ class LibretroHost : public Host void InitSound() override {} void UpdateSound() override { - extern const int hostAttemptBlockSize; + int hostAttemptBlockSize = __AudioGetHostAttemptBlockSize(); const int blockSizeMax = 512; static int16_t audio[blockSizeMax * 2]; assert(hostAttemptBlockSize <= blockSizeMax); From 9097fdaae61f9d39cc06758643c9de3a2f0a1852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 14:21:34 +0200 Subject: [PATCH 31/99] Shrink VkRenderData from 88 to 64 bytes. --- Common/GPU/Vulkan/VulkanQueueRunner.cpp | 19 ++++++++++++++----- Common/GPU/Vulkan/VulkanQueueRunner.h | 12 ++++++------ Common/GPU/Vulkan/VulkanRenderManager.h | 16 ++++++++-------- Common/GPU/Vulkan/thin3d_vulkan.cpp | 10 +++++----- GPU/Vulkan/DrawEngineVulkan.cpp | 12 ++++++------ 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 2acf0ff1c0..b00ec67a01 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -347,6 +347,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st profile->cpuStartTime = time_now_d(); bool emitLabels = vulkan_->Extensions().EXT_debug_utils; + for (size_t i = 0; i < steps.size(); i++) { const VKRStep &step = *steps[i]; @@ -1152,6 +1153,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c // The stencil ones are very commonly mostly redundant so let's eliminate them where possible. // Might also want to consider scissor and viewport. VkPipeline lastPipeline = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + int lastStencilWriteMask = -1; int lastStencilCompareMask = -1; int lastStencilReference = -1; @@ -1167,6 +1170,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VkPipeline pipeline = c.pipeline.pipeline; if (pipeline != lastGraphicsPipeline) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; lastGraphicsPipeline = pipeline; // Reset dynamic state so it gets refreshed with the new pipeline. lastStencilWriteMask = -1; @@ -1188,6 +1192,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } if (pipeline->pipeline != lastGraphicsPipeline && pipeline->pipeline != VK_NULL_HANDLE) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); + pipelineLayout = c.pipeline.pipelineLayout; lastGraphicsPipeline = pipeline->pipeline; // Reset dynamic state so it gets refreshed with the new pipeline. lastStencilWriteMask = -1; @@ -1209,6 +1214,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } if (pipeline->pipeline != lastComputePipeline && pipeline->pipeline != VK_NULL_HANDLE) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); + pipelineLayout = c.pipeline.pipelineLayout; lastComputePipeline = pipeline->pipeline; } break; @@ -1258,7 +1264,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } case VKRRenderCommand::PUSH_CONSTANTS: - vkCmdPushConstants(cmd, c.push.pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data); + vkCmdPushConstants(cmd, pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data); break; case VKRRenderCommand::STENCIL: @@ -1277,14 +1283,17 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c break; case VKRRenderCommand::DRAW_INDEXED: - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); - vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, c.drawIndexed.indexType); - vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + { + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, (VkIndexType)c.drawIndexed.indexType); + VkDeviceSize voffset = c.drawIndexed.voffset; + vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset); vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); break; + } case VKRRenderCommand::DRAW: - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); if (c.draw.vbuffer) { vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); } diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index ba79634702..401a125d6b 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -52,15 +52,17 @@ struct VkRenderData { union { struct { VkPipeline pipeline; + VkPipelineLayout pipelineLayout; } pipeline; struct { VKRGraphicsPipeline *pipeline; + VkPipelineLayout pipelineLayout; } graphics_pipeline; struct { VKRComputePipeline *pipeline; + VkPipelineLayout pipelineLayout; } compute_pipeline; struct { - VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; uint32_t uboOffsets[3]; @@ -70,17 +72,16 @@ struct VkRenderData { uint32_t offset; } draw; struct { - VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; uint32_t uboOffsets[3]; VkBuffer vbuffer; // might need to increase at some point - VkDeviceSize voffset; VkBuffer ibuffer; - VkDeviceSize ioffset; + uint32_t voffset; + uint32_t ioffset; uint32_t count; int16_t instances; - VkIndexType indexType; + int16_t indexType; } drawIndexed; struct { uint32_t clearColor; @@ -103,7 +104,6 @@ struct VkRenderData { uint32_t color; } blendColor; struct { - VkPipelineLayout pipelineLayout; VkShaderStageFlags stages; uint8_t offset; uint8_t size; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 1df2fc2361..a541bea57e 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -242,29 +242,32 @@ public: return pipeline; } - void BindPipeline(VkPipeline pipeline, PipelineFlags flags) { + void BindPipeline(VkPipeline pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != VK_NULL_HANDLE); VkRenderData data{ VKRRenderCommand::BIND_PIPELINE }; data.pipeline.pipeline = pipeline; + data.pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); } - void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags) { + void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE }; data.graphics_pipeline.pipeline = pipeline; + data.graphics_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); } - void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags) { + void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE }; data.compute_pipeline.pipeline = pipeline; + data.compute_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); } @@ -351,7 +354,6 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(size + offset < 40); VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS }; - data.push.pipelineLayout = pipelineLayout; data.push.stages = stages; data.push.offset = offset; data.push.size = size; @@ -386,12 +388,11 @@ public: curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE; } - void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) { + void Draw(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_); VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; data.draw.offset = offset; - data.draw.pipelineLayout = layout; data.draw.ds = descSet; data.draw.vbuffer = vbuffer; data.draw.voffset = voffset; @@ -403,12 +404,11 @@ public: curRenderStep_->render.numDraws++; } - void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { + void DrawIndexed(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_); VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.instances = numInstances; - data.drawIndexed.pipelineLayout = layout; data.drawIndexed.ds = descSet; data.drawIndexed.vbuffer = vbuffer; data.drawIndexed.voffset = voffset; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 171decd3eb..d6cc587944 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1339,7 +1339,7 @@ void VKContext::Draw(int vertexCount, int offset) { BindCompatiblePipeline(); ApplyDynamicState(); - renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset); + renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset); } void VKContext::DrawIndexed(int vertexCount, int offset) { @@ -1359,7 +1359,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { BindCompatiblePipeline(); ApplyDynamicState(); - renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16); + renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1375,15 +1375,15 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { BindCompatiblePipeline(); ApplyDynamicState(); - renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount); + renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount); } void VKContext::BindCompatiblePipeline() { VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass(); if (renderPass == renderManager_.GetBackbufferRenderPass()) { - renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags); + renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags, pipelineLayout_); } else { - renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags); + renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags, pipelineLayout_); } } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 37243cac85..60997e582a 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -787,7 +787,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags); + renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -814,9 +814,9 @@ void DrawEngineVulkan::DoFlush() { if (!ibuf) { ibOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); } - renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16); } else { - renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount); + renderManager->Draw(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } } else { PROFILE_THIS_SCOPE("soft"); @@ -917,7 +917,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags); + renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -949,11 +949,11 @@ void DrawEngineVulkan::DoFlush() { VkBuffer vbuf, ibuf; vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); ibOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(short) * result.drawNumTrans, &ibuf); - renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, result.drawNumTrans * sizeof(TransformedVertex), &vbuf); - renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans); + renderManager->Draw(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans); } } else if (result.action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. From f2e0f7611c255406dc45153fc1a91c656c02799c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 14:31:52 +0200 Subject: [PATCH 32/99] Display paths with backslashes on Windows. Show texture path when saving. --- Common/File/Path.cpp | 7 ++++++- Core/TextureReplacer.cpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Common/File/Path.cpp b/Common/File/Path.cpp index d0bda53328..ba3372e78a 100644 --- a/Common/File/Path.cpp +++ b/Common/File/Path.cpp @@ -258,8 +258,13 @@ std::wstring Path::ToWString() const { std::string Path::ToVisualString() const { if (type_ == PathType::CONTENT_URI) { return AndroidContentURI(path_).ToVisualString(); +#if PPSSPP_PLATFORM(WINDOWS) + } else if (type_ == PathType::NATIVE) { + return ReplaceAll(path_, "/", "\\"); +#endif + } else { + return path_; } - return path_; } bool Path::CanNavigateUp() const { diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index c0b965e9b1..4544fdcd72 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -573,7 +573,7 @@ public: if (png.warning_or_error >= 2) { ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors."); } else if (success) { - NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfoHash, w, h); + NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str()); } } }; From 11d4f9acb6516ac151a75789becfb9cf26453cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 14:49:02 +0200 Subject: [PATCH 33/99] Fix texture saving for Vulkan --- GPU/Vulkan/TextureCacheVulkan.cpp | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 2f82eae187..4b4ec639a9 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -499,12 +499,29 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { imageLayout = VK_IMAGE_LAYOUT_GENERAL; } + ReplacedTextureDecodeInfo replacedInfo; + bool willSaveTex = false; + if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { + // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? + replacedInfo.cachekey = entry->CacheKey(); + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = entry->addr; + replacedInfo.isVideo = plan.isVideo; + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = plan.scaleFactor; + replacedInfo.fmt = FromVulkanFormat(VULKAN_8888_FORMAT); + willSaveTex = replacer_.WillSave(replacedInfo); + if (willSaveTex) { + actualFmt = VULKAN_8888_FORMAT; + } + } + char texName[128]{}; snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); image->SetTag(texName); bool allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); - if (!allocSuccess && !lowMemoryMode_) { + if (!allocSuccess && !lowMemoryMode_ && !replacer_.Enabled()) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; decimationCounter_ = 0; @@ -540,20 +557,6 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { return; } - ReplacedTextureDecodeInfo replacedInfo; - bool willSaveTex = false; - if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { - // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? - replacedInfo.cachekey = entry->CacheKey(); - replacedInfo.hash = entry->fullhash; - replacedInfo.addr = entry->addr; - replacedInfo.isVideo = plan.isVideo; - replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; - replacedInfo.scaleFactor = plan.scaleFactor; - replacedInfo.fmt = FromVulkanFormat(actualFmt); - willSaveTex = replacer_.WillSave(replacedInfo); - } - VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "Texture Upload (%08x) video=%d", entry->addr, plan.isVideo); @@ -591,7 +594,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { } else { data = drawEngine_->GetPushBufferForTextureData()->PushAligned(sz, &bufferOffset, &texBuf, pushAlignment); } - LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, dstFmt); + LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, actualFmt); if (willSaveTex) bufferOffset = drawEngine_->GetPushBufferForTextureData()->PushAligned(&saveData[0], sz, pushAlignment, &texBuf); }; From 2bf0c86ac7b3e8c8d98077474eea1aa3f9ada77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 15:02:51 +0200 Subject: [PATCH 34/99] Fix image format when dumping textures. Fixes #15831 --- GPU/Common/TextureCacheCommon.cpp | 15 ++++++++++++++- GPU/Common/TextureCacheCommon.h | 1 + GPU/D3D11/TextureCacheD3D11.cpp | 2 +- GPU/Directx9/TextureCacheDX9.cpp | 2 +- GPU/GLES/TextureCacheGLES.cpp | 8 +++----- GPU/Vulkan/TextureCacheVulkan.cpp | 30 +++++++++++++----------------- 6 files changed, 33 insertions(+), 25 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index faa03ad261..acce603667 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2388,6 +2388,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.replaceValid = plan.replaced->Valid(); } + plan.saveTexture = false; if (plan.replaceValid) { // We're replacing, so we won't scale. plan.scaleFactor = 1; @@ -2397,6 +2398,18 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt // But, we still need to create the texture at a larger size. plan.replaced->GetSize(0, plan.createW, plan.createH); } else { + if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { + ReplacedTextureDecodeInfo replacedInfo; + // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? + replacedInfo.cachekey = entry->CacheKey(); + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = entry->addr; + replacedInfo.isVideo = plan.isVideo; + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = plan.scaleFactor; + replacedInfo.fmt = Draw::DataFormat::R8G8B8A8_UNORM; + plan.saveTexture = replacer_.WillSave(replacedInfo); + } plan.createW = plan.w * plan.scaleFactor; plan.createH = plan.h * plan.scaleFactor; } @@ -2454,7 +2467,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i decPitch = stride; } - bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1; + bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM; CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, reverseColors, expand32); entry.SetAlphaStatus(alphaResult, srcLevel); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index e394923090..7cb79d44ce 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -272,6 +272,7 @@ struct BuildTexturePlan { ReplacedTexture *replaced; // Need to only check once since it can change during the load! bool replaceValid; + bool saveTexture; void GetMipSize(int level, int *w, int *h) const { if (replaceValid) { diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index baa8ee5e04..5cc42adee6 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -279,7 +279,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.replaceValid) { dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc)); - } else if (plan.scaleFactor > 1) { + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 0ef616914d..289d52d3b7 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -241,7 +241,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.replaceValid) { dstFmt = ToD3D9Format(plan.replaced->Format(plan.baseLevelSrc)); - } else if (plan.scaleFactor > 1) { + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = D3DFMT_A8R8G8B8; } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index cf9a8e13af..928854f1e2 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -258,15 +258,13 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { // the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not // be as good quality as the game's own (might even be better in some cases though). - int tw = plan.w; - int th = plan.h; + int tw = plan.createW; + int th = plan.createH; Draw::DataFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) { dstFmt = plan.replaced->Format(plan.baseLevelSrc); - } else if (plan.scaleFactor > 1) { - tw *= plan.scaleFactor; - th *= plan.scaleFactor; + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = Draw::DataFormat::R8G8B8A8_UNORM; } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 4b4ec639a9..e72464a469 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -499,21 +499,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { imageLayout = VK_IMAGE_LAYOUT_GENERAL; } - ReplacedTextureDecodeInfo replacedInfo; - bool willSaveTex = false; - if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { - // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? - replacedInfo.cachekey = entry->CacheKey(); - replacedInfo.hash = entry->fullhash; - replacedInfo.addr = entry->addr; - replacedInfo.isVideo = plan.isVideo; - replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; - replacedInfo.scaleFactor = plan.scaleFactor; - replacedInfo.fmt = FromVulkanFormat(VULKAN_8888_FORMAT); - willSaveTex = replacer_.WillSave(replacedInfo); - if (willSaveTex) { - actualFmt = VULKAN_8888_FORMAT; - } + if (plan.saveTexture) { + actualFmt = VULKAN_8888_FORMAT; } char texName[128]{}; @@ -588,14 +575,14 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { std::vector saveData; auto loadLevel = [&](int sz, int srcLevel, int lstride, int lfactor) { - if (willSaveTex) { + if (plan.saveTexture) { saveData.resize(sz); data = &saveData[0]; } else { data = drawEngine_->GetPushBufferForTextureData()->PushAligned(sz, &bufferOffset, &texBuf, pushAlignment); } LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, actualFmt); - if (willSaveTex) + if (plan.saveTexture) bufferOffset = drawEngine_->GetPushBufferForTextureData()->PushAligned(&saveData[0], sz, pushAlignment, &texBuf); }; @@ -646,6 +633,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { int w = dataScaled ? mipWidth : mipUnscaledWidth; int h = dataScaled ? mipHeight : mipUnscaledHeight; // At this point, data should be saveData, and not slow. + ReplacedTextureDecodeInfo replacedInfo; + replacedInfo.cachekey = entry->CacheKey(); + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = entry->addr; + replacedInfo.isVideo = IsVideo(entry->addr); + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = plan.scaleFactor; + replacedInfo.fmt = FromVulkanFormat(actualFmt); + replacer_.NotifyTextureDecoded(replacedInfo, data, stride, plan.baseLevelSrc + i, w, h); } } From 044d9a416bf6f7f4256ee00113d3946ad4b0b96e Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 1 Sep 2022 18:08:08 +0200 Subject: [PATCH 35/99] OpenXR - Emulate mouse cursor --- Common/VR/PPSSPPVR.cpp | 71 ++++++++++++++++++++++++++++++++++++- Common/VR/PPSSPPVR.h | 2 +- Common/VR/VRInput.cpp | 9 +++++ Common/VR/VRInput.h | 1 + Common/VR/VRRenderer.cpp | 39 +++++++++++++------- Common/VR/VRRenderer.h | 29 ++++++++------- android/jni/app-android.cpp | 2 +- 7 files changed, 124 insertions(+), 29 deletions(-) diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 9c6cac4640..d4ab5471cc 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -1,6 +1,7 @@ #include "Common/VR/PPSSPPVR.h" #include "Common/VR/VRBase.h" #include "Common/VR/VRInput.h" +#include "Common/VR/VRMath.h" #include "Common/VR/VRRenderer.h" #include "Common/VR/VRTweaks.h" @@ -31,6 +32,16 @@ struct ButtonMapping { } }; +struct MouseActivator { + bool activate; + ovrButton ovr; + + MouseActivator(bool activate, ovrButton ovr) { + this->activate = activate; + this->ovr = ovr; + } +}; + static std::vector leftControllerMapping = { ButtonMapping(NKCODE_BUTTON_X, ovrButton_X), ButtonMapping(NKCODE_BUTTON_Y, ovrButton_Y), @@ -61,6 +72,16 @@ static std::vector controllerMapping[2] = { leftControllerMapping, rightControllerMapping }; +static int mouseController = -1; +static bool mousePressed[] = {false, false}; + +static std::vector mouseActivators = { + MouseActivator(true, ovrButton_Trigger), + MouseActivator(false, ovrButton_Up), + MouseActivator(false, ovrButton_Down), + MouseActivator(false, ovrButton_Left), + MouseActivator(false, ovrButton_Right), +}; /* ================================================================================ @@ -99,7 +120,8 @@ void GetVRResolutionPerEye(int* width, int* height) { } } -void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) { +void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) { + //buttons KeyInput keyInput = {}; for (int j = 0; j < 2; j++) { int status = IN_VRGetButtonState(j); @@ -125,6 +147,53 @@ void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) { } } } + + //enable or disable mouse + for (int j = 0; j < 2; j++) { + int status = IN_VRGetButtonState(j); + for (MouseActivator& m : mouseActivators) { + if (status & m.ovr) { + mouseController = m.activate ? j : -1; + } + } + } + + //mouse cursor + if (mouseController >= 0) { + //get position on screen + XrPosef pose = IN_VRGetPose(mouseController); + XrVector3f angles = XrQuaternionf_ToEulerAngles(pose.orientation); + float width = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_WIDTH); + float height = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_HEIGHT); + float cx = width / 2; + float cy = height / 2; + float speed = (cx + cy) / 2; + float x = cx - tan(ToRadians(angles.y - (float)VR_GetConfig(VR_CONFIG_MENU_YAW))) * speed; + float y = cy - tan(ToRadians(angles.x)) * speed; + + //set renderer + VR_SetConfig(VR_CONFIG_MOUSE_X, (int)x); + VR_SetConfig(VR_CONFIG_MOUSE_Y, (int)y); + VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 6 * (int)pow(VR_GetConfig(VR_CONFIG_CANVAS_DISTANCE), 0.25f)); + + //inform engine about the status + TouchInput touch; + touch.id = mouseController; + touch.x = x * dp_xscale; + touch.y = (height - y - 1) * dp_yscale; + bool pressed = IN_VRGetButtonState(mouseController) & ovrButton_Trigger; + if (mousePressed[mouseController] != pressed) { + if (!pressed) { + touch.flags = TOUCH_DOWN; + NativeTouch(touch); + touch.flags = TOUCH_UP; + NativeTouch(touch); + } + mousePressed[mouseController] = pressed; + } + } else { + VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 0); + } } void UpdateVRScreenKey(const KeyInput &key) { diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h index c605980d28..4b6b6774c2 100644 --- a/Common/VR/PPSSPPVR.h +++ b/Common/VR/PPSSPPVR.h @@ -10,7 +10,7 @@ bool IsVRBuild(); void InitVROnAndroid(void* vm, void* activity, int version, char* name); void EnterVR(bool firstStart); void GetVRResolutionPerEye(int* width, int* height); -void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics); +void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale); void UpdateVRScreenKey(const KeyInput &key); // VR rendering integration diff --git a/Common/VR/VRInput.cpp b/Common/VR/VRInput.cpp index b25bc4b6ca..63fc97a030 100644 --- a/Common/VR/VRInput.cpp +++ b/Common/VR/VRInput.cpp @@ -506,3 +506,12 @@ uint32_t IN_VRGetButtonState( int controllerIndex ) { XrVector2f IN_VRGetJoystickState( int controllerIndex ) { return moveJoystickState[controllerIndex].currentState; } + +XrPosef IN_VRGetPose( int controllerIndex ) { + engine_t* engine = VR_GetEngine(); + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + XrSpace aimSpace[] = { leftControllerAimSpace, rightControllerAimSpace }; + xrLocateSpace(aimSpace[controllerIndex], engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc); + return loc.pose; +} diff --git a/Common/VR/VRInput.h b/Common/VR/VRInput.h index 241bf2791e..126cc56eaa 100644 --- a/Common/VR/VRInput.h +++ b/Common/VR/VRInput.h @@ -32,4 +32,5 @@ void IN_VRInit( engine_t *engine ); void IN_VRInputFrame( engine_t* engine ); uint32_t IN_VRGetButtonState( int controllerIndex ); XrVector2f IN_VRGetJoystickState( int controllerIndex ); +XrPosef IN_VRGetPose( int controllerIndex ); void INVR_Vibrate( int duration, int chan, float intensity ); diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp index 343a496620..f4c4d7f805 100644 --- a/Common/VR/VRRenderer.cpp +++ b/Common/VR/VRRenderer.cpp @@ -16,9 +16,6 @@ GLboolean initialized = GL_FALSE; GLboolean stageSupported = GL_FALSE; int vrConfig[VR_CONFIG_MAX] = {}; -float menuPitch = 0; -float menuYaw = 0; -float recenterYaw = 0; XrVector3f hmdorientation; XrVector3f hmdposition; @@ -135,7 +132,8 @@ void VR_Recenter(engine_t* engine) { OXR(xrLocateSpace(engine->appState.HeadSpace, engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc)); hmdorientation = XrQuaternionf_ToEulerAngles(loc.pose.orientation); - recenterYaw += ToRadians(hmdorientation.y); + vrConfig[VR_CONFIG_RECENTER_YAW] += (int)hmdorientation.y; + float recenterYaw = ToRadians((float)vrConfig[VR_CONFIG_RECENTER_YAW]); spaceCreateInfo.poseInReferenceSpace.orientation.x = 0; spaceCreateInfo.poseInReferenceSpace.orientation.y = sin(recenterYaw / 2); spaceCreateInfo.poseInReferenceSpace.orientation.z = 0; @@ -171,8 +169,8 @@ void VR_Recenter(engine_t* engine) { } // Update menu orientation - menuPitch = hmdorientation.x; - menuYaw = 0; + vrConfig[VR_CONFIG_MENU_PITCH] = (int)hmdorientation.x; + vrConfig[VR_CONFIG_MENU_YAW] = 0; } void VR_InitRenderer( engine_t* engine, bool multiview ) { @@ -182,6 +180,8 @@ void VR_InitRenderer( engine_t* engine, bool multiview ) { int eyeW, eyeH; VR_GetResolution(engine, &eyeW, &eyeH); + vrConfig[VR_CONFIG_VIEWPORT_WIDTH] = eyeW; + vrConfig[VR_CONFIG_VIEWPORT_HEIGHT] = eyeH; // Get the viewport configuration info for the chosen viewport configuration type. engine->appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; @@ -321,15 +321,26 @@ void VR_EndFrame( engine_t* engine ) { glClear(GL_COLOR_BUFFER_BIT); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + // Show mouse cursor + int vrMode = vrConfig[VR_CONFIG_MODE]; + int size = vrConfig[VR_CONFIG_MOUSE_SIZE]; + if ((vrMode == VR_MODE_FLAT_SCREEN) && (size > 0)) { + glEnable(GL_SCISSOR_TEST); + glScissor(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); + glViewport(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); + glClearColor(1, 1, 1, 1); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer; //ovrFramebuffer_Resolve(frameBuffer); ovrFramebuffer_Release(frameBuffer); ovrFramebuffer_SetNone(); XrCompositionLayerProjectionView projection_layer_elements[2] = {}; - int vrMode = vrConfig[VR_CONFIG_MODE]; if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) { - menuYaw = hmdorientation.y; + vrConfig[VR_CONFIG_MENU_YAW] = (int)hmdorientation.y; for (int eye = 0; eye < ovrMaxNumEyes; eye++) { int imageLayer = eye; @@ -379,14 +390,16 @@ void VR_EndFrame( engine_t* engine ) { cylinder_layer.subImage.imageRect.extent.width = width; cylinder_layer.subImage.imageRect.extent.height = height; cylinder_layer.subImage.imageArrayIndex = 0; - float distance = vrConfig[VR_CONFIG_CANVAS_DISTANCE]; + float distance = (float)vrConfig[VR_CONFIG_CANVAS_DISTANCE]; + float menuPitch = ToRadians((float)vrConfig[VR_CONFIG_MENU_PITCH]); + float menuYaw = ToRadians((float)vrConfig[VR_CONFIG_MENU_YAW]); XrVector3f pos = { - invViewTransform[0].position.x - sin(ToRadians(menuYaw)) * distance, + invViewTransform[0].position.x - sin(menuYaw) * distance, invViewTransform[0].position.y, - invViewTransform[0].position.z - cos(ToRadians(menuYaw)) * distance + invViewTransform[0].position.z - cos(menuYaw) * distance }; - XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -ToRadians(menuPitch)); - XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, ToRadians(menuYaw)); + XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -menuPitch); + XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, menuYaw); cylinder_layer.pose.orientation = XrQuaternionf_Multiply(pitch, yaw); cylinder_layer.pose.position = pos; cylinder_layer.radius = 12.0f; diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h index 872bda7772..5527480bc3 100644 --- a/Common/VR/VRRenderer.h +++ b/Common/VR/VRRenderer.h @@ -4,19 +4,22 @@ #include "VRMath.h" enum VRConfig { - VR_CONFIG_MODE, - VR_CONFIG_6DOF_ENABLED, - VR_CONFIG_6DOF_SCALE, - VR_CONFIG_MIRROR_AXIS_X, - VR_CONFIG_MIRROR_AXIS_Y, - VR_CONFIG_MIRROR_AXIS_Z, - VR_CONFIG_MIRROR_PITCH, - VR_CONFIG_MIRROR_YAW, - VR_CONFIG_MIRROR_ROLL, - VR_CONFIG_3D_GEOMETRY_COUNT, - VR_CONFIG_FOV_SCALE, - VR_CONFIG_FORCE_2D, - VR_CONFIG_CANVAS_DISTANCE, + //switching between 2D and 3D + VR_CONFIG_MODE, VR_CONFIG_3D_GEOMETRY_COUNT, VR_CONFIG_FORCE_2D, + //camera setup + VR_CONFIG_FOV_SCALE, VR_CONFIG_CANVAS_DISTANCE, + //6DoF + VR_CONFIG_6DOF_ENABLED, VR_CONFIG_6DOF_SCALE, + VR_CONFIG_MIRROR_AXIS_X, VR_CONFIG_MIRROR_AXIS_Y, VR_CONFIG_MIRROR_AXIS_Z, + VR_CONFIG_MIRROR_PITCH, VR_CONFIG_MIRROR_YAW, VR_CONFIG_MIRROR_ROLL, + //2D canvas positioning + VR_CONFIG_MENU_PITCH, VR_CONFIG_MENU_YAW, VR_CONFIG_RECENTER_YAW, + //mouse cursor + VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y, + //viewport size + VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, + + //end VR_CONFIG_MAX }; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 6cc3ea0885..482b864ea5 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1066,7 +1066,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, } if (IsVRBuild()) { - UpdateVRInput(NativeKey, g_Config.bHapticFeedback); + UpdateVRInput(NativeKey, NativeTouch, g_Config.bHapticFeedback, dp_xscale, dp_yscale); } } From 22db37d128fd75e7628a8369633b64a5b82fef13 Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 1 Sep 2022 19:43:43 +0200 Subject: [PATCH 36/99] OpenXR - Viewport init better --- Common/VR/PPSSPPVR.cpp | 7 ++++++- Common/VR/VRRenderer.h | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index d4ab5471cc..e66e87d991 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -111,7 +111,7 @@ void EnterVR(bool firstStart) { VR_EnterVR(VR_GetEngine()); IN_VRInit(VR_GetEngine()); } - VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported()); + VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, false); } void GetVRResolutionPerEye(int* width, int* height) { @@ -220,6 +220,11 @@ void BindVRFramebuffer() { } bool PreVRRender() { + if (!VR_GetConfig(VR_CONFIG_VIEWPORT_VALID)) { + VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported()); + VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true); + } + if (VR_BeginFrame(VR_GetEngine())) { // Decide if the scene is 3D or not diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h index 5527480bc3..82173a060b 100644 --- a/Common/VR/VRRenderer.h +++ b/Common/VR/VRRenderer.h @@ -16,8 +16,8 @@ enum VRConfig { VR_CONFIG_MENU_PITCH, VR_CONFIG_MENU_YAW, VR_CONFIG_RECENTER_YAW, //mouse cursor VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y, - //viewport size - VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, + //viewport setup + VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, VR_CONFIG_VIEWPORT_VALID, //end VR_CONFIG_MAX From bda42e5772271fbf1c38bb747c976aef38432578 Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 1 Sep 2022 19:56:38 +0200 Subject: [PATCH 37/99] compile fix --- Common/VR/PPSSPPVR.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h index 4b6b6774c2..76dafc6d07 100644 --- a/Common/VR/PPSSPPVR.h +++ b/Common/VR/PPSSPPVR.h @@ -30,7 +30,7 @@ inline bool IsVRBuild() { return false; } inline void InitVROnAndroid(void* vm, void* activity, int version, char* name) {} inline void EnterVR(bool firstTime) {} inline void GetVRResolutionPerEye(int* width, int* height) {} -inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) {} +inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) {} inline void UpdateVRScreenKey(const KeyInput &key) {} // VR rendering integration From 5200208a846e1f8360ebb7fc7f3ec34ea3846b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 1 Sep 2022 20:44:00 +0200 Subject: [PATCH 38/99] Revert "Merge pull request #15930 from sum2012/kernel-minor" This reverts commit fd863beb963e1dd5ef299f225104b0117310d4ee, reversing changes made to 59813fff6dd0995bced9b365e526f33fbda4a05d. --- Core/HLE/sceKernelMutex.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 915baa9e81..899230142b 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -657,10 +657,8 @@ int sceKernelUnlockMutex(SceUID id, int count) if (mutex->nm.lockLevel == 0) { - if (__KernelUnlockMutex(mutex, error)) { + if (__KernelUnlockMutex(mutex, error)) hleReSchedule("mutex unlocked"); - return hleDelayResult(0, "unlock", 150); - } } return 0; From 1033fa6cd79a05feb7f5eae83e7735b0efff0141 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Sep 2022 18:52:52 -0700 Subject: [PATCH 39/99] Vulkan: Prevent saving tex during replace load. --- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index e72464a469..c903451deb 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -628,7 +628,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, stride / bpp); VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT); } - if (replacer_.Enabled()) { + if (replacer_.Enabled() && plan.replaced->IsInvalid()) { // When hardware texture scaling is enabled, this saves the original. int w = dataScaled ? mipWidth : mipUnscaledWidth; int h = dataScaled ? mipHeight : mipUnscaledHeight; From c5fbb400de9e92ae30a96e16bebedb6083858577 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Sep 2022 19:05:20 -0700 Subject: [PATCH 40/99] Replacement: Detect OOM even with replacements. Also allow saving video textures if so configured. --- GPU/Common/TextureCacheCommon.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index acce603667..599c39c9db 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2380,7 +2380,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } } - if (plan.isVideo || isPPGETexture) { + if (isPPGETexture) { plan.replaced = &replacer_.FindNone(); plan.replaceValid = false; } else { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index c903451deb..d11e92ba05 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -508,7 +508,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { image->SetTag(texName); bool allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); - if (!allocSuccess && !lowMemoryMode_ && !replacer_.Enabled()) { + if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; decimationCounter_ = 0; @@ -628,7 +628,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, stride / bpp); VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT); } - if (replacer_.Enabled() && plan.replaced->IsInvalid()) { + // Format might be wrong in lowMemoryMode_, so don't save. + if (replacer_.Enabled() && plan.replaced->IsInvalid() && !lowMemoryMode_) { // When hardware texture scaling is enabled, this saves the original. int w = dataScaled ? mipWidth : mipUnscaledWidth; int h = dataScaled ? mipHeight : mipUnscaledHeight; From cbeb6c147744f38c19b46d31376469a3cbeffb9c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Sep 2022 19:17:04 -0700 Subject: [PATCH 41/99] Replacement: Fix reload of replaced textures. Were showing as ready before they were ready after decimate, causing flickering. --- Core/TextureReplacer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 4544fdcd72..fb6b436661 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -970,6 +970,7 @@ void ReplacedTexture::PrepareData(int level) { void ReplacedTexture::PurgeIfOlder(double t) { if (lastUsed_ < t && (!threadWaitable_ || threadWaitable_->WaitFor(0.0))) { levelData_.clear(); + initDone_ = false; } } From 9f48ddb18ca36bee7fc1ac034e9c9c2c3cc8b030 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Sep 2022 23:11:58 -0700 Subject: [PATCH 42/99] vertexjit: Clamp through float pos during decode. Rather than decoding it on read, better to decode it right away. --- GPU/Common/VertexDecoderArm.cpp | 3 +-- GPU/Common/VertexDecoderArm64.cpp | 23 +++++++++++++++++++++-- GPU/Common/VertexDecoderCommon.cpp | 12 +++++++----- GPU/Common/VertexDecoderCommon.h | 9 ++------- GPU/Common/VertexDecoderX86.cpp | 30 ++++++++++++++++++++++++++++-- 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index 52cefc0d71..c2f6ae46fd 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -136,7 +136,6 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through}, {&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through}, - {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat}, {&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8}, {&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16}, @@ -882,7 +881,7 @@ void VertexDecoderJitCache::Jit_PosS8Through() { // TODO: SIMD LDRSB(tempReg1, srcReg, dec_->posoff); LDRSB(tempReg2, srcReg, dec_->posoff + 1); - LDRSB(tempReg3, srcReg, dec_->posoff + 2); // signed? + LDRB(tempReg3, srcReg, dec_->posoff + 2); static const ARMReg tr[3] = { tempReg1, tempReg2, tempReg3 }; static const ARMReg fr[3] = { fpScratchReg, fpScratchReg2, fpScratchReg3 }; ADD(scratchReg, dstReg, dec_->decFmt.posoff); diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index 305ba3ed90..32cc5bc291 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -113,7 +113,7 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through}, {&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through}, - {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat}, + {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloatThrough}, {&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8}, {&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16}, @@ -670,7 +670,7 @@ void VertexDecoderJitCache::Jit_PosFloat() { void VertexDecoderJitCache::Jit_PosS8Through() { LDRSB(INDEX_UNSIGNED, tempReg1, srcReg, dec_->posoff); LDRSB(INDEX_UNSIGNED, tempReg2, srcReg, dec_->posoff + 1); - LDRSB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->posoff + 2); // signed? + LDRB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->posoff + 2); fp.SCVTF(fpScratchReg, tempReg1); fp.SCVTF(fpScratchReg2, tempReg2); fp.SCVTF(fpScratchReg3, tempReg3); @@ -691,6 +691,25 @@ void VertexDecoderJitCache::Jit_PosS16Through() { STR(INDEX_UNSIGNED, src[1], dstReg, dec_->decFmt.posoff + 8); } +void VertexDecoderJitCache::Jit_PosFloatThrough() { + // Instead of just copying 12 bytes, we copy 8 and clamp Z. + if ((dec_->posoff & 7) == 0 && (dec_->decFmt.posoff & 7) == 0) { + LDR(INDEX_UNSIGNED, EncodeRegTo64(tempReg1), srcReg, dec_->posoff); + STR(INDEX_UNSIGNED, EncodeRegTo64(tempReg1), dstReg, dec_->decFmt.posoff); + } else { + LDP(INDEX_SIGNED, tempReg1, tempReg2, srcReg, dec_->posoff); + STP(INDEX_SIGNED, tempReg1, tempReg2, dstReg, dec_->decFmt.posoff); + } + + fp.LDUR(32, neonScratchRegD, srcReg, dec_->posoff + 8); + fp.FCVTZU(32, neonScratchRegD, neonScratchRegD); + // Narrow to 16 bit, saturating meanwhile. + fp.UQXTN(16, neonScratchRegD, neonScratchRegD); + fp.UXTL(16, neonScratchRegD, neonScratchRegD); + fp.UCVTF(32, neonScratchRegD, neonScratchRegD); + fp.STUR(32, neonScratchRegD, dstReg, dec_->decFmt.posoff + 8); +} + void VertexDecoderJitCache::Jit_NormalS8() { LDURH(tempReg1, srcReg, dec_->nrmoff); LDRB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->nrmoff + 2); diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index c6a4631636..64ef34aa80 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -776,10 +776,11 @@ void VertexDecoder::Step_PosFloatSkin() const void VertexDecoder::Step_PosS8Through() const { float *v = (float *)(decoded_ + decFmt.posoff); - const s8 *sv = (const s8*)(ptr_ + posoff); + const s8 *sv = (const s8 *)(ptr_ + posoff); + const u8 *uv = (const u8 *)(ptr_ + posoff); v[0] = sv[0]; v[1] = sv[1]; - v[2] = sv[2]; + v[2] = uv[2]; } void VertexDecoder::Step_PosS16Through() const @@ -794,9 +795,10 @@ void VertexDecoder::Step_PosS16Through() const void VertexDecoder::Step_PosFloatThrough() const { - u8 *v = (u8 *)(decoded_ + decFmt.posoff); - const u8 *fv = (const u8 *)(ptr_ + posoff); - memcpy(v, fv, 12); + float *v = (float *)(decoded_ + decFmt.posoff); + const float *fv = (const float *)(ptr_ + posoff); + memcpy(v, fv, 8); + v[2] = fv[2] > 65535.0f ? 65535.0f : (fv[2] < 0.0f ? 0.0f : fv[2]); } void VertexDecoder::Step_PosS8Morph() const diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index b80c18c2dc..257e7751ba 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -131,8 +131,7 @@ public: pos[2] = f[2]; } else { // Integer value passed in a float. Clamped to 0, 65535. - const float z = (int)f[2] * (1.0f / 65535.0f); - pos[2] = z > 1.0f ? 1.0f : (z < 0.0f ? 0.0f : z); + pos[2] = (int)f[2] * (1.0f / 65535.0f); } } break; @@ -179,11 +178,6 @@ public: { const float *f = (const float *)(data_ + decFmt_.posoff); memcpy(pos, f, 12); - if (isThrough()) { - // Integer value passed in a float. Clamped to 0, 65535. - const float z = (int)pos[2]; - pos[2] = z > 65535.0f ? 65535.0f : (z < 0.0f ? 0.0f : z); - } } break; case DEC_S16_3: @@ -666,6 +660,7 @@ public: void Jit_PosFloat(); void Jit_PosS8Through(); void Jit_PosS16Through(); + void Jit_PosFloatThrough(); void Jit_PosS8Skin(); void Jit_PosS16Skin(); diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index d16c3fd047..611bf56c21 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -138,7 +138,7 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through}, {&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through}, - {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat}, + {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloatThrough}, {&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8}, {&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16}, @@ -1348,7 +1348,10 @@ void VertexDecoderJitCache::Jit_PosS8Through() { DEBUG_LOG_REPORT_ONCE(vertexS8Through, G3D, "Using S8 positions in throughmode"); // SIMD doesn't really matter since this isn't useful on hardware. for (int i = 0; i < 3; i++) { - MOVSX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); + if (i == 2) + MOVZX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); + else + MOVSX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); CVTSI2SS(fpScratchReg, R(tempReg1)); MOVSS(MDisp(dstReg, dec_->decFmt.posoff + i * 4), fpScratchReg); } @@ -1377,6 +1380,29 @@ void VertexDecoderJitCache::Jit_PosS16Through() { } } +void VertexDecoderJitCache::Jit_PosFloatThrough() { + PXOR(fpScratchReg2, R(fpScratchReg2)); + if (cpu_info.Mode64bit) { + MOV(64, R(tempReg1), MDisp(srcReg, dec_->posoff)); + MOVSS(fpScratchReg, MDisp(srcReg, dec_->posoff + 8)); + MOV(64, MDisp(dstReg, dec_->decFmt.posoff), R(tempReg1)); + } else { + MOV(32, R(tempReg1), MDisp(srcReg, dec_->posoff)); + MOV(32, R(tempReg2), MDisp(srcReg, dec_->posoff + 4)); + MOVSS(fpScratchReg, MDisp(srcReg, dec_->posoff + 8)); + MOV(32, MDisp(dstReg, dec_->decFmt.posoff), R(tempReg1)); + MOV(32, MDisp(dstReg, dec_->decFmt.posoff + 4), R(tempReg2)); + } + + CVTTPS2DQ(fpScratchReg, R(fpScratchReg)); + // Use pack to saturate to 0,65535. + PACKUSDW(fpScratchReg, R(fpScratchReg)); + PUNPCKLWD(fpScratchReg, R(fpScratchReg2)); + CVTDQ2PS(fpScratchReg, R(fpScratchReg)); + + MOVSS(MDisp(dstReg, dec_->decFmt.posoff + 8), fpScratchReg); +} + void VertexDecoderJitCache::Jit_PosS8() { Jit_AnyS8ToFloat(dec_->posoff); MOVUPS(MDisp(dstReg, dec_->decFmt.posoff), XMM3); From f2d5d668a51deabcab2e8104409592d958b487ea Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Sep 2022 23:15:22 -0700 Subject: [PATCH 43/99] vertexjit: Remove unused ReadPos() cases. --- GPU/Common/VertexDecoderCommon.cpp | 2 + GPU/Common/VertexDecoderCommon.h | 102 ++++------------------------- 2 files changed, 14 insertions(+), 90 deletions(-) diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 64ef34aa80..e8d3310eed 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -1264,6 +1264,8 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, ERROR_LOG_REPORT(G3D, "Vertices without position found: (%08x) %s", fmt_, temp); } + _assert_msg_(decFmt.posfmt == DEC_FLOAT_3, "Reader only supports float pos"); + // Attempt to JIT as well. But only do that if the main CPU JIT is enabled, in order to aid // debugging attempts - if the main JIT doesn't work, this one won't do any better, probably. if (jitCache && g_Config.bVertexDecoderJit && g_Config.iCpuCore == (int)CPUCore::JIT) { diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 257e7751ba..a12eb250c4 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -121,100 +121,22 @@ public: VertexReader(u8 *base, const DecVtxFormat &decFmt, int vtype) : base_(base), data_(base), decFmt_(decFmt), vtype_(vtype) {} void ReadPos(float pos[3]) const { - switch (decFmt_.posfmt) { - case DEC_FLOAT_3: - { - const float *f = (const float *)(data_ + decFmt_.posoff); - pos[0] = f[0]; - pos[1] = f[1]; - if (!isThrough()) { - pos[2] = f[2]; - } else { - // Integer value passed in a float. Clamped to 0, 65535. - pos[2] = (int)f[2] * (1.0f / 65535.0f); - } - } - break; - case DEC_S16_3: - { - // X and Y are signed 16 bit, Z is unsigned 16 bit - const s16 *s = (const s16 *)(data_ + decFmt_.posoff); - const u16 *u = (const u16 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = s[i]; - pos[2] = u[2] * (1.0f / 65535.0f); - } else { - for (int i = 0; i < 3; i++) - pos[i] = s[i] * (1.0f / 32768.0f); - } - } - break; - case DEC_S8_3: - { - // X and Y are signed 8 bit, Z is unsigned 8 bit - const s8 *b = (const s8 *)(data_ + decFmt_.posoff); - const u8 *u = (const u8 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = b[i]; - pos[2] = u[2] * (1.0f / 255.0f); - } else { - for (int i = 0; i < 3; i++) - pos[i] = b[i] * (1.0f / 128.0f); - } - } - break; - default: - ERROR_LOG_REPORT_ONCE(fmtpos, G3D, "Reader: Unsupported Pos Format %d", decFmt_.posfmt); - memset(pos, 0, sizeof(float) * 3); - break; + // Only DEC_FLOAT_3 is supported. + const float *f = (const float *)(data_ + decFmt_.posoff); + pos[0] = f[0]; + pos[1] = f[1]; + if (!isThrough()) { + pos[2] = f[2]; + } else { + // Integer value passed in a float. Clamped to 0, 65535. + pos[2] = (int)f[2] * (1.0f / 65535.0f); } } void ReadPosThroughZ16(float pos[3]) const { - switch (decFmt_.posfmt) { - case DEC_FLOAT_3: - { - const float *f = (const float *)(data_ + decFmt_.posoff); - memcpy(pos, f, 12); - } - break; - case DEC_S16_3: - { - // X and Y are signed 16 bit, Z is unsigned 16 bit - const s16 *s = (const s16 *)(data_ + decFmt_.posoff); - const u16 *u = (const u16 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = s[i]; - pos[2] = u[2]; - } else { - for (int i = 0; i < 3; i++) - pos[i] = s[i] * (1.0f / 32768.0f); - } - } - break; - case DEC_S8_3: - { - // X and Y are signed 8 bit, Z is unsigned 8 bit - const s8 *b = (const s8 *)(data_ + decFmt_.posoff); - const u8 *u = (const u8 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = b[i]; - pos[2] = u[2]; - } else { - for (int i = 0; i < 3; i++) - pos[i] = b[i] * (1.0f / 128.0f); - } - } - break; - default: - ERROR_LOG_REPORT_ONCE(fmtz16, G3D, "Reader: Unsupported Pos Format %d", decFmt_.posfmt); - memset(pos, 0, sizeof(float) * 3); - break; - } + // Only DEC_FLOAT_3 is supported. + const float *f = (const float *)(data_ + decFmt_.posoff); + memcpy(pos, f, 12); } void ReadNrm(float nrm[3]) const { From 470d2f0f4efe18652f3bcf6b738d8511cea332fd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 1 Sep 2022 23:22:52 -0700 Subject: [PATCH 44/99] vertexjit: Remove unused ReadUV() cases. --- GPU/Common/VertexDecoderCommon.cpp | 1 + GPU/Common/VertexDecoderCommon.h | 34 ++++-------------------------- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index e8d3310eed..6ac9806c01 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -1265,6 +1265,7 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, } _assert_msg_(decFmt.posfmt == DEC_FLOAT_3, "Reader only supports float pos"); + _assert_msg_(decFmt.uvfmt == DEC_FLOAT_2 || decFmt.uvfmt == DEC_NONE, "Reader only supports float UV"); // Attempt to JIT as well. But only do that if the main CPU JIT is enabled, in order to aid // debugging attempts - if the main JIT doesn't work, this one won't do any better, probably. diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index a12eb250c4..3d6377a86c 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -171,36 +171,10 @@ public: } void ReadUV(float uv[2]) const { - switch (decFmt_.uvfmt) { - case DEC_U8_2: - { - const u8 *b = (const u8 *)(data_ + decFmt_.uvoff); - uv[0] = b[0] * (1.f / 128.f); - uv[1] = b[1] * (1.f / 128.f); - } - break; - - case DEC_U16_2: - { - const u16 *s = (const u16 *)(data_ + decFmt_.uvoff); - uv[0] = s[0] * (1.f / 32768.f); - uv[1] = s[1] * (1.f / 32768.f); - } - break; - - case DEC_FLOAT_2: - { - const float *f = (const float *)(data_ + decFmt_.uvoff); - uv[0] = f[0]; - uv[1] = f[1]; - } - break; - - default: - ERROR_LOG_REPORT_ONCE(fmtuv, G3D, "Reader: Unsupported UV Format %d", decFmt_.uvfmt); - memset(uv, 0, sizeof(float) * 2); - break; - } + // Only DEC_FLOAT_2 is supported. + const float *f = (const float *)(data_ + decFmt_.uvoff); + uv[0] = f[0]; + uv[1] = f[1]; } void ReadColor0(float color[4]) const { From 5e2ecb5ebb8c2350ed63d648c59f3a8a7488f046 Mon Sep 17 00:00:00 2001 From: Lubos Date: Fri, 2 Sep 2022 12:40:41 +0200 Subject: [PATCH 45/99] OpenXR - FBO intialization better --- Common/VR/VRBase.h | 3 +- Common/VR/VRFramebuffer.cpp | 82 +++++++++++++++---------------------- 2 files changed, 35 insertions(+), 50 deletions(-) diff --git a/Common/VR/VRBase.h b/Common/VR/VRBase.h index 138c8f09f4..2d0be5d8d2 100644 --- a/Common/VR/VRBase.h +++ b/Common/VR/VRBase.h @@ -96,8 +96,9 @@ typedef struct { uint32_t TextureSwapChainLength; uint32_t TextureSwapChainIndex; ovrSwapChain ColorSwapChain; + ovrSwapChain DepthSwapChain; XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; - unsigned int* DepthBuffers; + XrSwapchainImageOpenGLESKHR* DepthSwapChainImage; unsigned int* FrameBuffers; bool Acquired; } ovrFramebuffer; diff --git a/Common/VR/VRFramebuffer.cpp b/Common/VR/VRFramebuffer.cpp index df9b4eebea..0f13150b3e 100644 --- a/Common/VR/VRFramebuffer.cpp +++ b/Common/VR/VRFramebuffer.cpp @@ -33,8 +33,12 @@ void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; frameBuffer->ColorSwapChain.Width = 0; frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->DepthSwapChain.Width = 0; + frameBuffer->DepthSwapChain.Height = 0; + frameBuffer->DepthSwapChainImage = NULL; - frameBuffer->DepthBuffers = NULL; frameBuffer->FrameBuffers = NULL; frameBuffer->Acquired = false; } @@ -62,8 +66,6 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w XrSwapchainCreateInfo swapChainCreateInfo; memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo)); swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO; - swapChainCreateInfo.format = GL_RGBA8; - swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; swapChainCreateInfo.sampleCount = 1; swapChainCreateInfo.width = width; swapChainCreateInfo.height = height; @@ -73,69 +75,55 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + frameBuffer->DepthSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->DepthSwapChain.Height = swapChainCreateInfo.height; // Create the color swapchain. + swapChainCreateInfo.format = GL_RGBA8; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); OXR(xrEnumerateSwapchainImages(frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + // Create the depth swapchain. + swapChainCreateInfo.format = GL_DEPTH24_STENCIL8; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->DepthSwapChain.Handle)); + frameBuffer->DepthSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + // Populate the swapchain image array. for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; frameBuffer->ColorSwapChainImage[i].next = NULL; + frameBuffer->DepthSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->DepthSwapChainImage[i].next = NULL; } OXR(xrEnumerateSwapchainImages( frameBuffer->ColorSwapChain.Handle, frameBuffer->TextureSwapChainLength, &frameBuffer->TextureSwapChainLength, (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + OXR(xrEnumerateSwapchainImages( + frameBuffer->DepthSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->DepthSwapChainImage)); - frameBuffer->DepthBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); frameBuffer->FrameBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + const GLuint depthTexture = frameBuffer->DepthSwapChainImage[i].image; + + // Create the frame buffer. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); if (multiview) { - // Create the color buffer texture. - GLenum textureTarget = GL_TEXTURE_2D_ARRAY; - GL(glBindTexture(textureTarget, colorTexture)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GL(glBindTexture(textureTarget, 0)); - - // Create depth buffer. - GL(glGenTextures(1, &frameBuffer->DepthBuffers[i])); - GL(glBindTexture(textureTarget, frameBuffer->DepthBuffers[i])); - GL(glTexStorage3D(textureTarget, 1, GL_DEPTH_COMPONENT24, width, height, 2)); - GL(glBindTexture(textureTarget, 0)); - - // Create the frame buffer. - GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); - GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); - GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, frameBuffer->DepthBuffers[i], 0, 0, 2)); + GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexture, 0, 0, 2)); + GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 0, 2)); GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0, 0, 2)); } else { - // Create the color buffer texture. - GLenum colorTextureTarget = GL_TEXTURE_2D; - GL(glBindTexture(colorTextureTarget, colorTexture)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GL(glBindTexture(colorTextureTarget, 0)); - - // Create depth buffer. - GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); - GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); - GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height)); - GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); - - // Create the frame buffer. - GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); - GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); - GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); - GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0)); + GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0)); GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); } GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); @@ -151,14 +139,10 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); - if (frameBuffer->Multiview) { - GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); - } else { - GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); - } OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + OXR(xrDestroySwapchain(frameBuffer->DepthSwapChain.Handle)); free(frameBuffer->ColorSwapChainImage); - free(frameBuffer->DepthBuffers); + free(frameBuffer->DepthSwapChainImage); free(frameBuffer->FrameBuffers); ovrFramebuffer_Clear(frameBuffer); From c784c0e94be4d32531a42e0eaeb17531a717ecd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 2 Sep 2022 21:07:29 +0200 Subject: [PATCH 46/99] Some renaming. Store the BlendState/MaskState. --- GPU/Common/DrawEngineCommon.h | 3 +++ GPU/Common/GPUStateUtils.cpp | 38 +++++++++++++++---------------- GPU/Common/GPUStateUtils.h | 16 ++++++++++--- GPU/D3D11/StateMappingD3D11.cpp | 10 ++++---- GPU/Directx9/StateMappingDX9.cpp | 10 ++++---- GPU/GLES/StateMappingGLES.cpp | 16 +++++-------- GPU/Vulkan/StateMappingVulkan.cpp | 16 ++++++------- 7 files changed, 56 insertions(+), 53 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index b55bb85cbd..3186766bb7 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -23,6 +23,7 @@ #include "Common/Data/Collections/Hashmaps.h" #include "GPU/GPUState.h" +#include "GPU/Common/GPUStateUtils.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Common/IndexGenerator.h" #include "GPU/Common/VertexDecoderCommon.h" @@ -192,6 +193,8 @@ protected: // Sometimes, unusual situations mean we need to reset dirty flags after state calc finishes. uint64_t dirtyRequiresRecheck_ = 0; + ComputedPipelineState pipelineState_; + // Hardware tessellation TessellationDataTransfer *tessDataTransfer; }; diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index a9f99f793f..43484fcfb4 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -956,7 +956,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS case STENCIL_VALUE_INCR_4: case STENCIL_VALUE_INCR_8: // We'll add the incremented value output by the shader. - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE); blendState.setEquation(blendEq, BlendEq::ADD); break; @@ -964,23 +964,23 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS case STENCIL_VALUE_DECR_4: case STENCIL_VALUE_DECR_8: // We'll subtract the incremented value output by the shader. - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE); blendState.setEquation(blendEq, BlendEq::SUBTRACT); break; case STENCIL_VALUE_INVERT: // The shader will output one, and reverse subtracting will essentially invert. - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE); blendState.setEquation(blendEq, BlendEq::REVERSE_SUBTRACT); break; default: if (srcBlend == BlendFactor::ONE && dstBlend == BlendFactor::ZERO && blendEq == BlendEq::ADD) { - blendState.enabled = false; + blendState.blendEnabled = false; } else { - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ZERO); blendState.setEquation(blendEq, BlendEq::ADD); } @@ -1023,10 +1023,10 @@ bool IsColorWriteMaskComplex(bool allowFramebufferRead) { void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { if (gstate_c.blueToAlpha) { maskState.applyFramebufferRead = false; - maskState.rgba[0] = false; - maskState.rgba[1] = false; - maskState.rgba[2] = false; - maskState.rgba[3] = true; + maskState.maskRGBA[0] = false; + maskState.maskRGBA[1] = false; + maskState.maskRGBA[2] = false; + maskState.maskRGBA[3] = true; return; } @@ -1038,20 +1038,20 @@ void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { int channelMask = colorMask & 0xFF; switch (channelMask) { case 0x0: - maskState.rgba[i] = false; + maskState.maskRGBA[i] = false; break; case 0xFF: - maskState.rgba[i] = true; + maskState.maskRGBA[i] = true; break; default: if (allowFramebufferRead) { // Instead of just 'true', restrict shader bitmasks to Outrun temporarily. // TODO: This check must match the one in IsColorWriteMaskComplex. maskState.applyFramebufferRead = PSP_CoreParameter().compat.flags().ShaderColorBitmask; - maskState.rgba[i] = true; + maskState.maskRGBA[i] = true; } else { // Use the old heuristic. - maskState.rgba[i] = channelMask >= 128; + maskState.maskRGBA[i] = channelMask >= 128; } } colorMask >>= 8; @@ -1059,10 +1059,10 @@ void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { // Let's not write to alpha if stencil isn't enabled. if (IsStencilTestOutputDisabled()) { - maskState.rgba[3] = false; + maskState.maskRGBA[3] = false; } else if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) { // If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel. - maskState.rgba[3] = false; + maskState.maskRGBA[3] = false; } } @@ -1101,12 +1101,12 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, case REPLACE_BLEND_BLUE_TO_ALPHA: blueToAlpha = true; - blendState.enabled = gstate.isAlphaBlendEnabled(); + blendState.blendEnabled = gstate.isAlphaBlendEnabled(); // We'll later convert the color blend to blend in the alpha channel. break; case REPLACE_BLEND_COPY_FBO: - blendState.enabled = true; + blendState.blendEnabled = true; blendState.applyFramebufferRead = true; blendState.resetFramebufferRead = false; blendState.replaceAlphaWithStencil = replaceAlphaWithStencil; @@ -1114,14 +1114,14 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, case REPLACE_BLEND_PRE_SRC: case REPLACE_BLEND_PRE_SRC_2X_ALPHA: - blendState.enabled = true; + blendState.blendEnabled = true; usePreSrc = true; break; case REPLACE_BLEND_STANDARD: case REPLACE_BLEND_2X_ALPHA: case REPLACE_BLEND_2X_SRC: - blendState.enabled = true; + blendState.blendEnabled = true; break; } diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index 1bdd7cce5b..d40c47ff93 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -137,13 +137,18 @@ enum class BlendEq : uint8_t { COUNT }; +// Computed blend setup, including shader stuff. struct GenericBlendState { - bool enabled; bool resetFramebufferRead; bool applyFramebufferRead; bool dirtyShaderBlendFixValues; + + // Shader generation state ReplaceAlphaType replaceAlphaWithStencil; + // Resulting hardware blend state + bool blendEnabled; + BlendFactor srcColor; BlendFactor dstColor; BlendFactor srcAlpha; @@ -181,7 +186,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS struct GenericMaskState { bool applyFramebufferRead; uint32_t uniformMask; // For each bit, opposite to the PSP. - bool rgba[4]; // true = draw, false = don't draw this channel + bool maskRGBA[4]; // true = draw, false = don't draw this channel }; void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead); @@ -197,9 +202,14 @@ struct GenericStencilFuncState { GEStencilOp zFail; GEStencilOp zPass; }; - void ConvertStencilFuncState(GenericStencilFuncState &stencilFuncState); +struct ComputedPipelineState { + GenericBlendState blendState; + GenericMaskState maskState; + // TODO: Add logic and possibly stencil here. +}; + // See issue #15898 inline bool SpongebobDepthInverseConditions(const GenericStencilFuncState &stencilState) { // Check that the depth/stencil state matches the conditions exactly. diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 239d8def45..d4919792c3 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -154,11 +154,9 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } else { keys_.blend.value = 0; - GenericMaskState maskState; + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - // Set blend - unless we need to do it in the shader. - GenericBlendState blendState; ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -186,7 +184,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } - if (blendState.enabled) { + if (blendState.blendEnabled) { keys_.blend.blendEnable = true; keys_.blend.logicOpEnable = false; keys_.blend.blendOpColor = d3d11BlendEqLookup[(size_t)blendState.eqColor]; @@ -219,7 +217,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } } - keys_.blend.colorWriteMask = (maskState.rgba[0] ? 1 : 0) | (maskState.rgba[1] ? 2 : 0) | (maskState.rgba[2] ? 4 : 0) | (maskState.rgba[3] ? 8 : 0); + keys_.blend.colorWriteMask = (maskState.maskRGBA[0] ? 1 : 0) | (maskState.maskRGBA[1] ? 2 : 0) | (maskState.maskRGBA[2] ? 4 : 0) | (maskState.maskRGBA[3] ? 8 : 0); } } diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index bcc2d23b72..d4160438f1 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -132,11 +132,9 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } dxstate.colorMask.set(mask); } else { - GenericMaskState maskState; + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - // Set blend - unless we need to do it in the shader. - GenericBlendState blendState; ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -162,7 +160,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { ResetFramebufferRead(); } - if (blendState.enabled) { + if (blendState.blendEnabled) { dxstate.blend.enable(); dxstate.blendSeparate.enable(); dxstate.blendEquation.set(dxBlendEqLookup[(size_t)blendState.eqColor], dxBlendEqLookup[(size_t)blendState.eqAlpha]); @@ -182,7 +180,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { u32 mask = 0; for (int i = 0; i < 4; i++) { - if (maskState.rgba[i]) + if (maskState.maskRGBA[i]) mask |= 1 << i; } dxstate.colorMask.set(mask); diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 53fd0711b4..256b97f56a 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -153,13 +153,9 @@ void DrawEngineGLES::ApplyDrawState(int prim) { bool alphaMask = gstate.isClearModeAlphaMask(); renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0)); } else { - // Do the large chunks of state conversion. We might be able to hide these two behind a dirty-flag each, - // to avoid recomputing heavy stuff unnecessarily every draw call. - - GenericMaskState maskState; + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - GenericBlendState blendState; ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -189,7 +185,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } - if (blendState.enabled) { + if (blendState.blendEnabled) { if (blendState.dirtyShaderBlendFixValues) { // Not quite sure how necessary this is. dirtyRequiresRecheck_ |= DIRTY_SHADERBLEND; @@ -203,9 +199,9 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } } - int mask = (int)maskState.rgba[0] | ((int)maskState.rgba[1] << 1) | ((int)maskState.rgba[2] << 2) | ((int)maskState.rgba[3] << 3); - if (blendState.enabled) { - renderManager->SetBlendAndMask(mask, blendState.enabled, + int mask = (int)maskState.maskRGBA[0] | ((int)maskState.maskRGBA[1] << 1) | ((int)maskState.maskRGBA[2] << 2) | ((int)maskState.maskRGBA[3] << 3); + if (blendState.blendEnabled) { + renderManager->SetBlendAndMask(mask, blendState.blendEnabled, glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor], glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha], glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]); diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 96c145e6f7..b3abb486f7 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -162,11 +162,9 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag key.logicOp = VK_LOGIC_OP_CLEAR; } - GenericMaskState maskState; + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - // Set blend - unless we need to do it in the shader. - GenericBlendState blendState; ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -181,7 +179,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } - if (blendState.enabled) { + if (blendState.blendEnabled) { key.blendEnable = true; key.blendOpColor = vkBlendEqLookup[(size_t)blendState.eqColor]; key.blendOpAlpha = vkBlendEqLookup[(size_t)blendState.eqAlpha]; @@ -209,10 +207,10 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } key.colorWriteMask = - (maskState.rgba[0] ? VK_COLOR_COMPONENT_R_BIT : 0) | - (maskState.rgba[1] ? VK_COLOR_COMPONENT_G_BIT : 0) | - (maskState.rgba[2] ? VK_COLOR_COMPONENT_B_BIT : 0) | - (maskState.rgba[3] ? VK_COLOR_COMPONENT_A_BIT : 0); + (maskState.maskRGBA[0] ? VK_COLOR_COMPONENT_R_BIT : 0) | + (maskState.maskRGBA[1] ? VK_COLOR_COMPONENT_G_BIT : 0) | + (maskState.maskRGBA[2] ? VK_COLOR_COMPONENT_B_BIT : 0) | + (maskState.maskRGBA[3] ? VK_COLOR_COMPONENT_A_BIT : 0); // Workaround proposed in #10421, for bug where the color write mask is not applied correctly on Adreno. if ((gstate.pmskc & 0x00FFFFFF) == 0x00FFFFFF && g_Config.bVendorBugChecksEnabled && draw_->GetBugs().Has(Draw::Bugs::COLORWRITEMASK_BROKEN_WITH_DEPTHTEST)) { From b84cda2876c868b87ac4ab37f2ef6a6f9566cadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 2 Sep 2022 22:07:42 +0200 Subject: [PATCH 47/99] Plumb the computed pipeline state into ComputeFragmentShaderID. --- GPU/Common/ShaderId.cpp | 2 +- GPU/Common/ShaderId.h | 4 ++-- GPU/D3D11/DrawEngineD3D11.cpp | 4 ++-- GPU/D3D11/ShaderManagerD3D11.cpp | 4 ++-- GPU/D3D11/ShaderManagerD3D11.h | 2 +- GPU/Directx9/DrawEngineDX9.cpp | 4 ++-- GPU/Directx9/ShaderManagerDX9.cpp | 4 ++-- GPU/Directx9/ShaderManagerDX9.h | 2 +- GPU/GLES/DrawEngineGLES.cpp | 4 ++-- GPU/GLES/ShaderManagerGLES.cpp | 4 ++-- GPU/GLES/ShaderManagerGLES.h | 2 +- GPU/Vulkan/DrawEngineVulkan.cpp | 4 ++-- GPU/Vulkan/ShaderManagerVulkan.cpp | 4 ++-- GPU/Vulkan/ShaderManagerVulkan.h | 2 +- 14 files changed, 23 insertions(+), 23 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 00c39e4b71..60decf45d3 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -246,7 +246,7 @@ std::string FragmentShaderDesc(const FShaderID &id) { // Here we must take all the bits of the gstate that determine what the fragment shader will // look like, and concatenate them together into an ID. -void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) { +void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs) { FShaderID id; if (gstate.isModeClear()) { // We only need one clear shader, so let's ignore the rest of the bits. diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index a105af6746..bfde30c281 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -226,11 +226,11 @@ namespace Draw { class Bugs; } - void ComputeVertexShaderID(VShaderID *id, uint32_t vertexType, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); // Generates a compact string that describes the shader. Useful in a list to get an overview // of the current flora of shaders. std::string VertexShaderDesc(const VShaderID &id); -void ComputeFragmentShaderID(FShaderID *id, const Draw::Bugs &bugs); +struct ComputedPipelineState; +void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs); std::string FragmentShaderDesc(const FShaderID &id); diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 1822fc5256..bc3c67da1d 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -538,7 +538,7 @@ rotateVBO: D3D11VertexShader *vshader; D3D11FragmentShader *fshader; - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform, useHWTessellation_, decOptions_.expandAllWeightsToFloat); + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, useHWTransform, useHWTessellation_, decOptions_.expandAllWeightsToFloat); ID3D11InputLayout *inputLayout = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType()); context_->PSSetShader(fshader->GetShader(), nullptr, 0); context_->VSSetShader(vshader->GetShader(), nullptr, 0); @@ -648,7 +648,7 @@ rotateVBO: if (result.action == SW_DRAW_PRIMITIVES) { D3D11VertexShader *vshader; D3D11FragmentShader *fshader; - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat); + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); context_->PSSetShader(fshader->GetShader(), nullptr, 0); context_->VSSetShader(vshader->GetShader(), nullptr, 0); shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering()); diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index c141c1ed5b..34c8980ad0 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -177,7 +177,7 @@ void ShaderManagerD3D11::BindUniforms() { context_->PSSetConstantBuffers(0, 1, ps_cbs); } -void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { +void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { VShaderID VSID; FShaderID FSID; @@ -190,7 +190,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader ** if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/D3D11/ShaderManagerD3D11.h b/GPU/D3D11/ShaderManagerD3D11.h index df8f7e4809..0f7a297965 100644 --- a/GPU/D3D11/ShaderManagerD3D11.h +++ b/GPU/D3D11/ShaderManagerD3D11.h @@ -85,7 +85,7 @@ public: ShaderManagerD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context, D3D_FEATURE_LEVEL featureLevel); ~ShaderManagerD3D11(); - void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); + void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); void ClearShaders(); void DirtyLastShader() override; diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 1ab4da1a63..e05df6c98c 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -522,7 +522,7 @@ rotateVBO: ApplyDrawState(prim); ApplyDrawStateLate(); - VSShader *vshader = shaderManager_->ApplyShader(true, useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat); + VSShader *vshader = shaderManager_->ApplyShader(true, useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat, pipelineState_); IDirect3DVertexDeclaration9 *pHardwareVertexDecl = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType()); if (pHardwareVertexDecl) { @@ -613,7 +613,7 @@ rotateVBO: ApplyDrawStateLate(); - VSShader *vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat); + VSShader *vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat, pipelineState_); if (result.action == SW_DRAW_PRIMITIVES) { if (result.setStencil) { diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 7cae12bb53..1700206a02 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -544,7 +544,7 @@ void ShaderManagerDX9::DirtyLastShader() { // disables vertex arrays lastPShader_ = nullptr; } -VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat) { +VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, const ComputedPipelineState &pipelineState) { VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { gstate_c.Clean(DIRTY_VERTEXSHADER_STATE); @@ -556,7 +556,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/Directx9/ShaderManagerDX9.h b/GPU/Directx9/ShaderManagerDX9.h index 04cdf79544..9c7404d856 100644 --- a/GPU/Directx9/ShaderManagerDX9.h +++ b/GPU/Directx9/ShaderManagerDX9.h @@ -78,7 +78,7 @@ public: ~ShaderManagerDX9(); void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected - VSShader *ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat); + VSShader *ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, const ComputedPipelineState &pipelineState); void DirtyShader(); void DirtyLastShader() override; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index ff3eb56a0f..4eb23cd4d4 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -315,7 +315,7 @@ void DrawEngineGLES::DoFlush() { ApplyDrawState(prim); ApplyDrawStateLate(false, 0); - LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, framebufferManager_->UseBufferedRendering()); + LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, lastVType_, framebufferManager_->UseBufferedRendering()); GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt()); render_->BindVertexBuffer(inputLayout, vertexBuffer, vertexBufferOffset); if (useElements) { @@ -405,7 +405,7 @@ void DrawEngineGLES::DoFlush() { ApplyDrawStateLate(result.setStencil, result.stencilValue); - shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, framebufferManager_->UseBufferedRendering()); + shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, lastVType_, framebufferManager_->UseBufferedRendering()); if (result.action == SW_DRAW_PRIMITIVES) { if (result.drawIndexed) { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 6a174332ee..057bf91d4f 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -773,7 +773,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess return vs; } -LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, u32 vertType, bool useBufferedRendering) { +LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, u32 vertType, bool useBufferedRendering) { uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty) { if (lastShader_) @@ -785,7 +785,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index b6f6272d70..b91ba8e7c9 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -159,7 +159,7 @@ public: // This is the old ApplyShader split into two parts, because of annoying information dependencies. // If you call ApplyVertexShader, you MUST call ApplyFragmentShader soon afterwards. Shader *ApplyVertexShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, VShaderID *VSID); - LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, u32 vertType, bool useBufferedRendering); + LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, u32 vertType, bool useBufferedRendering); void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 60997e582a..074f29f18a 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -761,7 +761,7 @@ void DrawEngineVulkan::DoFlush() { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform if (!vshader) { // We're screwed. return; @@ -890,7 +890,7 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index a7fe4b252c..400f4d180c 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -217,7 +217,7 @@ uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) { return dirty; } -void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { +void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { gstate_c.Clean(DIRTY_VERTEXSHADER_STATE); @@ -229,7 +229,7 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 4b167e002b..ff7e5d8c5c 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -89,7 +89,7 @@ public: void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); - void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); + void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); void ClearShaders(); void DirtyShader(); void DirtyLastShader() override; From de712b7804994fa1544693775a7c0d8dc8b66d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 2 Sep 2022 22:20:11 +0200 Subject: [PATCH 48/99] More renaming --- GPU/Common/FragmentShaderGenerator.cpp | 4 +-- GPU/Common/GPUStateUtils.cpp | 40 +++++++++++++------------- GPU/Common/GPUStateUtils.h | 2 +- GPU/Common/ShaderId.cpp | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 8cf705cce5..1a694a5e60 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -121,7 +121,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool useDiscardStencilBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL); - bool readFramebuffer = replaceBlend == REPLACE_BLEND_COPY_FBO || colorWriteMask; + bool readFramebuffer = replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER || colorWriteMask; bool readFramebufferTex = readFramebuffer && !gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH); bool needFragCoord = readFramebuffer || gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); @@ -943,7 +943,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " v.rgb = v.rgb * %s;\n", srcFactor); } - if (replaceBlend == REPLACE_BLEND_COPY_FBO) { + if (replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER) { const char *srcFactor = nullptr; const char *dstFactor = nullptr; diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 43484fcfb4..d781b22a92 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -179,7 +179,7 @@ ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) { return REPLACE_ALPHA_NO; } - if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_COPY_FBO) { + if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_READ_FRAMEBUFFER) { if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) { return REPLACE_ALPHA_YES; } else { @@ -270,14 +270,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma // Let's get the non-factor modes out of the way first. switch (eq) { case GE_BLENDMODE_ABSDIFF: - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_BLENDMODE_MIN: case GE_BLENDMODE_MAX: if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) { return REPLACE_BLEND_STANDARD; } else { - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; } default: @@ -300,19 +300,19 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_2X_ALPHA; // Can't double, we need the source color to be correct. // Doubling only alpha would clamp the src alpha incorrectly. - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: if (bufferFormat == GE_FORMAT_565) return REPLACE_BLEND_2X_ALPHA; - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: // We can't technically do this correctly (due to clamping) without reading the dst color. // Using a copy isn't accurate either, though, when there's overlap. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; case GE_DSTBLEND_DOUBLEINVSRCALPHA: @@ -339,7 +339,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_STANDARD; } // Can't double, we need the source color to be correct. - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: @@ -348,7 +348,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma // Doubling will have no effect here. return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: case GE_DSTBLEND_DOUBLEINVSRCALPHA: @@ -357,7 +357,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma } // Double both src (for dst alpha) and alpha (for dst factor.) // But to be accurate (clamping), we need to read the dst color. - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_SRCALPHA: case GE_DSTBLEND_INVSRCALPHA: @@ -369,7 +369,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_STANDARD; } // We can't technically do this correctly (due to clamping) without reading the dst alpha. - return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_READ_FRAMEBUFFER; } case GE_SRCBLEND_DOUBLEINVDSTALPHA: @@ -383,14 +383,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: case GE_DSTBLEND_DOUBLEINVSRCALPHA: if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_2X_ALPHA; } - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_SRCALPHA: case GE_DSTBLEND_INVSRCALPHA: @@ -401,7 +401,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; } case GE_SRCBLEND_FIXA: @@ -409,7 +409,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma switch (funcB) { case GE_DSTBLEND_DOUBLESRCALPHA: // Can't safely double alpha, will clamp. - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEINVSRCALPHA: // Doubling alpha is safe for the inverse, will clamp to zero either way. @@ -420,7 +420,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_FIXB: default: @@ -454,14 +454,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) { // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; } else { // This means dst alpha/color is used in the src factor. // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?) // We will just hope that doubling alpha for the dst factor will not clamp too badly. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_2X_ALPHA; } @@ -478,7 +478,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; default: return REPLACE_BLEND_STANDARD; @@ -1085,7 +1085,7 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, ReplaceBlendType replaceBlend = ReplaceBlendWithShader(allowFramebufferRead, gstate_c.framebufFormat); if (forceReplaceBlend) { - replaceBlend = REPLACE_BLEND_COPY_FBO; + replaceBlend = REPLACE_BLEND_READ_FRAMEBUFFER; } ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend); bool usePreSrc = false; @@ -1105,7 +1105,7 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, // We'll later convert the color blend to blend in the alpha channel. break; - case REPLACE_BLEND_COPY_FBO: + case REPLACE_BLEND_READ_FRAMEBUFFER: blendState.blendEnabled = true; blendState.applyFramebufferRead = true; blendState.resetFramebufferRead = false; diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index d40c47ff93..b753807f18 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -39,7 +39,7 @@ enum ReplaceBlendType { // Full blend equation runs in shader. // We might have to make a copy of the framebuffer target to read from. - REPLACE_BLEND_COPY_FBO, + REPLACE_BLEND_READ_FRAMEBUFFER, // Color blend mode and color gets copied to alpha blend mode. REPLACE_BLEND_BLUE_TO_ALPHA, diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 60decf45d3..7de9c1feaf 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -269,7 +269,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip // Not ideal! At least we share the code. ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowFramebufferRead, gstate_c.framebufFormat); if (colorWriteMask) { - replaceBlend = REPLACE_BLEND_COPY_FBO; + replaceBlend = REPLACE_BLEND_READ_FRAMEBUFFER; } ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend); From f81a28f6617e60dad3ee69e95e8395de12536b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 2 Sep 2022 22:40:15 +0200 Subject: [PATCH 49/99] Some reordering --- GPU/Vulkan/DrawEngineVulkan.cpp | 4 ++-- GPU/Vulkan/StateMappingVulkan.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 074f29f18a..0ada8b9b06 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -890,11 +890,11 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform - _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform + _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false); diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index b3abb486f7..1752a41730 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -127,7 +127,6 @@ void DrawEngineVulkan::ResetFramebufferRead() { fboTexBound_ = false; } -// TODO: Do this more progressively. No need to compute the entire state if the entire state hasn't changed. // In Vulkan, we simply collect all the state together into a "pipeline key" - we don't actually set any state here // (the caller is responsible for setting the little dynamic state that is supported, dynState). void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState) { From 0fe064d9705b4ec2f8fcc527f21e4c4201441239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 2 Sep 2022 22:43:58 +0200 Subject: [PATCH 50/99] Small steps --- GPU/Common/GPUStateUtils.cpp | 6 ++++++ GPU/Common/GPUStateUtils.h | 1 + 2 files changed, 7 insertions(+) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index d781b22a92..0cde82c378 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1020,9 +1020,12 @@ bool IsColorWriteMaskComplex(bool allowFramebufferRead) { // by modern hardware, we do that. This is 99.9% of the time. // When that's not enough, we fall back on a technique similar to shader blending, // we read from the framebuffer (or a copy of it). +// We also prepare uniformMask so that if doing this in the shader gets forced-on, +// we have the right mask already. void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { if (gstate_c.blueToAlpha) { maskState.applyFramebufferRead = false; + maskState.uniformMask = 0xFF000000; maskState.maskRGBA[0] = false; maskState.maskRGBA[1] = false; maskState.maskRGBA[2] = false; @@ -1033,6 +1036,7 @@ void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { // Invert to convert masks from the PSP's format where 1 is don't draw to PC where 1 is draw. uint32_t colorMask = ~((gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24)); + maskState.uniformMask = colorMask; maskState.applyFramebufferRead = false; for (int i = 0; i < 4; i++) { int channelMask = colorMask & 0xFF; @@ -1087,6 +1091,8 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, if (forceReplaceBlend) { replaceBlend = REPLACE_BLEND_READ_FRAMEBUFFER; } + blendState.replaceBlend = replaceBlend; + ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend); bool usePreSrc = false; diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index b753807f18..3f53aa19fb 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -145,6 +145,7 @@ struct GenericBlendState { // Shader generation state ReplaceAlphaType replaceAlphaWithStencil; + ReplaceBlendType replaceBlend; // Resulting hardware blend state bool blendEnabled; From fd6ab4c49549c799ec1d841617f9e13a0e1ade8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 2 Sep 2022 23:06:41 +0200 Subject: [PATCH 51/99] Avoid recomputing the shaderblend setup in ComputeFragmentShaderID. --- GPU/Common/GPUStateUtils.cpp | 4 ++-- GPU/Common/ShaderId.cpp | 16 ++++++++-------- GPU/Vulkan/StateMappingVulkan.cpp | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 0cde82c378..00ad99ebff 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1085,7 +1085,6 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, blendState.applyFramebufferRead = false; blendState.dirtyShaderBlendFixValues = false; blendState.useBlendColor = false; - blendState.replaceAlphaWithStencil = REPLACE_ALPHA_NO; ReplaceBlendType replaceBlend = ReplaceBlendWithShader(allowFramebufferRead, gstate_c.framebufFormat); if (forceReplaceBlend) { @@ -1094,6 +1093,8 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, blendState.replaceBlend = replaceBlend; ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend); + blendState.replaceAlphaWithStencil = replaceAlphaWithStencil; + bool usePreSrc = false; bool blueToAlpha = false; @@ -1115,7 +1116,6 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, blendState.blendEnabled = true; blendState.applyFramebufferRead = true; blendState.resetFramebufferRead = false; - blendState.replaceAlphaWithStencil = replaceAlphaWithStencil; break; case REPLACE_BLEND_PRE_SRC: diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 7de9c1feaf..00a58cfd40 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -263,15 +263,15 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT; bool useShaderDepal = gstate_c.useShaderDepal; bool useSmoothedDepal = gstate_c.useSmoothedShaderDepal; - bool colorWriteMask = IsColorWriteMaskComplex(gstate_c.allowFramebufferRead); + bool colorWriteMask = pipelineState.maskState.applyFramebufferRead; - // Note how we here recompute some of the work already done in state mapping. - // Not ideal! At least we share the code. - ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowFramebufferRead, gstate_c.framebufFormat); - if (colorWriteMask) { - replaceBlend = REPLACE_BLEND_READ_FRAMEBUFFER; - } - ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend); + ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend; + ReplaceAlphaType stencilToAlpha = pipelineState.blendState.replaceAlphaWithStencil; + + // For debugging, can probably delete soon. + // _assert_(colorWriteMask == IsColorWriteMaskComplex(gstate_c.allowFramebufferRead)); + // _assert_(replaceBlend == ReplaceBlendWithShader(gstate_c.allowFramebufferRead, gstate_c.framebufFormat); + // _assert_(stencilToAlpha == ReplaceAlphaWithStencil(replaceBlend)); // All texfuncs except replace are the same for RGB as for RGBA with full alpha. // Note that checking this means that we must dirty the fragment shader ID whenever textureFullAlpha changes. diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 1752a41730..c758fdb688 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -169,6 +169,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); // The shader takes over the responsibility for blending, so recompute. + // We might still end up using blend to write something to alpha. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); From e9880d39bee2abc29ccde6c6a4e9273049a86b38 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 2 Sep 2022 18:09:47 -0700 Subject: [PATCH 52/99] vertexjit: Update unit test, oops. --- unittest/TestVertexJit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/TestVertexJit.cpp b/unittest/TestVertexJit.cpp index 17e63651d2..e704c09d67 100644 --- a/unittest/TestVertexJit.cpp +++ b/unittest/TestVertexJit.cpp @@ -400,7 +400,7 @@ static bool TestVertexFloatThrough() { dec.Execute(vtype, 0, jit == 1); dec.AssertFloat("TestVertexFloatThrough-TC", 1.0f, -1.0f); dec.AssertFloat("TestVertexFloatThrough-Nrm", 1.0f, 0.5f, -1.0f); - dec.AssertFloat("TestVertexFloatThrough-Pos", 1.0f, 0.5f, -1.0f); + dec.AssertFloat("TestVertexFloatThrough-Pos", 1.0f, 0.5f, 0.0f); } return !dec.HasFailed(); From 2dbd9d8c708015e637309dd25b7120f083a4692d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 2 Sep 2022 20:04:25 -0700 Subject: [PATCH 53/99] unitest: Correct wrong free, initialize data. Prevents valgrind warnings in unittest. --- unittest/TestSoftwareGPUJit.cpp | 4 ++++ unittest/TestVertexJit.cpp | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/unittest/TestSoftwareGPUJit.cpp b/unittest/TestSoftwareGPUJit.cpp index 23292ed214..593ac69f41 100644 --- a/unittest/TestSoftwareGPUJit.cpp +++ b/unittest/TestSoftwareGPUJit.cpp @@ -50,6 +50,8 @@ static bool TestSamplerJit() { u8 **tptr = new u8 *[8]; int *bufw = new int[8]; u8 *clut = new u8[1024]; + memset(clut, 0, 1024); + for (int i = 0; i < 8; ++i) { tptr[i] = new u8[1024 * 1024 * 4]; memset(tptr[i], 0, 1024 * 1024 * 4); @@ -119,6 +121,8 @@ static bool TestPixelJit() { u16 *zb_data = new u16[512 * 2]; fb.as32 = fb_data; depthbuf.as16 = zb_data; + memset(fb_data, 0, sizeof(u32) * 512 * 2); + memset(zb_data, 0, sizeof(u16) * 512 * 2); for (int i = 0; i < count; ) { PixelFuncID id; diff --git a/unittest/TestVertexJit.cpp b/unittest/TestVertexJit.cpp index 17e63651d2..4ba19b2e52 100644 --- a/unittest/TestVertexJit.cpp +++ b/unittest/TestVertexJit.cpp @@ -45,8 +45,8 @@ public: gstate_c.uv.vScale = 1.0f; } ~VertexDecoderTestHarness() { - delete src_; - delete dst_; + delete [] src_; + delete [] dst_; delete cache_; delete dec_; } From 759dc25503571e0e28c02458568d35518e3154db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 10:52:48 +0200 Subject: [PATCH 54/99] langtool: Add easy command to remove a key. --- Tools/langtool/Cargo.lock | 20 ++++++++++---------- Tools/langtool/src/main.rs | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Tools/langtool/Cargo.lock b/Tools/langtool/Cargo.lock index e837567757..6f7ad9ef41 100644 --- a/Tools/langtool/Cargo.lock +++ b/Tools/langtool/Cargo.lock @@ -76,9 +76,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "proc-macro-error" @@ -106,18 +106,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -174,9 +174,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-segmentation" diff --git a/Tools/langtool/src/main.rs b/Tools/langtool/src/main.rs index d3ffa95193..8507bc5723 100644 --- a/Tools/langtool/src/main.rs +++ b/Tools/langtool/src/main.rs @@ -19,6 +19,7 @@ enum Command { CommentUnknownLines {}, RemoveUnknownLines {}, MoveKey { old: String, new: String, key: String }, + RemoveKey { section: String, key: String }, } fn copy_missing_lines(reference_ini: &IniFile, target_ini: &mut IniFile) -> io::Result<()> { @@ -79,6 +80,20 @@ fn move_key( Ok(()) } +fn remove_key( + target_ini: &mut IniFile, + section: &str, + key: &str, +) -> io::Result<()> { + // Insert any missing full sections. + if let Some(old_section) = target_ini.get_section_mut(section) { + let _ = old_section.remove_line(key); + } else { + println!("No section {}", section); + } + Ok(()) +} + fn main() { let opt = Opt::from_args(); @@ -130,6 +145,9 @@ fn main() { Command::MoveKey { ref old, ref new,ref key, } => { move_key(&mut target_ini, &old, &new, &key).unwrap(); } + Command::RemoveKey { ref section, ref key } => { + remove_key(&mut target_ini, section, key).unwrap(); + } } target_ini.write().unwrap(); From af54ff79417f5c326c71ba335bde04ef873f599e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 10:53:47 +0200 Subject: [PATCH 55/99] langtool: Run fmt, remove wrong comments --- Tools/langtool/src/inifile.rs | 4 ++-- Tools/langtool/src/main.rs | 42 ++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Tools/langtool/src/inifile.rs b/Tools/langtool/src/inifile.rs index 8669175c9b..ac109af0f7 100644 --- a/Tools/langtool/src/inifile.rs +++ b/Tools/langtool/src/inifile.rs @@ -1,6 +1,6 @@ -use std::path::{Path, PathBuf}; -use std::io::{self, Write}; use std::fs::File; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; use crate::section::Section; diff --git a/Tools/langtool/src/main.rs b/Tools/langtool/src/main.rs index 8507bc5723..6b99a272d3 100644 --- a/Tools/langtool/src/main.rs +++ b/Tools/langtool/src/main.rs @@ -18,13 +18,20 @@ enum Command { CopyMissingLines {}, CommentUnknownLines {}, RemoveUnknownLines {}, - MoveKey { old: String, new: String, key: String }, - RemoveKey { section: String, key: String }, + MoveKey { + old: String, + new: String, + key: String, + }, + RemoveKey { + section: String, + key: String, + }, } fn copy_missing_lines(reference_ini: &IniFile, target_ini: &mut IniFile) -> io::Result<()> { - // Insert any missing full sections. for reference_section in &reference_ini.sections { + // Insert any missing full sections. if !target_ini.insert_section_if_missing(reference_section) { if let Some(target_section) = target_ini.get_section_mut(&reference_section.name) { for line in &reference_section.lines { @@ -44,7 +51,6 @@ fn deal_with_unknown_lines( target_ini: &mut IniFile, remove: bool, ) -> io::Result<()> { - // Insert any missing full sections. for reference_section in &reference_ini.sections { if let Some(target_section) = target_ini.get_section_mut(&reference_section.name) { if remove { @@ -57,13 +63,7 @@ fn deal_with_unknown_lines( Ok(()) } -fn move_key( - target_ini: &mut IniFile, - old: &str, - new: &str, - key: &str, -) -> io::Result<()> { - // Insert any missing full sections. +fn move_key(target_ini: &mut IniFile, old: &str, new: &str, key: &str) -> io::Result<()> { if let Some(old_section) = target_ini.get_section_mut(old) { if let Some(line) = old_section.remove_line(key) { if let Some(new_section) = target_ini.get_section_mut(new) { @@ -80,12 +80,7 @@ fn move_key( Ok(()) } -fn remove_key( - target_ini: &mut IniFile, - section: &str, - key: &str, -) -> io::Result<()> { - // Insert any missing full sections. +fn remove_key(target_ini: &mut IniFile, section: &str, key: &str) -> io::Result<()> { if let Some(old_section) = target_ini.get_section_mut(section) { let _ = old_section.remove_line(key); } else { @@ -142,10 +137,17 @@ fn main() { Command::RemoveUnknownLines {} => { deal_with_unknown_lines(&reference_ini, &mut target_ini, true).unwrap(); } - Command::MoveKey { ref old, ref new,ref key, } => { - move_key(&mut target_ini, &old, &new, &key).unwrap(); + Command::MoveKey { + ref old, + ref new, + ref key, + } => { + move_key(&mut target_ini, &old, &new, &key).unwrap(); } - Command::RemoveKey { ref section, ref key } => { + Command::RemoveKey { + ref section, + ref key, + } => { remove_key(&mut target_ini, section, key).unwrap(); } } From 68d88726aec5a8428108b0b49f16d4aabc3fa7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 01:17:04 +0200 Subject: [PATCH 56/99] Remove unused function --- GPU/Common/GPUStateUtils.cpp | 27 --------------------------- GPU/Common/GPUStateUtils.h | 1 - 2 files changed, 28 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 00ad99ebff..9ed25471ff 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -988,33 +988,6 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS } } -bool IsColorWriteMaskComplex(bool allowFramebufferRead) { - // Restrict to Outrun temporarily (by uglily reusing the ReinterpretFramebuffers flag) - // This check must match the one in ConvertMaskState. - if (!allowFramebufferRead || !PSP_CoreParameter().compat.flags().ShaderColorBitmask) { - // Don't have a choice - we'll make do but it won't always be right. - return false; - } - - if (gstate_c.blueToAlpha) { - // We'll generate a simple ___A mask. - return false; - } - - uint32_t colorMask = (gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24); - - for (int i = 0; i < 4; i++) { - switch (colorMask & 0xFF) { - case 0x0: - case 0xFF: - break; - default: - return true; - } - colorMask >>= 8; - } - return false; -} // If we can we emulate the colorMask by simply toggling the full R G B A masks offered // by modern hardware, we do that. This is 99.9% of the time. diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index 3f53aa19fb..f930d49ede 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -191,7 +191,6 @@ struct GenericMaskState { }; void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead); -bool IsColorWriteMaskComplex(bool allowFramebufferRead); struct GenericStencilFuncState { bool enabled; From 8c56abd3dc96cbe01a12f3a362b6d100729d2ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 01:22:32 +0200 Subject: [PATCH 57/99] Remove setting "Disable slower framebuffer effects" --- Core/Config.cpp | 1 - Core/Config.h | 1 - GPU/Common/GPUStateUtils.cpp | 55 ++++++++++++++----------------- GPU/Common/GPUStateUtils.h | 6 ++-- GPU/Common/ShaderId.cpp | 4 +-- GPU/D3D11/StateMappingD3D11.cpp | 5 ++- GPU/Directx9/StateMappingDX9.cpp | 5 ++- GPU/GLES/StateMappingGLES.cpp | 6 ++-- GPU/GPUState.h | 7 ---- GPU/Vulkan/StateMappingVulkan.cpp | 5 ++- UI/GameSettingsScreen.cpp | 3 -- assets/lang/ar_AE.ini | 3 +- assets/lang/az_AZ.ini | 3 +- assets/lang/bg_BG.ini | 3 +- assets/lang/ca_ES.ini | 3 +- assets/lang/cz_CZ.ini | 3 +- assets/lang/da_DK.ini | 3 +- assets/lang/de_DE.ini | 13 ++++---- assets/lang/dr_ID.ini | 3 +- assets/lang/en_US.ini | 1 - assets/lang/es_ES.ini | 3 +- assets/lang/es_LA.ini | 11 +++---- assets/lang/fa_IR.ini | 3 +- assets/lang/fi_FI.ini | 3 +- assets/lang/fr_FR.ini | 3 +- assets/lang/gl_ES.ini | 3 +- assets/lang/gr_EL.ini | 3 +- assets/lang/he_IL.ini | 3 +- assets/lang/he_IL_invert.ini | 3 +- assets/lang/hr_HR.ini | 3 +- assets/lang/hu_HU.ini | 3 +- assets/lang/id_ID.ini | 13 ++++---- assets/lang/it_IT.ini | 5 ++- assets/lang/ja_JP.ini | 3 +- assets/lang/jv_ID.ini | 3 +- assets/lang/ko_KR.ini | 1 - assets/lang/lo_LA.ini | 3 +- assets/lang/lt-LT.ini | 3 +- assets/lang/ms_MY.ini | 3 +- assets/lang/nl_NL.ini | 3 +- assets/lang/no_NO.ini | 3 +- assets/lang/pl_PL.ini | 3 +- assets/lang/pt_BR.ini | 9 +++-- assets/lang/pt_PT.ini | 11 +++---- assets/lang/ro_RO.ini | 3 +- assets/lang/ru_RU.ini | 3 +- assets/lang/sv_SE.ini | 3 +- assets/lang/tg_PH.ini | 5 ++- assets/lang/th_TH.ini | 1 - assets/lang/tr_TR.ini | 3 +- assets/lang/uk_UA.ini | 3 +- assets/lang/vi_VN.ini | 3 +- assets/lang/zh_CN.ini | 5 ++- assets/lang/zh_TW.ini | 1 - libretro/libretro.cpp | 3 -- 55 files changed, 100 insertions(+), 170 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 512e463a9b..54216c31cd 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -934,7 +934,6 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("ShaderChainRequires60FPS", &g_Config.bShaderChainRequires60FPS, false, true, true), ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true), - ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableShaderBlending, false, true, true), ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true), ConfigSetting("GfxDebugOutput", &g_Config.bGfxDebugOutput, false, false, false), diff --git a/Core/Config.h b/Core/Config.h index ea1d7e0783..16de8394bd 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -232,7 +232,6 @@ public: float fGameListScrollPosition; int iBloomHack; //0 = off, 1 = safe, 2 = balanced, 3 = aggressive bool bBlockTransferGPU; - bool bDisableShaderBlending; bool bFragmentTestCache; int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High bool bHardwareTessellation; diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 9ed25471ff..93db4f15b0 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -257,7 +257,7 @@ StencilValueType ReplaceAlphaWithStencilType() { return STENCIL_VALUE_KEEP; } -ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferFormat bufferFormat) { +ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat) { if (gstate_c.blueToAlpha) { return REPLACE_BLEND_BLUE_TO_ALPHA; } @@ -270,14 +270,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma // Let's get the non-factor modes out of the way first. switch (eq) { case GE_BLENDMODE_ABSDIFF: - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_BLENDMODE_MIN: case GE_BLENDMODE_MAX: if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) { return REPLACE_BLEND_STANDARD; } else { - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; } default: @@ -300,19 +300,19 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_2X_ALPHA; // Can't double, we need the source color to be correct. // Doubling only alpha would clamp the src alpha incorrectly. - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: if (bufferFormat == GE_FORMAT_565) return REPLACE_BLEND_2X_ALPHA; - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: // We can't technically do this correctly (due to clamping) without reading the dst color. // Using a copy isn't accurate either, though, when there's overlap. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; case GE_DSTBLEND_DOUBLEINVSRCALPHA: @@ -339,7 +339,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_STANDARD; } // Can't double, we need the source color to be correct. - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: @@ -348,7 +348,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma // Doubling will have no effect here. return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: case GE_DSTBLEND_DOUBLEINVSRCALPHA: @@ -357,7 +357,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma } // Double both src (for dst alpha) and alpha (for dst factor.) // But to be accurate (clamping), we need to read the dst color. - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_SRCALPHA: case GE_DSTBLEND_INVSRCALPHA: @@ -369,7 +369,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_STANDARD; } // We can't technically do this correctly (due to clamping) without reading the dst alpha. - return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; } case GE_SRCBLEND_DOUBLEINVDSTALPHA: @@ -383,14 +383,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: case GE_DSTBLEND_DOUBLEINVSRCALPHA: if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_2X_ALPHA; } - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_SRCALPHA: case GE_DSTBLEND_INVSRCALPHA: @@ -401,7 +401,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; } case GE_SRCBLEND_FIXA: @@ -409,7 +409,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma switch (funcB) { case GE_DSTBLEND_DOUBLESRCALPHA: // Can't safely double alpha, will clamp. - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEINVSRCALPHA: // Doubling alpha is safe for the inverse, will clamp to zero either way. @@ -420,7 +420,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_FIXB: default: @@ -454,14 +454,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) { // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; } else { // This means dst alpha/color is used in the src factor. // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?) // We will just hope that doubling alpha for the dst factor will not clamp too badly. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_2X_ALPHA; } @@ -478,7 +478,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_READ_FRAMEBUFFER; + return REPLACE_BLEND_READ_FRAMEBUFFER; default: return REPLACE_BLEND_STANDARD; @@ -988,14 +988,13 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS } } - // If we can we emulate the colorMask by simply toggling the full R G B A masks offered // by modern hardware, we do that. This is 99.9% of the time. // When that's not enough, we fall back on a technique similar to shader blending, // we read from the framebuffer (or a copy of it). // We also prepare uniformMask so that if doing this in the shader gets forced-on, // we have the right mask already. -void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { +void ConvertMaskState(GenericMaskState &maskState) { if (gstate_c.blueToAlpha) { maskState.applyFramebufferRead = false; maskState.uniformMask = 0xFF000000; @@ -1021,15 +1020,9 @@ void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { maskState.maskRGBA[i] = true; break; default: - if (allowFramebufferRead) { - // Instead of just 'true', restrict shader bitmasks to Outrun temporarily. - // TODO: This check must match the one in IsColorWriteMaskComplex. - maskState.applyFramebufferRead = PSP_CoreParameter().compat.flags().ShaderColorBitmask; - maskState.maskRGBA[i] = true; - } else { - // Use the old heuristic. - maskState.maskRGBA[i] = channelMask >= 128; - } + maskState.applyFramebufferRead = PSP_CoreParameter().compat.flags().ShaderColorBitmask; + maskState.maskRGBA[i] = true; + break; } colorMask >>= 8; } @@ -1044,7 +1037,7 @@ void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { } // Called even if AlphaBlendEnable == false - it also deals with stencil-related blend state. -void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, bool forceReplaceBlend) { +void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) { // Blending is a bit complex to emulate. This is due to several reasons: // // * Doubled blend modes (src, dst, inversed) aren't supported in OpenGL. @@ -1059,7 +1052,7 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, blendState.dirtyShaderBlendFixValues = false; blendState.useBlendColor = false; - ReplaceBlendType replaceBlend = ReplaceBlendWithShader(allowFramebufferRead, gstate_c.framebufFormat); + ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.framebufFormat); if (forceReplaceBlend) { replaceBlend = REPLACE_BLEND_READ_FRAMEBUFFER; } diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index f930d49ede..60dce4da80 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -60,7 +60,7 @@ bool IsStencilTestOutputDisabled(); StencilValueType ReplaceAlphaWithStencilType(); ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend); -ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bufferFormat); +ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat); LogicOpReplaceType ReplaceLogicOpType(); @@ -181,7 +181,7 @@ struct GenericBlendState { } }; -void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend, bool forceReplaceBlend); +void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend); void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState); struct GenericMaskState { @@ -190,7 +190,7 @@ struct GenericMaskState { bool maskRGBA[4]; // true = draw, false = don't draw this channel }; -void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead); +void ConvertMaskState(GenericMaskState &maskState); struct GenericStencilFuncState { bool enabled; diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 00a58cfd40..e5ca1aad80 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -269,8 +269,8 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip ReplaceAlphaType stencilToAlpha = pipelineState.blendState.replaceAlphaWithStencil; // For debugging, can probably delete soon. - // _assert_(colorWriteMask == IsColorWriteMaskComplex(gstate_c.allowFramebufferRead)); - // _assert_(replaceBlend == ReplaceBlendWithShader(gstate_c.allowFramebufferRead, gstate_c.framebufFormat); + // _assert_(colorWriteMask == IsColorWriteMaskComplex()); + // _assert_(replaceBlend == ReplaceBlendWithShader(gstate_c.framebufFormat); // _assert_(stencilToAlpha == ReplaceAlphaWithStencil(replaceBlend)); // All texfuncs except replace are the same for RGB as for RGBA with full alpha. diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index d4919792c3..b905cb35ab 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -142,7 +142,6 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); // Blend if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { keys_.blend.value = 0; // full wipe keys_.blend.blendEnable = false; @@ -156,8 +155,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + ConvertMaskState(maskState); + ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index d4160438f1..a7dafacea6 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -119,7 +119,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { dxstate.blend.disable(); // Color Mask @@ -134,8 +133,8 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } else { GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + ConvertMaskState(maskState); + ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 256b97f56a..273c75e630 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -145,8 +145,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); - if (gstate.isModeClear()) { // Color Test bool colorMask = gstate.isClearModeColorMask(); @@ -155,8 +153,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } else { GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + ConvertMaskState(maskState); + ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 9f7f98df48..5b011374ac 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -550,12 +550,6 @@ struct GPUStateCache { Dirty(DIRTY_TEXCLAMP); } } - void SetAllowFramebufferRead(bool allow) { - if (allowFramebufferRead != allow) { - allowFramebufferRead = allow; - Dirty(DIRTY_FRAGMENTSHADER_STATE); - } - } void SetTextureIs3D(bool is3D) { if (is3D != curTextureIs3D) { curTextureIs3D = is3D; @@ -583,7 +577,6 @@ struct GPUStateCache { bool bgraTexture; bool needShaderTexClamp; - bool allowFramebufferRead; float morphWeights[8]; u32 deferredVertTypeDirty; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index c758fdb688..c249aefbb8 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -135,7 +135,6 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { key.logicOpEnable = false; key.logicOp = VK_LOGIC_OP_CLEAR; @@ -163,8 +162,8 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + ConvertMaskState(maskState); + ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index def6924c12..259b12b00b 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -507,9 +507,6 @@ void GameSettingsScreen::CreateViews() { }); texSecondary_->SetDisabledPtr(&g_Config.bSoftwareRendering); - CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableShaderBlending, gr->T("Disable slower effects (speedup)"))); - framebufferSlowEffects->SetDisabledPtr(&g_Config.bSoftwareRendering); - // Seems solid, so we hide the setting. /*CheckBox *vtxJit = graphicsSettings->Add(new CheckBox(&g_Config.bVertexDecoderJit, gr->T("Vertex Decoder JIT"))); diff --git a/assets/lang/ar_AE.ini b/assets/lang/ar_AE.ini index 05c6b51ce8..20d525747b 100644 --- a/assets/lang/ar_AE.ini +++ b/assets/lang/ar_AE.ini @@ -498,7 +498,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1207,7 +1206,7 @@ YYYYMMDD = ‎سنة شهر يوم [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/az_AZ.ini b/assets/lang/az_AZ.ini index 669ddeabd8..26b022eb25 100644 --- a/assets/lang/az_AZ.ini +++ b/assets/lang/az_AZ.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/bg_BG.ini b/assets/lang/bg_BG.ini index b730dd6242..1c2ba43a4e 100644 --- a/assets/lang/bg_BG.ini +++ b/assets/lang/bg_BG.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = ггггммдд [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ca_ES.ini b/assets/lang/ca_ES.ini index 5c222da16f..1f823575e8 100644 --- a/assets/lang/ca_ES.ini +++ b/assets/lang/ca_ES.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/cz_CZ.ini b/assets/lang/cz_CZ.ini index 6f3cbd0efe..6682c46ee5 100644 --- a/assets/lang/cz_CZ.ini +++ b/assets/lang/cz_CZ.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Zakázat pomalejší efekty (zrychlení) Disabled = Disabled Display layout editor = Zobrazit editor rozvržení Display Resolution (HW scaler) = Rozlišení obrazovky (Hardwarové zvětšení) @@ -1199,7 +1198,7 @@ YYYYMMDD = RRRRMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/da_DK.ini b/assets/lang/da_DK.ini index 23d3c0a24d..406dee1abd 100644 --- a/assets/lang/da_DK.ini +++ b/assets/lang/da_DK.ini @@ -490,7 +490,6 @@ Deposterize Tip = Retter visuel banding fejl in opskalerede textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Deaktiver langsommere effekter (hurtigere) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Skærmopløsning (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/de_DE.ini b/assets/lang/de_DE.ini index 0cba36ee2e..0903a464a1 100644 --- a/assets/lang/de_DE.ini +++ b/assets/lang/de_DE.ini @@ -57,7 +57,7 @@ Gesture = Geste Gesture mapping = Gesten mapping Glowing borders = Leuchtende Ränder HapticFeedback = Haptisches Feedback (Vibration) -Hide touch analog stick background circle = Verstecke Kreis des touch analog Stick Hintergrundes +Hide touch analog stick background circle = Verstecke Kreis des touch analog Stick Hintergrundes Icon = Symbol Ignore gamepads when not focused = Ignoriere Gamepads falls nicht fokusiert Ignore Windows Key = Ignoriere Windowstaste @@ -490,7 +490,6 @@ Deposterize Tip = Behebt visuelle Artifakte von hochskalierten Texturen Device = Gerät Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Deaktiviere langsame Effekte (schneller) Disabled = Deaktiviert Display layout editor = Bildschirmlayout Editor Display Resolution (HW scaler) = Bildschirmauflösung (HW Skalierung) @@ -539,7 +538,7 @@ Postprocessing effect = Nachbearbeitungs-Effekte Postprocessing Shader = Nachbearbeitungs-Shader Recreate Activity = Recreate activity Render duplicate frames to 60hz = Render duplicate frames to 60 Hz -RenderDuplicateFrames Tip = Kann Framerate smoother in Spielen machen, die eine niedrige Framerate haben. +RenderDuplicateFrames Tip = Kann Framerate smoother in Spielen machen, die eine niedrige Framerate haben. Rendering Mode = Rendermodus Rendering Resolution = Renderauflösung RenderingMode NonBuffered Tip = Schneller, aber keine Anzeige in einigen Spielen @@ -720,9 +719,9 @@ Wlan = WLAN Already contains PSP data = Enthält bereits PSP Daten Create or Choose a PSP folder = Create or Choose a PSP folder Current = Current -DataCanBeShared = Daten können mit PPSSPP Regulär/Gold geteilt werden! -DataCannotBeShared = Daten können NICHT mit Regulär/Gold geteilt werden! -DataWillBeLostOnUninstall = Warnung! Dateien gehen verloren beim deinstallieren von PPSSPP. +DataCanBeShared = Daten können mit PPSSPP Regulär/Gold geteilt werden! +DataCannotBeShared = Daten können NICHT mit Regulär/Gold geteilt werden! +DataWillBeLostOnUninstall = Warnung! Dateien gehen verloren beim deinstallieren von PPSSPP. DataWillStay = Dateien gehen NICHT verloren beim deinstallieren von PPSSPP. Done! = Done! EasyUSBAccess = Easy USB access @@ -1199,7 +1198,7 @@ YYYYMMDD = JJJJMMTT [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/dr_ID.ini b/assets/lang/dr_ID.ini index 7b429a9621..d0176fe3a9 100644 --- a/assets/lang/dr_ID.ini +++ b/assets/lang/dr_ID.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index 2d696f4ee4..b3389a229b 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -514,7 +514,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) diff --git a/assets/lang/es_ES.ini b/assets/lang/es_ES.ini index 7111db4d95..a251e5cbb7 100644 --- a/assets/lang/es_ES.ini +++ b/assets/lang/es_ES.ini @@ -490,7 +490,6 @@ Deposterize Tip = Arregla pequeños errores en las texturas causadas por el esca Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desactivar efectos más lentos (rápido) Disabled = Desactivado Display layout editor = Editor del área de pantalla Display Resolution (HW scaler) = Resolución de pantalla (escalado por hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = Año/Mes/Día [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/es_LA.ini b/assets/lang/es_LA.ini index 0ec79ab54e..46ad0c39e3 100644 --- a/assets/lang/es_LA.ini +++ b/assets/lang/es_LA.ini @@ -29,7 +29,7 @@ Auto = Automática Auto-centering analog stick = Auto-centrado del análogo Auto-hide buttons after seconds = Ocultar botones después de (en segundos) Auto-switch = Auto-switch -Base tilt position = Posición de inclinación de la base +Base tilt position = Posición de inclinación de la base Binds = Asignaciones Button Binding = Vincular botones Button Opacity = Transparencia de botones @@ -90,7 +90,7 @@ Screen Rotation = Rotación de pantalla seconds, 0 : off = Segundos, 0 = No Sensitivity (scale) = Sensibilidad (escala) Sensitivity = Sensibilidad -Shape = Forma +Shape = Forma Show Touch Pause Menu Button = Mostrar botón del menú Swipe = Deslizar hacia Swipe sensitivity = Sensibilidad del deslizado @@ -101,7 +101,7 @@ Tilt Input Type = Tipo de control del acelerómetro Tilt Sensitivity along X axis = Sensibilidad de inclinación del eje X Tilt Sensitivity along Y axis = Sensibilidad de inclinación del eje Y To Calibrate = Para calibrar, pon el aparato en una superficie plana y presiona "Calibrar". -Toggle mode = Modo de alternancia +Toggle mode = Modo de alternancia Touch Control Visibility = Visibilidad de pantalla táctil Use custom right analog = Usar Analog derecho personalizado Use Mouse Control = Usar el control del ratón @@ -490,7 +490,6 @@ Deposterize Tip = Arregla glitches de banda visual en texturas causadas por el e Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desactivar efectos más lentos (rápido) Disabled = Apagado Display layout editor = Editor del área de pantalla Display Resolution (HW scaler) = Resolución de pantalla (escalado por HW) @@ -895,7 +894,7 @@ tools = Software libre utilizado: # Add translators1-6 for up to 6 lines of translator credits. # Leave extra lines blank. 4 contributors per line seems to look best. translators1 = VR0, KAGE-008 -translators2 = +translators2 = translators3 = translators4 = translators5 = @@ -1199,7 +1198,7 @@ YYYYMMDD = Año/Mes/Día [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/fa_IR.ini b/assets/lang/fa_IR.ini index 6f1d4f06ad..4ea5d675dc 100644 --- a/assets/lang/fa_IR.ini +++ b/assets/lang/fa_IR.ini @@ -490,7 +490,6 @@ Deposterize Tip = ‎مشکل نواری شدن در تکسچر های تغیی Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = ‎غیر فعال کردن افکت های کند (افزایش سرعت) Disabled = Disabled Display layout editor = ‎ویرایشگر چیدمان صفحه Display Resolution (HW scaler) = ‎رزولوشن صفحه @@ -1199,7 +1198,7 @@ YYYYMMDD = ‎روز/ماه/سال [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/fi_FI.ini b/assets/lang/fi_FI.ini index c0d6038c50..94dd50c642 100644 --- a/assets/lang/fi_FI.ini +++ b/assets/lang/fi_FI.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/fr_FR.ini b/assets/lang/fr_FR.ini index eb2beec75c..6c28c420e0 100644 --- a/assets/lang/fr_FR.ini +++ b/assets/lang/fr_FR.ini @@ -490,7 +490,6 @@ Deposterize Tip = Corrige le bug graphique qui fait apparaître des bandes dans Device = Appareil Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Désactiver les effets les plus lourds (gain de vitesse) Disabled = Désactivé Display layout editor = Éditeur d'affichage Display Resolution (HW scaler) = Définition d'affichage (mise à l'échelle matérielle) @@ -1190,7 +1189,7 @@ YYYYMMDD = AAAAMMJJ [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/gl_ES.ini b/assets/lang/gl_ES.ini index 767b24ea0a..f644af1246 100644 --- a/assets/lang/gl_ES.ini +++ b/assets/lang/gl_ES.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desactivar efectos máis lentos (rápido) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Resolución de pantalla (escalado por hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = AAAA/MM/DD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/gr_EL.ini b/assets/lang/gr_EL.ini index e8f8413fe9..51df0a1cd9 100644 --- a/assets/lang/gr_EL.ini +++ b/assets/lang/gr_EL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Διορθώνει μικρές ατέλειες υφών με Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Απενεργοποίηση χρονοβόρων εφέ (άυξηση ταχύτητας) Disabled = Απενεργοποιημένο Display layout editor = Επεξεργασία διάταξης οθόνης Display Resolution (HW scaler) = Ανάλυση οθόνης (Κλιμακοτής hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = ΧΧΧΧΜΜΗΗ [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/he_IL.ini b/assets/lang/he_IL.ini index ceee0141a9..95cda4cb26 100644 --- a/assets/lang/he_IL.ini +++ b/assets/lang/he_IL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = יום/חודש/שנה [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/he_IL_invert.ini b/assets/lang/he_IL_invert.ini index e93e0eedbc..ee1ed7aced 100644 --- a/assets/lang/he_IL_invert.ini +++ b/assets/lang/he_IL_invert.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = הנש/שדוח/םוי [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/hr_HR.ini b/assets/lang/hr_HR.ini index c2ecfa8ffa..8238d63b17 100644 --- a/assets/lang/hr_HR.ini +++ b/assets/lang/hr_HR.ini @@ -490,7 +490,6 @@ Deposterize Tip = Popravlja vizualne banding glitcheve u podignutim teksturama Device = Uređaj Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Isključite sporije efekte (ubrzava) Disabled = Isključeno Display layout editor = Prikaz layout uređivača Display Resolution (HW scaler) = Prikaz rezolucije (HW mjeritelj) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/hu_HU.ini b/assets/lang/hu_HU.ini index dabbdcc906..13078c4f7f 100644 --- a/assets/lang/hu_HU.ini +++ b/assets/lang/hu_HU.ini @@ -490,7 +490,6 @@ Deposterize Tip = Skálázásból eredő kisebb textúra hibák megszüntetése. Device = Eszköz Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Lassú effektek letiltása (gyorsítás) Disabled = Letiltva Display layout editor = Elrendezés szerkesztő Display Resolution (HW scaler) = Megjelenítési felbontás (HW skálázó) @@ -1199,7 +1198,7 @@ YYYYMMDD = ÉÉÉÉHHNN [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/id_ID.ini b/assets/lang/id_ID.ini index fb6c9b376b..00af881bf1 100644 --- a/assets/lang/id_ID.ini +++ b/assets/lang/id_ID.ini @@ -490,7 +490,6 @@ Deposterize Tip = Memperbaiki gangguan pita visual pada tekstur yang ditingkatka Device = Perangkat Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Matikan efek lambat (mempercepat) Disabled = Nonaktif Display layout editor = Penyesuaian tata letak tampilan Display Resolution (HW scaler) = Resolusi tampilan (penskala HW) @@ -725,7 +724,7 @@ DataCannotBeShared = Data tidak bisa dibagi antar PPSSPP reguler/emas! DataWillBeLostOnUninstall = Peringatan! Data akan hilang ketika Anda menghapus PPSSPP! DataWillStay = Data akan tetap ada meskipun Anda menghapus PPSSPP. Done! = Selesai! -EasyUSBAccess = Akses mudah USB +EasyUSBAccess = Akses mudah USB Failed to move some files! = Gagal memindahkan beberapa berkas! Failed to save config = Gagal menyimpan konfigurasi Free space = Penyimpanan bebas @@ -746,7 +745,7 @@ Welcome to PPSSPP! = Selamat Datang di PPSSPP! WhatsThis = Apa ini? [Networking] -AdHoc Server = Server Ad Hoc +AdHoc Server = Server Ad Hoc AdhocServer Failed to Bind Port = Server Ad Hoc gagal memuat port AM: Data from Unknown Port = AM: Data dari port tidak dikenal Auto = Otomatis @@ -791,7 +790,7 @@ Network Initialized = Jaringan terinisiasi None = Tidak ada Please change your Port Offset = Harap ubah letak port Anda Port offset = Letak port (0 = kecocokan dengan PSP) -Open PPSSPP Multiplayer Wiki Page = Halaman Wiki PPSSPP Ad-Hoc +Open PPSSPP Multiplayer Wiki Page = Halaman Wiki PPSSPP Ad-Hoc proAdhocServer Address: = Ad Hoc alamat server: Quick Chat 1 = Obrolan cepat 1 Quick Chat 2 = Obrolan cepat 2 @@ -842,7 +841,7 @@ Cartoon = Kartun CatmullRom = Bicubic (Catmull-Rom) Upscaler ColorCorrection = Koreksi Warna Contrast = Kontras -CRT = CRT +CRT = CRT FXAA = Mengurangi Artifak Distorsi (FXAA) Gamma = Gamma Grayscale = Abu-abu @@ -853,7 +852,7 @@ Natural = Warna Natural NaturalA = Warna Natural (tidak kabur) Off = Mati Power = Daya -PSPColor = Warna PSP +PSPColor = Warna PSP Saturation = Saturasi Scanlines = Pindai Garis (CRT) Sharpen = Pertajam @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTT/BB/HH [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/it_IT.ini b/assets/lang/it_IT.ini index ab827b38b7..849b469c39 100644 --- a/assets/lang/it_IT.ini +++ b/assets/lang/it_IT.ini @@ -49,7 +49,7 @@ Customize tilt = Personalizza Inclinazione D-PAD = Croce direzionale Deadzone radius = Raggio della zona morta Disable D-Pad diagonals (4-way touch) = Disattiva Diagonali del D-Pad (Tocco a 4-Vie) -Disable diagonal input = Disabilita input diagonale +Disable diagonal input = Disabilita input diagonale Double tap = Doppio tocco Enable gesture control = Abilita controllo gesti Enable standard shortcut keys = Abilita scorciatoie standard @@ -490,7 +490,6 @@ Deposterize Tip = Sistema i possibili problemi visivi nelle texture upscalate Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disattiva gli effetti più lenti (veloce) Disabled = Disabilitato Display layout editor = Editor Display Display Resolution (HW scaler) = Risoluzione Display (scaler HW) @@ -1199,7 +1198,7 @@ YYYYMMDD = AAAAMMGG [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ja_JP.ini b/assets/lang/ja_JP.ini index b4fdb34460..10c66c47f1 100644 --- a/assets/lang/ja_JP.ini +++ b/assets/lang/ja_JP.ini @@ -490,7 +490,6 @@ Deposterize Tip = アップスケールされたテクスチャのバンディ Device = デバイス Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 重いエフェクトを無効にする (高速化) Disabled = 無効 Display layout editor = 画面のレイアウトを編集する Display Resolution (HW scaler) = 画面解像度 (HWスケーラー) @@ -1199,7 +1198,7 @@ YYYYMMDD = 年/月/日 [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/jv_ID.ini b/assets/lang/jv_ID.ini index 0e9d0b5bc4..016ae0e857 100644 --- a/assets/lang/jv_ID.ini +++ b/assets/lang/jv_ID.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Mateni efek alon (luwih cepet) Disabled = Disabled Display layout editor = Tata letak Tampilan editor Display Resolution (HW scaler) = Resolusi tampilan (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTTBBHH [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ko_KR.ini b/assets/lang/ko_KR.ini index b83bb8a381..d08d55dd14 100644 --- a/assets/lang/ko_KR.ini +++ b/assets/lang/ko_KR.ini @@ -490,7 +490,6 @@ Deposterize Tip = 업스케일링된 텍스처의 시각적 밴딩 결함 수정 Device = 장치 Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 느린 효과 비활성화 (속도 향상) Disabled = 비활성화 Display layout editor = 화면 레이아웃 편집기 Display Resolution (HW scaler) = 화면 해상도(HW 스케일러) diff --git a/assets/lang/lo_LA.ini b/assets/lang/lo_LA.ini index 805339cc3f..dad15456b5 100644 --- a/assets/lang/lo_LA.ini +++ b/assets/lang/lo_LA.ini @@ -490,7 +490,6 @@ Deposterize Tip = ແກ້ໄຂການສະແດງຜົນຜິດພ Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = ປິດເອັບເຟກທີ່ເຮັດໃຫ້ເກມຊ້າ (ໄວຂຶ້ນ) Disabled = Disabled Display layout editor = ແກ້ໄຂຮູບແບບໜ້າຈໍ Display Resolution (HW scaler) = ຄວາມລະອຽດໜ້າຈໍ (ຕາມຮາດແວຣ໌) @@ -1199,7 +1198,7 @@ YYYYMMDD = ປີ/ເດືອນ/ວັນ [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/lt-LT.ini b/assets/lang/lt-LT.ini index 2c99319418..917e171242 100644 --- a/assets/lang/lt-LT.ini +++ b/assets/lang/lt-LT.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Išjungti lėtesnius efektus (greičio didintojas) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Ekrano rezoliucija ("HW" ištiesinimas) @@ -1199,7 +1198,7 @@ YYYYMMDD = MMMMMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ms_MY.ini b/assets/lang/ms_MY.ini index 598c4bdf18..5a6687e035 100644 --- a/assets/lang/ms_MY.ini +++ b/assets/lang/ms_MY.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTTBBHH [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/nl_NL.ini b/assets/lang/nl_NL.ini index 60d35500c7..ad38f550cb 100644 --- a/assets/lang/nl_NL.ini +++ b/assets/lang/nl_NL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Verhelpt visuele streepglitches in opgeschaalde textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Tragere effecten uitschakelen (sneller) Disabled = Disabled Display layout editor = Schermweergave bewerken Display Resolution (HW scaler) = Schermresolutie (hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = JJJJMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/no_NO.ini b/assets/lang/no_NO.ini index 603cf5280f..70163c25c0 100644 --- a/assets/lang/no_NO.ini +++ b/assets/lang/no_NO.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/pl_PL.ini b/assets/lang/pl_PL.ini index 38810bf550..4493f6db83 100644 --- a/assets/lang/pl_PL.ini +++ b/assets/lang/pl_PL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Poprawia banding koloru na przeskalowanych teksturach Device = Urządzenie Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Wył. wolniejsze efekty (przyśpieszenie) Disabled = Wył. Display layout editor = Edytor położenia obrazu Display Resolution (HW scaler) = Rozdzielczość ekranu (skaler sprz.) @@ -1199,7 +1198,7 @@ YYYYMMDD = RRRR/MM/DD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/pt_BR.ini b/assets/lang/pt_BR.ini index 2e1796deb0..6d5b63cdcf 100644 --- a/assets/lang/pt_BR.ini +++ b/assets/lang/pt_BR.ini @@ -514,7 +514,6 @@ Deposterize Tip = Conserta erros gráficos visuais das faixas nas texturas ampli Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desativa efeitos mais lentos (mais rápido) Disabled = Desativado Display layout editor = Exibe o editor dos esquemas Display Resolution (HW scaler) = Resolução da tela (Dimensionador do hardware) @@ -891,10 +890,10 @@ tools = Ferramentas grátis usadas: # Leave extra lines blank. 4 contributors per line seems to look best. translators1 = Papel, gabrielmop, Efraim Lopes, AkiraJkr translators2 = Felipe -translators3 = -translators4 = -translators5 = -translators6 = +translators3 = +translators4 = +translators5 = +translators6 = Twitter @PPSSPP_emu = Twitter @PPSSPP_emu website = Verifique o site da web: written = Escrito em C++ pela velocidade e portabilidade diff --git a/assets/lang/pt_PT.ini b/assets/lang/pt_PT.ini index 54bc9d92f4..b50d6f0d95 100644 --- a/assets/lang/pt_PT.ini +++ b/assets/lang/pt_PT.ini @@ -514,7 +514,6 @@ Deposterize Tip = Conserta erros gráficos visuais das faixas nas texturas ampli Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desativa efeitos mais lentos (mais rápido) Disabled = Desativado Display layout editor = Mostrar o editor dos esquemas Display Resolution (HW scaler) = Resolução da tela (Dimensionador do hardware) @@ -890,11 +889,11 @@ tools = Ferramentas gratuitas usadas: # Add translators1-6 for up to 6 lines of translator credits. # Leave extra lines blank. 4 contributors per line seems to look best. translators1 = MicSuit -translators2 = -translators3 = -translators4 = -translators5 = -translators6 = +translators2 = +translators3 = +translators4 = +translators5 = +translators6 = Twitter @PPSSPP_emu = Twitter @PPSSPP_emu website = Verifique o website: written = PPSSPP foi escrito em C++ pela sua velocidade e portabilidade diff --git a/assets/lang/ro_RO.ini b/assets/lang/ro_RO.ini index 46e5b6e9d4..0ec247f441 100644 --- a/assets/lang/ro_RO.ini +++ b/assets/lang/ro_RO.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Dezactivare efecte lente (mărire viteză) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Rezoluție ecran (scalare HW) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ru_RU.ini b/assets/lang/ru_RU.ini index 09f1af9598..35b73386d5 100644 --- a/assets/lang/ru_RU.ini +++ b/assets/lang/ru_RU.ini @@ -490,7 +490,6 @@ Deposterize Tip = Исправляет полосатость в масштаб Device = Устройство Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Отключить медленные эффекты (быстрее) Disabled = Отключено Display layout editor = Редактор расположения экрана Display Resolution (HW scaler) = Разрешение экрана (аппаратное) @@ -1200,7 +1199,7 @@ YYYYMMDD = ГГГГММДД [TextureShaders] Off = Выкл. TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index ba6b4e043a..56ae1987d1 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Enhet Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Stäng av långsamma effects (speedup) Disabled = Avstängd Display layout editor = Skärmlayouteditor Display Resolution (HW scaler) = Skärmupplösning (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/tg_PH.ini b/assets/lang/tg_PH.ini index 9ad12dc737..5afd25561b 100644 --- a/assets/lang/tg_PH.ini +++ b/assets/lang/tg_PH.ini @@ -18,7 +18,7 @@ Use global volume = Paggamit ng pangkalahatang tunog WASAPI (fast) = WASAPI (Mabilis) [Controls] -Analog Binding = Bigkisin ang analog +Analog Binding = Bigkisin ang analog Analog Limiter = Taga limit ng analog Analog Settings = Ayusin ang analog Analog Stick = Analog stick @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Wag paganahin ang slower effects (pampa-bilis) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTTBBPP [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/th_TH.ini b/assets/lang/th_TH.ini index c4340eae45..c63fa3125b 100644 --- a/assets/lang/th_TH.ini +++ b/assets/lang/th_TH.ini @@ -490,7 +490,6 @@ Deposterize Tip = แก้ไขขอบภาพเบลอ หรือภ Device = การ์ดจอ Direct3D 9 = ไดเร็คท์ 3D 9 Direct3D 11 = ไดเร็คท์ 3D 11 -Disable slower effects (speedup) = ปิดเอฟเฟ็คท์ที่ทำให้เกมช้า (เร่งความเร็ว) Disabled = ปิดการใช้งาน Display layout editor = แก้ไขรูปแบบของหน้าจอ Display Resolution (HW scaler) = ความละเอียดหน้าจอ (ตามฮาร์ดแวร์) diff --git a/assets/lang/tr_TR.ini b/assets/lang/tr_TR.ini index 761b6598bf..d6c04817fd 100644 --- a/assets/lang/tr_TR.ini +++ b/assets/lang/tr_TR.ini @@ -492,7 +492,6 @@ Deposterize Tip = Yeniden ölçeklenen dokulardaki görsel şerit hatalarını d Device = Cihaz Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Daha yavaş efektleri kapatın (daha hızlı) Disabled = Devre dışı Display layout editor = Görüntü Düzeni Düzenleyicisi Display Resolution (HW scaler) = Görüntü Çözünürlüğü (HW scaler) @@ -1200,7 +1199,7 @@ YYYYMMDD = YYYYAAGG [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/uk_UA.ini b/assets/lang/uk_UA.ini index c25ba59a58..b6ec199493 100644 --- a/assets/lang/uk_UA.ini +++ b/assets/lang/uk_UA.ini @@ -490,7 +490,6 @@ Deposterize Tip = Виправляє візуальні глюки в масшт Device = Пристрій Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Вимкнути повільні ефекти (прискорення) Disabled = Вимкнено Display layout editor = Редактор розташування екрану Display Resolution (HW scaler) = Розширення екрану (HW масштабування) @@ -1199,7 +1198,7 @@ YYYYMMDD = РРРРММДД [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/vi_VN.ini b/assets/lang/vi_VN.ini index 040f2f8540..40e6bb7b30 100644 --- a/assets/lang/vi_VN.ini +++ b/assets/lang/vi_VN.ini @@ -490,7 +490,6 @@ Deposterize Tip = Sửa lỗi trục trặc hình ảnh trong textures Device = Thiết bị Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Tắt hiệu ứng làm chậm (tăng tốc) Disabled = Vô hiệu hóa Display layout editor = Chỉnh bố trí hiển thị Display Resolution (HW scaler) = Độ phân giải màn hình (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = NamThangNgay [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/zh_CN.ini b/assets/lang/zh_CN.ini index 93ba6ebe35..486a2d5230 100644 --- a/assets/lang/zh_CN.ini +++ b/assets/lang/zh_CN.ini @@ -490,7 +490,6 @@ Deposterize Tip = 修复纹理被放大时可见的带状错误 Device = 设备 Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 停用较慢的渲染效果(提速) Disabled = 禁用 Display layout editor = 显示布局编辑器 Display Resolution (HW scaler) = 显示分辨率(硬件缩放) @@ -1193,7 +1192,7 @@ Show Memory Stick folder = 显示记忆棒文件夹 [TextureShaders] Off = 关闭 TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] @@ -1222,4 +1221,4 @@ Filtering settings by '%1' = 设置筛选'%1' Find settings = 搜索设置 Filter = 筛选 Clear filter = 清除筛选结果 -No settings matched '%1' = 未找到设置'%1' +No settings matched '%1' = 未找到设置'%1' diff --git a/assets/lang/zh_TW.ini b/assets/lang/zh_TW.ini index 86670da9e3..3559284b19 100644 --- a/assets/lang/zh_TW.ini +++ b/assets/lang/zh_TW.ini @@ -494,7 +494,6 @@ Deposterize Tip = 修復在紋理被放大時可能發生的小紋理內部毛 Device = 設備 Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 停用較慢的效果(提升速度) Disabled = 已禁用 Display layout editor = 自訂版面 Display Resolution (HW scaler) = 顯示解析度(硬體縮放) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index a5a720b7a7..9b4a20bd97 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -563,7 +563,6 @@ static RetroOption ppsspp_lazy_texture_caching("ppsspp_lazy_texture_cachin static RetroOption ppsspp_retain_changed_textures("ppsspp_retain_changed_textures", "Retain changed textures (Speedup, mem hog)", false); static RetroOption ppsspp_force_lag_sync("ppsspp_force_lag_sync", "Force real clock sync (Slower, less lag)", false); static RetroOption ppsspp_spline_quality("ppsspp_spline_quality", "Spline/Bezier curves quality", { {"Low", 0}, {"Medium", 1}, {"High", 2} }); -static RetroOption ppsspp_disable_shader_blending("ppsspp_disable_slow_framebuffer_effects", "Disable shader blending (speedup)", false); static RetroOption ppsspp_enable_wlan("ppsspp_enable_wlan", "Enable Networking/WLAN (beta, may break games)", false); static RetroOption ppsspp_change_mac_address[] = { {"ppsspp_change_mac_address01", "MAC address Pt 1: X-:--:--:--:--:--", MAC_INITIALIZER_LIST}, @@ -699,7 +698,6 @@ void retro_set_environment(retro_environment_t cb) vars.push_back(ppsspp_lazy_texture_caching.GetOptions()); vars.push_back(ppsspp_retain_changed_textures.GetOptions()); vars.push_back(ppsspp_force_lag_sync.GetOptions()); - vars.push_back(ppsspp_disable_shader_blending.GetOptions()); vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions()); vars.push_back(ppsspp_texture_scaling_level.GetOptions()); vars.push_back(ppsspp_texture_scaling_type.GetOptions()); @@ -831,7 +829,6 @@ static void check_variables(CoreParameter &coreParam) ppsspp_retain_changed_textures.Update(&g_Config.bTextureSecondaryCache); ppsspp_force_lag_sync.Update(&g_Config.bForceLagSync); ppsspp_spline_quality.Update(&g_Config.iSplineBezierQuality); - ppsspp_disable_shader_blending.Update(&g_Config.bDisableShaderBlending); ppsspp_inflight_frames.Update(&g_Config.iInflightFrames); const bool do_scaling_type_update = ppsspp_texture_scaling_type.Update(&g_Config.iTexScalingType); const bool do_scaling_level_update = ppsspp_texture_scaling_level.Update(&g_Config.iTexScalingLevel); From c846c2dfa8551976e177436ef7bfa21e87cb374f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 14:47:47 +0200 Subject: [PATCH 58/99] Remove confusing resetFramebufferRead flag from secondary framebuffer binding --- GPU/Common/DrawEngineCommon.h | 1 - GPU/Common/GPUStateUtils.cpp | 4 ---- GPU/Common/GPUStateUtils.h | 1 - GPU/D3D11/DrawEngineD3D11.h | 1 - GPU/D3D11/StateMappingD3D11.cpp | 22 +++++++++------------- GPU/Directx9/DrawEngineDX9.h | 3 ++- GPU/Directx9/StateMappingDX9.cpp | 18 +++++++++--------- GPU/GLES/DrawEngineGLES.h | 1 - GPU/GLES/StateMappingGLES.cpp | 26 +++++++++++--------------- GPU/Vulkan/DrawEngineVulkan.h | 2 +- GPU/Vulkan/StateMappingVulkan.cpp | 16 +++++++--------- 11 files changed, 39 insertions(+), 56 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 3186766bb7..a8997454d9 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -187,7 +187,6 @@ protected: GEPrimitiveType prevPrim_ = GE_PRIM_INVALID; // Shader blending state - bool fboTexNeedsBind_ = false; bool fboTexBound_ = false; // Sometimes, unusual situations mean we need to reset dirty flags after state calc finishes. diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 93db4f15b0..a78bad276b 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1067,7 +1067,6 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) { switch (replaceBlend) { case REPLACE_BLEND_NO: - blendState.resetFramebufferRead = true; // We may still want to do something about stencil -> alpha. ApplyStencilReplaceAndLogicOpIgnoreBlend(replaceAlphaWithStencil, blendState); return; @@ -1081,7 +1080,6 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) { case REPLACE_BLEND_READ_FRAMEBUFFER: blendState.blendEnabled = true; blendState.applyFramebufferRead = true; - blendState.resetFramebufferRead = false; break; case REPLACE_BLEND_PRE_SRC: @@ -1097,8 +1095,6 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) { break; } - blendState.resetFramebufferRead = true; - const GEBlendMode blendFuncEq = gstate.getBlendEq(); GEBlendSrcFactor blendFuncA = gstate.getBlendFuncA(); GEBlendDstFactor blendFuncB = gstate.getBlendFuncB(); diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index 60dce4da80..dcbb134226 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -139,7 +139,6 @@ enum class BlendEq : uint8_t { // Computed blend setup, including shader stuff. struct GenericBlendState { - bool resetFramebufferRead; bool applyFramebufferRead; bool dirtyShaderBlendFixValues; diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index acfbc01be3..ca7fb04dbb 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -159,7 +159,6 @@ private: void ApplyDrawState(int prim); void ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRef); - void ResetFramebufferRead(); ID3D11InputLayout *SetupDecFmtForDraw(D3D11VertexShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt); diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index b905cb35ab..d764374e05 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -122,12 +122,6 @@ static const D3D11_LOGIC_OP logicOps[] = { D3D11_LOGIC_OP_SET, }; -void DrawEngineD3D11::ResetFramebufferRead() { - if (fboTexBound_) { - fboTexBound_ = false; - } -} - class FramebufferManagerD3D11; class ShaderManagerD3D11; @@ -159,15 +153,15 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { - ApplyFramebufferRead(&fboTexNeedsBind_); + bool fboTexNeedsBind = false; + ApplyFramebufferRead(&fboTexNeedsBind); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); - if (fboTexNeedsBind_) { + if (fboTexNeedsBind) { framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // No sampler required, we do a plain Load in the pixel shader. fboTexBound_ = true; - fboTexNeedsBind_ = false; framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState"); // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. @@ -177,10 +171,12 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); - dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; - gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } else { + if (fboTexBound_) { + fboTexBound_ = false; + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } } if (blendState.blendEnabled) { diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index 616620a412..a0ee23e60d 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -148,7 +148,6 @@ private: void ApplyDrawState(int prim); void ApplyDrawStateLate(); - void ResetFramebufferRead(); IDirect3DVertexDeclaration9 *SetupDecFmtForDraw(VSShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt); @@ -172,4 +171,6 @@ private: TessellationDataTransferDX9 *tessDataTransferDX9; int lastRenderStepId_ = -1; + + bool fboTexNeedsBind_ = false; }; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index a7dafacea6..dd0e7690dc 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -88,10 +88,6 @@ static const D3DSTENCILOP stencilOps[] = { D3DSTENCILOP_KEEP, // reserved }; -inline void DrawEngineDX9::ResetFramebufferRead() { - fboTexBound_ = false; -} - void DrawEngineDX9::ApplyDrawState(int prim) { if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { // nothing to do @@ -137,26 +133,30 @@ void DrawEngineDX9::ApplyDrawState(int prim) { ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { - ApplyFramebufferRead(&fboTexNeedsBind_); + bool fboTexNeedsBind = false; + ApplyFramebufferRead(&fboTexNeedsBind); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); - if (fboTexNeedsBind_) { + if (fboTexNeedsBind) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); fboTexBound_ = true; - fboTexNeedsBind_ = false; dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE; gstate_c.Dirty(DIRTY_BLEND_STATE); } dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); + } else { + if (fboTexBound_) { + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + fboTexBound_ = false; + } } if (blendState.blendEnabled) { diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 8d45ff0d84..d843c83ff4 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -123,7 +123,6 @@ private: void DoFlush(); void ApplyDrawState(int prim); void ApplyDrawStateLate(bool setStencil, int stencilValue); - void ResetFramebufferRead(); GLRInputLayout *SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt); diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 273c75e630..386f6d7f17 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -122,14 +122,6 @@ static const GLushort logicOps[] = { }; #endif -inline void DrawEngineGLES::ResetFramebufferRead() { - if (fboTexBound_) { - GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr); - fboTexBound_ = false; - } -} - void DrawEngineGLES::ApplyDrawState(int prim) { GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); @@ -157,18 +149,18 @@ void DrawEngineGLES::ApplyDrawState(int prim) { ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { - ApplyFramebufferRead(&fboTexNeedsBind_); + bool fboTexNeedsBind = false; + ApplyFramebufferRead(&fboTexNeedsBind); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); // We copy the framebuffer here, as doing so will wipe any blend state if we do it later. - if (fboTexNeedsBind_) { + if (fboTexNeedsBind) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f); fboTexBound_ = true; - fboTexNeedsBind_ = false; framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState"); // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. @@ -177,10 +169,14 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); - dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; - gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } else { + if (fboTexBound_) { + GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr); + fboTexBound_ = false; + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } } if (blendState.blendEnabled) { diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index f89a28b7f9..531e05c4ed 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -195,7 +195,6 @@ private: void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); void BindShaderBlendTex(); - void ResetFramebufferRead(); void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -282,6 +281,7 @@ private: VulkanDynamicState dynState_{}; int tessOffset_ = 0; + bool fboTexNeedsBind_ = false; // Hardware tessellation TessellationDataTransferVulkan *tessDataTransferVulkan; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index c249aefbb8..1955f1b772 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -122,11 +122,6 @@ static const VkLogicOp logicOps[] = { VK_LOGIC_OP_SET, }; -void DrawEngineVulkan::ResetFramebufferRead() { - boundSecondary_ = VK_NULL_HANDLE; - fboTexBound_ = false; -} - // In Vulkan, we simply collect all the state together into a "pipeline key" - we don't actually set any state here // (the caller is responsible for setting the little dynamic state that is supported, dynState). void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState) { @@ -172,10 +167,13 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); - dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; - gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } else { + if (fboTexBound_) { + boundSecondary_ = VK_NULL_HANDLE; + fboTexBound_ = false; + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } } if (blendState.blendEnabled) { From ec0586ba501fdf01dc1223f62d548b4778e242b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Nov 2021 22:34:51 +0100 Subject: [PATCH 59/99] Use promises to hold shader modules, for later asyncification --- Common/GPU/Vulkan/VulkanRenderManager.cpp | 4 +++ Common/Thread/Promise.h | 11 +++++++- GPU/Vulkan/PipelineManagerVulkan.cpp | 8 +++--- GPU/Vulkan/ShaderManagerVulkan.cpp | 31 +++++++++++++---------- GPU/Vulkan/ShaderManagerVulkan.h | 9 ++++--- 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index ba2c64f758..0d857844b6 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -449,6 +449,10 @@ void VulkanRenderManager::CompileThreadFunc() { if (!run_) { break; } + + // TODO: Here we can sort the pending pipelines by vertex and fragment shaders, + // and split up further. + // Those with the same pairs of shaders should be on the same thread. for (auto &entry : toCompile) { switch (entry.type) { case CompileQueueEntry::Type::GRAPHICS: diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index fe2ddb3807..78cfd65f59 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -33,6 +33,7 @@ public: // Has ownership over the data. Single use. // TODO: Split Mailbox (rx_ and tx_) up into separate proxy objects. // NOTE: Poll/BlockUntilReady should only be used from one thread. +// TODO: Make movable? template class Promise { public: @@ -47,6 +48,13 @@ public: return promise; } + static Promise *AlreadyDone(T data) { + Promise *promise = new Promise(); + promise->data_ = data; + promise->ready_ = true; + return promise; + } + ~Promise() { // A promise should have been fulfilled before it's destroyed. _assert_(ready_); @@ -85,7 +93,8 @@ public: private: Promise() {} + // Promise can only be constructed in Spawn. T data_ = nullptr; bool ready_ = false; - Mailbox *rx_; + Mailbox *rx_ = nullptr; }; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 86fa279b9c..f513f6ddd8 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -260,13 +260,13 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; ss[0].pSpecializationInfo = nullptr; - ss[0].module = vs->GetModule(); + ss[0].module = vs->GetModule()->BlockUntilReady(); ss[0].pName = "main"; ss[0].flags = 0; ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; ss[1].pSpecializationInfo = nullptr; - ss[1].module = fs->GetModule(); + ss[1].module = fs->GetModule()->BlockUntilReady(); ss[1].pName = "main"; ss[1].flags = 0; @@ -369,8 +369,8 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * key.raster = rasterKey; key.renderPass = renderPass; key.useHWTransform = useHwTransform; - key.vShader = vs->GetModule(); - key.fShader = fs->GetModule(); + key.vShader = vs->GetModule()->BlockUntilReady(); + key.fShader = fs->GetModule()->BlockUntilReady(); key.vtxFmtId = useHwTransform ? decFmt->id : 0; auto iter = pipelines_.Get(key); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 400f4d180c..5c30151515 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -42,7 +42,7 @@ #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/FramebufferManagerVulkan.h" -VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { +Promise *CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { PROFILE_THIS_SCOPE("shadercomp"); std::string errorMessage; @@ -67,11 +67,10 @@ VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits #endif Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } else { + VkShaderModule shaderModule; + success = vulkan->CreateShaderModule(spirv, &shaderModule); #ifdef SHADERLOG - OutputDebugStringA(LineNumberString(code).c_str()); -#endif -#ifdef SHADERLOG - OutputDebugStringA("OK\n"); + OutputDebugStringA("OK"); #endif } @@ -79,7 +78,7 @@ VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits vulkan->CreateShaderModule(spirv, &shaderModule); } - return shaderModule; + return Promise::AlreadyDone(shaderModule); } @@ -96,8 +95,9 @@ VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, } VulkanFragmentShader::~VulkanFragmentShader() { - if (module_ != VK_NULL_HANDLE) { - vulkan_->Delete().QueueDeleteShaderModule(module_); + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); } } @@ -120,13 +120,14 @@ VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, cons failed_ = true; return; } else { - VERBOSE_LOG(G3D, "Compiled vertex shader:\n%s\n", (const char *)code); + VERBOSE_LOG(G3D, "Compiled fragment shader:\n%s\n", (const char *)code); } } VulkanVertexShader::~VulkanVertexShader() { - if (module_ != VK_NULL_HANDLE) { - vulkan_->Delete().QueueDeleteShaderModule(module_); + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); } } @@ -334,7 +335,9 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade VulkanVertexShader *ShaderManagerVulkan::GetVertexShaderFromModule(VkShaderModule module) { VulkanVertexShader *vs = nullptr; vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *shader) { - if (shader->GetModule() == module) + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) vs = shader; }); return vs; @@ -343,7 +346,9 @@ VulkanVertexShader *ShaderManagerVulkan::GetVertexShaderFromModule(VkShaderModul VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderModule module) { VulkanFragmentShader *fs = nullptr; fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *shader) { - if (shader->GetModule() == module) + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) fs = shader; }); return fs; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index ff7e5d8c5c..5ab37fa723 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -20,6 +20,7 @@ #include #include +#include "Common/Thread/Promise.h" #include "Common/Data/Collections/Hashmaps.h" #include "Common/GPU/Vulkan/VulkanMemory.h" #include "GPU/Common/ShaderCommon.h" @@ -43,11 +44,11 @@ public: bool Failed() const { return failed_; } std::string GetShaderString(DebugShaderStringType type) const; - VkShaderModule GetModule() const { return module_; } + Promise *GetModule() { return module_; } const FShaderID &GetID() { return id_; } protected: - VkShaderModule module_ = VK_NULL_HANDLE; + Promise *module_; VulkanContext *vulkan_; std::string source_; @@ -66,11 +67,11 @@ public: bool UseHWTransform() const { return useHWTransform_; } std::string GetShaderString(DebugShaderStringType type) const; - VkShaderModule GetModule() const { return module_; } + Promise *GetModule() { return module_; } const VShaderID &GetID() { return id_; } protected: - VkShaderModule module_ = VK_NULL_HANDLE; + Promise *module_ = nullptr; VulkanContext *vulkan_; std::string source_; From 010c3b4f4bbeba4284205649b2219e59cfc087e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jun 2022 15:30:05 +0200 Subject: [PATCH 60/99] minor sv translation fix --- assets/lang/sv_SE.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index 56ae1987d1..fbacbb1219 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -1094,8 +1094,8 @@ AVI Dump started. = AVI dump started AVI Dump stopped. = AVI dump stopped Cache ISO in RAM = Cachea hela ISO:n i RAM Change CPU Clock = Ändra CPU-frekvens (kan orsaka instabilitet) -Color Saturation = Color Saturation -Color Tint = Color Tint +Color Saturation = Färgmättnad +Color Tint = Färgförskjutning Error: load undo state is from a different game = Error: load undo state is from a different game Failed to load state for load undo. Error in the file system. = Failed to load state for load undo. Error in the file system. Floating symbols = Floating symbols From fc418ee16f1dd83e50c46d38757f9040d36cd7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jun 2022 16:19:14 +0200 Subject: [PATCH 61/99] Create shader modules (and compile GLSL) on worker threads --- GPU/Vulkan/ShaderManagerVulkan.cpp | 58 +++++++++++++++--------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 5c30151515..80f1db0350 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -43,42 +43,44 @@ #include "GPU/Vulkan/FramebufferManagerVulkan.h" Promise *CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { - PROFILE_THIS_SCOPE("shadercomp"); + return Promise::Spawn(&g_threadManager, [=] { + PROFILE_THIS_SCOPE("shadercomp"); - std::string errorMessage; - std::vector spirv; + std::string errorMessage; + std::vector spirv; - bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage); + bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage); - VkShaderModule shaderModule = VK_NULL_HANDLE; + VkShaderModule shaderModule = VK_NULL_HANDLE; - if (!errorMessage.empty()) { - if (success) { - ERROR_LOG(G3D, "Warnings in shader compilation!"); + if (!errorMessage.empty()) { + if (success) { + ERROR_LOG(G3D, "Warnings in shader compilation!"); + } else { + ERROR_LOG(G3D, "Error in shader compilation!"); + } + ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); + ERROR_LOG(G3D, "Shader source:\n%s", code); +#ifdef SHADERLOG + OutputDebugStringA(LineNumberString(code).c_str()); + OutputDebugStringA("Error messages:\n"); + OutputDebugStringA(errorMessage.c_str()); +#endif + Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } else { - ERROR_LOG(G3D, "Error in shader compilation!"); + VkShaderModule shaderModule; + success = vulkan->CreateShaderModule(spirv, &shaderModule); +#ifdef SHADERLOG + OutputDebugStringA("OK"); +#endif } - ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); - ERROR_LOG(G3D, "Shader source:\n%s", code); -#ifdef SHADERLOG - OutputDebugStringA(LineNumberString(code).c_str()); - OutputDebugStringA("Error messages:\n"); - OutputDebugStringA(errorMessage.c_str()); -#endif - Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); - } else { - VkShaderModule shaderModule; - success = vulkan->CreateShaderModule(spirv, &shaderModule); -#ifdef SHADERLOG - OutputDebugStringA("OK"); -#endif - } - if (success) { - vulkan->CreateShaderModule(spirv, &shaderModule); - } + if (success) { + vulkan->CreateShaderModule(spirv, &shaderModule); + } - return Promise::AlreadyDone(shaderModule); + return shaderModule; + }, TaskType::CPU_COMPUTE); } From e2c740827a97db793ebac6b34531154b418e9594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jun 2022 16:24:19 +0200 Subject: [PATCH 62/99] Use shader module promises as keys in pipeline cache instead of the actual shader modules. Prevents early block. --- GPU/Vulkan/PipelineManagerVulkan.cpp | 8 ++++---- GPU/Vulkan/PipelineManagerVulkan.h | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index f513f6ddd8..72203c208c 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -369,8 +369,8 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * key.raster = rasterKey; key.renderPass = renderPass; key.useHWTransform = useHwTransform; - key.vShader = vs->GetModule()->BlockUntilReady(); - key.fShader = fs->GetModule()->BlockUntilReady(); + key.vShader = vs->GetModule(); + key.fShader = fs->GetModule(); key.vtxFmtId = useHwTransform ? decFmt->id : 0; auto iter = pipelines_.Get(key); @@ -633,8 +633,8 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) { if (failed) return; - VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader); - VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader); + VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader->BlockUntilReady()); + VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader->BlockUntilReady()); if (!vshader || !fshader) { failed = true; return; diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 37d542e9a0..a6ab695cef 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -18,7 +18,9 @@ #pragma once #include + #include "Common/Data/Collections/Hashmaps.h" +#include "Common/Thread/Promise.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/ShaderId.h" @@ -33,8 +35,8 @@ class VulkanRenderManager; struct VulkanPipelineKey { VulkanPipelineRasterStateKey raster; // prim is included here VkRenderPass renderPass; - VkShaderModule vShader; - VkShaderModule fShader; + Promise *vShader; + Promise *fShader; uint32_t vtxFmtId; bool useHWTransform; From 93422f6dea961f9c1bc65f66df813bb9950e30bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jun 2022 23:28:26 +0200 Subject: [PATCH 63/99] Don't block on shader creation until it's time to create the pipeline. --- Common/GPU/Vulkan/VulkanRenderManager.cpp | 28 +++++++++++++++++++++++ Common/GPU/Vulkan/VulkanRenderManager.h | 7 +++++- GPU/Vulkan/PipelineManagerVulkan.cpp | 25 ++------------------ 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 0d857844b6..45e3a78aaa 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -29,6 +29,34 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { // Already failed to create this one. return false; } + + // Fill in the last part of the desc since now it's time to block. + VkShaderModule vs = desc->vertexShader->BlockUntilReady(); + VkShaderModule fs = desc->fragmentShader->BlockUntilReady(); + + if (!vs || !fs) { + ERROR_LOG(G3D, "Failed creating graphics pipeline - missing shader modules"); + // We're kinda screwed here? + return false; + } + + VkPipelineShaderStageCreateInfo ss[2]{}; + ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + ss[0].pSpecializationInfo = nullptr; + ss[0].module = vs; + ss[0].pName = "main"; + ss[0].flags = 0; + ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + ss[1].pSpecializationInfo = nullptr; + ss[1].module = fs; + ss[1].pName = "main"; + ss[1].flags = 0; + + desc->pipe.pStages = ss; + desc->pipe.stageCount = 2; + VkPipeline vkpipeline; VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index a541bea57e..efadc784c6 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -12,6 +12,7 @@ #include #include +#include "Common/Thread/Promise.h" #include "Common/System/Display.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/Data/Convert/SmallDataConvert.h" @@ -121,7 +122,11 @@ struct VKRGraphicsPipelineDesc { VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - VkPipelineShaderStageCreateInfo shaderStageInfo[2]{}; + + // Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish. + Promise *vertexShader; + Promise *fragmentShader; + VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; VkVertexInputAttributeDescription attrs[8]{}; VkVertexInputBindingDescription ibd{}; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 72203c208c..73768e38bc 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -256,28 +256,8 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, ms.pSampleMask = nullptr; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - VkPipelineShaderStageCreateInfo *ss = &desc->shaderStageInfo[0]; - ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - ss[0].pSpecializationInfo = nullptr; - ss[0].module = vs->GetModule()->BlockUntilReady(); - ss[0].pName = "main"; - ss[0].flags = 0; - ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - ss[1].pSpecializationInfo = nullptr; - ss[1].module = fs->GetModule()->BlockUntilReady(); - ss[1].pName = "main"; - ss[1].flags = 0; - - if (!ss[0].module || !ss[1].module) { - ERROR_LOG(G3D, "Failed creating graphics pipeline - bad shaders"); - // Create a placeholder to avoid creating over and over if shader compiler broken. - VulkanPipeline *nullPipeline = new VulkanPipeline(); - nullPipeline->pipeline = VK_NULL_HANDLE; - nullPipeline->flags = 0; - return nullPipeline; - } + desc->fragmentShader = fs->GetModule(); + desc->vertexShader = vs->GetModule(); VkPipelineInputAssemblyStateCreateInfo &inputAssembly = desc->inputAssembly; inputAssembly.flags = 0; @@ -321,7 +301,6 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkGraphicsPipelineCreateInfo &pipe = desc->pipe; pipe.flags = 0; pipe.stageCount = 2; - pipe.pStages = ss; pipe.basePipelineIndex = 0; pipe.pColorBlendState = &desc->cbs; From 8775837c11dcdc94d8e2143f6f1f1247441e2e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jun 2022 23:42:04 +0200 Subject: [PATCH 64/99] Work towards finding the leak --- Common/Thread/Promise.h | 3 +-- GPU/Vulkan/ShaderManagerVulkan.cpp | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index 78cfd65f59..1788eb6a4b 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -59,7 +59,6 @@ public: // A promise should have been fulfilled before it's destroyed. _assert_(ready_); _assert_(!rx_); - delete data_; } // Returns T if the data is ready, nullptr if it's not. @@ -93,7 +92,7 @@ public: private: Promise() {} - // Promise can only be constructed in Spawn. + // Promise can only be constructed in Spawn (or AlreadyDone). T data_ = nullptr; bool ready_ = false; Mailbox *rx_ = nullptr; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 80f1db0350..22ad34afbe 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -100,6 +100,7 @@ VulkanFragmentShader::~VulkanFragmentShader() { if (module_) { VkShaderModule shaderModule = module_->BlockUntilReady(); vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; } } @@ -130,6 +131,7 @@ VulkanVertexShader::~VulkanVertexShader() { if (module_) { VkShaderModule shaderModule = module_->BlockUntilReady(); vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; } } From 6e605fa2bcd9178b777b763d1b726249e5b5942d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jun 2022 23:50:21 +0200 Subject: [PATCH 65/99] Fix silly logic, fixes the shader leak. --- GPU/Vulkan/ShaderManagerVulkan.cpp | 20 +++++++++----------- GPU/Vulkan/ShaderManagerVulkan.h | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 22ad34afbe..5f2d1ffb5d 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -42,7 +42,9 @@ #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/FramebufferManagerVulkan.h" -Promise *CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { +// Most drivers treat vkCreateShaderModule as pretty much a memcpy. What actually +// takes time here, and makes this worthy of parallelization, is GLSLtoSPV. +Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { return Promise::Spawn(&g_threadManager, [=] { PROFILE_THIS_SCOPE("shadercomp"); @@ -51,8 +53,6 @@ Promise *CompileShaderModule(VulkanContext *vulkan, VkShaderStag bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage); - VkShaderModule shaderModule = VK_NULL_HANDLE; - if (!errorMessage.empty()) { if (success) { ERROR_LOG(G3D, "Warnings in shader compilation!"); @@ -67,18 +67,16 @@ Promise *CompileShaderModule(VulkanContext *vulkan, VkShaderStag OutputDebugStringA(errorMessage.c_str()); #endif Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); - } else { - VkShaderModule shaderModule; + } + + VkShaderModule shaderModule = VK_NULL_HANDLE; + if (success) { success = vulkan->CreateShaderModule(spirv, &shaderModule); #ifdef SHADERLOG OutputDebugStringA("OK"); #endif } - if (success) { - vulkan->CreateShaderModule(spirv, &shaderModule); - } - return shaderModule; }, TaskType::CPU_COMPUTE); } @@ -87,7 +85,7 @@ Promise *CompileShaderModule(VulkanContext *vulkan, VkShaderStag VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code) : vulkan_(vulkan), id_(id) { source_ = code; - module_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); if (!module_) { failed_ = true; return; @@ -118,7 +116,7 @@ std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) co VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform) : vulkan_(vulkan), useHWTransform_(useHWTransform), id_(id) { source_ = code; - module_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str()); + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str()); if (!module_) { failed_ = true; return; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 5ab37fa723..50915be6a2 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -48,7 +48,7 @@ public: const FShaderID &GetID() { return id_; } protected: - Promise *module_; + Promise *module_ = nullptr; VulkanContext *vulkan_; std::string source_; From e70103726718cb42ac166150bc58c828a1f92a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 11 Jun 2022 23:56:51 +0200 Subject: [PATCH 66/99] Buildfix --- Common/Thread/Promise.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index 1788eb6a4b..40a0029fd6 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -2,6 +2,7 @@ #include +#include "Common/Log.h" #include "Common/Thread/Channel.h" #include "Common/Thread/ThreadManager.h" From 210233ede06dbfe6da17eca975a6d1187afb7cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 Jun 2022 00:35:46 +0200 Subject: [PATCH 67/99] Buildfix --- Common/Thread/Promise.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index 40a0029fd6..1833a7fa0c 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -94,7 +94,7 @@ private: Promise() {} // Promise can only be constructed in Spawn (or AlreadyDone). - T data_ = nullptr; + T data_{}; bool ready_ = false; Mailbox *rx_ = nullptr; }; From ebe2d1b30fd6111b75ff066d49e58b6e98293f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 Jun 2022 09:55:41 +0200 Subject: [PATCH 68/99] More buildfixing --- Common/Thread/Channel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Thread/Channel.h b/Common/Thread/Channel.h index 9ccbbd7096..2e522caf11 100644 --- a/Common/Thread/Channel.h +++ b/Common/Thread/Channel.h @@ -19,7 +19,7 @@ struct Mailbox { std::mutex mutex_; std::condition_variable condvar_; - T data_ = nullptr; + T data_{}; T Wait() { std::unique_lock lock(mutex_); From 2bb845a0ca5bf4b06a0c1bb2bc1a61206023204b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 Jun 2022 11:34:14 +0200 Subject: [PATCH 69/99] VS 2022 warning fix --- ext/gason/gason.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/gason/gason.h b/ext/gason/gason.h index 922751099f..cddb3615a7 100644 --- a/ext/gason/gason.h +++ b/ext/gason/gason.h @@ -114,10 +114,10 @@ public: JsonAllocator() : head(nullptr) {}; JsonAllocator(const JsonAllocator &) = delete; JsonAllocator &operator=(const JsonAllocator &) = delete; - JsonAllocator(JsonAllocator &&x) : head(x.head) { + JsonAllocator(JsonAllocator &&x) noexcept : head(x.head) { x.head = nullptr; } - JsonAllocator &operator=(JsonAllocator &&x) { + JsonAllocator &operator=(JsonAllocator &&x) noexcept { head = x.head; x.head = nullptr; return *this; From c06cf8efaaaf7ede48046d0f1cacfdb75000baca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 Jun 2022 11:37:34 +0200 Subject: [PATCH 70/99] Switch Vulkan pipelines to use promises for synchronization Slightly more expensive I guess but shouldn't be much of a bottleneck. --- Common/GPU/Vulkan/VulkanQueueRunner.cpp | 31 ++++++----------------- Common/GPU/Vulkan/VulkanQueueRunner.h | 6 ++--- Common/GPU/Vulkan/VulkanRenderManager.cpp | 16 ++++++++---- Common/GPU/Vulkan/VulkanRenderManager.h | 12 +++++---- Common/Thread/Promise.h | 12 +++++++++ GPU/Vulkan/PipelineManagerVulkan.cpp | 2 +- 6 files changed, 42 insertions(+), 37 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index b00ec67a01..92b7fb7e83 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -1182,18 +1182,11 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::BIND_GRAPHICS_PIPELINE: { - VKRGraphicsPipeline *pipeline = c.graphics_pipeline.pipeline; - if (pipeline->Pending()) { - // Stall processing, waiting for the compile queue to catch up. - std::unique_lock lock(compileDoneMutex_); - while (!pipeline->pipeline) { - compileDone_.wait(lock); - } - } - if (pipeline->pipeline != lastGraphicsPipeline && pipeline->pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); + VkPipeline pipeline = c.graphics_pipeline.pipeline->BlockUntilReady(); + if (pipeline != lastGraphicsPipeline && pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); pipelineLayout = c.pipeline.pipelineLayout; - lastGraphicsPipeline = pipeline->pipeline; + lastGraphicsPipeline = pipeline; // Reset dynamic state so it gets refreshed with the new pipeline. lastStencilWriteMask = -1; lastStencilCompareMask = -1; @@ -1204,18 +1197,11 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::BIND_COMPUTE_PIPELINE: { - VKRComputePipeline *pipeline = c.compute_pipeline.pipeline; - if (pipeline->Pending()) { - // Stall processing, waiting for the compile queue to catch up. - std::unique_lock lock(compileDoneMutex_); - while (!pipeline->pipeline) { - compileDone_.wait(lock); - } - } - if (pipeline->pipeline != lastComputePipeline && pipeline->pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); + VkPipeline pipeline = c.graphics_pipeline.pipeline->BlockUntilReady(); + if (pipeline != lastComputePipeline && pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); pipelineLayout = c.pipeline.pipelineLayout; - lastComputePipeline = pipeline->pipeline; + lastComputePipeline = pipeline; } break; } @@ -1335,7 +1321,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } default: ERROR_LOG(G3D, "Unimpl queue command"); - ; } } vkCmdEndRenderPass(cmd); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 401a125d6b..1d1c4efe8f 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -4,7 +4,7 @@ #include #include - +#include "Common/Thread/Promise.h" #include "Common/Data/Collections/Hashmaps.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanBarrier.h" @@ -55,11 +55,11 @@ struct VkRenderData { VkPipelineLayout pipelineLayout; } pipeline; struct { - VKRGraphicsPipeline *pipeline; + Promise *pipeline; VkPipelineLayout pipelineLayout; } graphics_pipeline; struct { - VKRComputePipeline *pipeline; + Promise *pipeline; VkPipelineLayout pipelineLayout; } compute_pipeline; struct { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 45e3a78aaa..850af030b2 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -5,6 +5,7 @@ #include "Common/Log.h" #include "Common/StringUtils.h" +#include "Common/TimeUtil.h" #include "Common/GPU/Vulkan/VulkanAlloc.h" #include "Common/GPU/Vulkan/VulkanContext.h" @@ -57,23 +58,26 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { desc->pipe.pStages = ss; desc->pipe.stageCount = 2; + double start = time_now_d(); VkPipeline vkpipeline; VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline); + NOTICE_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0); + bool success = true; if (result == VK_INCOMPLETE) { // Bad (disallowed by spec) return value seen on Adreno in Burnout :( Try to ignore? // Would really like to log more here, we could probably attach more info to desc. // // At least create a null placeholder to avoid creating over and over if something is broken. - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); success = false; } else if (result != VK_SUCCESS) { - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result)); success = false; } else { - pipeline = vkpipeline; + pipeline->Post(vkpipeline); } delete desc; @@ -91,11 +95,11 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) { bool success = true; if (result != VK_SUCCESS) { - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); ERROR_LOG(G3D, "Failed creating compute pipeline! result='%s'", VulkanResultToString(result)); success = false; } else { - pipeline = vkpipeline; + pipeline->Post(vkpipeline); } delete desc; @@ -478,6 +482,8 @@ void VulkanRenderManager::CompileThreadFunc() { break; } + NOTICE_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size()); + // TODO: Here we can sort the pending pipelines by vertex and fragment shaders, // and split up further. // Those with the same pairs of shaders should be on the same thread. diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index efadc784c6..4b69247525 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -144,10 +144,12 @@ struct VKRComputePipelineDesc { // Wrapped pipeline, which will later allow for background compilation while emulating the rest of the frame. struct VKRGraphicsPipeline { VKRGraphicsPipeline() { - pipeline = VK_NULL_HANDLE; + pipeline = Promise::CreateEmpty(); } + VKRGraphicsPipelineDesc *desc = nullptr; // While non-zero, is pending and pipeline isn't valid. - std::atomic pipeline; + + Promise *pipeline; bool Create(VulkanContext *vulkan); bool Pending() const { @@ -160,7 +162,7 @@ struct VKRComputePipeline { pipeline = VK_NULL_HANDLE; } VKRComputePipelineDesc *desc = nullptr; - std::atomic pipeline; + Promise *pipeline; bool Create(VulkanContext *vulkan); bool Pending() const { @@ -261,7 +263,7 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE }; - data.graphics_pipeline.pipeline = pipeline; + data.graphics_pipeline.pipeline = pipeline->pipeline; data.graphics_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); @@ -271,7 +273,7 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE }; - data.compute_pipeline.pipeline = pipeline; + data.compute_pipeline.pipeline = pipeline->pipeline; data.compute_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index 1833a7fa0c..1e83d62564 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -56,6 +56,13 @@ public: return promise; } + static Promise *CreateEmpty() { + Mailbox *mailbox = new Mailbox(); + Promise *promise = new Promise(); + promise->rx_ = mailbox; + return promise; + } + ~Promise() { // A promise should have been fulfilled before it's destroyed. _assert_(ready_); @@ -90,6 +97,11 @@ public: } } + // For outside injection of data, when not using Spawn + void Post(T data) { + rx_->Send(data); + } + private: Promise() {} diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 73768e38bc..8909283884 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -36,7 +36,7 @@ void PipelineManagerVulkan::Clear() { pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) { if (value->pipeline) { - VkPipeline pipeline = value->pipeline->pipeline; + VkPipeline pipeline = value->pipeline->pipeline->BlockUntilReady(); vulkan_->Delete().QueueDeletePipeline(pipeline); vulkan_->Delete().QueueCallback([](void *p) { VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p; From 76bd2395bf98dff88e12340d195af4e76c8383d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 Jun 2022 13:22:26 +0200 Subject: [PATCH 71/99] Promise: Add SpawnEmpty (couldn't come up with a better name), fix bug with multiple waiters --- Common/Thread/Channel.h | 2 +- Common/Thread/Promise.h | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Common/Thread/Channel.h b/Common/Thread/Channel.h index 2e522caf11..b6b23fdebe 100644 --- a/Common/Thread/Channel.h +++ b/Common/Thread/Channel.h @@ -43,7 +43,7 @@ struct Mailbox { std::unique_lock lock(mutex_); if (!data_) { data_ = data; - condvar_.notify_one(); + condvar_.notify_all(); return true; } else { // Already has value. diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index 1e83d62564..4145b4b6c1 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "Common/Log.h" #include "Common/Thread/Channel.h" @@ -63,7 +64,14 @@ public: return promise; } + // Allow an empty promise to spawn, too, in case we want to delay it. + void SpawnEmpty(ThreadManager *threadman, std::function fun, TaskType taskType) { + PromiseTask *task = new PromiseTask(fun, rx_, taskType); + threadman->EnqueueTask(task); + } + ~Promise() { + std::lock_guard guard(readyMutex_); // A promise should have been fulfilled before it's destroyed. _assert_(ready_); _assert_(!rx_); @@ -71,6 +79,7 @@ public: // Returns T if the data is ready, nullptr if it's not. T Poll() { + std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { @@ -86,6 +95,7 @@ public: } T BlockUntilReady() { + std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { @@ -108,5 +118,6 @@ private: // Promise can only be constructed in Spawn (or AlreadyDone). T data_{}; bool ready_ = false; + std::mutex readyMutex_; Mailbox *rx_ = nullptr; }; From fb3f417e77faf081c47d292ae7de95a1f18ee227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 16:13:51 +0200 Subject: [PATCH 72/99] Fix lying comment, log level reduction --- Common/GPU/Vulkan/VulkanRenderManager.cpp | 4 ++-- GPU/Vulkan/ShaderManagerVulkan.cpp | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 850af030b2..7741fb2e44 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -62,7 +62,7 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { VkPipeline vkpipeline; VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline); - NOTICE_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0); + INFO_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0); bool success = true; if (result == VK_INCOMPLETE) { @@ -482,7 +482,7 @@ void VulkanRenderManager::CompileThreadFunc() { break; } - NOTICE_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size()); + INFO_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size()); // TODO: Here we can sort the pending pipelines by vertex and fragment shaders, // and split up further. diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 5f2d1ffb5d..439602ae46 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -88,7 +88,6 @@ VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); if (!module_) { failed_ = true; - return; } else { VERBOSE_LOG(G3D, "Compiled fragment shader:\n%s\n", (const char *)code); } @@ -119,9 +118,8 @@ VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, cons module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str()); if (!module_) { failed_ = true; - return; } else { - VERBOSE_LOG(G3D, "Compiled fragment shader:\n%s\n", (const char *)code); + VERBOSE_LOG(G3D, "Compiled vertex shader:\n%s\n", (const char *)code); } } From 867eb995055b532c05185c701c60be3980f883ae Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 07:25:03 -0700 Subject: [PATCH 73/99] PPGe: Cleanup memory writes for clarity. Switch from WriteStruct to PSPPointer as well. --- Core/Util/PPGeDraw.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 425552a453..2b83bb4ccd 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -178,18 +178,16 @@ static void BeginVertexData() { static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color = 0xFFFFFFFF) { if (g_RemasterMode) { - PPGeRemasterVertex vtx; - vtx.x = x; vtx.y = y; vtx.z = 0; - vtx.u = u * tw; vtx.v = v * th; - vtx.color = color; - Memory::WriteStruct(dataWritePtr, &vtx); + auto vtx = PSPPointer::Create(dataWritePtr); + vtx->x = x; vtx->y = y; vtx->z = 0; + vtx->u = u * tw; vtx->v = v * th; + vtx->color = color; dataWritePtr += sizeof(vtx); } else { - PPGeVertex vtx; - vtx.x = x; vtx.y = y; vtx.z = 0; - vtx.u = u * tw; vtx.v = v * th; - vtx.color = color; - Memory::WriteStruct(dataWritePtr, &vtx); + auto vtx = PSPPointer::Create(dataWritePtr); + vtx->x = x; vtx->y = y; vtx->z = 0; + vtx->u = u * tw; vtx->v = v * th; + vtx->color = color; dataWritePtr += sizeof(vtx); } _dbg_assert_(dataWritePtr <= dataPtr + dataSize); @@ -197,8 +195,11 @@ static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color } static void EndVertexDataAndDraw(int prim) { + _assert_msg_(vertexStart != 0, "Missing matching call to BeginVertexData()"); + NotifyMemInfo(MemBlockFlags::WRITE, vertexStart, dataWritePtr - vertexStart, "PPGe Vertex"); WriteCmdAddrWithBase(GE_CMD_VADDR, vertexStart); WriteCmd(GE_CMD_PRIM, (prim << 16) | vertexCount); + vertexStart = 0; } bool PPGeIsFontTextureAddress(u32 addr) { @@ -272,6 +273,7 @@ void __PPGeInit() { int val = i; palette[i] = (val << 12) | 0xFFF; } + NotifyMemInfo(MemBlockFlags::WRITE, palette.ptr, 16 * sizeof(u16_le), "PPGe Palette"); const u32_le *imagePtr = (u32_le *)imageData[0]; u8 *ramPtr = atlasPtr == 0 ? nullptr : (u8 *)Memory::GetPointer(atlasPtr); @@ -286,7 +288,10 @@ void __PPGeInit() { u8 cval = (a2 << 4) | a1; ramPtr[i] = cval; } - atlasHash = XXH3_64bits(ramPtr, atlasWidth * atlasHeight / 2); + if (atlasPtr != 0) { + atlasHash = XXH3_64bits(ramPtr, atlasSize); + NotifyMemInfo(MemBlockFlags::WRITE, atlasPtr, atlasSize, "PPGe Atlas"); + } free(imageData[0]); @@ -457,6 +462,7 @@ void PPGeEnd() if (dataWritePtr > dataPtr) { // We actually drew something gpu->EnableInterrupts(false); + NotifyMemInfo(MemBlockFlags::WRITE, dlPtr, dlWritePtr - dlPtr, "PPGe ListCmds"); u32 list = sceGeListEnQueue(dlPtr, dlWritePtr, -1, listArgs.ptr); DEBUG_LOG(SCEGE, "PPGe enqueued display list %i", list); gpu->EnableInterrupts(true); From 3a372aa615b7dc27ae2a52396af6bfa875f39348 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 07:48:52 -0700 Subject: [PATCH 74/99] HLE: Remove misc usage of WriteStruct(). Prefer PSPPointer and notifying. --- Core/HLE/sceChnnlsv.cpp | 82 ++++++++++++++++------------------------ Core/HLE/sceOpenPSID.cpp | 14 ++++--- Core/HLE/sceUsbCam.cpp | 33 ++++++++++------ Core/HLE/sceUsbGps.cpp | 16 +++++--- Core/MemMap.cpp | 9 +++++ Core/MemMap.h | 15 +++++++- 6 files changed, 94 insertions(+), 75 deletions(-) diff --git a/Core/HLE/sceChnnlsv.cpp b/Core/HLE/sceChnnlsv.cpp index 82e063b344..fc73837687 100644 --- a/Core/HLE/sceChnnlsv.cpp +++ b/Core/HLE/sceChnnlsv.cpp @@ -214,13 +214,12 @@ static int sub_17A8(u8* data) return -261; } -static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) -{ - pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx, &ctx); - int res = sceSdGetLastIndex_(ctx, Memory::GetPointerWrite(addressHash), Memory::GetPointerWrite(addressKey)); - Memory::WriteStruct(addressCtx, &ctx); - return res; +static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) { + auto ctx = PSPPointer::Create(addressCtx); + u8 *hash = Memory::GetPointerWrite(addressHash); + if (!ctx.IsValid() || !hash) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceSdGetLastIndex_(*ctx, hash, Memory::GetPointerWrite(addressKey))); } int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) @@ -325,13 +324,11 @@ int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) return 0; } -static int sceSdSetIndex(u32 addressCtx, int value) -{ - pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx,&ctx); - int res = sceSdSetIndex_(ctx, value); - Memory::WriteStruct(addressCtx,&ctx); - return res; +static int sceSdSetIndex(u32 addressCtx, int value) { + auto ctx = PSPPointer::Create(addressCtx); + if (!ctx.IsValid()) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceSdSetIndex_(*ctx, value)); } int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value) @@ -344,14 +341,11 @@ int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value) } -static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) -{ - pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx, &ctx); - int res = sceSdRemoveValue_(ctx, Memory::GetPointerWrite(addressData), length); - Memory::WriteStruct(addressCtx, &ctx); - - return res; +static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) { + auto ctx = PSPPointer::Create(addressCtx); + if (!ctx.IsValid() || !Memory::IsValidAddress(addressData)) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceSdRemoveValue_(*ctx, Memory::GetPointerWrite(addressData), length)); } int sceSdRemoveValue_(pspChnnlsvContext1& ctx, u8* data, int length) @@ -396,18 +390,14 @@ int sceSdRemoveValue_(pspChnnlsvContext1& ctx, u8* data, int length) return 0; } -static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr) -{ - pspChnnlsvContext2 ctx2; - Memory::ReadStruct(ctx2Addr, &ctx2); +static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr) { + auto ctx2 = PSPPointer::Create(ctx2Addr); u8* data = Memory::GetPointerWrite(dataAddr); u8* cryptkey = Memory::GetPointerWrite(cryptkeyAddr); + if (!ctx2.IsValid() || !data) + return hleLogError(SCEMISC, 0, "Invalid pointer"); - int res = sceSdCreateList_(ctx2, mode, unkwn, data, cryptkey); - - Memory::WriteStruct(ctx2Addr, &ctx2); - - return res; + return hleLogSuccessI(SCEMISC, sceSdCreateList_(*ctx2, mode, unkwn, data, cryptkey)); } int sceSdCreateList_(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, u8* cryptkey) @@ -464,17 +454,13 @@ int sceSdCreateList_(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, u8* return 0; } -static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen) -{ - pspChnnlsvContext2 ctx; - Memory::ReadStruct(ctxAddr, &ctx); - u8* data = Memory::GetPointerWrite(dataAddr); +static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen) { + auto ctx = PSPPointer::Create(ctxAddr); + u8 *data = Memory::GetPointerWrite(dataAddr); + if (!ctx.IsValid() || !data) + return hleLogError(SCEMISC, 0, "Invalid pointer"); - int res = sceSdSetMember_(ctx, data, alignedLen); - - Memory::WriteStruct(ctxAddr, &ctx); - - return res; + return hleLogSuccessI(SCEMISC, sceSdSetMember_(*ctx, data, alignedLen)); } int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen) @@ -511,15 +497,11 @@ int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen) return res; } -static int sceChnnlsv_21BE78B4(u32 ctxAddr) -{ - pspChnnlsvContext2 ctx; - Memory::ReadStruct(ctxAddr, &ctx); - - int res = sceChnnlsv_21BE78B4_(ctx); - - Memory::WriteStruct(ctxAddr, &ctx); - return res; +static int sceChnnlsv_21BE78B4(u32 ctxAddr) { + auto ctx = PSPPointer::Create(ctxAddr); + if (!ctx.IsValid()) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceChnnlsv_21BE78B4_(*ctx)); } int sceChnnlsv_21BE78B4_(pspChnnlsvContext2& ctx) diff --git a/Core/HLE/sceOpenPSID.cpp b/Core/HLE/sceOpenPSID.cpp index 43665f530f..9d47b48374 100644 --- a/Core/HLE/sceOpenPSID.cpp +++ b/Core/HLE/sceOpenPSID.cpp @@ -38,9 +38,10 @@ static int sceOpenPSIDGetOpenPSID(u32 OpenPSIDPtr) { WARN_LOG(HLE, "UNTESTED %s(%08x)", __FUNCTION__, OpenPSIDPtr); - if (Memory::IsValidAddress(OpenPSIDPtr)) - { - Memory::WriteStruct(OpenPSIDPtr, &dummyOpenPSID); + auto ptr = PSPPointer::Create(OpenPSIDPtr); + if (ptr.IsValid()) { + *ptr = dummyOpenPSID; + ptr.NotifyWrite("OpenPSIDGetOpenPSID"); } return 0; } @@ -49,9 +50,10 @@ static int sceOpenPSIDGetPSID(u32 OpenPSIDPtr,u32 unknown) { WARN_LOG(HLE, "UNTESTED %s(%08x, %08x)", __FUNCTION__, OpenPSIDPtr, unknown); - if (Memory::IsValidAddress(OpenPSIDPtr)) - { - Memory::WriteStruct(OpenPSIDPtr, &dummyOpenPSID); + auto ptr = PSPPointer::Create(OpenPSIDPtr); + if (ptr.IsValid()) { + *ptr = dummyOpenPSID; + ptr.NotifyWrite("OpenPSIDGetPSID"); } return 0; } diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 18417bb9c2..c8d43a5027 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -113,11 +113,12 @@ static int getCameraResolution(Camera::ConfigType type, int *width, int *height) static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) { - INFO_LOG(HLE, "sceUsbCamSetupMic"); - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) { - Memory::ReadStruct(paramAddr, &config->micParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->micParam = *param; + param.NotifyRead("UsbCamSetupMic"); } - return 0; + return hleLogSuccessInfoI(SCEMISC, 0); } static int sceUsbCamStartMic() { @@ -155,16 +156,20 @@ static int sceUsbCamGetMicDataLength() { } static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoParam))) { - Memory::ReadStruct(paramAddr, &config->videoParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->videoParam = *param; + param.NotifyRead("UsbCamSetupVideo"); } config->type = Camera::ConfigType::CfVideo; return 0; } static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) { - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoExParam))) { - Memory::ReadStruct(paramAddr, &config->videoExParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->videoExParam = *param; + param.NotifyRead("UsbCamSetupVideoEx"); } config->type = Camera::ConfigType::CfVideoEx; return 0; @@ -222,8 +227,10 @@ static int sceUsbCamPollReadVideoFrameEnd() { static int sceUsbCamSetupStill(u32 paramAddr) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStill"); - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillParam))) { - Memory::ReadStruct(paramAddr, &config->stillParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->stillParam = *param; + param.NotifyRead("UsbCamSetupStill"); } config->type = Camera::ConfigType::CfStill; return 0; @@ -231,8 +238,10 @@ static int sceUsbCamSetupStill(u32 paramAddr) { static int sceUsbCamSetupStillEx(u32 paramAddr) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStillEx"); - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillExParam))) { - Memory::ReadStruct(paramAddr, &config->stillExParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->stillExParam = *param; + param.NotifyRead("UsbCamSetupStillEx"); } config->type = Camera::ConfigType::CfStillEx; return 0; diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index 2c9f320044..020339fe10 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -59,8 +59,8 @@ static int sceUsbGpsGetInitDataLocation(u32 addr) { } static int sceUsbGpsGetState(u32 stateAddr) { - if (Memory::IsValidAddress(stateAddr)) { - Memory::Write_U32(gpsStatus, stateAddr); + if (Memory::IsValidRange(stateAddr, 4)) { + Memory::WriteUnchecked_U32(gpsStatus, stateAddr); } return 0; } @@ -81,11 +81,15 @@ static int sceUsbGpsClose() { } static int sceUsbGpsGetData(u32 gpsDataAddr, u32 satDataAddr) { - if (Memory::IsValidRange(gpsDataAddr, sizeof(GpsData))) { - Memory::WriteStruct(gpsDataAddr, GPS::getGpsData()); + auto gpsData = PSPPointer::Create(gpsDataAddr); + if (gpsData.IsValid()) { + *gpsData = *GPS::getGpsData(); + gpsData.NotifyWrite("UsbGpsGetData"); } - if (Memory::IsValidRange(satDataAddr, sizeof(SatData))) { - Memory::WriteStruct(satDataAddr, GPS::getSatData()); + auto satData = PSPPointer::Create(satDataAddr); + if (satData.IsValid()) { + *satData = *GPS::getSatData(); + gpsData.NotifyWrite("UsbGpsGetData"); } return 0; } diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 1a0fd654f6..52ef9ff5f9 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -490,3 +490,12 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength, const char } } // namespace + +void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char * tag, size_t tagLen) { + if (MemBlockInfoDetailed(bytes)) { + if (rw & 1) + NotifyMemInfo(MemBlockFlags::WRITE, ptr, bytes, tag, tagLen); + if (rw & 2) + NotifyMemInfo(MemBlockFlags::READ, ptr, bytes, tag, tagLen); + } +} diff --git a/Core/MemMap.h b/Core/MemMap.h index 97b7fd76e7..306190f249 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -328,6 +328,9 @@ inline bool IsValidRange(const u32 address, const u32 size) { } // namespace Memory +// Avoiding a global include for NotifyMemInfo. +void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char *tag, size_t tagLen); + template struct PSPPointer { @@ -440,7 +443,17 @@ struct PSPPointer bool IsValid() const { - return Memory::IsValidAddress(ptr); + return Memory::IsValidRange(ptr, (u32)sizeof(T)); + } + + template + void NotifyWrite(const char(&tag)[tagLen]) const { + PSPPointerNotifyRW(1, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1); + } + + template + void NotifyRead(const char(&tag)[tagLen]) const { + PSPPointerNotifyRW(2, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1); } static PSPPointer Create(u32 ptr) { From 8f96405ee51a412fb58fb21091cf9e4b1d7ef0b1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 08:38:34 -0700 Subject: [PATCH 75/99] Net: Remove usage of Memory::WriteStruct(). This also validates pointer write size better. --- Core/HLE/sceNet.cpp | 126 +++++++++++++++++++++++++++------------ Core/HLE/sceNetAdhoc.cpp | 52 +++++++--------- Core/HLE/sceNp.cpp | 82 +++++++++++++------------ Core/HLE/sceNp2.cpp | 6 +- 4 files changed, 159 insertions(+), 107 deletions(-) diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index ff2cb8ed7e..c2a8987408 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -785,11 +785,12 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) { // Write static data since we don't actually manage any memory for sceNet* yet. static int sceNetGetMallocStat(u32 statPtr) { VERBOSE_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x) at %08x", statPtr, currentMIPS->pc); - if(Memory::IsValidAddress(statPtr)) - Memory::WriteStruct(statPtr, &netMallocStat); - else - ERROR_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x): tried to request invalid address!", statPtr); + auto stat = PSPPointer::Create(statPtr); + if (!stat.IsValid()) + return hleLogError(SCENET, 0, "invalid address"); + *stat = netMallocStat; + stat.NotifyWrite("sceNetGetMallocStat"); return 0; } @@ -893,77 +894,128 @@ static int sceNetApctlGetInfo(int code, u32 pInfoAddr) { if (!netApctlInited) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_IN_BSS, "apctl not in bss"); // Only have valid info after joining an AP and got an IP, right? - if (!Memory::IsValidAddress(pInfoAddr)) - return hleLogError(SCENET, -1, "apctl invalid arg"); - - u8* info = Memory::GetPointerWrite(pInfoAddr); // FIXME: Points to a union instead of a struct thus each field have the same address - switch (code) { case PSP_NET_APCTL_INFO_PROFILE_NAME: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.name); + if (!Memory::IsValidRange(pInfoAddr, APCTL_PROFILENAME_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.name, APCTL_PROFILENAME_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_PROFILENAME_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - ProfileName: %s", netApctlInfo.name); break; case PSP_NET_APCTL_INFO_BSSID: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.bssid); + if (!Memory::IsValidRange(pInfoAddr, ETHER_ADDR_LEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.bssid, ETHER_ADDR_LEN); DEBUG_LOG(SCENET, "ApctlInfo - BSSID: %s", mac2str((SceNetEtherAddr*)&netApctlInfo.bssid).c_str()); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, ETHER_ADDR_LEN, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_SSID: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssid); + if (!Memory::IsValidRange(pInfoAddr, APCTL_SSID_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.ssid, APCTL_SSID_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_SSID_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - SSID: %s", netApctlInfo.ssid); break; case PSP_NET_APCTL_INFO_SSID_LENGTH: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssidLength); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.ssidLength, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_SECURITY_TYPE: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.securityType); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.securityType, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_STRENGTH: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.strength); + if (!Memory::IsValidRange(pInfoAddr, 1)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U8(netApctlInfo.strength, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_CHANNEL: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.channel); + if (!Memory::IsValidRange(pInfoAddr, 1)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U8(netApctlInfo.channel, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_POWER_SAVE: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.powerSave); + if (!Memory::IsValidRange(pInfoAddr, 1)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U8(netApctlInfo.powerSave, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_IP: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.ip); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.ip, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - IP: %s", netApctlInfo.ip); break; case PSP_NET_APCTL_INFO_SUBNETMASK: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.subNetMask); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.subNetMask, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - SubNet Mask: %s", netApctlInfo.subNetMask); break; case PSP_NET_APCTL_INFO_GATEWAY: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.gateway); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.gateway, APCTL_IPADDR_MAXLEN); DEBUG_LOG(SCENET, "ApctlInfo - Gateway IP: %s", netApctlInfo.gateway); break; case PSP_NET_APCTL_INFO_PRIMDNS: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.primaryDns); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.primaryDns, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - Primary DNS: %s", netApctlInfo.primaryDns); break; case PSP_NET_APCTL_INFO_SECDNS: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.secondaryDns); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.secondaryDns, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - Secondary DNS: %s", netApctlInfo.secondaryDns); break; case PSP_NET_APCTL_INFO_USE_PROXY: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.useProxy); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.useProxy, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_PROXY_URL: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyUrl); + if (!Memory::IsValidRange(pInfoAddr, APCTL_URL_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.proxyUrl, APCTL_URL_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_URL_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - Proxy URL: %s", netApctlInfo.proxyUrl); break; case PSP_NET_APCTL_INFO_PROXY_PORT: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyPort); + if (!Memory::IsValidRange(pInfoAddr, 2)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U16(netApctlInfo.proxyPort, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 2, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_8021_EAP_TYPE: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.eapType); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.eapType, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_START_BROWSER: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.startBrowser); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.startBrowser, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_WIFISP: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.wifisp); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.wifisp, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; default: return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "apctl invalid code"); @@ -1234,28 +1286,28 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { switch (infoId) { case PSP_NET_APCTL_DESC_IBSS: // IBSS, 6 bytes if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.bssid); + Memory::Memcpy(resultAddr, netApctlInfo.bssid, sizeof(netApctlInfo.bssid), "GetBSSDescEntryUser"); else { // Generate a BSSID/MAC address char dummyMAC[ETHER_ADDR_LEN]; memset(dummyMAC, entryId, sizeof(dummyMAC)); // Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo) dummyMAC[0] &= 0xfc; - Memory::WriteStruct(resultAddr, &dummyMAC); + Memory::Memcpy(resultAddr, dummyMAC, sizeof(dummyMAC), "GetBSSDescEntryUser"); } break; case PSP_NET_APCTL_DESC_SSID_NAME: // Return 32 bytes if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.ssid); + Memory::Memcpy(resultAddr, netApctlInfo.ssid, sizeof(netApctlInfo.ssid), "GetBSSDescEntryUser"); else { - Memory::WriteStruct(resultAddr, &dummySSID); + Memory::Memcpy(resultAddr, dummySSID, sizeof(dummySSID), "GetBSSDescEntryUser"); } break; case PSP_NET_APCTL_DESC_SSID_NAME_LENGTH: // Return one 32-bit value if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.ssidLength); + Memory::Write_U32(netApctlInfo.ssidLength, resultAddr); else { // Calculate the SSID length Memory::Write_U32((u32)strlen(dummySSID), resultAddr); @@ -1264,7 +1316,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { case PSP_NET_APCTL_DESC_CHANNEL: // FIXME: Return one 1 byte value or may be 32-bit if this is not a channel? if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.channel); + Memory::Write_U8(netApctlInfo.channel, resultAddr); else { // Generate channel for testing purposes, not even sure whether this is channel or not, MGS:PW seems to treat the data as u8 Memory::Write_U8(entryId, resultAddr); @@ -1273,7 +1325,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { case PSP_NET_APCTL_DESC_SIGNAL_STRENGTH: // Return 1 byte if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.strength); + Memory::Write_U8(netApctlInfo.strength, resultAddr); else { // Randomize signal strength between 1%~99% since games like MGS:PW are using signal strength to determine the strength of the recruit Memory::Write_U8((int)(((float)rand() / (float)RAND_MAX) * 99.0 + 1.0), resultAddr); @@ -1281,7 +1333,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { break; case PSP_NET_APCTL_DESC_SECURITY: // Return one 32-bit value - Memory::WriteStruct(resultAddr, &netApctlInfo.securityType); + Memory::Write_U32(netApctlInfo.securityType, resultAddr); break; default: return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "unknown info id"); @@ -1432,7 +1484,7 @@ const HLEFunction sceNet[] = { {0XD27961C9, &WrapV_UU, "sceNetEtherStrton", 'v', "xx" }, {0X0BF0A3AE, &WrapU_U, "sceNetGetLocalEtherAddr", 'x', "x" }, {0X50647530, &WrapI_I, "sceNetFreeThreadinfo", 'i', "i" }, - {0XCC393E48, &WrapI_U, "sceNetGetMallocStat", 'i', "x" }, + {0XCC393E48, &WrapI_U, "sceNetGetMallocStat", 'i', "p" }, {0XAD6844C6, &WrapI_I, "sceNetThreadAbort", 'i', "i" }, }; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index ec3cced95e..5e5da07fd1 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1303,8 +1303,10 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { if (netAdhocctlInited) return ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED; - if (Memory::IsValidAddress(productAddr)) { - Memory::ReadStruct(productAddr, &product_code); + auto product = PSPPointer::Create(productAddr); + if (product.IsValid()) { + product_code = *product; + product.NotifyRead("NetAdhocctlInit"); } adhocctlEvents.clear(); @@ -1539,21 +1541,17 @@ static int sceNetAdhocctlGetParameter(u32 paramAddr) { } // Library initialized - if (netAdhocctlInited) { - // Valid Arguments - if (Memory::IsValidAddress(paramAddr)) { - // Copy Parameter - Memory::WriteStruct(paramAddr,¶meter); - // Return Success - return 0; - } - - // Invalid Arguments - return ERROR_NET_ADHOCCTL_INVALID_ARG; + if (!netAdhocctlInited) { + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED); } - // Library uninitialized - return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; + auto ptr = PSPPointer::Create(paramAddr); + if (!ptr.IsValid()) + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG); + + *ptr = parameter; + ptr.NotifyWrite("NetAdhocctlGetParameter"); + return 0; } /** @@ -2258,25 +2256,17 @@ static int sceNetAdhocPdpDelete(int id, int unknown) { static int sceNetAdhocctlGetAdhocId(u32 productStructAddr) { INFO_LOG(SCENET, "sceNetAdhocctlGetAdhocId(%08x) at %08x", productStructAddr, currentMIPS->pc); - // Library initialized - if (netAdhocctlInited) - { - // Valid Arguments - if (Memory::IsValidAddress(productStructAddr)) - { - // Copy Product ID - Memory::WriteStruct(productStructAddr, &product_code); + if (!netAdhocctlInited) + return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); - // Return Success - return hleLogDebug(SCENET, 0, "type = %d, code = %s", product_code.type, product_code.data); - } - - // Invalid Arguments + auto productStruct = PSPPointer::Create(productStructAddr); + if (!productStruct.IsValid()) return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); - } - // Library uninitialized - return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); + *productStruct = product_code; + productStruct.NotifyWrite("NetAdhocctlGetAdhocId"); + + return hleLogDebug(SCENET, 0, "type = %d, code = %s", product_code.type, product_code.data); } // FIXME: Scan probably not a blocking function since there is ADHOCCTL_STATE_SCANNING state that can be polled by the game, right? But apparently it need to be delayed for Naruto Shippuden Ultimate Ninja Heroes 3 diff --git a/Core/HLE/sceNp.cpp b/Core/HLE/sceNp.cpp index 50bb3f317b..c5ca4be4c6 100644 --- a/Core/HLE/sceNp.cpp +++ b/Core/HLE/sceNp.cpp @@ -154,15 +154,15 @@ static int sceNpGetOnlineId(u32 idPtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr); - if (!Memory::IsValidAddress(idPtr)) + auto id = PSPPointer::Create(idPtr); + if (!id.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - SceNpOnlineId dummyOnlineId{}; - truncate_cpy(dummyOnlineId.data, sizeof(dummyOnlineId.data), npOnlineId.c_str()); + memset((SceNpOnlineId *)id, 0, sizeof(SceNpOnlineId)); + truncate_cpy(id->data, sizeof(id->data), npOnlineId.c_str()); + id.NotifyWrite("NpGetOnlineId"); - INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyOnlineId.data); - - Memory::WriteStruct(idPtr, &dummyOnlineId); + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, id->data); return 0; } @@ -177,20 +177,21 @@ static int sceNpGetNpId(u32 idPtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr); - if (!Memory::IsValidAddress(idPtr)) + auto id = PSPPointer::Create(idPtr); + if (!id.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); SceNpId dummyNpId{}; - int retval = NpGetNpId(&dummyNpId); + memset((SceNpId *)id, 0, sizeof(SceNpId)); + int retval = NpGetNpId(id); if (retval < 0) return hleLogError(SCENET, retval); - INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyNpId.handle.data); + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, id->handle.data); std::string datahex; - DataToHexString(dummyNpId.opt, sizeof(dummyNpId.opt), &datahex); + DataToHexString(id->opt, sizeof(id->opt), &datahex); INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str()); - - Memory::WriteStruct(idPtr, &dummyNpId); + id.NotifyWrite("NpGetNpId"); return 0; } @@ -199,19 +200,21 @@ static int sceNpGetAccountRegion(u32 countryCodePtr, u32 regionCodePtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, countryCodePtr, regionCodePtr); - if (!Memory::IsValidAddress(countryCodePtr) || !Memory::IsValidAddress(regionCodePtr)) + auto countryCode = PSPPointer::Create(countryCodePtr); + auto regionCode = PSPPointer::Create(regionCodePtr); + if (!countryCode.IsValid() || !regionCode.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - SceNpCountryCode dummyCountryCode{}; - memcpy(dummyCountryCode.data, npCountryCode, sizeof(dummyCountryCode.data)); - SceNpCountryCode dummyRegionCode{}; - memcpy(dummyRegionCode.data, npRegionCode, sizeof(dummyRegionCode.data)); + memset((SceNpCountryCode *)countryCode, 0, sizeof(SceNpCountryCode)); + memcpy(countryCode->data, npCountryCode, sizeof(countryCode->data)); + memset((SceNpCountryCode *)regionCode, 0, sizeof(SceNpCountryCode)); + memcpy(regionCode->data, npRegionCode, sizeof(regionCode->data)); - INFO_LOG(SCENET, "%s - Country Code: %s", __FUNCTION__, dummyCountryCode.data); - INFO_LOG(SCENET, "%s - Region? Code: %s", __FUNCTION__, dummyRegionCode.data); + INFO_LOG(SCENET, "%s - Country Code: %s", __FUNCTION__, countryCode->data); + INFO_LOG(SCENET, "%s - Region? Code: %s", __FUNCTION__, regionCode->data); - Memory::WriteStruct(countryCodePtr, &dummyCountryCode); - Memory::WriteStruct(regionCodePtr, &dummyRegionCode); + countryCode.NotifyWrite("NpGetAccountRegion"); + regionCode.NotifyWrite("NpGetAccountRegion"); return 0; } @@ -220,14 +223,16 @@ static int sceNpGetMyLanguages(u32 langListPtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, langListPtr); - if (!Memory::IsValidAddress(langListPtr)) + auto langList = PSPPointer::Create(langListPtr); + if (!langList.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); INFO_LOG(SCENET, "%s - Language1 Code: %d", __FUNCTION__, npMyLangList.language1); INFO_LOG(SCENET, "%s - Language2 Code: %d", __FUNCTION__, npMyLangList.language2); INFO_LOG(SCENET, "%s - Language3 Code: %d", __FUNCTION__, npMyLangList.language3); - Memory::WriteStruct(langListPtr, &npMyLangList); + *langList = npMyLangList; + langList.NotifyWrite("NpGetMyLanguages"); return 0; } @@ -236,20 +241,21 @@ static int sceNpGetUserProfile(u32 profilePtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, profilePtr); + auto profile = PSPPointer::Create(profilePtr); if (!Memory::IsValidAddress(profilePtr)) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - SceNpUserInformation dummyProfile{}; - truncate_cpy(dummyProfile.userId.handle.data, sizeof(dummyProfile.userId.handle.data), npOnlineId.c_str()); - truncate_cpy(dummyProfile.icon.data, sizeof(dummyProfile.icon.data), npAvatarUrl.c_str()); + memset((SceNpUserInformation *)profile, 0, sizeof(SceNpUserInformation)); + truncate_cpy(profile->userId.handle.data, sizeof(profile->userId.handle.data), npOnlineId.c_str()); + truncate_cpy(profile->icon.data, sizeof(profile->icon.data), npAvatarUrl.c_str()); - INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyProfile.userId.handle.data); + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, profile->userId.handle.data); std::string datahex; - DataToHexString(dummyProfile.userId.opt, sizeof(dummyProfile.userId.opt), &datahex); + DataToHexString(profile->userId.opt, sizeof(profile->userId.opt), &datahex); INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str()); - INFO_LOG(SCENET, "%s - Avatar URL: %s", __FUNCTION__, dummyProfile.icon.data); + INFO_LOG(SCENET, "%s - Avatar URL: %s", __FUNCTION__, profile->icon.data); - Memory::WriteStruct(profilePtr, &dummyProfile); + profile.NotifyWrite("NpGetUserProfile"); return 0; } @@ -295,10 +301,12 @@ int sceNpAuthGetMemoryStat(u32 memStatAddr) { ERROR_LOG(SCENET, "UNIMPL %s(%08x)", __FUNCTION__, memStatAddr); - if (!Memory::IsValidAddress(memStatAddr)) + auto memStat = PSPPointer::Create(memStatAddr); + if (!memStat.IsValid()) return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); - Memory::WriteStruct(memStatAddr, &npAuthMemStat); + *memStat = npAuthMemStat; + memStat.NotifyWrite("NpAuthGetMemoryStat"); return 0; } @@ -386,7 +394,7 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); int result = length; - Memory::Memset(bufferAddr, 0, length); + Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket"); SceNpTicket ticket = {}; // Dummy Login ticket returned as Login response. Dummy ticket contents were taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket ticket.header.version = TICKET_VER_2_1; @@ -415,14 +423,14 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL); ticket.section.type = SECTION_TYPE_BODY; ticket.section.size = ofs; - Memory::WriteStruct(bufferAddr, &ticket); + Memory::Memcpy(bufferAddr, &ticket, sizeof(SceNpTicket), "NpAuthGetTicket"); SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on version 2.1 containing 4-chars ASCII + 20-chars ASCII - Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, &footer); + Memory::Memcpy(bufferAddr + sizeof(ticket) + ofs, &footer, sizeof(SceNpTicketSection), "NpAuthGetTicket"); ofs += sizeof(footer); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x34\xcd\x3c\xa9", 4); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x3a\x4b\x42\x66\x92\xda\x6b\x7c\xb7\x4c\xe8\xd9\x4f\x2b\x77\x15\x91\xb8\xa4\xa9", 20); // 20 random letters, token key or SceNpSignature? - u8 unknownBytes[36] = {}; // includes Language list? - Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, unknownBytes); + // includes Language list? + Memory::Memset(bufferAddr + sizeof(ticket) + ofs, 0, 36); result = ticket.header.size + sizeof(ticket.header); // dummy ticket is 248 bytes diff --git a/Core/HLE/sceNp2.cpp b/Core/HLE/sceNp2.cpp index 51a1ada775..59f7b0cfdf 100644 --- a/Core/HLE/sceNp2.cpp +++ b/Core/HLE/sceNp2.cpp @@ -280,10 +280,12 @@ static int sceNpMatching2GetMemoryStat(u32 memStatPtr) if (!npMatching2Inited) return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); - if (!Memory::IsValidAddress(memStatPtr)) + auto memStat = PSPPointer::Create(memStatPtr); + if (!memStat.IsValid()) return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT); - Memory::WriteStruct(memStatPtr, &npMatching2MemStat); + *memStat = npMatching2MemStat; + memStat.NotifyWrite("NpMatching2GetMemoryStat"); return 0; } From 8b41178cb8b2f29d3c784f7dc1a7dcfcafae9d6e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 08:47:13 -0700 Subject: [PATCH 76/99] Psmf: Use PSPPointer instead of WriteStruct. --- Core/HLE/sceMpeg.cpp | 14 ++++++------ Core/HLE/scePsmf.cpp | 53 ++++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 5a3cd77d35..8aa429ba1d 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -125,7 +125,7 @@ struct SceMpegLLI }; void SceMpegAu::read(u32 addr) { - Memory::ReadStruct(addr, this); + Memory::Memcpy(this, addr, sizeof(this), "SceMpegAu"); pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32); dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32); } @@ -133,7 +133,7 @@ void SceMpegAu::read(u32 addr) { void SceMpegAu::write(u32 addr) { pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32); dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32); - Memory::WriteStruct(addr, this); + Memory::Memcpy(addr, this, sizeof(this), "SceMpegAu"); } /* @@ -2410,15 +2410,15 @@ static u32 sceMpegBasePESpacketCopy(u32 p) { pmp_videoSource = p; pmp_nBlocks = 0; - SceMpegLLI lli; - while (1){ - Memory::ReadStruct(p, &lli); + + auto lli = PSPPointer::Create(p); + while (lli.IsValid()) { pmp_nBlocks++; // lli.Next ==0 for last block - if (lli.Next == 0){ + if (lli->Next == 0){ break; } - p = p + sizeof(SceMpegLLI); + ++lli; } DEBUG_LOG(ME, "sceMpegBasePESpacketCopy(%08x), received %d block(s)", pmp_videoSource, pmp_nBlocks); diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index 020ca8a808..b2aefa9a1a 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -766,20 +766,21 @@ static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { // Note: this structure changes between versions. // TODO: These values are not right, but games probably don't read them. - PsmfData data = {0}; - data.version = psmf->version; - data.headerSize = 0x800; - data.streamSize = psmf->streamSize; + auto data = PSPPointer::Create(psmfStruct); + memset((PsmfData *)data, 0, sizeof(PsmfData)); + data->version = psmf->version; + data->headerSize = 0x800; + data->streamSize = psmf->streamSize; // This should be and needs to be the current stream. - data.streamNum = psmf->currentStreamNum; - data.headerOffset = psmf->headerOffset; - Memory::WriteStruct(psmfStruct, &data); + data->streamNum = psmf->currentStreamNum; + data->headerOffset = psmf->headerOffset; + data.NotifyWrite("PsmfSetPsmf"); // Because the Psmf struct is sometimes copied, we use a value inside as an id. - auto iter = psmfMap.find(data.headerOffset); + auto iter = psmfMap.find(data->headerOffset); if (iter != psmfMap.end()) delete iter->second; - psmfMap[data.headerOffset] = psmf; + psmfMap[data->headerOffset] = psmf; return hleLogSuccessI(ME, 0); } @@ -1046,45 +1047,43 @@ static u32 scePsmfGetEPWithId(u32 psmfStruct, int epid, u32 entryAddr) { Psmf *psmf = getPsmf(psmfStruct); if (!psmf) { - ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x): invalid psmf", psmfStruct, epid, entryAddr); - return ERROR_PSMF_NOT_INITIALIZED; + return hleLogError(ME, ERROR_PSMF_NOT_INITIALIZED, "invalid psmf"); } - DEBUG_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x)", psmfStruct, epid, entryAddr); if (epid < 0 || epid >= (int)psmf->EPMap.size()) { - ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i): invalid id", psmfStruct, epid); - return ERROR_PSMF_NOT_FOUND; + return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid id"); } - if (Memory::IsValidAddress(entryAddr)) { - Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]); + + auto entry = PSPPointer::Create(entryAddr); + if (entry.IsValid()) { + *entry = psmf->EPMap[epid]; + entry.NotifyWrite("PsmfGetEPWithId"); } - return 0; + return hleLogSuccessI(ME, 0); } static u32 scePsmfGetEPWithTimestamp(u32 psmfStruct, u32 ts, u32 entryAddr) { Psmf *psmf = getPsmf(psmfStruct); if (!psmf) { - ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x): invalid psmf", psmfStruct, ts, entryAddr); - return ERROR_PSMF_NOT_INITIALIZED; + return hleLogError(ME, ERROR_PSMF_NOT_INITIALIZED, "invalid psmf"); } - DEBUG_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x)", psmfStruct, ts, entryAddr); if (ts < psmf->presentationStartTime) { - ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid timestamp", psmfStruct, ts); - return ERROR_PSMF_NOT_FOUND; + return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid timestamp"); } int epid = psmf->FindEPWithTimestamp(ts); if (epid < 0 || epid >= (int)psmf->EPMap.size()) { - ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid id", psmfStruct, epid); - return ERROR_PSMF_NOT_FOUND; + return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid id"); } - if (Memory::IsValidAddress(entryAddr)) { - Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]); + auto entry = PSPPointer::Create(entryAddr); + if (entry.IsValid()) { + *entry = psmf->EPMap[epid]; + entry.NotifyWrite("PsmfGetEPWithTimestamp"); } - return 0; + return hleLogSuccessI(ME, 0); } static u32 scePsmfGetEPidWithTimestamp(u32 psmfStruct, u32 ts) From a86b904cf2e0b155acff7254a6cab3389642dcd8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 09:01:23 -0700 Subject: [PATCH 77/99] Ge: Switch from ReadStruct to PSPPointer. --- Core/HLE/sceGe.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index e56f631c16..46230373d2 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -427,8 +427,6 @@ static int sceGeBreak(u32 mode, u32 unknownPtr) { } static u32 sceGeSetCallback(u32 structAddr) { - DEBUG_LOG(SCEGE, "sceGeSetCallback(struct=%08x)", structAddr); - int cbID = -1; for (size_t i = 0; i < ARRAY_SIZE(ge_used_callbacks); ++i) { if (!ge_used_callbacks[i]) { @@ -438,12 +436,13 @@ static u32 sceGeSetCallback(u32 structAddr) { } if (cbID == -1) { - WARN_LOG(SCEGE, "sceGeSetCallback(): out of callback ids"); - return SCE_KERNEL_ERROR_OUT_OF_MEMORY; + return hleLogWarning(SCEGE, SCE_KERNEL_ERROR_OUT_OF_MEMORY, "out of callback ids"); } ge_used_callbacks[cbID] = true; - Memory::ReadStruct(structAddr, &ge_callback_data[cbID]); + auto callbackData = PSPPointer::Create(structAddr); + ge_callback_data[cbID] = *callbackData; + callbackData.NotifyRead("GeSetCallback"); int subIntrBase = __GeSubIntrBase(cbID); @@ -458,7 +457,7 @@ static u32 sceGeSetCallback(u32 structAddr) { sceKernelEnableSubIntr(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL); } - return cbID; + return hleLogSuccessI(SCEGE, cbID); } static int sceGeUnsetCallback(u32 cbID) { @@ -603,7 +602,7 @@ const HLEFunction sceGe_user[] = { {0XB287BD61, &WrapU_U, "sceGeDrawSync", 'x', "x" }, {0XB448EC0D, &WrapI_UU, "sceGeBreak", 'i', "xx" }, {0X4C06E472, &WrapI_V, "sceGeContinue", 'i', "" }, - {0XA4FC06A4, &WrapU_U, "sceGeSetCallback", 'x', "x" }, + {0XA4FC06A4, &WrapU_U, "sceGeSetCallback", 'i', "p" }, {0X05DB22CE, &WrapI_U, "sceGeUnsetCallback", 'i', "x" }, {0X1F6752AD, &WrapU_V, "sceGeEdramGetSize", 'x', "" }, {0XB77905EA, &WrapU_I, "sceGeEdramSetAddrTranslation", 'x', "i" }, From 79c7a91d989e1845bdc9298851f1f30074e0acb0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 09:02:59 -0700 Subject: [PATCH 78/99] Font: Remove needless ReadStruct(). --- Core/HLE/sceFont.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index e91eeef88b..d55ce61514 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -486,8 +486,8 @@ public: // For save states only. } - FontLib(u32 paramPtr, u32 errorCodePtr) { - Memory::ReadStruct(paramPtr, ¶ms_); + FontLib(FontNewLibParams *params, u32 errorCodePtr) { + params_ = *params; if (params_.numFonts > 9) { params_.numFonts = 9; } @@ -998,7 +998,7 @@ static u32 sceFontNewLib(u32 paramPtr, u32 errorCodePtr) { INFO_LOG(SCEFONT, "sceFontNewLib(%08x, %08x)", paramPtr, errorCodePtr); *errorCode = 0; - FontLib *newLib = new FontLib(paramPtr, errorCodePtr); + FontLib *newLib = new FontLib(params, errorCodePtr); fontLibList.push_back(newLib); // The game should never see this value, the return value is replaced // by the action. Except if we disable the alloc, in this case we return From dd0de46ed96d8408174023a26d4a6c626996cb71 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 09:13:07 -0700 Subject: [PATCH 79/99] Io: Use PSPPointer instead of struct temps. --- Core/Dialog/PSPGamedataInstallDialog.cpp | 11 ++--- Core/HLE/sceIo.cpp | 51 ++++++++++++++---------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index 0e550a9d2e..9fe30db3c8 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -116,9 +116,9 @@ int PSPGamedataInstallDialog::Update(int animSpeed) { WriteSfoFile(); // TODO: What is this? Should one of these update per file or anything? - request.unknownResult1 = readFiles; - request.unknownResult2 = readFiles; - Memory::WriteStruct(param.ptr, &request); + param->unknownResult1 = readFiles; + param->unknownResult2 = readFiles; + param.NotifyWrite("DialogResult"); ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); } @@ -250,8 +250,9 @@ void PSPGamedataInstallDialog::UpdateProgress() { progressValue = (int)((allReadSize * 100) / allFilesSize); else progressValue = 100; - request.progress = progressValue; - Memory::WriteStruct(param.ptr, &request); + + param->progress = progressValue; + param.NotifyWrite("DialogResult"); } void PSPGamedataInstallDialog::DoState(PointerWrap &p) { diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 575f4be959..9e161828a8 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -931,12 +931,12 @@ static u32 sceIoGetstat(const char *filename, u32 addr) { // TODO: Improve timing (although this seems normally slow..) int usec = 1000; - SceIoStat stat; + auto stat = PSPPointer::Create(addr); PSPFileInfo info = pspFileSystem.GetFileInfo(filename); if (info.exists) { - __IoGetStat(&stat, info); - if (Memory::IsValidAddress(addr)) { - Memory::WriteStruct(addr, &stat); + if (stat.IsValid()) { + __IoGetStat(stat, info); + stat.NotifyWrite("IoGetstat"); DEBUG_LOG(SCEIO, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr, info.startSector); return hleDelayResult(0, "io getstat", usec); } else { @@ -1798,19 +1798,23 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; } // TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace? - if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr - u32 pointer = Memory::Read_U32(argAddr); + if (Memory::IsValidRange(argAddr, 4) && argLen >= 4) { // NOTE: not outPtr + u32 pointer = Memory::ReadUnchecked_U32(argAddr); u32 sectorSize = 0x200; u32 memStickSectorSize = 32 * 1024; u32 sectorCount = memStickSectorSize / sectorSize; u64 freeSize = 1 * 1024 * 1024 * 1024; - DeviceSize deviceSize; - deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); - deviceSize.freeClusters = deviceSize.maxClusters; - deviceSize.maxSectors = deviceSize.maxClusters; - deviceSize.sectorSize = sectorSize; - deviceSize.sectorCount = sectorCount; - Memory::WriteStruct(pointer, &deviceSize); + + auto deviceSize = PSPPointer::Create(pointer); + if (deviceSize.IsValid()) { + deviceSize->maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); + deviceSize->freeClusters = deviceSize->maxClusters; + deviceSize->maxSectors = deviceSize->maxClusters; + deviceSize->sectorSize = sectorSize; + deviceSize->sectorCount = sectorCount; + deviceSize.NotifyWrite("ms0:02425818"); + } + return 0; } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; @@ -1821,8 +1825,8 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) { return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; } - if (Memory::IsValidAddress(outPtr) && outLen == 4) { - Memory::Write_U32(0, outPtr); + if (Memory::IsValidRange(outPtr, 4) && outLen == 4) { + Memory::WriteUnchecked_U32(0, outPtr); return 0; } else { ERROR_LOG(SCEIO, "Failed 0x02425824 fat"); @@ -1939,13 +1943,16 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o u32 memStickSectorSize = 32 * 1024; u32 sectorCount = memStickSectorSize / sectorSize; u64 freeSize = 1 * 1024 * 1024 * 1024; - DeviceSize deviceSize; - deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); - deviceSize.freeClusters = deviceSize.maxClusters; - deviceSize.maxSectors = deviceSize.maxClusters; - deviceSize.sectorSize = sectorSize; - deviceSize.sectorCount = sectorCount; - Memory::WriteStruct(pointer, &deviceSize); + + auto deviceSize = PSPPointer::Create(pointer); + if (deviceSize.IsValid()) { + deviceSize->maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); + deviceSize->freeClusters = deviceSize->maxClusters; + deviceSize->maxSectors = deviceSize->maxClusters; + deviceSize->sectorSize = sectorSize; + deviceSize->sectorCount = sectorCount; + deviceSize.NotifyWrite("fatms0:02425818"); + } return 0; } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; From ccd4e921846a0174f1b2d4877ad7a3543af79343 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 10:37:58 -0700 Subject: [PATCH 80/99] Kernel: Use PSPPointer for status objects. Code makes more sense when reading ->size rather than offset 0. --- Core/HLE/sceKernel.cpp | 38 ++++++++-------- Core/HLE/sceKernelEventFlag.cpp | 9 ++-- Core/HLE/sceKernelInterrupt.cpp | 2 +- Core/HLE/sceKernelMbx.cpp | 20 ++++----- Core/HLE/sceKernelMemory.cpp | 58 +++++++++++------------- Core/HLE/sceKernelModule.cpp | 74 +++++++++++++++--------------- Core/HLE/sceKernelMsgPipe.cpp | 30 +++++-------- Core/HLE/sceKernelMutex.cpp | 79 +++++++++++---------------------- Core/HLE/sceKernelSemaphore.cpp | 28 +++++------- Core/HLE/sceKernelThread.cpp | 15 ++++--- Core/MemMap.h | 15 ++++++- Core/MemMapHelpers.h | 27 ----------- 12 files changed, 172 insertions(+), 223 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 738ce25303..cb858c0e90 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -646,12 +646,12 @@ struct SystemStatus { static int sceKernelReferSystemStatus(u32 statusPtr) { DEBUG_LOG(SCEKERNEL, "sceKernelReferSystemStatus(%08x)", statusPtr); - if (Memory::IsValidAddress(statusPtr)) { - SystemStatus status; - memset(&status, 0, sizeof(SystemStatus)); - status.size = sizeof(SystemStatus); + auto status = PSPPointer::Create(statusPtr); + if (status.IsValid()) { + memset((SystemStatus *)status, 0, sizeof(SystemStatus)); + status->size = sizeof(SystemStatus); // TODO: Fill in the struct! - Memory::WriteStruct(statusPtr, &status); + status.NotifyWrite("SystemStatus"); } return 0; } @@ -684,11 +684,11 @@ static u32 sceKernelReferThreadProfiler(u32 statusPtr) { // Can we confirm that the struct above is the right struct? // If so, re-enable this code. - //DebugProfilerRegs regs; - //memset(®s, 0, sizeof(regs)); + //auto regs = PSPPointer::Create(statusPtr); // TODO: fill the struct. - //if (Memory::IsValidAddress(statusPtr)) { - // Memory::WriteStruct(statusPtr, ®s); + //if (regs.IsValid()) { + // memset((DebugProfilerRegs *)regs, 0, sizeof(DebugProfilerRegs)); + // regs.NotifyWrite("ThreadProfiler"); //} return 0; } @@ -715,14 +715,14 @@ const HLEFunction ThreadManForUser[] = {0XD6DA4BA1, &WrapI_CUIIU, "sceKernelCreateSema", 'i', "sxiix" }, {0X28B6489C, &WrapI_I, "sceKernelDeleteSema", 'i', "i" }, {0X58B1F937, &WrapI_II, "sceKernelPollSema", 'i', "ii" }, - {0XBC6FEBC5, &WrapI_IU, "sceKernelReferSemaStatus", 'i', "ix" }, + {0XBC6FEBC5, &WrapI_IU, "sceKernelReferSemaStatus", 'i', "ip" }, {0X3F53E640, &WrapI_II, "sceKernelSignalSema", 'i', "ii" }, {0X4E3A1105, &WrapI_IIU, "sceKernelWaitSema", 'i', "iix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X6D212BAC, &WrapI_IIU, "sceKernelWaitSemaCB", 'i', "iix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X60107536, &WrapI_U, "sceKernelDeleteLwMutex", 'i', "x" }, {0X19CFF145, &WrapI_UCUIU, "sceKernelCreateLwMutex", 'i', "xsxix" }, - {0X4C145944, &WrapI_IU, "sceKernelReferLwMutexStatusByID", 'i', "ix" }, + {0X4C145944, &WrapI_IU, "sceKernelReferLwMutexStatusByID", 'i', "xp" }, // NOTE: LockLwMutex, UnlockLwMutex, and ReferLwMutexStatus are in Kernel_Library, see sceKernelInterrupt.cpp. // The below should not be called directly. //{0x71040D5C, nullptr, "_sceKernelTryLockLwMutex", '?', "" }, @@ -736,7 +736,7 @@ const HLEFunction ThreadManForUser[] = {0X6B30100F, &WrapI_II, "sceKernelUnlockMutex", 'i', "ii" }, {0XB7D098C6, &WrapI_CUIU, "sceKernelCreateMutex", 'i', "sxix" }, {0X0DDCD2C9, &WrapI_II, "sceKernelTryLockMutex", 'i', "ii" }, - {0XA9C2CB9A, &WrapI_IU, "sceKernelReferMutexStatus", 'i', "ix" }, + {0XA9C2CB9A, &WrapI_IU, "sceKernelReferMutexStatus", 'i', "ip" }, {0X87D9223C, &WrapI_IIU, "sceKernelCancelMutex", 'i', "iix" }, {0XFCCFAD26, &WrapI_I, "sceKernelCancelWakeupThread", 'i', "i" }, @@ -818,7 +818,7 @@ const HLEFunction ThreadManForUser[] = {0XF3986382, &WrapI_IUU, "sceKernelReceiveMbxCB", 'i', "ixx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X0D81716A, &WrapI_IU, "sceKernelPollMbx", 'i', "ix" }, {0X87D4DD36, &WrapI_IU, "sceKernelCancelReceiveMbx", 'i', "ix" }, - {0XA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ix" }, + {0XA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ip" }, {0X7C0DC2A0, &WrapI_CIUUU, "sceKernelCreateMsgPipe", 'i', "sixxx" }, {0XF0B7DA1C, &WrapI_I, "sceKernelDeleteMsgPipe", 'i', "i" }, @@ -829,7 +829,7 @@ const HLEFunction ThreadManForUser[] = {0XFBFA697D, &WrapI_IUUUUU, "sceKernelReceiveMsgPipeCB", 'i', "ixxxxx" }, {0XDF52098F, &WrapI_IUUUU, "sceKernelTryReceiveMsgPipe", 'i', "ixxxx" }, {0X349B864D, &WrapI_IUU, "sceKernelCancelMsgPipe", 'i', "ixx" }, - {0X33BE4024, &WrapI_IU, "sceKernelReferMsgPipeStatus", 'i', "ix" }, + {0X33BE4024, &WrapI_IU, "sceKernelReferMsgPipeStatus", 'i', "ip" }, {0X56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxx" }, {0X89B3D48C, &WrapI_I, "sceKernelDeleteVpl", 'i', "i" }, @@ -838,7 +838,7 @@ const HLEFunction ThreadManForUser[] = {0XAF36D708, &WrapI_IUU, "sceKernelTryAllocateVpl", 'i', "ixx" }, {0XB736E9FF, &WrapI_IU, "sceKernelFreeVpl", 'i', "ix" }, {0X1D371B8A, &WrapI_IU, "sceKernelCancelVpl", 'i', "ix" }, - {0X39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ix" }, + {0X39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ip" }, {0XC07BB470, &WrapI_CUUUUU, "sceKernelCreateFpl", 'i', "sxxxxx" }, {0XED1410E0, &WrapI_I, "sceKernelDeleteFpl", 'i', "i" }, @@ -847,7 +847,7 @@ const HLEFunction ThreadManForUser[] = {0X623AE665, &WrapI_IU, "sceKernelTryAllocateFpl", 'i', "ix" }, {0XF6414A71, &WrapI_IU, "sceKernelFreeFpl", 'i', "ix" }, {0XA8AA591F, &WrapI_IU, "sceKernelCancelFpl", 'i', "ix" }, - {0XD8199E4C, &WrapI_IU, "sceKernelReferFplStatus", 'i', "ix" }, + {0XD8199E4C, &WrapI_IU, "sceKernelReferFplStatus", 'i', "ip" }, {0X20FFF560, &WrapU_CU, "sceKernelCreateVTimer", 'x', "sx", HLE_NOT_IN_INTERRUPT }, {0X328F9E52, &WrapU_I, "sceKernelDeleteVTimer", 'x', "i", HLE_NOT_IN_INTERRUPT }, @@ -866,7 +866,7 @@ const HLEFunction ThreadManForUser[] = {0X8DAFF657, &WrapI_CUUUUU, "sceKernelCreateTlspl", 'i', "sxxxxx" }, {0X32BF938E, &WrapI_I, "sceKernelDeleteTlspl", 'i', "i" }, - {0X721067F3, &WrapI_IU, "sceKernelReferTlsplStatus", 'i', "ix" }, + {0X721067F3, &WrapI_IU, "sceKernelReferTlsplStatus", 'i', "xp" }, // Not completely certain about args. {0X4A719FB2, &WrapI_I, "sceKernelFreeTlspl", 'i', "i" }, // Internal. Takes (uid, &addr) as parameters... probably. @@ -931,7 +931,7 @@ const HLEFunction ThreadManForKernel[] = {0xF3986382, &WrapI_IUU, "sceKernelReceiveMbxCB", 'i', "ixx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0x0D81716A, &WrapI_IU, "sceKernelPollMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, {0x87D4DD36, &WrapI_IU, "sceKernelCancelReceiveMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, - {0xA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0xA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ip", HLE_KERNEL_SYSCALL }, {0x56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxx", HLE_KERNEL_SYSCALL }, {0x89B3D48C, &WrapI_I, "sceKernelDeleteVpl", 'i', "i", HLE_KERNEL_SYSCALL }, {0xBED27435, &WrapI_IUUU, "sceKernelAllocateVpl", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, @@ -939,7 +939,7 @@ const HLEFunction ThreadManForKernel[] = {0xAF36D708, &WrapI_IUU, "sceKernelTryAllocateVpl", 'i', "ixx", HLE_KERNEL_SYSCALL }, {0xB736E9FF, &WrapI_IU, "sceKernelFreeVpl", 'i', "ix", HLE_KERNEL_SYSCALL }, {0x1D371B8A, &WrapI_IU, "sceKernelCancelVpl", 'i', "ix", HLE_KERNEL_SYSCALL }, - {0x39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ip", HLE_KERNEL_SYSCALL }, }; void Register_ThreadManForUser() diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 9ea935f06d..b903f950d3 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -522,14 +522,17 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr) { u32 error; EventFlag *e = kernelObjects.Get(id, error); if (e) { - if (!Memory::IsValidAddress(statusPtr)) + auto status = PSPPointer::Create(statusPtr); + if (!status.IsValid()) return hleLogWarning(SCEKERNEL, -1, "invalid ptr"); HLEKernel::CleanupWaitingThreads(WAITTYPE_EVENTFLAG, id, e->waitingThreads); e->nef.numWaitThreads = (int) e->waitingThreads.size(); - if (Memory::Read_U32(statusPtr) != 0) - Memory::WriteStruct(statusPtr, &e->nef); + if (status->size != 0) { + *status = e->nef; + status.NotifyWrite("EventFlagStatus"); + } return hleLogSuccessI(SCEKERNEL, 0); } else { return hleLogDebug(SCEKERNEL, error, "invalid event flag"); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index cffedaeb46..4c91ffe529 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -677,7 +677,7 @@ const HLEFunction Kernel_Library[] = {0XBEA46419, &WrapI_UIU, "sceKernelLockLwMutex", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X1FC64E09, &WrapI_UIU, "sceKernelLockLwMutexCB", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X15B6446B, &WrapI_UI, "sceKernelUnlockLwMutex", 'i', "xi" }, - {0XC1734599, &WrapI_UU, "sceKernelReferLwMutexStatus", 'i', "xx" }, + {0XC1734599, &WrapI_UU, "sceKernelReferLwMutexStatus", 'i', "xp" }, {0X293B45B8, &WrapI_V, "sceKernelGetThreadId", 'i', "" }, {0XD13BDE95, &WrapI_V, "sceKernelCheckThreadStack", 'i', "" }, {0X1839852A, &WrapU_UUU, "sceKernelMemcpy", 'x', "xxx" }, diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 412c70188d..0a5f132f01 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -552,19 +552,17 @@ int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr) return 0; } -int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) -{ +int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) { u32 error; Mbx *m = kernelObjects.Get(id, error); - if (!m) - { - ERROR_LOG(SCEKERNEL, "sceKernelReferMbxStatus(%i, %08x): invalid mbx id", id, infoAddr); - return error; + if (!m) { + return hleLogError(SCEKERNEL, error, "invalid mbx id"); } // Should we crash the thread somehow? - if (!Memory::IsValidAddress(infoAddr)) - return -1; + auto info = PSPPointer::Create(infoAddr); + if (!info.IsValid()) + return hleLogError(SCEKERNEL, -1, "invalid pointer"); for (int i = 0, n = m->nmb.numMessages; i < n; ++i) m->nmb.packetListHead = Memory::Read_U32(m->nmb.packetListHead); @@ -572,10 +570,10 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) HLEKernel::CleanupWaitingThreads(WAITTYPE_MBX, id, m->waitingThreads); // For whatever reason, it won't write if the size (first member) is 0. - if (Memory::Read_U32(infoAddr) != 0) - { + if (info->size != 0) { m->nmb.numWaitThreads = (int) m->waitingThreads.size(); - Memory::WriteStruct(infoAddr, &m->nmb); + *info = m->nmb; + info.NotifyWrite("MbxStatus"); } return 0; diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 3c5cce3a9f..ac853d5bb3 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -881,30 +881,26 @@ int sceKernelCancelFpl(SceUID uid, u32 numWaitThreadsPtr) } } -int sceKernelReferFplStatus(SceUID uid, u32 statusPtr) -{ +int sceKernelReferFplStatus(SceUID uid, u32 statusPtr) { u32 error; FPL *fpl = kernelObjects.Get(uid, error); - if (fpl) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x)", uid, statusPtr); + if (fpl) { // Refresh waiting threads and free block count. __KernelSortFplThreads(fpl); fpl->nf.numWaitThreads = (int) fpl->waitingThreads.size(); fpl->nf.numFreeBlocks = 0; - for (int i = 0; i < (int)fpl->nf.numBlocks; ++i) - { + for (int i = 0; i < (int)fpl->nf.numBlocks; ++i) { if (!fpl->blocks[i]) ++fpl->nf.numFreeBlocks; } - if (Memory::Read_U32(statusPtr) != 0) - Memory::WriteStruct(statusPtr, &fpl->nf); - return 0; - } - else - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x): invalid fpl", uid, statusPtr); - return error; + auto status = PSPPointer::Create(statusPtr); + if (status.IsValid() && status->size != 0) { + *status = fpl->nf; + status.NotifyWrite("FplStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error, "invalid fpl"); } } @@ -1782,8 +1778,6 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr) { u32 error; VPL *vpl = kernelObjects.Get(uid, error); if (vpl) { - DEBUG_LOG(SCEKERNEL, "sceKernelReferVplStatus(%i, %08x)", uid, infoPtr); - __KernelSortVplThreads(vpl); vpl->nv.numWaitThreads = (int) vpl->waitingThreads.size(); if (vpl->header.IsValid()) { @@ -1791,12 +1785,14 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr) { } else { vpl->nv.freeSize = vpl->alloc.GetTotalFreeBytes(); } - if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) { - Memory::WriteStruct(infoPtr, &vpl->nv); + auto info = PSPPointer::Create(infoPtr); + if (info.IsValid() && info->size != 0) { + *info = vpl->nv; + info.NotifyWrite("VplStatus"); } - return 0; + return hleLogSuccessI(SCEKERNEL, 0); } else { - return error; + return hleLogError(SCEKERNEL, error, "invalid vpl"); } } @@ -2270,23 +2266,23 @@ int sceKernelFreeTlspl(SceUID uid) return error; } -int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr) -{ - DEBUG_LOG(SCEKERNEL, "sceKernelReferTlsplStatus(%08x, %08x)", uid, infoPtr); +int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr) { u32 error; TLSPL *tls = kernelObjects.Get(uid, error); - if (tls) - { + if (tls) { // Update the waiting threads in case of deletions, etc. __KernelSortTlsplThreads(tls); tls->ntls.numWaitThreads = (int) tls->waitingThreads.size(); - if (Memory::Read_U32(infoPtr) != 0) - Memory::WriteStruct(infoPtr, &tls->ntls); - return 0; + auto info = PSPPointer::Create(infoPtr); + if (info.IsValid() && info->size != 0) { + *info = tls->ntls; + info.NotifyWrite("TlsplStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error, "invalid tlspl"); } - else - return error; } const HLEFunction SysMemUserForUser[] = { diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 502c48fc2e..a32604e928 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -247,6 +247,10 @@ enum NativeModuleStatus { class PSPModule : public KernelObject { public: + PSPModule() { + modulePtr.ptr = 0; + } + ~PSPModule() { if (memoryBlockAddr) { // If it's either below user memory, or using a high kernel bit, it's in kernel. @@ -258,9 +262,9 @@ public: g_symbolMap->UnloadModule(memoryBlockAddr, memoryBlockSize); } - if (modulePtr) { + if (modulePtr.ptr) { //Only alloc at kernel memory. - kernelMemory.Free(modulePtr); + kernelMemory.Free(modulePtr.ptr); } } const char *GetName() override { return nm.name; } @@ -320,7 +324,7 @@ public: } if (s >= 5) { - Do(p, modulePtr); + Do(p, modulePtr.ptr); } ModuleWaitingThread mwt = {0}; @@ -454,7 +458,7 @@ public: u32 memoryBlockAddr = 0; u32 memoryBlockSize = 0; - u32 modulePtr = 0; + PSPPointer modulePtr; bool isFake = false; }; @@ -1616,11 +1620,13 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load u32 moduleSize = sizeof(module->nm); char tag[32]; snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid); - module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag); + module->modulePtr.ptr = kernelMemory.Alloc(moduleSize, true, tag); // Fill the struct. - if (Memory::IsValidAddress(module->modulePtr)) - Memory::WriteStruct(module->modulePtr, &module->nm); + if (module->modulePtr.IsValid()) { + *module->modulePtr = module->nm; + module->modulePtr.NotifyWrite("KernelModule"); + } error = 0; return module; @@ -1763,17 +1769,18 @@ void __KernelLoadReset() { } bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string) { - SceKernelLoadExecParam param; + SceKernelLoadExecParam param{}; PSP_SetLoading("Loading exec..."); - if (paramPtr) - Memory::ReadStruct(paramPtr, ¶m); - else - memset(¶m, 0, sizeof(SceKernelLoadExecParam)); + auto paramData = PSPPointer::Create(paramPtr); + if (paramData.IsValid()) { + param = *paramData; + paramData.NotifyRead("KernelLoadExec"); + } - u8 *param_argp = 0; - u8 *param_key = 0; + u8 *param_argp = nullptr; + u8 *param_key = nullptr; if (param.args > 0) { u32 argpAddr = param.argp; param_argp = new u8[param.args]; @@ -1792,10 +1799,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str if (!info.exists) { ERROR_LOG(LOADER, "Failed to load executable %s - file doesn't exist", filename); *error_string = StringFromFormat("Could not find executable %s", filename); - if (paramPtr) { - if (param_argp) delete[] param_argp; - if (param_key) delete[] param_key; - } + delete[] param_argp; + delete[] param_key; __KernelShutdown(); return false; } @@ -1817,10 +1822,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str ERROR_LOG(LOADER, "Failed to load module %s", filename); *error_string = "Failed to load executable: " + *error_string; delete [] temp; - if (paramPtr) { - if (param_argp) delete[] param_argp; - if (param_key) delete[] param_key; - } + delete[] param_argp; + delete[] param_key; return false; } @@ -1857,8 +1860,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str __KernelStartIdleThreads(module->GetUID()); - if (param_argp) delete[] param_argp; - if (param_key) delete[] param_key; + delete[] param_argp; + delete[] param_key; hleSkipDeadbeef(); return true; @@ -1989,11 +1992,13 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) { u32 moduleSize = sizeof(module->nm); char tag[32]; snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid); - module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag); + module->modulePtr.ptr = kernelMemory.Alloc(moduleSize, true, tag); // Fill the struct. - if(Memory::IsValidAddress(module->modulePtr)) - Memory::WriteStruct(module->modulePtr, &module->nm); + if (module->modulePtr.IsValid()) { + *module->modulePtr = module->nm; + module->modulePtr.NotifyWrite("KernelModule"); + } // TODO: It would be more ideal to allocate memory for this module. @@ -2147,10 +2152,7 @@ int KernelStartModule(SceUID moduleId, u32 argsize, u32 argAddr, u32 returnValue static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) { - SceKernelSMOption smoption = {0}; - if (optionAddr) { - Memory::ReadStruct(optionAddr, &smoption); - } + auto smoption = PSPPointer::Create(optionAddr); u32 error; PSPModule *module = kernelObjects.Get(moduleId, error); if (!module) { @@ -2176,7 +2178,7 @@ static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 ret moduleId,argsize,argAddr,returnValueAddr,optionAddr); bool needsWait; - int ret = KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, optionAddr ? &smoption : nullptr, &needsWait); + int ret = KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, smoption.PtrOrNull(), &needsWait); if (needsWait) { __KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "started module"); @@ -2464,8 +2466,8 @@ u32 sceKernelFindModuleByUID(u32 uid) ERROR_LOG(SCEMODULE, "0 = sceKernelFindModuleByUID(%d): Module Not Found or Fake", uid); return 0; } - INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByUID(%d)", module->modulePtr, uid); - return module->modulePtr; + INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByUID(%d)", module->modulePtr.ptr, uid); + return module->modulePtr.ptr; } u32 sceKernelFindModuleByName(const char *name) @@ -2477,8 +2479,8 @@ u32 sceKernelFindModuleByName(const char *name) continue; if (strcmp(name, module->nm.name) == 0) { if (!module->isFake) { - INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr, name); - return module->modulePtr; + INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr.ptr, name); + return module->modulePtr.ptr; } else { WARN_LOG(SCEMODULE, "0 = sceKernelFindModuleByName(%s): Module Fake", name); diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index 253bf9ae65..b16e8060e2 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -1006,33 +1006,27 @@ int sceKernelCancelMsgPipe(SceUID uid, u32 numSendThreadsAddr, u32 numReceiveThr return 0; } -int sceKernelReferMsgPipeStatus(SceUID uid, u32 statusPtr) -{ +int sceKernelReferMsgPipeStatus(SceUID uid, u32 statusPtr) { u32 error; MsgPipe *m = kernelObjects.Get(uid, error); - if (m) - { - if (!Memory::IsValidAddress(statusPtr)) - { - ERROR_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x): invalid address", uid, statusPtr); - return -1; + if (m) { + auto status = PSPPointer::Create(statusPtr); + if (!status.IsValid()) { + return hleLogError(SCEKERNEL, -1, "invalid address"); } - DEBUG_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x)", uid, statusPtr); - // Clean up any that have timed out. m->SortReceiveThreads(); m->SortSendThreads(); m->nmp.numSendWaitThreads = (int) m->sendWaitingThreads.size(); m->nmp.numReceiveWaitThreads = (int) m->receiveWaitingThreads.size(); - if (Memory::Read_U32(statusPtr) != 0) - Memory::WriteStruct(statusPtr, &m->nmp); - return 0; - } - else - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x): bad message pipe", uid, statusPtr); - return error; + if (status->size != 0) { + *status = m->nmp; + status.NotifyWrite("MsgPipeStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error, "bad message pipe"); } } diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 899230142b..2cba7b5ca5 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -664,31 +664,27 @@ int sceKernelUnlockMutex(SceUID id, int count) return 0; } -int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) -{ +int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) { u32 error; PSPMutex *m = kernelObjects.Get(id, error); - if (!m) - { - ERROR_LOG(SCEKERNEL, "sceKernelReferMutexStatus(%i, %08x): invalid mutex id", id, infoAddr); - return error; + if (!m) { + return hleLogError(SCEKERNEL, error, "invalid mutex id"); } - DEBUG_LOG(SCEKERNEL, "sceKernelReferMutexStatus(%08x, %08x)", id, infoAddr); - // Should we crash the thread somehow? - if (!Memory::IsValidAddress(infoAddr)) - return -1; + auto info = PSPPointer::Create(infoAddr); + if (!info.IsValid()) + return hleLogError(SCEKERNEL, -1, "invalid pointer"); // Don't write if the size is 0. Anything else is A-OK, though, apparently. - if (Memory::Read_U32(infoAddr) != 0) - { + if (info->size != 0) { HLEKernel::CleanupWaitingThreads(WAITTYPE_MUTEX, id, m->waitingThreads); m->nm.numWaitThreads = (int) m->waitingThreads.size(); - Memory::WriteStruct(infoAddr, &m->nm); + *info = m->nm; + info.NotifyWrite("MutexStatus"); } - return 0; + return hleLogSuccessI(SCEKERNEL, 0); } int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr) @@ -1075,18 +1071,18 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count) return 0; } -static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) -{ +static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) { u32 error; LwMutex *m = kernelObjects.Get(uid, error); if (!m) - return error; + return hleLogError(SCEKERNEL, error, "invalid id"); // Should we crash the thread somehow? - if (!Memory::IsValidAddress(infoPtr)) - return -1; + auto info = PSPPointer::Create(infoPtr); + if (!info.IsValid()) + return hleLogError(SCEKERNEL, -1, "invalid pointer"); - if (Memory::Read_U32(infoPtr) != 0) + if (info->size != 0) { auto workarea = m->nm.workarea; @@ -1096,44 +1092,21 @@ static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) m->nm.currentCount = workarea->lockLevel; m->nm.lockThread = workarea->lockThread == 0 ? SceUID_le(-1) : workarea->lockThread; m->nm.numWaitThreads = (int) m->waitingThreads.size(); - Memory::WriteStruct(infoPtr, &m->nm); + *info = m->nm; + info.NotifyWrite("LwMutexStatus"); } - return 0; + return hleLogSuccessI(SCEKERNEL, 0); } -int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr) -{ - int error = __KernelReferLwMutexStatus(uid, infoPtr); - if (error >= 0) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferLwMutexStatusByID(%08x, %08x)", uid, infoPtr); - return error; - } - else - { - ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferLwMutexStatusByID(%08x, %08x)", error, uid, infoPtr); - return error; - } +int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr) { + return __KernelReferLwMutexStatus(uid, infoPtr); } -int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr) -{ - if (!Memory::IsValidAddress(workareaPtr)) { - ERROR_LOG(SCEKERNEL, "Bad workarea pointer for LwMutex"); - return SCE_KERNEL_ERROR_ACCESS_ERROR; - } - +int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr) { auto workarea = PSPPointer::Create(workareaPtr); + if (!workarea.IsValid()) { + return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_ACCESS_ERROR, "bad workarea pointer for LwMutex"); + } - int error = __KernelReferLwMutexStatus(workarea->uid, infoPtr); - if (error >= 0) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferLwMutexStatus(%08x, %08x)", workareaPtr, infoPtr); - return error; - } - else - { - ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferLwMutexStatus(%08x, %08x)", error, workareaPtr, infoPtr); - return error; - } + return __KernelReferLwMutexStatus(workarea->uid, infoPtr); } diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index d6ebbaf2c1..fffb16c20f 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -263,28 +263,24 @@ int sceKernelDeleteSema(SceUID id) } } -int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) -{ +int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) { u32 error; PSPSemaphore *s = kernelObjects.Get(id, error); - if (s) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferSemaStatus(%i, %08x)", id, infoPtr); - - if (!Memory::IsValidAddress(infoPtr)) - return -1; + if (s) { + auto info = PSPPointer::Create(infoPtr); + if (!info.IsValid()) + return hleLogWarning(SCEKERNEL, -1, "invalid pointer"); HLEKernel::CleanupWaitingThreads(WAITTYPE_SEMA, id, s->waitingThreads); s->ns.numWaitThreads = (int) s->waitingThreads.size(); - if (Memory::Read_U32(infoPtr) != 0) - Memory::WriteStruct(infoPtr, &s->ns); - return 0; - } - else - { - ERROR_LOG(SCEKERNEL, "sceKernelReferSemaStatus: error %08x", error); - return error; + if (info->size != 0) { + *info = s->ns; + info.NotifyWrite("SemaStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error); } } diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 86a234cd3b..ea9caaba1e 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2902,13 +2902,14 @@ int sceKernelGetCallbackCount(SceUID cbId) } } -int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr) -{ +int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr) { u32 error; PSPCallback *c = kernelObjects.Get(cbId, error); if (c) { - if (Memory::IsValidAddress(statusAddr) && Memory::Read_U32(statusAddr) != 0) { - Memory::WriteStruct(statusAddr, &c->nc); + auto status = PSPPointer::Create(statusAddr); + if (status.IsValid() && status->size != 0) { + *status = c->nc; + status.NotifyWrite("CallbackStatus"); return hleLogSuccessI(SCEKERNEL, 0); } else { return hleLogDebug(SCEKERNEL, 0, "struct size was 0"); @@ -3789,8 +3790,10 @@ int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr) { return hleReportError(SCEKERNEL, error, "bad handler id"); } - if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) { - Memory::WriteStruct(infoPtr, &teh->nteh); + auto info = PSPPointer::Create(infoPtr); + if (info.IsValid() && info->size != 0) { + *info = teh->nteh; + info.NotifyWrite("ThreadEventHandlerStatus"); return hleLogSuccessI(SCEKERNEL, 0); } else { return hleLogDebug(SCEKERNEL, 0, "struct size was 0"); diff --git a/Core/MemMap.h b/Core/MemMap.h index 306190f249..0e8863b090 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -441,11 +441,22 @@ struct PSPPointer #endif } - bool IsValid() const - { + bool IsValid() const { return Memory::IsValidRange(ptr, (u32)sizeof(T)); } + T *PtrOrNull() { + if (IsValid()) + return (T *)*this; + return nullptr; + } + + const T *PtrOrNull() const { + if (IsValid()) + return (const T *)*this; + return nullptr; + } + template void NotifyWrite(const char(&tag)[tagLen]) const { PSPPointerNotifyRW(1, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1); diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index e623d9e768..fdee215d03 100644 --- a/Core/MemMapHelpers.h +++ b/Core/MemMapHelpers.h @@ -109,31 +109,4 @@ inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len) void Memset(const u32 _Address, const u8 _Data, const u32 _iLength, const char *tag = "Memset"); -template -void ReadStruct(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - Memcpy(ptr, address, sz); -} - -template -void ReadStructUnchecked(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - MemcpyUnchecked(ptr, address, sz); -} - -template -void WriteStruct(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - Memcpy(address, ptr, sz); -} - -template -void WriteStructUnchecked(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - MemcpyUnchecked(address, ptr, sz); -} } From ba1ced49927738935c6b842e7f91bf044d2091c7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 11:08:11 -0700 Subject: [PATCH 81/99] softgpu: Avoid rect fast path with subpixel offset. It doesn't handle it right, see #15876. We still handle these with DrawRectangle(), which gets it right. --- GPU/Software/RasterizerRectangle.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index bf24c39192..e7986f6667 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -272,7 +272,9 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b bool orient_check = xdiff >= 0 && ydiff >= 0; // We already have a fast path for clear in ClearRectangle. bool state_check = !state.pixelID.clearMode && !state.samplerID.hasAnyMips && NoClampOrWrap(state, v0.texturecoords) && NoClampOrWrap(state, v1.texturecoords); - if ((coord_check || !state.enableTextures) && orient_check && state_check) { + // This doesn't work well with offset drawing, see #15876. Through never has a subpixel offset. + bool subpixel_check = ((v0.screenpos.x | v0.screenpos.y | v1.screenpos.x | v1.screenpos.y) & 0xF) != 0; + if ((coord_check || !state.enableTextures) && orient_check && state_check && subpixel_check) { binner.AddSprite(v0, v1); return true; } From af004dd53d60900804547c07fc0e9c5911f2329e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 11:19:24 -0700 Subject: [PATCH 82/99] softgpu: Validate alpha test properly for fastpath. We don't want a weird LESS test being mishandled. --- GPU/Software/RasterizerRectangle.cpp | 31 +++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index e7986f6667..387b150818 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -78,6 +78,27 @@ static inline Vec4IntResult SOFTRAST_CALL ModulateRGBA(Vec4IntArg prim_in, Vec4I return ToVec4IntResult(out); } +// Check if we can safely ignore the alpha test. +static inline bool AlphaTestIsNeedless(const PixelFuncID &pixelID) { + switch (pixelID.AlphaTestFunc()) { + case GE_COMP_NEVER: + case GE_COMP_EQUAL: + case GE_COMP_LESS: + case GE_COMP_LEQUAL: + return false; + + case GE_COMP_ALWAYS: + return true; + + case GE_COMP_NOTEQUAL: + case GE_COMP_GREATER: + case GE_COMP_GEQUAL: + return pixelID.alphaBlend && pixelID.alphaTestRef == 0 && !pixelID.hasAlphaTestMask; + } + + return false; +} + void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state) { const u8 *texptr = state.texptr[0]; @@ -132,11 +153,8 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran !pixelID.applyLogicOp && !pixelID.colorTest && !pixelID.dithering && - // TODO: Safe? - pixelID.AlphaTestFunc() != GE_COMP_ALWAYS && - pixelID.alphaTestRef == 0 && - !pixelID.hasAlphaTestMask && pixelID.alphaBlend && + AlphaTestIsNeedless(pixelID) && samplerID.useTextureAlpha && samplerID.TexFunc() == GE_TEXFUNC_MODULATE && !pixelID.applyColorWriteMask && @@ -205,11 +223,8 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran !pixelID.applyLogicOp && !pixelID.colorTest && !pixelID.dithering && - // TODO: Safe? - pixelID.AlphaTestFunc() != GE_COMP_ALWAYS && - pixelID.alphaTestRef == 0 && - !pixelID.hasAlphaTestMask && pixelID.alphaBlend && + AlphaTestIsNeedless(pixelID) && samplerID.useTextureAlpha && samplerID.TexFunc() == GE_TEXFUNC_MODULATE && !pixelID.applyColorWriteMask && From 15b73b56bb269581b395b7a0752c625fc6187f0d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 11:20:32 -0700 Subject: [PATCH 83/99] softgpu: Correct texture check in fastpath. This is the no textures option, so we shouldn't check sampler state at all. Presumably this was failing and reading uninitialized data... --- GPU/Software/RasterizerRectangle.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index 387b150818..96e7a7f0fd 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -225,8 +225,6 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran !pixelID.dithering && pixelID.alphaBlend && AlphaTestIsNeedless(pixelID) && - samplerID.useTextureAlpha && - samplerID.TexFunc() == GE_TEXFUNC_MODULATE && !pixelID.applyColorWriteMask && pixelID.FBFormat() == GE_FORMAT_5551) { if (v1.color0.a() == 0) From 0fc3619d1d188b6acc53ffb0479e395142082463 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 13:15:21 -0700 Subject: [PATCH 84/99] interp: Handle jumps in branch delay slots better. This matches tests from a PSP-2000. Seems to consistently run the instruction even if likely, which writes rd. If the likely branch is not taken, the jump in the delay slot is taken. However, it should cancel the rd write (not implemented here.) --- Core/MIPS/MIPSInt.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 46585b69a9..c9811f5c96 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -65,10 +65,15 @@ static inline void DelayBranchTo(u32 where) mipsr4k.inDelaySlot = true; } -static inline void SkipLikely() -{ - PC += 8; - --mipsr4k.downcount; +static inline void SkipLikely() { + MIPSInfo delaySlot = MIPSGetInfo(Memory::Read_Instruction(PC + 4, true)); + // Don't actually skip if it is a jump (seen in Brooktown High.) + if (delaySlot & IS_JUMP) { + PC += 4; + } else { + PC += 8; + --mipsr4k.downcount; + } } int MIPS_SingleStep() @@ -246,17 +251,21 @@ namespace MIPSInt void Int_JumpType(MIPSOpcode op) { if (mipsr4k.inDelaySlot) - _dbg_assert_msg_(false,"Jump in delay slot :("); + ERROR_LOG(CPU, "Jump in delay slot :("); u32 off = ((op & 0x03FFFFFF) << 2); u32 addr = (currentMIPS->pc & 0xF0000000) | off; switch (op>>26) { - case 2: DelayBranchTo(addr); break; //j + case 2: //j + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); + break; case 3: //jal - R(31) = PC + 8; - DelayBranchTo(addr); + R(MIPS_REG_RA) = PC + 8; + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); break; default: _dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted"); @@ -268,11 +277,8 @@ namespace MIPSInt { if (mipsr4k.inDelaySlot) { - // There's one of these in Star Soldier at 0881808c, which seems benign - it should probably be ignored. - if (op == 0x03e00008) - return; + // There's one of these in Star Soldier at 0881808c, which seems benign. ERROR_LOG(CPU, "Jump in delay slot :("); - _dbg_assert_msg_(false,"Jump in delay slot :("); } int rs = _RS; @@ -281,12 +287,15 @@ namespace MIPSInt switch (op & 0x3f) { case 8: //jr - DelayBranchTo(addr); + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); break; case 9: //jalr if (rd != 0) R(rd) = PC + 8; - DelayBranchTo(addr); + // Update rd, but otherwise do not take the branch if we're branching. + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); break; } } From 6b1e4806cfa18e21ed28b8e289356ee7bbc49e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 23:06:53 +0200 Subject: [PATCH 85/99] Don't parallelize shader compiles in debug mode, sync in debug allocator kills performance. --- GPU/Vulkan/ShaderManagerVulkan.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 439602ae46..52c2103a88 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -45,7 +45,7 @@ // Most drivers treat vkCreateShaderModule as pretty much a memcpy. What actually // takes time here, and makes this worthy of parallelization, is GLSLtoSPV. Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { - return Promise::Spawn(&g_threadManager, [=] { + auto compile = [=] { PROFILE_THIS_SCOPE("shadercomp"); std::string errorMessage; @@ -78,7 +78,14 @@ Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShade } return shaderModule; - }, TaskType::CPU_COMPUTE); + }; + +#ifdef _DEBUG + // Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator. + return Promise::AlreadyDone(compile()); +#else + return Promise::Spawn(&g_threadManager, compile, TaskType::CPU_COMPUTE); +#endif } From d3309dd8e9b1edcd2e4c2b9971996e4b5c20a163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 22:04:01 +0200 Subject: [PATCH 86/99] Minor refactor with pipelines in QueueRunner (makes more information available for easier debugging) --- Common/GPU/Vulkan/VulkanQueueRunner.cpp | 53 ++++++++++++----------- Common/GPU/Vulkan/VulkanQueueRunner.h | 4 +- Common/GPU/Vulkan/VulkanRenderManager.cpp | 3 ++ Common/GPU/Vulkan/VulkanRenderManager.h | 15 +++---- GPU/Common/FramebufferManagerCommon.cpp | 5 ++- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 92b7fb7e83..9b7ae11ea5 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -1144,8 +1144,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VKRFramebuffer *fb = step.render.framebuffer; - VkPipeline lastGraphicsPipeline = VK_NULL_HANDLE; - VkPipeline lastComputePipeline = VK_NULL_HANDLE; + VKRGraphicsPipeline *lastGraphicsPipeline = nullptr; + VKRComputePipeline *lastComputePipeline = nullptr; auto &commands = step.commands; @@ -1168,40 +1168,43 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::BIND_PIPELINE: { VkPipeline pipeline = c.pipeline.pipeline; - if (pipeline != lastGraphicsPipeline) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - pipelineLayout = c.pipeline.pipelineLayout; - lastGraphicsPipeline = pipeline; - // Reset dynamic state so it gets refreshed with the new pipeline. - lastStencilWriteMask = -1; - lastStencilCompareMask = -1; - lastStencilReference = -1; - } + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; + // Reset dynamic state so it gets refreshed with the new pipeline. + lastStencilWriteMask = -1; + lastStencilCompareMask = -1; + lastStencilReference = -1; break; } case VKRRenderCommand::BIND_GRAPHICS_PIPELINE: { - VkPipeline pipeline = c.graphics_pipeline.pipeline->BlockUntilReady(); - if (pipeline != lastGraphicsPipeline && pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - pipelineLayout = c.pipeline.pipelineLayout; - lastGraphicsPipeline = pipeline; - // Reset dynamic state so it gets refreshed with the new pipeline. - lastStencilWriteMask = -1; - lastStencilCompareMask = -1; - lastStencilReference = -1; + VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline; + if (graphicsPipeline != lastGraphicsPipeline) { + VkPipeline pipeline = graphicsPipeline->pipeline->BlockUntilReady(); + if (pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; + lastGraphicsPipeline = graphicsPipeline; + // Reset dynamic state so it gets refreshed with the new pipeline. + lastStencilWriteMask = -1; + lastStencilCompareMask = -1; + lastStencilReference = -1; + } } break; } case VKRRenderCommand::BIND_COMPUTE_PIPELINE: { - VkPipeline pipeline = c.graphics_pipeline.pipeline->BlockUntilReady(); - if (pipeline != lastComputePipeline && pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); - pipelineLayout = c.pipeline.pipelineLayout; - lastComputePipeline = pipeline; + VKRComputePipeline *computePipeline = c.compute_pipeline.pipeline; + if (computePipeline != lastComputePipeline) { + VkPipeline pipeline = computePipeline->pipeline->BlockUntilReady(); + if (pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; + lastComputePipeline = computePipeline; + } } break; } diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 1d1c4efe8f..ce6dc9f5eb 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -55,11 +55,11 @@ struct VkRenderData { VkPipelineLayout pipelineLayout; } pipeline; struct { - Promise *pipeline; + VKRGraphicsPipeline *pipeline; VkPipelineLayout pipelineLayout; } graphics_pipeline; struct { - Promise *pipeline; + VKRComputePipeline *pipeline; VkPipelineLayout pipelineLayout; } compute_pipeline; struct { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 7741fb2e44..a4e14a1b8d 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -80,8 +80,11 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { pipeline->Post(vkpipeline); } + // Having the desc stick around can be useful for debugging. +#ifndef _DEBUG delete desc; desc = nullptr; +#endif return success; } diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 4b69247525..67dff36880 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -141,20 +141,19 @@ struct VKRComputePipelineDesc { VkComputePipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; }; -// Wrapped pipeline, which will later allow for background compilation while emulating the rest of the frame. +// Wrapped pipeline. struct VKRGraphicsPipeline { VKRGraphicsPipeline() { pipeline = Promise::CreateEmpty(); } + ~VKRGraphicsPipeline() { + delete desc; + } - VKRGraphicsPipelineDesc *desc = nullptr; // While non-zero, is pending and pipeline isn't valid. - + VKRGraphicsPipelineDesc *desc = nullptr; Promise *pipeline; bool Create(VulkanContext *vulkan); - bool Pending() const { - return pipeline == VK_NULL_HANDLE && desc != nullptr; - } }; struct VKRComputePipeline { @@ -263,7 +262,7 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE }; - data.graphics_pipeline.pipeline = pipeline->pipeline; + data.graphics_pipeline.pipeline = pipeline; data.graphics_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); @@ -273,7 +272,7 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE }; - data.compute_pipeline.pipeline = pipeline->pipeline; + data.compute_pipeline.pipeline = pipeline; data.compute_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 00f5df9b07..5a74382fb6 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -808,7 +808,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra } if (dst != currentRenderVfb_ && tookActions) { - draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After FakeReinterpret"); + // Will probably just change the name of the current renderpass, since one was started by the reinterpret itself. + draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After Reinterpret"); } shaderManager_->DirtyLastShader(); @@ -2791,7 +2792,7 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX draw_->InvalidateCachedState(); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); } // The input is raw pixel coordinates, scale not taken into account. From 5f1ffeae701e15b72171804dba3f6859cb1033d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 22:41:57 +0200 Subject: [PATCH 87/99] Correct LogicOp API support detection --- Common/GPU/D3D11/thin3d_d3d11.cpp | 3 ++- Common/GPU/OpenGL/thin3d_gl.cpp | 4 ++++ Common/GPU/Vulkan/thin3d_vulkan.cpp | 1 + GPU/Common/GPUStateUtils.cpp | 1 - GPU/Vulkan/GPU_Vulkan.cpp | 2 +- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 09ffd4fa6a..0464235eb5 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -275,10 +275,11 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de if (SUCCEEDED(result)) { if (options.OutputMergerLogicOp) { // Actually, need to check that the format supports logic ops as well. - // Which normal UNORM formats don't seem to do. So meh. + // Which normal UNORM formats don't seem to do in D3D11. So meh. We can't enable logicOp support. // caps_.logicOpSupported = true; } } + IDXGIDevice* dxgiDevice = nullptr; IDXGIAdapter* adapter = nullptr; HRESULT hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgiDevice)); diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 130ba2d0fe..e25fd6b4f7 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -569,6 +569,10 @@ OpenGLContext::OpenGLContext() { caps_.fragmentShaderDepthWriteSupported = true; } +#ifndef USING_GLES2 + caps_.logicOpSupported = true; +#endif + // Interesting potential hack for emulating GL_DEPTH_CLAMP (use a separate varying, force depth in fragment shader): // This will induce a performance penalty on many architectures though so a blanket enable of this // is probably not a good idea. diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index d6cc587944..8a1112d446 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -795,6 +795,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) caps_.fragmentShaderInt32Supported = true; caps_.textureNPOTFullySupported = true; caps_.fragmentShaderDepthWriteSupported = true; + caps_.logicOpSupported = vulkan->GetDeviceFeatures().enabled.logicOp != 0; auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties; switch (deviceProps.vendorID) { diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index a78bad276b..7c3c7c4f22 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -937,7 +937,6 @@ static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq & } } -// Try to simulate some common logic ops. void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) { StencilValueType stencilType = STENCIL_VALUE_KEEP; if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 17f6f01aca..6cf56b17f3 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -254,7 +254,7 @@ void GPU_Vulkan::CheckGPUFeatures() { features |= GPU_SUPPORTS_DUALSOURCE_BLEND; } } - if (enabledFeatures.logicOp) { + if (draw_->GetDeviceCaps().logicOpSupported) { features |= GPU_SUPPORTS_LOGIC_OP; } if (draw_->GetDeviceCaps().anisoSupported) { From 8e39018b95b5c88d47ae04bc3334d82c13e13ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 22:51:50 +0200 Subject: [PATCH 88/99] Channel mask refactor --- GPU/Common/GPUStateUtils.cpp | 33 +++++++++++++++++-------------- GPU/Common/GPUStateUtils.h | 4 ++-- GPU/D3D11/StateMappingD3D11.cpp | 4 ++-- GPU/Directx9/StateMappingDX9.cpp | 9 ++------- GPU/GLES/StateMappingGLES.cpp | 4 ++-- GPU/Vulkan/StateMappingVulkan.cpp | 8 ++------ 6 files changed, 28 insertions(+), 34 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 7c3c7c4f22..c0886343a5 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -993,14 +993,11 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS // we read from the framebuffer (or a copy of it). // We also prepare uniformMask so that if doing this in the shader gets forced-on, // we have the right mask already. -void ConvertMaskState(GenericMaskState &maskState) { +void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported) { if (gstate_c.blueToAlpha) { maskState.applyFramebufferRead = false; maskState.uniformMask = 0xFF000000; - maskState.maskRGBA[0] = false; - maskState.maskRGBA[1] = false; - maskState.maskRGBA[2] = false; - maskState.maskRGBA[3] = true; + maskState.channelMask = 0x8; return; } @@ -1009,29 +1006,35 @@ void ConvertMaskState(GenericMaskState &maskState) { maskState.uniformMask = colorMask; maskState.applyFramebufferRead = false; + maskState.channelMask = 0; for (int i = 0; i < 4; i++) { int channelMask = colorMask & 0xFF; switch (channelMask) { case 0x0: - maskState.maskRGBA[i] = false; break; case 0xFF: - maskState.maskRGBA[i] = true; + maskState.channelMask |= 1 << i; break; default: - maskState.applyFramebufferRead = PSP_CoreParameter().compat.flags().ShaderColorBitmask; - maskState.maskRGBA[i] = true; - break; + if (shaderBitOpsSupported && PSP_CoreParameter().compat.flags().ShaderColorBitmask) { + // Shaders can emulate masking accurately. Let's make use of that. + maskState.applyFramebufferRead = true; + maskState.channelMask |= 1 << i; + } else { + // Use the old inaccurate heuristic. + if (channelMask >= 128) { + maskState.channelMask |= 1 << i; + } + } } colorMask >>= 8; } // Let's not write to alpha if stencil isn't enabled. - if (IsStencilTestOutputDisabled()) { - maskState.maskRGBA[3] = false; - } else if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) { - // If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel. - maskState.maskRGBA[3] = false; + // Also if the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel. + if (IsStencilTestOutputDisabled() || ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) { + maskState.channelMask &= ~8; + maskState.uniformMask &= ~0xFF000000; } } diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index dcbb134226..a1e7f0a06e 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -186,10 +186,10 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS struct GenericMaskState { bool applyFramebufferRead; uint32_t uniformMask; // For each bit, opposite to the PSP. - bool maskRGBA[4]; // true = draw, false = don't draw this channel + uint8_t channelMask; }; -void ConvertMaskState(GenericMaskState &maskState); +void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported); struct GenericStencilFuncState { bool enabled; diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index d764374e05..e42cf795b1 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -149,7 +149,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState); + ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -212,7 +212,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } } - keys_.blend.colorWriteMask = (maskState.maskRGBA[0] ? 1 : 0) | (maskState.maskRGBA[1] ? 2 : 0) | (maskState.maskRGBA[2] ? 4 : 0) | (maskState.maskRGBA[3] ? 8 : 0); + keys_.blend.colorWriteMask = maskState.channelMask; } } diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index dd0e7690dc..a8ba75f6c7 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -129,7 +129,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } else { GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState); + ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -177,12 +177,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { dxstate.blend.disable(); } - u32 mask = 0; - for (int i = 0; i < 4; i++) { - if (maskState.maskRGBA[i]) - mask |= 1 << i; - } - dxstate.colorMask.set(mask); + dxstate.colorMask.set(maskState.channelMask); } } diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 386f6d7f17..d3cbf11f21 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -145,7 +145,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } else { GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState); + ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -193,7 +193,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } } - int mask = (int)maskState.maskRGBA[0] | ((int)maskState.maskRGBA[1] << 1) | ((int)maskState.maskRGBA[2] << 2) | ((int)maskState.maskRGBA[3] << 3); + int mask = (int)maskState.channelMask; if (blendState.blendEnabled) { renderManager->SetBlendAndMask(mask, blendState.blendEnabled, glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor], diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 1955f1b772..b0625b4e72 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -157,7 +157,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState); + ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { @@ -203,11 +203,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag dynState.useBlendColor = false; } - key.colorWriteMask = - (maskState.maskRGBA[0] ? VK_COLOR_COMPONENT_R_BIT : 0) | - (maskState.maskRGBA[1] ? VK_COLOR_COMPONENT_G_BIT : 0) | - (maskState.maskRGBA[2] ? VK_COLOR_COMPONENT_B_BIT : 0) | - (maskState.maskRGBA[3] ? VK_COLOR_COMPONENT_A_BIT : 0); + key.colorWriteMask = maskState.channelMask; // flags match // Workaround proposed in #10421, for bug where the color write mask is not applied correctly on Adreno. if ((gstate.pmskc & 0x00FFFFFF) == 0x00FFFFFF && g_Config.bVendorBugChecksEnabled && draw_->GetBugs().Has(Draw::Bugs::COLORWRITEMASK_BROKEN_WITH_DEPTHTEST)) { From 87b14fe1c2a261a0aa5da0cdc6dca814d5c8eb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 23:28:03 +0200 Subject: [PATCH 89/99] Some more renaming and refactoring, extracted from a future PR --- GPU/Common/FragmentShaderGenerator.cpp | 7 +-- GPU/Common/GPUStateUtils.cpp | 60 +++++++++++++++----------- GPU/Common/GPUStateUtils.h | 16 ++++++- GPU/Common/ShaderId.cpp | 11 ++--- GPU/Common/ShaderId.h | 2 +- GPU/D3D11/StateMappingD3D11.cpp | 3 +- GPU/Directx9/StateMappingDX9.cpp | 3 +- GPU/GLES/StateMappingGLES.cpp | 3 +- GPU/Vulkan/StateMappingVulkan.cpp | 19 ++++---- 9 files changed, 69 insertions(+), 55 deletions(-) diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 1a694a5e60..2173130295 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -1060,9 +1060,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu return false; } - // TODO: This could support more ops using the shader blending mechanism. - LogicOpReplaceType replaceLogicOpType = (LogicOpReplaceType)id.Bits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2); - switch (replaceLogicOpType) { + // TODO: We could have a separate mechanism to support more ops using the shader blending mechanism, + // on hardware that can do proper bit math in fragment shaders. + SimulateLogicOpType simulateLogicOpType = (SimulateLogicOpType)id.Bits(FS_BIT_SIMULATE_LOGIC_OP_TYPE, 2); + switch (simulateLogicOpType) { case LOGICOPTYPE_ONE: WRITE(p, " %s.rgb = splat3(1.0);\n", compat.fragColor0); break; diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index c0886343a5..33548c2f5d 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -489,27 +489,6 @@ ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat) { return REPLACE_BLEND_STANDARD; } -LogicOpReplaceType ReplaceLogicOpType() { - if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) { - switch (gstate.getLogicOp()) { - case GE_LOGIC_COPY_INVERTED: - case GE_LOGIC_AND_INVERTED: - case GE_LOGIC_OR_INVERTED: - case GE_LOGIC_NOR: - case GE_LOGIC_NAND: - case GE_LOGIC_EQUIV: - return LOGICOPTYPE_INVERT; - case GE_LOGIC_INVERTED: - return LOGICOPTYPE_ONE; - case GE_LOGIC_SET: - return LOGICOPTYPE_ONE; - default: - return LOGICOPTYPE_NORMAL; - } - } - return LOGICOPTYPE_NORMAL; -} - static const float DEPTH_SLICE_FACTOR_HIGH = 4.0f; static const float DEPTH_SLICE_FACTOR_16BIT = 256.0f; @@ -874,8 +853,10 @@ static inline bool blendColorSimilar(uint32_t a, uint32_t b, int margin = 25) { return false; } -// Try to simulate some common logic ops. -static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { +// Try to simulate some common logic ops by using blend, if needed. +// The shader might also need modification, the below function SimulateLogicOpShaderTypeIfNeeded +// takes care of that. +static void SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { // Note: our shader solution applies logic ops BEFORE blending, not correctly after. // This is however fine for the most common ones, like CLEAR/NOOP/SET, etc. if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { @@ -937,6 +918,28 @@ static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq & } } +// Choose the shader part of the above logic op fallback simulation. +SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded() { + if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) { + switch (gstate.getLogicOp()) { + case GE_LOGIC_COPY_INVERTED: + case GE_LOGIC_AND_INVERTED: + case GE_LOGIC_OR_INVERTED: + case GE_LOGIC_NOR: + case GE_LOGIC_NAND: + case GE_LOGIC_EQUIV: + return LOGICOPTYPE_INVERT; + case GE_LOGIC_INVERTED: + return LOGICOPTYPE_ONE; + case GE_LOGIC_SET: + return LOGICOPTYPE_ONE; + default: + return LOGICOPTYPE_NORMAL; + } + } + return LOGICOPTYPE_NORMAL; +} + void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) { StencilValueType stencilType = STENCIL_VALUE_KEEP; if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { @@ -947,7 +950,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS BlendFactor srcBlend = BlendFactor::ONE; BlendFactor dstBlend = BlendFactor::ZERO; BlendEq blendEq = BlendEq::ADD; - ApplyLogicOp(srcBlend, dstBlend, blendEq); + SimulateLogicOpIfNeeded(srcBlend, dstBlend, blendEq); // We're not blending, but we may still want to "blend" for stencil. // This is only useful for INCR/DECR/INVERT. Others can write directly. @@ -1245,8 +1248,8 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) { colorEq = eqLookupNoMinMax[blendFuncEq]; } - // Attempt to apply the logic op, if any. - ApplyLogicOp(glBlendFuncA, glBlendFuncB, colorEq); + // Attempt to apply simulated logic ops, if any and if needed. + SimulateLogicOpIfNeeded(glBlendFuncA, glBlendFuncB, colorEq); // The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't // do any blending in the alpha channel as that doesn't seem to happen on PSP. So, we attempt to @@ -1508,3 +1511,8 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) { break; } } + +void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) { + ConvertMaskState(maskState, shaderBitOpsSuppported); + ConvertBlendState(blendState, maskState.applyFramebufferRead); +} diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index a1e7f0a06e..cfa5fb33fb 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -45,7 +45,7 @@ enum ReplaceBlendType { REPLACE_BLEND_BLUE_TO_ALPHA, }; -enum LogicOpReplaceType { +enum SimulateLogicOpType { LOGICOPTYPE_NORMAL, LOGICOPTYPE_ONE, LOGICOPTYPE_INVERT, @@ -62,7 +62,8 @@ StencilValueType ReplaceAlphaWithStencilType(); ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend); ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat); -LogicOpReplaceType ReplaceLogicOpType(); +// This is for the fallback path if real logic ops are not available. +SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded(); // Common representation, should be able to set this directly with any modern API. struct ViewportAndScissor { @@ -186,7 +187,16 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS struct GenericMaskState { bool applyFramebufferRead; uint32_t uniformMask; // For each bit, opposite to the PSP. + + // The hardware channel masks, 1 bit per color component. From bit 0, order is RGBA like in all APIs! uint8_t channelMask; + + void ConvertToShaderBlend() { + // If we have to do it in the shader, we simply pass through all channels but mask only in the shader instead. + // Some GPUs have minor penalties for masks that are not all-channels-on or all-channels-off. + channelMask = 0xF; + applyFramebufferRead = true; + } }; void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported); @@ -207,6 +217,8 @@ struct ComputedPipelineState { GenericBlendState blendState; GenericMaskState maskState; // TODO: Add logic and possibly stencil here. + + void Convert(bool shaderBitOpsSupported); }; // See issue #15898 diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index e5ca1aad80..0c5ffd2d52 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -267,16 +267,13 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend; ReplaceAlphaType stencilToAlpha = pipelineState.blendState.replaceAlphaWithStencil; - - // For debugging, can probably delete soon. - // _assert_(colorWriteMask == IsColorWriteMaskComplex()); - // _assert_(replaceBlend == ReplaceBlendWithShader(gstate_c.framebufFormat); - // _assert_(stencilToAlpha == ReplaceAlphaWithStencil(replaceBlend)); + SimulateLogicOpType simulateLogicOpType = SimulateLogicOpShaderTypeIfNeeded(); // All texfuncs except replace are the same for RGB as for RGBA with full alpha. // Note that checking this means that we must dirty the fragment shader ID whenever textureFullAlpha changes. - if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE) + if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE) { doTextureAlpha = false; + } if (gstate.isTextureMapEnabled()) { id.SetBit(FS_BIT_DO_TEXTURE); @@ -326,7 +323,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip } // 2 bits. - id.SetBits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2, ReplaceLogicOpType()); + id.SetBits(FS_BIT_SIMULATE_LOGIC_OP_TYPE, 2, simulateLogicOpType); // If replaceBlend == REPLACE_BLEND_STANDARD (or REPLACE_BLEND_NO) nothing is done, so we kill these bits. if (replaceBlend == REPLACE_BLEND_BLUE_TO_ALPHA) { diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index bfde30c281..914824d6bc 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -83,7 +83,7 @@ enum FShaderBit : uint8_t { FS_BIT_COLOR_DOUBLE = 23, FS_BIT_STENCIL_TO_ALPHA = 24, // 2 bits FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE = 26, // 4 bits (ReplaceAlphaType) - FS_BIT_REPLACE_LOGIC_OP_TYPE = 30, // 2 bits + FS_BIT_SIMULATE_LOGIC_OP_TYPE = 30, // 2 bits FS_BIT_REPLACE_BLEND = 32, // 3 bits (ReplaceBlendType) FS_BIT_BLENDEQ = 35, // 3 bits FS_BIT_BLENDFUNC_A = 38, // 4 bits diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index e42cf795b1..0f2c55f19f 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -147,10 +147,9 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } else { keys_.blend.value = 0; + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); - ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { bool fboTexNeedsBind = false; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index a8ba75f6c7..70c2f7518d 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -127,10 +127,9 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } dxstate.colorMask.set(mask); } else { + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); - ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { bool fboTexNeedsBind = false; diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index d3cbf11f21..f86f35f830 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -143,10 +143,9 @@ void DrawEngineGLES::ApplyDrawState(int prim) { bool alphaMask = gstate.isClearModeAlphaMask(); renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0)); } else { + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); - ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { bool fboTexNeedsBind = false; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index b0625b4e72..781b0c990a 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -147,18 +147,9 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool alphaMask = gstate.isClearModeAlphaMask(); key.colorWriteMask = (colorMask ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT) : 0) | (alphaMask ? VK_COLOR_COMPONENT_A_BIT : 0); } else { - if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) { - key.logicOpEnable = true; - key.logicOp = logicOps[gstate.getLogicOp()]; - } else { - key.logicOpEnable = false; - key.logicOp = VK_LOGIC_OP_CLEAR; - } - + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; - ConvertMaskState(maskState, draw_->GetDeviceCaps().fragmentShaderInt32Supported); - ConvertBlendState(blendState, maskState.applyFramebufferRead); if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); @@ -218,6 +209,14 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag key.srcColor = VK_BLEND_FACTOR_ZERO; key.destColor = VK_BLEND_FACTOR_ONE; } + + if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) { + key.logicOpEnable = true; + key.logicOp = logicOps[gstate.getLogicOp()]; + } else { + key.logicOpEnable = false; + key.logicOp = VK_LOGIC_OP_CLEAR; + } } } From d35749bfc8d56dcf5862ef8f18e66b93886e71ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 23:32:01 +0200 Subject: [PATCH 90/99] Move the computation of simulate-logic shader type earlier --- GPU/Common/GPUStateUtils.cpp | 2 ++ GPU/Common/GPUStateUtils.h | 1 + GPU/Common/ShaderId.cpp | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 33548c2f5d..4cce911b59 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1063,6 +1063,8 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) { } blendState.replaceBlend = replaceBlend; + blendState.simulateLogicOpType = SimulateLogicOpShaderTypeIfNeeded(); + ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend); blendState.replaceAlphaWithStencil = replaceAlphaWithStencil; diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index cfa5fb33fb..2d9fa62fae 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -146,6 +146,7 @@ struct GenericBlendState { // Shader generation state ReplaceAlphaType replaceAlphaWithStencil; ReplaceBlendType replaceBlend; + SimulateLogicOpType simulateLogicOpType; // Resulting hardware blend state bool blendEnabled; diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 0c5ffd2d52..53176ebcba 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -267,7 +267,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend; ReplaceAlphaType stencilToAlpha = pipelineState.blendState.replaceAlphaWithStencil; - SimulateLogicOpType simulateLogicOpType = SimulateLogicOpShaderTypeIfNeeded(); + SimulateLogicOpType simulateLogicOpType = pipelineState.blendState.simulateLogicOpType; // All texfuncs except replace are the same for RGB as for RGBA with full alpha. // Note that checking this means that we must dirty the fragment shader ID whenever textureFullAlpha changes. From ceda7aef49320e1252a06da294635d0e9e4077a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Sep 2022 23:35:58 +0200 Subject: [PATCH 91/99] Add log functions to state structs --- GPU/Common/GPUStateUtils.cpp | 8 ++++++++ GPU/Common/GPUStateUtils.h | 4 ++++ GPU/Vulkan/StateMappingVulkan.cpp | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 4cce911b59..af88e77532 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1514,6 +1514,14 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) { } } +void GenericMaskState::Log() { + WARN_LOG(G3D, "Mask: %01X readfb=%d", uniformMask, channelMask, applyFramebufferRead); +} + +void GenericBlendState::Log() { + WARN_LOG(G3D, "Blend: hwenable=%d readfb=%d replblend=%d replalpha=%d", blendEnabled, applyFramebufferRead, replaceBlend, (int)replaceAlphaWithStencil); +} + void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) { ConvertMaskState(maskState, shaderBitOpsSuppported); ConvertBlendState(blendState, maskState.applyFramebufferRead); diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index 2d9fa62fae..529e81cf87 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -180,6 +180,8 @@ struct GenericBlendState { blendColor = 0xFFFFFF | ((uint32_t)alpha << 24); useBlendColor = true; } + + void Log(); }; void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend); @@ -198,6 +200,8 @@ struct GenericMaskState { channelMask = 0xF; applyFramebufferRead = true; } + + void Log(); }; void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported); diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 781b0c990a..8532e8339c 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -364,7 +364,8 @@ void DrawEngineVulkan::BindShaderBlendTex() { // Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { if (fboTexNeedsBind_) { - framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); + bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); + _dbg_assert_(bindResult); boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW); fboTexBound_ = true; fboTexNeedsBind_ = false; From ec173559f833ae36230ead3d914817c86e46e4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Sep 2022 00:00:33 +0200 Subject: [PATCH 92/99] Additional cleanup --- GPU/Common/GPUStateUtils.cpp | 14 ++++++++++++++ GPU/Common/GPUStateUtils.h | 25 +++++++++++++++++++++++++ GPU/D3D11/StateMappingD3D11.cpp | 1 + GPU/Directx9/StateMappingDX9.cpp | 1 + GPU/GLES/StateMappingGLES.cpp | 6 ++++-- GPU/Vulkan/StateMappingVulkan.cpp | 17 +++++++++-------- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index af88e77532..97f69be509 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1466,6 +1466,19 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { } } +void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported) { + // Just do the non-shader case for now. + if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { + logicOpState.logicOpEnabled = gstate.isLogicOpEnabled() && logicSupported; + logicOpState.logicOp = gstate.isLogicOpEnabled() ? gstate.getLogicOp() : GE_LOGIC_COPY; + logicOpState.applyFramebufferRead = false; + } else { + logicOpState.logicOpEnabled = false; + logicOpState.logicOp = GE_LOGIC_COPY; + logicOpState.applyFramebufferRead = false; // true later? + } +} + static void ConvertStencilMask5551(GenericStencilFuncState &state) { state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00; } @@ -1524,5 +1537,6 @@ void GenericBlendState::Log() { void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) { ConvertMaskState(maskState, shaderBitOpsSuppported); + ConvertLogicOpState(logicState, gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP), shaderBitOpsSuppported); ConvertBlendState(blendState, maskState.applyFramebufferRead); } diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index 529e81cf87..23e41eb10f 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -218,9 +218,34 @@ struct GenericStencilFuncState { }; void ConvertStencilFuncState(GenericStencilFuncState &stencilFuncState); +struct GenericLogicState { + // If set, logic op is applied in the shader INSTEAD of in hardware. + // In this case, simulateLogicOpType and all that should be off. + bool applyFramebufferRead; + + // Hardware + bool logicOpEnabled; + + // Hardware and shader generation + GELogicOp logicOp; + + void ConvertToShaderBlend() { + if (logicOp != GE_LOGIC_COPY) { + logicOpEnabled = false; + applyFramebufferRead = true; + // Same logicOp is kept. + } + } + + void Log(); +}; + +void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported); + struct ComputedPipelineState { GenericBlendState blendState; GenericMaskState maskState; + GenericLogicState logicState; // TODO: Add logic and possibly stencil here. void Convert(bool shaderBitOpsSupported); diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 0f2c55f19f..e525b3f765 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -150,6 +150,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; + // We ignore the logicState on D3D since there's no support, the emulation of it is blend-and-shader only. if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { bool fboTexNeedsBind = false; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 70c2f7518d..7a324219a5 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -130,6 +130,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; + // We ignore the logicState on D3D since there's no support, the emulation of it is blend-and-shader only. if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { bool fboTexNeedsBind = false; diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index f86f35f830..007c280832 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -146,6 +146,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; + GenericLogicState &logicState = pipelineState_.logicState; if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { bool fboTexNeedsBind = false; @@ -201,10 +202,11 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } else { renderManager->SetNoBlendAndMask(mask); } + + // TODO: Get rid of the ifdef #ifndef USING_GLES2 if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { - renderManager->SetLogicOp(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY, - logicOps[gstate.getLogicOp()]); + renderManager->SetLogicOp(logicState.logicOpEnabled, logicOps[(int)logicState.logicOp]); } #endif } diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 8532e8339c..5c7f7b77af 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -150,6 +150,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); GenericMaskState &maskState = pipelineState_.maskState; GenericBlendState &blendState = pipelineState_.blendState; + GenericLogicState &logicState = pipelineState_.logicState; if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); @@ -196,6 +197,14 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag key.colorWriteMask = maskState.channelMask; // flags match + if (logicState.logicOpEnabled) { + key.logicOpEnable = true; + key.logicOp = logicOps[(int)logicState.logicOp]; + } else { + key.logicOpEnable = false; + key.logicOp = VK_LOGIC_OP_COPY; + } + // Workaround proposed in #10421, for bug where the color write mask is not applied correctly on Adreno. if ((gstate.pmskc & 0x00FFFFFF) == 0x00FFFFFF && g_Config.bVendorBugChecksEnabled && draw_->GetBugs().Has(Draw::Bugs::COLORWRITEMASK_BROKEN_WITH_DEPTHTEST)) { key.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; @@ -209,14 +218,6 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag key.srcColor = VK_BLEND_FACTOR_ZERO; key.destColor = VK_BLEND_FACTOR_ONE; } - - if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) { - key.logicOpEnable = true; - key.logicOp = logicOps[gstate.getLogicOp()]; - } else { - key.logicOpEnable = false; - key.logicOp = VK_LOGIC_OP_CLEAR; - } } } From d6b953bddde85471ac7bbaeb728303385d38759d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Sep 2022 00:04:21 +0200 Subject: [PATCH 93/99] Fix GLES check --- Common/GPU/OpenGL/thin3d_gl.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index e25fd6b4f7..5283b064ec 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -569,9 +569,8 @@ OpenGLContext::OpenGLContext() { caps_.fragmentShaderDepthWriteSupported = true; } -#ifndef USING_GLES2 - caps_.logicOpSupported = true; -#endif + // GLES has no support for logic framebuffer operations. There doesn't even seem to exist any such extensions. + caps_.logicOpSupported = !gl_extensions.IsGLES; // Interesting potential hack for emulating GL_DEPTH_CLAMP (use a separate varying, force depth in fragment shader): // This will induce a performance penalty on many architectures though so a blanket enable of this From 3d2e78fbf47607ab3d3127d11dc32d6369c0815a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Sep 2022 00:05:28 +0200 Subject: [PATCH 94/99] spaces->tabs --- GPU/Common/GPUStateUtils.cpp | 3 ++- GPU/Common/GPUStateUtils.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 97f69be509..c0d2df2e5b 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -1532,7 +1532,8 @@ void GenericMaskState::Log() { } void GenericBlendState::Log() { - WARN_LOG(G3D, "Blend: hwenable=%d readfb=%d replblend=%d replalpha=%d", blendEnabled, applyFramebufferRead, replaceBlend, (int)replaceAlphaWithStencil); + WARN_LOG(G3D, "Blend: hwenable=%d readfb=%d replblend=%d replalpha=%d", + blendEnabled, applyFramebufferRead, replaceBlend, (int)replaceAlphaWithStencil); } void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) { diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index 23e41eb10f..b91311207d 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -246,7 +246,6 @@ struct ComputedPipelineState { GenericBlendState blendState; GenericMaskState maskState; GenericLogicState logicState; - // TODO: Add logic and possibly stencil here. void Convert(bool shaderBitOpsSupported); }; From 65c016d1542e4f744d2ecdd1e6c7fc7465e8e70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Sep 2022 00:41:52 +0200 Subject: [PATCH 95/99] Fix dialog rendering (PPGe). Fixes #15954 Was caused by 867eb99 , part of #15950 . --- Core/MemMap.h | 5 +++++ Core/Util/PPGeDraw.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Core/MemMap.h b/Core/MemMap.h index 0e8863b090..b123af4f3a 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -467,6 +467,11 @@ struct PSPPointer PSPPointerNotifyRW(2, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1); } + size_t ElementSize() const + { + return sizeof(T); + } + static PSPPointer Create(u32 ptr) { PSPPointer p; p = ptr; diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 2b83bb4ccd..13fd62a22f 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -182,13 +182,13 @@ static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color vtx->x = x; vtx->y = y; vtx->z = 0; vtx->u = u * tw; vtx->v = v * th; vtx->color = color; - dataWritePtr += sizeof(vtx); + dataWritePtr += (u32)vtx.ElementSize(); } else { auto vtx = PSPPointer::Create(dataWritePtr); vtx->x = x; vtx->y = y; vtx->z = 0; vtx->u = u * tw; vtx->v = v * th; vtx->color = color; - dataWritePtr += sizeof(vtx); + dataWritePtr += (u32)vtx.ElementSize(); } _dbg_assert_(dataWritePtr <= dataPtr + dataSize); vertexCount++; From 7a7158d8d4d4738ec86e3d81a0d915901e5f0262 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 3 Sep 2022 21:16:59 -0700 Subject: [PATCH 96/99] UI: Only force ASCII messages if they're ASCII. Otherwise we could ruin messages that don't fit in the atlas. --- UI/OnScreenDisplay.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 1b38c93093..06d3d2f9f7 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -3,6 +3,7 @@ #include "UI/OnScreenDisplay.h" #include "Common/Data/Color/RGBAUtil.h" +#include "Common/Data/Encoding/Utf8.h" #include "Common/Render/TextureAtlas.h" #include "Common/Render/DrawBuffer.h" @@ -33,9 +34,10 @@ void OnScreenMessagesView::Draw(UIContext &dc) { // Messages that are wider than the screen are left-aligned instead of centered. int align = 0; - // Simple heuristic: Draw messages that contain line breaks with ASCII_FONT (likely debug output) + // If we have newlines, we may be looking at ASCII debug output. But let's verify. if (iter->text.find('\n') != 0) { - align |= FLAG_DYNAMIC_ASCII; + if (!UTF8StringHasNonASCII(iter->text.c_str())) + align |= FLAG_DYNAMIC_ASCII; } float tw, th; From 1df1d79b9e131be0f6aea215d9ad6f054d61ac0e Mon Sep 17 00:00:00 2001 From: Lubos Date: Sun, 4 Sep 2022 12:36:52 +0200 Subject: [PATCH 97/99] OpenXR - Stereo without multiview added --- Common/GPU/OpenGL/GLRenderManager.cpp | 40 +++++++++++++++------- Common/GPU/OpenGL/GLRenderManager.h | 1 + Common/VR/PPSSPPVR.cpp | 14 +++++++- Common/VR/PPSSPPVR.h | 6 ++++ Common/VR/VRBase.h | 4 +-- Common/VR/VRFramebuffer.cpp | 18 ++++++---- Common/VR/VRRenderer.cpp | 49 +++++++++++++++++---------- Common/VR/VRRenderer.h | 6 +++- GPU/GLES/ShaderManagerGLES.cpp | 4 +-- UI/GameSettingsScreen.cpp | 6 ++-- 10 files changed, 103 insertions(+), 45 deletions(-) diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index b9173c2beb..6aebd20a70 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -5,6 +5,8 @@ #include "Common/Thread/ThreadUtil.h" #include "Common/VR/PPSSPPVR.h" +#include "Core/Config.h" + #include "Common/Log.h" #include "Common/MemoryUtil.h" #include "Common/Math/math_util.h" @@ -233,15 +235,7 @@ bool GLRenderManager::ThreadFrame() { INFO_LOG(G3D, "Running first frame (%d)", threadFrame_); firstFrame = false; } - - if (IsVRBuild()) { - if (PreVRRender()) { - Run(threadFrame_); - PostVRRender(); - } - } else { - Run(threadFrame_); - } + Run(threadFrame_); VLOG("PULL: Finished frame %d", threadFrame_); } while (!nextFrame); @@ -564,8 +558,7 @@ void GLRenderManager::EndSubmitFrame(int frame) { } // Render thread -void GLRenderManager::Run(int frame) { - BeginSubmitFrame(frame); +void GLRenderManager::Render(int frame) { FrameData &frameData = frameData_[frame]; @@ -584,6 +577,7 @@ void GLRenderManager::Run(int frame) { } queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); + stepsOnThread.clear(); if (!skipGLCalls_) { @@ -591,8 +585,30 @@ void GLRenderManager::Run(int frame) { iter->MapDevice(bufferStrategy_); } } +} - switch (frameData.type) { +// Render thread +void GLRenderManager::Run(int frame) { + BeginSubmitFrame(frame); + + if (IsVRBuild()) { + if (PreVRRender()) { + int passes = 1; + if (!IsMultiviewSupported() && g_Config.bEnableStereo) { + passes = 2; + } + for (int i = 0; i < passes; i++) { + PreVRFrameRender(i); + Render(frame); + PostVRFrameRender(); + } + PostVRRender(); + } + } else { + Render(frame); + } + + switch (frameData_[frame].type) { case GLRRunType::END: EndSubmitFrame(frame); break; diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 5423262c98..5f4b8216f8 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -377,6 +377,7 @@ public: void BeginFrame(); // Can run on a different thread! void Finish(); + void Render(int frame); void Run(int frame); // Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown. diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index e66e87d991..2d3562468c 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -225,7 +225,7 @@ bool PreVRRender() { VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true); } - if (VR_BeginFrame(VR_GetEngine())) { + if (VR_InitFrame(VR_GetEngine())) { // Decide if the scene is 3D or not if (g_Config.bEnableVR && !VR_GetConfig(VR_CONFIG_FORCE_2D) && (VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) > 15)) { @@ -245,9 +245,21 @@ bool PreVRRender() { } void PostVRRender() { + VR_FinishFrame(VR_GetEngine()); +} + +void PreVRFrameRender(int fboIndex) { + VR_BeginFrame(VR_GetEngine(), fboIndex); +} + +void PostVRFrameRender() { VR_EndFrame(VR_GetEngine()); } +int GetVRFBOIndex() { + return VR_GetConfig(VR_CONFIG_CURRENT_FBO); +} + bool IsMultiviewSupported() { return false; } diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h index 76dafc6d07..b41b69f1be 100644 --- a/Common/VR/PPSSPPVR.h +++ b/Common/VR/PPSSPPVR.h @@ -17,6 +17,9 @@ void UpdateVRScreenKey(const KeyInput &key); void BindVRFramebuffer(); bool PreVRRender(); void PostVRRender(); +void PreVRFrameRender(int fboIndex); +void PostVRFrameRender(); +int GetVRFBOIndex(); bool IsMultiviewSupported(); bool IsFlatVRScene(); bool Is2DVRObject(float* projMatrix, bool ortho); @@ -37,6 +40,9 @@ inline void UpdateVRScreenKey(const KeyInput &key) {} inline void BindVRFramebuffer() {} inline bool PreVRRender() { return false; } inline void PostVRRender() {} +inline void PreVRFrameRender(int fboIndex) {} +inline void PostVRFrameRender() {} +inline int GetVRFBOIndex() { return 0; } inline bool IsMultiviewSupported() { return false; } inline bool IsFlatVRScene() { return true; } inline bool Is2DVRObject(float* projMatrix, bool ortho) { return false; } diff --git a/Common/VR/VRBase.h b/Common/VR/VRBase.h index 2d0be5d8d2..fffd234868 100644 --- a/Common/VR/VRBase.h +++ b/Common/VR/VRBase.h @@ -92,7 +92,6 @@ typedef struct { typedef struct { int Width; int Height; - bool Multiview; uint32_t TextureSwapChainLength; uint32_t TextureSwapChainIndex; ovrSwapChain ColorSwapChain; @@ -104,7 +103,8 @@ typedef struct { } ovrFramebuffer; typedef struct { - ovrFramebuffer FrameBuffer; + bool Multiview; + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; } ovrRenderer; typedef struct { diff --git a/Common/VR/VRFramebuffer.cpp b/Common/VR/VRFramebuffer.cpp index 0f13150b3e..b01775c588 100644 --- a/Common/VR/VRFramebuffer.cpp +++ b/Common/VR/VRFramebuffer.cpp @@ -47,12 +47,10 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w frameBuffer->Width = width; frameBuffer->Height = height; - frameBuffer->Multiview = multiview; if (strstr((const char*)glGetString(GL_EXTENSIONS), "GL_OVR_multiview2") == nullptr) { ALOGE("OpenGL implementation does not support GL_OVR_multiview2 extension.\n"); - exit(EXIT_FAILURE); } typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); @@ -60,7 +58,6 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w if (!glFramebufferTextureMultiviewOVR) { ALOGE("Can not get proc address for glFramebufferTextureMultiviewOVR.\n"); - exit(EXIT_FAILURE); } XrSwapchainCreateInfo swapChainCreateInfo; @@ -200,15 +197,24 @@ ovrRenderer */ void ovrRenderer_Clear(ovrRenderer* renderer) { - ovrFramebuffer_Clear(&renderer->FrameBuffer); + for (int i = 0; i < ovrMaxNumEyes; i++) { + ovrFramebuffer_Clear(&renderer->FrameBuffer[i]); + } } void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview) { - ovrFramebuffer_Create(session, &renderer->FrameBuffer, width, height, multiview); + renderer->Multiview = multiview; + int instances = renderer->Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer_Create(session, &renderer->FrameBuffer[i], width, height, multiview); + } } void ovrRenderer_Destroy(ovrRenderer* renderer) { - ovrFramebuffer_Destroy(&renderer->FrameBuffer); + int instances = renderer->Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[i]); + } } /* diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp index f4c4d7f805..bc066035d2 100644 --- a/Common/VR/VRRenderer.cpp +++ b/Common/VR/VRRenderer.cpp @@ -235,7 +235,7 @@ void VR_ClearFrameBuffer( int width, int height) { glDisable( GL_SCISSOR_TEST ); } -bool VR_BeginFrame( engine_t* engine ) { +bool VR_InitFrame( engine_t* engine ) { GLboolean stageBoundsDirty = GL_TRUE; if (ovrApp_HandleXrEvents(&engine->appState)) { VR_Recenter(engine); @@ -304,16 +304,21 @@ bool VR_BeginFrame( engine_t* engine ) { engine->appState.LayerCount = 0; memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); - - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer; - ovrFramebuffer_Acquire(frameBuffer); - ovrFramebuffer_SetCurrent(frameBuffer); - VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); return true; } +void VR_BeginFrame( engine_t* engine, int fboIndex ) { + vrConfig[VR_CONFIG_CURRENT_FBO] = fboIndex; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; + ovrFramebuffer_Acquire(frameBuffer); + ovrFramebuffer_SetCurrent(frameBuffer); + VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); +} + void VR_EndFrame( engine_t* engine ) { + int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO]; + // Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully VR_BindFramebuffer(engine); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); @@ -322,9 +327,8 @@ void VR_EndFrame( engine_t* engine ) { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // Show mouse cursor - int vrMode = vrConfig[VR_CONFIG_MODE]; int size = vrConfig[VR_CONFIG_MOUSE_SIZE]; - if ((vrMode == VR_MODE_FLAT_SCREEN) && (size > 0)) { + if ((vrConfig[VR_CONFIG_MODE] == VR_MODE_FLAT_SCREEN) && (size > 0)) { glEnable(GL_SCISSOR_TEST); glScissor(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); glViewport(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); @@ -333,21 +337,27 @@ void VR_EndFrame( engine_t* engine ) { glDisable(GL_SCISSOR_TEST); } - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; //ovrFramebuffer_Resolve(frameBuffer); ovrFramebuffer_Release(frameBuffer); ovrFramebuffer_SetNone(); +} +void VR_FinishFrame( engine_t* engine ) { + + int vrMode = vrConfig[VR_CONFIG_MODE]; XrCompositionLayerProjectionView projection_layer_elements[2] = {}; if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) { vrConfig[VR_CONFIG_MENU_YAW] = (int)hmdorientation.y; for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - int imageLayer = eye; + int imageLayer = engine->appState.Renderer.Multiview ? eye : 0; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[0]; XrFovf fov = projections[eye].fov; if (vrMode == VR_MODE_MONO_6DOF) { fov = projections[0].fov; - imageLayer = 0; + } else if (!engine->appState.Renderer.Multiview) { + frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; } memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); @@ -376,15 +386,15 @@ void VR_EndFrame( engine_t* engine ) { } else if (vrMode == VR_MODE_FLAT_SCREEN) { // Build the cylinder layer - int width = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Width; - int height = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Height; + int width = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Width; + int height = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Height; XrCompositionLayerCylinderKHR cylinder_layer = {}; cylinder_layer.type = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR; cylinder_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; cylinder_layer.space = engine->appState.CurrentSpace; cylinder_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; memset(&cylinder_layer.subImage, 0, sizeof(XrSwapchainSubImage)); - cylinder_layer.subImage.swapchain = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Handle; + cylinder_layer.subImage.swapchain = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Handle; cylinder_layer.subImage.imageRect.offset.x = 0; cylinder_layer.subImage.imageRect.offset.y = 0; cylinder_layer.subImage.imageRect.extent.width = width; @@ -425,8 +435,12 @@ void VR_EndFrame( engine_t* engine ) { endFrameInfo.layers = layers; OXR(xrEndFrame(engine->appState.Session, &endFrameInfo)); - frameBuffer->TextureSwapChainIndex++; - frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength; + int instances = engine->appState.Renderer.Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[instances]; + frameBuffer->TextureSwapChainIndex++; + frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength; + } } int VR_GetConfig( VRConfig config ) { @@ -439,7 +453,8 @@ void VR_SetConfig( VRConfig config, int value) { void VR_BindFramebuffer(engine_t *engine) { if (!initialized) return; - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer; + int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO]; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; unsigned int swapchainIndex = frameBuffer->TextureSwapChainIndex; unsigned int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex]; GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glFramebuffer)); diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h index 82173a060b..abec8dfbaf 100644 --- a/Common/VR/VRRenderer.h +++ b/Common/VR/VRRenderer.h @@ -18,6 +18,8 @@ enum VRConfig { VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y, //viewport setup VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, VR_CONFIG_VIEWPORT_VALID, + //render status + VR_CONFIG_CURRENT_FBO, //end VR_CONFIG_MAX @@ -41,8 +43,10 @@ void VR_GetResolution( engine_t* engine, int *pWidth, int *pHeight ); void VR_InitRenderer( engine_t* engine, bool multiview ); void VR_DestroyRenderer( engine_t* engine ); -bool VR_BeginFrame( engine_t* engine ); +bool VR_InitFrame( engine_t* engine ); +void VR_BeginFrame( engine_t* engine, int fboIndex ); void VR_EndFrame( engine_t* engine ); +void VR_FinishFrame( engine_t* engine ); int VR_GetConfig( VRConfig config ); void VR_SetConfig( VRConfig config, int value); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index bb3ba416c4..29a6d391b5 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -410,7 +410,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu if (IsMultiviewSupported()) { render_->SetUniformM4x4Stereo("u_proj", &u_proj, leftEyeMatrix.m, rightEyeMatrix.m); } else { - render_->SetUniformM4x4(&u_proj, leftEyeMatrix.m); + render_->SetUniformM4x4(&u_proj, GetVRFBOIndex() == 0 ? leftEyeMatrix.m : rightEyeMatrix.m); } } else { Matrix4x4 flippedMatrix; @@ -537,7 +537,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu if (IsMultiviewSupported()) { render_->SetUniformM4x4Stereo("u_view", &u_view, leftEyeView, rightEyeView); } else { - render_->SetUniformM4x4(&u_view, leftEyeView); + render_->SetUniformM4x4(&u_view, GetVRFBOIndex() == 0 ? leftEyeView : rightEyeView); } } else { SetMatrix4x3(render_, &u_view, gstate.viewMatrix); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 896a74dea5..e6303f2f10 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1120,10 +1120,8 @@ void GameSettingsScreen::CreateViews() { vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Enable virtual reality"))); CheckBox *vr6DoF = vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("Enable 6 degrees of freedom movement"))); vr6DoF->SetEnabledPtr(&g_Config.bEnableVR); - if (IsMultiviewSupported()) { - CheckBox *vrStereo = vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Enable stereoscopic vision"))); - vrStereo->SetEnabledPtr(&g_Config.bEnableVR); - } + CheckBox *vrStereo = vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Enable stereoscopic vision"))); + vrStereo->SetEnabledPtr(&g_Config.bEnableVR); PopupSliderChoice *vrFieldOfView = vrSettings->Add(new PopupSliderChoice(&g_Config.iFieldOfViewPercentage, 100, 150, vr->T("Field of view scale", "Headset's field of view scale"), 10, screenManager(), vr->T("% of native FoV"))); vrFieldOfView->SetEnabledPtr(&g_Config.bEnableVR); vrSettings->Add(new PopupSliderChoice(&g_Config.iCanvasDistance, 1, 10, vr->T("Distance to 2D menus and scenes", "Distance to 2D menus and scenes"), 1, screenManager(), "")); From fb875b94de26c9900d38f412ce96af9860a248b3 Mon Sep 17 00:00:00 2001 From: Lubos Date: Sun, 4 Sep 2022 14:01:17 +0200 Subject: [PATCH 98/99] OpenXR - Multipass rendering fixed --- Common/GPU/OpenGL/GLQueueRunner.cpp | 6 +++-- Common/GPU/OpenGL/GLQueueRunner.h | 2 +- Common/GPU/OpenGL/GLRenderManager.cpp | 37 ++++++++++++--------------- Common/GPU/OpenGL/GLRenderManager.h | 1 - 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index f29ffbe2cd..502d656e39 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -642,7 +642,7 @@ retry_depth: currentReadHandle_ = fbo->handle; } -void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCalls) { +void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps) { if (skipGLCalls) { // Dry run for (size_t i = 0; i < steps.size(); i++) { @@ -724,7 +724,9 @@ void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCal glPopDebugGroup(); #endif - delete steps[i]; + if (!keepSteps) { + delete steps[i]; + } } CHECK_GL_ERROR_IF_DEBUG(); } diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index a01fad23ab..1e009787ae 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -363,7 +363,7 @@ public: void RunInitSteps(const std::vector &steps, bool skipGLCalls); - void RunSteps(const std::vector &steps, bool skipGLCalls); + void RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps = false); void LogSteps(const std::vector &steps); void CreateDeviceObjects(); diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 6aebd20a70..8b8e190801 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -558,7 +558,9 @@ void GLRenderManager::EndSubmitFrame(int frame) { } // Render thread -void GLRenderManager::Render(int frame) { +void GLRenderManager::Run(int frame) { + BeginSubmitFrame(frame); + FrameData &frameData = frameData_[frame]; @@ -566,30 +568,12 @@ void GLRenderManager::Render(int frame) { auto &initStepsOnThread = frameData_[frame].initSteps; // queueRunner_.LogSteps(stepsOnThread); queueRunner_.RunInitSteps(initStepsOnThread, skipGLCalls_); - initStepsOnThread.clear(); - - // Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created. - if (!skipGLCalls_) { - for (auto iter : frameData.activePushBuffers) { - iter->Flush(); - iter->UnmapDevice(); - } - } - - queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); - - stepsOnThread.clear(); if (!skipGLCalls_) { for (auto iter : frameData.activePushBuffers) { iter->MapDevice(bufferStrategy_); } } -} - -// Render thread -void GLRenderManager::Run(int frame) { - BeginSubmitFrame(frame); if (IsVRBuild()) { if (PreVRRender()) { @@ -599,15 +583,26 @@ void GLRenderManager::Run(int frame) { } for (int i = 0; i < passes; i++) { PreVRFrameRender(i); - Render(frame); + queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1); PostVRFrameRender(); } PostVRRender(); } } else { - Render(frame); + queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); } + // Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created. + if (!skipGLCalls_) { + for (auto iter : frameData.activePushBuffers) { + iter->Flush(); + iter->UnmapDevice(); + } + } + + initStepsOnThread.clear(); + stepsOnThread.clear(); + switch (frameData_[frame].type) { case GLRRunType::END: EndSubmitFrame(frame); diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 5f4b8216f8..5423262c98 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -377,7 +377,6 @@ public: void BeginFrame(); // Can run on a different thread! void Finish(); - void Render(int frame); void Run(int frame); // Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown. From 2195f4b2e773549638b6cda4d503a19f9a934c68 Mon Sep 17 00:00:00 2001 From: Lubos Date: Sun, 4 Sep 2022 14:08:03 +0200 Subject: [PATCH 99/99] OpenXR - Mismatched call order fixed --- Common/GPU/OpenGL/GLRenderManager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 8b8e190801..7c228f240b 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -568,10 +568,13 @@ void GLRenderManager::Run(int frame) { auto &initStepsOnThread = frameData_[frame].initSteps; // queueRunner_.LogSteps(stepsOnThread); queueRunner_.RunInitSteps(initStepsOnThread, skipGLCalls_); + initStepsOnThread.clear(); + // Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created. if (!skipGLCalls_) { for (auto iter : frameData.activePushBuffers) { - iter->MapDevice(bufferStrategy_); + iter->Flush(); + iter->UnmapDevice(); } } @@ -592,18 +595,15 @@ void GLRenderManager::Run(int frame) { queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); } - // Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created. if (!skipGLCalls_) { for (auto iter : frameData.activePushBuffers) { - iter->Flush(); - iter->UnmapDevice(); + iter->MapDevice(bufferStrategy_); } } - initStepsOnThread.clear(); stepsOnThread.clear(); - switch (frameData_[frame].type) { + switch (frameData.type) { case GLRRunType::END: EndSubmitFrame(frame); break;