From 66f88633aef3338738be836cc7ebae2883ef8130 Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Tue, 14 Nov 2017 11:26:16 +0100 Subject: [PATCH 01/25] Add missing ID for CTTR compat hack. --- assets/compat.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/compat.ini b/assets/compat.ini index 5831556907..22a5c903c5 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -145,6 +145,7 @@ UCES00373 = true # Crash Tag Team Racing needs it to pass checking memory stick screen. ULUS10044 = true ULES00168 = true +ULES00172 = true ULJM05036 = true # Gundam Battle Royale might need it to avoid crashes when certain Ace enemies shows up ULJS00083 = true From c12f17134b2175859909b9fd3bfe6923c80e0e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 14 Nov 2017 12:50:27 +0100 Subject: [PATCH 02/25] Links from the UI: Change from http:// to https:// wherever possible. --- Qt/mainwindow.cpp | 4 ++-- UI/GameSettingsScreen.cpp | 2 +- UI/MainScreen.cpp | 8 ++++---- UI/MiscScreens.cpp | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 1e90418ace..5e4fc65535 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -391,12 +391,12 @@ void MainWindow::fullscrAct() void MainWindow::websiteAct() { - QDesktopServices::openUrl(QUrl("http://www.ppsspp.org/")); + QDesktopServices::openUrl(QUrl("https://www.ppsspp.org/")); } void MainWindow::forumAct() { - QDesktopServices::openUrl(QUrl("http://forums.ppsspp.org/")); + QDesktopServices::openUrl(QUrl("https://forums.ppsspp.org/")); } void MainWindow::gitAct() diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5e7d7fa2f7..4f0fb0a68a 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -838,7 +838,7 @@ static void RecreateActivity() { } UI::EventReturn GameSettingsScreen::OnAdhocGuides(UI::EventParams &e) { - LaunchBrowser("http://forums.ppsspp.org/forumdisplay.php?fid=34"); + LaunchBrowser("https://forums.ppsspp.org/forumdisplay.php?fid=34"); return UI::EVENT_DONE; } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 65207a8510..66967869b3 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -920,7 +920,7 @@ UI::EventReturn MainScreen::OnDownloadUpgrade(UI::EventParams &e) { } #else // Go directly to ppsspp.org and let the user sort it out - LaunchBrowser("http://www.ppsspp.org/downloads.html"); + LaunchBrowser("https://www.ppsspp.org/downloads.html"); #endif return UI::EVENT_DONE; } @@ -1131,18 +1131,18 @@ UI::EventReturn MainScreen::OnSupport(UI::EventParams &e) { #ifdef __ANDROID__ LaunchBrowser("market://details?id=org.ppsspp.ppssppgold"); #else - LaunchBrowser("http://central.ppsspp.org/buygold"); + LaunchBrowser("https://central.ppsspp.org/buygold"); #endif return UI::EVENT_DONE; } UI::EventReturn MainScreen::OnPPSSPPOrg(UI::EventParams &e) { - LaunchBrowser("http://www.ppsspp.org"); + LaunchBrowser("https://www.ppsspp.org"); return UI::EVENT_DONE; } UI::EventReturn MainScreen::OnForums(UI::EventParams &e) { - LaunchBrowser("http://forums.ppsspp.org"); + LaunchBrowser("https://forums.ppsspp.org"); return UI::EVENT_DONE; } diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index d01a98fe51..9b0d7a6672 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -530,9 +530,9 @@ void CreditsScreen::CreateViews() { Button *back = root_->Add(new Button(di->T("Back"), new AnchorLayoutParams(260, 64, NONE, NONE, 10, 10, false))); back->OnClick.Handle(this, &CreditsScreen::OnOK); root_->SetDefaultFocusView(back); -#ifndef GOLD - root_->Add(new Button(cr->T("Buy Gold"), new AnchorLayoutParams(260, 64, 10, NONE, NONE, 10, false)))->OnClick.Handle(this, &CreditsScreen::OnSupport); -#endif + if (!System_GetPropertyBool(SYSPROP_APP_GOLD)) { + root_->Add(new Button(cr->T("Buy Gold"), new AnchorLayoutParams(260, 64, 10, NONE, NONE, 10, false)))->OnClick.Handle(this, &CreditsScreen::OnSupport); + } root_->Add(new Button(cr->T("PPSSPP Forums"), new AnchorLayoutParams(260, 64, 10, NONE, NONE, 84, false)))->OnClick.Handle(this, &CreditsScreen::OnForums); root_->Add(new Button("www.ppsspp.org", new AnchorLayoutParams(260, 64, 10, NONE, NONE, 158, false)))->OnClick.Handle(this, &CreditsScreen::OnPPSSPPOrg); #ifdef __ANDROID__ @@ -550,7 +550,7 @@ UI::EventReturn CreditsScreen::OnSupport(UI::EventParams &e) { #ifdef __ANDROID__ LaunchBrowser("market://details?id=org.ppsspp.ppssppgold"); #else - LaunchBrowser("http://central.ppsspp.org/buygold"); + LaunchBrowser("https://central.ppsspp.org/buygold"); #endif return UI::EVENT_DONE; } @@ -565,12 +565,12 @@ UI::EventReturn CreditsScreen::OnTwitter(UI::EventParams &e) { } UI::EventReturn CreditsScreen::OnPPSSPPOrg(UI::EventParams &e) { - LaunchBrowser("http://www.ppsspp.org"); + LaunchBrowser("https://www.ppsspp.org"); return UI::EVENT_DONE; } UI::EventReturn CreditsScreen::OnForums(UI::EventParams &e) { - LaunchBrowser("http://forums.ppsspp.org"); + LaunchBrowser("https://forums.ppsspp.org"); return UI::EVENT_DONE; } @@ -689,7 +689,7 @@ void CreditsScreen::render() { "", "", cr->T("check", "Also check out Dolphin, the best Wii/GC emu around:"), - "http://www.dolphin-emu.org", + "https://www.dolphin-emu.org", "", "", cr->T("info1", "PPSSPP is only intended to play games you own."), From 8ccf32af47d433ab4a65057bc55b474e12ec9486 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 14 Nov 2017 22:33:56 -0800 Subject: [PATCH 03/25] Headless: Fix crash with null GPU. --- GPU/Null/NullGpu.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index 95a9b8c157..2772718ec9 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -15,7 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - +#include "GPU/Common/DrawEngineCommon.h" #include "GPU/Null/NullGpu.h" #include "GPU/GPUState.h" #include "GPU/ge_constants.h" @@ -25,7 +25,17 @@ #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceGe.h" -NullGPU::NullGPU() : GPUCommon(nullptr, nullptr) { } +class NullDrawEngine : public DrawEngineCommon { +public: + void DispatchFlush() override { + } + void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override { + } +}; + +NullGPU::NullGPU() : GPUCommon(nullptr, nullptr) { + drawEngineCommon_ = new NullDrawEngine(); +} NullGPU::~NullGPU() { } void NullGPU::FastRunLoop(DisplayList &list) { From 9080f41642911b50c1feeb72bba4ce3cc9af1db6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 14 Nov 2017 22:34:18 -0800 Subject: [PATCH 04/25] Headless: Prevent segfaults with Vulkan. --- headless/Headless.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index a20e540a8f..db9645b39f 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -161,6 +161,8 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool Core_UpdateDebugStats(g_Config.bShowDebugStats || g_Config.bLogFrameDrops); PSP_BeginHostFrame(); + if (coreParameter.thin3d) + coreParameter.thin3d->BeginFrame(); coreState = CORE_RUNNING; while (coreState == CORE_RUNNING) @@ -185,6 +187,8 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool } } + if (coreParameter.thin3d) + coreParameter.thin3d->EndFrame(); PSP_EndHostFrame(); PSP_Shutdown(); From 5582b8fe2298160af58e75d0ea4c3902e8c50655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 14 Nov 2017 13:58:13 +0100 Subject: [PATCH 05/25] Just more accurate feature flags for Vulkan. --- GPU/Vulkan/GPU_Vulkan.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index bb25eef794..42474eba55 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -214,6 +214,9 @@ void GPU_Vulkan::CheckGPUFeatures() { features |= GPU_SUPPORTS_OES_TEXTURE_NPOT; features |= GPU_SUPPORTS_LARGE_VIEWPORTS; features |= GPU_SUPPORTS_16BIT_FORMATS; + features |= GPU_SUPPORTS_INSTANCE_RENDERING; + features |= GPU_SUPPORTS_VERTEX_TEXTURE_FETCH; + features |= GPU_SUPPORTS_TEXTURE_FLOAT; gstate_c.featureFlags = features; } From 3594623a8d4a160ad75955022f01699608648012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 09:07:51 +0100 Subject: [PATCH 06/25] Vulkan: Show the preferred depth-stencil format in the feature list. --- Common/Vulkan/VulkanContext.cpp | 4 ++++ ext/native/thin3d/thin3d_vulkan.cpp | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index e75719e53a..31abd4215e 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -431,6 +431,10 @@ void VulkanContext::ChooseDevice(int physical_device) { break; } } + if (deviceInfo_.preferredDepthStencilFormat == VK_FORMAT_UNDEFINED) { + // WTF? This is bad. + ELOG("Could not find a usable depth stencil format."); + } // This is as good a place as any to do this vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 0dd024b390..fcb7b73cf8 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1187,6 +1187,20 @@ void AddFeature(std::vector &features, const char *name, VkBool32 a features.push_back(buf); } +// Limited to depth buffer formats as that's what we need right now. +static const char *VulkanFormatToString(VkFormat fmt) { + switch (fmt) { + case VkFormat::VK_FORMAT_D24_UNORM_S8_UINT: return "D24S8"; + case VkFormat::VK_FORMAT_D16_UNORM: return "D16"; + case VkFormat::VK_FORMAT_D16_UNORM_S8_UINT: return "D16S8"; + case VkFormat::VK_FORMAT_D32_SFLOAT: return "D32f"; + case VkFormat::VK_FORMAT_D32_SFLOAT_S8_UINT: return "D32fS8"; + case VkFormat::VK_FORMAT_S8_UINT: return "S8"; + case VkFormat::VK_FORMAT_UNDEFINED: return "UNDEFINED (BAD!)"; + default: return "UNKNOWN"; + } +} + std::vector VKContext::GetFeatureList() const { const VkPhysicalDeviceFeatures &available = vulkan_->GetFeaturesAvailable(); const VkPhysicalDeviceFeatures &enabled = vulkan_->GetFeaturesEnabled(); @@ -1210,6 +1224,8 @@ std::vector VKContext::GetFeatureList() const { AddFeature(features, "occlusionQueryPrecise", available.occlusionQueryPrecise, enabled.occlusionQueryPrecise); AddFeature(features, "multiDrawIndirect", available.multiDrawIndirect, enabled.multiDrawIndirect); + features.push_back(std::string("Preferred depth buffer format: ") + VulkanFormatToString(vulkan_->GetDeviceInfo().preferredDepthStencilFormat)); + // Also list texture formats and their properties. for (int i = VK_FORMAT_BEGIN_RANGE; i <= VK_FORMAT_END_RANGE; i++) { // TODO From 656ec18ded04913d3f7b9268c4ed93bb38ecfffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 10:24:11 +0100 Subject: [PATCH 07/25] Vulkan: Dual Src blending strikes again! Disable it on nVidia X1 (Shield TV) due to driver bugs, sigh. Should help #10136 --- GPU/Vulkan/GPU_Vulkan.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 42474eba55..371a55fe92 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -181,8 +181,13 @@ void GPU_Vulkan::CheckGPUFeatures() { } if (vulkan_->GetFeaturesEnabled().dualSrcBlend) { switch (vulkan_->GetPhysicalDeviceProperties().vendorID) { + case VULKAN_VENDOR_NVIDIA: + // Workaround for Shield TV driver bug. + if (strcmp(vulkan_->GetPhysicalDeviceProperties().deviceName, "NVIDIA Tegra X1") != 0) + features |= GPU_SUPPORTS_DUALSOURCE_BLEND; + break; case VULKAN_VENDOR_INTEL: - // Work around for Intel driver bug. + // Workaround for Intel driver bug. break; case VULKAN_VENDOR_AMD: // See issue #10074, and also #10065 (AMD) and #10109 for the choice of the driver version to check for From 10792cc8e7c1ddf550f63e8cf09ed5dc02aaef7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 13:18:29 +0100 Subject: [PATCH 08/25] Windows/Vulkan: implement screenshots. Something is iffy with the red/blue swap though... --- Common/Vulkan/VulkanContext.cpp | 5 ++ GPU/Vulkan/FramebufferVulkan.cpp | 12 ++++- GPU/Vulkan/FramebufferVulkan.h | 3 ++ ext/native/thin3d/DataFormat.h | 1 + ext/native/thin3d/VulkanQueueRunner.cpp | 66 +++++++++++++++++------ ext/native/thin3d/VulkanQueueRunner.h | 4 +- ext/native/thin3d/VulkanRenderManager.cpp | 24 +++++++-- ext/native/thin3d/VulkanRenderManager.h | 2 +- ext/native/thin3d/thin3d.cpp | 25 +++++++++ ext/native/thin3d/thin3d_vulkan.cpp | 3 +- 10 files changed, 118 insertions(+), 27 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 31abd4215e..cc774f4b91 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -800,6 +800,11 @@ bool VulkanContext::InitSwapchain() { swap_chain_info.oldSwapchain = VK_NULL_HANDLE; swap_chain_info.clipped = true; swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; +#ifndef ANDROID + // We don't support screenshots on Android + swap_chain_info.imageUsage = swap_chain_info.imageUsage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; +#endif + swap_chain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swap_chain_info.queueFamilyIndexCount = 0; swap_chain_info.pQueueFamilyIndices = NULL; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 1684e70638..f5ab503601 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -699,4 +699,14 @@ void FramebufferManagerVulkan::CompilePostShader() { usePostShader_ = true; -} \ No newline at end of file +} + +bool FramebufferManagerVulkan::GetOutputFramebuffer(GPUDebugBuffer &buffer) { + int w, h; + draw_->GetFramebufferDimensions(nullptr, &w, &h); + // I'm really not sure why I have to pass the wrong format here, it seems all the other color conversion are correct. + // But I get R/B swapped if I do what seems right... Maybe driver bug for copies from the backbuffer, who knows. + buffer.Allocate(w, h, GPU_DBG_FORMAT_8888_BGRA, false); + draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); + return true; +} diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 49538dfa99..5979201536 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -97,6 +97,9 @@ protected: bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; + // Small tweak to the Common one. + bool GetOutputFramebuffer(GPUDebugBuffer &buffer); + private: // The returned texture does not need to be free'd, might be returned from a pool (currently single entry) void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; diff --git a/ext/native/thin3d/DataFormat.h b/ext/native/thin3d/DataFormat.h index 831b34d626..22cd4d3c07 100644 --- a/ext/native/thin3d/DataFormat.h +++ b/ext/native/thin3d/DataFormat.h @@ -73,6 +73,7 @@ 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); } // namespace diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index a2e56e483d..29170b8004 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -837,22 +837,6 @@ void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageMemor } void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd) { - VKRImage *srcImage; - if (step.readback.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { - srcImage = &step.readback.src->color; - } else if (step.readback.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - srcImage = &step.readback.src->depth; - } else { - assert(false); - } - - VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - VkPipelineStageFlags stage = 0; - if (srcImage->layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(*srcImage, barrier, stage, step.readback.aspectMask); - vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - } - ResizeReadbackBuffer(sizeof(uint32_t) * step.readback.srcRect.extent.width * step.readback.srcRect.extent.height); VkBufferImageCopy region{}; @@ -863,9 +847,55 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd region.bufferOffset = 0; region.bufferRowLength = step.readback.srcRect.extent.width; region.bufferImageHeight = step.readback.srcRect.extent.height; - vkCmdCopyImageToBuffer(cmd, srcImage->image, srcImage->layout, readbackBuffer_, 1, ®ion); + + VkImage image; + VkImageLayout copyLayout; + // Special case for backbuffer readbacks. + if (step.readback.src == nullptr) { + // We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT, + // and then back into it. + TransitionImageLayout2(cmd, backbufferImage_, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, VK_ACCESS_TRANSFER_READ_BIT); + copyLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + image = backbufferImage_; + } else { + VKRImage *srcImage; + if (step.readback.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + srcImage = &step.readback.src->color; + } + else if (step.readback.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + srcImage = &step.readback.src->depth; + } + else { + assert(false); + } + + VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + VkPipelineStageFlags stage = 0; + if (srcImage->layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(*srcImage, barrier, stage, step.readback.aspectMask); + vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + image = srcImage->image; + copyLayout = srcImage->layout; + } + + vkCmdCopyImageToBuffer(cmd, image, copyLayout, readbackBuffer_, 1, ®ion); // NOTE: Can't read the buffer using the CPU here - need to sync first. + + // If we copied from the backbuffer, transition it back. + if (step.readback.src == nullptr) { + // We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT, + // and then back into it. + TransitionImageLayout2(cmd, backbufferImage_, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_READ_BIT, 0); + copyLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + } } void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffer cmd) { @@ -911,6 +941,8 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataForm assert(res == VK_SUCCESS); if (srcFormat == Draw::DataFormat::R8G8B8A8_UNORM) { ConvertFromRGBA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat); + } else if (srcFormat == Draw::DataFormat::B8G8R8A8_UNORM) { + ConvertFromBGRA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat); } else if (srcFormat == destFormat) { uint8_t *dst = pixels; const uint8_t *src = (const uint8_t *)mappedData; diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index c49bb207ff..f2bafcff40 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -145,8 +145,9 @@ struct VKRStep { class VulkanQueueRunner { public: VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan) {} - void SetBackbuffer(VkFramebuffer fb) { + void SetBackbuffer(VkFramebuffer fb, VkImage img) { backbuffer_ = fb; + backbufferImage_ = img; } void RunSteps(VkCommandBuffer cmd, const std::vector &steps); void LogSteps(const std::vector &steps); @@ -192,6 +193,7 @@ private: VulkanContext *vulkan_; VkFramebuffer backbuffer_; + VkImage backbufferImage_; VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 36ca452f21..a6182e48b6 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -413,7 +413,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); } -void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { +bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { VKRStep *step = new VKRStep{ VKRStepType::READBACK }; step->readback.aspectMask = aspectBits; step->readback.src = src; @@ -427,9 +427,22 @@ void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a Draw::DataFormat srcFormat; if (aspectBits & VK_IMAGE_ASPECT_COLOR_BIT) { - switch (src->color.format) { - case VK_FORMAT_R8G8B8A8_UNORM: srcFormat = Draw::DataFormat::R8G8B8A8_UNORM; break; - default: assert(false); + if (src) { + switch (src->color.format) { + case VK_FORMAT_R8G8B8A8_UNORM: srcFormat = Draw::DataFormat::R8G8B8A8_UNORM; break; + default: assert(false); + } + } + else { + // Backbuffer. + switch (vulkan_->GetSwapchainFormat()) { + case VK_FORMAT_B8G8R8A8_UNORM: srcFormat = Draw::DataFormat::B8G8R8A8_UNORM; break; + case VK_FORMAT_R8G8B8A8_UNORM: srcFormat = Draw::DataFormat::R8G8B8A8_UNORM; break; + // NOTE: If you add supported formats here, make sure to also support them in VulkanQueueRunner::CopyReadbackBuffer. + default: + ELOG("Unsupported backbuffer format for screenshots"); + return false; + } } } else if (aspectBits & VK_IMAGE_ASPECT_STENCIL_BIT) { // Copies from stencil are always S8. @@ -446,6 +459,7 @@ void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a } // 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; } void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { @@ -724,7 +738,7 @@ void VulkanRenderManager::BeginSubmitFrame(int frame) { assert(res == VK_SUCCESS); - queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage]); + queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage], swapchainImages_[frameData.curSwapchainImage].image); frameData.hasBegun = true; } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 0f9995771c..6a7f4ffbec 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -94,7 +94,7 @@ public: void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); - void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); + bool CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 70a7fb0feb..fa21453ded 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -381,6 +381,31 @@ void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u } } +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const uint32_t *src32 = (const uint32_t *)src; + + if (format == Draw::DataFormat::R8G8B8A8_UNORM) { + uint32_t *dst32 = (uint32_t *)dst; + if (src == dst) { + return; + } + else { + for (uint32_t y = 0; y < height; ++y) { + ConvertBGRA8888ToRGBA8888(dst32, src32, width); + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } + else { + // Don't even bother with these, this path only happens in screenshots and we don't save those to 16-bit. + assert(false); + } +} void ConvertToD32F(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/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index fcb7b73cf8..504f900275 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1337,8 +1337,7 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride); - return true; + return renderManager_.CopyFramebufferToMemorySync(src ? src->GetFB() : nullptr, aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride); } void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { From 96d6f1cae56c401af63fd420bbc61669ef42f23e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 13:57:22 +0100 Subject: [PATCH 09/25] Make sure the device supports transfers from swapchain images when taking screenshots. --- Common/Vulkan/VulkanContext.cpp | 30 +++++++++++++---------- Common/Vulkan/VulkanContext.h | 3 +++ ext/native/thin3d/VulkanRenderManager.cpp | 4 +++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index cc774f4b91..12e3ab1a09 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -712,9 +712,8 @@ bool VulkanContext::InitQueue() { bool VulkanContext::InitSwapchain() { VkResult U_ASSERT_ONLY res; - VkSurfaceCapabilitiesKHR surfCapabilities; - res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices_[physical_device_], surface_, &surfCapabilities); + res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices_[physical_device_], surface_, &surfCapabilities_); assert(res == VK_SUCCESS); uint32_t presentModeCount; @@ -727,7 +726,7 @@ bool VulkanContext::InitSwapchain() { VkExtent2D swapChainExtent; // width and height are either both -1, or both not -1. - if (surfCapabilities.currentExtent.width == (uint32_t)-1) { + if (surfCapabilities_.currentExtent.width == (uint32_t)-1) { // If the surface size is undefined, the size is set to // the size of the images requested. ILOG("initSwapchain: %dx%d", width_, height_); @@ -735,7 +734,7 @@ bool VulkanContext::InitSwapchain() { swapChainExtent.height = height_; } else { // If the surface size is defined, the swap chain size must match - swapChainExtent = surfCapabilities.currentExtent; + swapChainExtent = surfCapabilities_.currentExtent; } // TODO: Find a better way to specify the prioritized present mode while being able @@ -771,20 +770,20 @@ bool VulkanContext::InitSwapchain() { // Determine the number of VkImage's to use in the swap chain (we desire to // own only 1 image at a time, besides the images being displayed and // queued for display): - uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount + 1; + uint32_t desiredNumberOfSwapChainImages = surfCapabilities_.minImageCount + 1; ILOG("numSwapChainImages: %d", desiredNumberOfSwapChainImages); - if ((surfCapabilities.maxImageCount > 0) && - (desiredNumberOfSwapChainImages > surfCapabilities.maxImageCount)) + if ((surfCapabilities_.maxImageCount > 0) && + (desiredNumberOfSwapChainImages > surfCapabilities_.maxImageCount)) { // Application must settle for fewer images than desired: - desiredNumberOfSwapChainImages = surfCapabilities.maxImageCount; + desiredNumberOfSwapChainImages = surfCapabilities_.maxImageCount; } VkSurfaceTransformFlagBitsKHR preTransform; - if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { + if (surfCapabilities_.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else { - preTransform = surfCapabilities.currentTransform; + preTransform = surfCapabilities_.currentTransform; } VkSwapchainCreateInfoKHR swap_chain_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; @@ -799,17 +798,22 @@ bool VulkanContext::InitSwapchain() { swap_chain_info.presentMode = swapchainPresentMode; swap_chain_info.oldSwapchain = VK_NULL_HANDLE; swap_chain_info.clipped = true; - swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) + swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + #ifndef ANDROID // We don't support screenshots on Android - swap_chain_info.imageUsage = swap_chain_info.imageUsage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + // Add more usage flags if they're supported. + if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) + swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; #endif swap_chain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swap_chain_info.queueFamilyIndexCount = 0; swap_chain_info.pQueueFamilyIndices = NULL; // OPAQUE is not supported everywhere. - if (surfCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) { + if (surfCapabilities_.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) { swap_chain_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; } else { // This should be supported anywhere, and is the only thing supported on the SHIELD TV, for example. diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 476edfcf10..a71d3a0f9e 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -198,6 +198,7 @@ public: const VkPhysicalDeviceFeatures &GetFeaturesAvailable() const { return featuresAvailable_; } const VkPhysicalDeviceFeatures &GetFeaturesEnabled() const { return featuresEnabled_; } const VulkanPhysicalDeviceInfo &GetDeviceInfo() const { return deviceInfo_; } + const VkSurfaceCapabilitiesKHR &GetSurfaceCapabilities() const { return surfCapabilities_; } bool IsDeviceExtensionAvailable(const char *name) const { for (auto &iter : device_extension_properties_) { @@ -302,6 +303,8 @@ private: VkPhysicalDeviceFeatures featuresAvailable_{}; VkPhysicalDeviceFeatures featuresEnabled_{}; + VkSurfaceCapabilitiesKHR surfCapabilities_{}; + std::vector cmdQueue_; }; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index a6182e48b6..27175983fe 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -435,6 +435,10 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a } else { // Backbuffer. + if (!(vulkan_->GetSurfaceCapabilities().supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) { + ELOG("Copying from backbuffer not supported, can't take screenshots"); + return false; + } switch (vulkan_->GetSwapchainFormat()) { case VK_FORMAT_B8G8R8A8_UNORM: srcFormat = Draw::DataFormat::B8G8R8A8_UNORM; break; case VK_FORMAT_R8G8B8A8_UNORM: srcFormat = Draw::DataFormat::R8G8B8A8_UNORM; break; From de67c9feff087c002e45fe278afb501a9b32a8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 17:07:25 +0100 Subject: [PATCH 10/25] Vulkan screenshots: Fix R/G swap bug. Thanks unknown. --- GPU/Vulkan/FramebufferVulkan.cpp | 10 ---------- GPU/Vulkan/FramebufferVulkan.h | 3 --- ext/native/thin3d/thin3d.cpp | 14 ++++---------- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index f5ab503601..402ae9ba2d 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -700,13 +700,3 @@ void FramebufferManagerVulkan::CompilePostShader() { usePostShader_ = true; } - -bool FramebufferManagerVulkan::GetOutputFramebuffer(GPUDebugBuffer &buffer) { - int w, h; - draw_->GetFramebufferDimensions(nullptr, &w, &h); - // I'm really not sure why I have to pass the wrong format here, it seems all the other color conversion are correct. - // But I get R/B swapped if I do what seems right... Maybe driver bug for copies from the backbuffer, who knows. - buffer.Allocate(w, h, GPU_DBG_FORMAT_8888_BGRA, false); - draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); - return true; -} diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 5979201536..49538dfa99 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -97,9 +97,6 @@ protected: bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; - // Small tweak to the Common one. - bool GetOutputFramebuffer(GPUDebugBuffer &buffer); - private: // The returned texture does not need to be free'd, might be returned from a pool (currently single entry) void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index fa21453ded..add987548d 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -389,16 +389,10 @@ void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u if (format == Draw::DataFormat::R8G8B8A8_UNORM) { uint32_t *dst32 = (uint32_t *)dst; - if (src == dst) { - return; - } - else { - for (uint32_t y = 0; y < height; ++y) { - ConvertBGRA8888ToRGBA8888(dst32, src32, width); - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } + for (uint32_t y = 0; y < height; ++y) { + ConvertBGRA8888ToRGBA8888(dst32, src32, width); + src32 += srcStride; + dst32 += dstStride; } } else { From fce077f19181b1e9ab3702e6a4973b8a259d32ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 14:53:59 +0100 Subject: [PATCH 11/25] Make test.py python3-compatible, print out command lines better. --- test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.py b/test.py index 7c9b1f8731..bf22ff7280 100755 --- a/test.py +++ b/test.py @@ -40,7 +40,7 @@ class Command(object): def run(self, timeout): def target(): self.process = subprocess.Popen(self.cmd, bufsize=1, stdin=subprocess.PIPE, stdout=sys.stdout, stderr=subprocess.STDOUT) - self.process.stdin.write(self.data) + self.process.stdin.write(self.data.encode('utf-8')) self.process.stdin.close() self.process.communicate() @@ -397,7 +397,7 @@ def run_tests(test_list, args): c = Command(cmdline, '\n'.join(test_filenames)) c.run(TIMEOUT * len(test_filenames)) - print("Ran " + PPSSPP_EXE) + print("Ran " + ' '.join(cmdline)) def main(): From babfec52379c13b6fde72005ffbf553e750ab098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 15:35:15 +0100 Subject: [PATCH 12/25] Fix headless to work better in vulkan mode --- headless/Headless.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index db9645b39f..068285a988 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -186,10 +186,10 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, bool Core_Stop(); } } + PSP_EndHostFrame(); if (coreParameter.thin3d) coreParameter.thin3d->EndFrame(); - PSP_EndHostFrame(); PSP_Shutdown(); @@ -272,7 +272,7 @@ int main(int argc, const char* argv[]) else if (!strcasecmp(gpuName, "null")) gpuCore = GPUCORE_NULL; else - return printUsage(argv[0], "Unknown gpu backend specified after --graphics="); + return printUsage(argv[0], "Unknown gpu backend specified after --graphics=. Allowed: software, directx9, directx11, vulkan, gles, null."); } // Default to GLES if no value selected. else if (!strcmp(argv[i], "--graphics")) From 925ac84069c546491e564585164176de7e770e00 Mon Sep 17 00:00:00 2001 From: orbea Date: Wed, 15 Nov 2017 10:43:55 -0800 Subject: [PATCH 13/25] Update .gitignore for libretro builds. --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 10ecce6bab..26321f6cf4 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,6 @@ build*/ # Temp file used by jenkins windows build (TODO: remove) desc.txt - Logs Memstick memstick @@ -96,5 +95,8 @@ debian/files debian/ppsspp.substvars debian/ppsspp/ +# Libretro build +*.o + # YouCompleteMe file .ycm_extra_conf.pyc From 3e3214d54a7062594c99d9341133f8c9c682d300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 20:43:29 +0100 Subject: [PATCH 14/25] Vulkan: Fix bug where shader blend texture overwrote normal texture binding. Fixes #10135 --- GPU/Vulkan/DrawEngineVulkan.cpp | 20 +++++++++++--------- GPU/Vulkan/DrawEngineVulkan.h | 3 ++- GPU/Vulkan/StateMappingVulkan.cpp | 5 ++++- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 209f5eeaa4..fc5bd2d841 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -523,16 +523,16 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView VkWriteDescriptorSet writes[8]{}; // Main texture int n = 0; - VkDescriptorImageInfo tex{}; + VkDescriptorImageInfo tex[2]{}; if (imageView) { // TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them? - tex.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - tex.imageView = imageView; - tex.sampler = sampler; + tex[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tex[0].imageView = imageView; + tex[0].sampler = sampler; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].pNext = nullptr; writes[n].dstBinding = DRAW_BINDING_TEXTURE; - writes[n].pImageInfo = &tex; + writes[n].pImageInfo = &tex[0]; writes[n].descriptorCount = 1; writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[n].dstSet = desc; @@ -541,13 +541,13 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView if (boundSecondary_) { // TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them? - tex.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - tex.imageView = boundSecondary_; - tex.sampler = samplerSecondary_; + tex[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tex[1].imageView = boundSecondary_; + tex[1].sampler = samplerSecondary_; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].pNext = nullptr; writes[n].dstBinding = DRAW_BINDING_2ND_TEXTURE; - writes[n].pImageInfo = &tex; + writes[n].pImageInfo = &tex[1]; writes[n].descriptorCount = 1; writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[n].dstSet = desc; @@ -877,6 +877,7 @@ void DrawEngineVulkan::DoFlush() { // Already logged, let's bail out. return; } + BindShaderBlendTex(); // This might cause copies so important to do before BindPipeline. renderManager->BindPipeline(pipeline->pipeline); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { @@ -976,6 +977,7 @@ void DrawEngineVulkan::DoFlush() { // Already logged, let's bail out. return; } + BindShaderBlendTex(); // This might cause copies so super important to do before BindPipeline. renderManager->BindPipeline(pipeline->pipeline); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index b9709f261f..fdcce0a73c 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -179,6 +179,7 @@ private: struct FrameData; 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 ResetShaderBlending(); void InitDeviceObjects(); @@ -203,7 +204,7 @@ private: // Secondary texture for shader blending VkImageView boundSecondary_ = VK_NULL_HANDLE; - VkSampler samplerSecondary_ = VK_NULL_HANDLE; + VkSampler samplerSecondary_ = VK_NULL_HANDLE; // This one is actually never used since we use fetch. PrehashMap vai_; VulkanPushBuffer *vertexCache_; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 4b2f1f3e3c..9af27db042 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -362,7 +362,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } } -void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { +void DrawEngineVulkan::BindShaderBlendTex() { // At this point, we know if the vertices are full alpha or not. // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { @@ -377,6 +377,9 @@ void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bo } } +} + +void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { renderManager->SetScissor(dynState_.scissor); renderManager->SetViewport(dynState_.viewport); From 23f78193295a78d398107b0349f436ada28abe3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 20:44:25 +0100 Subject: [PATCH 15/25] Unify RebindFramebuffer, mostly. --- GPU/Common/FramebufferCommon.cpp | 10 ++++++++++ GPU/Common/FramebufferCommon.h | 2 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 2 -- GPU/Directx9/FramebufferDX9.cpp | 9 --------- GPU/Directx9/FramebufferDX9.h | 2 -- GPU/GLES/FramebufferManagerGLES.cpp | 7 +------ GPU/GLES/FramebufferManagerGLES.h | 1 - GPU/Vulkan/FramebufferVulkan.cpp | 10 ---------- GPU/Vulkan/FramebufferVulkan.h | 1 - 9 files changed, 12 insertions(+), 32 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 73f0550b95..66d01f0e84 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -2103,6 +2103,16 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo } } +void FramebufferManagerCommon::RebindFramebuffer() { + if (currentRenderVfb_ && currentRenderVfb_->fbo) { + draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); + } else { + // Should this even happen? + draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); + } + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); +} + std::vector FramebufferManagerCommon::GetFramebufferList() { std::vector list; diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 0b1a4125aa..926f3211c3 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -214,7 +214,7 @@ public: return vfb; } } - virtual void RebindFramebuffer() = 0; + virtual void RebindFramebuffer(); std::vector GetFramebufferList(); void CopyDisplayToOutput(); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 095d659856..5a532d9d9b 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -459,8 +459,6 @@ void FramebufferManagerD3D11::BindPostShader(const PostShaderUniforms &uniforms) context_->Unmap(postConstants_, 0); context_->VSSetConstantBuffers(0, 1, &postConstants_); // Probably not necessary context_->PSSetConstantBuffers(0, 1, &postConstants_); - - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); } void FramebufferManagerD3D11::RebindFramebuffer() { diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index af12aa999a..22466f1620 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -304,15 +304,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } } - void FramebufferManagerDX9::RebindFramebuffer() { - if (currentRenderVfb_ && currentRenderVfb_->fbo) { - draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); - } else { - // Should this even happen? - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); - } - } - void FramebufferManagerDX9::Bind2DShader() { device_->SetVertexDeclaration(pFramebufferVertexDecl); device_->SetPixelShader(pFramebufferPixelShader); diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 544438ed39..dd1b3bd2ca 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -66,8 +66,6 @@ public: bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; - virtual void RebindFramebuffer() override; - LPDIRECT3DSURFACE9 GetOffscreenSurface(LPDIRECT3DSURFACE9 similarSurface, VirtualFramebuffer *vfb); LPDIRECT3DSURFACE9 GetOffscreenSurface(D3DFORMAT fmt, u32 w, u32 h); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 1ec5a83c95..e62cc79463 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -438,12 +438,7 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float } void FramebufferManagerGLES::RebindFramebuffer() { - if (currentRenderVfb_ && currentRenderVfb_->fbo) { - draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); - } else { - // Should this even happen? - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); - } + FramebufferManagerCommon::RebindFramebuffer(); if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) glstate.viewport.restore(); } diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index f867f833d6..03b279c54c 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -81,7 +81,6 @@ public: bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; - virtual void RebindFramebuffer() override; protected: diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 402ae9ba2d..1c1a5d2507 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -367,16 +367,6 @@ void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); } -void FramebufferManagerVulkan::RebindFramebuffer() { - if (currentRenderVfb_ && currentRenderVfb_->fbo) { - draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); - } else { - // Should this even happen? - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); - } - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); -} - int FramebufferManagerVulkan::GetLineWidth() { if (g_Config.iInternalResolution == 0) { return std::max(1, (int)(renderWidth_ / 480)); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 49538dfa99..10d739642c 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -78,7 +78,6 @@ public: bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - virtual void RebindFramebuffer() override; VkImageView BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); // If within a render pass, this will just issue a regular clear. If beginning a new render pass, From 53a351c29de107f95a500dbe68adfac04a5decc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 12:23:10 +0100 Subject: [PATCH 16/25] Workaround orientation bug in Android when unlocking the screen. Or I'm doing something wrong somewhere... Also kill off "Auto" orientation. It's just a pain. Fixes #10128 --- UI/GameSettingsScreen.cpp | 4 +- .../src/org/ppsspp/ppsspp/NativeActivity.java | 38 ++++++++++++------- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 1 + 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 4f0fb0a68a..aeae190a76 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -677,8 +677,8 @@ void GameSettingsScreen::CreateViews() { #if PPSSPP_PLATFORM(ANDROID) if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) { - static const char *screenRotation[] = {"Auto", "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed"}; - PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 0, ARRAY_SIZE(screenRotation), co->GetName(), screenManager())); + static const char *screenRotation[] = {"Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed"}; + PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 1, ARRAY_SIZE(screenRotation), co->GetName(), screenManager())); rot->OnChoice.Handle(this, &GameSettingsScreen::OnScreenRotation); if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) { diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 3f6245e65f..1f0848036d 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -310,7 +310,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // OK, config should be initialized, we can query for screen rotation. if (Build.VERSION.SDK_INT >= 9) { - updateScreenRotation(); + updateScreenRotation("Initialize"); } // Detect OpenGL support. @@ -355,7 +355,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } @TargetApi(9) - private void updateScreenRotation() { + private void updateScreenRotation(String cause) { // Query the native application on the desired rotation. int rot = 0; String rotString = NativeApp.queryConfig("screenRotation"); @@ -365,11 +365,12 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C Log.e(TAG, "Invalid rotation: " + rotString); return; } - Log.i(TAG, "Setting requested rotation: " + rot + " ('" + rotString + "')"); + Log.i(TAG, "Setting requested rotation: " + rot + " ('" + rotString + "') (" + cause + ")"); switch (rot) { case 0: - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + // Auto is no longer supported. + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; case 1: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); @@ -462,7 +463,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C refreshRate = display.getRefreshRate(); // OK, config should be initialized, we can query for screen rotation. - updateScreenRotation(); + updateScreenRotation("onCreate"); updateSustainedPerformanceMode(); // Keep the screen bright - very annoying if it goes dark when tilting away @@ -524,7 +525,18 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C public void surfaceCreated(SurfaceHolder holder) { pixelWidth = holder.getSurfaceFrame().width(); pixelHeight = holder.getSurfaceFrame().height(); - Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString()); + + // Workaround for terrible bug when locking and unlocking the screen in landscape mode on Nexus 5X. + int requestedOr = getRequestedOrientation(); + boolean requestedPortrait = requestedOr == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOr == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + boolean detectedPortrait = pixelHeight > pixelWidth; + if (requestedPortrait != detectedPortrait) { + Log.e(TAG, "Bad orientation detected! Recreating activity."); + recreate();; + return; + } + + Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int)densityDpi, refreshRate); getDesiredBackbufferSize(desiredSize); @@ -687,7 +699,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } // OK, config should be initialized, we can query for screen rotation. if (javaGL || Build.VERSION.SDK_INT >= 9) { - updateScreenRotation(); + updateScreenRotation("onResume"); } Log.i(TAG, "onResume"); @@ -840,10 +852,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C return input.getDescriptor(); } else { List motions = input.getMotionRanges(); - String fakeid = ""; + StringBuilder fakeid = new StringBuilder(); for (InputDevice.MotionRange range : motions) - fakeid += range.getAxis(); - return fakeid; + fakeid.append(range.getAxis()); + return fakeid.toString(); } } @@ -999,7 +1011,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C input.selectAll(); // Lovely! - AlertDialog.Builder bld = null; + AlertDialog.Builder bld; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) bld = new AlertDialog.Builder(this); else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) @@ -1181,13 +1193,13 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C finish(); } else if (command.equals("rotate")) { if (javaGL) { - updateScreenRotation(); + updateScreenRotation("rotate"); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { Log.i(TAG, "Must recreate activity on rotation"); } } else { if (Build.VERSION.SDK_INT >= 9) { - updateScreenRotation(); + updateScreenRotation("rotate"); } } } else if (command.equals("sustainedPerfMode")) { diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 8b0b618954..8d224c0023 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -92,6 +92,7 @@ public class PpssppActivity extends NativeActivity { super.onCreate(savedInstanceState); } + // called by the C++ code through JNI. Dispatch anything we can't directly handle // on the gfx thread to the UI thread. public void postCommand(String command, String parameter) { From 3222af5f7afe5db2276e619b48f5bec01793f836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 13:33:10 +0100 Subject: [PATCH 17/25] Make remoteiso_subdir and server editable on Android. Fixes #10142. Really need a better mechanism for these fields. --- UI/GameSettingsScreen.cpp | 1 + UI/NativeApp.cpp | 6 +++++- UI/RemoteISOScreen.cpp | 24 +++++++++++++++++++++++- UI/RemoteISOScreen.h | 3 +++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index aeae190a76..b9a77aab0e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1108,6 +1108,7 @@ UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { g_Config.sNickName = StripSpaces(name); } #elif defined(__ANDROID__) + // TODO: The return value is handled in NativeApp::inputbox_completed. This is horrific. System_SendMessage("inputbox", ("nickname:" + g_Config.sNickName).c_str()); #endif return UI::EVENT_DONE; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 3c54be8a3c..f9e8a5fb03 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -874,8 +874,12 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { std::string setString = inputboxValue.size() > 1 ? inputboxValue[1] : ""; if (inputboxValue[0] == "IP") g_Config.proAdhocServer = setString; - if (inputboxValue[0] == "nickname") + else if (inputboxValue[0] == "nickname") g_Config.sNickName = setString; + else if (inputboxValue[0] == "remoteiso_subdir") + g_Config.sRemoteISOSubdir = setString; + else if (inputboxValue[0] == "remoteiso_server") + g_Config.sLastRemoteISOServer = setString; inputboxValue.clear(); } if (msg == "bgImage_updated") { diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 39a04f157e..71cf9bf88c 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -675,13 +675,25 @@ void RemoteISOSettingsScreen::CreateViews() { remoteisoSettings->Add(new ItemHeader(ri->T("Remote disc streaming"))); remoteisoSettings->Add(new CheckBox(&g_Config.bRemoteISOManual, ri->T("Manual Mode Client", "Manual Mode Client"))); +#if !defined(MOBILE_DEVICE) PopupTextInputChoice *remoteServer = remoteisoSettings->Add(new PopupTextInputChoice(&g_Config.sLastRemoteISOServer, ri->T("Remote Server"), "", 255, screenManager())); +#else + ChoiceWithValueDisplay *remoteServer = new ChoiceWithValueDisplay(&g_Config.sLastRemoteISOServer, ri->T("Remote Server"), nullptr); + remoteisoSettings->Add(remoteServer); + remoteServer->OnClick.Handle(this, &RemoteISOSettingsScreen::OnClickRemoteServer); +#endif remoteServer->SetEnabledPtr(&g_Config.bRemoteISOManual); PopupSliderChoice *remotePort = remoteisoSettings->Add(new PopupSliderChoice(&g_Config.iLastRemoteISOPort, 0, 65535, ri->T("Remote Port", "Remote Port"), 100, screenManager())); remotePort->SetEnabledPtr(&g_Config.bRemoteISOManual); +#if !defined(MOBILE_DEVICE) PopupTextInputChoice *remoteSubdir = remoteisoSettings->Add(new PopupTextInputChoice(&g_Config.sRemoteISOSubdir, ri->T("Remote Subdirectory"), "", 255, screenManager())); - remoteSubdir->SetEnabledPtr(&g_Config.bRemoteISOManual); remoteSubdir->OnChange.Handle(this, &RemoteISOSettingsScreen::OnChangeRemoteISOSubdir); +#else + ChoiceWithValueDisplay *remoteSubdir = remoteisoSettings->Add( + new ChoiceWithValueDisplay(&g_Config.sRemoteISOSubdir, ri->T("Remote Subdirectory"), nullptr)); + remoteSubdir->OnClick.Handle(this, &RemoteISOSettingsScreen::OnClickRemoteISOSubdir); +#endif + remoteSubdir->SetEnabledPtr(&g_Config.bRemoteISOManual); PopupSliderChoice *portChoice = new PopupSliderChoice(&g_Config.iRemoteISOPort, 0, 65535, ri->T("Local Server Port", "Local Server Port"), 100, screenManager()); remoteisoSettings->Add(portChoice); @@ -693,6 +705,16 @@ void RemoteISOSettingsScreen::CreateViews() { AddStandardBack(root_); } +UI::EventReturn RemoteISOSettingsScreen::OnClickRemoteServer(UI::EventParams &e) { + System_SendMessage("inputbox", ("remoteiso_server:" + g_Config.sLastRemoteISOServer).c_str()); + return UI::EVENT_DONE; +} + +UI::EventReturn RemoteISOSettingsScreen::OnClickRemoteISOSubdir(UI::EventParams &e) { + System_SendMessage("inputbox", ("remoteiso_subdir:" + g_Config.sRemoteISOSubdir).c_str()); + return UI::EVENT_DONE; +} + UI::EventReturn RemoteISOSettingsScreen::OnChangeRemoteISOSubdir(UI::EventParams &e) { //Conform to HTTP standards ReplaceAll(g_Config.sRemoteISOSubdir, " ", "%20"); diff --git a/UI/RemoteISOScreen.h b/UI/RemoteISOScreen.h index 85995e2194..b7f7ff0f5c 100644 --- a/UI/RemoteISOScreen.h +++ b/UI/RemoteISOScreen.h @@ -89,7 +89,10 @@ class RemoteISOSettingsScreen : public UIDialogScreenWithBackground { public: RemoteISOSettingsScreen(); + UI::EventReturn OnClickRemoteISOSubdir(UI::EventParams &e); + UI::EventReturn OnClickRemoteServer(UI::EventParams &e); protected: + void update() override; void CreateViews() override; From a1e593b6babcb5aa3e65bc856b71372d71e2e720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 13:45:15 +0100 Subject: [PATCH 18/25] Fix the "vulkan not found" message on android. Fixes #10034 --- android/jni/app-android.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 76a499bf51..6692e6ae48 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -291,6 +291,7 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, } if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), VULKAN_FLAG_PRESENT_MAILBOX | VULKAN_FLAG_PRESENT_FIFO_RELAXED)) { ELOG("Failed to create vulkan context: %s", g_Vulkan->InitError().c_str()); + System_SendMessage("toast", "No Vulkan compatible device found. Using OpenGL instead."); delete g_Vulkan; g_Vulkan = nullptr; return false; From 1fb7560a14b603cbf7887bc6eb0117eb6f89cfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 14:45:47 +0100 Subject: [PATCH 19/25] Android: When creating shortcuts, put the file path in data, not in Extras. Actually, let's keep extras around now for very limited backwards compatibility but check data first. --- android/jni/app-android.cpp | 3 +++ .../src/org/ppsspp/ppsspp/PpssppActivity.java | 27 ++++++++++--------- .../org/ppsspp/ppsspp/ShortcutActivity.java | 9 ++++++- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 6692e6ae48..c51d4a4a26 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -965,13 +965,16 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env if (msg == "moga") { mogaVersion = prm; } else if (msg == "permission_pending") { + ILOG("STORAGE PERMISSION: PENDING"); // TODO: Add support for other permissions permissions[SYSTEM_PERMISSION_STORAGE] = PERMISSION_STATUS_PENDING; NativePermissionStatus(SYSTEM_PERMISSION_STORAGE, PERMISSION_STATUS_PENDING); } else if (msg == "permission_denied") { + ILOG("STORAGE PERMISSION: DENIED"); permissions[SYSTEM_PERMISSION_STORAGE] = PERMISSION_STATUS_DENIED; NativePermissionStatus(SYSTEM_PERMISSION_STORAGE, PERMISSION_STATUS_PENDING); } else if (msg == "permission_granted") { + ILOG("STORAGE PERMISSION: GRANTED"); permissions[SYSTEM_PERMISSION_STORAGE] = PERMISSION_STATUS_GRANTED; NativePermissionStatus(SYSTEM_PERMISSION_STORAGE, PERMISSION_STATUS_PENDING); } else if (msg == "sustained_perf_supported") { diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 8d224c0023..98141c4a22 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -75,20 +75,23 @@ public class PpssppActivity extends NativeActivity { // using Intent extra string. Intent extra will be null if launch normal // (from app drawer or file explorer). Intent intent = getIntent(); - String action = intent.getAction(); - if (Intent.ACTION_VIEW.equals(action)) { - Uri data = intent.getData(); - if (data != null) { - String path = intent.getData().getPath(); - super.setShortcutParam(path); - Toast.makeText(getApplicationContext(), path, Toast.LENGTH_SHORT).show(); - } else { - Log.e(TAG, "Got ACTION_VIEW without a valid uri"); - } + // String action = intent.getAction(); + Uri data = intent.getData(); + if (data != null) { + String path = intent.getData().getPath(); + Log.i(TAG, "Found Shortcut Parameter in data: " + path); + super.setShortcutParam(path); + // Toast.makeText(getApplicationContext(), path, Toast.LENGTH_SHORT).show(); } else { - super.setShortcutParam(getIntent().getStringExtra(SHORTCUT_EXTRA_KEY)); + String param = getIntent().getStringExtra(SHORTCUT_EXTRA_KEY); + Log.e(TAG, "Got ACTION_VIEW without a valid uri, trying param"); + if (param != null) { + Log.i(TAG, "Found Shortcut Parameter in extra-data: " + param); + super.setShortcutParam(getIntent().getStringExtra(SHORTCUT_EXTRA_KEY)); + } else { + Log.e(TAG, "Shortcut missing parameter!"); + } } - super.onCreate(savedInstanceState); } diff --git a/android/src/org/ppsspp/ppsspp/ShortcutActivity.java b/android/src/org/ppsspp/ppsspp/ShortcutActivity.java index 4c565af95b..dde0eeb703 100644 --- a/android/src/org/ppsspp/ppsspp/ShortcutActivity.java +++ b/android/src/org/ppsspp/ppsspp/ShortcutActivity.java @@ -6,15 +6,18 @@ import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.content.Intent.ShortcutIconResource; +import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Looper; +import android.util.Log; /** * This class will respond to android.intent.action.CREATE_SHORTCUT intent from * launcher homescreen. Register this class in AndroidManifest.xml. */ public class ShortcutActivity extends Activity { + private static final String TAG = "PPSSPP"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -33,8 +36,12 @@ public class ShortcutActivity extends Activity { private void respondToShortcutRequest(String path) { // This is Intent that will be sent when user execute our shortcut on // homescreen. Set our app as target Context. Set Main activity as - // target class. Add any parameter to extra. + // target class. Add any parameter as data. Intent shortcutIntent = new Intent(this, PpssppActivity.class); + Uri uri = Uri.fromFile(new File(path)); + Log.i(TAG, "Shortcut URI: " + uri.toString()); + shortcutIntent.setData(uri); + shortcutIntent.putExtra(PpssppActivity.SHORTCUT_EXTRA_KEY, path); PpssppActivity.CheckABIAndLoadLibrary(); From 5675e8a816bb316d70884ebe81e02cded232b77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 14:59:37 +0100 Subject: [PATCH 20/25] Minor cleanup, and more terrifying orientation hackery --- UI/NativeApp.cpp | 9 ++------- android/src/org/ppsspp/ppsspp/NativeActivity.java | 9 ++++++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index f9e8a5fb03..a5d9e0605a 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -302,12 +302,6 @@ bool CheckFontIsUsable(const wchar_t *fontFace) { #endif void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir, bool fs) { -#ifdef ANDROID_NDK_PROFILER - setenv("CPUPROFILE_FREQUENCY", "500", 1); - setenv("CPUPROFILE", "/sdcard/gmon.out", 1); - monstartup("ppsspp_jni.so"); -#endif - net::Init(); // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times. InitFastMath(cpu_info.bNEON); @@ -442,6 +436,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } else { if (boot_filename.empty()) { + ILOG("Boot filename found in args: %s", argv[i]); boot_filename = argv[i]; #ifdef _WIN32 boot_filename = ReplaceAll(boot_filename, "\\", "/"); @@ -460,7 +455,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } - if (fileToLog != NULL) + if (fileToLog) LogManager::GetInstance()->ChangeFileLog(fileToLog); #ifndef _WIN32 diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 1f0848036d..58e748410f 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -520,7 +520,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } private Point desiredSize = new Point(); - + private int badOrientationCount = 0; @Override public void surfaceCreated(SurfaceHolder holder) { pixelWidth = holder.getSurfaceFrame().width(); @@ -530,10 +530,13 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C int requestedOr = getRequestedOrientation(); boolean requestedPortrait = requestedOr == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOr == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; boolean detectedPortrait = pixelHeight > pixelWidth; - if (requestedPortrait != detectedPortrait) { - Log.e(TAG, "Bad orientation detected! Recreating activity."); + if (badOrientationCount < 3 && requestedPortrait != detectedPortrait) { + Log.e(TAG, "Bad orientation detected (w=" + pixelWidth + " h=" + pixelHeight + "! Recreating activity."); + badOrientationCount++; recreate();; return; + } else if (requestedPortrait == detectedPortrait) { + badOrientationCount = 0; } Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); From a56d6f2399a81d6054389114b444a0147ce44141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 16:27:24 +0100 Subject: [PATCH 21/25] Vulkan: Flush at display list waits. Fixes #10095 --- GPU/Vulkan/DrawEngineVulkan.h | 9 +++++++++ GPU/Vulkan/GPU_Vulkan.cpp | 13 ++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index fdcce0a73c..d33653d2ef 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -147,6 +147,15 @@ public: DoFlush(); } + void FinishDeferred() { + if (!numDrawCalls) + return; + // Decode any pending vertices. And also flush while we're at it, for simplicity. + // It might be possible to only decode like in the other backends, but meh, it can't matter. + // Issue #10095 has a nice example of where this is required. + DoFlush(); + } + void DispatchFlush() override { Flush(); } void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override { SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead); diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 371a55fe92..665c912c90 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -462,6 +462,7 @@ void GPU_Vulkan::FastRunLoop(DisplayList &list) { } void GPU_Vulkan::FinishDeferred() { + drawEngine_.FinishDeferred(); } inline void GPU_Vulkan::CheckFlushOp(int cmd, u32 diff) { @@ -533,12 +534,14 @@ void GPU_Vulkan::Execute_Prim(u32 op, u32 diff) { void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr); void *inds = 0; + u32 vertexType = gstate.vertType; if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { - if (!Memory::IsValidAddress(gstate_c.indexAddr)) { - ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + u32 indexAddr = gstate_c.indexAddr; + if (!Memory::IsValidAddress(indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", indexAddr); return; } - inds = Memory::GetPointerUnchecked(gstate_c.indexAddr); + inds = Memory::GetPointerUnchecked(indexAddr); } #ifndef MOBILE_DEVICE @@ -555,12 +558,12 @@ void GPU_Vulkan::Execute_Prim(u32 op, u32 diff) { int bytesRead = 0; UpdateUVScaleOffset(); - drawEngine_.SubmitPrim(verts, inds, prim, count, gstate.vertType, &bytesRead); + drawEngine_.SubmitPrim(verts, inds, prim, count, vertexType, &bytesRead); // After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed). // Some games rely on this, they don't bother reloading VADDR and IADDR. // The VADDR/IADDR registers are NOT updated. - AdvanceVerts(gstate.vertType, count, bytesRead); + AdvanceVerts(vertexType, count, bytesRead); } void GPU_Vulkan::Execute_LoadClut(u32 op, u32 diff) { From 23b795e94700c40c895eba78bd388480585d9cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 15 Nov 2017 21:49:02 +0100 Subject: [PATCH 22/25] Vulkan: Forgot to enable our various depth rounding features. --- GPU/Vulkan/GPU_Vulkan.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 665c912c90..5fc94ca58f 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -210,6 +210,17 @@ void GPU_Vulkan::CheckGPUFeatures() { features |= GPU_USE_CLEAR_RAM_HACK; } + if (!g_Config.bHighQualityDepth && (features & GPU_SUPPORTS_ACCURATE_DEPTH) != 0) { + features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT; + } + else if (PSP_CoreParameter().compat.flags().PixelDepthRounding) { + // Use fragment rounding on desktop and GLES3, most accurate. + features |= GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT; + } + else if (PSP_CoreParameter().compat.flags().VertexDepthRounding) { + features |= GPU_ROUND_DEPTH_TO_16BIT; + } + // Mandatory features on Vulkan, which may be checked in "centralized" code features |= GPU_SUPPORTS_ACCURATE_DEPTH; features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL; From 766ddf9148950921f520692247b20fd22cb6d4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 21:40:00 +0100 Subject: [PATCH 23/25] Temporarily turn off Vulkan threading on AMD, see issue #10097. --- GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 24 ++++++++++++----------- ext/native/thin3d/VulkanRenderManager.h | 3 +++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index fc5bd2d841..ff981317ec 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -516,7 +516,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView descAlloc.descriptorSetCount = 1; VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc); // Even in release mode, this is bad. - _assert_msg_(G3D, result == VK_SUCCESS, "Ran out of descriptors in pool. sz=%d", (int)frame->descSets.size()); + _assert_msg_(G3D, result == VK_SUCCESS, "Ran out of descriptor space in pool. sz=%d", (int)frame->descSets.size()); // We just don't write to the slots we don't care about. // We need 8 now that we support secondary texture bindings. diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 27175983fe..fabc3339c8 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -11,9 +11,6 @@ #define VLOG(...) #endif -// This works great. Not much reason to disable so let's not even bother with an option. -const bool useThread = true; - #ifndef UINT64_MAX #define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL #endif @@ -127,6 +124,11 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan } queueRunner_.CreateDeviceObjects(); + + // Temporary AMD hack for issue #10097 + if (vulkan_->GetPhysicalDeviceProperties().vendorID == VULKAN_VENDOR_AMD) { + useThread_ = false; + } } void VulkanRenderManager::CreateBackbuffers() { @@ -188,7 +190,7 @@ void VulkanRenderManager::CreateBackbuffers() { } // Start the thread. - if (useThread && HasBackbuffers()) { + if (useThread_ && HasBackbuffers()) { run_ = true; // Won't necessarily be 0. threadInitFrame_ = vulkan_->GetCurFrame(); @@ -198,7 +200,7 @@ void VulkanRenderManager::CreateBackbuffers() { } void VulkanRenderManager::StopThread() { - if (useThread && run_) { + if (useThread_ && run_) { run_ = false; // Stop the thread. for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { @@ -337,7 +339,7 @@ void VulkanRenderManager::BeginFrame() { FrameData &frameData = frameData_[curFrame]; // Make sure the very last command buffer from the frame before the previous has been fully executed. - if (useThread) { + if (useThread_) { std::unique_lock lock(frameData.push_mutex); while (!frameData.readyForFence) { VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); @@ -693,7 +695,7 @@ void VulkanRenderManager::Finish() { curRenderStep_ = nullptr; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; - if (!useThread) { + if (!useThread_) { frameData.steps = std::move(steps_); frameData.type = VKRRunType::END; Run(curFrame); @@ -800,7 +802,7 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { } // When !triggerFence, we notify after syncing with Vulkan. - if (useThread && triggerFence) { + if (useThread_ && triggerFence) { VLOG("PULL: Frame %d.readyForFence = true", frame); std::unique_lock lock(frameData.push_mutex); frameData.readyForFence = true; @@ -880,7 +882,7 @@ void VulkanRenderManager::EndSyncFrame(int frame) { VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); assert(res == VK_SUCCESS); - if (useThread) { + if (useThread_) { std::unique_lock lock(frameData.push_mutex); frameData.readyForFence = true; frameData.push_condVar.notify_all(); @@ -891,7 +893,7 @@ void VulkanRenderManager::FlushSync() { // TODO: Reset curRenderStep_? int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; - if (!useThread) { + if (!useThread_) { frameData.steps = std::move(steps_); frameData.type = VKRRunType::SYNC; Run(curFrame); @@ -905,7 +907,7 @@ void VulkanRenderManager::FlushSync() { frameData.pull_condVar.notify_all(); } - if (useThread) { + if (useThread_) { std::unique_lock lock(frameData.push_mutex); // Wait for the flush to be hit, since we're syncing. while (!frameData.readyForFence) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 6a7f4ffbec..86f282d43d 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -283,4 +283,7 @@ private: VkImageView view = VK_NULL_HANDLE; }; DepthBufferInfo depth_; + + // This works great - except see issue #10097. WTF? + bool useThread_ = true; }; From 506c4eef177ed1285ef118d534b414be392b94fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 16 Nov 2017 21:54:48 +0100 Subject: [PATCH 24/25] Win32: Improvements to vulkan init error handling --- Windows/GPU/WindowsVulkanContext.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index c120e5cd1d..b7a3b44039 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -173,6 +173,12 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m Version gitVer(PPSSPP_GIT_VERSION); g_Vulkan = new VulkanContext(); + if (g_Vulkan->InitError().size()) { + *error_message = g_Vulkan->InitError(); + delete g_Vulkan; + g_Vulkan = nullptr; + return false; + } // int vulkanFlags = VULKAN_FLAG_PRESENT_FIFO_RELAXED; int vulkanFlags = VULKAN_FLAG_PRESENT_MAILBOX; @@ -181,6 +187,8 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m } if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), vulkanFlags)) { *error_message = g_Vulkan->InitError(); + delete g_Vulkan; + g_Vulkan = nullptr; return false; } g_Vulkan->ChooseDevice(g_Vulkan->GetBestPhysicalDevice()); @@ -189,6 +197,8 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m } if (g_Vulkan->CreateDevice() != VK_SUCCESS) { *error_message = g_Vulkan->InitError(); + delete g_Vulkan; + g_Vulkan = nullptr; return false; } if (g_validate_) { @@ -197,6 +207,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m } g_Vulkan->InitSurfaceWin32(hInst, hWnd); if (!g_Vulkan->InitObjects()) { + *error_message = g_Vulkan->InitError(); Shutdown(); return false; } From ca21f93d52ba6ebf013ce0620820d59c61779f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 17 Nov 2017 13:36:48 +0100 Subject: [PATCH 25/25] GPU: Advance the vertex pointer after BBOX vertices are sent. Fixes #6554, finally! --- GPU/Common/DrawEngineCommon.cpp | 13 ++++++++++--- GPU/Common/DrawEngineCommon.h | 5 ++--- GPU/Common/SplineCommon.cpp | 2 ++ GPU/GPUCommon.cpp | 18 +++++++++++------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index e3a0335456..e17111fa06 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -140,9 +140,11 @@ void DrawEngineCommon::Resized() { ClearTrackedVertexArrays(); } -u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, u32 vertType) { +u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, u32 vertType, int *vertexSize) { const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24); VertexDecoder *dec = GetVertexDecoder(vertTypeID); + if (vertexSize) + *vertexSize = dec->VertexSize(); return DrawEngineCommon::NormalizeVertices(outPtr, bufPtr, inPtr, dec, lowerBound, upperBound, vertType); } @@ -150,7 +152,7 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, // // It does the simplest and safest test possible: If all points of a bbox is outside a single of // our clipping planes, we reject the box. Tighter bounds would be desirable but would take more calculations. -bool DrawEngineCommon::TestBoundingBox(void* control_points, int vertexCount, u32 vertType) { +bool DrawEngineCommon::TestBoundingBox(void* control_points, int vertexCount, u32 vertType, int *bytesRead) { SimpleVertex *corners = (SimpleVertex *)(decoded + 65536 * 12); float *verts = (float *)(decoded + 65536 * 18); @@ -158,25 +160,30 @@ bool DrawEngineCommon::TestBoundingBox(void* control_points, int vertexCount, u3 // and a large vertex format. if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_FLOAT) { verts = (float *)control_points; + *bytesRead = 3 * sizeof(float) * vertexCount; } else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_8BIT) { const s8 *vtx = (const s8 *)control_points; for (int i = 0; i < vertexCount * 3; i++) { verts[i] = vtx[i] * (1.0f / 128.0f); } + *bytesRead = 3 * sizeof(s8) * vertexCount; } else if ((vertType & 0xFFFFFF) == GE_VTYPE_POS_16BIT) { const s16 *vtx = (const s16*)control_points; for (int i = 0; i < vertexCount * 3; i++) { verts[i] = vtx[i] * (1.0f / 32768.0f); } + *bytesRead = 3 * sizeof(s16) * vertexCount; } else { // Simplify away bones and morph before proceeding u8 *temp_buffer = decoded + 65536 * 24; - NormalizeVertices((u8 *)corners, temp_buffer, (u8 *)control_points, 0, vertexCount, vertType); + int vertexSize = 0; + NormalizeVertices((u8 *)corners, temp_buffer, (u8 *)control_points, 0, vertexCount, vertType, &vertexSize); for (int i = 0; i < vertexCount; i++) { verts[i * 3] = corners[i].pos.x; verts[i * 3 + 1] = corners[i].pos.y; verts[i * 3 + 2] = corners[i].pos.z; } + *bytesRead = vertexSize * vertexCount; } Plane planes[6]; diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index fe40686ef4..39a53bb83a 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -49,8 +49,6 @@ public: DrawEngineCommon(); virtual ~DrawEngineCommon(); - bool TestBoundingBox(void* control_points, int vertexCount, u32 vertType); - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices); static u32 NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, VertexDecoder *dec, int lowerBound, int upperBound, u32 vertType); @@ -61,6 +59,7 @@ public: // Same for SubmitPrim virtual void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) = 0; + bool TestBoundingBox(void* control_points, int vertexCount, u32 vertType, int *bytesRead); void SubmitSpline(const void *control_points, const void *indices, int tess_u, int tess_v, int count_u, int count_v, int type_u, int type_v, GEPatchPrimType prim_type, bool computeNormals, bool patchFacing, u32 vertType, int *bytesRead); void SubmitBezier(const void *control_points, const void *indices, int tess_u, int tess_v, int count_u, int count_v, GEPatchPrimType prim_type, bool computeNormals, bool patchFacing, u32 vertType, int *bytesRead); @@ -79,7 +78,7 @@ protected: virtual void ClearTrackedVertexArrays() {} // Preprocessing for spline/bezier - u32 NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, u32 vertType); + u32 NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, u32 vertType, int *vertexSize = nullptr); // Utility for vertex caching u32 ComputeMiniHash(); diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 8e428b695e..9afe34aea8 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -855,6 +855,7 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi GetIndexBounds(indices, count_u * count_v, vertType, &index_lower_bound, &index_upper_bound); VertexDecoder *origVDecoder = GetVertexDecoder((vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24)); + // TODO: If indexing is on, this may be wrong! *bytesRead = count_u * count_v * origVDecoder->VertexSize(); // Real hardware seems to draw nothing when given < 4 either U or V. @@ -971,6 +972,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi GetIndexBounds(indices, count_u*count_v, vertType, &index_lower_bound, &index_upper_bound); VertexDecoder *origVDecoder = GetVertexDecoder((vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24)); + // TODO: If indexing is on, this may be wrong! *bytesRead = count_u * count_v * origVDecoder->VertexSize(); // Real hardware seems to draw nothing when given < 4 either U or V. diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 0578ec7bf7..4a836550f1 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1505,13 +1505,17 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { void GPUCommon::Execute_BoundingBox(u32 op, u32 diff) { // Just resetting, nothing to check bounds for. - const u32 data = op & 0x00FFFFFF; - if (data == 0) { + const u32 count = op & 0xFFFFFF; + if (count == 0) { currentList->bboxResult = false; return; } - if (((data & 7) == 0) && data <= 64) { // Sanity check + if (((count & 7) == 0) && count <= 64) { // Sanity check void *control_points = Memory::GetPointer(gstate_c.vertexAddr); + if (!control_points) { + return; + } + if (gstate.vertType & GE_VTYPE_IDX_MASK) { ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Indexed bounding box data not supported."); // Data seems invalid. Let's assume the box test passed. @@ -1520,11 +1524,11 @@ void GPUCommon::Execute_BoundingBox(u32 op, u32 diff) { } // Test if the bounding box is within the drawing region. - if (control_points) { - currentList->bboxResult = drawEngineCommon_->TestBoundingBox(control_points, data, gstate.vertType); - } + int bytesRead; + currentList->bboxResult = drawEngineCommon_->TestBoundingBox(control_points, count, gstate.vertType, &bytesRead); + AdvanceVerts(gstate.vertType, count, bytesRead); } else { - ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Bad bounding box data: %06x", data); + ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Bad bounding box data: %06x", count); // Data seems invalid. Let's assume the box test passed. currentList->bboxResult = true; }