From 00daa00ae5e9bffed4676bde6c19f3e9920630e1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 29 Aug 2018 22:07:27 -0700 Subject: [PATCH 01/37] TexCache: Keep maxSeenV on clut variants in sync. This ensures that we detect changes properly even when they are outside the max V used with one CLUT. Fixes #9355, enemy fade out in FF2. --- GPU/Common/TextureCacheCommon.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index cadf9abf0d..171c0bb297 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -257,6 +257,19 @@ void TextureCacheCommon::UpdateSamplingParams(TexCacheEntry &entry, SamplerCache void TextureCacheCommon::UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode) { // If the texture is >= 512 pixels tall... if (entry->dim >= 0x900) { + if (entry->cluthash != 0 && entry->maxSeenV == 0) { + const u64 cachekeyMin = (u64)(entry->addr & 0x3FFFFFFF) << 32; + const u64 cachekeyMax = cachekeyMin + (1ULL << 32); + for (auto it = cache_.lower_bound(cachekeyMin), end = cache_.upper_bound(cachekeyMax); it != end; ++it) { + // They should all be the same, just make sure we take any that has already increased. + // This is for a new texture. + if (it->second->maxSeenV != 0) { + entry->maxSeenV = it->second->maxSeenV; + break; + } + } + } + // Texture scale/offset and gen modes don't apply in through. // So we can optimize how much of the texture we look at. if (throughMode) { @@ -274,6 +287,16 @@ void TextureCacheCommon::UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode) // TODO: We could tell for texcoord UV gen, and apply scale to max? entry->maxSeenV = 512; } + + // We need to keep all CLUT variants in sync so we detect changes properly. + // See HandleTextureChange / STATUS_CLUT_RECHECK. + if (entry->cluthash != 0) { + const u64 cachekeyMin = (u64)(entry->addr & 0x3FFFFFFF) << 32; + const u64 cachekeyMax = cachekeyMin + (1ULL << 32); + for (auto it = cache_.lower_bound(cachekeyMin), end = cache_.upper_bound(cachekeyMax); it != end; ++it) { + it->second->maxSeenV = entry->maxSeenV; + } + } } } From 1b79924038f134d22e3a18a41ac30643ada7fd08 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 29 Aug 2018 22:09:10 -0700 Subject: [PATCH 02/37] Debugger: Fix texture readback in GLES. Was using the wrong size, causing the buffer not to be allocated large enough. This caused crashes sometimes. --- ext/native/thin3d/GLQueueRunner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index d3ad94095e..c8380d012f 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -1166,7 +1166,7 @@ void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { int pixelStride = pass.readback_image.srcRect.w; glPixelStorei(GL_PACK_ALIGNMENT, 4); - GLRect2D rect = pass.readback.srcRect; + GLRect2D rect = pass.readback_image.srcRect; int size = 4 * rect.w * rect.h; if (size > readbackBufferSize_) { From 9324d7c3861c914ea4ea49f48c4df906a4744dbc Mon Sep 17 00:00:00 2001 From: weihuoya Date: Thu, 30 Aug 2018 22:51:55 +0800 Subject: [PATCH 03/37] glslang options for vulkan --- Common/Vulkan/VulkanContext.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 3b43e80c02..aeb29f20ea 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -1040,6 +1040,8 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, glslang::TProgram program; const char *shaderStrings[1]; + EProfile profile = ECoreProfile; + int defaultVersion = 450; TBuiltInResource Resources; init_resources(Resources); @@ -1052,7 +1054,7 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, shaderStrings[0] = pshader; shader.setStrings(shaderStrings, 1); - if (!shader.parse(&Resources, 100, false, messages)) { + if (!shader.parse(&Resources, defaultVersion, profile, false, true, messages)) { puts(shader.getInfoLog()); puts(shader.getInfoDebugLog()); if (errorMessage) { @@ -1076,7 +1078,11 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, } // Can't fail, parsing worked, "linking" worked. - glslang::GlslangToSpv(*program.getIntermediate(stage), spirv); + glslang::SpvOptions options; + options.disableOptimizer = false; + options.optimizeSize = false; + options.generateDebugInfo = false; + glslang::GlslangToSpv(*program.getIntermediate(stage), spirv, &options); return true; } From 45753d87de3546a8b7118cdfce9e3fbff6b2041d Mon Sep 17 00:00:00 2001 From: AreaScout Date: Thu, 30 Aug 2018 15:05:35 +0000 Subject: [PATCH 04/37] Add: EGL info for GLES on Linux --- ext/native/gfx_es2/gpu_features.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index 01b8c6c570..2d7e00d16a 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -418,6 +418,15 @@ void CheckGLExtensions() { } else { g_all_egl_extensions = ""; } +#elif defined(USING_GLES2) && defined(__linux__) + const char *eglString = eglQueryString(NULL, EGL_EXTENSIONS); + if (eglString) { + g_all_egl_extensions = std::string(eglString); + g_all_egl_extensions.append(" "); + g_all_egl_extensions.append(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS)); + } else { + g_all_egl_extensions = ""; + } #endif glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &gl_extensions.maxVertexTextureUnits); From 8e6a1bc84905c19d15f072905ef27da1b25cd5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Aug 2018 19:49:33 +0200 Subject: [PATCH 05/37] Set a minimum of 1x1 for Android text bitmap measurement. Should fix some crashes seen on Google Play --- android/src/org/ppsspp/ppsspp/TextRenderer.java | 10 ++++++---- ext/native/thin3d/thin3d_vulkan.cpp | 3 +-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index 69d411e32d..7ecdeb9a7f 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -54,6 +54,12 @@ public class TextRenderer { total.x = Math.max(sz.x, total.x); } total.y = (int) (p.descent() - p.ascent()) * lines.length + 2; + // Returning a 0 size can create problems when the caller + // uses the measurement to create a texture. + if (total.x < 1) + total.x = 1; + if (total.y < 1) + total.y = 1; return total; } @@ -67,10 +73,6 @@ public class TextRenderer { int w = s.x; int h = s.y; - if (w == 0) - w = 1; - if (h == 0) - h = 1; Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bmp); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 8f6906d839..43e48283ea 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1078,12 +1078,11 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { } Texture *VKContext::CreateTexture(const TextureDesc &desc) { - if (!push_) { + if (!push_ || !renderManager_.GetInitCmd()) { // Too early! Fail. ELOG("Can't create textures before the first frame has started."); return nullptr; } - _assert_(renderManager_.GetInitCmd()); return new VKTexture(vulkan_, renderManager_.GetInitCmd(), push_, desc, allocator_); } From 16d7a8098045057fa83fef7f5799b8f9d36f8b8c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 30 Aug 2018 21:00:21 -0700 Subject: [PATCH 06/37] GPU: Clear alpha more consistently from 565. Before, the backends all did different things. Now they are more in sync, but Vulkan still behaves slightly differently. Fixes #11326. --- GPU/D3D11/FramebufferManagerD3D11.cpp | 38 +++++++++++++++++++++------ GPU/D3D11/FramebufferManagerD3D11.h | 3 +++ GPU/Directx9/FramebufferDX9.cpp | 29 +++++++++++++++----- GPU/Directx9/FramebufferDX9.h | 2 ++ GPU/GLES/FramebufferManagerGLES.cpp | 11 +++----- GPU/Vulkan/FramebufferVulkan.cpp | 8 +++--- 6 files changed, 65 insertions(+), 26 deletions(-) diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index f2910cbec6..bb6ee6b660 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -129,6 +129,21 @@ FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw) vb.BindFlags = D3D11_BIND_CONSTANT_BUFFER; ASSERT_SUCCESS(device_->CreateBuffer(&vb, nullptr, &postConstants_)); + D3D11_TEXTURE2D_DESC desc{}; + desc.CPUAccessFlags = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.Width = 1; + desc.Height = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.MipLevels = 1; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &nullTexture_)); + ASSERT_SUCCESS(device_->CreateShaderResourceView(nullTexture_, nullptr, &nullTextureView_)); + uint32_t nullData[1]{}; + context_->UpdateSubresource(nullTexture_, 0, nullptr, nullData, 1, 0); + ShaderTranslationInit(); CompilePostShader(); @@ -178,6 +193,11 @@ FramebufferManagerD3D11::~FramebufferManagerD3D11() { stencilUploadInputLayout_->Release(); if (stencilValueBuffer_) stencilValueBuffer_->Release(); + + if (nullTextureView_) + nullTextureView_->Release(); + if (nullTexture_) + nullTexture_->Release(); } void FramebufferManagerD3D11::SetTextureCache(TextureCacheD3D11 *tc) { @@ -437,31 +457,33 @@ void FramebufferManagerD3D11::ReformatFramebufferFrom(VirtualFramebuffer *vfb, G // Technically, we should at this point re-interpret the bytes of the old format to the new. // That might get tricky, and could cause unnecessary slowness in some games. // For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts. - // (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.) + // (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.) // // The best way to do this may ultimately be to create a new FBO (combine with any resize?) // and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex // to exactly reproduce in 4444 and 8888 formats. if (old == GE_FORMAT_565) { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::KEEP }); + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); - // TODO: There's no way this does anything useful :( - context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0xFF); - context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0], nullptr, 0xFFFFFFFF); + context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0xFF); + context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[D3D11_COLOR_WRITE_ENABLE_ALPHA], nullptr, 0xFFFFFFFF); context_->RSSetState(stockD3D11.rasterStateNoCull); context_->IASetInputLayout(quadInputLayout_); context_->PSSetShader(quadPixelShader_, nullptr, 0); context_->VSSetShader(quadVertexShader_, nullptr, 0); context_->IASetVertexBuffers(0, 1, &fsQuadBuffer_, &quadStride_, &quadOffset_); + context_->PSSetSamplers(0, 1, &stockD3D11.samplerPoint2DClamp); + context_->PSSetShaderResources(0, 1, &nullTextureView_); shaderManagerD3D11_->DirtyLastShader(); D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)vfb->renderWidth, (float)vfb->renderHeight, 0.0f, 1.0f }; context_->RSSetViewports(1, &vp); context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); context_->Draw(4, 0); - } - RebindFramebuffer(); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE); + textureCache_->ForgetLastTexture(); + + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE); + } } static void CopyPixelDepthOnly(u32 *dstp, const u32 *srcp, size_t c) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index d73b772b62..33f9bdbf36 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -107,6 +107,9 @@ private: ID3D11Buffer *stencilValueBuffer_ = nullptr; ID3D11DepthStencilState *stencilMaskStates_[256]{}; + ID3D11Texture2D *nullTexture_ = nullptr; + ID3D11ShaderResourceView *nullTextureView_ = nullptr; + TextureCacheD3D11 *textureCacheD3D11_; ShaderManagerD3D11 *shaderManagerD3D11_; DrawEngineD3D11 *drawEngineD3D11_; diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index a66faf39d1..88bddb3bee 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -100,6 +100,19 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } device_->CreateVertexDeclaration(g_FramebufferVertexElements, &pFramebufferVertexDecl); + + + int usage = 0; + D3DPOOL pool = D3DPOOL_MANAGED; + if (deviceEx_) { + pool = D3DPOOL_DEFAULT; + usage = D3DUSAGE_DYNAMIC; + } + HRESULT hr = device_->CreateTexture(1, 1, 1, usage, D3DFMT_A8R8G8B8, pool, &nullTex_, nullptr); + D3DLOCKED_RECT rect; + nullTex_->LockRect(0, &rect, nullptr, D3DLOCK_DISCARD); + memset(rect.pBits, 0, 4); + nullTex_->UnlockRect(0); } FramebufferManagerDX9::~FramebufferManagerDX9() { @@ -125,6 +138,8 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { if (stencilUploadVS_) { stencilUploadVS_->Release(); } + if (nullTex_) + nullTex_->Release(); } void FramebufferManagerDX9::SetTextureCache(TextureCacheDX9 *tc) { @@ -307,24 +322,24 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { return; } - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::KEEP }); - // Technically, we should at this point re-interpret the bytes of the old format to the new. // That might get tricky, and could cause unnecessary slowness in some games. // For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts. - // (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.) + // (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.) // // The best way to do this may ultimately be to create a new FBO (combine with any resize?) // and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex // to exactly reproduce in 4444 and 8888 formats. if (old == GE_FORMAT_565) { + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); + dxstate.scissorTest.disable(); dxstate.depthWrite.set(FALSE); dxstate.colorMask.set(false, false, false, true); dxstate.stencilFunc.set(D3DCMP_ALWAYS, 0, 0); dxstate.stencilMask.set(0xFF); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); float coord[20] = { -1.0f,-1.0f,0, 0,0, @@ -338,7 +353,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { device_->SetPixelShader(pFramebufferPixelShader); device_->SetVertexShader(pFramebufferVertexShader); shaderManagerDX9_->DirtyLastShader(); - device_->SetTexture(0, nullptr); + device_->SetTexture(0, nullTex_); D3DVIEWPORT9 vp{ 0, 0, (DWORD)vfb->renderWidth, (DWORD)vfb->renderHeight, 0.0f, 1.0f }; device_->SetViewport(&vp); @@ -349,9 +364,9 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { ERROR_LOG_REPORT(G3D, "ReformatFramebufferFrom() failed: %08x", hr); } dxstate.viewport.restore(); - } - RebindFramebuffer(); + textureCache_->ForgetLastTexture(); + } } static void CopyPixelDepthOnly(u32 *dstp, const u32 *srcp, size_t c) { diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 4c1498b786..5a67b34bf6 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -104,6 +104,8 @@ private: LPDIRECT3DVERTEXSHADER9 stencilUploadVS_ = nullptr; bool stencilUploadFailed_ = false; + LPDIRECT3DTEXTURE9 nullTex_ = nullptr; + TextureCacheDX9 *textureCacheDX9_; ShaderManagerDX9 *shaderManagerDX9_; DrawEngineDX9 *drawEngineD3D9_; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 81b2498346..6ddf06b3bd 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -466,20 +466,17 @@ void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GE // Technically, we should at this point re-interpret the bytes of the old format to the new. // That might get tricky, and could cause unnecessary slowness in some games. // For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts. - // (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.) + // (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.) // // The best way to do this may ultimately be to create a new FBO (combine with any resize?) // and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex // to exactly reproduce in 4444 and 8888 formats. if (old == GE_FORMAT_565) { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); - gstate_c.Dirty(DIRTY_BLEND_STATE); - } else { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }); + // Clear alpha and stencil. + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); + render_->Clear(0, 0.0f, 0, GL_COLOR_BUFFER_BIT, 0x8, 0, 0, 0, 0); } - - RebindFramebuffer(); } void FramebufferManagerGLES::BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) { diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 9665019ffa..7851238090 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -364,16 +364,16 @@ void FramebufferManagerVulkan::ReformatFramebufferFrom(VirtualFramebuffer *vfb, // Technically, we should at this point re-interpret the bytes of the old format to the new. // That might get tricky, and could cause unnecessary slowness in some games. // For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts. - // (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.) + // (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.) // // The best way to do this may ultimately be to create a new FBO (combine with any resize?) // and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex // to exactly reproduce in 4444 and 8888 formats. if (old == GE_FORMAT_565) { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); - } else { - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }); + // TODO: To match other backends, would be ideal to clear alpha only and not color. + // But probably doesn't matter that much... + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); } } From 2704b09dfd36e485986ecc01cda3cda0f9f497d3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 30 Aug 2018 21:17:30 -0700 Subject: [PATCH 07/37] UI: Load post shader names on non-Windows early. This way the ini "name" is used, rather than the stored code for it. Fixes #11015. --- UI/GameSettingsScreen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5e289b6151..f93783317c 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -121,6 +121,8 @@ static std::string PostShaderTranslateName(const char *value) { } void GameSettingsScreen::CreateViews() { + ReloadAllPostShaderInfo(); + if (editThenRestore_) { g_Config.loadGameConfig(gameID_); } From a4c0640f01db713ab5c9571d467e8075ca7307cd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 08:32:03 -0700 Subject: [PATCH 08/37] GE Debugger: Factor out host calls some. Moving more of this to cross platform for the web debugger. --- CMakeLists.txt | 2 ++ Core/Host.h | 2 -- GPU/Common/TextureCacheCommon.cpp | 8 ++--- GPU/D3D11/DrawEngineD3D11.cpp | 8 ++--- GPU/D3D11/GPU_D3D11.cpp | 4 +-- GPU/Debugger/Debugger.cpp | 48 +++++++++++++++++++++++++++++ GPU/Debugger/Debugger.h | 33 ++++++++++++++++++++ GPU/Directx9/DrawEngineDX9.cpp | 4 +-- GPU/Directx9/GPU_DX9.cpp | 3 +- GPU/GLES/DrawEngineGLES.cpp | 6 ++-- GPU/GLES/GPU_GLES.cpp | 4 +-- GPU/GPU.vcxproj | 2 ++ GPU/GPU.vcxproj.filters | 6 ++++ GPU/GPUCommon.cpp | 9 +++--- GPU/Software/SoftGpu.cpp | 4 +-- GPU/Software/TransformUnit.cpp | 5 ++- GPU/Vulkan/DrawEngineVulkan.cpp | 4 +-- GPU/Vulkan/GPU_Vulkan.cpp | 5 ++- UWP/GPU_UWP/GPU_UWP.vcxproj | 2 ++ UWP/GPU_UWP/GPU_UWP.vcxproj.filters | 14 +++++++-- Windows/GEDebugger/GEDebugger.cpp | 34 ++++++++------------ Windows/WindowsHost.h | 2 -- android/jni/Android.mk | 1 + libretro/Makefile.common | 3 +- 24 files changed, 149 insertions(+), 64 deletions(-) create mode 100644 GPU/Debugger/Debugger.cpp create mode 100644 GPU/Debugger/Debugger.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ff09b4c9bd..b787be7a77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1357,6 +1357,8 @@ set(GPU_SOURCES GPU/Common/SplineCommon.h GPU/Debugger/Breakpoints.cpp GPU/Debugger/Breakpoints.h + GPU/Debugger/Debugger.cpp + GPU/Debugger/Debugger.h GPU/Debugger/Record.cpp GPU/Debugger/Record.h GPU/Debugger/Stepping.cpp diff --git a/Core/Host.h b/Core/Host.h index b06473c4dc..d5b0dc9a7b 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -51,11 +51,9 @@ public: virtual void SetWindowTitle(const char *message) {} // While debugging is active, it's perfectly fine for these to block. - virtual bool GPUDebuggingActive() { return false; } virtual void GPUNotifyCommand(u32 pc) {} virtual void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {} virtual void GPUNotifyDraw() {} - virtual void GPUNotifyTextureAttachment(u32 addr) {} virtual bool CanCreateShortcut() {return false;} virtual bool CreateDesktopShortcut(std::string argumentPath, std::string title) {return false;} diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 171c0bb297..1409a6b403 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -20,7 +20,6 @@ #include "Common/ColorConv.h" #include "Common/MemoryUtil.h" #include "Core/Config.h" -#include "Core/Host.h" #include "Core/Reporting.h" #include "Core/System.h" #include "GPU/Common/FramebufferCommon.h" @@ -28,6 +27,7 @@ #include "GPU/Common/TextureDecoder.h" #include "GPU/Common/ShaderId.h" #include "GPU/Common/GPUStateUtils.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/GPUState.h" #include "GPU/GPUInterface.h" @@ -708,7 +708,7 @@ void TextureCacheCommon::AttachFramebufferValid(TexCacheEntry *entry, VirtualFra entry->maxLevel = 0; fbTexInfo_[cachekey] = fbInfo; framebuffer->last_frame_attached = gpuStats.numFlips; - host->GPUNotifyTextureAttachment(entry->addr); + GPUDebug::NotifyTextureAttachment(entry->addr); } else if (entry->framebuffer == framebuffer) { framebuffer->last_frame_attached = gpuStats.numFlips; } @@ -727,7 +727,7 @@ void TextureCacheCommon::AttachFramebufferInvalid(TexCacheEntry *entry, VirtualF entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; entry->maxLevel = 0; fbTexInfo_[cachekey] = fbInfo; - host->GPUNotifyTextureAttachment(entry->addr); + GPUDebug::NotifyTextureAttachment(entry->addr); } } @@ -740,7 +740,7 @@ void TextureCacheCommon::DetachFramebuffer(TexCacheEntry *entry, u32 address, Vi // Otherwise we never recreate the texture. entry->hash ^= 1; fbTexInfo_.erase(cachekey); - host->GPUNotifyTextureAttachment(entry->addr); + GPUDebug::NotifyTextureAttachment(entry->addr); } } diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index fb7b001b7b..eac5cf078f 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -22,7 +22,6 @@ #include "Common/MemoryUtil.h" #include "Core/MemMap.h" -#include "Core/Host.h" #include "Core/System.h" #include "Core/Reporting.h" #include "Core/Config.h" @@ -34,10 +33,10 @@ #include "GPU/Common/TextureDecoder.h" #include "GPU/Common/SplineCommon.h" - #include "GPU/Common/TransformCommon.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/SoftwareTransformCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/D3D11/FramebufferManagerD3D11.h" #include "GPU/D3D11/TextureCacheD3D11.h" #include "GPU/D3D11/DrawEngineD3D11.h" @@ -690,10 +689,7 @@ rotateVBO: gstate_c.vertBounds.maxU = 0; gstate_c.vertBounds.maxV = 0; -#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) - // We only support GPU debugging on Windows, and that's the only use case for this. - host->GPUNotifyDraw(); -#endif + GPUDebug::NotifyDraw(); } void DrawEngineD3D11::TessellationDataTransferD3D11::PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) { diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 8ad9ebd11a..a23eb2059f 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -45,7 +45,6 @@ #include "Core/Debugger/Breakpoints.h" #include "Core/MemMapHelpers.h" #include "Core/MIPS/MIPS.h" -#include "Core/Host.h" #include "Core/Config.h" #include "Core/Reporting.h" #include "Core/System.h" @@ -55,6 +54,7 @@ #include "GPU/GeDisasm.h" #include "GPU/Common/FramebufferCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/D3D11/ShaderManagerD3D11.h" #include "GPU/D3D11/GPU_D3D11.h" #include "GPU/D3D11/FramebufferManagerD3D11.h" @@ -254,7 +254,7 @@ void GPU_D3D11::BeginFrame() { void GPU_D3D11::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { // TODO: Some games like Spongebob - Yellow Avenger, never change framebuffer, they blit to it. // So breaking on frames doesn't work. Might want to move this to sceDisplay vsync. - host->GPUNotifyDisplay(framebuf, stride, format); + GPUDebug::NotifyDisplay(framebuf, stride, format); framebufferManagerD3D11_->SetDisplayFramebuffer(framebuf, stride, format); } diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp new file mode 100644 index 0000000000..8f2445c512 --- /dev/null +++ b/GPU/Debugger/Debugger.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Host.h" +#include "GPU/Debugger/Debugger.h" + +namespace GPUDebug { + +static bool active = false; + +void SetActive(bool flag) { + active = flag; +} + +bool IsActive() { + return active; +} + +void NotifyCommand(u32 pc) { + host->GPUNotifyCommand(pc); +} + +void NotifyDraw() { + host->GPUNotifyDraw(); +} + +void NotifyDisplay(u32 framebuf, u32 stride, int format) { + host->GPUNotifyDisplay(framebuf, stride, format); +} + +void NotifyTextureAttachment(u32 texaddr) { +} + +} diff --git a/GPU/Debugger/Debugger.h b/GPU/Debugger/Debugger.h new file mode 100644 index 0000000000..3c1fe9a062 --- /dev/null +++ b/GPU/Debugger/Debugger.h @@ -0,0 +1,33 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Common/CommonTypes.h" + +namespace GPUDebug { + +void SetActive(bool flag); +bool IsActive(); + +// While debugging is active, these may block. +void NotifyCommand(u32 pc); +void NotifyDraw(); +void NotifyDisplay(u32 framebuf, u32 stride, int format); +void NotifyTextureAttachment(u32 texaddr); + +} diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index de1a85d093..e4f24703ee 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -20,7 +20,6 @@ #include "Common/MemoryUtil.h" #include "Core/MemMap.h" -#include "Core/Host.h" #include "Core/System.h" #include "Core/Reporting.h" #include "Core/Config.h" @@ -36,6 +35,7 @@ #include "GPU/Common/TransformCommon.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/SoftwareTransformCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/Directx9/TextureCacheDX9.h" #include "GPU/Directx9/DrawEngineDX9.h" #include "GPU/Directx9/ShaderManagerDX9.h" @@ -621,7 +621,7 @@ rotateVBO: gstate_c.vertBounds.maxU = 0; gstate_c.vertBounds.maxV = 0; - host->GPUNotifyDraw(); + GPUDebug::NotifyDraw(); } void DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index afd5a86d10..154a5e44d6 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -39,6 +39,7 @@ #include "GPU/GeDisasm.h" #include "GPU/Common/FramebufferCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/Directx9/ShaderManagerDX9.h" #include "GPU/Directx9/GPU_DX9.h" #include "GPU/Directx9/FramebufferDX9.h" @@ -230,7 +231,7 @@ void GPU_DX9::BeginFrame() { } void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { - host->GPUNotifyDisplay(framebuf, stride, format); + GPUDebug::NotifyDisplay(framebuf, stride, format); framebufferManagerDX9_->SetDisplayFramebuffer(framebuf, stride, format); } diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 3880edc83b..a8857b17bc 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -20,7 +20,6 @@ #include "Common/MemoryUtil.h" #include "Core/MemMap.h" -#include "Core/Host.h" #include "Core/System.h" #include "Core/Reporting.h" #include "Core/Config.h" @@ -37,6 +36,7 @@ #include "GPU/Common/SplineCommon.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/SoftwareTransformCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/GLES/FragmentTestCacheGLES.h" #include "GPU/GLES/StateMappingGLES.h" #include "GPU/GLES/TextureCacheGLES.h" @@ -648,9 +648,7 @@ rotateVBO: gstate_c.vertBounds.maxU = 0; gstate_c.vertBounds.maxV = 0; -#ifndef MOBILE_DEVICE - host->GPUNotifyDraw(); -#endif + GPUDebug::NotifyDraw(); } bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 5eca0e9296..2a75ead250 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -35,7 +35,7 @@ #include "GPU/ge_constants.h" #include "GPU/GeDisasm.h" #include "GPU/Common/FramebufferCommon.h" - +#include "GPU/Debugger/Debugger.h" #include "GPU/GLES/ShaderManagerGLES.h" #include "GPU/GLES/GPU_GLES.h" #include "GPU/GLES/FramebufferManagerGLES.h" @@ -443,7 +443,7 @@ void GPU_GLES::BeginFrame() { } void GPU_GLES::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { - host->GPUNotifyDisplay(framebuf, stride, format); + GPUDebug::NotifyDisplay(framebuf, stride, format); framebufferManagerGL_->SetDisplayFramebuffer(framebuf, stride, format); } diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 296a2081b0..b531442bb8 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -217,6 +217,7 @@ + @@ -320,6 +321,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 7dc2f25cad..1710c3f790 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -273,6 +273,9 @@ Vulkan + + Debugger + @@ -542,5 +545,8 @@ Vulkan + + Debugger + \ No newline at end of file diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 69d733835b..fed71166ae 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -28,6 +28,7 @@ #include "GPU/Common/FramebufferCommon.h" #include "GPU/Common/SplineCommon.h" #include "GPU/Common/TextureCacheCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/Debugger/Record.h" const CommonCommandTableEntry commonCommandTable[] = { @@ -933,7 +934,7 @@ bool GPUCommon::InterpretList(DisplayList &list) { gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING; debugRecording_ = GPURecord::IsActive(); - const bool useDebugger = host->GPUDebuggingActive() || debugRecording_; + const bool useDebugger = GPUDebug::IsActive() || debugRecording_; const bool useFastRunLoop = !dumpThisFrame_ && !useDebugger; while (gpuState == GPUSTATE_RUNNING) { { @@ -1035,7 +1036,7 @@ void GPUCommon::SlowRunLoop(DisplayList &list) const bool dumpThisFrame = dumpThisFrame_; while (downcount > 0) { - host->GPUNotifyCommand(list.pc); + GPUDebug::NotifyCommand(list.pc); GPURecord::NotifyCommand(list.pc); u32 op = Memory::ReadUnchecked_U32(list.pc); u32 cmd = op >> 24; @@ -1586,10 +1587,8 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { if (!g_Config.bSoftwareSkinning) vtypeCheckMask = 0xFFFFFFFF; -#ifndef MOBILE_DEVICE - if (debugRecording_ || host->GPUDebuggingActive()) + if (debugRecording_ || GPUDebug::IsActive()) goto bail; -#endif while (src != stall) { uint32_t data = *src; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 381a40403a..2673ef4ab2 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -23,7 +23,6 @@ #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/Debugger/Breakpoints.h" -#include "Core/Host.h" #include "Core/MemMap.h" #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceGe.h" @@ -39,6 +38,7 @@ #include "GPU/Software/TransformUnit.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/FramebufferCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/Debugger/Record.h" const int FB_WIDTH = 480; @@ -141,7 +141,7 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for displayFramebuf_ = (framebuf & 0xFF000000) == 0 ? 0x44000000 | framebuf : framebuf; displayStride_ = stride; displayFormat_ = format; - host->GPUNotifyDisplay(framebuf, stride, format); + GPUDebug::NotifyDisplay(framebuf, stride, format); } // Copies RGBA8 data from RAM to the currently bound render target. diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index 05f1853119..1d9eef5a13 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -16,13 +16,12 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/MemoryUtil.h" -#include "Core/Host.h" #include "Core/Config.h" #include "GPU/GPUState.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/SplineCommon.h" - +#include "GPU/Debugger/Debugger.h" #include "GPU/Software/TransformUnit.h" #include "GPU/Software/Clipper.h" #include "GPU/Software/Lighting.h" @@ -490,7 +489,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy break; } - host->GPUNotifyDraw(); + GPUDebug::NotifyDraw(); } // TODO: This probably is not the best interface. diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0da871ba3e..16f0c495fb 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -25,7 +25,6 @@ #include "Common/MemoryUtil.h" #include "Core/MemMap.h" -#include "Core/Host.h" #include "Core/System.h" #include "Core/Reporting.h" #include "Core/Config.h" @@ -44,6 +43,7 @@ #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/SoftwareTransformCommon.h" #include "GPU/Common/DrawEngineCommon.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/TextureCacheVulkan.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" @@ -976,7 +976,7 @@ void DrawEngineVulkan::DoFlush() { gstate_c.vertBounds.maxU = 0; gstate_c.vertBounds.maxV = 0; - host->GPUNotifyDraw(); + GPUDebug::NotifyDraw(); } void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 69cd57feb6..18aaa60b2e 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -26,7 +26,6 @@ #include "Core/Config.h" #include "Core/Debugger/Breakpoints.h" #include "Core/MemMapHelpers.h" -#include "Core/Host.h" #include "Core/Config.h" #include "Core/Reporting.h" #include "Core/System.h" @@ -36,7 +35,7 @@ #include "GPU/ge_constants.h" #include "GPU/GeDisasm.h" #include "GPU/Common/FramebufferCommon.h" - +#include "GPU/Debugger/Debugger.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Vulkan/GPU_Vulkan.h" #include "GPU/Vulkan/FramebufferVulkan.h" @@ -405,7 +404,7 @@ void GPU_Vulkan::UpdateVsyncInterval(bool force) { } void GPU_Vulkan::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { - host->GPUNotifyDisplay(framebuf, stride, format); + GPUDebug::NotifyDisplay(framebuf, stride, format); framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format); } diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 0d1bc445ea..f3cfa762bf 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -332,6 +332,7 @@ + @@ -390,6 +391,7 @@ + diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters index 641d4feef0..6108deb70b 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters @@ -150,13 +150,18 @@ Debugger + + Debugger + Debugger Common - + + Software + Debugger @@ -291,10 +296,15 @@ Debugger + + Debugger + Debugger - + + Software + Debugger diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index ad68557f64..635ce7d220 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -39,6 +39,7 @@ #include "GPU/Common/GPUStateUtils.h" #include "GPU/GPUState.h" #include "GPU/Debugger/Breakpoints.h" +#include "GPU/Debugger/Debugger.h" #include "GPU/Debugger/Record.h" #include "GPU/Debugger/Stepping.h" #include @@ -49,8 +50,6 @@ const int POPUP_SUBMENU_ID_GEDBG_PREVIEW = 10; using namespace GPUBreakpoints; using namespace GPUStepping; -static bool attached = false; - static BreakNextType breakNext = BREAK_NONE; enum PrimaryDisplayType { @@ -627,7 +626,7 @@ void CGEDebugger::SavePosition() } void CGEDebugger::SetBreakNext(BreakNextType type) { - attached = true; + GPUDebug::SetActive(true); SetupPreviews(); breakNext = type; @@ -657,7 +656,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { return TRUE; case WM_CLOSE: - attached = false; + GPUDebug::SetActive(false); ResumeFromStepping(); breakNext = BREAK_NONE; @@ -685,7 +684,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case IDC_GEDBG_FBTABS: fbTabs->HandleNotify(lParam); - if (attached && gpuDebug != nullptr) { + if (GPUDebug::IsActive() && gpuDebug != nullptr) { UpdatePreviews(); } break; @@ -720,7 +719,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case IDC_GEDBG_BREAKTEX: { - attached = true; + GPUDebug::SetActive(true); if (!gpuDebug) { break; } @@ -739,7 +738,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case IDC_GEDBG_BREAKTARGET: { - attached = true; + GPUDebug::SetActive(true); if (!gpuDebug) { break; } @@ -758,14 +757,14 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case IDC_GEDBG_TEXLEVELDOWN: UpdateTextureLevel(textureLevel_ - 1); - if (attached && gpuDebug != nullptr) { + if (GPUDebug::IsActive() && gpuDebug != nullptr) { UpdatePreviews(); } break; case IDC_GEDBG_TEXLEVELUP: UpdateTextureLevel(textureLevel_ + 1); - if (attached && gpuDebug != nullptr) { + if (GPUDebug::IsActive() && gpuDebug != nullptr) { UpdatePreviews(); } break; @@ -785,14 +784,14 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case IDC_GEDBG_FORCEOPAQUE: - if (attached && gpuDebug != nullptr) { + if (GPUDebug::IsActive() && gpuDebug != nullptr) { forceOpaque_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_FORCEOPAQUE), BM_GETCHECK, 0, 0) != 0; UpdatePreviews(); } break; case IDC_GEDBG_SHOWCLUT: - if (attached && gpuDebug != nullptr) { + if (GPUDebug::IsActive() && gpuDebug != nullptr) { showClut_ = SendMessage(GetDlgItem(m_hDlg, IDC_GEDBG_SHOWCLUT), BM_GETCHECK, 0, 0) != 0; UpdatePreviews(); } @@ -823,7 +822,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case WM_GEDBG_TOGGLEPCBREAKPOINT: { - attached = true; + GPUDebug::SetActive(true); u32 pc = (u32)wParam; bool temp; bool isBreak = IsAddressBreakpoint(pc, temp); @@ -837,7 +836,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case WM_GEDBG_RUNTOWPARAM: { - attached = true; + GPUDebug::SetActive(true); u32 pc = (u32)wParam; AddAddressBreakpoint(pc, true); SendMessage(m_hDlg,WM_COMMAND,IDC_GEDBG_RESUME,0); @@ -862,16 +861,12 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { // The below WindowsHost methods are called on the GPU thread. -bool WindowsHost::GPUDebuggingActive() { - return attached; -} - static void DeliverMessage(UINT msg, WPARAM wParam, LPARAM lParam) { PostMessage(geDebuggerWindow->GetDlgHandle(), msg, wParam, lParam); } static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) { - if (attached) { + if (GPUDebug::IsActive()) { EnterStepping(std::bind(&DeliverMessage, msg, wParam, lParam)); } } @@ -897,6 +892,3 @@ void WindowsHost::GPUNotifyDraw() { PauseWithMessage(WM_GEDBG_BREAK_DRAW); } } - -void WindowsHost::GPUNotifyTextureAttachment(u32 addr) { -} diff --git a/Windows/WindowsHost.h b/Windows/WindowsHost.h index f05aeca316..904ead1c49 100644 --- a/Windows/WindowsHost.h +++ b/Windows/WindowsHost.h @@ -55,11 +55,9 @@ public: void SaveSymbolMap() override; void SetWindowTitle(const char *message) override; - bool GPUDebuggingActive() override; void GPUNotifyCommand(u32 pc) override; void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) override; void GPUNotifyDraw() override; - void GPUNotifyTextureAttachment(u32 addr) override; void ToggleDebugConsoleVisibility() override; bool CanCreateShortcut() override; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 171ca31310..b399653d63 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -242,6 +242,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/PostShader.cpp \ $(SRC)/GPU/Common/ShaderUniforms.cpp \ $(SRC)/GPU/Debugger/Breakpoints.cpp \ + $(SRC)/GPU/Debugger/Debugger.cpp \ $(SRC)/GPU/Debugger/Record.cpp \ $(SRC)/GPU/Debugger/Stepping.cpp \ $(SRC)/GPU/GLES/FramebufferManagerGLES.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index fd6e262e35..13475121f5 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -168,8 +168,9 @@ SOURCES_CXX += \ $(GPUCOMMONDIR)/PostShader.cpp \ $(COMMONDIR)/ColorConv.cpp \ $(GPUDIR)/Debugger/Breakpoints.cpp \ - $(GPUDIR)/Debugger/Stepping.cpp \ + $(GPUDIR)/Debugger/Debugger.cpp \ $(GPUDIR)/Debugger/Record.cpp \ + $(GPUDIR)/Debugger/Stepping.cpp \ $(GPUDIR)/Common/TextureCacheCommon.cpp \ $(GPUDIR)/Common/TextureScalerCommon.cpp \ $(GPUDIR)/Common/SoftwareTransformCommon.cpp \ From f1afc519940ecf4d5c6d8201f8ca39991061845d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 09:41:20 -0700 Subject: [PATCH 09/37] GE Debugger: Refactor out stepping callback. In case we have multiple connected debuggers, we don't want just a callback. A counter works fine. --- Core/Host.h | 5 -- GPU/Debugger/Debugger.cpp | 60 ++++++++++++++-- GPU/Debugger/Debugger.h | 12 ++++ GPU/Debugger/Stepping.cpp | 10 ++- GPU/Debugger/Stepping.h | 8 +-- Windows/GEDebugger/GEDebugger.cpp | 110 ++++++++---------------------- Windows/GEDebugger/GEDebugger.h | 16 +---- Windows/WindowsHost.h | 3 - 8 files changed, 109 insertions(+), 115 deletions(-) diff --git a/Core/Host.h b/Core/Host.h index d5b0dc9a7b..2fd79e9510 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -50,11 +50,6 @@ public: virtual void SaveSymbolMap() {} virtual void SetWindowTitle(const char *message) {} - // While debugging is active, it's perfectly fine for these to block. - virtual void GPUNotifyCommand(u32 pc) {} - virtual void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {} - virtual void GPUNotifyDraw() {} - virtual bool CanCreateShortcut() {return false;} virtual bool CreateDesktopShortcut(std::string argumentPath, std::string title) {return false;} diff --git a/GPU/Debugger/Debugger.cpp b/GPU/Debugger/Debugger.cpp index 8f2445c512..2f00c804d1 100644 --- a/GPU/Debugger/Debugger.cpp +++ b/GPU/Debugger/Debugger.cpp @@ -15,34 +15,86 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Core/Host.h" +#include "GPU/GPU.h" +#include "GPU/Debugger/Breakpoints.h" #include "GPU/Debugger/Debugger.h" +#include "GPU/Debugger/Stepping.h" namespace GPUDebug { static bool active = false; +static bool inited = false; +static BreakNext breakNext = BreakNext::NONE; + +static void Init() { + if (!inited) { + GPUBreakpoints::Init(); + Core_ListenStopRequest(&GPUStepping::ForceUnpause); + inited = true; + } +} void SetActive(bool flag) { + Init(); + active = flag; + if (!active) { + breakNext = BreakNext::NONE; + GPUStepping::ResumeFromStepping(); + } } bool IsActive() { return active; } +void SetBreakNext(BreakNext next) { + SetActive(true); + breakNext = next; + if (next == BreakNext::TEX) { + GPUBreakpoints::AddTextureChangeTempBreakpoint(); + } else if (next == BreakNext::PRIM) { + GPUBreakpoints::AddCmdBreakpoint(GE_CMD_PRIM, true); + GPUBreakpoints::AddCmdBreakpoint(GE_CMD_BEZIER, true); + GPUBreakpoints::AddCmdBreakpoint(GE_CMD_SPLINE, true); + } + GPUStepping::ResumeFromStepping(); +} + void NotifyCommand(u32 pc) { - host->GPUNotifyCommand(pc); + if (!active) + return; + u32 op = Memory::ReadUnchecked_U32(pc); + if (breakNext == BreakNext::OP || GPUBreakpoints::IsBreakpoint(pc, op)) { + GPUBreakpoints::ClearTempBreakpoints(); + + auto info = gpuDebug->DissassembleOp(pc); + NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str()); + GPUStepping::EnterStepping(); + } } void NotifyDraw() { - host->GPUNotifyDraw(); + if (!active) + return; + if (breakNext == BreakNext::DRAW) { + NOTICE_LOG(G3D, "Waiting at a draw"); + GPUStepping::EnterStepping(); + } } void NotifyDisplay(u32 framebuf, u32 stride, int format) { - host->GPUNotifyDisplay(framebuf, stride, format); + if (!active) + return; + if (breakNext == BreakNext::FRAME) { + // This should work fine, start stepping at the first op of the new frame. + breakNext = BreakNext::OP; + } } void NotifyTextureAttachment(u32 texaddr) { + if (!active) + return; } } diff --git a/GPU/Debugger/Debugger.h b/GPU/Debugger/Debugger.h index 3c1fe9a062..caf1e8f37f 100644 --- a/GPU/Debugger/Debugger.h +++ b/GPU/Debugger/Debugger.h @@ -21,9 +21,21 @@ namespace GPUDebug { +enum class BreakNext { + NONE, + OP, + DRAW, + TEX, + NONTEX, + FRAME, + PRIM, +}; + void SetActive(bool flag); bool IsActive(); +void SetBreakNext(BreakNext next); + // While debugging is active, these may block. void NotifyCommand(u32 pc); void NotifyDraw(); diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index 644d833750..b277496556 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -38,6 +38,7 @@ enum PauseAction { }; static bool isStepping; +static int stepCounter = 0; static std::mutex pauseLock; static std::condition_variable pauseWait; @@ -149,7 +150,7 @@ bool SingleStep() { return true; } -bool EnterStepping(std::function callback) { +bool EnterStepping() { std::unique_lock guard(pauseLock); if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) { // Shutting down, don't try to step. @@ -170,8 +171,7 @@ bool EnterStepping(std::function callback) { pauseAction = PAUSE_BREAK; } isStepping = true; - - callback(); + stepCounter++; do { RunPauseAction(); @@ -187,6 +187,10 @@ bool IsStepping() { return isStepping; } +int GetSteppingCounter() { + return stepCounter; +} + static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) { if (!isStepping && coreState != CORE_STEPPING) { return false; diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index fb24cf072a..85b8a1082e 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -24,12 +24,12 @@ #include "GPU/Common/GPUDebugInterface.h" namespace GPUStepping { - // Should be called from the GPU thread. - // Begins stepping and calls callback while inside a lock preparing stepping. - // This would be a good place to deliver a message to code that stepping is ready. - bool EnterStepping(std::function callback); + // Should be called from the emu thread. + // Begins stepping and increments the stepping counter while inside a lock. + bool EnterStepping(); bool SingleStep(); bool IsStepping(); + int GetSteppingCounter(); bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer); bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type); diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 635ce7d220..0b878c7dab 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -31,7 +31,6 @@ #include "Windows/GEDebugger/TabVertices.h" #include "Windows/W32Util/ShellUtil.h" #include "Windows/InputBox.h" -#include "Windows/WindowsHost.h" #include "Windows/MainWindow.h" #include "Windows/main.h" #include "GPU/GPUInterface.h" @@ -48,10 +47,9 @@ const int POPUP_SUBMENU_ID_GEDBG_PREVIEW = 10; using namespace GPUBreakpoints; +using namespace GPUDebug; using namespace GPUStepping; -static BreakNextType breakNext = BREAK_NONE; - enum PrimaryDisplayType { PRIMARY_FRAMEBUF, PRIMARY_DEPTHBUF, @@ -65,9 +63,6 @@ void CGEDebugger::Init() { CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) { - GPUBreakpoints::Init(); - Core_ListenStopRequest(ForceUnpause); - // minimum size = a little more than the default RECT windowRect; GetWindowRect(m_hDlg, &windowRect); @@ -128,6 +123,8 @@ CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) int h = g_Config.iGEWindowH == -1 ? minHeight_ : g_Config.iGEWindowH; MoveWindow(m_hDlg,x,y,w,h,FALSE); + SetTimer(m_hDlg, 1, USER_TIMER_MINIMUM, nullptr); + UpdateTextureLevel(textureLevel_); } @@ -317,6 +314,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) { bool bufferResult = false; u32 flags = SimpleGLWindow::ALPHA_IGNORE | SimpleGLWindow::RESIZE_SHRINK_CENTER; + SetupPreviews(); + primaryBuffer_ = nullptr; if (showClut_) { bufferResult = GPU_GetCurrentTexture(primaryBuffer_, textureLevel_); @@ -361,6 +360,8 @@ void CGEDebugger::UpdatePrimaryPreview(const GPUgstate &state) { void CGEDebugger::UpdateSecondPreview(const GPUgstate &state) { bool bufferResult = false; + SetupPreviews(); + secondBuffer_ = nullptr; if (showClut_) { bufferResult = GPU_GetCurrentClut(secondBuffer_); @@ -403,6 +404,8 @@ void CGEDebugger::PrimaryPreviewHover(int x, int y) { return; } + SetupPreviews(); + wchar_t desc[256] = {0}; if (!primaryWindow->HasTex()) { @@ -625,14 +628,6 @@ void CGEDebugger::SavePosition() } } -void CGEDebugger::SetBreakNext(BreakNextType type) { - GPUDebug::SetActive(true); - SetupPreviews(); - - breakNext = type; - ResumeFromStepping(); -} - BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: @@ -657,8 +652,6 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case WM_CLOSE: GPUDebug::SetActive(false); - ResumeFromStepping(); - breakNext = BREAK_NONE; Show(false); return TRUE; @@ -673,6 +666,16 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { } break; + case WM_TIMER: + if (GPUStepping::IsStepping()) { + static int lastCounter = 0; + if (lastCounter != GPUStepping::GetSteppingCounter()) { + UpdatePreviews(); + lastCounter = GPUStepping::GetSteppingCounter(); + } + } + break; + case WM_NOTIFY: switch (wParam) { @@ -694,27 +697,23 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_GEDBG_STEPDRAW: - SetBreakNext(BREAK_NEXT_DRAW); + SetBreakNext(BreakNext::DRAW); break; case IDC_GEDBG_STEP: - SetBreakNext(BREAK_NEXT_OP); + SetBreakNext(BreakNext::OP); break; case IDC_GEDBG_STEPTEX: - AddTextureChangeTempBreakpoint(); - SetBreakNext(BREAK_NEXT_TEX); + SetBreakNext(BreakNext::TEX); break; case IDC_GEDBG_STEPFRAME: - SetBreakNext(BREAK_NEXT_FRAME); + SetBreakNext(BreakNext::FRAME); break; case IDC_GEDBG_STEPPRIM: - AddCmdBreakpoint(GE_CMD_PRIM, true); - AddCmdBreakpoint(GE_CMD_BEZIER, true); - AddCmdBreakpoint(GE_CMD_SPLINE, true); - SetBreakNext(BREAK_NEXT_PRIM); + SetBreakNext(BreakNext::PRIM); break; case IDC_GEDBG_BREAKTEX: @@ -770,13 +769,13 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case IDC_GEDBG_RESUME: + SetupPreviews(); primaryWindow->Clear(); secondWindow->Clear(); SetDlgItemText(m_hDlg, IDC_GEDBG_FRAMEBUFADDR, L""); SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L""); - ResumeFromStepping(); - breakNext = BREAK_NONE; + SetBreakNext(BreakNext::NONE); break; case IDC_GEDBG_RECORD: @@ -799,25 +798,8 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { } break; - case WM_GEDBG_BREAK_CMD: - { - u32 pc = (u32)wParam; - ClearTempBreakpoints(); - auto info = gpuDebug->DissassembleOp(pc); - NOTICE_LOG(G3D, "Waiting at %08x, %s", pc, info.desc.c_str()); - UpdatePreviews(); - } - break; - - case WM_GEDBG_BREAK_DRAW: - { - NOTICE_LOG(G3D, "Waiting at a draw"); - UpdatePreviews(); - } - break; - case WM_GEDBG_STEPDISPLAYLIST: - SetBreakNext(BREAK_NEXT_OP); + SetBreakNext(BreakNext::OP); break; case WM_GEDBG_TOGGLEPCBREAKPOINT: @@ -844,9 +826,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { break; case WM_GEDBG_SETCMDWPARAM: - { - GPU_SetCmdValue((u32)wParam); - } + GPU_SetCmdValue((u32)wParam); break; case WM_GEDBG_UPDATE_WATCH: @@ -858,37 +838,3 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { return FALSE; } - -// The below WindowsHost methods are called on the GPU thread. - -static void DeliverMessage(UINT msg, WPARAM wParam, LPARAM lParam) { - PostMessage(geDebuggerWindow->GetDlgHandle(), msg, wParam, lParam); -} - -static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) { - if (GPUDebug::IsActive()) { - EnterStepping(std::bind(&DeliverMessage, msg, wParam, lParam)); - } -} - -void WindowsHost::GPUNotifyCommand(u32 pc) { - u32 op = Memory::ReadUnchecked_U32(pc); - u8 cmd = op >> 24; - - if (breakNext == BREAK_NEXT_OP || IsBreakpoint(pc, op)) { - PauseWithMessage(WM_GEDBG_BREAK_CMD, (WPARAM) pc); - } -} - -void WindowsHost::GPUNotifyDisplay(u32 framebuf, u32 stride, int format) { - if (breakNext == BREAK_NEXT_FRAME) { - // This should work fine, start stepping at the first op of the new frame. - breakNext = BREAK_NEXT_OP; - } -} - -void WindowsHost::GPUNotifyDraw() { - if (breakNext == BREAK_NEXT_DRAW) { - PauseWithMessage(WM_GEDBG_BREAK_DRAW); - } -} diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h index f47afc4236..79db21dc1b 100644 --- a/Windows/GEDebugger/GEDebugger.h +++ b/Windows/GEDebugger/GEDebugger.h @@ -19,31 +19,20 @@ #include "Common/CommonWindows.h" #include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Debugger/Debugger.h" #include "Windows/resource.h" #include "Windows/W32Util/DialogManager.h" #include "Windows/W32Util/TabControl.h" #include "Windows/GEDebugger/SimpleGLWindow.h" enum { - WM_GEDBG_BREAK_CMD = WM_USER + 200, - WM_GEDBG_BREAK_DRAW, - WM_GEDBG_STEPDISPLAYLIST, + WM_GEDBG_STEPDISPLAYLIST = WM_USER + 200, WM_GEDBG_TOGGLEPCBREAKPOINT, WM_GEDBG_RUNTOWPARAM, WM_GEDBG_SETCMDWPARAM, WM_GEDBG_UPDATE_WATCH, }; -enum BreakNextType { - BREAK_NONE, - BREAK_NEXT_OP, - BREAK_NEXT_DRAW, - BREAK_NEXT_TEX, - BREAK_NEXT_NONTEX, - BREAK_NEXT_FRAME, - BREAK_NEXT_PRIM, -}; - class CtrlDisplayListView; class TabDisplayLists; class TabStateFlags; @@ -76,7 +65,6 @@ private: void HandleRedraw(int which); void UpdateSize(WORD width, WORD height); void SavePosition(); - void SetBreakNext(BreakNextType type); void UpdateTextureLevel(int level); void DescribePrimaryPreview(const GPUgstate &state, wchar_t desc[256]); void DescribeSecondPreview(const GPUgstate &state, wchar_t desc[256]); diff --git a/Windows/WindowsHost.h b/Windows/WindowsHost.h index 904ead1c49..ed3ffeefbd 100644 --- a/Windows/WindowsHost.h +++ b/Windows/WindowsHost.h @@ -55,9 +55,6 @@ public: void SaveSymbolMap() override; void SetWindowTitle(const char *message) override; - void GPUNotifyCommand(u32 pc) override; - void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) override; - void GPUNotifyDraw() override; void ToggleDebugConsoleVisibility() override; bool CanCreateShortcut() override; From c6c66e7608916cd1878a99929f3e80ea0c28f0f1 Mon Sep 17 00:00:00 2001 From: AreaScout Date: Tue, 28 Aug 2018 19:40:58 +0000 Subject: [PATCH 10/37] Add: Get user-preferred locale from OS (linux only) --- SDL/SDLMain.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 85b49825a1..d28f21936e 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -19,6 +19,7 @@ SDLJoystick *joystick = NULL; #include #include #include +#include #include "base/display.h" #include "base/logging.h" @@ -187,8 +188,23 @@ std::string System_GetProperty(SystemProperty prop) { #else return "SDL:"; #endif - case SYSPROP_LANGREGION: + case SYSPROP_LANGREGION: { + // Get user-preferred locale from OS + setlocale(LC_ALL, ""); + std::string locale(setlocale(LC_ALL, NULL)); + // Set c and c++ strings back to POSIX + std::locale::global(std::locale("POSIX")); + if (!locale.empty()) { + if (locale.find("_", 0) != std::string::npos) { + if (locale.find(".", 0) != std::string::npos) { + return locale.substr(0, locale.find(".",0)); + } else { + return locale; + } + } + } return "en_US"; + } default: return ""; } From c10b2035b5739d1ed8f00be2891f41411325e77a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 09:45:35 -0700 Subject: [PATCH 11/37] Debugger: Prevent record with no commands. Should cut down on empty dumps, at least. --- Core/HLE/sceDisplay.cpp | 3 ++- GPU/Debugger/Record.cpp | 8 ++++++-- GPU/Debugger/Record.h | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index b86d467df8..02bbc1e078 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -55,6 +55,7 @@ #include "GPU/GPUInterface.h" #include "GPU/Common/FramebufferCommon.h" #include "GPU/Common/PostShader.h" +#include "GPU/Debugger/Record.h" struct FrameBufferState { u32 topaddr; @@ -737,7 +738,7 @@ void __DisplayFlip(int cyclesLate) { // 4 here means 1 drawn, 4 skipped - so 12 fps minimum. maxFrameskip = g_Config.iFrameSkip; } - if (numSkippedFrames >= maxFrameskip) { + if (numSkippedFrames >= maxFrameskip || GPURecord::IsActivePending()) { skipFrame = false; } diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index fb130ef34a..fe8e0a527c 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -637,6 +637,10 @@ bool IsActive() { return active; } +bool IsActivePending() { + return nextFrame || active; +} + void Activate() { nextFrame = true; } @@ -749,12 +753,12 @@ void NotifyUpload(u32 dest, u32 sz) { } void NotifyFrame() { - if (active && !writePending) { + if (active && !writePending && !commands.empty()) { // Delay write until the first command of the next frame, so we get the right display buf. NOTICE_LOG(SYSTEM, "Recording complete - waiting to get display buffer"); writePending = true; } - if (nextFrame) { + if (nextFrame && (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) { NOTICE_LOG(SYSTEM, "Recording starting..."); active = true; nextFrame = false; diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h index 3cfc995a80..d040d82a89 100644 --- a/GPU/Debugger/Record.h +++ b/GPU/Debugger/Record.h @@ -23,6 +23,7 @@ namespace GPURecord { bool IsActive(); +bool IsActivePending(); void Activate(); void NotifyCommand(u32 pc); From 4ce2b64ef753a389fdb683345e7f2792c35479c2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 10:15:22 -0700 Subject: [PATCH 12/37] Debugger: Refactor to allow simpler broadcasting. It's okay to mix a bit for simple ones, I think. --- Core/Debugger/WebSocket.cpp | 42 +++++++++---------- .../WebSocket/BreakpointSubscriber.cpp | 2 +- .../Debugger/WebSocket/BreakpointSubscriber.h | 2 +- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 2 +- Core/Debugger/WebSocket/CPUCoreSubscriber.h | 2 +- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 11 ++--- Core/Debugger/WebSocket/DisasmSubscriber.h | 3 +- .../WebSocket/GPUBufferSubscriber.cpp | 2 +- Core/Debugger/WebSocket/GPUBufferSubscriber.h | 2 +- Core/Debugger/WebSocket/GameSubscriber.cpp | 2 +- Core/Debugger/WebSocket/GameSubscriber.h | 2 +- Core/Debugger/WebSocket/HLESubscriber.cpp | 2 +- Core/Debugger/WebSocket/HLESubscriber.h | 2 +- .../Debugger/WebSocket/SteppingSubscriber.cpp | 10 ++--- Core/Debugger/WebSocket/SteppingSubscriber.h | 3 +- Core/Debugger/WebSocket/WebSocketUtils.h | 8 ++++ 16 files changed, 46 insertions(+), 51 deletions(-) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 4504c827f7..38101b553e 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -56,21 +56,15 @@ #include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/SteppingSubscriber.h" -typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map); -typedef void (*Subscribershutdown)(void *p); -struct SubscriberInfo { - SubscriberInit init; - Subscribershutdown shutdown; -}; - -static const std::vector subscribers({ - { &WebSocketBreakpointInit, nullptr }, - { &WebSocketCPUCoreInit, nullptr }, - { &WebSocketDisasmInit, &WebSocketDisasmShutdown }, - { &WebSocketGameInit, nullptr }, - { &WebSocketGPUBufferInit, nullptr }, - { &WebSocketHLEInit, nullptr }, - { &WebSocketSteppingInit, &WebSocketSteppingShutdown }, +typedef DebuggerSubscriber *(*SubscriberInit)(DebuggerEventHandlerMap &map); +static const std::vector subscribers({ + &WebSocketBreakpointInit, + &WebSocketCPUCoreInit, + &WebSocketDisasmInit, + &WebSocketGameInit, + &WebSocketGPUBufferInit, + &WebSocketHLEInit, + &WebSocketSteppingInit, }); // To handle webserver restart, keep track of how many running. @@ -132,10 +126,10 @@ void HandleDebuggerRequest(const http::Request &request) { SteppingBroadcaster stepping; std::unordered_map eventHandlers; - std::vector subscriberData; - for (auto info : subscribers) { + std::vector subscriberData; + for (auto init : subscribers) { std::lock_guard guard(lifecycleLock); - subscriberData.push_back(info.init(eventHandlers)); + subscriberData.push_back(init(eventHandlers)); } // There's a tradeoff between responsiveness to incoming events, and polling for changes. @@ -178,6 +172,12 @@ void HandleDebuggerRequest(const http::Request &request) { game.Broadcast(ws); stepping.Broadcast(ws); + for (size_t i = 0; i < subscribers.size(); ++i) { + if (subscriberData[i]) { + subscriberData[i]->Broadcast(ws); + } + } + if (stopRequested) { ws->Close(net::WebSocketClose::GOING_AWAY); } @@ -188,11 +188,7 @@ void HandleDebuggerRequest(const http::Request &request) { std::lock_guard guard(lifecycleLock); for (size_t i = 0; i < subscribers.size(); ++i) { - if (subscribers[i].shutdown) { - subscribers[i].shutdown(subscriberData[i]); - } else { - assert(!subscriberData[i]); - } + delete subscriberData[i]; } delete ws; diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp index ffe1797ce5..aab5bf8346 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -23,7 +23,7 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/MIPS/MIPSDebugInterface.h" -void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) { +DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) { // No need to bind or alloc state, these are all global. map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd; map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate; diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.h b/Core/Debugger/WebSocket/BreakpointSubscriber.h index c9c2e1effc..d0e622cd12 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.h +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.h @@ -19,7 +19,7 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map); +DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map); void WebSocketCPUBreakpointAdd(DebuggerRequest &req); void WebSocketCPUBreakpointUpdate(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 0d2c42f66c..6f371e5b85 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -25,7 +25,7 @@ #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSDebugInterface.h" -void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) { +DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) { // No need to bind or alloc state, these are all global. map["cpu.stepping"] = &WebSocketCPUStepping; map["cpu.resume"] = &WebSocketCPUResume; diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h index 3667979e96..99dc53adaa 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.h +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h @@ -19,7 +19,7 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map); +DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map); void WebSocketCPUStepping(DebuggerRequest &req); void WebSocketCPUResume(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 78ee607fdf..6d2f76d746 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -28,11 +28,12 @@ #include "Core/MIPS/MIPSAsm.h" #include "Core/MIPS/MIPSDebugInterface.h" -struct WebSocketDisasmState { +class WebSocketDisasmState : public DebuggerSubscriber { +public: WebSocketDisasmState() { disasm_.setCpu(currentDebugMIPS); } - ~WebSocketDisasmState() { + ~WebSocketDisasmState() override { disasm_.clear(); } @@ -49,7 +50,7 @@ protected: DisassemblyManager disasm_; }; -void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) { +DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map) { auto p = new WebSocketDisasmState(); map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1); map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1); @@ -59,10 +60,6 @@ void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) { return p; } -void WebSocketDisasmShutdown(void *p) { - delete static_cast(p); -} - static DebugInterface *CPUFromRequest(DebuggerRequest &req) { if (!req.HasParam("thread")) return currentDebugMIPS; diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.h b/Core/Debugger/WebSocket/DisasmSubscriber.h index e6c5b0e71c..35a32fa2bb 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.h +++ b/Core/Debugger/WebSocket/DisasmSubscriber.h @@ -19,5 +19,4 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketDisasmInit(DebuggerEventHandlerMap &map); -void WebSocketDisasmShutdown(void *p); +DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map); diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp index 98f65231c2..67b13d85b2 100644 --- a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp @@ -28,7 +28,7 @@ #include "Core/Screenshot.h" #include "GPU/Debugger/Stepping.h" -void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) { +DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) { // No need to bind or alloc state, these are all global. map["gpu.buffer.screenshot"] = &WebSocketGPUBufferScreenshot; map["gpu.buffer.renderColor"] = &WebSocketGPUBufferRenderColor; diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.h b/Core/Debugger/WebSocket/GPUBufferSubscriber.h index c7062fdb82..00b93d74c0 100644 --- a/Core/Debugger/WebSocket/GPUBufferSubscriber.h +++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.h @@ -19,7 +19,7 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map); +DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map); void WebSocketGPUBufferScreenshot(DebuggerRequest &req); void WebSocketGPUBufferRenderColor(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp index a17717e262..0acd4a9b3e 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.cpp +++ b/Core/Debugger/WebSocket/GameSubscriber.cpp @@ -20,7 +20,7 @@ #include "Core/ELF/ParamSFO.h" #include "Core/System.h" -void *WebSocketGameInit(DebuggerEventHandlerMap &map) { +DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) { map["game.status"] = &WebSocketGameStatus; map["version"] = &WebSocketVersion; diff --git a/Core/Debugger/WebSocket/GameSubscriber.h b/Core/Debugger/WebSocket/GameSubscriber.h index 77cec9e38b..e3d08f4522 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.h +++ b/Core/Debugger/WebSocket/GameSubscriber.h @@ -19,7 +19,7 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketGameInit(DebuggerEventHandlerMap &map); +DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map); void WebSocketGameStatus(DebuggerRequest &req); void WebSocketVersion(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/HLESubscriber.cpp b/Core/Debugger/WebSocket/HLESubscriber.cpp index 586f30199c..ae65e692fa 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.cpp +++ b/Core/Debugger/WebSocket/HLESubscriber.cpp @@ -27,7 +27,7 @@ #include "Core/MIPS/MIPSStackWalk.h" #include "Core/HLE/sceKernelThread.h" -void *WebSocketHLEInit(DebuggerEventHandlerMap &map) { +DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map) { map["hle.thread.list"] = &WebSocketHLEThreadList; map["hle.thread.wake"] = &WebSocketHLEThreadWake; map["hle.thread.stop"] = &WebSocketHLEThreadStop; diff --git a/Core/Debugger/WebSocket/HLESubscriber.h b/Core/Debugger/WebSocket/HLESubscriber.h index 9b059f4759..6d473c3563 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.h +++ b/Core/Debugger/WebSocket/HLESubscriber.h @@ -19,7 +19,7 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketHLEInit(DebuggerEventHandlerMap &map); +DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map); void WebSocketHLEThreadList(DebuggerRequest &req); void WebSocketHLEThreadWake(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index e1347f3a14..d3422b48f9 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -28,11 +28,11 @@ using namespace MIPSAnalyst; -struct WebSocketSteppingState { +struct WebSocketSteppingState : public DebuggerSubscriber { WebSocketSteppingState() { disasm_.setCpu(currentDebugMIPS); } - ~WebSocketSteppingState() { + ~WebSocketSteppingState() override { disasm_.clear(); } @@ -51,7 +51,7 @@ protected: DisassemblyManager disasm_; }; -void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) { +DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map) { auto p = new WebSocketSteppingState(); map["cpu.stepInto"] = std::bind(&WebSocketSteppingState::Into, p, std::placeholders::_1); map["cpu.stepOver"] = std::bind(&WebSocketSteppingState::Over, p, std::placeholders::_1); @@ -62,10 +62,6 @@ void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) { return p; } -void WebSocketSteppingShutdown(void *p) { - delete static_cast(p); -} - static DebugInterface *CPUFromRequest(DebuggerRequest &req, uint32_t *threadID = nullptr) { if (!req.HasParam("thread")) { if (threadID) diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.h b/Core/Debugger/WebSocket/SteppingSubscriber.h index 319c608caa..06983ea278 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.h +++ b/Core/Debugger/WebSocket/SteppingSubscriber.h @@ -19,5 +19,4 @@ #include "Core/Debugger/WebSocket/WebSocketUtils.h" -void *WebSocketSteppingInit(DebuggerEventHandlerMap &map); -void WebSocketSteppingShutdown(void *p); +DebuggerSubscriber *WebSocketSteppingInit(DebuggerEventHandlerMap &map); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 70c08828b2..85dca3f4a8 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -98,5 +98,13 @@ private: bool responsePartial_ = false; }; +class DebuggerSubscriber { +public: + virtual ~DebuggerSubscriber() {} + + // Subscribers can also broadcast if they have simple cases to. + virtual void Broadcast(net::WebSocketServer *ws) {} +}; + typedef std::function DebuggerEventHandler; typedef std::unordered_map DebuggerEventHandlerMap; From 653129c52891830d65b151954ca525c5266da443 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 10:58:28 -0700 Subject: [PATCH 13/37] Debugger: Add an event for GE dump recording. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/Debugger/WebSocket.cpp | 2 + .../WebSocket/GPURecordSubscriber.cpp | 107 ++++++++++++++++++ Core/Debugger/WebSocket/GPURecordSubscriber.h | 22 ++++ GPU/Debugger/Record.cpp | 41 +++++-- GPU/Debugger/Record.h | 5 +- android/jni/Android.mk | 1 + 9 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 Core/Debugger/WebSocket/GPURecordSubscriber.cpp create mode 100644 Core/Debugger/WebSocket/GPURecordSubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b787be7a77..2f9a21e4f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1432,6 +1432,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket/GameSubscriber.h Core/Debugger/WebSocket/GPUBufferSubscriber.cpp Core/Debugger/WebSocket/GPUBufferSubscriber.h + Core/Debugger/WebSocket/GPURecordSubscriber.cpp + Core/Debugger/WebSocket/GPURecordSubscriber.h Core/Debugger/WebSocket/HLESubscriber.cpp Core/Debugger/WebSocket/HLESubscriber.h Core/Debugger/WebSocket/LogBroadcaster.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 068241f0c6..c5a39b54be 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -188,6 +188,7 @@ + @@ -549,6 +550,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index ba79121304..b459851f73 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -734,6 +734,9 @@ HLE\Libraries + + Debugger\WebSocket + @@ -1361,6 +1364,9 @@ HLE\Libraries + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 38101b553e..6175279363 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -53,6 +53,7 @@ #include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/GameSubscriber.h" #include "Core/Debugger/WebSocket/GPUBufferSubscriber.h" +#include "Core/Debugger/WebSocket/GPURecordSubscriber.h" #include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/SteppingSubscriber.h" @@ -63,6 +64,7 @@ static const std::vector subscribers({ &WebSocketDisasmInit, &WebSocketGameInit, &WebSocketGPUBufferInit, + &WebSocketGPURecordInit, &WebSocketHLEInit, &WebSocketSteppingInit, }); diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.cpp b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp new file mode 100644 index 0000000000..961a4d0d04 --- /dev/null +++ b/Core/Debugger/WebSocket/GPURecordSubscriber.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "data/base64.h" +#include "Common/FileUtil.h" +#include "Core/Debugger/WebSocket/GPURecordSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/System.h" +#include "GPU/Debugger/Record.h" + +struct WebSocketGPURecordState : public DebuggerSubscriber { + ~WebSocketGPURecordState() override; + void Dump(DebuggerRequest &req); + + void Broadcast(net::WebSocketServer *ws) override; + +protected: + bool pending_ = false; + std::string lastTicket_; + std::string lastFilename_; +}; + +DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) { + auto p = new WebSocketGPURecordState(); + map["gpu.record.dump"] = std::bind(&WebSocketGPURecordState::Dump, p, std::placeholders::_1); + + return p; +} + +WebSocketGPURecordState::~WebSocketGPURecordState() { + // Clear the callback to hopefully avoid a crash. + if (pending_) + GPURecord::SetCallback(nullptr); +} + +// Begin recording (gpu.record.dump) +// +// No parameters. +// +// Response (same event name): +// - uri: data: URI containing debug dump data. +// +// Note: recording may take a moment. +void WebSocketGPURecordState::Dump(DebuggerRequest &req) { + if (!PSP_IsInited()) + return req.Fail("CPU not started"); + + if (!GPURecord::Activate()) + return req.Fail("Recording already in progress"); + + pending_ = true; + GPURecord::SetCallback([=](const std::string &filename) { + lastFilename_ = filename; + pending_ = false; + }); + + const JsonNode *value = req.data.get("ticket"); + lastTicket_ = value ? json_stringify(value) : ""; +} + +// This handles the asynchronous gpu.record.dump response. +void WebSocketGPURecordState::Broadcast(net::WebSocketServer *ws) { + if (!lastFilename_.empty()) { + FILE *fp = File::OpenCFile(lastFilename_, "rb"); + if (!fp) { + lastFilename_.clear(); + return; + } + + // We write directly to the stream since this is a large chunk of data. + ws->AddFragment(false, R"({"event":"gpu.record.dump")"); + if (!lastTicket_.empty()) { + ws->AddFragment(false, R"(,"ticket":)"); + ws->AddFragment(false, lastTicket_); + } + ws->AddFragment(false, R"(,"uri":"data:application/octet-stream;base64,)"); + + // Divisible by 3 for base64 reasons. + const size_t BUF_SIZE = 16383; + std::vector buf; + buf.resize(BUF_SIZE); + while (!feof(fp)) { + size_t bytes = fread(&buf[0], 1, BUF_SIZE, fp); + ws->AddFragment(false, Base64Encode(&buf[0], bytes)); + } + fclose(fp); + + ws->AddFragment(true, R"("})"); + + lastFilename_.clear(); + lastTicket_.clear(); + } +} diff --git a/Core/Debugger/WebSocket/GPURecordSubscriber.h b/Core/Debugger/WebSocket/GPURecordSubscriber.h new file mode 100644 index 0000000000..31a1f6b42d --- /dev/null +++ b/Core/Debugger/WebSocket/GPURecordSubscriber.h @@ -0,0 +1,22 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map); diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index fe8e0a527c..1fa237a600 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -48,6 +48,7 @@ static const int VERSION = 2; static bool active = false; static bool nextFrame = false; static bool writePending = false; +static std::function writeCallback; enum class CommandType : u8 { INIT = 0, @@ -409,7 +410,7 @@ static void WriteCompressed(FILE *fp, const void *p, size_t sz) { delete [] compressed; } -static void WriteRecording() { +static std::string WriteRecording() { FlushRegisters(); EmitDisplayBuf(); @@ -430,6 +431,8 @@ static void WriteRecording() { WriteCompressed(fp, pushbuf.data(), bufsz); fclose(fp); + + return filename; } static void GetVertDataSizes(int vcount, const void *indices, u32 &vbytes, u32 &ibytes) { @@ -641,8 +644,31 @@ bool IsActivePending() { return nextFrame || active; } -void Activate() { - nextFrame = true; +bool Activate() { + if (!nextFrame) { + nextFrame = true; + return true; + } + return false; +} + +void SetCallback(const std::function callback) { + writeCallback = callback; +} + +static void FinishRecording() { + // We're done - this was just to write the result out. + std::string filename = WriteRecording(); + commands.clear(); + pushbuf.clear(); + + NOTICE_LOG(SYSTEM, "Recording finished"); + writePending = false; + active = false; + + if (writeCallback) + writeCallback(filename); + writeCallback = nullptr; } void NotifyCommand(u32 pc) { @@ -650,14 +676,7 @@ void NotifyCommand(u32 pc) { return; } if (writePending) { - WriteRecording(); - commands.clear(); - pushbuf.clear(); - - writePending = false; - // We're done - this was just to write the result out. - NOTICE_LOG(SYSTEM, "Recording finished"); - active = false; + FinishRecording(); return; } diff --git a/GPU/Debugger/Record.h b/GPU/Debugger/Record.h index d040d82a89..230b361d19 100644 --- a/GPU/Debugger/Record.h +++ b/GPU/Debugger/Record.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include "Common/CommonTypes.h" @@ -24,7 +25,9 @@ namespace GPURecord { bool IsActive(); bool IsActivePending(); -void Activate(); +bool Activate(); +// Call only if Activate() returns true. +void SetCallback(const std::function callback); void NotifyCommand(u32 pc); void NotifyMemcpy(u32 dest, u32 src, u32 sz); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b399653d63..1ed3b4eb69 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -310,6 +310,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp \ + $(SRC)/Core/Debugger/WebSocket/GPURecordSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ From 360862617a7954657e0cf2bffa7837bfc35bd589 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 11:00:05 -0700 Subject: [PATCH 14/37] UI: Move debugger checkbox outside texture area. Oops, didn't mean for it to be there. --- UI/GameSettingsScreen.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index f93783317c..14da475a95 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1268,6 +1268,12 @@ void DeveloperToolsScreen::CreateViews() { cpuTests->SetEnabled(TestsAvailable()); #endif + allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); + canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER); + CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger")); + list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger); + allowDebugger->SetEnabledPtr(&canAllowDebugger_); + list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged); list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics"))); list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLogConfig); @@ -1284,12 +1290,6 @@ void DeveloperToolsScreen::CreateViews() { createTextureIni->SetEnabled(false); } #endif - - allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); - canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER); - CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger")); - list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger); - allowDebugger->SetEnabledPtr(&canAllowDebugger_); } void DeveloperToolsScreen::onFinish(DialogResult result) { From a004196d4388b958963083dee4619bc6fbc438a8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 14:29:23 -0700 Subject: [PATCH 15/37] Qt: Buildfix with removed debugger methods. --- Qt/QtHost.h | 3 --- UWP/UWPHost.h | 5 ----- Windows/PPSSPP.vcxproj | 24 ++++++++++++++++++++++++ Windows/PPSSPP.vcxproj.filters | 12 ++++++++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/Qt/QtHost.h b/Qt/QtHost.h index fbad0aa7ea..846da1058f 100644 --- a/Qt/QtHost.h +++ b/Qt/QtHost.h @@ -90,9 +90,6 @@ public: NativeMessageReceived(message.c_str(), value.c_str()); } - bool GPUDebuggingActive() override { - return false; - } private: const char* SymbolMapFilename(std::string currentFilename); MainWindow* mainWindow; diff --git a/UWP/UWPHost.h b/UWP/UWPHost.h index 635e37bccc..76f460a700 100644 --- a/UWP/UWPHost.h +++ b/UWP/UWPHost.h @@ -32,11 +32,6 @@ public: void SaveSymbolMap() override; void SetWindowTitle(const char *message) override; - bool GPUDebuggingActive() override { return false; } - void GPUNotifyCommand(u32 pc) override {} - void GPUNotifyDisplay(u32 framebuf, u32 stride, int format) override {} - void GPUNotifyDraw() override {} - void GPUNotifyTextureAttachment(u32 addr) override {} void ToggleDebugConsoleVisibility() override; bool CanCreateShortcut() override; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index e7057f6f41..0d78975928 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -345,6 +345,18 @@ true true + + true + true + true + true + + + true + true + true + true + @@ -439,6 +451,18 @@ + + true + true + true + true + + + true + true + true + true + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 80851fdc34..59a100aa64 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -182,6 +182,12 @@ Other Platforms + + Other Platforms\Qt + + + Other Platforms + @@ -335,6 +341,12 @@ Other Platforms + + Other Platforms\Qt + + + Other Platforms + From ed10b69b17f0a763a29ef8d1b4a5486c7dd67523 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 14:40:44 -0700 Subject: [PATCH 16/37] Savedata: Show confirm save without faded icon. See #9632. --- Core/Dialog/PSPSaveDialog.cpp | 24 ++++++++++++------------ Core/Dialog/PSPSaveDialog.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index e47f51dcf7..09b32dcc01 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -362,13 +362,13 @@ void PSPSaveDialog::DisplaySaveList(bool canMove) { } } -void PSPSaveDialog::DisplaySaveIcon() +void PSPSaveDialog::DisplaySaveIcon(bool checkExists) { std::lock_guard guard(paramLock); int textureColor = CalcFadedColor(0xFFFFFFFF); auto curSave = param.GetFileInfo(currentSelectedSave); - if (curSave.size == 0) + if (curSave.size == 0 && checkExists) textureColor = CalcFadedColor(0xFF777777); // Calc save image position on screen @@ -652,7 +652,7 @@ int PSPSaveDialog::Update(int animSpeed) case DS_SAVE_CONFIRM: StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(false); DisplaySaveDataInfo2(true); DisplayMessage(di->T("Confirm Save", "Do you want to save this data?"), true); @@ -673,7 +673,7 @@ int PSPSaveDialog::Update(int animSpeed) case DS_SAVE_CONFIRM_OVERWRITE: StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(); DisplayMessage(di->T("Confirm Overwrite","Do you want to overwrite the data?"), true); @@ -702,7 +702,7 @@ int PSPSaveDialog::Update(int animSpeed) StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(true); DisplayMessage(di->T("Saving","Saving\nPlease Wait...")); @@ -715,7 +715,7 @@ int PSPSaveDialog::Update(int animSpeed) JoinIOThread(); StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(true); DisplayMessage(di->T("SavingFailed", "Unable to save data.")); @@ -742,7 +742,7 @@ int PSPSaveDialog::Update(int animSpeed) } StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(true); DisplayMessage(di->T("Save completed")); @@ -782,7 +782,7 @@ int PSPSaveDialog::Update(int animSpeed) case DS_LOAD_CONFIRM: StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(); DisplayMessage(di->T("ConfirmLoad", "Load this data?"), true); @@ -807,7 +807,7 @@ int PSPSaveDialog::Update(int animSpeed) StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(); DisplayMessage(di->T("Loading","Loading\nPlease Wait...")); @@ -820,7 +820,7 @@ int PSPSaveDialog::Update(int animSpeed) JoinIOThread(); StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(); DisplayMessage(di->T("LoadingFailed", "Unable to load data.")); @@ -844,7 +844,7 @@ int PSPSaveDialog::Update(int animSpeed) JoinIOThread(); StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(); DisplayMessage(di->T("Load completed")); @@ -902,7 +902,7 @@ int PSPSaveDialog::Update(int animSpeed) case DS_DELETE_CONFIRM: StartDraw(); - DisplaySaveIcon(); + DisplaySaveIcon(true); DisplaySaveDataInfo2(); DisplayMessage(di->T("DeleteConfirm", diff --git a/Core/Dialog/PSPSaveDialog.h b/Core/Dialog/PSPSaveDialog.h index c0d42e4955..888cc63d6e 100644 --- a/Core/Dialog/PSPSaveDialog.h +++ b/Core/Dialog/PSPSaveDialog.h @@ -90,7 +90,7 @@ private: void DisplayBanner(int which); void DisplaySaveList(bool canMove = true); - void DisplaySaveIcon(); + void DisplaySaveIcon(bool checkExists); void DisplaySaveDataInfo1(); void DisplaySaveDataInfo2(bool showNewData = false); void DisplayMessage(std::string text, bool hasYesNo = false); From 65bf285e41e232b471ee46b56418a729590e712a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 14:56:56 -0700 Subject: [PATCH 17/37] UI: Fix state screenshot size on pause screen. Was stretched wide here, but fine in save data manager. --- UI/PauseScreen.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index f066103ba7..c046a11623 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -138,12 +138,15 @@ public: } protected: - virtual bool FillVertical() const override { return false; } + bool FillVertical() const override { return false; } + UI::Size PopupWidth() const override { return 500; } bool ShowButtons() const override { return true; } - virtual void CreatePopupContents(UI::ViewGroup *parent) override { - // TODO: Find an appropriate size for the image view - parent->Add(new AsyncImageFileView(filename_, UI::IS_DEFAULT, NULL, new UI::LayoutParams(480, 272)))->SetCanBeFocused(false); + void CreatePopupContents(UI::ViewGroup *parent) override { + UI::LinearLayout *content = new UI::LinearLayout(UI::ORIENT_VERTICAL); + parent->Add(content); + UI::Margins contentMargins(10, 0); + content->Add(new AsyncImageFileView(filename_, UI::IS_DEFAULT, nullptr, new UI::LinearLayoutParams(480, 272, contentMargins)))->SetCanBeFocused(false); } private: From 68317f0067c31b3336444ba657347e21d986f397 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 19:56:35 -0700 Subject: [PATCH 18/37] Core: Match up NOEN define usage a bit more. Some compile parameters were resulting in us try to use a func we didn't compile. We should at least use the same defines in usage and definition. --- Common/ColorConv.cpp | 2 +- Core/Util/AudioFormat.cpp | 4 ++-- Core/Util/AudioFormat.h | 3 +++ Core/Util/AudioFormatNEON.cpp | 2 +- ext/native/math/fast/fast_math.c | 2 +- ext/native/math/fast/fast_matrix.h | 2 ++ ext/native/math/fast/fast_matrix_neon.S | 2 +- 7 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Common/ColorConv.cpp b/Common/ColorConv.cpp index c032fd4bc9..bf4276f1e3 100644 --- a/Common/ColorConv.cpp +++ b/Common/ColorConv.cpp @@ -643,7 +643,7 @@ Convert16bppTo16bppFunc ConvertRGB565ToBGR565 = &ConvertRGB565ToBGR565Basic; #endif void SetupColorConv() { -#if PPSSPP_ARCH(ARMV7) && PPSSPP_ARCH(ARM_NEON) +#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64) if (cpu_info.bNEON) { ConvertRGBA4444ToABGR4444 = &ConvertRGBA4444ToABGR4444NEON; ConvertRGBA5551ToABGR1555 = &ConvertRGBA5551ToABGR1555NEON; diff --git a/Core/Util/AudioFormat.cpp b/Core/Util/AudioFormat.cpp index 2424b405d5..54610949f3 100644 --- a/Core/Util/AudioFormat.cpp +++ b/Core/Util/AudioFormat.cpp @@ -110,12 +110,12 @@ void ConvertS16ToF32(float *out, const s16 *in, size_t size) { } } -#ifndef _M_SSE +#if !defined(_M_SSE) && !PPSSPP_ARCH(ARM64) AdjustVolumeBlockFunc AdjustVolumeBlock = &AdjustVolumeBlockStandard; // This has to be done after CPUDetect has done its magic. void SetupAudioFormats() { -#if PPSSPP_ARCH(ARMV7) && PPSSPP_ARCH(ARM_NEON) +#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64) if (cpu_info.bNEON) { AdjustVolumeBlock = &AdjustVolumeBlockNEON; } diff --git a/Core/Util/AudioFormat.h b/Core/Util/AudioFormat.h index 7fc20eaf83..eb5098ad01 100644 --- a/Core/Util/AudioFormat.h +++ b/Core/Util/AudioFormat.h @@ -20,6 +20,7 @@ #include "ppsspp_config.h" #include "Common/Common.h" #include "Common/CommonTypes.h" +#include "Core/Util/AudioFormatNEON.h" #define IS_LITTLE_ENDIAN (*(const u16 *)"\0\xff" >= 0x100) @@ -71,6 +72,8 @@ void ConvertS16ToF32(float *ou, const s16 *in, size_t size); #ifdef _M_SSE #define AdjustVolumeBlock AdjustVolumeBlockStandard +#elif PPSSPP_ARCH(ARM64) +#define AdjustVolumeBlock AdjustVolumeBlockNEON #else typedef void (*AdjustVolumeBlockFunc)(s16 *out, s16 *in, size_t size, int leftVol, int rightVol); extern AdjustVolumeBlockFunc AdjustVolumeBlock; diff --git a/Core/Util/AudioFormatNEON.cpp b/Core/Util/AudioFormatNEON.cpp index 4653764b49..b3cc48ee5f 100644 --- a/Core/Util/AudioFormatNEON.cpp +++ b/Core/Util/AudioFormatNEON.cpp @@ -23,7 +23,7 @@ #include "Core/Util/AudioFormat.h" #include "Core/Util/AudioFormatNEON.h" -#if !defined(ARM) && !defined(ARM64) +#if !PPSSPP_ARCH(ARM) && !PPSSPP_ARCH(ARM64) #error Should not be compiled on non-ARM. #endif diff --git a/ext/native/math/fast/fast_math.c b/ext/native/math/fast/fast_math.c index 67506a1823..5363a32830 100644 --- a/ext/native/math/fast/fast_math.c +++ b/ext/native/math/fast/fast_math.c @@ -5,7 +5,7 @@ void InitFastMath(int enableNEON) { // Every architecture has its own define. This needs to be added to. if (enableNEON) { -#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7S__) +#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64) fast_matrix_mul_4x4 = &fast_matrix_mul_4x4_neon; #endif } diff --git a/ext/native/math/fast/fast_matrix.h b/ext/native/math/fast/fast_matrix.h index 676ab1acea..41200404f6 100644 --- a/ext/native/math/fast/fast_matrix.h +++ b/ext/native/math/fast/fast_matrix.h @@ -18,6 +18,8 @@ extern void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b) #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) // Hard link to SSE implementations on x86/amd64 #define fast_matrix_mul_4x4 fast_matrix_mul_4x4_sse +#elif PPSSPP_ARCH(ARM64) +#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_c #else extern fptr_fast_matrix_mul_4x4 fast_matrix_mul_4x4; #endif diff --git a/ext/native/math/fast/fast_matrix_neon.S b/ext/native/math/fast/fast_matrix_neon.S index 813b788471..746115314c 100644 --- a/ext/native/math/fast/fast_matrix_neon.S +++ b/ext/native/math/fast/fast_matrix_neon.S @@ -1,5 +1,5 @@ #include "ppsspp_config.h" -#if PPSSPP_ARCH(ARM) && PPSSPP_ARCH(ARM_NEON) +#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64) .syntax unified // Allow both ARM and Thumb-2 instructions .text From f906fbed35e243ea1693b22e76ab943e7a74831c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 13:57:20 -0700 Subject: [PATCH 19/37] Core: Track graphics startup failures and cycle. If the graphics driver segfaults, or some plugin segfaults, let's try a different one next time. This gives better hope of starting up next time. --- Core/Config.cpp | 42 ++++++++++++++++++++++++++ Core/Config.h | 2 ++ UI/NativeApp.cpp | 52 +++++++++++++++++++++++++++++--- Windows/EmuThread.cpp | 14 +++++++++ Windows/GPU/D3D11Context.cpp | 1 + Windows/GPU/WindowsGLContext.cpp | 1 + 6 files changed, 108 insertions(+), 4 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 03e1043ddb..5b251058bd 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "base/display.h" #include "base/NativeApp.h" @@ -545,6 +546,46 @@ static int DefaultGPUBackend() { return (int)GPUBackend::OPENGL; } +int Config::NextValidBackend() { + std::vector split; + std::set failed; + SplitString(sFailedGPUBackends, ',', split); + for (const auto &str : split) { + if (!str.empty() && str != "ALL") { + failed.insert(atoi(str.c_str())); + } + } + + if (failed.count(iGPUBackend)) { +#if (PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)) && !PPSSPP_PLATFORM(UWP) + if (VulkanMayBeAvailable() && !failed.count((int)GPUBackend::VULKAN)) { + return (int)GPUBackend::VULKAN; + } +#endif +#if PPSSPP_PLATFORM(WINDOWS) + if (DoesVersionMatchWindows(6, 1, 0, 0, true) && !failed.count((int)GPUBackend::DIRECT3D11)) { + return (int)GPUBackend::DIRECT3D11; + } +#endif +#if !PPSSPP_PLATFORM(UWP) + if (!failed.count((int)GPUBackend::OPENGL)) { + return (int)GPUBackend::OPENGL; + } +#endif +#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) + if (!failed.count((int)GPUBackend::DIRECT3D9)) { + return (int)GPUBackend::DIRECT3D9; + } +#endif + + // They've all failed. Let them try the default. + sFailedGPUBackends += ",ALL"; + return DefaultGPUBackend(); + } + + return iGPUBackend; +} + static bool DefaultVertexCache() { return DefaultGPUBackend() == (int)GPUBackend::OPENGL; } @@ -556,6 +597,7 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("CardboardYShift", &g_Config.iCardboardXShift, 0, true, true), ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true), ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend), + ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, ""), ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", true, false), #ifdef _WIN32 ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), diff --git a/Core/Config.h b/Core/Config.h index e956c2b97c..24e7d98664 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -121,6 +121,7 @@ public: // GFX int iGPUBackend; + std::string sFailedGPUBackends; // 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; @@ -441,6 +442,7 @@ public: void GetReportingInfo(UrlEncoder &data); bool IsPortrait() const; + int NextValidBackend(); protected: void LoadStandardControllerIni(); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 10850b33df..6f82c63fb6 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -29,13 +29,10 @@ // in NativeShutdown. #include -// Linux doesn't like using std::find with std::vector without this :/ -#if !defined(MOBILE_DEVICE) #include -#endif #include -#include #include +#include #if defined(_WIN32) #include "Windows/DSoundStream.h" @@ -347,6 +344,46 @@ void CreateDirectoriesAndroid() { File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/SAVEDATA/.nomedia"); } +static void CheckFailedGPUBackends() { + std::string cache = GetSysDirectory(DIRECTORY_APP_CACHE) + "/FailedGraphicsBackends.txt"; + + if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { + std::string data; + if (readFileToString(true, cache.c_str(), data)) + g_Config.sFailedGPUBackends = data; + } + + // Okay, let's not try a backend in the failed list. + g_Config.iGPUBackend = g_Config.NextValidBackend(); + // And then let's - for now - add the current to the failed list. + if (g_Config.sFailedGPUBackends.empty()) { + g_Config.sFailedGPUBackends = StringFromFormat("%d", g_Config.iGPUBackend); + } else if (g_Config.sFailedGPUBackends.find("ALL") == std::string::npos) { + g_Config.sFailedGPUBackends += StringFromFormat(",%d", g_Config.iGPUBackend); + } + + if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { + // Let's try to create, in case it doesn't exist. + if (!File::Exists(GetSysDirectory(DIRECTORY_APP_CACHE))) + File::CreateDir(GetSysDirectory(DIRECTORY_APP_CACHE)); + writeStringToFile(true, g_Config.sFailedGPUBackends, cache.c_str()); + } else { + // Just save immediately, since we have storage. + g_Config.Save(); + } +} + +static void ClearFailedGPUBackends() { + // We've successfully started graphics without crashing, hurray. + // In case they update drivers and have totally different problems much later, clear the failed list. + g_Config.sFailedGPUBackends.clear(); + if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { + File::Delete(GetSysDirectory(DIRECTORY_APP_CACHE) + "/FailedGraphicsBackends.txt"); + } else { + g_Config.Save(); + } +} + void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir) { net::Init(); // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times. @@ -630,6 +667,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // We do this here, instead of in NativeInitGraphics, because the display may be reset. // When it's reset we don't want to forget all our managed things. + CheckFailedGPUBackends(); SetGPUBackend((GPUBackend) g_Config.iGPUBackend); // Must be done restarting by now. @@ -951,6 +989,12 @@ void NativeRender(GraphicsContext *graphicsContext) { ui_draw2d.PopDrawMatrix(); ui_draw2d_front.PopDrawMatrix(); + + static int renderCounter = 0; + if (renderCounter < 10 && ++renderCounter == 10) { + // We're rendering fine, clear out failure info. + ClearFailedGPUBackends(); + } } void HandleGlobalMessage(const std::string &msg, const std::string &value) { diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 987bb8b5e4..5a81b5ceb8 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -132,6 +132,18 @@ void MainThreadFunc() { bool performingRestart = NativeIsRestarting(); NativeInit(static_cast(args.size()), &args[0], "1234", "1234", nullptr); + if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { + Reporting::ReportMessage("Graphics init error: %s", "ALL"); + + I18NCategory *err = GetI18NCategory("Error"); + const char *defaultErrorAll = "Failed initializing any graphics. Try upgrading your graphics drivers."; + const char *genericError = err->T("GenericAllGraphicsError", defaultErrorAll); + std::wstring title = ConvertUTF8ToWString(err->T("GenericGraphicsError", "Graphics Error")); + MessageBox(0, ConvertUTF8ToWString(genericError).c_str(), title.c_str(), MB_OK); + + // Let's continue (and probably crash) just so they have a way to keep trying. + } + host->UpdateUI(); std::string error_string; @@ -182,6 +194,8 @@ void MainThreadFunc() { if (yes) { // Change the config to the alternative and restart. g_Config.iGPUBackend = (int)nextBackend; + // Clear this to ensure we try their selection. + g_Config.sFailedGPUBackends.clear(); g_Config.Save(); W32Util::ExitAndRestart(); diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 806458b1e8..1689b51d6c 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -145,6 +145,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { if (yes) { // Change the config to D3D and restart. g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D9; + g_Config.sFailedGPUBackends.clear(); g_Config.Save(); W32Util::ExitAndRestart(); diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index f3808583b0..527860d2ee 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -267,6 +267,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { std::wstring whichD3D9 = ConvertUTF8ToWString(err->T("D3D9or11", d3d9Or11)); bool d3d9 = IDYES == MessageBox(hWnd_, whichD3D9.c_str(), title.c_str(), MB_YESNO); g_Config.iGPUBackend = d3d9 ? (int)GPUBackend::DIRECT3D9 : (int)GPUBackend::DIRECT3D11; + g_Config.sFailedGPUBackends.clear(); g_Config.Save(); W32Util::ExitAndRestart(); From e246a946c3a998adf8e186c2230018b8b815871b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Sep 2018 14:16:39 -0700 Subject: [PATCH 20/37] Core: Add flag to prevent graphics cycling. For debugging graphics crashes. --- UI/NativeApp.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 6f82c63fb6..8fb10bfbf7 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -353,6 +353,10 @@ static void CheckFailedGPUBackends() { g_Config.sFailedGPUBackends = data; } + // Use this if you want to debug a graphics crash... + if (g_Config.sFailedGPUBackends == "IGNORE") + return; + // Okay, let's not try a backend in the failed list. g_Config.iGPUBackend = g_Config.NextValidBackend(); // And then let's - for now - add the current to the failed list. @@ -374,6 +378,9 @@ static void CheckFailedGPUBackends() { } static void ClearFailedGPUBackends() { + if (g_Config.sFailedGPUBackends == "IGNORE") + return; + // We've successfully started graphics without crashing, hurray. // In case they update drivers and have totally different problems much later, clear the failed list. g_Config.sFailedGPUBackends.clear(); From 8ae2b1e6fbd095707269db09e4a9b7d2f6f9b61c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 2 Sep 2018 09:30:46 -0700 Subject: [PATCH 21/37] TexCache: Optimize DXT3/DXT5 decode to single pass. This is significantly faster on Vulkan, and in other situations where we're decoding directly to uncached memory. --- GPU/Common/TextureDecoder.cpp | 166 +++++++++++++++++++++------------- 1 file changed, 102 insertions(+), 64 deletions(-) diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 116c84c1a8..09fa197c73 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -327,16 +327,28 @@ void SetupTextureDecoder() { #endif } +// S3TC / DXT Decoder +class DXTDecoder { +public: + inline void DecodeColors(const DXT1Block *src, bool ignore1bitAlpha); + inline void DecodeAlphaDXT5(const DXT5Block *src); + inline void WriteColorsDXT1(u32 *dst, const DXT1Block *src, int pitch, int height); + inline void WriteColorsDXT3(u32 *dst, const DXT3Block *src, int pitch, int height); + inline void WriteColorsDXT5(u32 *dst, const DXT5Block *src, int pitch, int height); + +protected: + u32 colors_[4]; + u8 alpha_[8]; +}; + static inline u32 makecol(int r, int g, int b, int a) { return (a << 24) | (r << 16) | (g << 8) | b; } // This could probably be done faster by decoding two or four blocks at a time with SSE/NEON. -void DecodeDXT1Block(u32 *dst, const DXT1Block *src, int pitch, int height, bool ignore1bitAlpha) { - // S3TC Decoder - // Needs more speed and debugging. - u16 c1 = (src->color1); - u16 c2 = (src->color2); +void DXTDecoder::DecodeColors(const DXT1Block *src, bool ignore1bitAlpha) { + u16 c1 = src->color1; + u16 c2 = src->color2; int red1 = Convert5To8(c1 & 0x1F); int red2 = Convert5To8(c2 & 0x1F); int green1 = Convert6To8((c1 >> 5) & 0x3F); @@ -344,44 +356,22 @@ void DecodeDXT1Block(u32 *dst, const DXT1Block *src, int pitch, int height, bool int blue1 = Convert5To8((c1 >> 11) & 0x1F); int blue2 = Convert5To8((c2 >> 11) & 0x1F); - u32 colors[4]; - colors[0] = makecol(red1, green1, blue1, 255); - colors[1] = makecol(red2, green2, blue2, 255); + colors_[0] = makecol(red1, green1, blue1, 255); + colors_[1] = makecol(red2, green2, blue2, 255); if (c1 > c2 || ignore1bitAlpha) { - int blue3 = ((blue2 - blue1) >> 1) - ((blue2 - blue1) >> 3); + int red3 = ((red2 - red1) >> 1) - ((red2 - red1) >> 3); int green3 = ((green2 - green1) >> 1) - ((green2 - green1) >> 3); - int red3 = ((red2 - red1) >> 1) - ((red2 - red1) >> 3); - colors[2] = makecol(red1 + red3, green1 + green3, blue1 + blue3, 255); - colors[3] = makecol(red2 - red3, green2 - green3, blue2 - blue3, 255); + int blue3 = ((blue2 - blue1) >> 1) - ((blue2 - blue1) >> 3); + colors_[2] = makecol(red1 + red3, green1 + green3, blue1 + blue3, 255); + colors_[3] = makecol(red2 - red3, green2 - green3, blue2 - blue3, 255); } else { - colors[2] = makecol((red1 + red2 + 1) / 2, // Average - (green1 + green2 + 1) / 2, - (blue1 + blue2 + 1) / 2, 255); - colors[3] = makecol(red2, green2, blue2, 0); // Color2 but transparent - } - - for (int y = 0; y < height; y++) { - int val = src->lines[y]; - for (int x = 0; x < 4; x++) { - dst[x] = colors[val & 3]; - val >>= 2; - } - dst += pitch; - } -} - -void DecodeDXT3Block(u32 *dst, const DXT3Block *src, int pitch, int height) -{ - DecodeDXT1Block(dst, &src->color, pitch, height, true); - - for (int y = 0; y < height; y++) { - u32 line = src->alphaLines[y]; - for (int x = 0; x < 4; x++) { - const u8 a4 = line & 0xF; - dst[x] = (dst[x] & 0xFFFFFF) | (a4 << 24) | (a4 << 28); - line >>= 4; - } - dst += pitch; + // Average + int red3 = (red1 + red2 + 1) / 2; + int green3 = (green1 + green2 + 1) / 2; + int blue3 = (blue1 + blue2 + 1) / 2; + colors_[2] = makecol(red3, green3, blue3, 255); + // Color2 but transparent + colors_[3] = makecol(red2, green2, blue2, 0); } } @@ -395,40 +385,88 @@ static inline u8 lerp6(const DXT5Block *src, int n) { return (u8)(src->alpha1 + (src->alpha2 - src->alpha1) * d); } -// The alpha channel is not 100% correct -void DecodeDXT5Block(u32 *dst, const DXT5Block *src, int pitch, int height) { - DecodeDXT1Block(dst, &src->color, pitch, height, true); - u8 alpha[8]; - - alpha[0] = src->alpha1; - alpha[1] = src->alpha2; - if (alpha[0] > alpha[1]) { - alpha[2] = lerp8(src, 1); - alpha[3] = lerp8(src, 2); - alpha[4] = lerp8(src, 3); - alpha[5] = lerp8(src, 4); - alpha[6] = lerp8(src, 5); - alpha[7] = lerp8(src, 6); +void DXTDecoder::DecodeAlphaDXT5(const DXT5Block *src) { + // TODO: Check if alpha is still not 100% correct. + alpha_[0] = src->alpha1; + alpha_[1] = src->alpha2; + if (alpha_[0] > alpha_[1]) { + alpha_[2] = lerp8(src, 1); + alpha_[3] = lerp8(src, 2); + alpha_[4] = lerp8(src, 3); + alpha_[5] = lerp8(src, 4); + alpha_[6] = lerp8(src, 5); + alpha_[7] = lerp8(src, 6); } else { - alpha[2] = lerp6(src, 1); - alpha[3] = lerp6(src, 2); - alpha[4] = lerp6(src, 3); - alpha[5] = lerp6(src, 4); - alpha[6] = 0; - alpha[7] = 255; + alpha_[2] = lerp6(src, 1); + alpha_[3] = lerp6(src, 2); + alpha_[4] = lerp6(src, 3); + alpha_[5] = lerp6(src, 4); + alpha_[6] = 0; + alpha_[7] = 255; } +} - u64 data = ((u64)(u16)src->alphadata1 << 32) | (u32)src->alphadata2; - +void DXTDecoder::WriteColorsDXT1(u32 *dst, const DXT1Block *src, int pitch, int height) { for (int y = 0; y < height; y++) { + int colordata = src->lines[y]; for (int x = 0; x < 4; x++) { - dst[x] = (dst[x] & 0xFFFFFF) | (alpha[data & 7] << 24); - data >>= 3; + dst[x] = colors_[colordata & 3]; + colordata >>= 2; } dst += pitch; } } +void DXTDecoder::WriteColorsDXT3(u32 *dst, const DXT3Block *src, int pitch, int height) { + for (int y = 0; y < height; y++) { + int colordata = src->color.lines[y]; + u32 alphadata = src->alphaLines[y]; + for (int x = 0; x < 4; x++) { + const u8 a4 = alphadata & 0xF; + dst[x] = (colors_[colordata & 3] & 0x00FFFFFF) | (a4 << 24) | (a4 << 28); + colordata >>= 2; + alphadata >>= 4; + } + dst += pitch; + } +} + +void DXTDecoder::WriteColorsDXT5(u32 *dst, const DXT5Block *src, int pitch, int height) { + // 48 bits, 3 bit index per pixel, 12 bits per line. + u64 alphadata = ((u64)(u16)src->alphadata1 << 32) | (u32)src->alphadata2; + + for (int y = 0; y < height; y++) { + int colordata = src->color.lines[y]; + for (int x = 0; x < 4; x++) { + dst[x] = (colors_[colordata & 3] & 0x00FFFFFF) | (alpha_[alphadata & 7] << 24); + colordata >>= 2; + alphadata >>= 3; + } + dst += pitch; + } +} + +// This could probably be done faster by decoding two or four blocks at a time with SSE/NEON. +void DecodeDXT1Block(u32 *dst, const DXT1Block *src, int pitch, int height, bool ignore1bitAlpha) { + DXTDecoder dxt; + dxt.DecodeColors(src, ignore1bitAlpha); + dxt.WriteColorsDXT1(dst, src, pitch, height); +} + +void DecodeDXT3Block(u32 *dst, const DXT3Block *src, int pitch, int height) { + DXTDecoder dxt; + dxt.DecodeColors(&src->color, true); + dxt.WriteColorsDXT3(dst, src, pitch, height); +} + +// The alpha channel is not 100% correct +void DecodeDXT5Block(u32 *dst, const DXT5Block *src, int pitch, int height) { + DXTDecoder dxt; + dxt.DecodeColors(&src->color, true); + dxt.DecodeAlphaDXT5(src); + dxt.WriteColorsDXT5(dst, src, pitch, height); +} + #ifdef _M_SSE static inline u32 CombineSSEBitsToDWORD(const __m128i &v) { __m128i temp; From 3f35221f3ba8bdb4cd748f6f4adf797281f90ed8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 2 Sep 2018 09:53:31 -0700 Subject: [PATCH 22/37] TexCache: Avoid masking out alpha for DXT3/DXT5. A little faster. Also refactor colors a bit to be more readable. --- GPU/Common/TextureDecoder.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 09fa197c73..0237ed08e5 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -345,6 +345,11 @@ static inline u32 makecol(int r, int g, int b, int a) { return (a << 24) | (r << 16) | (g << 8) | b; } +static inline int mul_3_8(int c) { + // This is 3/8 * c = 4/8 * c - 1/8 * c. + return (c >> 1) - (c >> 3); +} + // This could probably be done faster by decoding two or four blocks at a time with SSE/NEON. void DXTDecoder::DecodeColors(const DXT1Block *src, bool ignore1bitAlpha) { u16 c1 = src->color1; @@ -356,14 +361,17 @@ void DXTDecoder::DecodeColors(const DXT1Block *src, bool ignore1bitAlpha) { int blue1 = Convert5To8((c1 >> 11) & 0x1F); int blue2 = Convert5To8((c2 >> 11) & 0x1F); - colors_[0] = makecol(red1, green1, blue1, 255); - colors_[1] = makecol(red2, green2, blue2, 255); + // Keep alpha zero for non-DXT1 to skip masking the colors. + int alpha = ignore1bitAlpha ? 0 : 255; + + colors_[0] = makecol(red1, green1, blue1, alpha); + colors_[1] = makecol(red2, green2, blue2, alpha); if (c1 > c2 || ignore1bitAlpha) { - int red3 = ((red2 - red1) >> 1) - ((red2 - red1) >> 3); - int green3 = ((green2 - green1) >> 1) - ((green2 - green1) >> 3); - int blue3 = ((blue2 - blue1) >> 1) - ((blue2 - blue1) >> 3); - colors_[2] = makecol(red1 + red3, green1 + green3, blue1 + blue3, 255); - colors_[3] = makecol(red2 - red3, green2 - green3, blue2 - blue3, 255); + int red3 = mul_3_8(red2 - red1); + int green3 = mul_3_8(green2 - green1); + int blue3 = mul_3_8(blue2 - blue1); + colors_[2] = makecol(red1 + red3, green1 + green3, blue1 + blue3, alpha); + colors_[3] = makecol(red2 - red3, green2 - green3, blue2 - blue3, alpha); } else { // Average int red3 = (red1 + red2 + 1) / 2; @@ -423,7 +431,7 @@ void DXTDecoder::WriteColorsDXT3(u32 *dst, const DXT3Block *src, int pitch, int u32 alphadata = src->alphaLines[y]; for (int x = 0; x < 4; x++) { const u8 a4 = alphadata & 0xF; - dst[x] = (colors_[colordata & 3] & 0x00FFFFFF) | (a4 << 24) | (a4 << 28); + dst[x] = colors_[colordata & 3] | (a4 << 24) | (a4 << 28); colordata >>= 2; alphadata >>= 4; } @@ -438,7 +446,7 @@ void DXTDecoder::WriteColorsDXT5(u32 *dst, const DXT5Block *src, int pitch, int for (int y = 0; y < height; y++) { int colordata = src->color.lines[y]; for (int x = 0; x < 4; x++) { - dst[x] = (colors_[colordata & 3] & 0x00FFFFFF) | (alpha_[alphadata & 7] << 24); + dst[x] = colors_[colordata & 3] | (alpha_[alphadata & 7] << 24); colordata >>= 2; alphadata >>= 3; } From d1c566861250cca52f7f47eeed310e91c0053cd5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 2 Sep 2018 10:19:08 -0700 Subject: [PATCH 23/37] Android: Add .nomedia files to system and textures. Due it on all platforms when saving textures to encourage including .nomedia in texture packs. --- Core/TextureReplacer.cpp | 2 ++ UI/NativeApp.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index fdb9254ec4..82ad35cac5 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -58,6 +58,7 @@ void TextureReplacer::NotifyConfigChanged() { // If we're saving, auto-create the directory. if (g_Config.bSaveNewTextures && !File::Exists(basePath_ + NEW_TEXTURE_DIR)) { File::CreateFullPath(basePath_ + NEW_TEXTURE_DIR); + File::CreateEmptyFile(basePath_ + NEW_TEXTURE_DIR + "/.nomedia"); } enabled_ = File::Exists(basePath_) && File::IsDirectory(basePath_); @@ -400,6 +401,7 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl const std::string saveDirectory = basePath_ + NEW_TEXTURE_DIR + hashfile.substr(0, slash); if (!File::Exists(saveDirectory)) { File::CreateFullPath(saveDirectory); + File::CreateEmptyFile(saveDirectory + "/.nomedia"); } } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 10850b33df..c661ac54ff 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -345,6 +345,7 @@ void CreateDirectoriesAndroid() { // Avoid media scanners in PPSSPP_STATE and SAVEDATA directories File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/PPSSPP_STATE/.nomedia"); File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/SAVEDATA/.nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SYSTEM) + ".nomedia"); } void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir) { From d715ed8fbaeb8a632b925beb4c2de7b829846eb3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 2 Sep 2018 10:27:11 -0700 Subject: [PATCH 24/37] Core: More consistently use directory constants. --- Common/FileUtil.cpp | 16 ++++++++++------ Core/SaveState.cpp | 11 +++-------- Core/System.cpp | 6 +++--- UI/MiscScreens.cpp | 2 +- UI/NativeApp.cpp | 20 ++++++++++---------- 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 88581332c7..145040f1ed 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -269,9 +269,11 @@ bool Delete(const std::string &filename) { // Returns true if successful, or path already exists. bool CreateDir(const std::string &path) { - DEBUG_LOG(COMMON, "CreateDir('%s')", path.c_str()); + std::string fn = path; + StripTailDirSlashes(fn); + DEBUG_LOG(COMMON, "CreateDir('%s')", fn.c_str()); #ifdef _WIN32 - if (::CreateDirectory(ConvertUTF8ToWString(path).c_str(), NULL)) + if (::CreateDirectory(ConvertUTF8ToWString(fn).c_str(), NULL)) return true; DWORD error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) @@ -282,25 +284,27 @@ bool CreateDir(const std::string &path) ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); return false; #else - if (mkdir(path.c_str(), 0755) == 0) + if (mkdir(fn.c_str(), 0755) == 0) return true; int err = errno; if (err == EEXIST) { - WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); + WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", fn.c_str()); return true; } - ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); + ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", fn.c_str(), strerror(err)); return false; #endif } // Creates the full path of fullPath returns true on success -bool CreateFullPath(const std::string &fullPath) +bool CreateFullPath(const std::string &path) { + std::string fullPath = path; + StripTailDirSlashes(fullPath); int panicCounter = 100; VERBOSE_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 0e6371ed5a..687ab0983c 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -410,13 +410,8 @@ namespace SaveState } fullDiscId = StringFromFormat("%s_%s", discId.c_str(), discVer.c_str()); - std::string temp = StringFromFormat("ms0:/PSP/PPSSPP_STATE/%s_%d.%s", fullDiscId.c_str(), slot, extension); - std::string hostPath; - if (pspFileSystem.GetHostPath(temp, hostPath)) { - return hostPath; - } else { - return ""; - } + std::string filename = StringFromFormat("%s_%d.%s", fullDiscId.c_str(), slot, extension); + return GetSysDirectory(DIRECTORY_SAVESTATE) + filename; } int GetCurrentSlot() @@ -878,7 +873,7 @@ namespace SaveState void Init() { // Make sure there's a directory for save slots - pspFileSystem.MkDir("ms0:/PSP/PPSSPP_STATE"); + File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVESTATE)); std::lock_guard guard(mutex); rewindStates.Clear(); diff --git a/Core/System.cpp b/Core/System.cpp index bd55510b06..5bc2208034 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -596,9 +596,9 @@ void InitSysDirectories() { // expect a standard environment. Skipping THEME though, that's pointless. File::CreateDir(g_Config.memStickDirectory + "PSP"); File::CreateDir(g_Config.memStickDirectory + "PSP/COMMON"); - File::CreateDir(g_Config.memStickDirectory + "PSP/GAME"); - File::CreateDir(g_Config.memStickDirectory + "PSP/SAVEDATA"); - File::CreateDir(g_Config.memStickDirectory + "PSP/PPSSPP_STATE"); + File::CreateDir(GetSysDirectory(DIRECTORY_GAME)); + File::CreateDir(GetSysDirectory(DIRECTORY_SAVEDATA)); + File::CreateDir(GetSysDirectory(DIRECTORY_SAVESTATE)); if (g_Config.currentDirectory.empty()) { g_Config.currentDirectory = GetSysDirectory(DIRECTORY_GAME); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 8ba665a7eb..8433bd213c 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -398,7 +398,7 @@ void NewLanguageScreen::OnCompleted(DialogResult result) { bool iniLoadedSuccessfully = false; // Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to // test new languages without recompiling the entire app, which is a hassle). - const std::string langOverridePath = g_Config.memStickDirectory + "PSP/SYSTEM/lang/"; + const std::string langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) + "lang/"; // If we run into the unlikely case that "lang" is actually a file, just use the built-in translations. if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath)) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c661ac54ff..690c810522 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -323,7 +323,7 @@ static void PostLoadConfig() { // Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to // test new languages without recompiling the entire app, which is a hassle). - const std::string langOverridePath = g_Config.memStickDirectory + "PSP/SYSTEM/lang/"; + const std::string langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) + "lang/"; // If we run into the unlikely case that "lang" is actually a file, just use the built-in translations. if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath)) @@ -336,15 +336,15 @@ void CreateDirectoriesAndroid() { // On Android, create a PSP directory tree in the external_dir, // to hopefully reduce confusion a bit. ILOG("Creating %s", (g_Config.memStickDirectory + "PSP").c_str()); - File::CreateDir(g_Config.memStickDirectory + "PSP"); - File::CreateDir(g_Config.memStickDirectory + "PSP/SAVEDATA"); - File::CreateDir(g_Config.memStickDirectory + "PSP/PPSSPP_STATE"); - File::CreateDir(g_Config.memStickDirectory + "PSP/GAME"); - File::CreateDir(g_Config.memStickDirectory + "PSP/SYSTEM"); + File::CreateFullPath(g_Config.memStickDirectory + "PSP"); + File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVEDATA)); + File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVESTATE)); + File::CreateFullPath(GetSysDirectory(DIRECTORY_GAME)); + File::CreateFullPath(GetSysDirectory(DIRECTORY_SYSTEM)); // Avoid media scanners in PPSSPP_STATE and SAVEDATA directories - File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/PPSSPP_STATE/.nomedia"); - File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/SAVEDATA/.nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVESTATE) + ".nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVEDATA) + ".nomedia"); File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SYSTEM) + ".nomedia"); } @@ -421,8 +421,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #ifndef _WIN32 g_Config.AddSearchPath(user_data_path); - g_Config.AddSearchPath(g_Config.memStickDirectory + "PSP/SYSTEM/"); - g_Config.SetDefaultPath(g_Config.memStickDirectory + "PSP/SYSTEM/"); + g_Config.AddSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); + g_Config.SetDefaultPath(GetSysDirectory(DIRECTORY_SYSTEM)); // Note that if we don't have storage permission here, loading the config will // fail and it will be set to the default. Later, we load again when we get permission. From f65edc20a3ebc7c4e67b5afc872f0e7f546282a2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 2 Sep 2018 11:41:27 -0700 Subject: [PATCH 25/37] TexCache: Optimize DXT5 alpha lerp. This makes the overall DXT5 decode about 8% faster. --- GPU/Common/TextureDecoder.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 0237ed08e5..5bd2e34083 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -384,13 +384,16 @@ void DXTDecoder::DecodeColors(const DXT1Block *src, bool ignore1bitAlpha) { } static inline u8 lerp8(const DXT5Block *src, int n) { - float d = n / 7.0f; - return (u8)(src->alpha1 + (src->alpha2 - src->alpha1) * d); + // These weights translate alpha1/alpha2 to fixed 8.8 point, pre-divided by 7. + int weight1 = ((7 - n) << 8) / 7; + int weight2 = (n << 8) / 7; + return (u8)((src->alpha1 * weight1 + src->alpha2 * weight2) >> 8); } static inline u8 lerp6(const DXT5Block *src, int n) { - float d = n / 5.0f; - return (u8)(src->alpha1 + (src->alpha2 - src->alpha1) * d); + int weight1 = ((5 - n) << 8) / 5; + int weight2 = (n << 8) / 5; + return (u8)((src->alpha1 * weight1 + src->alpha2 * weight2) >> 8); } void DXTDecoder::DecodeAlphaDXT5(const DXT5Block *src) { From f87dcefdd89b72953c6b7172ac3e887d9a7f823b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 3 Sep 2018 18:41:52 -0700 Subject: [PATCH 26/37] Android: Buildfix for CMake. May affect other arm64 too. Also, ifdef out a check in case of compiling with GL 3 headers (to fix a Linux buildbot issue.) --- CMakeLists.txt | 6 ++---- ext/native/gfx_es2/gpu_features.cpp | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f9a21e4f4..a8f229dae4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1194,10 +1194,6 @@ if (NOT MOBILE_DEVICE) Core/WaveFile.h) endif() -if(ARMV7) - set(CORE_NEON Core/Util/AudioFormatNEON.cpp Core/Util/AudioFormatNEON.h) -endif() - set(GPU_GLES GPU/GLES/DepalettizeShaderGLES.cpp GPU/GLES/DepalettizeShaderGLES.h @@ -1713,6 +1709,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/TextureReplacer.h Core/Util/AudioFormat.cpp Core/Util/AudioFormat.h + Core/Util/AudioFormatNEON.cpp + Core/Util/AudioFormatNEON.h Core/Util/GameManager.cpp Core/Util/GameManager.h Core/Util/BlockAllocator.cpp diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index 2d7e00d16a..eca5dc4869 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -431,6 +431,7 @@ void CheckGLExtensions() { glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &gl_extensions.maxVertexTextureUnits); +#ifdef GL_LOW_FLOAT // This is probably a waste of time, implementations lie. if (gl_extensions.IsGLES || strstr(extString, "GL_ARB_ES2_compatibility") || gl_extensions.VersionGEThan(4, 1)) { const GLint precisions[6] = { @@ -446,6 +447,7 @@ void CheckGLExtensions() { } } } +#endif gl_extensions.ARB_framebuffer_object = strstr(extString, "GL_ARB_framebuffer_object") != 0; gl_extensions.EXT_framebuffer_object = strstr(extString, "GL_EXT_framebuffer_object") != 0; From 246234b0b7c93822f29bd75e23d92b42ad3d25a5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 4 Sep 2018 21:53:20 -0700 Subject: [PATCH 27/37] UI: Automatically forget pinned deleted paths. Also, resolve the paths in case of symlinks. --- Core/Config.cpp | 5 ++++- UI/MainScreen.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 5b251058bd..dd6513341b 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1037,7 +1037,10 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { auto pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths")->ToMap(); vPinnedPaths.clear(); for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) { - vPinnedPaths.push_back(it->second); + // Unpin paths that are deleted automatically. + if (File::Exists(it->second)) { + vPinnedPaths.push_back(File::ResolvePath(it->second)); + } } // This caps the exponent 4 (so 16x.) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 5d1d521aac..1a5d31cee7 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -495,10 +495,11 @@ UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) { UI::EventReturn GameBrowser::PinToggleClick(UI::EventParams &e) { auto &pinnedPaths = g_Config.vPinnedPaths; + const std::string path = File::ResolvePath(path_.GetPath()); if (IsCurrentPathPinned()) { - pinnedPaths.erase(std::remove(pinnedPaths.begin(), pinnedPaths.end(), path_.GetPath()), pinnedPaths.end()); + pinnedPaths.erase(std::remove(pinnedPaths.begin(), pinnedPaths.end(), path), pinnedPaths.end()); } else { - pinnedPaths.push_back(path_.GetPath()); + pinnedPaths.push_back(path); } Refresh(); return UI::EVENT_DONE; @@ -662,7 +663,7 @@ void GameBrowser::Refresh() { bool GameBrowser::IsCurrentPathPinned() { const auto paths = g_Config.vPinnedPaths; - return std::find(paths.begin(), paths.end(), path_.GetPath()) != paths.end(); + return std::find(paths.begin(), paths.end(), File::ResolvePath(path_.GetPath())) != paths.end(); } const std::vector GameBrowser::GetPinnedPaths() { @@ -672,7 +673,7 @@ const std::vector GameBrowser::GetPinnedPaths() { static const std::string sepChars = "/\\"; #endif - const std::string currentPath = path_.GetPath(); + const std::string currentPath = File::ResolvePath(path_.GetPath()); const std::vector paths = g_Config.vPinnedPaths; std::vector results; for (size_t i = 0; i < paths.size(); ++i) { From 2388be544fa5203525859a1b9a54fd42b34a84a7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 4 Sep 2018 21:54:25 -0700 Subject: [PATCH 28/37] Debugger: Fix crash in softgpu with no texaddr yet. --- GPU/Software/Rasterizer.cpp | 13 ++++++++----- ext/native/thin3d/VulkanRenderManager.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index ebed8f36de..b0b266ee7b 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -1764,17 +1764,20 @@ bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) return false; } - int w = gstate.getTextureWidth(level); - int h = gstate.getTextureHeight(level); - buffer.Allocate(w, h, GE_FORMAT_8888, false); - GETextureFormat texfmt = gstate.getTextureFormat(); u32 texaddr = gstate.getTextureAddress(level); int texbufw = GetTextureBufw(level, texaddr, texfmt); - u8 *texptr = Memory::GetPointer(texaddr); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); + + if (!texaddr || !Memory::IsValidRange(texaddr, (textureBitsPerPixel[texfmt] * texbufw * h) / 8)) + return false; + + buffer.Allocate(w, h, GE_FORMAT_8888, false); Sampler::Funcs sampler = Sampler::GetFuncs(); + u8 *texptr = Memory::GetPointer(texaddr); u32 *row = (u32 *)buffer.GetData(); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 812943b571..a01482b69e 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -176,7 +176,7 @@ public: data.draw.vbuffer = vbuffer; data.draw.voffset = voffset; data.draw.numUboOffsets = numUboOffsets; - assert(numUboOffsets <= ARRAY_SIZE(data.drawIndexed.uboOffsets)); + assert(numUboOffsets <= ARRAY_SIZE(data.draw.uboOffsets)); for (int i = 0; i < numUboOffsets; i++) data.draw.uboOffsets[i] = uboOffsets[i]; curRenderStep_->commands.push_back(data); From db93a08fc9e9236fcd72d01eb788c9528b6951aa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 5 Sep 2018 23:13:15 -0700 Subject: [PATCH 29/37] Core: Log more on graphics backend failures. Just so we can tell for sure in logs what's happening. --- Core/Config.cpp | 3 +++ UI/NativeApp.cpp | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 5b251058bd..62c8808ae2 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -557,6 +557,8 @@ int Config::NextValidBackend() { } if (failed.count(iGPUBackend)) { + ERROR_LOG(LOADER, "Graphics backend failed for %d, trying another", iGPUBackend); + #if (PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)) && !PPSSPP_PLATFORM(UWP) if (VulkanMayBeAvailable() && !failed.count((int)GPUBackend::VULKAN)) { return (int)GPUBackend::VULKAN; @@ -580,6 +582,7 @@ int Config::NextValidBackend() { // They've all failed. Let them try the default. sFailedGPUBackends += ",ALL"; + ERROR_LOG(LOADER, "All graphics backends failed"); return DefaultGPUBackend(); } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 5beab45f1d..9c1610718b 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -357,9 +357,14 @@ static void CheckFailedGPUBackends() { // Use this if you want to debug a graphics crash... if (g_Config.sFailedGPUBackends == "IGNORE") return; + else if (!g_Config.sFailedGPUBackends.empty()) + ERROR_LOG(LOADER, "Failed graphics backends: %s", g_Config.sFailedGPUBackends.c_str()); // Okay, let's not try a backend in the failed list. - g_Config.iGPUBackend = g_Config.NextValidBackend(); + int newBackend = g_Config.NextValidBackend(); + if (newBackend != g_Config.iGPUBackend) + WARN_LOG(LOADER, "Failed graphics backend switched from %d to %d", g_Config.iGPUBackend, newBackend); + g_Config.iGPUBackend = newBackend; // And then let's - for now - add the current to the failed list. if (g_Config.sFailedGPUBackends.empty()) { g_Config.sFailedGPUBackends = StringFromFormat("%d", g_Config.iGPUBackend); From fb8f408db010943b5dc90ee090e7efece1662c61 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 6 Sep 2018 07:31:15 -0700 Subject: [PATCH 30/37] Core: Don't consider graphics failed on shutdown. On some devices, we'll lose context or get a resize mid-startup, so make sure we don't detect that as a graphics crash. --- UI/NativeApp.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 9c1610718b..c85c1ea6a5 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -346,6 +346,14 @@ void CreateDirectoriesAndroid() { } static void CheckFailedGPUBackends() { + // We only want to do this once per process run and backend, to detect process crashes. + // If NativeShutdown is called before we finish, we might call this multiple times. + static int lastBackend = -1; + if (lastBackend == g_Config.iGPUBackend) { + return; + } + lastBackend = g_Config.iGPUBackend; + std::string cache = GetSysDirectory(DIRECTORY_APP_CACHE) + "/FailedGraphicsBackends.txt"; if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { @@ -361,10 +369,9 @@ static void CheckFailedGPUBackends() { ERROR_LOG(LOADER, "Failed graphics backends: %s", g_Config.sFailedGPUBackends.c_str()); // Okay, let's not try a backend in the failed list. - int newBackend = g_Config.NextValidBackend(); - if (newBackend != g_Config.iGPUBackend) - WARN_LOG(LOADER, "Failed graphics backend switched from %d to %d", g_Config.iGPUBackend, newBackend); - g_Config.iGPUBackend = newBackend; + g_Config.iGPUBackend = g_Config.NextValidBackend(); + if (lastBackend != g_Config.iGPUBackend) + WARN_LOG(LOADER, "Failed graphics backend switched from %d to %d", lastBackend, g_Config.iGPUBackend); // And then let's - for now - add the current to the failed list. if (g_Config.sFailedGPUBackends.empty()) { g_Config.sFailedGPUBackends = StringFromFormat("%d", g_Config.iGPUBackend); From 120cf59237273e6197d87ecb305912dff0a894ba Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 6 Sep 2018 18:31:09 -0700 Subject: [PATCH 31/37] Core: Reset working graphics backend on restart. --- UI/NativeApp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c85c1ea6a5..00bf8a3074 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -142,6 +142,7 @@ static bool resized = false; static bool restarting = false; static bool askedForStoragePermission = false; +static int renderCounter = 0; struct PendingMessage { std::string msg; @@ -689,6 +690,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // When it's reset we don't want to forget all our managed things. CheckFailedGPUBackends(); SetGPUBackend((GPUBackend) g_Config.iGPUBackend); + renderCounter = 0; // Must be done restarting by now. restarting = false; @@ -1010,7 +1012,6 @@ void NativeRender(GraphicsContext *graphicsContext) { ui_draw2d.PopDrawMatrix(); ui_draw2d_front.PopDrawMatrix(); - static int renderCounter = 0; if (renderCounter < 10 && ++renderCounter == 10) { // We're rendering fine, clear out failure info. ClearFailedGPUBackends(); From 1b4f1d7d4e659dcad481bdc513acc8c987ec1124 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 7 Sep 2018 21:36:59 -0700 Subject: [PATCH 32/37] Replacement: Cut down on parsing for large inis. --- Core/TextureReplacer.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 82ad35cac5..cc9b4788be 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -112,29 +112,24 @@ bool TextureReplacer::LoadIni() { ERROR_LOG(G3D, "Unsupported texture replacement version %d, trying anyway", version); } - std::vector hashNames; - if (ini.GetKeys("hashes", hashNames)) { - auto hashes = ini.GetOrCreateSection("hashes"); + if (ini.HasSection("hashes")) { + auto hashes = ini.GetOrCreateSection("hashes")->ToMap(); // Format: hashname = filename.png - for (std::string hashName : hashNames) { + for (const auto &item : hashes) { ReplacementAliasKey key(0, 0, 0); - if (sscanf(hashName.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &key.level) >= 1) { - hashes->Get(hashName.c_str(), &aliases_[key], ""); + if (sscanf(item.first.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &key.level) >= 1) { + aliases_[key] = item.second; } else { - ERROR_LOG(G3D, "Unsupported syntax under [hashes]: %s", hashName.c_str()); + ERROR_LOG(G3D, "Unsupported syntax under [hashes]: %s", item.first.c_str()); } } } - std::vector hashrangeKeys; - if (ini.GetKeys("hashranges", hashrangeKeys)) { - auto hashranges = ini.GetOrCreateSection("hashranges"); + if (ini.HasSection("hashranges")) { + auto hashranges = ini.GetOrCreateSection("hashranges")->ToMap(); // Format: addr,w,h = newW,newH - for (const std::string &key : hashrangeKeys) { - std::string value; - if (hashranges->Get(key.c_str(), &value, "")) { - ParseHashRange(key, value); - } + for (const auto &item : hashranges) { + ParseHashRange(item.first, item.second); } } } From 1fb111c2bbb8f81ee5cfd250508ff13adeaa534b Mon Sep 17 00:00:00 2001 From: AreaScout Date: Fri, 7 Sep 2018 10:44:41 +0000 Subject: [PATCH 33/37] Add: Touchscreen event support for SDL2 --- SDL/SDLMain.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index d28f21936e..14afdd040c 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -355,6 +355,7 @@ int main(int argc, char *argv[]) { int set_xres = -1; int set_yres = -1; + int w = 0, h = 0; bool portrait = false; bool set_ipad = false; float set_dpi = 1.0f; @@ -601,7 +602,7 @@ int main(int argc, char *argv[]) { while (true) { double startTime = time_now_d(); - SDL_Event event; + SDL_Event event, touchEvent; while (SDL_PollEvent(&event)) { float mx = event.motion.x * g_dpi_scale_x; float my = event.motion.y * g_dpi_scale_y; @@ -681,6 +682,71 @@ int main(int argc, char *argv[]) { NativeKey(key); break; } + case SDL_FINGERMOTION: + { + if (!g_Config.bFullScreen) { break; } + SDL_GetWindowSize(window, &w, &h); + touchEvent.type = SDL_MOUSEMOTION; + touchEvent.motion.type = SDL_MOUSEMOTION; + touchEvent.motion.timestamp = event.tfinger.timestamp; + touchEvent.motion.windowID = SDL_GetWindowID(window); + touchEvent.motion.which = SDL_TOUCH_MOUSEID; + touchEvent.motion.state = SDL_GetMouseState(NULL, NULL); + touchEvent.motion.x = event.tfinger.x * w; + touchEvent.motion.y = event.tfinger.y * h; + + SDL_WarpMouseInWindow(window, event.tfinger.x * w, event.tfinger.y * h); + + SDL_PushEvent(&touchEvent); + break; + } + case SDL_FINGERDOWN: + { + if (!g_Config.bFullScreen) { break; } + SDL_GetWindowSize(window, &w, &h); + touchEvent.type = SDL_MOUSEBUTTONDOWN; + touchEvent.button.type = SDL_MOUSEBUTTONDOWN; + touchEvent.button.timestamp = SDL_GetTicks(); + touchEvent.button.windowID = SDL_GetWindowID(window); + touchEvent.button.which = SDL_TOUCH_MOUSEID; + touchEvent.button.button = SDL_BUTTON_LEFT; + touchEvent.button.state = SDL_PRESSED; + touchEvent.button.clicks = 1; + touchEvent.button.x = event.tfinger.x * w; + touchEvent.button.y = event.tfinger.y * h; + + touchEvent.motion.type = SDL_MOUSEMOTION; + touchEvent.motion.timestamp = SDL_GetTicks(); + touchEvent.motion.windowID = SDL_GetWindowID(window); + touchEvent.motion.which = SDL_TOUCH_MOUSEID; + touchEvent.motion.x = event.tfinger.x * w; + touchEvent.motion.y = event.tfinger.y * h; + // Any real mouse cursor should also move + SDL_WarpMouseInWindow(window, event.tfinger.x * w, event.tfinger.y * h); + // First finger down event also has to be a motion to that position + SDL_PushEvent(&touchEvent); + touchEvent.motion.type = SDL_MOUSEBUTTONDOWN; + // Now we push the mouse button event + SDL_PushEvent(&touchEvent); + break; + } + case SDL_FINGERUP: + { + if(!g_Config.bFullScreen) { break; } + SDL_GetWindowSize(window, &w, &h); + touchEvent.type = SDL_MOUSEBUTTONUP; + touchEvent.button.type = SDL_MOUSEBUTTONUP; + touchEvent.button.timestamp = SDL_GetTicks(); + touchEvent.button.windowID = SDL_GetWindowID(window); + touchEvent.button.which = SDL_TOUCH_MOUSEID; + touchEvent.button.button = SDL_BUTTON_LEFT; + touchEvent.button.state = SDL_RELEASED; + touchEvent.button.clicks = 1; + touchEvent.button.x = event.tfinger.x * w; + touchEvent.button.y = event.tfinger.y * h; + SDL_PushEvent(&touchEvent); + break; + } case SDL_MOUSEBUTTONDOWN: switch (event.button.button) { case SDL_BUTTON_LEFT: From a5f61e798e733dbbe2de5845bffd331fcd0aaa64 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Sep 2018 12:14:17 -0700 Subject: [PATCH 34/37] Mpeg: Avoid reading outside available bytes. When we're just looking for the size or offset, it shouldn't be necessary to load the stream, so hopefully we can avoid it. --- Core/HLE/sceMpeg.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index ae309d7b2e..e6bd1e0680 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -299,7 +299,8 @@ static u32 getMpegVersion(u32 mpegRawVersion) { default: return -1; } } -static void AnalyzeMpeg(u8 *buffer, MpegContext *ctx) { + +static void AnalyzeMpeg(u8 *buffer, u32 validSize, MpegContext *ctx) { ctx->mpegMagic = *(u32_le*)buffer; ctx->mpegRawVersion = *(u32_le*)(buffer + PSMF_STREAM_VERSION_OFFSET); ctx->mpegVersion = getMpegVersion(ctx->mpegRawVersion); @@ -329,7 +330,7 @@ static void AnalyzeMpeg(u8 *buffer, MpegContext *ctx) { return; } - if (ctx->mediaengine && (ctx->mpegStreamSize > 0) && !ctx->isAnalyzed) { + if (!ctx->isAnalyzed && ctx->mediaengine && ctx->mpegStreamSize > 0 && validSize >= ctx->mpegOffset) { // init mediaEngine auto ringbuffer = PSPPointer::Create(ctx->mpegRingbufferAddr); if (ringbuffer.IsValid()) { @@ -338,16 +339,16 @@ static void AnalyzeMpeg(u8 *buffer, MpegContext *ctx) { // TODO: Does this make any sense? ctx->mediaengine->loadStream(buffer, ctx->mpegOffset, 0); } + + // When used with scePsmf, some applications attempt to use sceMpegQueryStreamOffset + // and sceMpegQueryStreamSize, which forces a packet overwrite in the Media Engine and in + // the MPEG ringbuffer. + // Mark the current MPEG as analyzed to filter this, and restore it at sceMpegFinish. + ctx->isAnalyzed = true; } - - // When used with scePsmf, some applications attempt to use sceMpegQueryStreamOffset - // and sceMpegQueryStreamSize, which forces a packet overwrite in the Media Engine and in - // the MPEG ringbuffer. - // Mark the current MPEG as analyzed to filter this, and restore it at sceMpegFinish. - ctx->isAnalyzed = true; // copy header struct to mpeg header. - memcpy(ctx->mpegheader, buffer, 2048); + memcpy(ctx->mpegheader, buffer, validSize >= 2048 ? 2048 : validSize); *(u32_le*)(ctx->mpegheader + PSMF_STREAM_OFFSET_OFFSET) = 0x80000; INFO_LOG(ME, "Stream offset: %d, Stream size: 0x%X", ctx->mpegOffset, ctx->mpegStreamSize); @@ -601,7 +602,7 @@ static int sceMpegQueryStreamOffset(u32 mpeg, u32 bufferAddr, u32 offsetAddr) DEBUG_LOG(ME, "sceMpegQueryStreamOffset(%08x, %08x, %08x)", mpeg, bufferAddr, offsetAddr); // Kinda destructive, no? - AnalyzeMpeg(Memory::GetPointer(bufferAddr), ctx); + AnalyzeMpeg(Memory::GetPointer(bufferAddr), Memory::ValidSize(bufferAddr, 32768), ctx); if (ctx->mpegMagic != PSMF_MAGIC) { ERROR_LOG(ME, "sceMpegQueryStreamOffset: Bad PSMF magic"); @@ -633,7 +634,7 @@ static u32 sceMpegQueryStreamSize(u32 bufferAddr, u32 sizeAddr) MpegContext ctx; ctx.mediaengine = 0; - AnalyzeMpeg(Memory::GetPointer(bufferAddr), &ctx); + AnalyzeMpeg(Memory::GetPointer(bufferAddr), Memory::ValidSize(bufferAddr, 32768), &ctx); if (ctx.mpegMagic != PSMF_MAGIC) { ERROR_LOG(ME, "sceMpegQueryStreamSize: Bad PSMF magic"); @@ -1465,7 +1466,7 @@ void PostPutAction::run(MipsCall &call) { if (ringbuffer->packetsRead == 0 && ctx->mediaengine && packetsAdded > 0) { // init mediaEngine - AnalyzeMpeg(ctx->mpegheader, ctx); + AnalyzeMpeg(ctx->mpegheader, 2048, ctx); ctx->mediaengine->loadStream(ctx->mpegheader, 2048, ringbuffer->packets * ringbuffer->packetSize); } if (packetsAdded > 0) { From 97773d3dd55fdd5541a51553e57a6815dad1380b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Sep 2018 18:53:05 -0700 Subject: [PATCH 35/37] TexCache: Fix texture alignment in GLES. We must align to 4 bytes, and we aren't always aligned to 16 anymore, so we must check when dealing with swizzle. --- GPU/Common/TextureDecoder.cpp | 87 +++++++++++++++++---------------- GPU/Common/TextureDecoder.h | 2 +- GPU/D3D11/TextureCacheD3D11.cpp | 2 +- GPU/GLES/TextureCacheGLES.cpp | 24 ++++----- 4 files changed, 56 insertions(+), 59 deletions(-) diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 5bd2e34083..ea2df36d89 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -203,51 +203,54 @@ void DoSwizzleTex16(const u32 *ysrcp, u8 *texptr, int bxc, int byc, u32 pitch) { // ysrcp is in 32-bits, so this is convenient. const u32 pitchBy32 = pitch >> 2; #ifdef _M_SSE - __m128i *dest = (__m128i *)texptr; - // The pitch parameter is in bytes, so shift down for 128-bit. - // Note: it's always aligned to 16 bytes, so this is safe. - const u32 pitchBy128 = pitch >> 4; - for (int by = 0; by < byc; by++) { - const __m128i *xsrc = (const __m128i *)ysrcp; - for (int bx = 0; bx < bxc; bx++) { - const __m128i *src = xsrc; - for (int n = 0; n < 2; n++) { - // Textures are always 16-byte aligned so this is fine. - __m128i temp1 = _mm_load_si128(src); - src += pitchBy128; - __m128i temp2 = _mm_load_si128(src); - src += pitchBy128; - __m128i temp3 = _mm_load_si128(src); - src += pitchBy128; - __m128i temp4 = _mm_load_si128(src); - src += pitchBy128; + if (((uintptr_t)ysrcp & 0xF) == 0 && (pitch & 0xF) == 0) { + __m128i *dest = (__m128i *)texptr; + // The pitch parameter is in bytes, so shift down for 128-bit. + // Note: it's always aligned to 16 bytes, so this is safe. + const u32 pitchBy128 = pitch >> 4; + for (int by = 0; by < byc; by++) { + const __m128i *xsrc = (const __m128i *)ysrcp; + for (int bx = 0; bx < bxc; bx++) { + const __m128i *src = xsrc; + for (int n = 0; n < 2; n++) { + // Textures are always 16-byte aligned so this is fine. + __m128i temp1 = _mm_load_si128(src); + src += pitchBy128; + __m128i temp2 = _mm_load_si128(src); + src += pitchBy128; + __m128i temp3 = _mm_load_si128(src); + src += pitchBy128; + __m128i temp4 = _mm_load_si128(src); + src += pitchBy128; - _mm_store_si128(dest, temp1); - _mm_store_si128(dest + 1, temp2); - _mm_store_si128(dest + 2, temp3); - _mm_store_si128(dest + 3, temp4); - dest += 4; + _mm_store_si128(dest, temp1); + _mm_store_si128(dest + 1, temp2); + _mm_store_si128(dest + 2, temp3); + _mm_store_si128(dest + 3, temp4); + dest += 4; + } + xsrc++; } - xsrc++; + ysrcp += pitchBy32 * 8; } - ysrcp += pitchBy32 * 8; - } -#else - u32 *dest = (u32 *)texptr; - for (int by = 0; by < byc; by++) { - const u32 *xsrc = ysrcp; - for (int bx = 0; bx < bxc; bx++) { - const u32 *src = xsrc; - for (int n = 0; n < 8; n++) { - memcpy(dest, src, 16); - src += pitchBy32; - dest += 4; - } - xsrc += 4; - } - ysrcp += pitchBy32 * 8; - } + } else #endif + { + u32 *dest = (u32 *)texptr; + for (int by = 0; by < byc; by++) { + const u32 *xsrc = ysrcp; + for (int bx = 0; bx < bxc; bx++) { + const u32 *src = xsrc; + for (int n = 0; n < 8; n++) { + memcpy(dest, src, 16); + src += pitchBy32; + dest += 4; + } + xsrc += 4; + } + ysrcp += pitchBy32 * 8; + } + } } void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 pitch) { @@ -255,7 +258,7 @@ void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 const u32 pitchBy32 = pitch >> 2; #ifdef _M_SSE - if (((uintptr_t)ydestp & 0xF) == 0) { + if (((uintptr_t)ydestp & 0xF) == 0 && (pitch & 0xF) == 0) { const __m128i *src = (const __m128i *)texptr; // The pitch parameter is in bytes, so shift down for 128-bit. // Note: it's always aligned to 16 bytes, so this is safe. diff --git a/GPU/Common/TextureDecoder.h b/GPU/Common/TextureDecoder.h index 428d148a0b..30054168bd 100644 --- a/GPU/Common/TextureDecoder.h +++ b/GPU/Common/TextureDecoder.h @@ -41,7 +41,7 @@ u32 QuickTexHashSSE2(const void *checkp, u32 size); #define DoQuickTexHash QuickTexHashSSE2 #define StableQuickTexHash QuickTexHashSSE2 -// Pitch must be aligned to 16 bits (as is the case on a PSP) +// Pitch must be aligned to 16 bytes (as is the case on a PSP) void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 pitch); #define DoUnswizzleTex16 DoUnswizzleTex16Basic diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 3759e9c758..a85e5750f0 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -699,7 +699,7 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture & mapData = (u32 *)AllocateAlignedMemory(sizeof(u32) * (w * scaleFactor) * (h * scaleFactor), 16); mapRowPitch = w * scaleFactor * 4; } else { - mapRowPitch = std::max(bufw, w) * bpp; + mapRowPitch = std::max(w * bpp, 16); size_t bufSize = sizeof(u32) * (mapRowPitch / bpp) * h; mapData = (u32 *)AllocateAlignedMemory(bufSize, 16); if (!mapData) { diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 5f1c3e4a0f..04b629e1b1 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -744,11 +744,8 @@ TexCacheEntry::TexStatus TextureCacheGLES::CheckAlpha(const uint8_t *pixelData, void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int scaleFactor, GLenum dstFmt) { int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); - bool useUnpack = false; uint8_t *pixelData; - - // TODO: only do this once - u32 texByteAlign = 1; + int decPitch = 0; gpuStats.numTexturesDecoded++; @@ -756,13 +753,12 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r PROFILE_THIS_SCOPE("replacetex"); int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2; - uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(w * h * bpp, 16); - replaced.Load(level, rearrange, bpp * w); + decPitch = w * bpp; + uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(decPitch * h, 16); + replaced.Load(level, rearrange, decPitch); pixelData = rearrange; dstFmt = ToGLESFormat(replaced.Format(level)); - - texByteAlign = bpp; } else { PROFILE_THIS_SCOPE("decodetex"); @@ -771,14 +767,15 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r int bufw = GetTextureBufw(level, texaddr, GETextureFormat(entry.format)); int pixelSize = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - int decPitch = w * pixelSize; + // We leave GL_UNPACK_ALIGNMENT at 4, so this must be at least 4. + decPitch = std::max(w * pixelSize, 4); pixelData = (uint8_t *)AllocateAlignedMemory(decPitch * h * pixelSize, 16); DecodeTextureLevel(pixelData, decPitch, GETextureFormat(entry.format), clutformat, texaddr, level, bufw, true, false, false); // We check before scaling since scaling shouldn't invent alpha from a full alpha texture. if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { - TexCacheEntry::TexStatus alphaStatus = CheckAlpha(pixelData, dstFmt, useUnpack ? bufw : w, w, h); + TexCacheEntry::TexStatus alphaStatus = CheckAlpha(pixelData, dstFmt, decPitch / pixelSize, w, h); entry.SetAlphaStatus(alphaStatus, level); } else { entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); @@ -789,11 +786,9 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r scaler.ScaleAlways((u32 *)rearrange, (u32 *)pixelData, dstFmt, w, h, scaleFactor); FreeAlignedMemory(pixelData); pixelData = rearrange; + decPitch = w * 4; } - // Textures are always aligned to 16 bytes bufw, so this could safely be 4 always. - texByteAlign = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - if (replacer_.Enabled()) { ReplacedTextureDecodeInfo replacedInfo; replacedInfo.cachekey = entry.CacheKey(); @@ -804,8 +799,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r replacedInfo.scaleFactor = scaleFactor; replacedInfo.fmt = FromGLESFormat(dstFmt); - int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - replacer_.NotifyTextureDecoded(replacedInfo, pixelData, (useUnpack ? bufw : w) * bpp, level, w, h); + replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h); } } From 4dafb163a335f224e47831695418aba57578ab42 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Sep 2018 19:24:10 -0700 Subject: [PATCH 36/37] D3D11: Split color test out by component. Not working correctly together. See #10629. --- GPU/Directx9/PixelShaderGeneratorDX9.cpp | 11 ++++++++--- GPU/Directx9/ShaderManagerDX9.cpp | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index 7539dfa4e9..bd8e9d4e3c 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -294,6 +294,7 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag if (lang == HLSL_D3D11) { WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); } else { + // TODO: Use a texture to lookup bitwise ops? WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) clip(-1);\n", alphaTestFuncs[alphaTestFunc]); } } else { @@ -319,13 +320,17 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag } else { const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; // never/always don't make sense if (colorTestFuncs[colorTestFunc][0] != '#') { - const char * test = colorTestFuncs[colorTestFunc]; + const char *test = colorTestFuncs[colorTestFunc]; if (lang == HLSL_D3D11) { WRITE(p, " uint3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); - WRITE(p, " if ((v_scaled & u_alphacolormask.rgb) %s (u_alphacolorref.rgb & u_alphacolormask.rgb)) discard;\n", colorTestFuncs[colorTestFunc]); + WRITE(p, " uint3 v_masked = v_scaled & u_alphacolormask.rgb;\n"); + WRITE(p, " uint3 colorTestRef = u_alphacolorref.rgb & u_alphacolormask.rgb;\n"); + // We have to test the components separately, or we get incorrect results. See #10629. + WRITE(p, " if (v_masked.r %s colorTestRef.r && v_masked.g %s colorTestRef.g && v_masked.b %s colorTestRef.b) discard;\n", test, test, test); } else { + // TODO: Use a texture to lookup bitwise ops instead? WRITE(p, " float3 colortest = roundAndScaleTo255v(v.rgb);\n"); - WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b )) clip(-1);\n", test, test, test); + WRITE(p, " if ((colortest.r %s u_alphacolorref.r) && (colortest.g %s u_alphacolorref.g) && (colortest.b %s u_alphacolorref.b)) clip(-1);\n", test, test, test); } } else { diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 2e01495aa2..ba88643b91 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -58,7 +58,7 @@ PSShader::PSShader(LPDIRECT3DDEVICE9 device, FShaderID id, const char *code) : i ERROR_LOG(G3D, "Error in shader compilation!"); } ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); - ERROR_LOG(G3D, "Shader source:\n%s", code); + ERROR_LOG(G3D, "Shader source:\n%s", LineNumberString(code).c_str()); OutputDebugStringUTF8("Messages:\n"); OutputDebugStringUTF8(errorMessage.c_str()); Reporting::ReportMessage("D3D error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); From b05f75f667e34057c2965ccf2dddc77f827e9dff Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Sep 2018 20:05:19 -0700 Subject: [PATCH 37/37] D3D9: Set alpha mask reference in uniforms. Just so we don't have surprises later if we implement masking. --- GPU/Directx9/ShaderManagerDX9.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index ba88643b91..e91aa3e60a 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -265,10 +265,10 @@ void ShaderManagerDX9::PSUpdateUniforms(u64 dirtyUniforms) { PSSetColorUniform3(CONST_PS_TEXENV, gstate.texenvcolor); } if (dirtyUniforms & DIRTY_ALPHACOLORREF) { - PSSetColorUniform3Alpha255(CONST_PS_ALPHACOLORREF, gstate.getColorTestRef(), gstate.getAlphaTestRef()); + PSSetColorUniform3Alpha255(CONST_PS_ALPHACOLORREF, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask()); } if (dirtyUniforms & DIRTY_ALPHACOLORMASK) { - PSSetColorUniform3(CONST_PS_ALPHACOLORMASK, gstate.colortestmask); + PSSetColorUniform3Alpha255(CONST_PS_ALPHACOLORMASK, gstate.colortestmask, gstate.getAlphaTestMask()); } if (dirtyUniforms & DIRTY_FOGCOLOR) { PSSetColorUniform3(CONST_PS_FOGCOLOR, gstate.fogcolor);