From 375e347ef976e88aefdfc0b0c33b88f272685a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 8 Jun 2019 17:29:07 +0200 Subject: [PATCH 01/63] Set DrawSyncEatCycles for Patapon 2, recommended by pamford45 --- assets/compat.ini | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index f619ca9e02..53b8708aed 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -230,6 +230,17 @@ UCJS18014 = true UCUS98700 = true NPJG00032 = true UCJX90019 = true +# Fixes some double framerate issues in Patapon 2, contributed by pamford45 +UCJS10089 = true +NPJG00010 = true +PSPJ30000 = true +UCAS40232 = true +UCAS40239 = true +UCES01177 = true +UCUS98732 = true +UCJS18036 = true +UCAS40292 = true +UCJS18053 = true [FakeMipmapChange] # This hacks separates each mipmap to independent textures to display wrong-size mipmaps. From cf06b4a9471beeca6bfbb9ca7ae6574b94bb3a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Aug 2019 23:07:42 +0200 Subject: [PATCH 02/63] Vulkan: Image views: The driver is probably smart enough to recognize a no-swizzle, but let's use the proper method. --- Common/Vulkan/VulkanImage.cpp | 5 +---- GPU/Vulkan/DepalettizeShaderVulkan.cpp | 4 ++-- ext/native/thin3d/VulkanRenderManager.cpp | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 5c5e513948..8ad99b774d 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -142,10 +142,7 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *all if (mapping) { view_info.components = *mapping; } else { - view_info.components.r = VK_COMPONENT_SWIZZLE_R; - view_info.components.g = VK_COMPONENT_SWIZZLE_G; - view_info.components.b = VK_COMPONENT_SWIZZLE_B; - view_info.components.a = VK_COMPONENT_SWIZZLE_A; + view_info.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; } view_info.subresourceRange.aspectMask = aspect; view_info.subresourceRange.baseMipLevel = 0; diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index b759882ad7..a55352b31c 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -37,8 +37,8 @@ void main() { static const VkComponentMapping VULKAN_4444_SWIZZLE = { VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B }; static const VkComponentMapping VULKAN_1555_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A }; -static const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; -static const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; +static const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; +static const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; static VkFormat GetClutDestFormat(GEPaletteFormat format, VkComponentMapping *componentMapping) { switch (format) { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 1f8795a34e..81093108ad 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -64,7 +64,7 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + ivci.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; ivci.format = ici.format; ivci.image = img.image; ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; @@ -161,10 +161,10 @@ void VulkanRenderManager::CreateBackbuffers() { VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; color_image_view.format = vulkan_->GetSwapchainFormat(); - color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; - color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; - color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; - color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; + color_image_view.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + color_image_view.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + color_image_view.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + color_image_view.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; color_image_view.subresourceRange.baseMipLevel = 0; color_image_view.subresourceRange.levelCount = 1; @@ -394,6 +394,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == VKRStepType::RENDER) { if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR && stencil != VKRRenderPassAction::CLEAR) { // We don't move to a new step, this bind was unnecessary and we can safely skip it. + // Not sure how much this is still happening but probably worth checking for nevertheless. return; } } @@ -558,7 +559,6 @@ bool VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; image_info.flags = 0; - depth_.format = depth_format; VkDevice device = vulkan_->GetDevice(); @@ -608,10 +608,10 @@ bool VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; depth_view_info.image = depth_.image; depth_view_info.format = depth_format; - depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R; - depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G; - depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B; - depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A; + depth_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + depth_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + depth_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + depth_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; depth_view_info.subresourceRange.aspectMask = aspectMask; depth_view_info.subresourceRange.baseMipLevel = 0; depth_view_info.subresourceRange.levelCount = 1; From ddb5208130921c83b53552b7c3cd09ab8d61aec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Aug 2019 23:19:07 +0200 Subject: [PATCH 03/63] Vulkan: Reduce the amount of redundant stencil parameter sets --- ext/native/thin3d/VulkanQueueRunner.cpp | 28 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index fdbdf3ebe9..be7b62cf67 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -869,9 +869,11 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c auto &commands = step.commands; - // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only - // valid when a pipeline is bound with those as dynamic state. So we need to add some state tracking here - // for this to be correct. This is a bit of a pain but also will let us eliminate redundant calls. + // We can do a little bit of state tracking here to eliminate some calls into the driver. + // The stencil ones are very commonly mostly redundant so let's eliminate them where possible. + int lastStencilWriteMask = -1; + int lastStencilCompareMask = -1; + int lastStencilReference = -1; for (const auto &c : commands) { switch (c.cmd) { @@ -882,6 +884,10 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c if (c.pipeline.pipeline != lastPipeline) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline); lastPipeline = c.pipeline.pipeline; + // Reset dynamic state so it gets refreshed with the new pipeline. + lastStencilWriteMask = -1; + lastStencilCompareMask = -1; + lastStencilReference = -1; } break; @@ -902,9 +908,18 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c break; case VKRRenderCommand::STENCIL: - vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); - vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + if (lastStencilReference != c.stencil.stencilWriteMask) { + lastStencilReference = (int)c.stencil.stencilWriteMask; + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); + } + if (lastStencilCompareMask != c.stencil.stencilCompareMask) { + lastStencilCompareMask = c.stencil.stencilCompareMask; + vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); + } + if (lastStencilReference != c.stencil.stencilRef) { + lastStencilReference = c.stencil.stencilRef; + vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + } break; case VKRRenderCommand::DRAW_INDEXED: @@ -924,6 +939,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::CLEAR: { + // If we get here, we failed to merge a clear into a render pass load op. This is bad for perf. int numAttachments = 0; VkClearRect rc{}; rc.baseArrayLayer = 0; From f4d348ca0c5e1388aa5ec0702a81c6f888edbb8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Aug 2019 23:24:40 +0200 Subject: [PATCH 04/63] Micro-optimize the Vulkan loader: Load more device-specific function pointers to avoid a bounce in the device loader, reorder things a little. --- Common/Vulkan/VulkanLoader.cpp | 255 ++++++++++++++++----------------- 1 file changed, 126 insertions(+), 129 deletions(-) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index aedcfd639b..0a3dc04f57 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -61,9 +61,7 @@ PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImag PFN_vkQueueBindSparse vkQueueBindSparse; PFN_vkCreateFence vkCreateFence; PFN_vkDestroyFence vkDestroyFence; -PFN_vkResetFences vkResetFences; PFN_vkGetFenceStatus vkGetFenceStatus; -PFN_vkWaitForFences vkWaitForFences; PFN_vkCreateSemaphore vkCreateSemaphore; PFN_vkDestroySemaphore vkDestroySemaphore; PFN_vkCreateEvent vkCreateEvent; @@ -115,7 +113,7 @@ PFN_vkResetCommandPool vkResetCommandPool; PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; PFN_vkFreeCommandBuffers vkFreeCommandBuffers; -// Used frequently +// Used frequently together PFN_vkCmdBindPipeline vkCmdBindPipeline; PFN_vkCmdSetViewport vkCmdSetViewport; PFN_vkCmdSetScissor vkCmdSetScissor; @@ -128,14 +126,16 @@ PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; PFN_vkCmdDraw vkCmdDraw; PFN_vkCmdDrawIndexed vkCmdDrawIndexed; -PFN_vkCmdClearAttachments vkCmdClearAttachments; PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; PFN_vkCmdPushConstants vkCmdPushConstants; // Every frame to a few times per frame +PFN_vkWaitForFences vkWaitForFences; +PFN_vkResetFences vkResetFences; PFN_vkBeginCommandBuffer vkBeginCommandBuffer; PFN_vkEndCommandBuffer vkEndCommandBuffer; PFN_vkResetCommandBuffer vkResetCommandBuffer; +PFN_vkCmdClearAttachments vkCmdClearAttachments; PFN_vkCmdSetEvent vkCmdSetEvent; PFN_vkCmdResetEvent vkCmdResetEvent; PFN_vkCmdWaitEvents vkCmdWaitEvents; @@ -147,6 +147,7 @@ PFN_vkCmdBlitImage vkCmdBlitImage; PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; +// Rare or not used PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; PFN_vkCmdSetLineWidth vkCmdSetLineWidth; PFN_vkCmdSetDepthBias vkCmdSetDepthBias; @@ -439,125 +440,7 @@ void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanDeviceExtensio LOAD_INSTANCE_FUNC(instance, vkEnumerateDeviceExtensionProperties); LOAD_INSTANCE_FUNC(instance, vkEnumerateDeviceLayerProperties); LOAD_INSTANCE_FUNC(instance, vkGetDeviceQueue); - LOAD_INSTANCE_FUNC(instance, vkQueueSubmit); - LOAD_INSTANCE_FUNC(instance, vkQueueWaitIdle); LOAD_INSTANCE_FUNC(instance, vkDeviceWaitIdle); - LOAD_INSTANCE_FUNC(instance, vkAllocateMemory); - LOAD_INSTANCE_FUNC(instance, vkFreeMemory); - LOAD_INSTANCE_FUNC(instance, vkMapMemory); - LOAD_INSTANCE_FUNC(instance, vkUnmapMemory); - LOAD_INSTANCE_FUNC(instance, vkFlushMappedMemoryRanges); - LOAD_INSTANCE_FUNC(instance, vkInvalidateMappedMemoryRanges); - LOAD_INSTANCE_FUNC(instance, vkGetDeviceMemoryCommitment); - LOAD_INSTANCE_FUNC(instance, vkBindBufferMemory); - LOAD_INSTANCE_FUNC(instance, vkBindImageMemory); - LOAD_INSTANCE_FUNC(instance, vkGetBufferMemoryRequirements); - LOAD_INSTANCE_FUNC(instance, vkGetImageMemoryRequirements); - LOAD_INSTANCE_FUNC(instance, vkGetImageSparseMemoryRequirements); - LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSparseImageFormatProperties); - LOAD_INSTANCE_FUNC(instance, vkQueueBindSparse); - LOAD_INSTANCE_FUNC(instance, vkCreateFence); - LOAD_INSTANCE_FUNC(instance, vkDestroyFence); - LOAD_INSTANCE_FUNC(instance, vkResetFences); - LOAD_INSTANCE_FUNC(instance, vkGetFenceStatus); - LOAD_INSTANCE_FUNC(instance, vkWaitForFences); - LOAD_INSTANCE_FUNC(instance, vkCreateSemaphore); - LOAD_INSTANCE_FUNC(instance, vkDestroySemaphore); - LOAD_INSTANCE_FUNC(instance, vkCreateEvent); - LOAD_INSTANCE_FUNC(instance, vkDestroyEvent); - LOAD_INSTANCE_FUNC(instance, vkGetEventStatus); - LOAD_INSTANCE_FUNC(instance, vkSetEvent); - LOAD_INSTANCE_FUNC(instance, vkResetEvent); - LOAD_INSTANCE_FUNC(instance, vkCreateQueryPool); - LOAD_INSTANCE_FUNC(instance, vkDestroyQueryPool); - LOAD_INSTANCE_FUNC(instance, vkGetQueryPoolResults); - LOAD_INSTANCE_FUNC(instance, vkCreateBuffer); - LOAD_INSTANCE_FUNC(instance, vkDestroyBuffer); - LOAD_INSTANCE_FUNC(instance, vkCreateBufferView); - LOAD_INSTANCE_FUNC(instance, vkDestroyBufferView); - LOAD_INSTANCE_FUNC(instance, vkCreateImage); - LOAD_INSTANCE_FUNC(instance, vkDestroyImage); - LOAD_INSTANCE_FUNC(instance, vkGetImageSubresourceLayout); - LOAD_INSTANCE_FUNC(instance, vkCreateImageView); - LOAD_INSTANCE_FUNC(instance, vkDestroyImageView); - LOAD_INSTANCE_FUNC(instance, vkCreateShaderModule); - LOAD_INSTANCE_FUNC(instance, vkDestroyShaderModule); - LOAD_INSTANCE_FUNC(instance, vkCreatePipelineCache); - LOAD_INSTANCE_FUNC(instance, vkDestroyPipelineCache); - LOAD_INSTANCE_FUNC(instance, vkGetPipelineCacheData); - LOAD_INSTANCE_FUNC(instance, vkMergePipelineCaches); - LOAD_INSTANCE_FUNC(instance, vkCreateGraphicsPipelines); - LOAD_INSTANCE_FUNC(instance, vkCreateComputePipelines); - LOAD_INSTANCE_FUNC(instance, vkDestroyPipeline); - LOAD_INSTANCE_FUNC(instance, vkCreatePipelineLayout); - LOAD_INSTANCE_FUNC(instance, vkDestroyPipelineLayout); - LOAD_INSTANCE_FUNC(instance, vkCreateSampler); - LOAD_INSTANCE_FUNC(instance, vkDestroySampler); - LOAD_INSTANCE_FUNC(instance, vkCreateDescriptorSetLayout); - LOAD_INSTANCE_FUNC(instance, vkDestroyDescriptorSetLayout); - LOAD_INSTANCE_FUNC(instance, vkCreateDescriptorPool); - LOAD_INSTANCE_FUNC(instance, vkDestroyDescriptorPool); - LOAD_INSTANCE_FUNC(instance, vkResetDescriptorPool); - LOAD_INSTANCE_FUNC(instance, vkAllocateDescriptorSets); - LOAD_INSTANCE_FUNC(instance, vkFreeDescriptorSets); - LOAD_INSTANCE_FUNC(instance, vkUpdateDescriptorSets); - LOAD_INSTANCE_FUNC(instance, vkCreateFramebuffer); - LOAD_INSTANCE_FUNC(instance, vkDestroyFramebuffer); - LOAD_INSTANCE_FUNC(instance, vkCreateRenderPass); - LOAD_INSTANCE_FUNC(instance, vkDestroyRenderPass); - LOAD_INSTANCE_FUNC(instance, vkGetRenderAreaGranularity); - LOAD_INSTANCE_FUNC(instance, vkCreateCommandPool); - LOAD_INSTANCE_FUNC(instance, vkDestroyCommandPool); - LOAD_INSTANCE_FUNC(instance, vkResetCommandPool); - LOAD_INSTANCE_FUNC(instance, vkAllocateCommandBuffers); - LOAD_INSTANCE_FUNC(instance, vkFreeCommandBuffers); - LOAD_INSTANCE_FUNC(instance, vkBeginCommandBuffer); - LOAD_INSTANCE_FUNC(instance, vkEndCommandBuffer); - LOAD_INSTANCE_FUNC(instance, vkResetCommandBuffer); - LOAD_INSTANCE_FUNC(instance, vkCmdBindPipeline); - LOAD_INSTANCE_FUNC(instance, vkCmdSetViewport); - LOAD_INSTANCE_FUNC(instance, vkCmdSetScissor); - LOAD_INSTANCE_FUNC(instance, vkCmdSetLineWidth); - LOAD_INSTANCE_FUNC(instance, vkCmdSetDepthBias); - LOAD_INSTANCE_FUNC(instance, vkCmdSetBlendConstants); - LOAD_INSTANCE_FUNC(instance, vkCmdSetDepthBounds); - LOAD_INSTANCE_FUNC(instance, vkCmdSetStencilCompareMask); - LOAD_INSTANCE_FUNC(instance, vkCmdSetStencilWriteMask); - LOAD_INSTANCE_FUNC(instance, vkCmdSetStencilReference); - LOAD_INSTANCE_FUNC(instance, vkCmdBindDescriptorSets); - LOAD_INSTANCE_FUNC(instance, vkCmdBindIndexBuffer); - LOAD_INSTANCE_FUNC(instance, vkCmdBindVertexBuffers); - LOAD_INSTANCE_FUNC(instance, vkCmdDraw); - LOAD_INSTANCE_FUNC(instance, vkCmdDrawIndexed); - LOAD_INSTANCE_FUNC(instance, vkCmdDrawIndirect); - LOAD_INSTANCE_FUNC(instance, vkCmdDrawIndexedIndirect); - LOAD_INSTANCE_FUNC(instance, vkCmdDispatch); - LOAD_INSTANCE_FUNC(instance, vkCmdDispatchIndirect); - LOAD_INSTANCE_FUNC(instance, vkCmdCopyBuffer); - LOAD_INSTANCE_FUNC(instance, vkCmdCopyImage); - LOAD_INSTANCE_FUNC(instance, vkCmdBlitImage); - LOAD_INSTANCE_FUNC(instance, vkCmdCopyBufferToImage); - LOAD_INSTANCE_FUNC(instance, vkCmdCopyImageToBuffer); - LOAD_INSTANCE_FUNC(instance, vkCmdUpdateBuffer); - LOAD_INSTANCE_FUNC(instance, vkCmdFillBuffer); - LOAD_INSTANCE_FUNC(instance, vkCmdClearColorImage); - LOAD_INSTANCE_FUNC(instance, vkCmdClearDepthStencilImage); - LOAD_INSTANCE_FUNC(instance, vkCmdClearAttachments); - LOAD_INSTANCE_FUNC(instance, vkCmdResolveImage); - LOAD_INSTANCE_FUNC(instance, vkCmdSetEvent); - LOAD_INSTANCE_FUNC(instance, vkCmdResetEvent); - LOAD_INSTANCE_FUNC(instance, vkCmdWaitEvents); - LOAD_INSTANCE_FUNC(instance, vkCmdPipelineBarrier); - LOAD_INSTANCE_FUNC(instance, vkCmdBeginQuery); - LOAD_INSTANCE_FUNC(instance, vkCmdEndQuery); - LOAD_INSTANCE_FUNC(instance, vkCmdResetQueryPool); - LOAD_INSTANCE_FUNC(instance, vkCmdWriteTimestamp); - LOAD_INSTANCE_FUNC(instance, vkCmdCopyQueryPoolResults); - LOAD_INSTANCE_FUNC(instance, vkCmdPushConstants); - LOAD_INSTANCE_FUNC(instance, vkCmdBeginRenderPass); - LOAD_INSTANCE_FUNC(instance, vkCmdNextSubpass); - LOAD_INSTANCE_FUNC(instance, vkCmdEndRenderPass); - LOAD_INSTANCE_FUNC(instance, vkCmdExecuteCommands); LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceSupportKHR); LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR); @@ -609,15 +492,129 @@ void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanDeviceExtensio // On some implementations, loading functions (that have Device as their first parameter) via vkGetDeviceProcAddr may // increase performance - but then these function pointers will only work on that specific device. Thus, this loader is not very -// good for multi-device. +// good for multi-device - not likely we'll ever try that anyway though. void VulkanLoadDeviceFunctions(VkDevice device, const VulkanDeviceExtensions &enabledExtensions) { WLOG("Vulkan device functions loaded."); - // TODO: Move more functions VulkanLoadInstanceFunctions to here. - LOAD_DEVICE_FUNC(device, vkCreateSwapchainKHR); - LOAD_DEVICE_FUNC(device, vkDestroySwapchainKHR); - LOAD_DEVICE_FUNC(device, vkGetSwapchainImagesKHR); - LOAD_DEVICE_FUNC(device, vkAcquireNextImageKHR); - LOAD_DEVICE_FUNC(device, vkQueuePresentKHR); + + LOAD_DEVICE_FUNC(device, vkQueueSubmit); + LOAD_DEVICE_FUNC(device, vkQueueWaitIdle); + LOAD_DEVICE_FUNC(device, vkAllocateMemory); + LOAD_DEVICE_FUNC(device, vkFreeMemory); + LOAD_DEVICE_FUNC(device, vkMapMemory); + LOAD_DEVICE_FUNC(device, vkUnmapMemory); + LOAD_DEVICE_FUNC(device, vkFlushMappedMemoryRanges); + LOAD_DEVICE_FUNC(device, vkInvalidateMappedMemoryRanges); + LOAD_DEVICE_FUNC(device, vkGetDeviceMemoryCommitment); + LOAD_DEVICE_FUNC(device, vkBindBufferMemory); + LOAD_DEVICE_FUNC(device, vkBindImageMemory); + LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements); + LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements); + LOAD_DEVICE_FUNC(device, vkGetImageSparseMemoryRequirements); + LOAD_DEVICE_FUNC(device, vkGetPhysicalDeviceSparseImageFormatProperties); + LOAD_DEVICE_FUNC(device, vkQueueBindSparse); + LOAD_DEVICE_FUNC(device, vkCreateFence); + LOAD_DEVICE_FUNC(device, vkDestroyFence); + LOAD_DEVICE_FUNC(device, vkResetFences); + LOAD_DEVICE_FUNC(device, vkGetFenceStatus); + LOAD_DEVICE_FUNC(device, vkWaitForFences); + LOAD_DEVICE_FUNC(device, vkCreateSemaphore); + LOAD_DEVICE_FUNC(device, vkDestroySemaphore); + LOAD_DEVICE_FUNC(device, vkCreateEvent); + LOAD_DEVICE_FUNC(device, vkDestroyEvent); + LOAD_DEVICE_FUNC(device, vkGetEventStatus); + LOAD_DEVICE_FUNC(device, vkSetEvent); + LOAD_DEVICE_FUNC(device, vkResetEvent); + LOAD_DEVICE_FUNC(device, vkCreateQueryPool); + LOAD_DEVICE_FUNC(device, vkDestroyQueryPool); + LOAD_DEVICE_FUNC(device, vkGetQueryPoolResults); + LOAD_DEVICE_FUNC(device, vkCreateBuffer); + LOAD_DEVICE_FUNC(device, vkDestroyBuffer); + LOAD_DEVICE_FUNC(device, vkCreateBufferView); + LOAD_DEVICE_FUNC(device, vkDestroyBufferView); + LOAD_DEVICE_FUNC(device, vkCreateImage); + LOAD_DEVICE_FUNC(device, vkDestroyImage); + LOAD_DEVICE_FUNC(device, vkGetImageSubresourceLayout); + LOAD_DEVICE_FUNC(device, vkCreateImageView); + LOAD_DEVICE_FUNC(device, vkDestroyImageView); + LOAD_DEVICE_FUNC(device, vkCreateShaderModule); + LOAD_DEVICE_FUNC(device, vkDestroyShaderModule); + LOAD_DEVICE_FUNC(device, vkCreatePipelineCache); + LOAD_DEVICE_FUNC(device, vkDestroyPipelineCache); + LOAD_DEVICE_FUNC(device, vkGetPipelineCacheData); + LOAD_DEVICE_FUNC(device, vkMergePipelineCaches); + LOAD_DEVICE_FUNC(device, vkCreateGraphicsPipelines); + LOAD_DEVICE_FUNC(device, vkCreateComputePipelines); + LOAD_DEVICE_FUNC(device, vkDestroyPipeline); + LOAD_DEVICE_FUNC(device, vkCreatePipelineLayout); + LOAD_DEVICE_FUNC(device, vkDestroyPipelineLayout); + LOAD_DEVICE_FUNC(device, vkCreateSampler); + LOAD_DEVICE_FUNC(device, vkDestroySampler); + LOAD_DEVICE_FUNC(device, vkCreateDescriptorSetLayout); + LOAD_DEVICE_FUNC(device, vkDestroyDescriptorSetLayout); + LOAD_DEVICE_FUNC(device, vkCreateDescriptorPool); + LOAD_DEVICE_FUNC(device, vkDestroyDescriptorPool); + LOAD_DEVICE_FUNC(device, vkResetDescriptorPool); + LOAD_DEVICE_FUNC(device, vkAllocateDescriptorSets); + LOAD_DEVICE_FUNC(device, vkFreeDescriptorSets); + LOAD_DEVICE_FUNC(device, vkUpdateDescriptorSets); + LOAD_DEVICE_FUNC(device, vkCreateFramebuffer); + LOAD_DEVICE_FUNC(device, vkDestroyFramebuffer); + LOAD_DEVICE_FUNC(device, vkCreateRenderPass); + LOAD_DEVICE_FUNC(device, vkDestroyRenderPass); + LOAD_DEVICE_FUNC(device, vkGetRenderAreaGranularity); + LOAD_DEVICE_FUNC(device, vkCreateCommandPool); + LOAD_DEVICE_FUNC(device, vkDestroyCommandPool); + LOAD_DEVICE_FUNC(device, vkResetCommandPool); + LOAD_DEVICE_FUNC(device, vkAllocateCommandBuffers); + LOAD_DEVICE_FUNC(device, vkFreeCommandBuffers); + LOAD_DEVICE_FUNC(device, vkBeginCommandBuffer); + LOAD_DEVICE_FUNC(device, vkEndCommandBuffer); + LOAD_DEVICE_FUNC(device, vkResetCommandBuffer); + LOAD_DEVICE_FUNC(device, vkCmdBindPipeline); + LOAD_DEVICE_FUNC(device, vkCmdSetViewport); + LOAD_DEVICE_FUNC(device, vkCmdSetScissor); + LOAD_DEVICE_FUNC(device, vkCmdSetLineWidth); + LOAD_DEVICE_FUNC(device, vkCmdSetDepthBias); + LOAD_DEVICE_FUNC(device, vkCmdSetBlendConstants); + LOAD_DEVICE_FUNC(device, vkCmdSetDepthBounds); + LOAD_DEVICE_FUNC(device, vkCmdSetStencilCompareMask); + LOAD_DEVICE_FUNC(device, vkCmdSetStencilWriteMask); + LOAD_DEVICE_FUNC(device, vkCmdSetStencilReference); + LOAD_DEVICE_FUNC(device, vkCmdBindDescriptorSets); + LOAD_DEVICE_FUNC(device, vkCmdBindIndexBuffer); + LOAD_DEVICE_FUNC(device, vkCmdBindVertexBuffers); + LOAD_DEVICE_FUNC(device, vkCmdDraw); + LOAD_DEVICE_FUNC(device, vkCmdDrawIndexed); + LOAD_DEVICE_FUNC(device, vkCmdDrawIndirect); + LOAD_DEVICE_FUNC(device, vkCmdDrawIndexedIndirect); + LOAD_DEVICE_FUNC(device, vkCmdDispatch); + LOAD_DEVICE_FUNC(device, vkCmdDispatchIndirect); + LOAD_DEVICE_FUNC(device, vkCmdCopyBuffer); + LOAD_DEVICE_FUNC(device, vkCmdCopyImage); + LOAD_DEVICE_FUNC(device, vkCmdBlitImage); + LOAD_DEVICE_FUNC(device, vkCmdCopyBufferToImage); + LOAD_DEVICE_FUNC(device, vkCmdCopyImageToBuffer); + LOAD_DEVICE_FUNC(device, vkCmdUpdateBuffer); + LOAD_DEVICE_FUNC(device, vkCmdFillBuffer); + LOAD_DEVICE_FUNC(device, vkCmdClearColorImage); + LOAD_DEVICE_FUNC(device, vkCmdClearDepthStencilImage); + LOAD_DEVICE_FUNC(device, vkCmdClearAttachments); + LOAD_DEVICE_FUNC(device, vkCmdResolveImage); + LOAD_DEVICE_FUNC(device, vkCmdSetEvent); + LOAD_DEVICE_FUNC(device, vkCmdResetEvent); + LOAD_DEVICE_FUNC(device, vkCmdWaitEvents); + LOAD_DEVICE_FUNC(device, vkCmdPipelineBarrier); + LOAD_DEVICE_FUNC(device, vkCmdBeginQuery); + LOAD_DEVICE_FUNC(device, vkCmdEndQuery); + LOAD_DEVICE_FUNC(device, vkCmdResetQueryPool); + LOAD_DEVICE_FUNC(device, vkCmdWriteTimestamp); + LOAD_DEVICE_FUNC(device, vkCmdCopyQueryPoolResults); + LOAD_DEVICE_FUNC(device, vkCmdPushConstants); + LOAD_DEVICE_FUNC(device, vkCmdBeginRenderPass); + LOAD_DEVICE_FUNC(device, vkCmdNextSubpass); + LOAD_DEVICE_FUNC(device, vkCmdEndRenderPass); + LOAD_DEVICE_FUNC(device, vkCmdExecuteCommands); + if (enabledExtensions.EXT_external_memory_host) { LOAD_DEVICE_FUNC(device, vkGetMemoryHostPointerPropertiesEXT); } From 49e64f10d47272ebabbf577c6def844dcb07c3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Aug 2019 23:36:35 +0200 Subject: [PATCH 05/63] Minor code cleanup --- ext/native/thin3d/VulkanRenderManager.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 81093108ad..84bfcd3266 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -800,7 +800,8 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { - // Should just mark the dependency and return the image. + // Mark the dependency and return the image. + for (int i = (int)steps_.size() - 1; i >= 0; i--) { if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) { // If this framebuffer was rendered to earlier in this frame, make sure to pre-transition it to the correct layout. @@ -817,9 +818,10 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in curRenderStep_->preTransitions.back().targetLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { // We're done. return fb->color.imageView; + } else { + curRenderStep_->preTransitions.push_back({ fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); + return fb->color.imageView; } - curRenderStep_->preTransitions.push_back({ fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); - return fb->color.imageView; } void VulkanRenderManager::Finish() { From d0a341087511e314672fbeab7885f501615ea890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Aug 2019 00:22:47 +0200 Subject: [PATCH 06/63] Typo fix --- ext/native/thin3d/VulkanQueueRunner.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index be7b62cf67..d9ed03a426 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -392,7 +392,6 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st for (int j = 0; j < (int)steps.size() - 1; j++) { // Push down empty "Clear/Store" renderpasses, and merge them with the first "Load/Store" to the same framebuffer. - // Actually let's just bother with the first one for now. This affects Wipeout Pure. if (steps.size() > 1 && steps[j]->stepType == VKRStepType::RENDER && steps[j]->render.numDraws == 0 && steps[j]->render.numReads == 0 && @@ -400,8 +399,8 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st steps[j]->render.stencil == VKRRenderPassAction::CLEAR && steps[j]->render.depth == VKRRenderPassAction::CLEAR) { - // Drop the first step, and merge it into the next step that touches the same framebuffer. - for (size_t i = j + 1; i < steps.size(); i++) { + // Drop the clear step, and merge it into the next step that touches the same framebuffer. + for (int i = j + 1; i < (int)steps.size(); i++) { if (steps[i]->stepType == VKRStepType::RENDER && steps[i]->render.framebuffer == steps[j]->render.framebuffer) { if (steps[i]->render.color != VKRRenderPassAction::CLEAR) { @@ -908,8 +907,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c break; case VKRRenderCommand::STENCIL: - if (lastStencilReference != c.stencil.stencilWriteMask) { - lastStencilReference = (int)c.stencil.stencilWriteMask; + if (lastStencilWriteMask != c.stencil.stencilWriteMask) { + lastStencilWriteMask = (int)c.stencil.stencilWriteMask; vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); } if (lastStencilCompareMask != c.stencil.stencilCompareMask) { From 8788c1e678ef65e7ad5242cb300664d7a8ede94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Aug 2019 09:37:09 +0200 Subject: [PATCH 07/63] Vulkan: Simple blacklist of device names that are not allowed to check for Vulkan. Will help #12093. --- Common/Vulkan/VulkanLoader.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index aedcfd639b..f9ca4f090a 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -17,8 +17,11 @@ #include "Common/Vulkan/VulkanLoader.h" #include +#include + #include "base/logging.h" #include "base/basictypes.h" +#include "base/NativeApp.h" #ifndef _WIN32 #include @@ -226,6 +229,10 @@ bool g_vulkanMayBeAvailable = false; #define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x); +static const char *device_name_blacklist[] = { + "SHIELD Tablet", +}; + static const char *so_names[] = { "libvulkan.so", "libvulkan.so.1", @@ -239,6 +246,16 @@ void VulkanSetAvailable(bool available) { bool VulkanMayBeAvailable() { if (g_vulkanAvailabilityChecked) return g_vulkanMayBeAvailable; + + std::string name = System_GetProperty(SYSPROP_NAME); + for (size_t i = 0; i < ARRAY_SIZE(device_name_blacklist); i++) { + if (name == device_name_blacklist[i]) { + g_vulkanAvailabilityChecked = true; + g_vulkanMayBeAvailable = false; + return false; + } + } + #ifndef _WIN32 void *lib = nullptr; for (int i = 0; i < ARRAY_SIZE(so_names); i++) { From caa8f1a43331515bcc279594afd9e36d5f97da7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Aug 2019 10:12:15 +0200 Subject: [PATCH 08/63] Android: NativeApp.Init: Log the device name. --- android/jni/app-android.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 8d6e5f4c44..7014ee72ca 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -404,6 +404,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init systemName = GetJavaString(env, jmodel); langRegion = GetJavaString(env, jlangRegion); + ILOG("NativeApp.init(): device name: '%s'", systemName.c_str()); + std::string externalDir = GetJavaString(env, jexternalDir); std::string user_data_path = GetJavaString(env, jdataDir); if (user_data_path.size() > 0) From 44cb991d8cc745e9ba966123dbd2290c7cea7595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Aug 2019 10:17:24 +0200 Subject: [PATCH 09/63] Cleanup --- Common/Vulkan/VulkanLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index f9ca4f090a..f10c62391c 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -248,8 +248,8 @@ bool VulkanMayBeAvailable() { return g_vulkanMayBeAvailable; std::string name = System_GetProperty(SYSPROP_NAME); - for (size_t i = 0; i < ARRAY_SIZE(device_name_blacklist); i++) { - if (name == device_name_blacklist[i]) { + for (const char *blacklisted_name : device_name_blacklist) { + if (!strcmp(name.c_str(), blacklisted_name)) { g_vulkanAvailabilityChecked = true; g_vulkanMayBeAvailable = false; return false; From 18e7877526490410745a0f4349d90f08f6e6ebec Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Tue, 13 Aug 2019 20:57:36 +0300 Subject: [PATCH 10/63] Update PowerSaveModeReceiver --- android/jni/native-audio-so.cpp | 1 - android/jni/native-audio-so.h | 1 - .../src/org/ppsspp/ppsspp/NativeActivity.java | 2 ++ .../ppsspp/ppsspp/PowerSaveModeReceiver.java | 35 +++++++++++++++---- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/android/jni/native-audio-so.cpp b/android/jni/native-audio-so.cpp index 607142f022..27729ab2cd 100644 --- a/android/jni/native-audio-so.cpp +++ b/android/jni/native-audio-so.cpp @@ -157,7 +157,6 @@ OpenSLContext::~OpenSLContext() { bqPlayerObject = nullptr; bqPlayerPlay = nullptr; bqPlayerBufferQueue = nullptr; - bqPlayerMuteSolo = nullptr; bqPlayerVolume = nullptr; } diff --git a/android/jni/native-audio-so.h b/android/jni/native-audio-so.h index a69f879855..286a3338eb 100644 --- a/android/jni/native-audio-so.h +++ b/android/jni/native-audio-so.h @@ -35,7 +35,6 @@ private: SLObjectItf bqPlayerObject = nullptr; SLPlayItf bqPlayerPlay = nullptr; SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = nullptr; - SLMuteSoloItf bqPlayerMuteSolo = nullptr; SLVolumeItf bqPlayerVolume = nullptr; // Double buffering. diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 3bec1191cf..ddae8db8c0 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -703,6 +703,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mSurfaceView.onDestroy(); mSurfaceView = null; } + PowerSaveModeReceiver.destroy(this); // TODO: Can we ensure that the GL thread has stopped rendering here? // I've seen crashes that seem to indicate that sometimes it hasn't... NativeApp.audioShutdown(); @@ -770,6 +771,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mSurfaceView.onResume(); } } + PowerSaveModeReceiver.sendPowerSaving(this); gainAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.resume(); diff --git a/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java b/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java index 69b396dfa8..5ba40291ff 100644 --- a/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java +++ b/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java @@ -5,25 +5,27 @@ import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.PowerManager; import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; public class PowerSaveModeReceiver extends BroadcastReceiver { + private static final String TAG = PowerSaveModeReceiver.class.getSimpleName(); + private static BroadcastReceiver saveModeReceiver = null; private static boolean isPowerSaving = false; private static boolean isBatteryLow = false; - private static final String TAG = "PowerSaveModeReceiver"; - @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); - if (action.equals(Intent.ACTION_BATTERY_LOW)) { + if (Intent.ACTION_BATTERY_LOW.equals(action)) { isBatteryLow = true; - } else if (action.equals(Intent.ACTION_BATTERY_OKAY)) { + } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { isBatteryLow = false; } @@ -31,6 +33,18 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { } public static void initAndSend(final Activity activity) { + saveModeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { + sendPowerSaving(context); + } + } + }; + IntentFilter filter = new IntentFilter(); + filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + activity.registerReceiver(saveModeReceiver, filter); + sendPowerSaving(activity); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { @@ -46,13 +60,20 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { } key = key.substring(key.lastIndexOf("/") + 1, key.length()); if (key.equals("user_powersaver_enable") || key.equals("psm_switch") || key.equals("powersaving_switch")) { - PowerSaveModeReceiver.sendPowerSaving(activity); + sendPowerSaving(activity); } } }); } } + public static void destroy(final Activity activity) { + if (saveModeReceiver != null) { + activity.unregisterReceiver(saveModeReceiver); + saveModeReceiver = null; + } + } + @TargetApi(21) private static boolean getNativePowerSaving(final Context context) { final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); @@ -67,7 +88,7 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { } // Samsung (Touchwiz) String s5Value = Settings.System.getString(context.getContentResolver(), "powersaving_switch"); - boolean hasS5Value = s5Value != null && !s5Value.equals(""); + boolean hasS5Value = !TextUtils.isEmpty(s5Value); // On newer devices, psm_switch is always set, and powersaving_switch is used instead. if ((!hasS5Value && getBooleanSetting(context, "psm_switch")) || getBooleanSetting(context, "powersaving_switch")) { return true; @@ -80,7 +101,7 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { return value != null && value.equals("1"); } - private static void sendPowerSaving(final Context context) { + protected static void sendPowerSaving(final Context context) { if (Build.VERSION.SDK_INT >= 21) { isPowerSaving = getNativePowerSaving(context); } else { From 7811b596b25655d0f6fcc90151491fe230d2117f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Aug 2019 10:09:13 +0200 Subject: [PATCH 11/63] Rename "Immersive Mode" to Full Screen. We can reuse the FullScreen translation from desktop. Fixes #12229 --- UI/GameSettingsScreen.cpp | 5 +++-- assets/lang | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index c94c025bd2..2190928307 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -289,7 +289,7 @@ void GameSettingsScreen::CreateViews() { } #if !defined(MOBILE_DEVICE) - graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); + graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) { CheckBox *fullscreenMulti = new CheckBox(&g_Config.bFullScreenMulti, gr->T("Use all displays")); fullscreenMulti->SetEnabledPtr(&g_Config.bFullScreen); @@ -303,7 +303,8 @@ void GameSettingsScreen::CreateViews() { #ifdef __ANDROID__ // Hide Immersive Mode on pre-kitkat Android if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) { - graphicsSettings->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("Immersive Mode")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange); + // Let's reuse the Fullscreen translation string from desktop. + graphicsSettings->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange); } #endif diff --git a/assets/lang b/assets/lang index b2d7f53fa0..7f098771e0 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit b2d7f53fa056586504590d271ab5f3198bfc2acd +Subproject commit 7f098771e00ce8fca6d116c973c711275deec68c From 5d08f9fcba5fbff7fb256e6493df1974daa70696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Aug 2019 09:23:47 +0200 Subject: [PATCH 12/63] Vulkan loader: Fix the SHIELD Tablet blacklist string. --- Common/Vulkan/VulkanLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index 1337283da9..cb7467e95d 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -231,7 +231,7 @@ bool g_vulkanMayBeAvailable = false; #define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x); static const char *device_name_blacklist[] = { - "SHIELD Tablet", + "NVIDIA:SHIELD Tablet K1", }; static const char *so_names[] = { From 84dece9d96a7990e0d77e04b9bd230bec9c62384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Aug 2019 09:33:56 +0200 Subject: [PATCH 13/63] VulkanMayBeAvailable: Add more logging, disable blacklist check for testing (see #12093) --- Common/Vulkan/VulkanLoader.cpp | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index cb7467e95d..de97cb3986 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -231,7 +231,7 @@ bool g_vulkanMayBeAvailable = false; #define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x); static const char *device_name_blacklist[] = { - "NVIDIA:SHIELD Tablet K1", + "DISABLED:NVIDIA:SHIELD Tablet K1", }; static const char *so_names[] = { @@ -256,12 +256,14 @@ bool VulkanMayBeAvailable() { return false; } } + ILOG("VulkanMayBeAvailable: Device allowed ('%s')", name.c_str()); #ifndef _WIN32 void *lib = nullptr; for (int i = 0; i < ARRAY_SIZE(so_names); i++) { lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL); if (lib) { + ILOG("VulkanMayBeAvailable: Library loaded ('%s')", so_names[i]); break; } } @@ -270,6 +272,7 @@ bool VulkanMayBeAvailable() { HINSTANCE lib = LoadLibrary(L"vulkan-1.dll"); #endif if (!lib) { + ILOG("Vulkan loader: Library not available"); g_vulkanAvailabilityChecked = true; g_vulkanMayBeAvailable = false; return false; @@ -290,12 +293,13 @@ bool VulkanMayBeAvailable() { bool anyGood = false; const char *instanceExtensions[2]{}; VkInstance instance = VK_NULL_HANDLE; - VkResult res; + VkResult res = VK_SUCCESS; uint32_t physicalDeviceCount = 0; - uint32_t instanceExtCount = 1; + uint32_t instanceExtCount = 0; bool surfaceExtensionFound = false; bool platformSurfaceExtensionFound = false; std::vector instanceExts; + ci.enabledExtensionCount = 0; // Should have been reset by struct initialization anyway, just paranoia. instanceExtensions[ci.enabledExtensionCount++] = VK_KHR_SURFACE_EXTENSION_NAME; #ifdef _WIN32 @@ -306,9 +310,12 @@ bool VulkanMayBeAvailable() { const char *platformSurfaceExtension = 0; #endif - if (!localEnumerateInstanceExtensionProperties || !localCreateInstance || !localEnumerate || !localDestroyInstance || !localGetPhysicalDeviceProperties) + if (!localEnumerateInstanceExtensionProperties || !localCreateInstance || !localEnumerate || !localDestroyInstance || !localGetPhysicalDeviceProperties) { + WLOG("VulkanMayBeAvailable: Function pointer missing, bailing"); goto bail; + } + ILOG("VulkanMayBeAvailable: Enumerating instance extensions"); res = localEnumerateInstanceExtensionProperties(nullptr, &instanceExtCount, nullptr); // Maximum paranoia. if (res != VK_SUCCESS) { @@ -319,6 +326,7 @@ bool VulkanMayBeAvailable() { ELOG("No VK instance extensions - won't be able to present."); goto bail; } + ILOG("VulkanMayBeAvailable: Instance extension count: %d", instanceExtCount); instanceExts.resize(instanceExtCount); res = localEnumerateInstanceExtensionProperties(nullptr, &instanceExtCount, instanceExts.data()); if (res != VK_SUCCESS) { @@ -329,6 +337,7 @@ bool VulkanMayBeAvailable() { if (platformSurfaceExtension) { for (auto iter : instanceExts) { if (!strcmp(iter.extensionName, platformSurfaceExtension)) { + ILOG("VulkanMayBeAvailable: Found platform surface extension '%s'", platformSurfaceExtension); instanceExtensions[ci.enabledExtensionCount++] = platformSurfaceExtension; platformSurfaceExtensionFound = true; break; @@ -351,26 +360,27 @@ bool VulkanMayBeAvailable() { info.pEngineName = "VulkanChecker"; ci.pApplicationInfo = &info; ci.flags = 0; + ILOG("VulkanMayBeAvailable: Calling vkCreateInstance"); res = localCreateInstance(&ci, nullptr, &instance); if (res != VK_SUCCESS) { instance = nullptr; ELOG("Failed to create vulkan instance."); goto bail; } - ILOG("Vulkan test instance created successfully."); + ILOG("VulkanMayBeAvailable: Vulkan test instance created successfully."); res = localEnumerate(instance, &physicalDeviceCount, nullptr); if (res != VK_SUCCESS) { - ELOG("Failed to count physical devices."); + ELOG("VulkanMayBeAvailable: Failed to count physical devices."); goto bail; } if (physicalDeviceCount == 0) { - ELOG("No physical Vulkan devices."); + ELOG("VulkanMayBeAvailable: No physical Vulkan devices."); goto bail; } devices.resize(physicalDeviceCount); res = localEnumerate(instance, &physicalDeviceCount, devices.data()); if (res != VK_SUCCESS) { - ELOG("Failed to enumerate physical devices."); + ELOG("VulkanMayBeAvailable: Failed to enumerate physical devices."); goto bail; } anyGood = false; @@ -390,16 +400,17 @@ bool VulkanMayBeAvailable() { } if (!anyGood) { - WLOG("Found Vulkan API, but no good Vulkan device!"); + WLOG("VulkanMayBeAvailable: Found Vulkan API, but no good Vulkan device!"); g_vulkanMayBeAvailable = false; } else { - ILOG("Found working Vulkan API!"); + ILOG("VulkanMayBeAvailable: Found working Vulkan API!"); g_vulkanMayBeAvailable = true; } bail: g_vulkanAvailabilityChecked = true; if (instance) { + ILOG("VulkanMayBeAvailable: Destroying instance"); localDestroyInstance(instance, nullptr); } if (lib) { From 61e20d72cf09ab4f1a1ad481daf44cdff8916fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Aug 2019 09:45:55 +0200 Subject: [PATCH 14/63] Vulkan: On Android, only try to load libvulkan.so, not libvulkan.so.1. Plus more paranoia. --- Common/Vulkan/VulkanLoader.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index de97cb3986..5e9edc8cb1 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -236,7 +236,9 @@ static const char *device_name_blacklist[] = { static const char *so_names[] = { "libvulkan.so", +#if !defined(__ANDROID__) "libvulkan.so.1", +#endif }; void VulkanSetAvailable(bool available) { @@ -430,8 +432,10 @@ bool VulkanLoad() { #ifndef _WIN32 for (int i = 0; i < ARRAY_SIZE(so_names); i++) { vulkanLibrary = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL); - if (vulkanLibrary) + if (vulkanLibrary) { + ILOG("VulkanLoad: Found library '%s'", so_names[i]); break; + } } #else // LoadLibrary etc @@ -449,8 +453,19 @@ bool VulkanLoad() { LOAD_GLOBAL_FUNC(vkEnumerateInstanceExtensionProperties); LOAD_GLOBAL_FUNC(vkEnumerateInstanceLayerProperties); - WLOG("Vulkan base functions loaded."); - return true; + if (vkCreateInstance && vkGetInstanceProcAddr && vkGetDeviceProcAddr && vkEnumerateInstanceExtensionProperties && vkEnumerateInstanceLayerProperties) { + WLOG("VulkanLoad: Base functions loaded."); + return true; + } else { + ELOG("VulkanLoad: Failed to load Vulkan base functions."); +#ifndef _WIN32 + dlclose(vulkanLibrary); +#else + FreeLibrary(vulkanLibrary); +#endif + vulkanLibrary = nullptr; + return false; + } } void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanDeviceExtensions &enabledExtensions) { From c7ae0ed4bf8ad5b6d66ed8e5b03ebb52addfb284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Aug 2019 09:51:36 +0200 Subject: [PATCH 15/63] Vulkan: Re-enable the blacklist. --- Common/Vulkan/VulkanLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index 5e9edc8cb1..afa04a0537 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -231,7 +231,7 @@ bool g_vulkanMayBeAvailable = false; #define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x); static const char *device_name_blacklist[] = { - "DISABLED:NVIDIA:SHIELD Tablet K1", + "NVIDIA:SHIELD Tablet K1", }; static const char *so_names[] = { From 3356f9402932630ead5887b907b5ccbeb17d09cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Aug 2019 11:48:35 +0200 Subject: [PATCH 16/63] More extreme paranoia in VulkanMaybeAvailable --- Common/Vulkan/VulkanLoader.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index afa04a0537..4f4c25feb0 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -293,7 +293,7 @@ bool VulkanMayBeAvailable() { VkApplicationInfo info{ VK_STRUCTURE_TYPE_APPLICATION_INFO }; std::vector devices; bool anyGood = false; - const char *instanceExtensions[2]{}; + const char *instanceExtensions[10]{}; VkInstance instance = VK_NULL_HANDLE; VkResult res = VK_SUCCESS; uint32_t physicalDeviceCount = 0; @@ -302,7 +302,6 @@ bool VulkanMayBeAvailable() { bool platformSurfaceExtensionFound = false; std::vector instanceExts; ci.enabledExtensionCount = 0; // Should have been reset by struct initialization anyway, just paranoia. - instanceExtensions[ci.enabledExtensionCount++] = VK_KHR_SURFACE_EXTENSION_NAME; #ifdef _WIN32 const char * const platformSurfaceExtension = VK_KHR_WIN32_SURFACE_EXTENSION_NAME; @@ -338,12 +337,14 @@ bool VulkanMayBeAvailable() { if (platformSurfaceExtension) { for (auto iter : instanceExts) { + ILOG("VulkanMaybeAvailable: Instance extension found: %s (%08x)", iter.extensionName, iter.specVersion); if (!strcmp(iter.extensionName, platformSurfaceExtension)) { ILOG("VulkanMayBeAvailable: Found platform surface extension '%s'", platformSurfaceExtension); instanceExtensions[ci.enabledExtensionCount++] = platformSurfaceExtension; platformSurfaceExtensionFound = true; break; } else if (!strcmp(iter.extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) { + instanceExtensions[ci.enabledExtensionCount++] = VK_KHR_SURFACE_EXTENSION_NAME; surfaceExtensionFound = true; } } @@ -352,6 +353,11 @@ bool VulkanMayBeAvailable() { goto bail; } } + // This can't happen unless the driver is double-reporting a surface extension. + if (ci.enabledExtensionCount > 2) { + ELOG("Unexpected number of enabled instance extensions"); + goto bail; + } ci.ppEnabledExtensionNames = instanceExtensions; ci.enabledLayerCount = 0; @@ -359,7 +365,7 @@ bool VulkanMayBeAvailable() { info.applicationVersion = 1; info.engineVersion = 1; info.pApplicationName = "VulkanChecker"; - info.pEngineName = "VulkanChecker"; + info.pEngineName = "VulkanCheckerEngine"; ci.pApplicationInfo = &info; ci.flags = 0; ILOG("VulkanMayBeAvailable: Calling vkCreateInstance"); From abdf685ac9df3aaa7664e76354c1163fc4e617f9 Mon Sep 17 00:00:00 2001 From: Silent Date: Thu, 15 Aug 2019 16:25:16 +0200 Subject: [PATCH 17/63] Simplify openIniFile to use ShellExecute so .ini files get opened in an associated text editor This is safe, because by default .ini files are associated with Notepad --- Common/FileUtil.cpp | 34 +++------------------------------- Common/FileUtil.h | 2 +- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index d73b30468a..5eeee2c466 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -763,43 +763,15 @@ void CopyDir(const std::string &source_path, const std::string &dest_path) #endif } -void openIniFile(const std::string fileName) { - std::string iniFile; +void openIniFile(const std::string& fileName) { #if defined(_WIN32) #if PPSSPP_PLATFORM(UWP) // Do nothing. #else - iniFile = fileName; - // Can't rely on a .txt file extension to auto-open in the right editor, - // so let's find notepad - wchar_t notepad_path[MAX_PATH + 1]; - GetSystemDirectory(notepad_path, MAX_PATH); - wcscat(notepad_path, L"\\notepad.exe"); - - wchar_t ini_path[MAX_PATH + 1] = { 0 }; - wcsncpy(ini_path, ConvertUTF8ToWString(iniFile).c_str(), MAX_PATH); - // Flip any slashes... - for (size_t i = 0; i < wcslen(ini_path); i++) { - if (ini_path[i] == '/') - ini_path[i] = '\\'; - } - - // One for the space, one for the null. - wchar_t command_line[MAX_PATH * 2 + 1 + 1]; - wsprintf(command_line, L"%s %s", notepad_path, ini_path); - - STARTUPINFO si{}; - si.cb = sizeof(si); - si.wShowWindow = SW_SHOW; - PROCESS_INFORMATION pi{}; - UINT retval = CreateProcess(0, command_line, 0, 0, 0, 0, 0, 0, &si, &pi); - if (!retval) { - ERROR_LOG(COMMON, "Failed creating notepad process"); - } - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); + ShellExecuteW(nullptr, L"open", ConvertUTF8ToWString(fileName).c_str(), nullptr, nullptr, SW_SHOW); #endif #elif !defined(MOBILE_DEVICE) + std::string iniFile; #if defined(__APPLE__) iniFile = "open "; #else diff --git a/Common/FileUtil.h b/Common/FileUtil.h index 31e0e7a373..e088ceda4c 100644 --- a/Common/FileUtil.h +++ b/Common/FileUtil.h @@ -117,7 +117,7 @@ std::string GetCurrentDir(); void CopyDir(const std::string &source_path, const std::string &dest_path); // Opens ini file (cheats, texture replacements etc.) -void openIniFile(const std::string fileName); +void openIniFile(const std::string& fileName); // Set the current directory to given directory bool SetCurrentDir(const std::string &directory); From 06facfc567c9c5f5e136071715ad4f7b3b061e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 17 Aug 2019 11:52:09 +0200 Subject: [PATCH 18/63] Android: Get our base pointer in a saner way than YOLO, if possible. Should help #12249. --- Common/MemArenaAndroid.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Common/MemArenaAndroid.cpp b/Common/MemArenaAndroid.cpp index 0ff75317cf..31eda9d3d0 100644 --- a/Common/MemArenaAndroid.cpp +++ b/Common/MemArenaAndroid.cpp @@ -19,6 +19,7 @@ #ifdef __ANDROID__ +#include "base/logging.h" #include "MemoryUtil.h" #include "MemArena.h" #include "StringUtils.h" @@ -102,12 +103,28 @@ void MemArena::ReleaseView(void* view, size_t size) { u8* MemArena::Find4GBBase() { #if PPSSPP_ARCH(64BIT) - // Just grab some random 4GB... - return reinterpret_cast(0x2300000000ULL); + // We should probably just go look in /proc/self/maps for some free space. + // But let's try the anonymous mmap trick, just like on 32-bit, but bigger and + // aligned to 4GB for the movk trick. We can ensure that we get an aligned 4GB + // address by grabbing 8GB and aligning the pointer. + const uint64_t EIGHT_GIGS = 0x200000000ULL; + void *base = mmap(0, EIGHT_GIGS, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); + if (base) { + ILOG("base: %p", base); + uint64_t aligned_base = ((uint64_t)base + 0xFFFFFFFF) & ~0xFFFFFFFFULL; + ILOG("aligned_base: %p", (void *)aligned_base); + munmap(base, EIGHT_GIGS); + return reinterpret_cast(aligned_base); + } else { + u8 *hardcoded_ptr = reinterpret_cast(0x2300000000ULL); + ILOG("Failed to anonymously map 8GB. Fall back to the hardcoded pointer %p.", hardcoded_ptr); + // Just grab some random 4GB... + // This has been known to fail lately though, see issue #12249. + return hardcoded_ptr; + } #else // Address masking is used in 32-bit mode, so we can get away with less memory. - void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_SHARED, -1, 0); + void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (base == MAP_FAILED) { PanicAlert("Failed to map 256 MB of memory space: %s", strerror(errno)); return 0; From 3e51fc5a891431e8fb574d17a334cc5348cd7e0b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 10:58:19 -0700 Subject: [PATCH 19/63] GPU: Avoid some unused var/func warnings. --- GPU/Common/TextureScalerCommon.cpp | 7 +++++++ ext/native/thin3d/GLQueueRunner.cpp | 4 +--- ext/native/thin3d/GLRenderManager.cpp | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/GPU/Common/TextureScalerCommon.cpp b/GPU/Common/TextureScalerCommon.cpp index 4299d8a7ff..ca40ba50c9 100644 --- a/GPU/Common/TextureScalerCommon.cpp +++ b/GPU/Common/TextureScalerCommon.cpp @@ -44,6 +44,8 @@ // Report the time and throughput for each larger scaling operation in the log //#define SCALING_MEASURE_TIME +//#define DEBUG_SCALER_OUTPUT + #ifdef SCALING_MEASURE_TIME #include "base/timeutil.h" #endif @@ -453,6 +455,8 @@ void bilinearV(int factor, u32* data, u32* out, int w, int gl, int gu, int l, in #undef B #undef A +#ifdef DEBUG_SCALER_OUTPUT + // used for debugging texture scaling (writing textures to files) static int g_imgCount = 0; void dbgPPM(int w, int h, u8* pixels, const char* prefix = "dbg") { // 3 component RGB @@ -483,6 +487,9 @@ void dbgPGM(int w, int h, u32* pixels, const char* prefix = "dbg") { // 1 compon } fclose(fp); } + +#endif + } /////////////////////////////////////// Texture Scaler diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 5fd98a49f6..90540eb90a 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -107,7 +107,6 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps, bool ski } case GLRInitStepType::TEXTURE_IMAGE: { - GLRTexture *tex = step.texture_image.texture; if (step.texture_image.allocType == GLRAllocType::ALIGNED) { FreeAlignedMemory(step.texture_image.data); } else if (step.texture_image.allocType == GLRAllocType::NEW) { @@ -1317,9 +1316,8 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { } void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { - GLRTexture *tex = pass.readback_image.texture; - #ifndef USING_GLES2 + GLRTexture *tex = pass.readback_image.texture; GLRect2D rect = pass.readback_image.srcRect; if (gl_extensions.VersionGEThan(4, 5)) { diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 8affe78c8f..f3fe9ce65a 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -762,7 +762,7 @@ void GLPushBuffer::Defragment() { size_ = newSize; bool res = AddBuffer(); - _dbg_assert_msg_(G3D, res, "AddBuffer failed"); + _assert_msg_(G3D, res, "AddBuffer failed"); } size_t GLPushBuffer::GetTotalSize() const { From 9f98f440c825b6b311c746f62e3ff383495a3f1e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 10:59:08 -0700 Subject: [PATCH 20/63] Vulkan: Check default case with an assert. --- Common/Vulkan/VulkanContext.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 10c4c4de32..297bc72d2f 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -1014,6 +1014,8 @@ bool VulkanContext::InitSwapchain() { g_display_rot_matrix.setRotationZ270(); std::swap(swapChainExtent_.width, swapChainExtent_.height); break; + default: + assert(false); } } else { // Let the OS rotate the image (potentially slow on many Android devices) From 8d3a4fae8e3ef1fa7e75ea01110111746d96bfb1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 11:59:25 -0700 Subject: [PATCH 21/63] iOS: Ignore gl3stubInit() on iOS, to avoid warnings. It always returns true anyway. --- ext/native/gfx_es2/gl3stub.c | 14 ++++++++------ ext/native/gfx_es2/gl3stub.h | 4 +++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ext/native/gfx_es2/gl3stub.c b/ext/native/gfx_es2/gl3stub.c index 2a846a32d9..265004a9c3 100644 --- a/ext/native/gfx_es2/gl3stub.c +++ b/ext/native/gfx_es2/gl3stub.c @@ -14,15 +14,14 @@ * limitations under the License. */ +#include "ppsspp_config.h" #include "../gfx/gl_common.h" #if defined(USING_GLES2) -#if !defined(IOS) +#if !PPSSPP_PLATFORM(IOS) #include "EGL/egl.h" -#endif GLboolean gl3stubInit() { -#if !defined(IOS) #define FIND_PROC(s) s = (void*)eglGetProcAddress(#s) FIND_PROC(glReadBuffer); FIND_PROC(glDrawRangeElements); @@ -145,7 +144,6 @@ GLboolean gl3stubInit() { #undef FIND_PROC -#endif // IOS if (!glReadBuffer || !glDrawRangeElements || !glTexImage3D || @@ -257,8 +255,6 @@ GLboolean gl3stubInit() { return GL_TRUE; } -#if !defined(IOS) - /* Function pointer definitions */ GL_APICALL void (* GL_APIENTRY glReadBuffer) (GLenum mode); GL_APICALL void (* GL_APIENTRY glDrawRangeElements) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid* indices); @@ -379,6 +375,12 @@ GL_APICALL void (* GL_APIENTRY glBufferStorageEXT) (GLenum target, GLs /* OES_copy_image, etc. */ GL_APICALL void (* GL_APIENTRY glCopyImageSubDataOES) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#else + +GLboolean gl3stubInit() { + return GL_TRUE; +} + #endif // IOS #endif // GLES2 diff --git a/ext/native/gfx_es2/gl3stub.h b/ext/native/gfx_es2/gl3stub.h index 843b1fb667..26b571b982 100644 --- a/ext/native/gfx_es2/gl3stub.h +++ b/ext/native/gfx_es2/gl3stub.h @@ -11,6 +11,8 @@ * - Added gl3stubInit() declaration */ +#include "ppsspp_config.h" + #if defined(USING_GLES2) #ifdef __cplusplus @@ -45,7 +47,7 @@ extern "C" { * otherwise. */ GLboolean gl3stubInit(); -#if !defined(IOS) +#if !PPSSPP_PLATFORM(IOS) /*------------------------------------------------------------------------- * Data type definitions From 66687c2c82311b13c41f9c1531cf04d954070ea7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 13:31:16 -0700 Subject: [PATCH 22/63] Core: Fix a format type error. --- Core/MIPS/ARM/ArmJit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 0e091ff792..5820509c78 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -111,7 +111,7 @@ ArmJit::ArmJit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), fpr(m AllocCodeSpace(1024 * 1024 * 16); // 32MB is the absolute max because that's what an ARM branch instruction can reach, backwards and forwards. GenerateFixedCode(); - INFO_LOG(JIT, "ARM JIT initialized: %d MB of code space", GetSpaceLeft() / (1024 * 1024)); + INFO_LOG(JIT, "ARM JIT initialized: %lld MB of code space", (long long)(GetSpaceLeft() / (1024 * 1024))); js.startDefaultPrefix = mips_->HasDefaultPrefix(); From fdc28b16fbb375de087a5a9fc780096f16296ae6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 13:31:52 -0700 Subject: [PATCH 23/63] UI: Respect loop info for menu music. A good example for this is Wipeout Pure, which sounds much better when it loops using the sample looping data. --- UI/BackgroundAudio.cpp | 102 ++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/UI/BackgroundAudio.cpp b/UI/BackgroundAudio.cpp index c87237c0ac..53c5b9103f 100644 --- a/UI/BackgroundAudio.cpp +++ b/UI/BackgroundAudio.cpp @@ -17,13 +17,7 @@ class AT3PlusReader { public: AT3PlusReader(const std::string &data) - : file_((const uint8_t *)&data[0], (int32_t)data.size()), - raw_data_(0), - raw_data_size_(0), - raw_offset_(0), - buffer_(0), - decoder_(0) { - + : file_((const uint8_t *)&data[0], (int32_t)data.size()) { // Normally 8k but let's be safe. buffer_ = new short[32 * 1024]; @@ -78,18 +72,64 @@ public: return; } + // If we have no loop info, we'll just loop the entire audio. + raw_offset_loop_start_ = 0; + raw_offset_loop_end_ = 0; + skip_next_samples_ = 0; + + if (file_.Descend('smpl')) { + std::vector smplData; + smplData.resize(file_.GetCurrentChunkSize()); + file_.ReadData(&smplData[0], (int)smplData.size()); + + int numLoops = *(int *)&smplData[28]; + struct AtracLoopInfo { + int cuePointID; + int type; + int startSample; + int endSample; + int fraction; + int playCount; + }; + + if (numLoops > 0 && smplData.size() >= 36 + sizeof(AtracLoopInfo) * numLoops) { + AtracLoopInfo *loops = (AtracLoopInfo *)&smplData[36]; + int samplesPerFrame = codec == PSP_CODEC_AT3PLUS ? 2048 : 1024; + + for (int i = 0; i < numLoops; ++i) { + // Only seen forward loops, so let's ignore others. + if (loops[i].type != 0) + continue; + + // We ignore loop interpolation (fraction) and play count for now. + raw_offset_loop_start_ = (loops[i].startSample / samplesPerFrame) * raw_bytes_per_frame_; + loop_start_offset_ = loops[i].startSample % samplesPerFrame; + raw_offset_loop_end_ = (loops[i].endSample / samplesPerFrame) * raw_bytes_per_frame_; + loop_end_offset_ = loops[i].endSample % samplesPerFrame; + + if (loops[i].playCount == 0) { + // This was an infinite loop, so ignore the rest. + // In practice, there's usually only one and it's usually infinite. + break; + } + } + } + + file_.Ascend(); + } + if (file_.Descend('data')) { //enter the data chunk int numBytes = file_.GetCurrentChunkSize(); numFrames = numBytes / raw_bytes_per_frame_; // numFrames raw_data_ = (uint8_t *)malloc(numBytes); raw_data_size_ = numBytes; - if (/*raw_bytes_per_frame_ == 280 && */ num_channels == 1 || num_channels == 2) { + if (num_channels == 1 || num_channels == 2) { file_.ReadData(raw_data_, numBytes); } else { ELOG("Error - bad blockalign or channels"); free(raw_data_); - raw_data_ = 0; + raw_data_ = nullptr; return; } file_.Ascend(); @@ -116,30 +156,45 @@ public: void Shutdown() { free(raw_data_); - raw_data_ = 0; + raw_data_ = nullptr; delete[] buffer_; - buffer_ = 0; + buffer_ = nullptr; delete decoder_; - decoder_ = 0; + decoder_ = nullptr; } - bool IsOK() { return raw_data_ != 0; } + bool IsOK() { return raw_data_ != nullptr; } bool Read(int *buffer, int len) { if (!raw_data_) return false; while (bgQueue.size() < (size_t)(len * 2)) { - int outBytes; + int outBytes = 0; decoder_->Decode(raw_data_ + raw_offset_, raw_bytes_per_frame_, (uint8_t *)buffer_, &outBytes); if (!outBytes) return false; - for (int i = 0; i < outBytes / 2; i++) { + if (raw_offset_loop_end_ != 0 && raw_offset_ == raw_offset_loop_end_) { + // Only take the remaining bytes, but convert to stereo s16. + outBytes = std::min(outBytes, loop_end_offset_ * 4); + } + + int start = skip_next_samples_; + skip_next_samples_ = 0; + + for (int i = start; i < outBytes / 2; i++) { bgQueue.push(buffer_[i]); } - // loop! + if (raw_offset_loop_end_ != 0 && raw_offset_ == raw_offset_loop_end_) { + // Time to loop. Account for the addition below. + raw_offset_ = raw_offset_loop_start_ - raw_bytes_per_frame_; + // This time we're counting each stereo sample. + skip_next_samples_ = loop_start_offset_ * 2; + } + + // Handle loops when there's no loop info. raw_offset_ += raw_bytes_per_frame_; if (raw_offset_ >= raw_data_size_) { raw_offset_ = 0; @@ -154,13 +209,18 @@ public: private: RIFFReader file_; - uint8_t *raw_data_; - int raw_data_size_; - int raw_offset_; + uint8_t *raw_data_ = nullptr; + int raw_data_size_ = 0; + int raw_offset_ = 0; int raw_bytes_per_frame_; + int raw_offset_loop_start_ = 0; + int raw_offset_loop_end_ = 0; + int loop_start_offset_ = 0; + int loop_end_offset_ = 0; + int skip_next_samples_ = 0; FixedSizeQueue bgQueue; - short *buffer_; - SimpleAudio *decoder_; + short *buffer_ = nullptr; + SimpleAudio *decoder_ = nullptr; }; static std::mutex bgMutex; From 8e59343c4094b12ab86104d38bb634ac2a5e64b4 Mon Sep 17 00:00:00 2001 From: Bigpet Date: Sat, 17 Aug 2019 16:42:19 -0700 Subject: [PATCH 24/63] UI: Make IP/hostname dialog more generic. --- UI/GameSettingsScreen.cpp | 72 +++++++++++++++++++-------------------- UI/GameSettingsScreen.h | 7 ++-- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 2190928307..476c9fdaa2 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1216,11 +1216,11 @@ UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParam } } else - screenManager()->push(new ProAdhocServerScreen); + screenManager()->push(new HostnameSelectScreen(g_Config.proAdhocServer)); #elif defined(__ANDROID__) System_SendMessage("inputbox", ("IP:" + g_Config.proAdhocServer).c_str()); #else - screenManager()->push(new ProAdhocServerScreen); + screenManager()->push(new HostnameSelectScreen(g_Config.proAdhocServer)); #endif return UI::EVENT_DONE; @@ -1482,12 +1482,12 @@ void DeveloperToolsScreen::update() { canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER); } -void ProAdhocServerScreen::CreateViews() { +void HostnameSelectScreen::CreateViews() { using namespace UI; I18NCategory *sy = GetI18NCategory("System"); I18NCategory *di = GetI18NCategory("Dialog"); - tempProAdhocServer = g_Config.proAdhocServer; + tempProAdhocServer = targetConfig; root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); @@ -1495,115 +1495,115 @@ void ProAdhocServerScreen::CreateViews() { addrView_ = new TextView(tempProAdhocServer, ALIGN_LEFT, false); leftColumn->Add(addrView_); LinearLayout *rightColumn = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(0, 120, 10, NONE, NONE,10)); - rightColumn->Add(new Button("0"))->OnClick.Handle(this, &ProAdhocServerScreen::On0Click); - rightColumn->Add(new Button("1"))->OnClick.Handle(this, &ProAdhocServerScreen::On1Click); - rightColumn->Add(new Button("2"))->OnClick.Handle(this, &ProAdhocServerScreen::On2Click); - rightColumn->Add(new Button("3"))->OnClick.Handle(this, &ProAdhocServerScreen::On3Click); - rightColumn->Add(new Button("4"))->OnClick.Handle(this, &ProAdhocServerScreen::On4Click); - rightColumn->Add(new Button("5"))->OnClick.Handle(this, &ProAdhocServerScreen::On5Click); - rightColumn->Add(new Button("6"))->OnClick.Handle(this, &ProAdhocServerScreen::On6Click); - rightColumn->Add(new Button("7"))->OnClick.Handle(this, &ProAdhocServerScreen::On7Click); - rightColumn->Add(new Button("8"))->OnClick.Handle(this, &ProAdhocServerScreen::On8Click); - rightColumn->Add(new Button("9"))->OnClick.Handle(this, &ProAdhocServerScreen::On9Click); - rightColumn->Add(new Button("."))->OnClick.Handle(this, &ProAdhocServerScreen::OnPointClick); - rightColumn->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &ProAdhocServerScreen::OnDeleteClick); - rightColumn->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &ProAdhocServerScreen::OnDeleteAllClick); - rightColumn->Add(new Button(di->T("OK")))->OnClick.Handle(this, &ProAdhocServerScreen::OnOKClick); - rightColumn->Add(new Button(di->T("Cancel")))->OnClick.Handle(this, &ProAdhocServerScreen::OnCancelClick); + rightColumn->Add(new Button("0"))->OnClick.Handle(this, &HostnameSelectScreen::On0Click); + rightColumn->Add(new Button("1"))->OnClick.Handle(this, &HostnameSelectScreen::On1Click); + rightColumn->Add(new Button("2"))->OnClick.Handle(this, &HostnameSelectScreen::On2Click); + rightColumn->Add(new Button("3"))->OnClick.Handle(this, &HostnameSelectScreen::On3Click); + rightColumn->Add(new Button("4"))->OnClick.Handle(this, &HostnameSelectScreen::On4Click); + rightColumn->Add(new Button("5"))->OnClick.Handle(this, &HostnameSelectScreen::On5Click); + rightColumn->Add(new Button("6"))->OnClick.Handle(this, &HostnameSelectScreen::On6Click); + rightColumn->Add(new Button("7"))->OnClick.Handle(this, &HostnameSelectScreen::On7Click); + rightColumn->Add(new Button("8"))->OnClick.Handle(this, &HostnameSelectScreen::On8Click); + rightColumn->Add(new Button("9"))->OnClick.Handle(this, &HostnameSelectScreen::On9Click); + rightColumn->Add(new Button("."))->OnClick.Handle(this, &HostnameSelectScreen::OnPointClick); + rightColumn->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick); + rightColumn->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick); + rightColumn->Add(new Button(di->T("OK")))->OnClick.Handle(this, &HostnameSelectScreen::OnOKClick); + rightColumn->Add(new Button(di->T("Cancel")))->OnClick.Handle(this, &HostnameSelectScreen::OnCancelClick); root_->Add(leftColumn); root_->Add(rightColumn); } -UI::EventReturn ProAdhocServerScreen::On0Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On0Click(UI::EventParams &e) { if (tempProAdhocServer.length() > 0) tempProAdhocServer.append("0"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On1Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On1Click(UI::EventParams &e) { tempProAdhocServer.append("1"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On2Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On2Click(UI::EventParams &e) { tempProAdhocServer.append("2"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On3Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On3Click(UI::EventParams &e) { tempProAdhocServer.append("3"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On4Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On4Click(UI::EventParams &e) { tempProAdhocServer.append("4"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On5Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On5Click(UI::EventParams &e) { tempProAdhocServer.append("5"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On6Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On6Click(UI::EventParams &e) { tempProAdhocServer.append("6"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On7Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On7Click(UI::EventParams &e) { tempProAdhocServer.append("7"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On8Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On8Click(UI::EventParams &e) { tempProAdhocServer.append("8"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::On9Click(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::On9Click(UI::EventParams &e) { tempProAdhocServer.append("9"); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::OnPointClick(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::OnPointClick(UI::EventParams &e) { if (tempProAdhocServer.length() > 0 && tempProAdhocServer.at(tempProAdhocServer.length() - 1) != '.') tempProAdhocServer.append("."); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::OnDeleteClick(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::OnDeleteClick(UI::EventParams &e) { if (tempProAdhocServer.length() > 0) tempProAdhocServer.erase(tempProAdhocServer.length() -1, 1); addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::OnDeleteAllClick(UI::EventParams &e) { +UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) { tempProAdhocServer = ""; addrView_->SetText(tempProAdhocServer); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::OnOKClick(UI::EventParams &e) { - g_Config.proAdhocServer = StripSpaces(tempProAdhocServer); +UI::EventReturn HostnameSelectScreen::OnOKClick(UI::EventParams &e) { + targetConfig = tempProAdhocServer; UIScreen::OnBack(e); return UI::EVENT_DONE; } -UI::EventReturn ProAdhocServerScreen::OnCancelClick(UI::EventParams &e) { - tempProAdhocServer = g_Config.proAdhocServer; +UI::EventReturn HostnameSelectScreen::OnCancelClick(UI::EventParams &e) { + tempProAdhocServer = targetConfig; UIScreen::OnBack(e); return UI::EVENT_DONE; } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index a72ea24e1e..732e40441a 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -175,14 +175,15 @@ private: bool canAllowDebugger_ = true; }; -class ProAdhocServerScreen : public UIDialogScreenWithBackground { +class HostnameSelectScreen : public UIDialogScreenWithBackground { public: - ProAdhocServerScreen() {} + HostnameSelectScreen(std::string &target) : targetConfig(target) {} protected: void CreateViews() override; -private: +private: + std::string &targetConfig; std::string tempProAdhocServer; UI::TextView *addrView_; UI::EventReturn On0Click(UI::EventParams &e); From f52afb48ac94dcfe1a15d5fca4be079ec0c65486 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 17:14:31 -0700 Subject: [PATCH 25/63] UI: Cleanup hostname entry to use a dialog. --- UI/GameSettingsScreen.cpp | 149 +++++++++++--------------------------- UI/GameSettingsScreen.h | 30 +++----- ext/native/ui/ui_screen.h | 2 +- 3 files changed, 56 insertions(+), 125 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 476c9fdaa2..18e2b70bb2 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1203,6 +1203,8 @@ UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) { + I18NCategory *sy = GetI18NCategory("System"); + #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) if (!g_Config.bFullScreen) { const size_t name_len = 256; @@ -1214,13 +1216,13 @@ UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParam std::string stripped = StripSpaces(name); g_Config.proAdhocServer = stripped; } + } else { + screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, sy->T("proAdhocServer Address:"))); } - else - screenManager()->push(new HostnameSelectScreen(g_Config.proAdhocServer)); #elif defined(__ANDROID__) System_SendMessage("inputbox", ("IP:" + g_Config.proAdhocServer).c_str()); #else - screenManager()->push(new HostnameSelectScreen(g_Config.proAdhocServer)); + screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, sy->T("proAdhocServer Address:"))); #endif return UI::EVENT_DONE; @@ -1482,130 +1484,66 @@ void DeveloperToolsScreen::update() { canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER); } -void HostnameSelectScreen::CreateViews() { +void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; I18NCategory *sy = GetI18NCategory("System"); I18NCategory *di = GetI18NCategory("Dialog"); - tempProAdhocServer = targetConfig; - root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); - LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); + currentValue_ = *value_; + LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10))); - leftColumn->Add(new ItemHeader(sy->T("proAdhocServer Address:"))); - addrView_ = new TextView(tempProAdhocServer, ALIGN_LEFT, false); - leftColumn->Add(addrView_); - LinearLayout *rightColumn = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(0, 120, 10, NONE, NONE,10)); - rightColumn->Add(new Button("0"))->OnClick.Handle(this, &HostnameSelectScreen::On0Click); - rightColumn->Add(new Button("1"))->OnClick.Handle(this, &HostnameSelectScreen::On1Click); - rightColumn->Add(new Button("2"))->OnClick.Handle(this, &HostnameSelectScreen::On2Click); - rightColumn->Add(new Button("3"))->OnClick.Handle(this, &HostnameSelectScreen::On3Click); - rightColumn->Add(new Button("4"))->OnClick.Handle(this, &HostnameSelectScreen::On4Click); - rightColumn->Add(new Button("5"))->OnClick.Handle(this, &HostnameSelectScreen::On5Click); - rightColumn->Add(new Button("6"))->OnClick.Handle(this, &HostnameSelectScreen::On6Click); - rightColumn->Add(new Button("7"))->OnClick.Handle(this, &HostnameSelectScreen::On7Click); - rightColumn->Add(new Button("8"))->OnClick.Handle(this, &HostnameSelectScreen::On8Click); - rightColumn->Add(new Button("9"))->OnClick.Handle(this, &HostnameSelectScreen::On9Click); - rightColumn->Add(new Button("."))->OnClick.Handle(this, &HostnameSelectScreen::OnPointClick); - rightColumn->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick); - rightColumn->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick); - rightColumn->Add(new Button(di->T("OK")))->OnClick.Handle(this, &HostnameSelectScreen::OnOKClick); - rightColumn->Add(new Button(di->T("Cancel")))->OnClick.Handle(this, &HostnameSelectScreen::OnCancelClick); - root_->Add(leftColumn); - root_->Add(rightColumn); + addrView_ = new TextView(currentValue_, ALIGN_LEFT, false); + valueRow->Add(addrView_); + parent->Add(valueRow); + + LinearLayout *buttonsRow1 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + LinearLayout *buttonsRow2 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + parent->Add(buttonsRow1); + parent->Add(buttonsRow2); + + for (char c = '0'; c <= '9'; ++c) { + char label[] = { c, '\0' }; + auto button = buttonsRow1->Add(new Button(label)); + button->OnClick.Handle(this, &HostnameSelectScreen::OnNumberClick); + button->SetTag(label); + } + buttonsRow1->Add(new Button("."))->OnClick.Handle(this, &HostnameSelectScreen::OnPointClick); + + buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick); + buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick); } -UI::EventReturn HostnameSelectScreen::On0Click(UI::EventParams &e) { - if (tempProAdhocServer.length() > 0) - tempProAdhocServer.append("0"); - addrView_->SetText(tempProAdhocServer); +UI::EventReturn HostnameSelectScreen::OnNumberClick(UI::EventParams &e) { + std::string text = e.v ? e.v->Tag() : ""; + if (currentValue_.length() > 0 || text != "0") + currentValue_.append(text); + addrView_->SetText(currentValue_); return UI::EVENT_DONE; } -UI::EventReturn HostnameSelectScreen::On1Click(UI::EventParams &e) { - tempProAdhocServer.append("1"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On2Click(UI::EventParams &e) { - tempProAdhocServer.append("2"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On3Click(UI::EventParams &e) { - tempProAdhocServer.append("3"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On4Click(UI::EventParams &e) { - tempProAdhocServer.append("4"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On5Click(UI::EventParams &e) { - tempProAdhocServer.append("5"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On6Click(UI::EventParams &e) { - tempProAdhocServer.append("6"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On7Click(UI::EventParams &e) { - tempProAdhocServer.append("7"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On8Click(UI::EventParams &e) { - tempProAdhocServer.append("8"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::On9Click(UI::EventParams &e) { - tempProAdhocServer.append("9"); - addrView_->SetText(tempProAdhocServer); - return UI::EVENT_DONE; -} - - UI::EventReturn HostnameSelectScreen::OnPointClick(UI::EventParams &e) { - if (tempProAdhocServer.length() > 0 && tempProAdhocServer.at(tempProAdhocServer.length() - 1) != '.') - tempProAdhocServer.append("."); - addrView_->SetText(tempProAdhocServer); + if (currentValue_.length() > 0 && currentValue_.at(currentValue_.length() - 1) != '.') + currentValue_.append("."); + addrView_->SetText(currentValue_); return UI::EVENT_DONE; } UI::EventReturn HostnameSelectScreen::OnDeleteClick(UI::EventParams &e) { - if (tempProAdhocServer.length() > 0) - tempProAdhocServer.erase(tempProAdhocServer.length() -1, 1); - addrView_->SetText(tempProAdhocServer); + if (currentValue_.length() > 0) + currentValue_.erase(currentValue_.length() - 1, 1); + addrView_->SetText(currentValue_); return UI::EVENT_DONE; } UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) { - tempProAdhocServer = ""; - addrView_->SetText(tempProAdhocServer); + currentValue_ = ""; + addrView_->SetText(currentValue_); return UI::EVENT_DONE; } -UI::EventReturn HostnameSelectScreen::OnOKClick(UI::EventParams &e) { - targetConfig = tempProAdhocServer; - UIScreen::OnBack(e); - return UI::EVENT_DONE; -} - -UI::EventReturn HostnameSelectScreen::OnCancelClick(UI::EventParams &e) { - tempProAdhocServer = targetConfig; - UIScreen::OnBack(e); - return UI::EVENT_DONE; +void HostnameSelectScreen::OnCompleted(DialogResult result) { + if (result == DR_OK) + *value_ = currentValue_; } SettingInfoMessage::SettingInfoMessage(int align, UI::AnchorLayoutParams *lp) @@ -1614,7 +1552,6 @@ SettingInfoMessage::SettingInfoMessage(int align, UI::AnchorLayoutParams *lp) SetSpacing(0.0f); Add(new UI::Spacer(10.0f)); text_ = Add(new UI::TextView("", align, false, new LinearLayoutParams(1.0, Margins(0, 10)))); - text_->SetTag("TEST?"); Add(new UI::Spacer(10.0f)); } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 732e40441a..8c94329066 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -175,30 +175,24 @@ private: bool canAllowDebugger_ = true; }; -class HostnameSelectScreen : public UIDialogScreenWithBackground { +class HostnameSelectScreen : public PopupScreen { public: - HostnameSelectScreen(std::string &target) : targetConfig(target) {} + HostnameSelectScreen(std::string *value, const std::string &title) + : PopupScreen(title, "OK", "Cancel"), value_(value) { + } + + void CreatePopupContents(UI::ViewGroup *parent) override; protected: - void CreateViews() override; + void OnCompleted(DialogResult result) override; private: - std::string &targetConfig; - std::string tempProAdhocServer; - UI::TextView *addrView_; - UI::EventReturn On0Click(UI::EventParams &e); - UI::EventReturn On1Click(UI::EventParams &e); - UI::EventReturn On2Click(UI::EventParams &e); - UI::EventReturn On3Click(UI::EventParams &e); - UI::EventReturn On4Click(UI::EventParams &e); - UI::EventReturn On5Click(UI::EventParams &e); - UI::EventReturn On6Click(UI::EventParams &e); - UI::EventReturn On7Click(UI::EventParams &e); - UI::EventReturn On8Click(UI::EventParams &e); - UI::EventReturn On9Click(UI::EventParams &e); + UI::EventReturn OnNumberClick(UI::EventParams &e); UI::EventReturn OnPointClick(UI::EventParams &e); UI::EventReturn OnDeleteClick(UI::EventParams &e); UI::EventReturn OnDeleteAllClick(UI::EventParams &e); - UI::EventReturn OnOKClick(UI::EventParams &e); - UI::EventReturn OnCancelClick(UI::EventParams &e); + + std::string *value_; + std::string currentValue_; + UI::TextView *addrView_; }; diff --git a/ext/native/ui/ui_screen.h b/ext/native/ui/ui_screen.h index c044e961a0..1d2dfa4399 100644 --- a/ext/native/ui/ui_screen.h +++ b/ext/native/ui/ui_screen.h @@ -225,7 +225,7 @@ private: class TextEditPopupScreen : public PopupScreen { public: - TextEditPopupScreen(std::string *value, std::string &placeholder, const std::string &title, int maxLen) + TextEditPopupScreen(std::string *value, const std::string &placeholder, const std::string &title, int maxLen) : PopupScreen(title, "OK", "Cancel"), value_(value), placeholder_(placeholder), maxLen_(maxLen) {} virtual void CreatePopupContents(ViewGroup *parent) override; From 3bc2cad31b6e6631790cc210042296d1c5d18399 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 17:38:29 -0700 Subject: [PATCH 26/63] IO: Use TextEdit for hostnames on more platforms. --- UI/GameSettingsScreen.cpp | 57 +++++++++++++++------------------------ UI/GameSettingsScreen.h | 4 +-- 2 files changed, 24 insertions(+), 37 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 18e2b70bb2..399b666a8e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -625,13 +625,7 @@ void GameSettingsScreen::CreateViews() { networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)"))); networkingSettings->Add(new CheckBox(&g_Config.bDiscordPresence, n->T("Send Discord Presence information"))); -#if !defined(MOBILE_DEVICE) - networkingSettings->Add(new PopupTextInputChoice(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), "", 255, screenManager())); -#elif defined(__ANDROID__) networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeproAdhocServerAddress); -#else - networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeproAdhocServerAddress); -#endif networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in PRO Adhoc Server", "Enable built-in PRO Adhoc Server"))); networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sMACAddress, n->T("Change Mac Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeMacAddress); networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, n->T("Port offset", "Port offset(0 = PSP compatibility)"), 100, screenManager())); @@ -1205,21 +1199,7 @@ UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) { I18NCategory *sy = GetI18NCategory("System"); -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) - if (!g_Config.bFullScreen) { - const size_t name_len = 256; - - char name[name_len]; - memset(name, 0, sizeof(name)); - - if (System_InputBoxGetString("Enter an IP address", g_Config.proAdhocServer.c_str(), name, name_len)) { - std::string stripped = StripSpaces(name); - g_Config.proAdhocServer = stripped; - } - } else { - screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, sy->T("proAdhocServer Address:"))); - } -#elif defined(__ANDROID__) +#if defined(__ANDROID__) System_SendMessage("inputbox", ("IP:" + g_Config.proAdhocServer).c_str()); #else screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, sy->T("proAdhocServer Address:"))); @@ -1489,10 +1469,10 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { I18NCategory *sy = GetI18NCategory("System"); I18NCategory *di = GetI18NCategory("Dialog"); - currentValue_ = *value_; LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10))); - addrView_ = new TextView(currentValue_, ALIGN_LEFT, false); + addrView_ = new TextEdit(*value_, ""); + addrView_->SetTextAlign(FLAG_DYNAMIC_ASCII); valueRow->Add(addrView_); parent->Add(valueRow); @@ -1501,6 +1481,7 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { parent->Add(buttonsRow1); parent->Add(buttonsRow2); + buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, G_LEFT))); for (char c = '0'; c <= '9'; ++c) { char label[] = { c, '\0' }; auto button = buttonsRow1->Add(new Button(label)); @@ -1508,42 +1489,48 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { button->SetTag(label); } buttonsRow1->Add(new Button("."))->OnClick.Handle(this, &HostnameSelectScreen::OnPointClick); + buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT))); + buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_LEFT))); buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick); buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick); + buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT))); +} + +void HostnameSelectScreen::SendEditKey(int keyCode, int flags) { + auto oldView = UI::GetFocusedView(); + UI::SetFocusedView(addrView_); + KeyInput fakeKey{ DEVICE_ID_KEYBOARD, keyCode, KEY_DOWN | flags }; + addrView_->Key(fakeKey); + UI::SetFocusedView(oldView); } UI::EventReturn HostnameSelectScreen::OnNumberClick(UI::EventParams &e) { std::string text = e.v ? e.v->Tag() : ""; - if (currentValue_.length() > 0 || text != "0") - currentValue_.append(text); - addrView_->SetText(currentValue_); + if (text.length() == 1 && text[0] >= '0' && text[0] <= '9') { + SendEditKey(text[0], KEY_CHAR); + } return UI::EVENT_DONE; } UI::EventReturn HostnameSelectScreen::OnPointClick(UI::EventParams &e) { - if (currentValue_.length() > 0 && currentValue_.at(currentValue_.length() - 1) != '.') - currentValue_.append("."); - addrView_->SetText(currentValue_); + SendEditKey('.', KEY_CHAR); return UI::EVENT_DONE; } UI::EventReturn HostnameSelectScreen::OnDeleteClick(UI::EventParams &e) { - if (currentValue_.length() > 0) - currentValue_.erase(currentValue_.length() - 1, 1); - addrView_->SetText(currentValue_); + SendEditKey(NKCODE_DEL); return UI::EVENT_DONE; } UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) { - currentValue_ = ""; - addrView_->SetText(currentValue_); + addrView_->SetText(""); return UI::EVENT_DONE; } void HostnameSelectScreen::OnCompleted(DialogResult result) { if (result == DR_OK) - *value_ = currentValue_; + *value_ = addrView_->GetText(); } SettingInfoMessage::SettingInfoMessage(int align, UI::AnchorLayoutParams *lp) diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 8c94329066..a228cda4dc 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -187,12 +187,12 @@ protected: void OnCompleted(DialogResult result) override; private: + void SendEditKey(int keyCode, int flags = 0); UI::EventReturn OnNumberClick(UI::EventParams &e); UI::EventReturn OnPointClick(UI::EventParams &e); UI::EventReturn OnDeleteClick(UI::EventParams &e); UI::EventReturn OnDeleteAllClick(UI::EventParams &e); std::string *value_; - std::string currentValue_; - UI::TextView *addrView_; + UI::TextEdit *addrView_; }; From 5955861865b0e024baa1f6468f16b4367b13e45e Mon Sep 17 00:00:00 2001 From: KentuckyCompass Date: Sat, 17 Aug 2019 21:15:29 -0700 Subject: [PATCH 27/63] configure QT to get a higher version for opengl --- Qt/QtMain.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index cdef67f0a8..3c9bdb102d 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -439,6 +439,8 @@ void MainUI::initializeGL() { g_Config.iGPUBackend = (int)GPUBackend::OPENGL; } + SetGLCoreContext(format().profile() == QGLFormat::CoreProfile); + #ifndef USING_GLES2 // Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc. // glewExperimental allows us to force GLEW to search for the pointers anyway. @@ -569,6 +571,14 @@ int main(int argc, char *argv[]) #if defined(Q_OS_LINUX) QApplication::setAttribute(Qt::AA_X11InitThreads, true); #endif + + // Qt would otherwise default to a 3.0 compatibility profile + // except on Nvidia, where Nvidia gives us the highest supported anyway + QGLFormat format; + format.setVersion(4, 6); + format.setProfile(QGLFormat::CoreProfile); + QGLFormat::setDefaultFormat(format); + QApplication a(argc, argv); QSize res = QApplication::desktop()->screenGeometry().size(); if (res.width() < res.height()) From 30dcc2a13a94eaf287abeb0dcd0f606e074e0370 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Aug 2019 18:24:57 -0700 Subject: [PATCH 28/63] UI: Validate address entered in settings. --- UI/GameSettingsScreen.cpp | 84 +++++++++++++++++++++++++++++++++++++ UI/GameSettingsScreen.h | 34 ++++++++++++++- ext/native/ui/ui_screen.cpp | 10 +++-- ext/native/ui/ui_screen.h | 1 + 4 files changed, 125 insertions(+), 4 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 399b666a8e..b1bf46f218 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -22,6 +22,7 @@ #include "base/colorutil.h" #include "base/timeutil.h" #include "math/curves.h" +#include "net/resolve.h" #include "gfx_es2/gpu_features.h" #include "gfx_es2/draw_buffer.h" #include "i18n/i18n.h" @@ -1468,6 +1469,7 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; I18NCategory *sy = GetI18NCategory("System"); I18NCategory *di = GetI18NCategory("Dialog"); + I18NCategory *n = GetI18NCategory("Networking"); LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10))); @@ -1495,6 +1497,13 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick); buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick); buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT))); + + errorView_ = parent->Add(new TextView(n->T("Invalid IP or hostname"), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 10, 0, 0)))); + errorView_->SetTextColor(0xFF3030FF); + errorView_->SetVisibility(V_GONE); + + progressView_ = parent->Add(new TextView(n->T("Validating address..."), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 10, 0, 0)))); + progressView_->SetVisibility(V_GONE); } void HostnameSelectScreen::SendEditKey(int keyCode, int flags) { @@ -1528,6 +1537,81 @@ UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) { return UI::EVENT_DONE; } +void HostnameSelectScreen::ResolverThread() { + std::unique_lock guard(resolverLock_); + + while (resolverState_ != ResolverState::QUIT) { + resolverCond_.wait(guard); + + if (resolverState_ == ResolverState::QUEUED) { + resolverState_ = ResolverState::PROGRESS; + + addrinfo *resolved = nullptr; + std::string err; + toResolveResult_ = net::DNSResolve(toResolve_, "80", &resolved, err); + if (resolved) + net::DNSResolveFree(resolved); + + resolverState_ = ResolverState::READY; + } + } +} + +bool HostnameSelectScreen::CanComplete(DialogResult result) { + if (result != DR_OK) + return true; + + std::string value = addrView_->GetText(); + if (lastResolved_ == value) { + return true; + } + + // Currently running. + if (resolverState_ == ResolverState::PROGRESS) + return false; + + std::lock_guard guard(resolverLock_); + switch (resolverState_) { + case ResolverState::PROGRESS: + case ResolverState::QUIT: + return false; + + case ResolverState::QUEUED: + case ResolverState::WAITING: + break; + + case ResolverState::READY: + if (toResolve_ == value) { + // Reset the state, nothing there now. + resolverState_ = ResolverState::WAITING; + toResolve_.clear(); + lastResolved_ = value; + lastResolvedResult_ = toResolveResult_; + + if (lastResolvedResult_) { + errorView_->SetVisibility(UI::V_GONE); + } else { + errorView_->SetVisibility(UI::V_VISIBLE); + } + progressView_->SetVisibility(UI::V_GONE); + + return true; + } + + // Throw away that last result, it was for a different value. + break; + } + + resolverState_ = ResolverState::QUEUED; + toResolve_ = value; + resolverCond_.notify_one(); + + progressView_->SetVisibility(UI::V_VISIBLE); + errorView_->SetVisibility(UI::V_GONE); + + return false; +} + void HostnameSelectScreen::OnCompleted(DialogResult result) { if (result == DR_OK) *value_ = addrView_->GetText(); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index a228cda4dc..8620336907 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -18,6 +18,9 @@ #pragma once #include "ppsspp_config.h" +#include +#include +#include #include "ui/ui_screen.h" #include "UI/MiscScreens.h" @@ -179,20 +182,49 @@ class HostnameSelectScreen : public PopupScreen { public: HostnameSelectScreen(std::string *value, const std::string &title) : PopupScreen(title, "OK", "Cancel"), value_(value) { + resolver_ = std::thread([](HostnameSelectScreen *thiz) { + thiz->ResolverThread(); + }, this); + } + ~HostnameSelectScreen() { + resolverState_ = ResolverState::QUIT; + resolverCond_.notify_one(); + resolver_.join(); } void CreatePopupContents(UI::ViewGroup *parent) override; protected: void OnCompleted(DialogResult result) override; + bool CanComplete(DialogResult result) override; private: + void ResolverThread(); void SendEditKey(int keyCode, int flags = 0); UI::EventReturn OnNumberClick(UI::EventParams &e); UI::EventReturn OnPointClick(UI::EventParams &e); UI::EventReturn OnDeleteClick(UI::EventParams &e); UI::EventReturn OnDeleteAllClick(UI::EventParams &e); + enum class ResolverState { + WAITING, + QUEUED, + PROGRESS, + READY, + QUIT, + }; + std::string *value_; - UI::TextEdit *addrView_; + UI::TextEdit *addrView_ = nullptr; + UI::TextView *errorView_ = nullptr; + UI::TextView *progressView_ = nullptr; + + std::thread resolver_; + ResolverState resolverState_ = ResolverState::WAITING; + std::mutex resolverLock_; + std::condition_variable resolverCond_; + std::string toResolve_ = ""; + bool toResolveResult_ = false; + std::string lastResolved_ = ""; + bool lastResolvedResult_ = false; }; diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index a49877327d..2e90d14c8c 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -268,6 +268,8 @@ bool PopupScreen::key(const KeyInput &key) { void PopupScreen::update() { UIDialogScreen::update(); + defaultButton_->SetEnabled(CanComplete(DR_OK)); + float animatePos = 1.0f; ++frames_; @@ -314,10 +316,12 @@ void PopupScreen::SetPopupOrigin(const UI::View *view) { } void PopupScreen::TriggerFinish(DialogResult result) { - finishFrame_ = frames_; - finishResult_ = result; + if (CanComplete(result)) { + finishFrame_ = frames_; + finishResult_ = result; - OnCompleted(result); + OnCompleted(result); + } } void PopupScreen::resized() { diff --git a/ext/native/ui/ui_screen.h b/ext/native/ui/ui_screen.h index 1d2dfa4399..c45c8b1329 100644 --- a/ext/native/ui/ui_screen.h +++ b/ext/native/ui/ui_screen.h @@ -84,6 +84,7 @@ protected: virtual bool FillVertical() const { return false; } virtual UI::Size PopupWidth() const { return 550; } virtual bool ShowButtons() const { return true; } + virtual bool CanComplete(DialogResult result) { return true; } virtual void OnCompleted(DialogResult result) {} virtual void update() override; From 010da8080c85067a08ca542b981743a7e9491390 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Aug 2019 12:00:21 -0700 Subject: [PATCH 29/63] UI: Fix crash with no buttons in popup. --- ext/native/ui/ui_screen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index 2e90d14c8c..b2662ba385 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -268,7 +268,8 @@ bool PopupScreen::key(const KeyInput &key) { void PopupScreen::update() { UIDialogScreen::update(); - defaultButton_->SetEnabled(CanComplete(DR_OK)); + if (defaultButton_) + defaultButton_->SetEnabled(CanComplete(DR_OK)); float animatePos = 1.0f; From c398da48ac0c9b5bd270376305e83007ff58d252 Mon Sep 17 00:00:00 2001 From: Antx64 <46533123+Antx64@users.noreply.github.com> Date: Mon, 19 Aug 2019 23:37:14 -0400 Subject: [PATCH 30/63] Fixes delete button for downloaded games from homebrew store in UWP --- Common/FileUtil.cpp | 7 ++----- Core/HLE/HLETables.cpp | 2 ++ Core/HLE/sceUsbMic.h | 4 +++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 5eeee2c466..7b9e8259b7 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -648,10 +648,8 @@ bool CreateEmptyFile(const std::string &filename) // Deletes the given directory and anything under it. Returns true on success. bool DeleteDirRecursively(const std::string &directory) -{ -#if PPSSPP_PLATFORM(UWP) - return false; -#else +{ + //Removed check, it prevents the UWP from deleting store downloads INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); #ifdef _WIN32 @@ -720,7 +718,6 @@ bool DeleteDirRecursively(const std::string &directory) closedir(dirp); #endif return File::DeleteDir(directory); -#endif } diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 0f4c440c4b..575bd52569 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -299,7 +299,9 @@ void RegisterAllModules() { Register_sceNetIfhandle(); Register_KUBridge(); Register_sceUsbAcc(); +#if !PPSSPP_PLATFORM(UWP) Register_sceUsbMic(); +#endif // add new modules here. } diff --git a/Core/HLE/sceUsbMic.h b/Core/HLE/sceUsbMic.h index 6540246f12..aa7d2981fc 100644 --- a/Core/HLE/sceUsbMic.h +++ b/Core/HLE/sceUsbMic.h @@ -16,5 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #pragma once - +//UWP doesn't like this for some reason +#if !PPSSPP_PLATFORM(UWP) void Register_sceUsbMic(); +#endif From 187dfb28169adcd0005fbf2ea14675ad7ece6b82 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Tue, 20 Aug 2019 11:23:06 +0300 Subject: [PATCH 31/63] Update PowerSaveModeReceiver (again) --- android/AndroidManifest.xml | 8 ----- .../src/org/ppsspp/ppsspp/NativeActivity.java | 13 ++++++-- .../ppsspp/ppsspp/PowerSaveModeReceiver.java | 33 ++++++++----------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 07237cd121..246814efd8 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -102,14 +102,6 @@ - - - - - - - - diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index ddae8db8c0..1ced155cf3 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -106,6 +106,8 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private InputDeviceState inputPlayerC; private String inputPlayerADesc; + private PowerSaveModeReceiver mPowerSaveModeReceiver = null; + private static LocationHelper mLocationHelper; private static CameraHelper mCameraHelper; @@ -311,7 +313,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL")); sendInitialGrants(); - PowerSaveModeReceiver.initAndSend(this); // OK, config should be initialized, we can query for screen rotation. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { @@ -489,6 +490,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C initialized = true; } + if (mPowerSaveModeReceiver == null) { + mPowerSaveModeReceiver = new PowerSaveModeReceiver(this); + } + // OK, config should be initialized, we can query for screen rotation. updateScreenRotation("onCreate"); updateSustainedPerformanceMode(); @@ -703,7 +708,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mSurfaceView.onDestroy(); mSurfaceView = null; } - PowerSaveModeReceiver.destroy(this); + if (mPowerSaveModeReceiver != null) { + mPowerSaveModeReceiver.destroy(this); + mPowerSaveModeReceiver = null; + } // TODO: Can we ensure that the GL thread has stopped rendering here? // I've seen crashes that seem to indicate that sometimes it hasn't... NativeApp.audioShutdown(); @@ -771,7 +779,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mSurfaceView.onResume(); } } - PowerSaveModeReceiver.sendPowerSaving(this); gainAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.resume(); diff --git a/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java b/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java index 5ba40291ff..38649b873c 100644 --- a/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java +++ b/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java @@ -16,7 +16,6 @@ import android.util.Log; public class PowerSaveModeReceiver extends BroadcastReceiver { private static final String TAG = PowerSaveModeReceiver.class.getSimpleName(); - private static BroadcastReceiver saveModeReceiver = null; private static boolean isPowerSaving = false; private static boolean isBatteryLow = false; @@ -27,25 +26,21 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { isBatteryLow = true; } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { isBatteryLow = false; + } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) { + // sendPowerSaving() } sendPowerSaving(context); } - public static void initAndSend(final Activity activity) { - saveModeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { - sendPowerSaving(context); - } - } - }; + public PowerSaveModeReceiver(final Activity activity) { IntentFilter filter = new IntentFilter(); - filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); - activity.registerReceiver(saveModeReceiver, filter); - - sendPowerSaving(activity); + filter.addAction(Intent.ACTION_BATTERY_LOW); + filter.addAction(Intent.ACTION_BATTERY_OKAY); + if (Build.VERSION.SDK_INT >= 21) { + filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + } + activity.registerReceiver(this, filter); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { activity.getContentResolver().registerContentObserver(Settings.System.CONTENT_URI, true, new ContentObserver(null) { @@ -65,13 +60,11 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { } }); } + sendPowerSaving(activity); } - public static void destroy(final Activity activity) { - if (saveModeReceiver != null) { - activity.unregisterReceiver(saveModeReceiver); - saveModeReceiver = null; - } + public void destroy(final Context context) { + context.unregisterReceiver(this); } @TargetApi(21) @@ -101,7 +94,7 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { return value != null && value.equals("1"); } - protected static void sendPowerSaving(final Context context) { + protected void sendPowerSaving(final Context context) { if (Build.VERSION.SDK_INT >= 21) { isPowerSaving = getNativePowerSaving(context); } else { From a7ce74150e45c1e0caed8f65865b53adb8541a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Aug 2019 11:09:54 +0200 Subject: [PATCH 32/63] Add sceUsbMic.cpp/h to the UWP project --- UWP/CoreUWP/CoreUWP.vcxproj | 2 ++ UWP/CoreUWP/CoreUWP.vcxproj.filters | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 5cac0a005d..a26bcbac2c 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -501,6 +501,7 @@ + @@ -712,6 +713,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 9a887c8a3b..fe872876ad 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -631,6 +631,9 @@ HLE + + HLE + Debugger @@ -1243,6 +1246,9 @@ HLE + + HLE + Debugger @@ -1292,4 +1298,4 @@ MIPS\ARM64 - \ No newline at end of file + From 1f6e5e463e8381e35b24a841d10422b78170e6e5 Mon Sep 17 00:00:00 2001 From: Antx64 <46533123+Antx64@users.noreply.github.com> Date: Tue, 20 Aug 2019 08:41:59 -0400 Subject: [PATCH 33/63] removed the sceUsbMic checks --- Core/HLE/HLETables.cpp | 2 -- Core/HLE/sceUsbMic.h | 3 --- 2 files changed, 5 deletions(-) diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 575bd52569..0f4c440c4b 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -299,9 +299,7 @@ void RegisterAllModules() { Register_sceNetIfhandle(); Register_KUBridge(); Register_sceUsbAcc(); -#if !PPSSPP_PLATFORM(UWP) Register_sceUsbMic(); -#endif // add new modules here. } diff --git a/Core/HLE/sceUsbMic.h b/Core/HLE/sceUsbMic.h index aa7d2981fc..35e1132861 100644 --- a/Core/HLE/sceUsbMic.h +++ b/Core/HLE/sceUsbMic.h @@ -16,7 +16,4 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #pragma once -//UWP doesn't like this for some reason -#if !PPSSPP_PLATFORM(UWP) void Register_sceUsbMic(); -#endif From 653afeb7ab2d14f0e66d8e1467a2a985c394b050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 00:03:00 +0200 Subject: [PATCH 34/63] Vulkan: Implement basic integrated GPU profiling. Currently simply measures the total GPU time of the frame. Will later be extended to get the execution time of individual render passes. --- Core/Config.cpp | 1 + Core/Config.h | 1 + GPU/Vulkan/DebugVisVulkan.cpp | 25 +++++++++ GPU/Vulkan/DebugVisVulkan.h | 1 + GPU/Vulkan/GPU_Vulkan.cpp | 5 ++ GPU/Vulkan/GPU_Vulkan.h | 2 + UI/DevScreens.cpp | 5 +- UI/EmuScreen.cpp | 5 ++ ext/native/thin3d/VulkanRenderManager.cpp | 65 +++++++++++++++++++++++ ext/native/thin3d/VulkanRenderManager.h | 21 ++++++++ ext/native/thin3d/thin3d_vulkan.cpp | 1 + 11 files changed, 131 insertions(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index ceb13f165f..d544024f1f 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -962,6 +962,7 @@ static ConfigSetting debuggerSettings[] = { ConfigSetting("ShowBottomTabTitles",&g_Config.bShowBottomTabTitles,true), ConfigSetting("ShowDeveloperMenu", &g_Config.bShowDeveloperMenu, false), ConfigSetting("ShowAllocatorDebug", &g_Config.bShowAllocatorDebug, false, false), + ConfigSetting("ShowGpuProfile", &g_Config.bShowGpuProfile, false, false), ConfigSetting("SkipDeadbeefFilling", &g_Config.bSkipDeadbeefFilling, false), ConfigSetting("FuncHashMap", &g_Config.bFuncHashMap, false), diff --git a/Core/Config.h b/Core/Config.h index 859bfbb3ad..ad39197300 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -238,6 +238,7 @@ public: bool bShowDebugStats; bool bShowAudioDebug; bool bAudioResampler; + bool bShowGpuProfile; //Analog stick tilting //the base x and y tilt. this inclination is treated as (0,0) and the tilt input diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index d93195bc16..1745cf57c1 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -26,6 +26,8 @@ #include "GPU/Vulkan/GPU_Vulkan.h" #include "GPU/Vulkan/VulkanUtil.h" +#undef DrawText + void DrawAllocatorVis(UIContext *ui, GPUInterface *gpu) { if (!gpu) { return; @@ -94,3 +96,26 @@ void DrawAllocatorVis(UIContext *ui, GPUInterface *gpu) { for (auto iter : texturesToDelete) iter->Release(); } + +void DrawProfilerVis(UIContext *ui, GPUInterface *gpu) { + if (!gpu) { + return; + } + using namespace Draw; + const int padding = 10; + const int columnWidth = 256; + const int starty = padding * 8; + int x = padding; + int y = starty; + int w = columnWidth; // We will double this when actually drawing to make the pixels visible. + + ui->Begin(); + + GPU_Vulkan *gpuVulkan = static_cast(gpu); + + std::string text = gpuVulkan->GetGpuProfileString(); + + Draw::DrawContext *draw = ui->GetDrawContext(); + ui->DrawTextShadow(text.c_str(), 10, 50, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ui->Flush(); +} diff --git a/GPU/Vulkan/DebugVisVulkan.h b/GPU/Vulkan/DebugVisVulkan.h index 9c0590bab4..e220003a56 100644 --- a/GPU/Vulkan/DebugVisVulkan.h +++ b/GPU/Vulkan/DebugVisVulkan.h @@ -24,3 +24,4 @@ class UIContext; // gpu MUST be an instance of GPU_Vulkan. If not, will definitely crash. void DrawAllocatorVis(UIContext *ui, GPUInterface *gpu); +void DrawProfilerVis(UIContext *ui, GPUInterface *gpu); diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 6507f0f0de..178faac8d6 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -638,3 +638,8 @@ std::string GPU_Vulkan::DebugGetShaderString(std::string id, DebugShaderType typ return std::string(); } } + +std::string GPU_Vulkan::GetGpuProfileString() { + VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + return rm->GetGpuProfileString(); +} diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 42de6916c7..bb2efe5877 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -66,6 +66,8 @@ public: return textureCacheVulkan_; } + std::string GetGpuProfileString(); + protected: void FinishDeferred() override; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index b4f430266f..d3e1d54f5d 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -78,7 +78,10 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenu::OnDeveloperTools); items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare); items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenu::OnShaderView); - items->Add(new CheckBox(&g_Config.bShowAllocatorDebug, dev->T("Allocator Viewer"))); + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { + items->Add(new CheckBox(&g_Config.bShowAllocatorDebug, dev->T("Allocator Viewer"))); + items->Add(new CheckBox(&g_Config.bShowGpuProfile, dev->T("GPU Profile"))); + } items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame); items->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame); items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index f0b86207a4..81a008e095 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1359,6 +1359,11 @@ void EmuScreen::renderUI() { if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN && g_Config.bShowAllocatorDebug) { DrawAllocatorVis(ctx, gpu); } + + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN && g_Config.bShowGpuProfile) { + DrawProfilerVis(ctx, gpu); + } + #endif #ifdef USE_PROFILER diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 84bfcd3266..aff60e85d9 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -1,6 +1,8 @@ #include #include +#include + #include "Common/Log.h" #include "base/logging.h" @@ -131,6 +133,11 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].mainCmd); assert(res == VK_SUCCESS); frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on + + VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO }; + query_ci.queryCount = MAX_TIMESTAMP_QUERIES; + query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP; + res = vkCreateQueryPool(vulkan_->GetDevice(), &query_ci, nullptr, &frameData_[i].timestampQueryPool_); } queueRunner_.CreateDeviceObjects(); @@ -289,6 +296,7 @@ VulkanRenderManager::~VulkanRenderManager() { vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr); vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr); vkDestroyFence(device, frameData_[i].fence, nullptr); + vkDestroyQueryPool(device, frameData_[i].timestampQueryPool_, nullptr); } queueRunner_.DestroyDeviceObjects(); } @@ -360,6 +368,38 @@ void VulkanRenderManager::BeginFrame() { vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); + uint64_t queryResults[MAX_TIMESTAMP_QUERIES]; + if (gpuProfilingEnabled_) { + // Pull the profiling results from last time and produce a summary! + if (frameData.numQueries) { + VkResult res = vkGetQueryPoolResults( + vulkan_->GetDevice(), + frameData.timestampQueryPool_, 0, frameData.numQueries, sizeof(uint64_t) * frameData.numQueries, &queryResults[0], sizeof(uint64_t), + VK_QUERY_RESULT_64_BIT); + if (res == VK_SUCCESS) { + double timestampConversionFactor = (double)vulkan_->GetPhysicalDeviceProperties().properties.limits.timestampPeriod * (1.0 / 1000000.0); + uint64_t timestampDiffMask = 0xFFFFFFFFFFFFFFFFULL; // TODO: Get from queue family + std::stringstream str; + + char line[256]; + snprintf(line, sizeof(line), "Total GPU time: %0.3f ms\n", ((double)((queryResults[frameData.numQueries - 1] - queryResults[0]) & timestampDiffMask) * timestampConversionFactor)); + str << line; + for (int i = 0; i < frameData.numQueries - 1; i++) { + uint64_t diff = (queryResults[i + 1] - queryResults[i]) & timestampDiffMask; + double milliseconds = (double)diff * timestampConversionFactor; + snprintf(line, sizeof(line), "%s: %0.3f ms\n", frameData.timestampDescriptions[i + 1].c_str(), milliseconds); + str << line; + } + frameData.profileSummary = str.str(); + } else { + frameData.profileSummary = "(error getting GPU profile - not ready?)"; + } + } + else { + frameData.profileSummary = "(no GPU profile data collected)"; + } + } + // Must be after the fence - this performs deletes. VLOG("PUSH: BeginFrame %d", curFrame); if (!run_) { @@ -368,6 +408,20 @@ void VulkanRenderManager::BeginFrame() { vulkan_->BeginFrame(); insideFrame_ = true; + + if (gpuProfilingEnabled_) { + // For various reasons, we need to always use an init cmd buffer in this case to perform the vkCmdResetQueryPool, + // unless we want to limit ourselves to only measure the main cmd buffer. + // Reserve the first two queries for initCmd. + frameData.numQueries = 2; + frameData.timestampDescriptions.push_back("initCmd Begin"); + frameData.timestampDescriptions.push_back("initCmd"); + VkCommandBuffer initCmd = GetInitCmd(); + vkCmdResetQueryPool(initCmd, frameData.timestampQueryPool_, 0, MAX_TIMESTAMP_QUERIES); + vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, frameData.timestampQueryPool_, 0); + } else { + frameData.numQueries = 0; + } } VkCommandBuffer VulkanRenderManager::GetInitCmd() { @@ -884,6 +938,7 @@ void VulkanRenderManager::BeginSubmitFrame(int frame) { VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + _assert_msg_(G3D, res == VK_SUCCESS, "vkBeginCommandBuffer failed! result=%s", VulkanResultToString(res)); queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage], swapchainImages_[frameData.curSwapchainImage].image); @@ -895,10 +950,20 @@ void VulkanRenderManager::BeginSubmitFrame(int frame) { void VulkanRenderManager::Submit(int frame, bool triggerFence) { FrameData &frameData = frameData_[frame]; if (frameData.hasInitCommands) { + if (gpuProfilingEnabled_ && triggerFence) { + // Pre-allocated query ID 1. + vkCmdWriteTimestamp(frameData.initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, 1); + } VkResult res = vkEndCommandBuffer(frameData.initCmd); _assert_msg_(G3D, res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res)); } + if (gpuProfilingEnabled_) { + vkCmdWriteTimestamp(frameData.mainCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, frameData.numQueries); + frameData.numQueries++; + frameData.timestampDescriptions.push_back("mainCmd"); + } + VkResult res = vkEndCommandBuffer(frameData.mainCmd); _assert_msg_(G3D, res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res)); diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 84f7cbeed2..50c86a92bc 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -242,6 +242,15 @@ public: return &queueRunner_; } + // Call before BeginFrame. + void SetGPUProfilingEnabled(bool enabled) { + gpuProfilingEnabled_ = enabled; + } + + std::string GetGpuProfileString() const { + return frameData_[vulkan_->GetCurFrame()].profileSummary; + } + private: bool InitBackbufferFramebuffers(int width, int height); bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. @@ -284,9 +293,20 @@ private: // Swapchain. bool hasBegun = false; uint32_t curSwapchainImage = -1; + + // Profiling. + VkQueryPool timestampQueryPool_ = VK_NULL_HANDLE; + std::vector timestampDescriptions; + int numQueries = 0; + std::string profileSummary; }; + FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; + enum { + MAX_TIMESTAMP_QUERIES = 256, + }; + // Submission time state int curWidth_ = -1; int curHeight_ = -1; @@ -294,6 +314,7 @@ private: VKRStep *curRenderStep_ = nullptr; std::vector steps_; bool splitSubmit_ = false; + bool gpuProfilingEnabled_ = true; // Execution time state bool run_ = true; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index a62761feea..01075cfa8c 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -879,6 +879,7 @@ VKContext::~VKContext() { } void VKContext::BeginFrame() { + renderManager_.SetGPUProfilingEnabled(g_Config.bShowGpuProfile); renderManager_.BeginFrame(); FrameData &frame = frame_[vulkan_->GetCurFrame()]; From 19a443819bae961acf0d20e01d3cd767ca348449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 09:02:23 +0200 Subject: [PATCH 35/63] Bugfixes to VK gpu profiling. Properly get the valid bits. --- Common/Vulkan/VulkanContext.cpp | 8 ++++---- Common/Vulkan/VulkanContext.h | 8 ++++++-- Core/Config.h | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 7 ++++++- ext/native/thin3d/VulkanRenderManager.h | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 297bc72d2f..dc87073214 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -516,8 +516,8 @@ void VulkanContext::ChooseDevice(int physical_device) { vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, nullptr); assert(queue_count >= 1); - queue_props.resize(queue_count); - vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, queue_props.data()); + queueFamilyProperties_.resize(queue_count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, queueFamilyProperties_.data()); assert(queue_count >= 1); // Detect preferred formats, in this order. @@ -619,7 +619,7 @@ VkResult VulkanContext::CreateDevice() { queue_info.pQueuePriorities = queue_priorities; bool found = false; for (int i = 0; i < (int)queue_count; i++) { - if (queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + if (queueFamilyProperties_[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { queue_info.queueFamilyIndex = i; found = true; break; @@ -816,7 +816,7 @@ bool VulkanContext::InitQueue() { uint32_t graphicsQueueNodeIndex = UINT32_MAX; uint32_t presentQueueNodeIndex = UINT32_MAX; for (uint32_t i = 0; i < queue_count; i++) { - if ((queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { + if ((queueFamilyProperties_[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { if (graphicsQueueNodeIndex == UINT32_MAX) { graphicsQueueNodeIndex = i; } diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 01761aab6b..d3ecb96963 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -201,6 +201,10 @@ public: return physicalDeviceProperties_[i]; } + const VkQueueFamilyProperties &GetQueueFamilyProperties(int family) const { + return queueFamilyProperties_[family]; + } + VkResult GetInstanceLayerExtensionList(const char *layerName, std::vector &extensions); VkResult GetInstanceLayerProperties(); @@ -303,8 +307,8 @@ private: int physical_device_ = -1; uint32_t graphics_queue_family_index_ = -1; - std::vector physicalDeviceProperties_{}; - std::vector queue_props; + std::vector physicalDeviceProperties_; + std::vector queueFamilyProperties_; VkPhysicalDeviceMemoryProperties memory_properties{}; // Custom collection of things that are good to know diff --git a/Core/Config.h b/Core/Config.h index ad39197300..c23a651053 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -237,8 +237,8 @@ public: bool bLogFrameDrops; bool bShowDebugStats; bool bShowAudioDebug; - bool bAudioResampler; bool bShowGpuProfile; + bool bAudioResampler; //Analog stick tilting //the base x and y tilt. this inclination is treated as (0,0) and the tilt input diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index aff60e85d9..ac584fb71e 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -226,6 +226,9 @@ void VulkanRenderManager::StopThread() { std::unique_lock lock(frameData.pull_mutex); frameData.pull_condVar.notify_all(); } + // Zero the queries so we don't try to pull them later. + frameData.numQueries = 0; + frameData.timestampDescriptions.clear(); } thread_.join(); ILOG("Vulkan submission thread joined. Frame=%d", vulkan_->GetCurFrame()); @@ -378,7 +381,8 @@ void VulkanRenderManager::BeginFrame() { VK_QUERY_RESULT_64_BIT); if (res == VK_SUCCESS) { double timestampConversionFactor = (double)vulkan_->GetPhysicalDeviceProperties().properties.limits.timestampPeriod * (1.0 / 1000000.0); - uint64_t timestampDiffMask = 0xFFFFFFFFFFFFFFFFULL; // TODO: Get from queue family + int validBits = vulkan_->GetQueueFamilyProperties(vulkan_->GetGraphicsQueueFamilyIndex()).timestampValidBits; + uint64_t timestampDiffMask = validBits == 64 ? 0xFFFFFFFFFFFFFFFFULL : ((1ULL << validBits) - 1); std::stringstream str; char line[256]; @@ -409,6 +413,7 @@ void VulkanRenderManager::BeginFrame() { insideFrame_ = true; + frameData.timestampDescriptions.clear(); if (gpuProfilingEnabled_) { // For various reasons, we need to always use an init cmd buffer in this case to perform the vkCmdResetQueryPool, // unless we want to limit ourselves to only measure the main cmd buffer. diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 50c86a92bc..393efd21d4 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -314,7 +314,7 @@ private: VKRStep *curRenderStep_ = nullptr; std::vector steps_; bool splitSubmit_ = false; - bool gpuProfilingEnabled_ = true; + bool gpuProfilingEnabled_ = false; // Execution time state bool run_ = true; From 78e849bff1ec9fc660bc23fd66f7c1a966330423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 10:17:33 +0200 Subject: [PATCH 36/63] Decided to end the practice of #ifdefs in Config.h. Led to bizarre miscompilations when reading config with inconsistent ifdefs set. --- Core/Config.cpp | 2 +- Core/Config.h | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index d544024f1f..ab9be6da50 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -959,7 +959,7 @@ static ConfigSetting debuggerSettings[] = { ConfigSetting("FontWidth", &g_Config.iFontWidth, 8), ConfigSetting("FontHeight", &g_Config.iFontHeight, 12), ConfigSetting("DisplayStatusBar", &g_Config.bDisplayStatusBar, true), - ConfigSetting("ShowBottomTabTitles",&g_Config.bShowBottomTabTitles,true), + ConfigSetting("ShowBottomTabTitles",&g_Config.bShowBottomTabTitles, true), ConfigSetting("ShowDeveloperMenu", &g_Config.bShowDeveloperMenu, false), ConfigSetting("ShowAllocatorDebug", &g_Config.bShowAllocatorDebug, false, false), ConfigSetting("ShowGpuProfile", &g_Config.bShowGpuProfile, false, false), diff --git a/Core/Config.h b/Core/Config.h index c23a651053..2b04bf222d 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -68,21 +68,20 @@ public: bool bEnableLogging; bool bDumpDecryptedEboot; bool bFullscreenOnDoubleclick; -#if defined(USING_WIN_UI) + + // These four are Win UI only bool bPauseOnLostFocus; bool bTopMost; bool bIgnoreWindowsKey; bool bRestartRequired; -#endif -#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP) + std::string sFont; -#endif bool bPauseWhenMinimized; -#if !defined(MOBILE_DEVICE) + // Not used on mobile devices. bool bPauseExitsEmulator; -#endif + bool bPauseMenuExitsEmulator; // Core @@ -129,9 +128,8 @@ public: // We have separate device parameters for each backend so it doesn't get erased if you switch backends. // If not set, will use the "best" device. std::string sVulkanDevice; -#ifdef _WIN32 - std::string sD3D11Device; -#endif + std::string sD3D11Device; // Windows only + bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend bool bSoftwareSkinning; // may speed up some games @@ -367,9 +365,7 @@ public: int iPSPModel; int iFirmwareVersion; // TODO: Make this work with your platform, too! -#if defined(USING_WIN_UI) bool bBypassOSKWithKeyboard; -#endif // Debugger int iDisasmWindowX; From 5fcac1a9e2019d305cad231c16e16c465adb7284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 10:23:36 +0200 Subject: [PATCH 37/63] Vulkan: Disable some bad validation. Things are right and work fine. --- Common/Vulkan/VulkanDebug.cpp | 7 +++++++ ext/native/thin3d/VulkanRenderManager.cpp | 4 +++- ext/native/thin3d/VulkanRenderManager.h | 11 ++++------- ext/native/thin3d/thin3d_vulkan.cpp | 3 +-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Common/Vulkan/VulkanDebug.cpp b/Common/Vulkan/VulkanDebug.cpp index be7512b738..55825f8209 100644 --- a/Common/Vulkan/VulkanDebug.cpp +++ b/Common/Vulkan/VulkanDebug.cpp @@ -113,6 +113,13 @@ VkBool32 VKAPI_CALL VulkanDebugUtilsCallback( std::ostringstream message; const char *pMessage = pCallbackData->pMessage; + + // Apparent bugs around timestamp validation in the validation layers + if (strstr(pMessage, "vkCmdBeginQuery(): VkQueryPool")) + return false; + if (strstr(pMessage, "vkGetQueryPoolResults() on VkQueryPool")) + return false; + int messageCode = pCallbackData->messageIdNumber; const char *pLayerPrefix = ""; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index ac584fb71e..9dd5732900 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -350,7 +350,8 @@ void VulkanRenderManager::ThreadFunc() { VLOG("PULL: Quitting"); } -void VulkanRenderManager::BeginFrame() { +void VulkanRenderManager::BeginFrame(bool enableProfiling) { + gpuProfilingEnabled_ = enableProfiling; VLOG("BeginFrame"); VkDevice device = vulkan_->GetDevice(); @@ -372,6 +373,7 @@ void VulkanRenderManager::BeginFrame() { vkResetFences(device, 1, &frameData.fence); uint64_t queryResults[MAX_TIMESTAMP_QUERIES]; + if (gpuProfilingEnabled_) { // Pull the profiling results from last time and produce a summary! if (frameData.numQueries) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 393efd21d4..5e46b9c53f 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -92,7 +92,7 @@ public: void ThreadFunc(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. - void BeginFrame(); + void BeginFrame(bool enableProfiling); // Can run on a different thread! void Finish(); void Run(int frame); @@ -242,11 +242,6 @@ public: return &queueRunner_; } - // Call before BeginFrame. - void SetGPUProfilingEnabled(bool enabled) { - gpuProfilingEnabled_ = enabled; - } - std::string GetGpuProfileString() const { return frameData_[vulkan_->GetCurFrame()].profileSummary; } @@ -307,6 +302,9 @@ private: MAX_TIMESTAMP_QUERIES = 256, }; + // Global state + bool gpuProfilingEnabled_ = false; + // Submission time state int curWidth_ = -1; int curHeight_ = -1; @@ -314,7 +312,6 @@ private: VKRStep *curRenderStep_ = nullptr; std::vector steps_; bool splitSubmit_ = false; - bool gpuProfilingEnabled_ = false; // Execution time state bool run_ = true; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 01075cfa8c..a0f384f149 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -879,8 +879,7 @@ VKContext::~VKContext() { } void VKContext::BeginFrame() { - renderManager_.SetGPUProfilingEnabled(g_Config.bShowGpuProfile); - renderManager_.BeginFrame(); + renderManager_.BeginFrame(g_Config.bShowGpuProfile); FrameData &frame = frame_[vulkan_->GetCurFrame()]; push_ = frame.pushBuffer; From 06a71fdf49f150dca3521ad8aa35f453ac245e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 10:30:57 +0200 Subject: [PATCH 38/63] Get rid of numQueries --- ext/native/thin3d/VulkanRenderManager.cpp | 17 +++++++---------- ext/native/thin3d/VulkanRenderManager.h | 1 - 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 9dd5732900..2fe59d3be0 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -227,7 +227,6 @@ void VulkanRenderManager::StopThread() { frameData.pull_condVar.notify_all(); } // Zero the queries so we don't try to pull them later. - frameData.numQueries = 0; frameData.timestampDescriptions.clear(); } thread_.join(); @@ -376,10 +375,11 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { if (gpuProfilingEnabled_) { // Pull the profiling results from last time and produce a summary! - if (frameData.numQueries) { + if (!frameData.timestampDescriptions.empty()) { + int numQueries = (int)frameData.timestampDescriptions.size(); VkResult res = vkGetQueryPoolResults( vulkan_->GetDevice(), - frameData.timestampQueryPool_, 0, frameData.numQueries, sizeof(uint64_t) * frameData.numQueries, &queryResults[0], sizeof(uint64_t), + frameData.timestampQueryPool_, 0, numQueries, sizeof(uint64_t) * numQueries, &queryResults[0], sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); if (res == VK_SUCCESS) { double timestampConversionFactor = (double)vulkan_->GetPhysicalDeviceProperties().properties.limits.timestampPeriod * (1.0 / 1000000.0); @@ -388,9 +388,9 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { std::stringstream str; char line[256]; - snprintf(line, sizeof(line), "Total GPU time: %0.3f ms\n", ((double)((queryResults[frameData.numQueries - 1] - queryResults[0]) & timestampDiffMask) * timestampConversionFactor)); + snprintf(line, sizeof(line), "Total GPU time: %0.3f ms\n", ((double)((queryResults[numQueries - 1] - queryResults[0]) & timestampDiffMask) * timestampConversionFactor)); str << line; - for (int i = 0; i < frameData.numQueries - 1; i++) { + for (int i = 0; i < numQueries - 1; i++) { uint64_t diff = (queryResults[i + 1] - queryResults[i]) & timestampDiffMask; double milliseconds = (double)diff * timestampConversionFactor; snprintf(line, sizeof(line), "%s: %0.3f ms\n", frameData.timestampDescriptions[i + 1].c_str(), milliseconds); @@ -420,14 +420,11 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { // For various reasons, we need to always use an init cmd buffer in this case to perform the vkCmdResetQueryPool, // unless we want to limit ourselves to only measure the main cmd buffer. // Reserve the first two queries for initCmd. - frameData.numQueries = 2; frameData.timestampDescriptions.push_back("initCmd Begin"); frameData.timestampDescriptions.push_back("initCmd"); VkCommandBuffer initCmd = GetInitCmd(); vkCmdResetQueryPool(initCmd, frameData.timestampQueryPool_, 0, MAX_TIMESTAMP_QUERIES); vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, frameData.timestampQueryPool_, 0); - } else { - frameData.numQueries = 0; } } @@ -966,8 +963,8 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { } if (gpuProfilingEnabled_) { - vkCmdWriteTimestamp(frameData.mainCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, frameData.numQueries); - frameData.numQueries++; + int numQueries = (int)frameData.timestampDescriptions.size(); + vkCmdWriteTimestamp(frameData.mainCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, numQueries); frameData.timestampDescriptions.push_back("mainCmd"); } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 5e46b9c53f..0b272f1257 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -292,7 +292,6 @@ private: // Profiling. VkQueryPool timestampQueryPool_ = VK_NULL_HANDLE; std::vector timestampDescriptions; - int numQueries = 0; std::string profileSummary; }; From 66609b395f03c1177c06c6a8cbb574db83578e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 16:46:58 +0200 Subject: [PATCH 39/63] Vulkan: Profile individual render passes/steps. --- ext/native/thin3d/VulkanQueueRunner.cpp | 36 ++++++++++++++++++++++- ext/native/thin3d/VulkanQueueRunner.h | 4 ++- ext/native/thin3d/VulkanRenderManager.cpp | 12 ++++---- ext/native/thin3d/VulkanRenderManager.h | 4 +-- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index d9ed03a426..92f3847ef5 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -374,7 +374,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { return pass; } -void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &steps) { +void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &steps, VkQueryPool queryPool, std::vector *timestampDescriptions) { // Optimizes renderpasses, then sequences them. // Planned optimizations: // * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes @@ -461,6 +461,11 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st case VKRStepType::RENDER_SKIP: break; } + + if (queryPool) { + vkCmdWriteTimestamp(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, queryPool, (uint32_t)timestampDescriptions->size()); + timestampDescriptions->push_back(StepToString(step)); + } } // Deleting all in one go should be easier on the instruction cache than deleting @@ -695,10 +700,39 @@ void VulkanQueueRunner::ApplySonicHack(std::vector &steps) { } } +std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { + char buffer[256]; + switch (step.stepType) { + case VKRStepType::RENDER: + snprintf(buffer, sizeof(buffer), "RenderPass (fb: %p)", step.render.framebuffer); + break; + case VKRStepType::COPY: + snprintf(buffer, sizeof(buffer), "Copy (%dx%d)", step.copy.srcRect.extent.width, step.copy.srcRect.extent.height); + break; + case VKRStepType::BLIT: + snprintf(buffer, sizeof(buffer), "Blit"); + break; + case VKRStepType::READBACK: + snprintf(buffer, sizeof(buffer), "Readback"); + break; + case VKRStepType::READBACK_IMAGE: + snprintf(buffer, sizeof(buffer), "ReadbackImage"); + break; + case VKRStepType::RENDER_SKIP: + snprintf(buffer, sizeof(buffer), "(SKIPPED RenderPass)"); + break; + default: + buffer[0] = 0; + break; + } + return std::string(buffer); +} + void VulkanQueueRunner::LogSteps(const std::vector &steps) { ILOG("======================================="); for (size_t i = 0; i < steps.size(); i++) { const VKRStep &step = *steps[i]; + ILOG("%s", StepToString(step).c_str()); switch (step.stepType) { case VKRStepType::RENDER: LogRenderPass(step); diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 17f0acb69a..b7f4c33090 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -163,9 +163,11 @@ public: } // RunSteps can modify steps but will leave it in a valid state. - void RunSteps(VkCommandBuffer cmd, std::vector &steps); + void RunSteps(VkCommandBuffer cmd, std::vector &steps, VkQueryPool queryPool, std::vector *timestampDescriptions); void LogSteps(const std::vector &steps); + std::string StepToString(const VKRStep &step) const; + void CreateDeviceObjects(); void DestroyDeviceObjects(); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 2fe59d3be0..827ea1ab66 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -350,12 +350,12 @@ void VulkanRenderManager::ThreadFunc() { } void VulkanRenderManager::BeginFrame(bool enableProfiling) { - gpuProfilingEnabled_ = enableProfiling; VLOG("BeginFrame"); VkDevice device = vulkan_->GetDevice(); int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; + frameData.profilingEnabled_ = enableProfiling; // Make sure the very last command buffer from the frame before the previous has been fully executed. if (useThread_) { @@ -373,7 +373,7 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { uint64_t queryResults[MAX_TIMESTAMP_QUERIES]; - if (gpuProfilingEnabled_) { + if (frameData.profilingEnabled_) { // Pull the profiling results from last time and produce a summary! if (!frameData.timestampDescriptions.empty()) { int numQueries = (int)frameData.timestampDescriptions.size(); @@ -416,7 +416,7 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { insideFrame_ = true; frameData.timestampDescriptions.clear(); - if (gpuProfilingEnabled_) { + if (frameData_->profilingEnabled_) { // For various reasons, we need to always use an init cmd buffer in this case to perform the vkCmdResetQueryPool, // unless we want to limit ourselves to only measure the main cmd buffer. // Reserve the first two queries for initCmd. @@ -954,7 +954,7 @@ void VulkanRenderManager::BeginSubmitFrame(int frame) { void VulkanRenderManager::Submit(int frame, bool triggerFence) { FrameData &frameData = frameData_[frame]; if (frameData.hasInitCommands) { - if (gpuProfilingEnabled_ && triggerFence) { + if (frameData.profilingEnabled_ && triggerFence) { // Pre-allocated query ID 1. vkCmdWriteTimestamp(frameData.initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, 1); } @@ -962,7 +962,7 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { _assert_msg_(G3D, res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res)); } - if (gpuProfilingEnabled_) { + if (frameData.profilingEnabled_) { int numQueries = (int)frameData.timestampDescriptions.size(); vkCmdWriteTimestamp(frameData.mainCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, numQueries); frameData.timestampDescriptions.push_back("mainCmd"); @@ -1063,7 +1063,7 @@ void VulkanRenderManager::Run(int frame) { auto &stepsOnThread = frameData_[frame].steps; VkCommandBuffer cmd = frameData.mainCmd; // queueRunner_.LogSteps(stepsOnThread); - queueRunner_.RunSteps(cmd, stepsOnThread); + queueRunner_.RunSteps(cmd, stepsOnThread, frameData.profilingEnabled_ ? frameData.timestampQueryPool_ : VK_NULL_HANDLE, &frameData.timestampDescriptions); stepsOnThread.clear(); switch (frameData.type) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 0b272f1257..a06332aa69 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -291,6 +291,7 @@ private: // Profiling. VkQueryPool timestampQueryPool_ = VK_NULL_HANDLE; + bool profilingEnabled_; std::vector timestampDescriptions; std::string profileSummary; }; @@ -301,9 +302,6 @@ private: MAX_TIMESTAMP_QUERIES = 256, }; - // Global state - bool gpuProfilingEnabled_ = false; - // Submission time state int curWidth_ = -1; int curHeight_ = -1; From e3f1e563f25bb9b1778e595283212d723c7af510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 18:28:20 +0200 Subject: [PATCH 40/63] Improve renderpass profiling descriptions a little --- GPU/Vulkan/DebugVisVulkan.cpp | 1 + ext/native/thin3d/VulkanQueueRunner.cpp | 10 +++++++--- ext/native/thin3d/VulkanRenderManager.cpp | 6 ------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index 1745cf57c1..091f576679 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -116,6 +116,7 @@ void DrawProfilerVis(UIContext *ui, GPUInterface *gpu) { std::string text = gpuVulkan->GetGpuProfileString(); Draw::DrawContext *draw = ui->GetDrawContext(); + ui->SetFontScale(0.4f, 0.4f); ui->DrawTextShadow(text.c_str(), 10, 50, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui->Flush(); } diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 92f3847ef5..5b5d60c74c 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -704,16 +704,20 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { char buffer[256]; switch (step.stepType) { case VKRStepType::RENDER: - snprintf(buffer, sizeof(buffer), "RenderPass (fb: %p)", step.render.framebuffer); + { + int w = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth(); + int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight(); + snprintf(buffer, sizeof(buffer), "RenderPass (draws: %d, %dx%d, fb: %p, )", step.render.numDraws, w, h, step.render.framebuffer); break; + } case VKRStepType::COPY: snprintf(buffer, sizeof(buffer), "Copy (%dx%d)", step.copy.srcRect.extent.width, step.copy.srcRect.extent.height); break; case VKRStepType::BLIT: - snprintf(buffer, sizeof(buffer), "Blit"); + snprintf(buffer, sizeof(buffer), "Blit (%dx%d->%dx%d)", step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height); break; case VKRStepType::READBACK: - snprintf(buffer, sizeof(buffer), "Readback"); + snprintf(buffer, sizeof(buffer), "Readback (%dx%d)", step.readback.srcRect.extent.width, step.readback.srcRect.extent.height); break; case VKRStepType::READBACK_IMAGE: snprintf(buffer, sizeof(buffer), "ReadbackImage"); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 827ea1ab66..77e66ed7b8 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -962,12 +962,6 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { _assert_msg_(G3D, res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res)); } - if (frameData.profilingEnabled_) { - int numQueries = (int)frameData.timestampDescriptions.size(); - vkCmdWriteTimestamp(frameData.mainCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, numQueries); - frameData.timestampDescriptions.push_back("mainCmd"); - } - VkResult res = vkEndCommandBuffer(frameData.mainCmd); _assert_msg_(G3D, res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res)); From 93412aff3bfcebf7d97af5d25b04ee3823da907a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Aug 2019 23:07:58 +0200 Subject: [PATCH 41/63] Vulkan: Automatically merge render passes to the same target when possible. Should speed things up a bit on mobile in some games that do stupid things like GoW. Currently only enabled in GoW, but plan to enable this globally as it should be quite cheap when nothing is detected. --- Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + GPU/Vulkan/GPU_Vulkan.cpp | 3 ++ assets/compat.ini | 36 +++++++++++++ ext/native/thin3d/VulkanQueueRunner.cpp | 64 +++++++++++++++++++++++ ext/native/thin3d/VulkanQueueRunner.h | 56 ++++++++++++++++++++ ext/native/thin3d/VulkanRenderManager.cpp | 6 ++- 7 files changed, 166 insertions(+), 1 deletion(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 4763cf4c25..ff2d6d23fa 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -59,6 +59,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth); CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack); CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack); + CheckSetting(iniFile, gameID, "RenderPassMerge", &flags_.RenderPassMerge); CheckSetting(iniFile, gameID, "BlockTransferAllowCreateFB", &flags_.BlockTransferAllowCreateFB); CheckSetting(iniFile, gameID, "YugiohSaveFix", &flags_.YugiohSaveFix); CheckSetting(iniFile, gameID, "ForceUMDDelay", &flags_.ForceUMDDelay); diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 8632813e26..0938b4c731 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -59,6 +59,7 @@ struct CompatFlags { bool DisableAccurateDepth; bool MGS2AcidHack; bool SonicRivalsHack; + bool RenderPassMerge; bool BlockTransferAllowCreateFB; bool YugiohSaveFix; bool ForceUMDDelay; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 178faac8d6..0e55bbd84e 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -475,6 +475,9 @@ void GPU_Vulkan::InitDeviceObjects() { hacks |= QUEUE_HACK_MGS2_ACID; if (PSP_CoreParameter().compat.flags().SonicRivalsHack) hacks |= QUEUE_HACK_SONIC; + if (PSP_CoreParameter().compat.flags().RenderPassMerge) + hacks |= QUEUE_HACK_RENDERPASS_MERGE; + if (hacks) { rm->GetQueueRunner()->EnableHacks(hacks); } diff --git a/assets/compat.ini b/assets/compat.ini index 74e11597fd..96e5f172b1 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -633,3 +633,39 @@ NPUH10047 = true ULAS42214 = true ULJS19054 = true NPJH50184 = true + +[RenderPassMerge] +UCJS10114 = true +UCKS45084 = true +# GOW : Ghost of Sparta +UCUS98737 = true +UCAS40323 = true +NPHG00092 = true +NPEG00044 = true +NPEG00045 = true +NPJG00120 = true +NPUG80508 = true +UCJS10114 = true +UCES01401 = true +UCES01473 = true +# GOW : Ghost of Sparta Demo +NPEG90035 = true +NPUG70125 = true +NPJG90095 = true +# GOW : Chains Of Olympus +UCAS40198 = true +UCUS98653 = true +UCES00842 = true +ULJM05438 = true +ULJM05348 = true +UCKS45084 = true +NPUG80325 = true +NPEG00023 = true +NPHG00027 = true +NPHG00028 = true +NPJH50170 = true +UCET00844 = true +# GOW: Chains of Olympus Demo +UCUS98705 = true +UCED00971 = true +UCUS98713 = true diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 5b5d60c74c..a527bf30ee 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1,3 +1,4 @@ +#include #include "DataFormat.h" #include "VulkanQueueRunner.h" #include "VulkanRenderManager.h" @@ -438,6 +439,9 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st if (hacksEnabled_ & QUEUE_HACK_SONIC) { ApplySonicHack(steps); } + if (hacksEnabled_ & QUEUE_HACK_RENDERPASS_MERGE) { + ApplyRenderPassMerge(steps); + } } for (size_t i = 0; i < steps.size(); i++) { @@ -732,6 +736,66 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { return std::string(buffer); } +// Ideally, this should be cheap enough to be applied to all games. At least on mobile, it's pretty +// much a guaranteed neutral or win in terms of GPU power. However, dependency calculation really +// must be perfect! +void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { + return; + // First let's count how many times each framebuffer is rendered to. + // If it's more than one, let's do our best to merge them. This can help God of War quite a bit. + + std::map counts; + for (int i = 0; i < (int)steps.size(); i++) { + if (steps[i]->stepType == VKRStepType::RENDER) { + counts[steps[i]->render.framebuffer]++; + } + } + + // Now, let's go through the steps. If we find one that is rendered to more than once, + // we'll scan forward and slurp up any rendering that can be merged across. + for (int i = 0; i < (int)steps.size(); i++) { + if (steps[i]->stepType == VKRStepType::RENDER && counts[steps[i]->render.framebuffer] > 1) { + auto fb = steps[i]->render.framebuffer; + for (int j = i + 1; j < (int)steps.size(); j++) { + // If any other passes are reading from this framebuffer as-is, we cancel the scan. + switch (steps[j]->stepType) { + case VKRStepType::COPY: + if (steps[j]->copy.src == fb) { + // We're done. + goto done_fb; + } + break; + case VKRStepType::RENDER: + if (steps[j]->dependencies.contains(fb)) { + goto done_fb; + } + if (steps[j]->render.framebuffer == fb) { + // ok. Now, if it's a render, slurp up all the commands + // and kill the step. + // Also slurp up any pretransitions. + for (int k = 0; k < (int)steps[j]->preTransitions.size(); k++) { + steps[i]->preTransitions.push_back(steps[j]->preTransitions[k]); + } + for (int k = 0; k < (int)steps[j]->commands.size(); k++) { + steps[i]->commands.push_back(steps[j]->commands[k]); + } + steps[j]->stepType = VKRStepType::RENDER_SKIP; + } + // keep going. + break; + case VKRStepType::BLIT: + if (steps[j]->blit.src == fb) { + goto done_fb; + } + break; + } + } + done_fb: + ; + } + } +} + void VulkanQueueRunner::LogSteps(const std::vector &steps) { ILOG("======================================="); for (size_t i = 0; i < steps.size(); i++) { diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index b7f4c33090..8f15b8d0d8 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -13,6 +13,58 @@ struct VKRImage; enum { QUEUE_HACK_MGS2_ACID = 1, QUEUE_HACK_SONIC = 2, + // Killzone PR = 4. + QUEUE_HACK_RENDERPASS_MERGE = 8, +}; + +// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded. +template +struct TinySet { + ~TinySet() { delete slowLookup_; } + inline void insert(T t) { + // Fast linear scan. + for (int i = 0; i < fastCount; i++) { + if (fastLookup_[i] == t) + return; // We already have it. + } + // Fast insertion + if (fastCount < MaxFastSize) { + fastLookup_[fastCount++] = t; + return; + } + // Fall back to slow path. + insertSlow(t); + } + bool contains(T t) const { + for (int i = 0; i < fastCount; i++) { + if (fastLookup_[i] == t) + return true; + } + if (slowLookup_) { + for (auto x : *slowLookup_) { + if (x == t) + return true; + } + } + return false; + } + +private: + void insertSlow(T t) { + if (!slowLookup_) { + slowLookup_ = new std::vector(); + } else { + for (size_t i = 0; i < slowLookup_->size(); i++) { + if ((*slowLookup_)[i] == t) + return; + } + } + slowLookup_->push_back(t); + } + T fastLookup_[MaxFastSize]; + int fastCount = 0; + int slowCount = 0; + std::vector *slowLookup_ = nullptr; }; enum class VKRRenderCommand : uint8_t { @@ -109,9 +161,12 @@ struct TransitionRequest { struct VKRStep { VKRStep(VKRStepType _type) : stepType(_type) {} + ~VKRStep() {} + VKRStepType stepType; std::vector commands; std::vector preTransitions; + TinySet dependencies; union { struct { VKRFramebuffer *framebuffer; @@ -242,6 +297,7 @@ private: void ApplyMGSHack(std::vector &steps); void ApplySonicHack(std::vector &steps); + void ApplyRenderPassMerge(std::vector &steps); static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 77e66ed7b8..f9428cc462 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -858,7 +858,8 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { - // Mark the dependency and return the image. + _dbg_assert_(G3D, curRenderStep_ != nullptr); + // Mark the dependency, check for required transitions, and return the image. for (int i = (int)steps_.size() - 1; i >= 0; i--) { if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) { @@ -871,6 +872,9 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } } + // Track dependencies fully. + curRenderStep_->dependencies.insert(fb); + if (!curRenderStep_->preTransitions.empty() && curRenderStep_->preTransitions.back().fb == fb && curRenderStep_->preTransitions.back().targetLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { From 597b034b5c3f2a995886e01ba8f85051251be818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Aug 2019 23:16:37 +0200 Subject: [PATCH 42/63] Minor optimization --- ext/native/thin3d/VulkanQueueRunner.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index a527bf30ee..4021e93d62 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -773,12 +773,8 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { // ok. Now, if it's a render, slurp up all the commands // and kill the step. // Also slurp up any pretransitions. - for (int k = 0; k < (int)steps[j]->preTransitions.size(); k++) { - steps[i]->preTransitions.push_back(steps[j]->preTransitions[k]); - } - for (int k = 0; k < (int)steps[j]->commands.size(); k++) { - steps[i]->commands.push_back(steps[j]->commands[k]); - } + steps[i]->preTransitions.insert(steps[i]->preTransitions.end(), steps[j]->preTransitions.begin(), steps[j]->preTransitions.end()); + steps[i]->commands.insert(steps[i]->commands.end(), steps[j]->commands.begin(), steps[j]->commands.end()); steps[j]->stepType = VKRStepType::RENDER_SKIP; } // keep going. From 773cb5fa70ecb34e9a70b6ec855b0c2db5fd6eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 21:13:56 +0200 Subject: [PATCH 43/63] Stop renderpass merging at readbacks. --- ext/native/thin3d/VulkanQueueRunner.cpp | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 4021e93d62..b2468b556c 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -380,7 +380,6 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st // Planned optimizations: // * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes // as early as possible in the frame (Wipeout billboards). - // * Merge subsequent render passes to the same target that are interspersed with unrelated draws to other render targets (God of War). for (int j = 0; j < (int)steps.size(); j++) { if (steps[j]->stepType == VKRStepType::RENDER && @@ -721,10 +720,10 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { snprintf(buffer, sizeof(buffer), "Blit (%dx%d->%dx%d)", step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height); break; case VKRStepType::READBACK: - snprintf(buffer, sizeof(buffer), "Readback (%dx%d)", step.readback.srcRect.extent.width, step.readback.srcRect.extent.height); + snprintf(buffer, sizeof(buffer), "Readback (%dx%d, fb: %p)", step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, step.readback.src); break; case VKRStepType::READBACK_IMAGE: - snprintf(buffer, sizeof(buffer), "ReadbackImage"); + snprintf(buffer, sizeof(buffer), "ReadbackImage (%dx%d)", step.readback_image.srcRect.extent.width, step.readback_image.srcRect.extent.height); break; case VKRStepType::RENDER_SKIP: snprintf(buffer, sizeof(buffer), "(SKIPPED RenderPass)"); @@ -740,7 +739,6 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { // much a guaranteed neutral or win in terms of GPU power. However, dependency calculation really // must be perfect! void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { - return; // First let's count how many times each framebuffer is rendered to. // If it's more than one, let's do our best to merge them. This can help God of War quite a bit. @@ -784,6 +782,11 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { goto done_fb; } break; + case VKRStepType::READBACK: + if (steps[j]->readback.src == fb) { + goto done_fb; + } + break; } } done_fb: @@ -864,20 +867,20 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass) { ILOG("RenderPass End(%x)", fb); } -void VulkanQueueRunner::LogCopy(const VKRStep &pass) { - ILOG("Copy()"); +void VulkanQueueRunner::LogCopy(const VKRStep &step) { + ILOG("%s", StepToString(step).c_str()); } -void VulkanQueueRunner::LogBlit(const VKRStep &pass) { - ILOG("Blit()"); +void VulkanQueueRunner::LogBlit(const VKRStep &step) { + ILOG("%s", StepToString(step).c_str()); } -void VulkanQueueRunner::LogReadback(const VKRStep &pass) { - ILOG("Readback"); +void VulkanQueueRunner::LogReadback(const VKRStep &step) { + ILOG("%s", StepToString(step).c_str()); } -void VulkanQueueRunner::LogReadbackImage(const VKRStep &pass) { - ILOG("ReadbackImage"); +void VulkanQueueRunner::LogReadbackImage(const VKRStep &step) { + ILOG("%s", StepToString(step).c_str()); } void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { From 05b2e11c5c709dc076d7f803936795da55e3fea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 21:30:36 +0200 Subject: [PATCH 44/63] Vulkan: Prevent some bad merging of renderpasses. Thanks Unknown for writing it up. --- ext/native/thin3d/VulkanQueueRunner.cpp | 21 +++++++++++++++------ ext/native/thin3d/VulkanQueueRunner.h | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index b2468b556c..c92031149a 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -754,19 +754,18 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { for (int i = 0; i < (int)steps.size(); i++) { if (steps[i]->stepType == VKRStepType::RENDER && counts[steps[i]->render.framebuffer] > 1) { auto fb = steps[i]->render.framebuffer; + TinySet touchedFramebuffers; // must be the same fast-size as the dependencies TinySet for annoying reasons. for (int j = i + 1; j < (int)steps.size(); j++) { // If any other passes are reading from this framebuffer as-is, we cancel the scan. switch (steps[j]->stepType) { - case VKRStepType::COPY: - if (steps[j]->copy.src == fb) { - // We're done. - goto done_fb; - } - break; case VKRStepType::RENDER: if (steps[j]->dependencies.contains(fb)) { goto done_fb; } + // Prevent Unknown's example case from https://github.com/hrydgard/ppsspp/pull/12242 + if (steps[j]->dependencies.contains(touchedFramebuffers)) { + goto done_fb; + } if (steps[j]->render.framebuffer == fb) { // ok. Now, if it's a render, slurp up all the commands // and kill the step. @@ -775,8 +774,18 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { steps[i]->commands.insert(steps[i]->commands.end(), steps[j]->commands.begin(), steps[j]->commands.end()); steps[j]->stepType = VKRStepType::RENDER_SKIP; } + // Remember the framebuffer this wrote to. We can't merge with later passes that depend on these. + if (steps[j]->render.framebuffer != fb) { + touchedFramebuffers.insert(steps[j]->render.framebuffer); + } // keep going. break; + case VKRStepType::COPY: + if (steps[j]->copy.src == fb) { + // We're done. + goto done_fb; + } + break; case VKRStepType::BLIT: if (steps[j]->blit.src == fb) { goto done_fb; diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 8f15b8d0d8..3c840856c6 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -48,6 +48,20 @@ struct TinySet { } return false; } + bool contains(const TinySet &otherSet) { + // Awkward, kind of ruins the fun. + for (int i = 0; i < fastCount; i++) { + if (otherSet.contains(fastLookup_[i])) + return true; + } + if (slowLookup_) { + for (auto x : *slowLookup_) { + if (otherSet.contains(x)) + return true; + } + } + return false; + } private: void insertSlow(T t) { From 7ad10ff0ef58c13384b7ef44c85ef96e1db04887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Aug 2019 23:10:41 +0200 Subject: [PATCH 45/63] Vulkan profiling vis bugfix: Restore font scale after drawing. --- GPU/Vulkan/DebugVisVulkan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index 091f576679..8f2cc381a4 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -118,5 +118,6 @@ void DrawProfilerVis(UIContext *ui, GPUInterface *gpu) { Draw::DrawContext *draw = ui->GetDrawContext(); ui->SetFontScale(0.4f, 0.4f); ui->DrawTextShadow(text.c_str(), 10, 50, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ui->SetFontScale(1.0f, 1.0f); ui->Flush(); } From 7bb6febc32569dc473afb6d42fcf4e1eb1120eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Aug 2019 09:23:02 +0200 Subject: [PATCH 46/63] Add a few more checks. --- ext/native/thin3d/VulkanQueueRunner.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index c92031149a..8be1ca7120 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -741,7 +741,6 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { // First let's count how many times each framebuffer is rendered to. // If it's more than one, let's do our best to merge them. This can help God of War quite a bit. - std::map counts; for (int i = 0; i < (int)steps.size(); i++) { if (steps[i]->stepType == VKRStepType::RENDER) { @@ -778,20 +777,20 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { if (steps[j]->render.framebuffer != fb) { touchedFramebuffers.insert(steps[j]->render.framebuffer); } - // keep going. break; case VKRStepType::COPY: - if (steps[j]->copy.src == fb) { - // We're done. + if (steps[j]->copy.src == fb || steps[j]->copy.dst == fb) { goto done_fb; } break; case VKRStepType::BLIT: - if (steps[j]->blit.src == fb) { + if (steps[j]->blit.src == fb || steps[j]->blit.dst == fb) { goto done_fb; } break; case VKRStepType::READBACK: + // Not sure this has much effect, when executed READBACK is always the last step + // since we stall the GPU and wait immediately after. if (steps[j]->readback.src == fb) { goto done_fb; } From 3229b93673a69ca1cf6c96e4e8e0478933bebcb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Aug 2019 23:40:38 +0200 Subject: [PATCH 47/63] SasAudio: Cap the cycle estimate at 1200 to prevent crackling in FFT. Fixes #9956. --- Core/HW/SasAudio.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 2758759f2d..e36bdd1c1b 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -413,7 +413,9 @@ int SasInstance::EstimateMixUs() { } // Each voice costs extra time, and each byte of grain costs extra time. - return 20 + voicesPlayingCount * 68 + (grainSize * 60) / 100; + int cycles = 20 + voicesPlayingCount * 68 + (grainSize * 60) / 100; + // Cap to 1200 to fix FFT, see issue #9956. + return std::min(cycles, 1200); } void SasVoice::ReadSamples(s16 *output, int numSamples) { From d8db8ecfcc0de67fadc91bcee13b0ab532a5a734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 23 Aug 2019 00:11:13 +0200 Subject: [PATCH 48/63] Revert "GPU: Download CLUTs when loading at 256 stride." This reverts commit dc03050730ff5ce41c2b20433c11771589b49f2e. --- GPU/Common/FramebufferCommon.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index bdd4490275..6c900531e7 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -2083,11 +2083,6 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - if (w == 0 && h > 0) { - // Exactly aligned (means pixels == vfb->fb_stride, so modulus gave zero.) - w = std::min(vfb->fb_stride, (int)vfb->width); - } - // We might still have a pending draw to the fb in question, flush if so. FlushBeforeCopy(); From c619a20a65caf0fbf3f26fdc2118510dcbec58d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 23 Aug 2019 16:01:27 +0200 Subject: [PATCH 49/63] Fix display of memory view window on high-DPI displays. Fixes #11913. --- Windows/Debugger/Debugger_MemoryDlg.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Windows/Debugger/Debugger_MemoryDlg.cpp b/Windows/Debugger/Debugger_MemoryDlg.cpp index a60f3ebfec..6943a8b1a3 100644 --- a/Windows/Debugger/Debugger_MemoryDlg.cpp +++ b/Windows/Debugger/Debugger_MemoryDlg.cpp @@ -4,6 +4,7 @@ #include #include "..\resource.h" +#include "base/display.h" #include "util/text/utf8.h" #include "Core/Debugger/SymbolMap.h" @@ -218,6 +219,8 @@ void CMemoryDlg::Goto(u32 addr) void CMemoryDlg::Size() { + const float fontScale = 1.0f / g_dpi_scale_real_y; + RECT rc; GetClientRect(m_hDlg,&rc); int dw=rc.right-rc.left; @@ -225,8 +228,8 @@ void CMemoryDlg::Size() HWND memView = GetDlgItem(m_hDlg, IDC_MEMVIEW); HWND symList = GetDlgItem(m_hDlg, IDC_SYMBOLS); int wf = slRect.right-slRect.left; - int w = dw-3-wf; - int top = 48; + int w = dw - 3 * fontScale - wf; + int top = 48 * fontScale; MoveWindow(symList,0,top,wf,dh-top,TRUE); MoveWindow(memView,wf+4,top,w,dh-top,TRUE); } From 385579c5ad36716a816084a33a085fdef1515035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 23 Aug 2019 16:11:05 +0200 Subject: [PATCH 50/63] Android: Update the gradle version again. --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index dcf3372b38..6fe5d2708d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:3.5.0' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cd3f2c2a38..17c3841aef 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Apr 19 22:49:10 CEST 2019 +#Fri Aug 23 16:10:47 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip From 7196d6ac7e683c29eae52051b2e833f8a2762285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 23 Aug 2019 17:14:11 +0200 Subject: [PATCH 51/63] Default to Vulkan on Android Pie or later. Fixes #12228. --- Core/Config.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index ab9be6da50..69ee998692 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -574,8 +574,16 @@ static int DefaultGPUBackend() { if (DoesVersionMatchWindows(6, 2, 0, 0, true)) { return (int)GPUBackend::DIRECT3D11; } -#endif +#elif PPSSPP_PLATFORM(ANDROID) + // Default to Vulkan only on Pie (level 28) devices or newer. Drivers before Pie + // were generally too unreliable to default to (with some exceptions, of course). + if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 28) { + return (int)GPUBackend::VULKAN; + } +#else + // TODO: On some additional Linux platforms, we should also default to Vulkan. return (int)GPUBackend::OPENGL; +#endif } int Config::NextValidBackend() { From 31de5a50826d3a14c5180303518558d8e212263e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 23 Aug 2019 18:40:13 +0200 Subject: [PATCH 52/63] Improve an assert message that AkiraJkr saw. Fix a silly bug in the last commit. --- Core/MIPS/x86/RegCacheFPU.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/x86/RegCacheFPU.h b/Core/MIPS/x86/RegCacheFPU.h index dfd22d5c35..dd0916ebc9 100644 --- a/Core/MIPS/x86/RegCacheFPU.h +++ b/Core/MIPS/x86/RegCacheFPU.h @@ -126,11 +126,11 @@ public: const Gen::OpArg &R(int freg) const {return regs[freg].location;} const Gen::OpArg &V(int vreg) const { - _dbg_assert_msg_(JIT, vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VS instead)", vreg); + _dbg_assert_msg_(JIT, vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VS instead). pc=%08x", vreg, mips->pc); return vregs[vreg].location; } const Gen::OpArg &VS(const u8 *vs) const { - _dbg_assert_msg_(JIT, vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use V instead)", vs[0]); + _dbg_assert_msg_(JIT, vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use V instead). pc=%08x", vs[0], mips->pc); return vregs[vs[0]].location; } @@ -142,7 +142,7 @@ public: } Gen::X64Reg VX(int vreg) const { - _dbg_assert_msg_(JIT, vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VSX instead)", vreg); + _dbg_assert_msg_(JIT, vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VSX instead). pc=%08x", vreg, mips->pc); if (vregs[vreg].away && vregs[vreg].location.IsSimpleReg()) return vregs[vreg].location.GetSimpleReg(); PanicAlert("Not so simple - v%i", vreg); @@ -150,7 +150,7 @@ public: } Gen::X64Reg VSX(const u8 *vs) const { - _dbg_assert_msg_(JIT, vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use VX instead)", vs[0]); + _dbg_assert_msg_(JIT, vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use VX instead). pc=%08x", vs[0], mips->pc); if (vregs[vs[0]].away && vregs[vs[0]].location.IsSimpleReg()) return vregs[vs[0]].location.GetSimpleReg(); PanicAlert("Not so simple - v%i", vs[0]); From 088901324df79c19755e8fab214f619fcda17b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 24 Aug 2019 09:56:19 +0200 Subject: [PATCH 53/63] Missed this in the last commit, sigh --- Core/Config.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 69ee998692..b83d1bc893 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -580,10 +580,9 @@ static int DefaultGPUBackend() { if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 28) { return (int)GPUBackend::VULKAN; } -#else +#endif // TODO: On some additional Linux platforms, we should also default to Vulkan. return (int)GPUBackend::OPENGL; -#endif } int Config::NextValidBackend() { From e1164cfe4fc0ed8252549953b86d6cc5725ac418 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Thu, 22 Aug 2019 15:41:01 +0300 Subject: [PATCH 54/63] [Android] Updates for camera -rotate camera frames to follow display orientation -release camera resources when a game is closed -release camera resources when the activity is paused --- Core/HLE/sceUsbCam.cpp | 10 +- .../src/org/ppsspp/ppsspp/CameraHelper.java | 149 ++++++++++++++---- .../src/org/ppsspp/ppsspp/NativeActivity.java | 6 + 3 files changed, 133 insertions(+), 32 deletions(-) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index e1b5986e8d..7120c34896 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -116,7 +116,6 @@ static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) { } static int sceUsbCamPollReadVideoFrameEnd() { - INFO_LOG(HLE, "UNIMPL sceUsbCamPollReadVideoFrameEnd: %d", nextVideoFrame); return nextVideoFrame; } @@ -186,7 +185,12 @@ void Register_sceUsbCam() void Camera::pushCameraImage(long long length, unsigned char* image) { std::lock_guard lock(videoBufferMutex); - videoBufferLength = length; memset (videoBuffer, 0, sizeof(videoBuffer)); - memcpy (videoBuffer, image, length); + if (length > sizeof(videoBuffer)) { + videoBufferLength = 0; + ERROR_LOG(HLE, "pushCameraImage: length error"); + } else { + videoBufferLength = length; + memcpy (videoBuffer, image, length); + } } diff --git a/android/src/org/ppsspp/ppsspp/CameraHelper.java b/android/src/org/ppsspp/ppsspp/CameraHelper.java index ca4bd8894a..9979b81660 100644 --- a/android/src/org/ppsspp/ppsspp/CameraHelper.java +++ b/android/src/org/ppsspp/ppsspp/CameraHelper.java @@ -7,7 +7,12 @@ import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.graphics.YuvImage; import android.hardware.Camera; +import android.os.SystemClock; import android.util.Log; +import android.view.Display; +import android.view.Surface; +import android.view.WindowManager; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; @@ -15,67 +20,140 @@ import java.util.List; @TargetApi(23) @SuppressWarnings("deprecation") class CameraHelper { - private static final String TAG = "CameraHelper"; + private static final String TAG = CameraHelper.class.getSimpleName(); + private Display mDisplay; private Camera mCamera = null; - private int previewWidth = 0; - private int previewHeight = 0; - private long lastTime = 0; + private boolean mIsCameraRunning = false; + private int mCameraOrientation = 0; + private Camera.Size mPreviewSize = null; + private long mLastFrameTime = 0; private SurfaceTexture mSurfaceTexture; + private int getCameraRotation() { + int displayRotation = mDisplay.getRotation(); + int displayDegrees = 0; + switch (displayRotation) { + case Surface.ROTATION_0: displayDegrees = 0; break; + case Surface.ROTATION_90: displayDegrees = 90; break; + case Surface.ROTATION_180: displayDegrees = 180; break; + case Surface.ROTATION_270: displayDegrees = 270; break; + } + return (mCameraOrientation - displayDegrees + 360) % 360; + } + + static byte[] rotateNV21(final byte[] input, final int inWidth, final int inHeight, + final int outWidth, final int outHeight, final int rotation) { + + final int inFrameSize = inWidth * inHeight; + final int outFrameSize = outWidth * outHeight; + final byte[] output = new byte[outFrameSize + outFrameSize/2]; + + if (rotation == 0 || rotation == 180) { + final int crop_left = (inWidth - outWidth) / 2; + final int crop_top = (inHeight - outHeight) / 2; + for (int j = 0; j < outHeight; j++) { + final int yInCol = (crop_top + j) * inWidth + crop_left; + final int uvInCol = inFrameSize + ((crop_top + j) >> 1) * inWidth + crop_left; + final int jOut = rotation == 180 ? outHeight - j - 1 : j; + final int yOutCol = jOut * outWidth; + final int uvOutCol = outFrameSize + (jOut >> 1) * outWidth; + for (int i = 0; i < outWidth; i++) { + final int yIn = yInCol + i; + final int uIn = uvInCol + (i & ~1); + final int vIn = uIn + 1; + final int iOut = rotation == 180 ? outWidth - i - 1 : i; + final int yOut = yOutCol + iOut; + final int uOut = uvOutCol + (iOut & ~1); + final int vOut = uOut + 1; + output[yOut] = input[yIn]; + output[uOut] = input[uIn]; + output[vOut] = input[vIn]; + } + } + } else if (rotation == 90 || rotation == 270) { + int crop_left = (inWidth - outHeight) / 2; + int crop_top = (inHeight - outWidth) / 2; + for (int j = 0; j < outWidth; j++) { + final int yInCol = (crop_top + j) * inWidth + crop_left; + final int uvInCol = inFrameSize + ((crop_top + j) >> 1) * inWidth + crop_left; + final int iOut = rotation == 90 ? outWidth - j - 1 : j; + for (int i = 0; i < outHeight; i++) { + final int yIn = yInCol + i; + final int uIn = uvInCol + (i & ~1); + final int vIn = uIn + 1; + final int jOut = rotation == 270 ? outHeight - i - 1 : i; + final int yOut = jOut * outWidth + iOut; + final int uOut = outFrameSize + (jOut >> 1) * outWidth + (iOut & ~1); + final int vOut = uOut + 1; + output[yOut] = input[yIn]; + output[uOut] = input[uIn]; + output[vOut] = input[vIn]; + } + } + } + return output; + } + private Camera.PreviewCallback mPreviewCallback = new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] previewData, Camera camera) { - // throttle at 100 ms - long currentTime = System.currentTimeMillis(); - if (currentTime - lastTime < 100) { + // throttle at 66 ms + long currentTime = SystemClock.elapsedRealtime(); + if (currentTime - mLastFrameTime < 66) { return; } - lastTime = currentTime; - - YuvImage yuvImage = new YuvImage(previewData, ImageFormat.NV21, previewWidth, previewHeight, null); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mLastFrameTime = currentTime; // the expected values arrives in sceUsbCamSetupVideo int targetW = 480; int targetH = 272; - // crop to expected size and convert to Jpeg - Rect rect = new Rect( - (previewWidth - targetW) / 2, - (previewHeight - targetH) / 2, - previewWidth - (previewWidth - targetW) / 2, - previewHeight - (previewHeight - targetH) / 2 - ); - yuvImage.compressToJpeg(rect, 80, baos); + int cameraRotation = getCameraRotation(); + byte[] newPreviewData = rotateNV21(previewData, mPreviewSize.width, mPreviewSize.height, + targetW, targetH, cameraRotation); + YuvImage yuvImage = new YuvImage(newPreviewData, ImageFormat.NV21, targetW, targetH, null); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // convert to Jpeg + Rect crop = new Rect(0, 0, targetW, targetH); + yuvImage.compressToJpeg(crop, 80, baos); NativeApp.pushCameraImage(baos.toByteArray()); + try { + baos.close(); + } catch (IOException e) { + e.printStackTrace(); + } } }; @SuppressWarnings("unused") - CameraHelper(Context context) { + CameraHelper(final Context context) { + mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); mSurfaceTexture = new SurfaceTexture(10); } void startCamera() { Log.d(TAG, "startCamera"); try { + Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); + Camera.getCameraInfo(0, info); + mCameraOrientation = info.orientation; + mCamera = Camera.open(); Camera.Parameters param = mCamera.getParameters(); // Set preview size List previewSizes = param.getSupportedPreviewSizes(); - previewWidth = previewSizes.get(0).width; - previewHeight = previewSizes.get(0).height; + mPreviewSize = previewSizes.get(0); for (int i = 0; i < previewSizes.size(); i++) { Log.d(TAG, "getSupportedPreviewSizes[" + i + "]: " + previewSizes.get(i).height + " " + previewSizes.get(i).width); if (previewSizes.get(i).width <= 640 && previewSizes.get(i).height <= 480) { - previewWidth = previewSizes.get(i).width; - previewHeight = previewSizes.get(i).height; + mPreviewSize = previewSizes.get(i); break; } } - Log.d(TAG, "setPreviewSize(" + previewWidth + ", " + previewHeight + ")"); - param.setPreviewSize(previewWidth, previewHeight); + Log.d(TAG, "setPreviewSize(" + mPreviewSize.width + ", " + mPreviewSize.height + ")"); + param.setPreviewSize(mPreviewSize.width, mPreviewSize.height); // Set preview FPS int[] fps; @@ -94,18 +172,31 @@ class CameraHelper { mCamera.setPreviewTexture(mSurfaceTexture); mCamera.setPreviewCallback(mPreviewCallback); mCamera.startPreview(); + mIsCameraRunning = true; } catch (IOException e) { Log.e(TAG, "Cannot start camera: " + e.toString()); } } - void stopCamera() { - Log.d(TAG, "stopCamera"); - if (mCamera != null) { + void pause() { + if (mIsCameraRunning && mCamera != null) { + Log.d(TAG, "pause"); mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; } } + + void resume() { + if (mIsCameraRunning) { + Log.d(TAG, "resume"); + startCamera(); + } + } + + void stopCamera() { + pause(); + mIsCameraRunning = false; + } } diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 1ced155cf3..c32d0321e3 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -740,6 +740,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C Log.e(TAG, "mGLSurfaceView really shouldn't be null in onPause"); } } + mCameraHelper.pause(); Log.i(TAG, "onPause completed"); } @@ -779,6 +780,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mSurfaceView.onResume(); } } + mCameraHelper.resume(); gainAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.resume(); @@ -1335,6 +1337,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Only keep the screen bright ingame. window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } + } else if (command.equals("event")) { + if (params.equals("exitgame")) { + mCameraHelper.stopCamera(); + } } return false; } From b114984c887008e9daae24188ff1c06d87ae5c23 Mon Sep 17 00:00:00 2001 From: Florin9doi Date: Sun, 25 Aug 2019 13:04:33 +0300 Subject: [PATCH 55/63] [Android] Fix code style --- Core/HLE/sceUsbCam.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 7120c34896..ae0599c97d 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -116,6 +116,7 @@ static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) { } static int sceUsbCamPollReadVideoFrameEnd() { + VERBOSE_LOG(HLE, "UNIMPL sceUsbCamPollReadVideoFrameEnd: %d", nextVideoFrame); return nextVideoFrame; } @@ -185,12 +186,12 @@ void Register_sceUsbCam() void Camera::pushCameraImage(long long length, unsigned char* image) { std::lock_guard lock(videoBufferMutex); - memset (videoBuffer, 0, sizeof(videoBuffer)); + memset(videoBuffer, 0, sizeof(videoBuffer)); if (length > sizeof(videoBuffer)) { videoBufferLength = 0; - ERROR_LOG(HLE, "pushCameraImage: length error"); + ERROR_LOG(HLE, "pushCameraImage: length error: %lld", length); } else { videoBufferLength = length; - memcpy (videoBuffer, image, length); + memcpy(videoBuffer, image, length); } } From 2e5065a6f5a14ffd6374a17145295328dd417895 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 Aug 2019 12:55:10 -0700 Subject: [PATCH 56/63] http: Improve EBOOT.PBP handling. At least when it works, we should use a better filename. --- Core/WebServer.cpp | 150 +++++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index b9fd5169f0..53225472ff 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -120,81 +120,100 @@ bool RemoteISOFileSupported(const std::string &filename) { return true; } return false; - } -static void RegisterDiscHandlers(http::Server *http, std::unordered_map *paths) { - for (std::string filename : g_Config.recentIsos) { +static std::string RemotePathForRecent(const std::string &filename) { #ifdef _WIN32 - static const std::string sep = "\\/"; + static const std::string sep = "\\/"; #else - static const std::string sep = "/"; + static const std::string sep = "/"; #endif - size_t basepos = filename.find_last_of(sep); - std::string basename = "/" + (basepos == filename.npos ? filename : filename.substr(basepos + 1)); + size_t basepos = filename.find_last_of(sep); + std::string basename = "/" + (basepos == filename.npos ? filename : filename.substr(basepos + 1)); - // Let's not serve directories, since they won't work. Only single files. - // Maybe can do PBPs and other files later. Would be neat to stream virtual disc filesystems. - if (RemoteISOFileSupported(basename)) { - (*paths)[ReplaceAll(basename, " ", "%20")] = filename; - } + if (basename == "/EBOOT.PBP") { + // Go up one more folder. + size_t nextpos = filename.find_last_of(sep, basepos - 1); + basename = "/" + (nextpos == filename.npos ? filename : filename.substr(nextpos + 1)); } - auto handler = [paths](const http::Request &request) { - std::string filename = (*paths)[request.resource()]; - s64 sz = File::GetFileSize(filename); + // Let's not serve directories, since they won't work. Only single files. + // Maybe can do PBPs and other files later. Would be neat to stream virtual disc filesystems. + if (RemoteISOFileSupported(basename)) { + return ReplaceAll(basename, " ", "%20"); + } + return ""; +} - std::string range; - if (request.Method() == http::RequestHeader::HEAD) { - request.WriteHttpResponseHeader("1.0", 200, sz, "application/octet-stream", "Accept-Ranges: bytes\r\n"); - } else if (request.GetHeader("range", &range)) { - s64 begin = 0, last = 0; - if (sscanf(range.c_str(), "bytes=%lld-%lld", &begin, &last) != 2) { - request.WriteHttpResponseHeader("1.0", 400, -1, "text/plain"); - request.Out()->Push("Could not understand range request."); - return; - } - - if (begin < 0 || begin > last || last >= sz) { - request.WriteHttpResponseHeader("1.0", 416, -1, "text/plain"); - request.Out()->Push("Range goes outside of file."); - return; - } - - FILE *fp = File::OpenCFile(filename, "rb"); - if (!fp || fseek(fp, begin, SEEK_SET) != 0) { - request.WriteHttpResponseHeader("1.0", 500, -1, "text/plain"); - request.Out()->Push("File access failed."); - if (fp) { - fclose(fp); - } - return; - } - - s64 len = last - begin + 1; - char contentRange[1024]; - sprintf(contentRange, "Content-Range: bytes %lld-%lld/%lld\r\n", begin, last, sz); - request.WriteHttpResponseHeader("1.0", 206, len, "application/octet-stream", contentRange); - - const size_t CHUNK_SIZE = 16 * 1024; - char *buf = new char[CHUNK_SIZE]; - for (s64 pos = 0; pos < len; pos += CHUNK_SIZE) { - s64 chunklen = std::min(len - pos, (s64)CHUNK_SIZE); - if (fread(buf, chunklen, 1, fp) != 1) - break; - request.Out()->Push(buf, chunklen); - } - fclose(fp); - delete [] buf; - request.Out()->Flush(); - } else { - request.WriteHttpResponseHeader("1.0", 418, -1, "text/plain"); - request.Out()->Push("This server only supports range requests."); +static std::string LocalFromRemotePath(const std::string &path) { + for (const std::string &filename : g_Config.recentIsos) { + std::string basename = RemotePathForRecent(filename); + if (basename == path) { + return filename; } - }; + } + return ""; +} - for (auto pair : *paths) { - http->RegisterHandler(pair.first.c_str(), handler); +static void DiscHandler(const http::Request &request) { + std::string filename = LocalFromRemotePath(request.resource()); + s64 sz = File::GetFileSize(filename); + + std::string range; + if (request.Method() == http::RequestHeader::HEAD) { + request.WriteHttpResponseHeader("1.0", 200, sz, "application/octet-stream", "Accept-Ranges: bytes\r\n"); + } else if (request.GetHeader("range", &range)) { + s64 begin = 0, last = 0; + if (sscanf(range.c_str(), "bytes=%lld-%lld", &begin, &last) != 2) { + request.WriteHttpResponseHeader("1.0", 400, -1, "text/plain"); + request.Out()->Push("Could not understand range request."); + return; + } + + if (begin < 0 || begin > last || last >= sz) { + request.WriteHttpResponseHeader("1.0", 416, -1, "text/plain"); + request.Out()->Push("Range goes outside of file."); + return; + } + + FILE *fp = File::OpenCFile(filename, "rb"); + if (!fp || fseek(fp, begin, SEEK_SET) != 0) { + request.WriteHttpResponseHeader("1.0", 500, -1, "text/plain"); + request.Out()->Push("File access failed."); + if (fp) { + fclose(fp); + } + return; + } + + s64 len = last - begin + 1; + char contentRange[1024]; + sprintf(contentRange, "Content-Range: bytes %lld-%lld/%lld\r\n", begin, last, sz); + request.WriteHttpResponseHeader("1.0", 206, len, "application/octet-stream", contentRange); + + const size_t CHUNK_SIZE = 16 * 1024; + char *buf = new char[CHUNK_SIZE]; + for (s64 pos = 0; pos < len; pos += CHUNK_SIZE) { + s64 chunklen = std::min(len - pos, (s64)CHUNK_SIZE); + if (fread(buf, chunklen, 1, fp) != 1) + break; + request.Out()->Push(buf, chunklen); + } + fclose(fp); + delete[] buf; + request.Out()->Flush(); + } else { + request.WriteHttpResponseHeader("1.0", 418, -1, "text/plain"); + request.Out()->Push("This server only supports range requests."); + } +} + +static void RegisterDiscHandlers(http::Server *http) { + for (const std::string &filename : g_Config.recentIsos) { + std::string basename = RemotePathForRecent(filename); + if (!basename.empty()) { + http->RegisterHandler(basename.c_str(), &DiscHandler); + } } } @@ -202,10 +221,9 @@ static void ExecuteWebServer() { setCurrentThreadName("HTTPServer"); auto http = new http::Server(new threading::NewThreadExecutor()); - std::unordered_map discPaths; if (serverFlags & (int)WebServerFlags::DISCS) { - RegisterDiscHandlers(http, &discPaths); + RegisterDiscHandlers(http); } if (serverFlags & (int)WebServerFlags::DEBUGGER) { http->RegisterHandler("/debugger", &HandleDebuggerRequest); From b2005f87e6fd3c38804ef671fdaf5e54770f61d4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 Aug 2019 13:08:04 -0700 Subject: [PATCH 57/63] http: Show recent games in same order. Rather than sorting alphabetically by filename. --- Core/WebServer.cpp | 51 +++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 53225472ff..7a239ffadc 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -155,8 +155,7 @@ static std::string LocalFromRemotePath(const std::string &path) { return ""; } -static void DiscHandler(const http::Request &request) { - std::string filename = LocalFromRemotePath(request.resource()); +static void DiscHandler(const http::Request &request, const std::string &filename) { s64 sz = File::GetFileSize(filename); std::string range; @@ -208,26 +207,50 @@ static void DiscHandler(const http::Request &request) { } } -static void RegisterDiscHandlers(http::Server *http) { - for (const std::string &filename : g_Config.recentIsos) { - std::string basename = RemotePathForRecent(filename); - if (!basename.empty()) { - http->RegisterHandler(basename.c_str(), &DiscHandler); +static void HandleListing(const http::Request &request) { + request.WriteHttpResponseHeader("1.0", 200, -1, "text/plain"); + request.Out()->Printf("/\n"); + if (serverFlags & (int)WebServerFlags::DISCS) { + // List the current discs in their recent order. + for (const std::string &filename : g_Config.recentIsos) { + std::string basename = RemotePathForRecent(filename); + if (!basename.empty()) { + request.Out()->Printf("%s\n", basename.c_str()); + } } } + if (serverFlags & (int)WebServerFlags::DEBUGGER) { + request.Out()->Printf("/debugger\n"); + } +} + +static void HandleFallback(const http::Request &request) { + std::string filename = LocalFromRemotePath(request.resource()); + if (!filename.empty()) { + DiscHandler(request, filename); + } else { + static const std::string payload = "404 not found\r\n"; + request.WriteHttpResponseHeader("1.0", 404, (int)payload.size(), "text/plain"); + request.Out()->Push(payload); + } +} + +static void ForwardDebuggerRequest(const http::Request &request) { + if (serverFlags & (int)WebServerFlags::DEBUGGER) { + HandleDebuggerRequest(request); + } else { + HandleFallback(request); + } } static void ExecuteWebServer() { setCurrentThreadName("HTTPServer"); auto http = new http::Server(new threading::NewThreadExecutor()); - - if (serverFlags & (int)WebServerFlags::DISCS) { - RegisterDiscHandlers(http); - } - if (serverFlags & (int)WebServerFlags::DEBUGGER) { - http->RegisterHandler("/debugger", &HandleDebuggerRequest); - } + http->RegisterHandler("/", &HandleListing); + // This lists all the (current) recent ISOs. + http->SetFallbackHandler(&HandleFallback); + http->RegisterHandler("/debugger", &ForwardDebuggerRequest); if (!http->Listen(g_Config.iRemoteISOPort)) { if (!http->Listen(0)) { From 5f7872885d7b12be9d2db9a3c12ad7486e83174d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 25 Aug 2019 13:14:49 -0700 Subject: [PATCH 58/63] http: Remove web server restart handling. Now it serves the response dynamically, so it doesn't need to restart. --- Core/WebServer.cpp | 44 ++++++++++++-------------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 7a239ffadc..3bec6d622a 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -37,7 +37,6 @@ enum class ServerStatus { STARTING, RUNNING, STOPPING, - RESTARTING, }; static const char *REPORT_HOSTNAME = "report.ppsspp.org"; @@ -53,15 +52,6 @@ static void UpdateStatus(ServerStatus s) { serverStatus = s; } -static bool UpdateStatus(ServerStatus s, ServerStatus old) { - std::lock_guard guard(serverStatusLock); - if (serverStatus == old) { - serverStatus = s; - return true; - } - return false; -} - static ServerStatus RetrieveStatus() { std::lock_guard guard(serverStatusLock); return serverStatus; @@ -225,14 +215,17 @@ static void HandleListing(const http::Request &request) { } static void HandleFallback(const http::Request &request) { - std::string filename = LocalFromRemotePath(request.resource()); - if (!filename.empty()) { - DiscHandler(request, filename); - } else { - static const std::string payload = "404 not found\r\n"; - request.WriteHttpResponseHeader("1.0", 404, (int)payload.size(), "text/plain"); - request.Out()->Push(payload); + if (serverFlags & (int)WebServerFlags::DISCS) { + std::string filename = LocalFromRemotePath(request.resource()); + if (!filename.empty()) { + DiscHandler(request, filename); + return; + } } + + static const std::string payload = "404 not found\r\n"; + request.WriteHttpResponseHeader("1.0", 404, (int)payload.size(), "text/plain"); + request.Out()->Push(payload); } static void ForwardDebuggerRequest(const http::Request &request) { @@ -278,24 +271,16 @@ static void ExecuteWebServer() { StopAllDebuggers(); delete http; - // Move to STARTING to lock flags/STOPPING. - if (UpdateStatus(ServerStatus::STARTING, ServerStatus::RESTARTING)) { - ExecuteWebServer(); - } else { - UpdateStatus(ServerStatus::STOPPED); - } + UpdateStatus(ServerStatus::STOPPED); } bool StartWebServer(WebServerFlags flags) { std::lock_guard guard(serverStatusLock); switch (serverStatus) { case ServerStatus::RUNNING: - case ServerStatus::RESTARTING: if ((serverFlags & (int)flags) == (int)flags) { - // Already running those flags. return false; } - serverStatus = ServerStatus::RESTARTING; serverFlags |= (int)flags; return true; @@ -313,24 +298,19 @@ bool StartWebServer(WebServerFlags flags) { bool StopWebServer(WebServerFlags flags) { std::lock_guard guard(serverStatusLock); - if (serverStatus != ServerStatus::RUNNING && serverStatus != ServerStatus::RESTARTING) { + if (serverStatus != ServerStatus::RUNNING) { return false; } serverFlags &= ~(int)flags; if (serverFlags == 0) { serverStatus = ServerStatus::STOPPING; - } else { - serverStatus = ServerStatus::RESTARTING; } return true; } bool WebServerStopping(WebServerFlags flags) { std::lock_guard guard(serverStatusLock); - if (serverStatus == ServerStatus::RESTARTING) { - return (serverFlags & (int)flags) == 0; - } return serverStatus == ServerStatus::STOPPING; } From 5a68fc29c928d4b2cd98c669f03a5018d90a4208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Aug 2019 21:14:01 +0200 Subject: [PATCH 59/63] sceUsbCam: Heap-allocate the buffer. --- Core/HLE/sceKernel.cpp | 4 ++++ Core/HLE/sceUsbCam.cpp | 35 ++++++++++++++++++++++++++--------- Core/HLE/sceUsbCam.h | 3 +++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index bf409a247c..c375c59a04 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -81,6 +81,7 @@ #include "sceHeap.h" #include "sceDmac.h" #include "sceMp4.h" +#include "sceUsbCam.h" #include "../Util/PPGeDraw.h" @@ -143,6 +144,7 @@ void __KernelInit() __AudioCodecInit(); __VideoPmpInit(); __UsbGpsInit(); + __UsbCamInit(); SaveState::Init(); // Must be after IO, as it may create a directory Reporting::Init(); @@ -166,6 +168,8 @@ void __KernelShutdown() hleCurrentThreadName = NULL; kernelObjects.Clear(); + __UsbCamShutdown(); + __AudioCodecShutdown(); __VideoPmpShutdown(); __AACShutdown(); diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index ae0599c97d..04e81fd3a7 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include #include "base/NativeApp.h" @@ -29,9 +30,25 @@ PspUsbCamSetupVideoParam videoParam; unsigned int videoBufferLength = 0; unsigned int nextVideoFrame = 0; -unsigned char videoBuffer[40 * 1000]; +uint8_t *videoBuffer; std::mutex videoBufferMutex; +enum { + VIDEO_BUFFER_SIZE = 40 * 1000, +}; + +void __UsbCamInit() { + videoBuffer = new uint8_t[VIDEO_BUFFER_SIZE]; +} + +void __UsbCamShutdown() { + delete[] videoBuffer; + videoBuffer = nullptr; +} + +// TODO: Technically, we should store the videoBuffer into the savestate, if this +// module has been initialized. + static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupMic"); if (Memory::IsValidRange(paramAddr, sizeof(micParam))) { @@ -74,6 +91,7 @@ static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - size: %d", videoParam.size); INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - resolution: %d", videoParam.resolution); + INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - framesize: %d", videoParam.framesize); return 0; } @@ -96,20 +114,19 @@ static int sceUsbCamAutoImageReverseSW(int rev) { static int sceUsbCamReadVideoFrameBlocking(u32 bufAddr, u32 size) { std::lock_guard lock(videoBufferMutex); - for (unsigned int i = 0; i < videoBufferLength && i < size; i++) { - if (Memory::IsValidAddress(bufAddr + i)) { - Memory::Write_U8(videoBuffer[i], bufAddr + i); - } + + u32 transferSize = std::min(videoBufferLength, size); + if (Memory::IsValidRange(bufAddr, size)) { + Memory::Memcpy(bufAddr, videoBuffer, transferSize); } return videoBufferLength; } static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) { std::lock_guard lock(videoBufferMutex); - for (unsigned int i = 0; i < videoBufferLength && i < size; i++) { - if (Memory::IsValidAddress(bufAddr + i)) { - Memory::Write_U8(videoBuffer[i], bufAddr + i); - } + u32 transferSize = std::min(videoBufferLength, size); + if (Memory::IsValidRange(bufAddr, size)) { + Memory::Memcpy(bufAddr, videoBuffer, transferSize); } nextVideoFrame = videoBufferLength; return 0; diff --git a/Core/HLE/sceUsbCam.h b/Core/HLE/sceUsbCam.h index ef088fe91d..ebb5388db6 100644 --- a/Core/HLE/sceUsbCam.h +++ b/Core/HLE/sceUsbCam.h @@ -19,6 +19,9 @@ void Register_sceUsbCam(); +void __UsbCamInit(); +void __UsbCamShutdown(); + namespace Camera { void pushCameraImage(long long length, unsigned char *image); } From 402b6b245796e24b5a70a3bc7413e80e6cd287bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 26 Aug 2019 00:09:59 +0200 Subject: [PATCH 60/63] Improve some logging (%08x for addresses) --- Core/HLE/sceJpeg.cpp | 19 +++++++++++-------- Core/HLE/sceUsb.cpp | 10 +++++----- Core/HLE/sceUsbCam.cpp | 8 ++++---- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Core/HLE/sceJpeg.cpp b/Core/HLE/sceJpeg.cpp index 790bcb0bb6..7fcd0a4295 100644 --- a/Core/HLE/sceJpeg.cpp +++ b/Core/HLE/sceJpeg.cpp @@ -23,6 +23,7 @@ #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceJpeg.h" #include "Core/HLE/sceMpeg.h" +#include "GPU/GPUCommon.h" #include "Core/MemMap.h" #include "Core/Reporting.h" @@ -112,7 +113,9 @@ static void __JpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferW static int sceJpegMJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth) { __JpegCsc(imageAddr, yCbCrAddr, widthHeight, bufferWidth); - DEBUG_LOG(ME, "sceJpegMJpegCsc(%i, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth); + int width = (widthHeight >> 16) & 0xFFF; + int height = widthHeight & 0xFFF; + DEBUG_LOG(ME, "sceJpegMJpegCsc(%08x, %08x, (%dx%d), %i)", imageAddr, yCbCrAddr, width, height, bufferWidth); return 0; } @@ -165,7 +168,7 @@ static int sceJpegDecodeMJpeg(u32 jpegAddr, int jpegSize, u32 imageAddr, int dht return 0; } - DEBUG_LOG(ME, "sceJpegDecodeMJpeg(%i, %i, %i, %i)", jpegAddr, jpegSize, imageAddr, dhtMode); + DEBUG_LOG(ME, "sceJpegDecodeMJpeg(%08x, %i, %08x, %i)", jpegAddr, jpegSize, imageAddr, dhtMode); return __DecodeJpeg(jpegAddr, jpegSize, imageAddr); } @@ -180,19 +183,19 @@ static int sceJpegDecodeMJpegSuccessively(u32 jpegAddr, int jpegSize, u32 imageA return 0; } - DEBUG_LOG(ME, "sceJpegDecodeMJpegSuccessively(%i, %i, %i, %i)", jpegAddr, jpegSize, imageAddr, dhtMode); + DEBUG_LOG(ME, "sceJpegDecodeMJpegSuccessively(%08x, %i, %08x, %i)", jpegAddr, jpegSize, imageAddr, dhtMode); return __DecodeJpeg(jpegAddr, jpegSize, imageAddr); } static int sceJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth, int colourInfo) { if (bufferWidth < 0 || widthHeight < 0){ - WARN_LOG(ME, "sceJpegCsc(%i, %i, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth, colourInfo); + WARN_LOG(ME, "sceJpegCsc(%08x, %08x, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth, colourInfo); return ERROR_JPEG_INVALID_VALUE; } __JpegCsc(imageAddr, yCbCrAddr, widthHeight, bufferWidth); - DEBUG_LOG(ME, "sceJpegCsc(%i, %i, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth, colourInfo); + DEBUG_LOG(ME, "sceJpegCsc(%08x, %08x, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth, colourInfo); return 0; } @@ -257,7 +260,7 @@ static int sceJpegGetOutputInfo(u32 jpegAddr, int jpegSize, u32 colourInfoAddr, return getYCbCrBufferSize(0, 0); } - DEBUG_LOG(ME, "sceJpegGetOutputInfo(%i, %i, %i, %i)", jpegAddr, jpegSize, colourInfoAddr, dhtMode); + DEBUG_LOG(ME, "sceJpegGetOutputInfo(%08x, %i, %08x, %i)", jpegAddr, jpegSize, colourInfoAddr, dhtMode); return __JpegGetOutputInfo(jpegAddr, jpegSize, colourInfoAddr); } @@ -345,7 +348,7 @@ static int sceJpegDecodeMJpegYCbCr(u32 jpegAddr, int jpegSize, u32 yCbCrAddr, in return getWidthHeight(0, 0); } - DEBUG_LOG(ME, "sceJpegDecodeMJpegYCbCr(%i, %i, %i, %i, %i)", jpegAddr, jpegSize, yCbCrAddr, yCbCrSize, dhtMode); + DEBUG_LOG(ME, "sceJpegDecodeMJpegYCbCr(%08x, %i, %08x, %i, %i)", jpegAddr, jpegSize, yCbCrAddr, yCbCrSize, dhtMode); return __JpegDecodeMJpegYCbCr(jpegAddr, jpegSize, yCbCrAddr); } @@ -355,7 +358,7 @@ static int sceJpegDecodeMJpegYCbCrSuccessively(u32 jpegAddr, int jpegSize, u32 y return getWidthHeight(0, 0); } - DEBUG_LOG(ME, "sceJpegDecodeMJpegYCbCrSuccessively(%i, %i, %i, %i, %i)", jpegAddr, jpegSize, yCbCrAddr, yCbCrSize, dhtMode); + DEBUG_LOG(ME, "sceJpegDecodeMJpegYCbCrSuccessively(%08x, %i, %08x, %i, %i)", jpegAddr, jpegSize, yCbCrAddr, yCbCrSize, dhtMode); // Do as same way as sceJpegDecodeMJpegYCbCr() but with smaller block size return __JpegDecodeMJpegYCbCr(jpegAddr, jpegSize, yCbCrAddr); } diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index 7ddf16ec61..361f55f26b 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -63,13 +63,13 @@ void __UsbDoState(PointerWrap &p) } static int sceUsbStart(const char* driverName, u32 argsSize, u32 argsPtr) { - ERROR_LOG(HLE, "UNIMPL sceUsbStart(%s, %i, %08x)", driverName, argsSize, argsPtr); + INFO_LOG(HLE, "sceUsbStart(%s, %i, %08x)", driverName, argsSize, argsPtr); usbStarted = true; return 0; } static int sceUsbStop(const char* driverName, u32 argsSize, u32 argsPtr) { - ERROR_LOG(HLE, "UNIMPL sceUsbStop(%s, %i, %08x)", driverName, argsSize, argsPtr); + INFO_LOG(HLE, "sceUsbStop(%s, %i, %08x)", driverName, argsSize, argsPtr); usbStarted = false; return 0; } @@ -83,18 +83,18 @@ static int sceUsbGetState() { | (usbConnected ? USB_STATUS_CONNECTED : USB_STATUS_DISCONNECTED) | (usbActivated ? USB_STATUS_ACTIVATED : USB_STATUS_DEACTIVATED); } - ERROR_LOG(HLE, "UNIMPL sceUsbGetState: 0x%x", state); + INFO_LOG(HLE, "sceUsbGetState: 0x%x", state); return state; } static int sceUsbActivate(u32 pid) { - ERROR_LOG(HLE, "UNIMPL sceUsbActivate(%i)", pid); + INFO_LOG(HLE, "sceUsbActivate(%i)", pid); usbActivated = true; return 0; } static int sceUsbDeactivate(u32 pid) { - ERROR_LOG(HLE, "UNIMPL sceUsbDeactivate(%i)", pid); + INFO_LOG(HLE, "sceUsbDeactivate(%i)", pid); usbActivated = false; return 0; } diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 04e81fd3a7..b2be019245 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -84,14 +84,14 @@ static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { Memory::ReadStruct(paramAddr, &videoParam); } + INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - size: %d", videoParam.size); + INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - resolution: %d", videoParam.resolution); + INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - framesize: %d", videoParam.framesize); + std::lock_guard lock(videoBufferMutex); videoBufferLength = sizeof(sceUsbCamDummyImage); memset(videoBuffer, 0, sizeof(videoBuffer)); memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage)); - - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - size: %d", videoParam.size); - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - resolution: %d", videoParam.resolution); - INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - framesize: %d", videoParam.framesize); return 0; } From a17fe334a140fa4f4fc950706fd2c58034e62803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 26 Aug 2019 18:25:05 +0200 Subject: [PATCH 61/63] MJPEG in sceJpeg: Notify the framebuffer manager that there's been a picture written to memory. Fixes part of #8521 --- Core/HLE/sceJpeg.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/HLE/sceJpeg.cpp b/Core/HLE/sceJpeg.cpp index 7fcd0a4295..f532233203 100644 --- a/Core/HLE/sceJpeg.cpp +++ b/Core/HLE/sceJpeg.cpp @@ -116,6 +116,7 @@ static int sceJpegMJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bu int width = (widthHeight >> 16) & 0xFFF; int height = widthHeight & 0xFFF; DEBUG_LOG(ME, "sceJpegMJpegCsc(%08x, %08x, (%dx%d), %i)", imageAddr, yCbCrAddr, width, height, bufferWidth); + gpu->NotifyVideoUpload(imageAddr, width * height * 4, width, GE_FORMAT_8888); return 0; } From 25dfc9ea9c47ed8a7832456c4c527d0dc3bf36e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 26 Aug 2019 23:25:33 +0200 Subject: [PATCH 62/63] sceUsbCam: Fix some bugs my last commits introduced. --- Core/HLE/sceUsbCam.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index b2be019245..7667cc335f 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -90,7 +90,7 @@ static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { std::lock_guard lock(videoBufferMutex); videoBufferLength = sizeof(sceUsbCamDummyImage); - memset(videoBuffer, 0, sizeof(videoBuffer)); + memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage)); return 0; } @@ -203,10 +203,10 @@ void Register_sceUsbCam() void Camera::pushCameraImage(long long length, unsigned char* image) { std::lock_guard lock(videoBufferMutex); - memset(videoBuffer, 0, sizeof(videoBuffer)); - if (length > sizeof(videoBuffer)) { + memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); + if (length > VIDEO_BUFFER_SIZE) { videoBufferLength = 0; - ERROR_LOG(HLE, "pushCameraImage: length error: %lld", length); + ERROR_LOG(HLE, "pushCameraImage: length error: %lld > %d", length, VIDEO_BUFFER_SIZE); } else { videoBufferLength = length; memcpy(videoBuffer, image, length); From c8730c933d5149ea0f2b993502f82592a2450a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 26 Aug 2019 23:56:42 +0200 Subject: [PATCH 63/63] Remove deleted GE dumps from Recent. Fixes a part of #11904 (the recent list wasn't updated properly). --- UI/GameInfoCache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 7070139a4c..2ccdd7f6ed 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -92,6 +92,7 @@ bool GameInfo::Delete() { { const std::string &fileToRemove = filePath_; File::Delete(fileToRemove); + g_Config.RemoveRecent(filePath_); return true; }