diff --git a/CMakeLists.txt b/CMakeLists.txt index 032d1770ac..54cb808fa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1459,7 +1459,6 @@ set(GPU_GLES GPU/GLES/ShaderManagerGLES.h GPU/GLES/StateMappingGLES.cpp GPU/GLES/StateMappingGLES.h - GPU/GLES/StencilBufferGLES.cpp GPU/GLES/TextureCacheGLES.cpp GPU/GLES/TextureCacheGLES.h GPU/GLES/DrawEngineGLES.cpp @@ -1483,7 +1482,6 @@ set(GPU_VULKAN GPU/Vulkan/ShaderManagerVulkan.h GPU/Vulkan/StateMappingVulkan.cpp GPU/Vulkan/StateMappingVulkan.h - GPU/Vulkan/StencilBufferVulkan.cpp GPU/Vulkan/TextureCacheVulkan.cpp GPU/Vulkan/TextureCacheVulkan.h GPU/Vulkan/VulkanUtil.cpp @@ -1503,7 +1501,6 @@ set(GPU_D3D9 GPU/Directx9/ShaderManagerDX9.h GPU/Directx9/StateMappingDX9.cpp GPU/Directx9/StateMappingDX9.h - GPU/Directx9/StencilBufferDX9.cpp GPU/Directx9/TextureCacheDX9.cpp GPU/Directx9/TextureCacheDX9.h ) @@ -1523,7 +1520,6 @@ set(GPU_D3D11 GPU/D3D11/ShaderManagerD3D11.h GPU/D3D11/StateMappingD3D11.cpp GPU/D3D11/StateMappingD3D11.h - GPU/D3D11/StencilBufferD3D11.cpp GPU/D3D11/TextureCacheD3D11.cpp GPU/D3D11/TextureCacheD3D11.h ) @@ -1537,6 +1533,8 @@ endif() set(GPU_SOURCES ${GPU_IMPLS} ${GPU_NEON} + GPU/Common/Draw2D.cpp + GPU/Common/Draw2D.h GPU/Common/DepalettizeShaderCommon.cpp GPU/Common/DepalettizeShaderCommon.h GPU/Common/FragmentShaderGenerator.cpp @@ -1715,6 +1713,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Dialog/PSPMsgDialog.h Core/Dialog/PSPNetconfDialog.cpp Core/Dialog/PSPNetconfDialog.h + Core/Dialog/PSPNpSigninDialog.cpp + Core/Dialog/PSPNpSigninDialog.h Core/Dialog/PSPOskDialog.cpp Core/Dialog/PSPOskDialog.h Core/Dialog/PSPPlaceholderDialog.cpp @@ -1898,6 +1898,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/scePspNpDrm_user.h Core/HLE/sceNp.cpp Core/HLE/sceNp.h + Core/HLE/sceNp2.cpp + Core/HLE/sceNp2.h Core/HLE/scePauth.cpp Core/HLE/scePauth.h Core/HW/SimpleAudioDec.cpp diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 9d13d0789d..eeba9d24a3 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -38,6 +38,7 @@ class D3D11Pipeline; class D3D11BlendState; class D3D11DepthStencilState; class D3D11SamplerState; +class D3D11Buffer; class D3D11RasterState; class D3D11Framebuffer; @@ -248,10 +249,12 @@ private: uint8_t stencilRef_ = 0; uint8_t stencilWriteMask_ = 0xFF; uint8_t stencilCompareMask_ = 0xFF; + bool stencilDirty_ = true; // Temporaries ID3D11Texture2D *packTexture_ = nullptr; + Buffer *upBuffer_ = nullptr; // System info D3D_FEATURE_LEVEL featureLevel_; @@ -282,7 +285,9 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de caps_.framebufferBlitSupported = false; caps_.framebufferCopySupported = true; caps_.framebufferDepthBlitSupported = false; + caps_.framebufferStencilBlitSupported = false; caps_.framebufferDepthCopySupported = true; + caps_.framebufferSeparateDepthCopySupported = false; // Though could be emulated with a draw. caps_.texture3DSupported = true; D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; @@ -337,9 +342,14 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de _assert_(SUCCEEDED(hr)); shaderLanguageDesc_.Init(HLSL_D3D11); + + const size_t UP_MAX_BYTES = 65536 * 24; + + upBuffer_ = CreateBuffer(UP_MAX_BYTES, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA); } D3D11DrawContext::~D3D11DrawContext() { + upBuffer_->Release(); packTexture_->Release(); // Release references. @@ -755,9 +765,6 @@ public: shaderModule->Release(); } } - bool RequiresBuffer() override { - return true; - } AutoRef input; ID3D11InputLayout *il = nullptr; @@ -1247,7 +1254,13 @@ void D3D11DrawContext::DrawIndexed(int indexCount, int offset) { void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) { ApplyCurrentState(); - // TODO: Upload the data then draw.. + + int byteSize = vertexCount * curPipeline_->input->strides[0]; + + UpdateBuffer(upBuffer_, (const uint8_t *)vdata, 0, byteSize, Draw::UPDATE_DISCARD); + BindVertexBuffers(0, 1, &upBuffer_, nullptr); + int offset = 0; + Draw(vertexCount, offset); } uint32_t D3D11DrawContext::GetDataFormatSupport(DataFormat fmt) const { diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 9afba75110..2b0f1a0faa 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -23,6 +23,7 @@ #include "Common/Math/lin/matrix4x4.h" #include "Common/GPU/thin3d.h" #include "Common/GPU/D3D9/D3D9StateCache.h" +#include "Common/StringUtils.h" #include "Common/Log.h" @@ -272,9 +273,6 @@ public: D3D9Pipeline() {} ~D3D9Pipeline() { } - bool RequiresBuffer() override { - return false; - } D3D9ShaderModule *vshader; D3D9ShaderModule *pshader; @@ -666,7 +664,9 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = false; caps_.framebufferDepthBlitSupported = true; + caps_.framebufferStencilBlitSupported = false; caps_.framebufferDepthCopySupported = false; + caps_.framebufferSeparateDepthCopySupported = false; caps_.texture3DSupported = true; if (d3d) { @@ -1064,6 +1064,10 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si // No D3D9-compatible shader compiler installed. error = "D3D9 shader compiler not installed"; } + + ERROR_LOG(G3D, "Compile error: %s", error); + ERROR_LOG(G3D, "%s", LineNumberString(std::string((const char *)data)).c_str()); + OutputDebugStringA(source); OutputDebugStringA(error); if (errorBuffer) @@ -1241,14 +1245,26 @@ void D3D9Context::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { bool D3D9Context::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) { D3D9Framebuffer *src = (D3D9Framebuffer *)srcfb; D3D9Framebuffer *dst = (D3D9Framebuffer *)dstfb; - if (channelBits != FB_COLOR_BIT) - return false; + + LPDIRECT3DSURFACE9 srcSurf; + LPDIRECT3DSURFACE9 dstSurf; RECT srcRect{ (LONG)srcX1, (LONG)srcY1, (LONG)srcX2, (LONG)srcY2 }; RECT dstRect{ (LONG)dstX1, (LONG)dstY1, (LONG)dstX2, (LONG)dstY2 }; - LPDIRECT3DSURFACE9 srcSurf = src ? src->surf : deviceRTsurf; - LPDIRECT3DSURFACE9 dstSurf = dst ? dst->surf : deviceRTsurf; + if (channelBits == FB_COLOR_BIT) { + srcSurf = src ? src->surf : deviceRTsurf; + dstSurf = dst ? dst->surf : deviceRTsurf; + } else if (channelBits & FB_DEPTH_BIT) { + if (!src || !dst) { + // Might have implications for non-buffered rendering. + return false; + } + srcSurf = src->depthstencil; + dstSurf = dst->depthstencil; + } else { + return false; + } stepId_++; - return SUCCEEDED(device_->StretchRect(srcSurf, &srcRect, dstSurf, &dstRect, filter == FB_BLIT_LINEAR ? D3DTEXF_LINEAR : D3DTEXF_POINT)); + return SUCCEEDED(device_->StretchRect(srcSurf, &srcRect, dstSurf, &dstRect, (filter == FB_BLIT_LINEAR && channelBits == FB_COLOR_BIT) ? D3DTEXF_LINEAR : D3DTEXF_POINT)); } void D3D9Context::HandleEvent(Event ev, int width, int height, void *param1, void *param2) { diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index e61cb93aae..29c51cbeb5 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -261,9 +261,6 @@ public: ~OpenGLInputLayout(); void Compile(const InputLayoutDesc &desc); - bool RequiresBuffer() { - return false; - } GLRInputLayout *inputLayout_ = nullptr; int stride = 0; @@ -284,10 +281,6 @@ public: bool LinkShaders(); - bool RequiresBuffer() override { - return inputLayout && inputLayout->RequiresBuffer(); - } - GLuint prim = 0; std::vector shaders; AutoRef inputLayout; @@ -541,8 +534,11 @@ OpenGLContext::OpenGLContext() { caps_.preferredDepthBufferFormat = DataFormat::D24_S8; caps_.texture3DSupported = true; } - caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object; + + caps_.framebufferCopySupported = gl_extensions.OES_copy_image || gl_extensions.NV_copy_image || gl_extensions.EXT_copy_image || gl_extensions.ARB_copy_image; + caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object || gl_extensions.GLES3; caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported; + caps_.framebufferStencilBlitSupported = caps_.framebufferBlitSupported; caps_.depthClampSupported = gl_extensions.ARB_depth_clamp; if (gl_extensions.IsGLES) { caps_.clipDistanceSupported = gl_extensions.EXT_clip_cull_distance || gl_extensions.APPLE_clip_distance; @@ -1117,7 +1113,7 @@ void OpenGLContext::ApplySamplers() { const OpenGLSamplerState *samp = boundSamplers_[i]; const GLRTexture *tex = boundTextures_[i]; if (tex) { - _assert_(samp); + _assert_msg_(samp, "Sampler missing"); } else { continue; } diff --git a/Common/GPU/Shader.cpp b/Common/GPU/Shader.cpp index eaca068ff5..5033e55ac1 100644 --- a/Common/GPU/Shader.cpp +++ b/Common/GPU/Shader.cpp @@ -86,12 +86,14 @@ void ShaderLanguageDesc::Init(ShaderLanguage lang) { texelFetch = "texelFetch"; forceMatrix4x4 = false; coefsFromBuffers = true; + vertexIndex = true; break; case HLSL_D3D9: case HLSL_D3D11: if (lang == HLSL_D3D11) { fragColor0 = "outfragment.target"; fragColor1 = "outfragment.target1"; + vertexIndex = true; // if declared as a semantic input } else { fragColor0 = "outfragment.target"; } diff --git a/Common/GPU/Shader.h b/Common/GPU/Shader.h index 71cd26cae6..bc8c0f4e81 100644 --- a/Common/GPU/Shader.h +++ b/Common/GPU/Shader.h @@ -52,6 +52,8 @@ struct ShaderLanguageDesc { const char *framebufferFetchExtension = nullptr; const char *vsOutPrefix = ""; const char *viewportYSign = ""; + + bool vertexIndex = false; bool glslES30 = false; // really glslES30Features. TODO: Clean this up. bool bitwiseOps = false; bool forceMatrix4x4 = false; diff --git a/Common/GPU/ShaderWriter.cpp b/Common/GPU/ShaderWriter.cpp index d53f00169e..3fee6a6fd3 100644 --- a/Common/GPU/ShaderWriter.cpp +++ b/Common/GPU/ShaderWriter.cpp @@ -6,7 +6,7 @@ #include "Common/GPU/ShaderWriter.h" #include "Common/Log.h" -const char *vulkan_glsl_preamble_fs = +const char * const vulkan_glsl_preamble_fs = "#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" "#extension GL_ARB_shading_language_420pack : enable\n" @@ -18,7 +18,7 @@ const char *vulkan_glsl_preamble_fs = "precision highp int;\n" "\n"; -const char *hlsl_preamble_fs = +const char * const hlsl_preamble_fs = "#define vec2 float2\n" "#define vec3 float3\n" "#define vec4 float4\n" @@ -35,14 +35,14 @@ const char *hlsl_preamble_fs = "#define highp\n" "#define mod(x, y) fmod(x, y)\n"; -const char *hlsl_d3d11_preamble_fs = +static const char * const hlsl_d3d11_preamble_fs = "#define DISCARD discard\n" "#define DISCARD_BELOW(x) clip(x);\n"; -const char *hlsl_d3d9_preamble_fs = +static const char * const hlsl_d3d9_preamble_fs = "#define DISCARD clip(-1)\n" "#define DISCARD_BELOW(x) clip(x)\n"; -const char *vulkan_glsl_preamble_vs = +static const char * const vulkan_glsl_preamble_vs = "#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" "#extension GL_ARB_shading_language_420pack : enable\n" @@ -51,7 +51,7 @@ const char *vulkan_glsl_preamble_vs = "precision highp float;\n" "\n"; -const char *hlsl_preamble_vs = +static const char * const hlsl_preamble_vs = "#define vec2 float2\n" "#define vec3 float3\n" "#define vec4 float4\n" @@ -66,6 +66,16 @@ const char *hlsl_preamble_vs = "#define highp\n" "\n"; +static const char * const semanticNames[8] = { + "POSITION", + "COLOR0", + "TEXCOORD0", + "TEXCOORD1", + "NORMAL", + "TANGENT", + "BINORMAL", +}; + // Unsafe. But doesn't matter, we'll use big buffers for shader gen. ShaderWriter & ShaderWriter::F(const char *format, ...) { va_list args; @@ -153,7 +163,7 @@ void ShaderWriter::BeginVSMain(Slice inputs, Slice uniform { C("struct VS_OUTPUT {\n"); for (auto &varying : varyings) { - F(" %s %s : %s;\n", varying.type, varying.name, varying.semantic); + F(" %s %s : %s;\n", varying.type, varying.name, semanticNames[varying.semantic]); } F(" vec4 pos : %s;\n", lang_.shaderLanguage == HLSL_D3D11 ? "SV_Position" : "POSITION"); C("};\n"); @@ -162,24 +172,37 @@ void ShaderWriter::BeginVSMain(Slice inputs, Slice uniform if (lang_.shaderLanguage == HLSL_D3D11) { C("uint gl_VertexIndex : SV_VertexID, "); } + // List the inputs. + for (auto &input : inputs) { + F("in %s %s : %s, ", input.type, input.name, semanticNames[input.semantic]); + } + Rewind(2); // Get rid of the last comma. C(") {\n"); C(" vec4 gl_Position;\n"); for (auto &varying : varyings) { - F(" %s %s;\n", varying.type, varying.name); + F(" %s %s; // %s\n", varying.type, varying.name, semanticNames[varying.semantic]); } break; } case GLSL_VULKAN: + { + for (auto &input : inputs) { + F("layout(location = %d) in %s %s;\n", input.semantic /*index*/, input.type, input.name); + } for (auto &varying : varyings) { F("layout(location = %d) %s out %s %s; // %s\n", - varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic); + varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, semanticNames[varying.semantic]); } C("void main() {\n"); break; + } default: // OpenGL + for (auto &input : inputs) { + F("in %s %s;\n", input.type, input.name); + } for (auto &varying : varyings) { - F("%s %s %s %s; // %s (%d)\n", lang_.varying_vs, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic, varying.index); + F("%s %s %s %s; // %s (%d)\n", lang_.varying_vs, varying.precision ? varying.precision : "", varying.type, varying.name, semanticNames[varying.semantic], varying.index); } C("void main() {\n"); break; @@ -191,14 +214,19 @@ void ShaderWriter::BeginFSMain(Slice uniforms, Slice var switch (lang_.shaderLanguage) { case HLSL_D3D11: if (!uniforms.is_empty()) { + C("cbuffer base : register(b0) {\n"); + for (auto &uniform : uniforms) { - //F(" %s %s : %s;\n", uniform.type, uniform.name, uniform.index); + F(" %s %s;\n", uniform.type, uniform.name); } + + C("};\n"); } + // Let's do the varyings as parameters to main, no struct. C("vec4 main("); for (auto &varying : varyings) { - F(" %s %s : %s, ", varying.type, varying.name, varying.semantic); + F(" %s %s : %s, ", varying.type, varying.name, semanticNames[varying.semantic]); } // Erase the last comma Rewind(2); @@ -212,7 +240,7 @@ void ShaderWriter::BeginFSMain(Slice uniforms, Slice var // Let's do the varyings as parameters to main, no struct. C("vec4 main("); for (auto &varying : varyings) { - F(" %s %s : %s, ", varying.type, varying.name, varying.semantic); + F(" %s %s : %s, ", varying.type, varying.name, semanticNames[varying.semantic]); } // Erase the last comma Rewind(2); @@ -221,14 +249,25 @@ void ShaderWriter::BeginFSMain(Slice uniforms, Slice var break; case GLSL_VULKAN: for (auto &varying : varyings) { - F("layout(location = %d) %s in %s %s; // %s\n", varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic); + F("layout(location = %d) %s in %s %s; // %s\n", varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, semanticNames[varying.semantic]); } C("layout(location = 0, index = 0) out vec4 fragColor0;\n"); + if (!uniforms.is_empty()) { + C("layout(std140, set = 0, binding = 0) uniform bufferVals {\n"); + for (auto &uniform : uniforms) { + F("%s %s;\n", uniform.type, uniform.name); + } + C("};\n"); + } C("\nvoid main() {\n"); break; - default: + + default: // GLSL OpenGL for (auto &varying : varyings) { - F("%s %s %s %s; // %s\n", lang_.varying_fs, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic); + F("%s %s %s %s; // %s\n", lang_.varying_fs, varying.precision ? varying.precision : "", varying.type, varying.name, semanticNames[varying.semantic]); + } + for (auto &uniform : uniforms) { + F("uniform %s %s;\n", uniform.type, uniform.name); } if (!strcmp(lang_.fragColor0, "fragColor0")) { C("out vec4 fragColor0;\n"); @@ -284,6 +323,7 @@ void ShaderWriter::DeclareTexture2D(const char *name, int binding) { F("Texture2D %s : register(t%d);\n", name, binding); break; case HLSL_D3D9: + F("sampler %s: register(s%d);\n", name, binding); break; case GLSL_VULKAN: // In the thin3d descriptor set layout, textures start at 1 in set 0. Hence the +1. diff --git a/Common/GPU/ShaderWriter.h b/Common/GPU/ShaderWriter.h index cb8d963be3..b4a4beb3cc 100644 --- a/Common/GPU/ShaderWriter.h +++ b/Common/GPU/ShaderWriter.h @@ -19,6 +19,7 @@ struct InputDef { const char *type; const char *name; + int semantic; }; struct UniformDef { @@ -30,7 +31,7 @@ struct UniformDef { struct VaryingDef { const char *type; const char *name; - const char *semantic; + int semantic; int index; const char *precision; }; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 06bca64a33..1a84cb5ecb 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -271,9 +271,6 @@ public: int GetUBOSize() const { return uboSize_; } - bool RequiresBuffer() override { - return false; - } VkPipeline backbufferPipeline = VK_NULL_HANDLE; VkPipeline framebufferPipeline = VK_NULL_HANDLE; @@ -578,7 +575,7 @@ static int GetBpp(VkFormat format) { } } -VkFormat DataFormatToVulkan(DataFormat format) { +static VkFormat DataFormatToVulkan(DataFormat format) { switch (format) { case DataFormat::D16: return VK_FORMAT_D16_UNORM; case DataFormat::D32F: return VK_FORMAT_D32_SFLOAT; @@ -774,6 +771,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) : vulkan_(vulkan), renderManager_(vulkan) { shaderLanguageDesc_.Init(GLSL_VULKAN); + VkFormat depthStencilFormat = vulkan->GetDeviceInfo().preferredDepthStencilFormat; + caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.samplerAnisotropy != 0; caps_.geometryShaderSupported = vulkan->GetDeviceFeatures().enabled.geometryShader != 0; caps_.tesselationShaderSupported = vulkan->GetDeviceFeatures().enabled.tessellationShader != 0; @@ -784,8 +783,10 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) caps_.cullDistanceSupported = vulkan->GetDeviceFeatures().enabled.shaderCullDistance != 0; caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = true; - caps_.framebufferDepthBlitSupported = false; // Can be checked for. + caps_.framebufferDepthBlitSupported = vulkan->GetDeviceInfo().canBlitToPreferredDepthStencilFormat; + caps_.framebufferStencilBlitSupported = caps_.framebufferDepthBlitSupported; caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case. + caps_.framebufferSeparateDepthCopySupported = true; // Will pretty much always be the case. caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan. caps_.texture3DSupported = true; @@ -1440,18 +1441,24 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const { VkFormatProperties properties; vkGetPhysicalDeviceFormatProperties(vulkan_->GetCurrentPhysicalDevice(), vulkan_format, &properties); uint32_t flags = 0; - if (properties.optimalTilingFeatures & VkFormatFeatureFlagBits::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) { + if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) { flags |= FMT_RENDERTARGET; } - if (properties.optimalTilingFeatures & VkFormatFeatureFlagBits::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { + if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { flags |= FMT_DEPTHSTENCIL; } - if (properties.optimalTilingFeatures & VkFormatFeatureFlagBits::VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { + if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { flags |= FMT_TEXTURE; } - if (properties.bufferFeatures & VkFormatFeatureFlagBits::VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) { + if (properties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) { flags |= FMT_INPUTLAYOUT; } + if ((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { + flags |= FMT_BLIT; + } + if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) { + flags |= FMT_STORAGE_IMAGE; + } return flags; } diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 55093b83d0..845914d344 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -25,7 +25,7 @@ namespace Draw { // Useful in UBOs typedef int bool32; -enum class Comparison : int { +enum class Comparison : uint8_t { NEVER, LESS, EQUAL, @@ -37,7 +37,7 @@ enum class Comparison : int { }; // Had to prefix with LOGIC, too many clashes -enum class LogicOp : int { +enum class LogicOp : uint8_t { LOGIC_CLEAR, LOGIC_SET, LOGIC_COPY, @@ -56,7 +56,7 @@ enum class LogicOp : int { LOGIC_OR_INVERTED, }; -enum class BlendOp : int { +enum class BlendOp : uint8_t { ADD, SUBTRACT, REV_SUBTRACT, @@ -96,7 +96,7 @@ enum class StencilOp { DECREMENT_AND_WRAP = 7, }; -enum class TextureFilter : int { +enum class TextureFilter : uint8_t { NEAREST = 0, LINEAR = 1, }; @@ -202,6 +202,8 @@ enum FormatSupport { FMT_INPUTLAYOUT = 4, FMT_DEPTHSTENCIL = 8, FMT_AUTOGEN_MIPS = 16, + FMT_BLIT = 32, + FMT_STORAGE_IMAGE = 64, }; enum InfoField { @@ -448,7 +450,6 @@ public: class Pipeline : public RefCountedObject { public: virtual ~Pipeline() {} - virtual bool RequiresBuffer() = 0; }; class RasterState : public RefCountedObject {}; @@ -530,7 +531,9 @@ struct DeviceCaps { bool framebufferCopySupported; bool framebufferBlitSupported; bool framebufferDepthCopySupported; + bool framebufferSeparateDepthCopySupported; bool framebufferDepthBlitSupported; + bool framebufferStencilBlitSupported; bool framebufferFetchSupported; bool texture3DSupported; diff --git a/Common/Render/DrawBuffer.cpp b/Common/Render/DrawBuffer.cpp index 35e3f0176b..9b09e7675b 100644 --- a/Common/Render/DrawBuffer.cpp +++ b/Common/Render/DrawBuffer.cpp @@ -32,12 +32,6 @@ void DrawBuffer::Init(Draw::DrawContext *t3d, Draw::Pipeline *pipeline) { draw_ = t3d; inited_ = true; - - if (pipeline->RequiresBuffer()) { - vbuf_ = draw_->CreateBuffer(MAX_VERTS * sizeof(Vertex), BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA); - } else { - vbuf_ = nullptr; - } } Draw::InputLayout *DrawBuffer::CreateInputLayout(Draw::DrawContext *t3d) { @@ -57,10 +51,6 @@ Draw::InputLayout *DrawBuffer::CreateInputLayout(Draw::DrawContext *t3d) { } void DrawBuffer::Shutdown() { - if (vbuf_) { - vbuf_->Release(); - vbuf_ = nullptr; - } inited_ = false; alphaStack_.clear(); drawMatrixStack_.clear(); @@ -90,14 +80,7 @@ void DrawBuffer::Flush(bool set_blend_state) { ub.tint = tint_; ub.saturation = saturation_; draw_->UpdateDynamicUniformBuffer(&ub, sizeof(ub)); - if (vbuf_) { - draw_->UpdateBuffer(vbuf_, (const uint8_t *)verts_, 0, sizeof(Vertex) * count_, Draw::UPDATE_DISCARD); - draw_->BindVertexBuffers(0, 1, &vbuf_, nullptr); - int offset = 0; - draw_->Draw(count_, offset); - } else { - draw_->DrawUP((const void *)verts_, count_); - } + draw_->DrawUP((const void *)verts_, count_); count_ = 0; } diff --git a/Common/Render/DrawBuffer.h b/Common/Render/DrawBuffer.h index e103805d2e..4a29893c15 100644 --- a/Common/Render/DrawBuffer.h +++ b/Common/Render/DrawBuffer.h @@ -199,7 +199,6 @@ private: std::vector alphaStack_; Draw::DrawContext *draw_ = nullptr; - Draw::Buffer *vbuf_ = nullptr; Draw::Pipeline *pipeline_ = nullptr; Vertex *verts_; diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index c8f74ee1f1..72d3443ab0 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -1046,7 +1046,7 @@ void TextEdit::Draw(UIContext &dc) { } void TextEdit::GetContentDimensions(const UIContext &dc, float &w, float &h) const { - dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, text_.size() ? text_.c_str() : "Wj", &w, &h, align_); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, !text_.empty() ? text_.c_str() : "Wj", &w, &h, align_); w += 2; h += 2; } diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 05bdb72eae..bdfaea4361 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -535,6 +535,7 @@ + @@ -572,6 +573,7 @@ + @@ -1086,6 +1088,7 @@ + @@ -1118,6 +1121,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index e7a1ff33e2..3a24903dbc 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -523,6 +523,9 @@ Dialog + + Dialog + Debugger @@ -1184,6 +1187,9 @@ HW + + HLE\Libraries + @@ -1597,6 +1603,9 @@ Dialog + + Dialog + MIPS\JitCommon @@ -1910,6 +1919,9 @@ HW + + HLE\Libraries + diff --git a/Core/Dialog/PSPNpSigninDialog.cpp b/Core/Dialog/PSPNpSigninDialog.cpp new file mode 100644 index 0000000000..87fcf602c5 --- /dev/null +++ b/Core/Dialog/PSPNpSigninDialog.cpp @@ -0,0 +1,349 @@ +// Copyright (c) 2012- 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/. + +#if defined(_WIN32) +#include "Common/CommonWindows.h" +#endif +#include "Common/TimeUtil.h" +#include "Common/Data/Text/I18n.h" +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Core/Config.h" +#include "Core/MemMapHelpers.h" +#include "Core/Util/PPGeDraw.h" +#include "Core/HLE/sceKernelMemory.h" +#include "Core/HLE/sceCtrl.h" +#include "Core/HLE/sceUtility.h" +#include "Core/HLE/sceNet.h" +#include "Core/HLE/sceNetAdhoc.h" +#include "Core/HLE/sceNp.h" +#include "Core/Dialog/PSPNpSigninDialog.h" +#include "Common/Data/Encoding/Utf8.h" +#include "Core/Reporting.h" + + +static const float FONT_SCALE = 0.65f; + +// Needs testing. +const static int NP_INIT_DELAY_US = 200000; +const static int NP_SHUTDOWN_DELAY_US = 501000; +const static int NP_RUNNING_DELAY_US = 1000000; // faked delay to simulate signin process to give chance for players to read the text on the dialog + +PSPNpSigninDialog::PSPNpSigninDialog(UtilityDialogType type) : PSPDialog(type) { +} + +PSPNpSigninDialog::~PSPNpSigninDialog() { +} + +int PSPNpSigninDialog::Init(u32 paramAddr) { + // Already running + if (ReadStatus() != SCE_UTILITY_STATUS_NONE) + return SCE_ERROR_UTILITY_INVALID_STATUS; + + requestAddr = paramAddr; + int size = Memory::Read_U32(paramAddr); + memset(&request, 0, sizeof(request)); + // Only copy the right size to support different request format + Memory::Memcpy(&request, paramAddr, size); + + WARN_LOG_REPORT_ONCE(PSPNpSigninDialogInit, SCENET, "NpSignin Init Params: %08x, %08x, %08x, %08x", request.npSigninStatus, request.unknown1, request.unknown2, request.unknown3); + + ChangeStatusInit(NP_INIT_DELAY_US); + + // Eat any keys pressed before the dialog inited. + UpdateButtons(); + okButtonImg = ImageID("I_CIRCLE"); + cancelButtonImg = ImageID("I_CROSS"); + okButtonFlag = CTRL_CIRCLE; + cancelButtonFlag = CTRL_CROSS; + if (request.common.buttonSwap == 1) + { + okButtonImg = ImageID("I_CROSS"); + cancelButtonImg = ImageID("I_CIRCLE"); + okButtonFlag = CTRL_CROSS; + cancelButtonFlag = CTRL_CIRCLE; + } + + //npSigninResult = -1; + startTime = (u64)(time_now_d() * 1000000.0); + step = 0; + + StartFade(true); + return 0; +} + +void PSPNpSigninDialog::DrawBanner() { + + PPGeDrawRect(0, 0, 480, 22, CalcFadedColor(0x65636358)); + + PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f); + textStyle.hasShadow = false; + + // TODO: Draw a hexagon icon + PPGeDrawImage(10, 5, 11.0f, 10.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle()); + auto di = GetI18NCategory("Dialog"); + PPGeDrawText(di->T("Sign In"), 31, 10, textStyle); +} + +void PSPNpSigninDialog::DrawIndicator() { + // TODO: Draw animated circle as processing indicator + PPGeDrawImage(456, 248, 20.0f, 20.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle()); +} + +void PSPNpSigninDialog::DrawLogo() { + // TODO: Draw OpenDNAS logo + PPGeDrawImage(416, 22, 64.0f, 64.0f, 1, 10, 1, 10, 64, 64, FadedImageStyle()); +} + +void PSPNpSigninDialog::DisplayMessage(std::string text1, std::string text2a, std::string text2b, std::string text3a, std::string text3b, bool hasYesNo, bool hasOK) { + auto di = GetI18NCategory("Dialog"); + + PPGeStyle buttonStyle = FadedStyle(PPGeAlign::BOX_CENTER, FONT_SCALE); + PPGeStyle messageStyle = FadedStyle(PPGeAlign::BOX_HCENTER, FONT_SCALE); + PPGeStyle messageStyleRight = FadedStyle(PPGeAlign::BOX_RIGHT, FONT_SCALE); + PPGeStyle messageStyleLeft = FadedStyle(PPGeAlign::BOX_LEFT, FONT_SCALE); + + std::string text2 = text2a + " " + text2b; + std::string text3 = text3a + " " + text3b; + + // Without the scrollbar, we have 350 total pixels. + float WRAP_WIDTH = 300.0f; + if (UTF8StringNonASCIICount(text1.c_str()) >= (int)text1.size() / 4) { + WRAP_WIDTH = 336.0f; + if (text1.size() > 12) { + messageStyle.scale = 0.6f; + } + } + + float totalHeight1 = 0.0f; + PPGeMeasureText(nullptr, &totalHeight1, text1.c_str(), FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH); + float totalHeight2 = 0.0f; + if (text2 != " ") + PPGeMeasureText(nullptr, &totalHeight2, text2.c_str(), FONT_SCALE, PPGE_LINE_USE_ELLIPSIS, WRAP_WIDTH); + float totalHeight3 = 0.0f; + if (text3 != " ") + PPGeMeasureText(nullptr, &totalHeight3, text3.c_str(), FONT_SCALE, PPGE_LINE_USE_ELLIPSIS, WRAP_WIDTH); + float marginTop = 0.0f; + if (text2 != " " || text3 != " ") + marginTop = 11.0f; + float totalHeight = totalHeight1 + totalHeight2 + totalHeight3 + marginTop; + // The PSP normally only shows about 8 lines at a time. + // For improved UX, we intentionally show part of the next line. + float visibleHeight = std::min(totalHeight, 175.0f); + float h2 = visibleHeight / 2.0f; + + float centerY = 135.0f; + float sy = centerY - h2 - 15.0f; + float ey = centerY + h2 + 20.0f; + float buttonY = centerY + h2 + 5.0f; + + auto drawSelectionBoxAndAdjust = [&](float x) { + // Box has a fixed size. + float w = 15.0f; + float h = 8.0f; + PPGeDrawRect(x - w, buttonY - h, x + w, buttonY + h, CalcFadedColor(0x6DCFCFCF)); + + centerY -= h + 5.0f; + sy -= h + 5.0f; + ey = buttonY + h * 2.0f + 5.0f; + }; + + if (hasYesNo) { + if (yesnoChoice == 1) { + drawSelectionBoxAndAdjust(204.0f); + } + else { + drawSelectionBoxAndAdjust(273.0f); + } + + PPGeDrawText(di->T("Yes"), 203.0f, buttonY - 1.0f, buttonStyle); + PPGeDrawText(di->T("No"), 272.0f, buttonY - 1.0f, buttonStyle); + if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) { + yesnoChoice = 1; + } + else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) { + yesnoChoice = 0; + } + buttonY += 8.0f + 5.0f; + } + + if (hasOK) { + drawSelectionBoxAndAdjust(240.0f); + + PPGeDrawText(di->T("OK"), 239.0f, buttonY - 1.0f, buttonStyle); + buttonY += 8.0f + 5.0f; + } + + PPGeScissor(0, (int)(centerY - h2 - 2), 480, (int)(centerY + h2 + 2)); + PPGeDrawTextWrapped(text1.c_str(), 240.0f, centerY - h2 - scrollPos_, WRAP_WIDTH, 0, messageStyle); + if (text2a != "") { + if (text2b != "") + PPGeDrawTextWrapped(text2a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleRight); + else + PPGeDrawTextWrapped(text2a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyle); + } + if (text2b != "") + PPGeDrawTextWrapped(text2b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleLeft); + if (text3a != "") { + if (text3b != "") + PPGeDrawTextWrapped(text3a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleRight); + else + PPGeDrawTextWrapped(text3a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyle); + } + if (text3b != "") + PPGeDrawTextWrapped(text3b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleLeft); + PPGeScissorReset(); + + // Do we need a scrollbar? + if (visibleHeight < totalHeight) { + float scrollSpeed = 5.0f; + float scrollMax = totalHeight - visibleHeight; + + float bobHeight = (visibleHeight / totalHeight) * visibleHeight; + float bobOffset = (scrollPos_ / scrollMax) * (visibleHeight - bobHeight); + float bobY1 = centerY - h2 + bobOffset; + PPGeDrawRect(415.0f, bobY1, 420.0f, bobY1 + bobHeight, CalcFadedColor(0xFFCCCCCC)); + + auto buttonDown = [this](int btn, int& held) { + if (IsButtonPressed(btn)) { + held = 0; + return true; + } + return IsButtonHeld(btn, held, 1, 1); + }; + if (buttonDown(CTRL_DOWN, framesDownHeld_) && scrollPos_ < scrollMax) { + scrollPos_ = std::min(scrollMax, scrollPos_ + scrollSpeed); + } + if (buttonDown(CTRL_UP, framesUpHeld_) && scrollPos_ > 0.0f) { + scrollPos_ = std::max(0.0f, scrollPos_ - scrollSpeed); + } + } + + PPGeDrawRect(60.0f, sy, 420.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawRect(60.0f, ey, 420.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF)); +} + +int PSPNpSigninDialog::Update(int animSpeed) { + if (ReadStatus() != SCE_UTILITY_STATUS_RUNNING) { + return SCE_ERROR_UTILITY_INVALID_STATUS; + } + + UpdateButtons(); + auto di = GetI18NCategory("Dialog"); + auto err = GetI18NCategory("Error"); + u64 now = (u64)(time_now_d() * 1000000.0); + + if (request.npSigninStatus == NP_SIGNIN_STATUS_NONE) { + UpdateFade(animSpeed); + StartDraw(); + + PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC)); + DrawBanner(); + DrawIndicator(); + + // TODO: Not sure what should happen here.. may be something like this https://pastebin.com/1eW48zBb ? but we can do test on Open DNAS Server later https://dnas.hashsploit.net/us-gw/ + // DNAS dialog + if (step >= 2 && now - startTime > NP_RUNNING_DELAY_US) { + DrawLogo(); + DisplayMessage(di->T("PleaseWait", "Please wait...")); + step++; + } + // Signin dialog + else { + // Skipping the Select Connection screen since we only have 1 fake profile + DisplayMessage(di->T("SigninPleaseWait", "Signing in...\nPlease wait.")); + } + DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel")); + + if (step >= 2 && now - startTime > NP_RUNNING_DELAY_US*2) { + if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) { + StartFade(false); + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NP_SHUTDOWN_DELAY_US); + step++; + } + } + + else if (step == 1 && now - startTime > NP_RUNNING_DELAY_US) { + // Switch to the next message (with DNAS logo) + StartFade(true); + step++; + } + + else if (step == 0) { + /*if (npAuthResult < 0 && request.NpSigninData.IsValid()) { + npAuthResult = sceNpAuthCreateStartRequest(request.NpSigninData->paramAddr); + }*/ + step++; + } + + if (/*npAuthResult >= 0 &&*/ IsButtonPressed(cancelButtonFlag)) { + StartFade(false); + //sceNpAuthAbortRequest(npAuthResult); + //sceNpAuthDestroyRequest(npAuthResult); + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NP_SHUTDOWN_DELAY_US); + request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT; + request.npSigninStatus = NP_SIGNIN_STATUS_CANCELED; + //step = 0; + } + + EndDraw(); + } + + if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED) { + npSigninState = NP_SIGNIN_STATUS_SUCCESS; + __RtcTimeOfDay(&npSigninTimestamp); + request.npSigninStatus = npSigninState; + } + return 0; +} + +int PSPNpSigninDialog::Shutdown(bool force) { + if (ReadStatus() != SCE_UTILITY_STATUS_FINISHED && !force) + return SCE_ERROR_UTILITY_INVALID_STATUS; + + PSPDialog::Shutdown(force); + if (!force) { + ChangeStatusShutdown(NP_SHUTDOWN_DELAY_US); + } + + // FIXME: This should probably be done within FinishShutdown to prevent some games (ie. UNO) from progressing further while the Dialog is still being faded-out, since we can't override non-virtual method... so here is the closes one to FinishShutdown. + if (Memory::IsValidAddress(requestAddr)) // Need to validate first to prevent Invalid address when the game is being Shutdown/Exited to menu + Memory::Memcpy(requestAddr, &request, request.common.size, "NpSigninDialogParam"); + + return 0; +} + +void PSPNpSigninDialog::DoState(PointerWrap &p) { + PSPDialog::DoState(p); + + auto s = p.Section("PSPNpSigninDialog", 1, 1); + if (!s) + return; + + Do(p, request); + Do(p, step); + //Do(p, npSigninResult); + + if (p.mode == p.MODE_READ) { + startTime = 0; + } +} + +pspUtilityDialogCommon* PSPNpSigninDialog::GetCommonParam() +{ + return &request.common; +} diff --git a/Core/Dialog/PSPNpSigninDialog.h b/Core/Dialog/PSPNpSigninDialog.h new file mode 100644 index 0000000000..15aac7b0c9 --- /dev/null +++ b/Core/Dialog/PSPNpSigninDialog.h @@ -0,0 +1,67 @@ +// Copyright (c) 2012- 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/Dialog/PSPDialog.h" +#include "Core/MemMapHelpers.h" + + +struct SceUtilityNpSigninParam { + pspUtilityDialogCommon common; + // Initially all zero? Or is there a possibility for one of these unknown to be a buffer to a packet data if it wasn't null? + int npSigninStatus; + int unknown1; + int unknown2; + int unknown3; +}; + + +class PSPNpSigninDialog: public PSPDialog { +public: + PSPNpSigninDialog(UtilityDialogType type); + virtual ~PSPNpSigninDialog(); + + virtual int Init(u32 paramAddr); + virtual int Update(int animSpeed) override; + virtual int Shutdown(bool force = false) override; + virtual void DoState(PointerWrap &p) override; + virtual pspUtilityDialogCommon* GetCommonParam() override; + +protected: + bool UseAutoStatus() override { + return false; + } + +private: + void DisplayMessage(std::string text1, std::string text2a = "", std::string text2b = "", std::string text3a = "", std::string text3b = "", bool hasYesNo = false, bool hasOK = false); + void DrawBanner(); + void DrawIndicator(); + void DrawLogo(); + + SceUtilityNpSigninParam request = {}; + u32 requestAddr = 0; + //int npSigninResult = -1; + + int yesnoChoice = 0; + float scrollPos_ = 0.0f; + int framesUpHeld_ = 0; + int framesDownHeld_ = 0; + + u64 startTime = 0; + int step = 0; +}; diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index 9feb73cd04..d745818338 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -765,6 +765,11 @@ template void WrapU_UUUUUUU() { RETURN(retval); } +template void WrapI_IUUUUUU() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6)); + RETURN(retval); +} + template void WrapI_UIUU() { u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); RETURN(retval); diff --git a/Core/HLE/sceNp.cpp b/Core/HLE/sceNp.cpp index 3bd9d228b0..4c71f3db55 100644 --- a/Core/HLE/sceNp.cpp +++ b/Core/HLE/sceNp.cpp @@ -20,26 +20,32 @@ #include #include -#include #include #include "Core/MemMapHelpers.h" -#include +#include "Core/CoreTiming.h" +#include "Core/Config.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" - #include "Core/HLE/sceNp.h" -#include bool npAuthInited = false; +int npSigninState = NP_SIGNIN_STATUS_NONE; SceNpAuthMemoryStat npAuthMemStat = {}; -std::string serviceId = ""; +PSPTimeval npSigninTimestamp{}; -int parentalControl = PARENTAL_CONTROL_ENABLED; -int userAge = 24; // faking user Age to 24 yo -int chatRestriction = 0; // default/initial value on Patapon 3 is 1 (restricted boolean?) -std::string onlineId = "DummyOnlineId"; -std::string avatarUrl = "http://DummyAvatarUrl"; +// TODO: These should probably be grouped in a struct, since they're used to generate an auth ticket +int npParentalControl = PARENTAL_CONTROL_ENABLED; +int npUserAge = 24; // faking user Age to 24 yo +int npChatRestriction = 0; // default/initial value on Patapon 3 is 1 (restricted boolean?) +SceNpMyLanguages npMyLangList = { 1033, 2057, 1036 }; +char npCountryCode[3] = "fr"; // dummy data taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket +char npRegionCode[3] = "c9"; // not sure what "c9" meant, since it was close to country code data, might be region-related data? +std::string npOnlineId = "DummyOnlineId"; // SceNpOnlineId struct? +std::string npServiceId = ""; // UNO game uses EP2006-NPEH00020_00 +std::string npAvatarUrl = "http://DummyAvatarUrl"; // SceNpAvatarUrl struct? + +SceNpCommunicationId npTitleId; std::recursive_mutex npAuthEvtMtx; std::deque npAuthEvents; @@ -53,7 +59,8 @@ int writeTicketParam(u8* buffer, const u16_be type, const char* data = nullptr, u16_be sz = (data == nullptr)? static_cast(0): size; memcpy(buffer, &type, 2); memcpy(buffer + 2, &sz, 2); - if (sz>0) memcpy(buffer + 4, data, sz); + if (sz > 0 && data != nullptr) + memcpy(buffer + 4, data, sz); return sz + 4; } @@ -100,38 +107,149 @@ void notifyNpAuthHandlers(u32 id, u32 result, u32 argAddr) { static int sceNpInit() { - ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__); + ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); + npOnlineId = g_Config.sNickName; + return 0; } static int sceNpTerm() { // No parameters - ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__); + ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); return 0; } static int sceNpGetContentRatingFlag(u32 parentalControlAddr, u32 userAgeAddr) { - WARN_LOG(HLE, "UNTESTED %s(%08x, %08x)", __FUNCTION__, parentalControlAddr, userAgeAddr); + WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, parentalControlAddr, userAgeAddr); if (!Memory::IsValidAddress(parentalControlAddr) || !Memory::IsValidAddress(userAgeAddr)) - return hleLogError(HLE, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); + return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - Memory::Write_U32(parentalControl, parentalControlAddr); - Memory::Write_U32(userAge, userAgeAddr); + INFO_LOG(SCENET, "%s - Parental Control: %d", __FUNCTION__, npParentalControl); + INFO_LOG(SCENET, "%s - User Age: %d", __FUNCTION__, npUserAge); + + Memory::Write_U32(npParentalControl, parentalControlAddr); + Memory::Write_U32(npUserAge, userAgeAddr); return 0; } static int sceNpGetChatRestrictionFlag(u32 flagAddr) { - WARN_LOG(HLE, "UNTESTED %s(%08x)", __FUNCTION__, flagAddr); + WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, flagAddr); if (!Memory::IsValidAddress(flagAddr)) - return hleLogError(HLE, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); + return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - Memory::Write_U32(chatRestriction, flagAddr); + INFO_LOG(SCENET, "%s - Chat Restriction: %d", __FUNCTION__, npChatRestriction); + + Memory::Write_U32(npChatRestriction, flagAddr); + + return 0; +} + +static int sceNpGetOnlineId(u32 idPtr) +{ + WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr); + + if (!Memory::IsValidAddress(idPtr)) + return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); + + SceNpOnlineId dummyOnlineId{}; + truncate_cpy(dummyOnlineId.data, sizeof(dummyOnlineId.data), npOnlineId.c_str()); + + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyOnlineId.data); + + Memory::WriteStruct(idPtr, &dummyOnlineId); + + return 0; +} + +int NpGetNpId(SceNpId* npid) +{ + truncate_cpy(npid->handle.data, sizeof(npid->handle.data), npOnlineId.c_str()); + return 0; +} + +static int sceNpGetNpId(u32 idPtr) +{ + WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr); + + if (!Memory::IsValidAddress(idPtr)) + return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); + + SceNpId dummyNpId{}; + int retval = NpGetNpId(&dummyNpId); + if (retval < 0) + return hleLogError(SCENET, retval); + + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyNpId.handle.data); + std::string datahex; + DataToHexString(dummyNpId.opt, sizeof(dummyNpId.opt), &datahex); + INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str()); + + Memory::WriteStruct(idPtr, &dummyNpId); + + return 0; +} + +static int sceNpGetAccountRegion(u32 countryCodePtr, u32 regionCodePtr) +{ + WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, countryCodePtr, regionCodePtr); + + if (!Memory::IsValidAddress(countryCodePtr) || !Memory::IsValidAddress(regionCodePtr)) + return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); + + SceNpCountryCode dummyCountryCode{}; + memcpy(dummyCountryCode.data, npCountryCode, sizeof(dummyCountryCode.data)); + SceNpCountryCode dummyRegionCode{}; + memcpy(dummyRegionCode.data, npRegionCode, sizeof(dummyRegionCode.data)); + + INFO_LOG(SCENET, "%s - Country Code: %d", __FUNCTION__, dummyCountryCode.data); + INFO_LOG(SCENET, "%s - Region? Code: %d", __FUNCTION__, dummyRegionCode.data); + + Memory::WriteStruct(countryCodePtr, &dummyCountryCode); + Memory::WriteStruct(regionCodePtr, &dummyRegionCode); + + return 0; +} + +static int sceNpGetMyLanguages(u32 langListPtr) +{ + WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, langListPtr); + + if (!Memory::IsValidAddress(langListPtr)) + return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); + + INFO_LOG(SCENET, "%s - Language1 Code: %d", __FUNCTION__, npMyLangList.language1); + INFO_LOG(SCENET, "%s - Language2 Code: %d", __FUNCTION__, npMyLangList.language2); + INFO_LOG(SCENET, "%s - Language3 Code: %d", __FUNCTION__, npMyLangList.language3); + + Memory::WriteStruct(langListPtr, &npMyLangList); + + return 0; +} + +static int sceNpGetUserProfile(u32 profilePtr) +{ + WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, profilePtr); + + if (!Memory::IsValidAddress(profilePtr)) + return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); + + SceNpUserInformation dummyProfile{}; + truncate_cpy(dummyProfile.userId.handle.data, sizeof(dummyProfile.userId.handle.data), npOnlineId.c_str()); + truncate_cpy(dummyProfile.icon.data, sizeof(dummyProfile.icon.data), npAvatarUrl.c_str()); + + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyProfile.userId.handle.data); + std::string datahex; + DataToHexString(dummyProfile.userId.opt, sizeof(dummyProfile.userId.opt), &datahex); + INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str()); + INFO_LOG(SCENET, "%s - Avatar URL: %s", __FUNCTION__, dummyProfile.icon.data); + + Memory::WriteStruct(profilePtr, &dummyProfile); return 0; } @@ -141,6 +259,11 @@ const HLEFunction sceNp[] = { {0X37E1E274, &WrapI_V, "sceNpTerm", 'i', "" }, {0XBB069A87, &WrapI_UU, "sceNpGetContentRatingFlag", 'i', "xx" }, {0X1D60AE4B, &WrapI_U, "sceNpGetChatRestrictionFlag", 'i', "x" }, + {0x4B5C71C8, &WrapI_U, "sceNpGetOnlineId", 'i', "x" }, + {0x633B5F71, &WrapI_U, "sceNpGetNpId", 'i', "x" }, + {0x7E0864DF, &WrapI_U, "sceNpGetUserProfile", 'i', "x" }, + {0xA0BE3C4B, &WrapI_UU, "sceNpGetAccountRegion", 'i', "xx" }, + {0xCDCC21D3, &WrapI_U, "sceNpGetMyLanguages", 'i', "x" }, }; void Register_sceNp() @@ -151,29 +274,29 @@ void Register_sceNp() static int sceNpAuthTerm() { // No parameters - ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__); + ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); npAuthInited = false; return 0; } static int sceNpAuthInit(u32 poolSize, u32 stackSize, u32 threadPrio) { - ERROR_LOG(HLE, "UNIMPL %s(%d, %d, %d)", __FUNCTION__, poolSize, stackSize, threadPrio); - npAuthMemStat.npMemSize = poolSize; - npAuthMemStat.npMaxMemSize = poolSize / 2; // Dummy - npAuthMemStat.npFreeMemSize = poolSize - 16; // Dummy. + ERROR_LOG(SCENET, "UNIMPL %s(%d, %d, %d)", __FUNCTION__, poolSize, stackSize, threadPrio); + npAuthMemStat.npMemSize = poolSize - 0x20; + npAuthMemStat.npMaxMemSize = 0x4050; // Dummy maximum foot print + npAuthMemStat.npFreeMemSize = npAuthMemStat.npMemSize; npAuthEvents.clear(); npAuthInited = true; return 0; } -static int sceNpAuthGetMemoryStat(u32 memStatAddr) +int sceNpAuthGetMemoryStat(u32 memStatAddr) { - ERROR_LOG(HLE, "UNIMPL %s(%08x)", __FUNCTION__, memStatAddr); + ERROR_LOG(SCENET, "UNIMPL %s(%08x)", __FUNCTION__, memStatAddr); if (!Memory::IsValidAddress(memStatAddr)) - return hleLogError(HLE, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); + return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); Memory::WriteStruct(memStatAddr, &npAuthMemStat); @@ -192,22 +315,23 @@ param seems to be a struct where offset: +20: 32-bit a pointer to a random data (4 to 8-bytes data max? both 2x 32-bit seems to be a valid pointer). optional handler args? return value >= 0 and <0 seems to be stored at a different location by the game (valid result vs error code?) */ -static int sceNpAuthCreateStartRequest(u32 paramAddr) +int sceNpAuthCreateStartRequest(u32 paramAddr) { - WARN_LOG(HLE, "UNTESTED %s(%08x) at %08x", __FUNCTION__, paramAddr, currentMIPS->pc); + WARN_LOG(SCENET, "UNTESTED %s(%08x) at %08x", __FUNCTION__, paramAddr, currentMIPS->pc); if (!Memory::IsValidAddress(paramAddr)) - return hleLogError(HLE, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); + return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); SceNpAuthRequestParameter params = {}; int size = Memory::Read_U32(paramAddr); Memory::Memcpy(¶ms, paramAddr, size); - serviceId = Memory::GetCharPointer(params.serviceIdAddr); + npServiceId = Memory::GetCharPointer(params.serviceIdAddr); - INFO_LOG(HLE, "%s - Max Version: %u.%u", __FUNCTION__, params.version.major, params.version.minor); - INFO_LOG(HLE, "%s - Service ID: %s", __FUNCTION__, Memory::GetCharPointer(params.serviceIdAddr)); - INFO_LOG(HLE, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(params.entitlementIdAddr)); - INFO_LOG(HLE, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, Memory::GetCharPointer(params.cookieAddr)); + INFO_LOG(SCENET, "%s - Max Version: %u.%u", __FUNCTION__, params.version.major, params.version.minor); + INFO_LOG(SCENET, "%s - Service ID: %s", __FUNCTION__, Memory::GetCharPointer(params.serviceIdAddr)); + INFO_LOG(SCENET, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(params.entitlementIdAddr)); + INFO_LOG(SCENET, "%s - Consumed Count: %d", __FUNCTION__, params.consumedCount); + INFO_LOG(SCENET, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, Memory::GetCharPointer(params.cookieAddr)); u32 retval = 0; if (params.size >= 32 && params.ticketCbAddr != 0) { @@ -254,12 +378,12 @@ static int sceNpAuthCreateStartRequest(u32 paramAddr) // Used within callback of sceNpAuthCreateStartRequest (arg1 = callback's args[0], arg2 = output structPtr?, arg3 = callback's args[1]) // Is this using request id for Arg1 or cbId? // JPCSP is using length = 248 for dummy ticket -static int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) +int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) { - ERROR_LOG(HLE, "UNIMPL %s(%d, %08x, %d) at %08x", __FUNCTION__, requestId, bufferAddr, length, currentMIPS->pc); + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %d) at %08x", __FUNCTION__, requestId, bufferAddr, length, currentMIPS->pc); if (!Memory::IsValidAddress(bufferAddr)) - return hleLogError(HLE, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); + return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); int result = length; Memory::Memset(bufferAddr, 0, length); @@ -269,35 +393,35 @@ static int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) ticket.header.size = 0xF0; // size excluding the header u8* buf = Memory::GetPointerWrite(bufferAddr + sizeof(ticket)); int ofs = 0; - ofs += writeTicketParam(buf, PARAM_TYPE_STRING_ASCII, "\x4c\x47\x56\x3b\x81\x39\x4a\x22\xd8\x6b\xc1\x57\x71\x6e\xfd\xb8\xab\x63\xcc\x51", 20); // 20 random letters, token key? + ofs += writeTicketParam(buf, PARAM_TYPE_STRING_ASCII, "\x4c\x47\x56\x3b\x81\x39\x4a\x22\xd8\x6b\xc1\x57\x71\x6e\xfd\xb8\xab\x63\xcc\x51", 20); // 20 random letters, token key or SceNpSignature? ofs += writeTicketU32Param(buf + ofs, PARAM_TYPE_INT, 0x0100); // a flags? - PSPTimeval tv; + PSPTimeval tv; //npSigninTimestamp __RtcTimeOfDay(&tv); u64 now = 1000ULL*tv.tv_sec + tv.tv_usec/1000ULL; // in milliseconds, since 1900? ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_DATE, now); ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_DATE, now + 10 * 60 * 1000); // now + 10 minutes, expired time? ofs += writeTicketU64Param(buf + ofs, PARAM_TYPE_LONG, 0x592e71c546e86859); // seems to be consistent, 8-bytes password hash may be? or related to entitlement? or console id? - ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, onlineId.c_str(), 32); // username - ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "fr\0\2", 4); // SceNpCountryCode ? ie. "fr" + 00 02 - ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, "c9", 4); // 2-char code? related to country/lang code? ie. "c9" + 00 00 - ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, serviceId.c_str(), 24); + ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, npOnlineId.c_str(), 32); // username + ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, npCountryCode, 4); // SceNpCountryCode ? ie. "fr" + 00 02 + ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, npRegionCode, 4); // 2-char code? related to country/lang code? ie. "c9" + 00 00 + ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, npServiceId.c_str(), 24); int status = 0; - if (parentalControl == PARENTAL_CONTROL_ENABLED) { + if (npParentalControl == PARENTAL_CONTROL_ENABLED) { status |= STATUS_ACCOUNT_PARENTAL_CONTROL_ENABLED; } - status |= (userAge & 0x7F) << 24; + status |= (npUserAge & 0x7F) << 24; ofs += writeTicketU32Param(buf + ofs, PARAM_TYPE_INT, status); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL); ticket.section.type = SECTION_TYPE_BODY; ticket.section.size = ofs; Memory::WriteStruct(bufferAddr, &ticket); - SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on ver 2.1 containing 4-chars ASCII + 20-chars ASCII + SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on version 2.1 containing 4-chars ASCII + 20-chars ASCII Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, &footer); ofs += sizeof(footer); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x34\xcd\x3c\xa9", 4); - ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x3a\x4b\x42\x66\x92\xda\x6b\x7c\xb7\x4c\xe8\xd9\x4f\x2b\x77\x15\x91\xb8\xa4\xa9", 20); - u8 unknownBytes[36] = {}; + ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x3a\x4b\x42\x66\x92\xda\x6b\x7c\xb7\x4c\xe8\xd9\x4f\x2b\x77\x15\x91\xb8\xa4\xa9", 20); // 20 random letters, token key or SceNpSignature? + u8 unknownBytes[36] = {}; // includes Language list? Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, unknownBytes); result = ticket.header.size + sizeof(ticket.header); // dummy ticket is 248 bytes @@ -307,28 +431,30 @@ static int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) // Used within callback of sceNpAuthCreateStartRequest (arg1 = structPtr?, arg2 = callback's args[1], arg3 = DLCcode? ie. "EP9000-UCES01421_00-DLL001", arg4 = Patapon 3 always set to 0?) // Patapon 3 will loop (for each DLC?) through an array of 4+4 bytes, ID addr (pchar) + result (int). Each loop calls this function using the same ticket addr but use different ID addr (arg3) and store the return value in result field (default/initial = -1) -static int sceNpAuthGetEntitlementById(u32 ticketBufferAddr, u32 ticketLength, u32 entitlementIdAddr, u32 arg4) +int sceNpAuthGetEntitlementById(u32 ticketBufferAddr, u32 ticketLength, u32 entitlementIdAddr, u32 arg4) { - ERROR_LOG(HLE, "UNIMPL %s(%08x, %d, %08x, %d)", __FUNCTION__, ticketBufferAddr, ticketLength, entitlementIdAddr, arg4); - INFO_LOG(HLE, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(entitlementIdAddr)); + ERROR_LOG(SCENET, "UNIMPL %s(%08x, %d, %08x, %d)", __FUNCTION__, ticketBufferAddr, ticketLength, entitlementIdAddr, arg4); + INFO_LOG(SCENET, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(entitlementIdAddr)); + // Do we return the entitlement through function result? or update the ticket content? or replace the arg3 data with SceNpEntitlement struct? return 1; // dummy value assuming it's a boolean/flag, since we don't know how to return the entitlement result yet } -static int sceNpAuthAbortRequest(int requestId) +int sceNpAuthAbortRequest(int requestId) { - WARN_LOG(HLE, "UNTESTED %s(%i)", __FUNCTION__, requestId); + WARN_LOG(SCENET, "UNTESTED %s(%i)", __FUNCTION__, requestId); // TODO: Disconnect HTTPS connection & cancel the callback event std::lock_guard npAuthGuard(npAuthEvtMtx); for (auto it = npAuthEvents.begin(); it != npAuthEvents.end(); ) { (it->data[0] == requestId) ? it = npAuthEvents.erase(it) : ++it; } + return 0; } -static int sceNpAuthDestroyRequest(int requestId) +int sceNpAuthDestroyRequest(int requestId) { - WARN_LOG(HLE, "UNTESTED %s(%i)", __FUNCTION__, requestId); + WARN_LOG(SCENET, "UNTESTED %s(%i)", __FUNCTION__, requestId); // Remove callback handler int handlerID = requestId - 1; if (npAuthHandlers.find(handlerID) != npAuthHandlers.end()) { @@ -338,20 +464,50 @@ static int sceNpAuthDestroyRequest(int requestId) else { ERROR_LOG(SCENET, "%s: Invalid request ID %d", __FUNCTION__, requestId); } + // Patapon 3 is checking for error code 0x80550402 return 0; } +int sceNpAuthGetTicketParam(u32 ticketBufPtr, int ticketLen, int paramNum, u32 bufferPtr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%08x, %0d, %d, %08x) at %08x", __FUNCTION__, ticketBufPtr, ticketLen, paramNum, bufferPtr, currentMIPS->pc); + const u32 PARAM_BUFFER_MAX_SIZE = 256; + Memory::Memset(bufferPtr, 0, PARAM_BUFFER_MAX_SIZE); // JPCSP: This clear is always done, even when an error is returned + if (paramNum < 0 || paramNum >= NUMBER_PARAMETERS) { + return SCE_NP_MANAGER_ERROR_INVALID_ARGUMENT; + } + + SceNpTicket* ticket = (SceNpTicket*)Memory::GetPointer(ticketBufPtr); + u32 inbuf = ticketBufPtr; + inbuf += sizeof(ticket->header); + inbuf += ticket->section.size + sizeof(ticket->section); + u32 outbuf = bufferPtr; + for (int i = 0; i < paramNum; i++) { + SceNpTicketParamData* ticketParam = (SceNpTicketParamData*)Memory::GetPointer(inbuf); + u32 sz = (u32)sizeof(SceNpTicketParamData) + ticketParam->length; + Memory::Memcpy(outbuf, inbuf, sz); + DEBUG_LOG(SCENET, "%s - Param #%d: Type = %04x, Length = %u", __FUNCTION__, i, ticketParam->type, static_cast(ticketParam->length)); + outbuf += sz; + inbuf += sz; + if (outbuf - bufferPtr >= PARAM_BUFFER_MAX_SIZE || inbuf - ticketBufPtr >= (u32)ticketLen) + break; + } + + return 0; +} const HLEFunction sceNpAuth[] = { - {0X4EC1F667, &WrapI_V, "sceNpAuthTerm", 'i', "" }, - {0XA1DE86F8, &WrapI_UUU, "sceNpAuthInit", 'i', "xxx" }, - {0XF4531ADC, &WrapI_U, "sceNpAuthGetMemoryStat", 'i', "x" }, - {0XCD86A656, &WrapI_U, "sceNpAuthCreateStartRequest", 'i', "x" }, - {0X3F1C1F70, &WrapI_UUU, "sceNpAuthGetTicket", 'i', "xxx" }, - {0X6900F084, &WrapI_UUUU, "sceNpAuthGetEntitlementById", 'i', "xxxx" }, - {0XD99455DD, &WrapI_I, "sceNpAuthAbortRequest", 'i', "i" }, - {0X72BB0467, &WrapI_I, "sceNpAuthDestroyRequest", 'i', "i" }, + {0X4EC1F667, &WrapI_V, "sceNpAuthTerm", 'i', "" }, + {0XA1DE86F8, &WrapI_UUU, "sceNpAuthInit", 'i', "xxx" }, + {0XF4531ADC, &WrapI_U, "sceNpAuthGetMemoryStat", 'i', "x" }, + {0XCD86A656, &WrapI_U, "sceNpAuthCreateStartRequest", 'i', "x" }, + {0X3F1C1F70, &WrapI_UUU, "sceNpAuthGetTicket", 'i', "xxx" }, + {0X6900F084, &WrapI_UUUU, "sceNpAuthGetEntitlementById", 'i', "xxxx" }, + {0XD99455DD, &WrapI_I, "sceNpAuthAbortRequest", 'i', "i" }, + {0X72BB0467, &WrapI_I, "sceNpAuthDestroyRequest", 'i', "i" }, + {0x5A3CB57A, &WrapI_UIIU, "sceNpAuthGetTicketParam", 'i', "xiix" }, + {0x75FB0AE3, nullptr, "sceNpAuthGetEntitlementIdList", 'i', "" }, }; void Register_sceNpAuth() @@ -362,46 +518,90 @@ void Register_sceNpAuth() static int sceNpServiceTerm() { // No parameters - ERROR_LOG(HLE, "UNIMPL %s()", __FUNCTION__); + ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); return 0; } static int sceNpServiceInit(u32 poolSize, u32 stackSize, u32 threadPrio) { - ERROR_LOG(HLE, "UNIMPL %s(%08x, %08x, %08x)", __FUNCTION__, poolSize, stackSize, threadPrio); + ERROR_LOG(SCENET, "UNIMPL %s(%08x, %08x, %08x)", __FUNCTION__, poolSize, stackSize, threadPrio); return 0; } +// FIXME: On Patapon 3 the Arg is pointing to a String, but based on RPCS3 the Arg is an Id integer ? static int sceNpLookupCreateTransactionCtx(u32 lookupTitleCtxIdAddr) { - ERROR_LOG(HLE, "UNIMPL %s(%08x)", __FUNCTION__, lookupTitleCtxIdAddr); + ERROR_LOG(SCENET, "UNIMPL %s(%08x)", __FUNCTION__, lookupTitleCtxIdAddr); INFO_LOG(SCENET, "%s - Title ID: %s", __FUNCTION__, Memory::GetCharPointer(lookupTitleCtxIdAddr)); // Patapon 3 will only Destroy if returned Id > 0. Is 0 a valid id? return 1; // returning dummy transaction id } // transId: id returned from sceNpLookupCreateTransactionCtx -static int sceNpLookupDestroyTransactionCtx(u32 transId) +static int sceNpLookupDestroyTransactionCtx(s32 transId) { - ERROR_LOG(HLE, "UNIMPL %s(%d)", __FUNCTION__, transId); + ERROR_LOG(SCENET, "UNIMPL %s(%d)", __FUNCTION__, transId); return 0; } // transId: id returned from sceNpLookupCreateTransactionCtx // Patapon 3 always set Arg5 to 0 // Addr args have something to do with GameUpdate? -static int sceNpLookupTitleSmallStorage(u32 transId, u32 arg2Addr, u32 arg3, u32 arg4Addr, u32 arg5) +// FIXME: maxSize and contentLength are u64 based on https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/sceNp.cpp ? But on Patapon 3 optionAddr will be deadbeef if maxSize is u64 ? +static int sceNpLookupTitleSmallStorage(s32 transId, u32 dataAddr, u32 maxSize, u32 contentLengthAddr, u32 optionAddr) { - ERROR_LOG(HLE, "UNIMPL %s(%d, %08x, %08x, %08x, %08x)", __FUNCTION__, transId, arg2Addr, arg3, arg4Addr, arg5); + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %d, %08x[%d], %08x) at %08x", __FUNCTION__, transId, dataAddr, maxSize, contentLengthAddr, (Memory::IsValidAddress(contentLengthAddr)? Memory::Read_U32(contentLengthAddr): 0), optionAddr, currentMIPS->pc); + return 0; +} + +// On Resistance Retribution: +// unknownAddr pointing to a struct of: +// 32-bit pointer (ie. 08efc6c4)? or a timestamp combined with the next 32-bit value? +// 32-bit pointer (ie. 08f9d101)? but unaligned (the lowest byte seems to be intentionally set to 1)? so probably not a pointer, may be a timestamp combined with previous 32-bit value? +// 32-bit pointer? Seems to be pointing to dummy ticket data generated by sceNpAuthGetTicket +// 32-bit value (248) dummy ticket length from NpAuth Ticket? +// There could be more data in the struct? (at least 48-bytes?) +static int sceNpRosterCreateRequest(u32 unknownAddr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%08x) at %08x", __FUNCTION__, unknownAddr, currentMIPS->pc); + return 1; // returning dummy roster id +} + +// On Resistance Retribution: +// unknown1 set to 50 (max entries?), +// unknown2 set to 0, +// unknown3Addr pointing to unset buffer? (output entry data? usually located right after number of entries?), +// unknown4Addr pointing to 32-bit value set to 0 (output number of entries?), +// unknown5Addr pointing to zeroed buffer?, +// unknown6 set to 0 +static int sceNpRosterGetFriendListEntry(s32 rosterId, u32 unknown1, u32 unknown2, u32 unknown3Addr, u32 unknown4Addr, u32 unknown5Addr, u32 unknown6) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %08x, %08x, %08x, %08x, %08x) at %08x", __FUNCTION__, rosterId, unknown1, unknown2, unknown3Addr, unknown4Addr, unknown5Addr, unknown6, currentMIPS->pc); + return 0; +} + +static int sceNpRosterAbort(s32 rosterId) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d) at %08x", __FUNCTION__, rosterId, currentMIPS->pc); + return 0; +} + +static int sceNpRosterDeleteRequest(s32 rosterId) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d) at %08x", __FUNCTION__, rosterId, currentMIPS->pc); return 0; } const HLEFunction sceNpService[] = { - {0X00ACFAC3, &WrapI_V, "sceNpServiceTerm", 'i', "" }, - {0X0F8F5821, &WrapI_UUU, "sceNpServiceInit", 'i', "xxx" }, - {0X5494274B, &WrapI_U, "sceNpLookupCreateTransactionCtx", 'i', "x" }, - {0XA670D3A3, &WrapI_U, "sceNpLookupDestroyTransactionCtx", 'i', "x" }, - {0XC76F55ED, &WrapI_UUUUU, "sceNpLookupTitleSmallStorage", 'i', "xxxxx" }, + {0X00ACFAC3, &WrapI_V, "sceNpServiceTerm", 'i', "" }, + {0X0F8F5821, &WrapI_UUU, "sceNpServiceInit", 'i', "xxx" }, + {0X5494274B, &WrapI_U, "sceNpLookupCreateTransactionCtx", 'i', "x" }, + {0XA670D3A3, &WrapI_I, "sceNpLookupDestroyTransactionCtx", 'i', "i" }, + {0XC76F55ED, &WrapI_IUUUU, "sceNpLookupTitleSmallStorage", 'i', "ixxxx" }, + {0XBE22EEA3, &WrapI_U, "sceNpRosterCreateRequest", 'i', "x" }, + {0X4E851B10, &WrapI_IUUUUUU, "sceNpRosterGetFriendListEntry", 'i', "ixxxxxx"}, + {0X5F5E32AF, &WrapI_I, "sceNpRosterAbort", 'i', "i" }, + {0X66C64821, &WrapI_I, "sceNpRosterDeleteRequest", 'i', "i" }, }; void Register_sceNpService() @@ -409,6 +609,7 @@ void Register_sceNpService() RegisterModule("sceNpService", ARRAY_SIZE(sceNpService), sceNpService); } +// TODO: Moves NpCommerce2-related stuff to sceNpCommerce2.cpp const HLEFunction sceNpCommerce2[] = { {0X005B5F20, nullptr, "sceNpCommerce2GetProductInfoStart", '?', "" }, {0X0E9956E3, nullptr, "sceNpCommerce2Init", '?', "" }, @@ -432,3 +633,4 @@ void Register_sceNpCommerce2() { RegisterModule("sceNpCommerce2", ARRAY_SIZE(sceNpCommerce2), sceNpCommerce2); } + diff --git a/Core/HLE/sceNp.h b/Core/HLE/sceNp.h index 3c117b33c7..b937213546 100644 --- a/Core/HLE/sceNp.h +++ b/Core/HLE/sceNp.h @@ -17,9 +17,16 @@ #pragma once -#ifdef _MSC_VER +#include +#include +#include +#include "Core/HLE/sceRtc.h" +#include "Common/Net/HTTPClient.h" +#include "Common/Net/Resolve.h" +#include "Common/Net/URL.h" + + #pragma pack(push,1) -#endif // Based on https://playstationdev.wiki/psvitadevwiki/index.php?title=Error_Codes #define SCE_NP_ERROR_ALREADY_INITIALIZED 0x80550001 @@ -47,6 +54,140 @@ #define SCE_NP_AUTH_ERROR_SERVICE_END 0x80550400 #define SCE_NP_AUTH_ERROR_SERVICE_DOWN 0x80550401 #define SCE_NP_AUTH_ERROR_SERVICE_BUSY 0x80550402 +#define SCE_NP_AUTH_ERROR_SERVER_MAINTENANCE 0x80550403 +#define SCE_NP_AUTH_ERROR_UNKNOWN 0x80550480 + +#define SCE_NP_MANAGER_ERROR_ALREADY_INITIALIZED 0x80550501 +#define SCE_NP_MANAGER_ERROR_NOT_INITIALIZED 0x80550502 +#define SCE_NP_MANAGER_ERROR_INVALID_ARGUMENT 0x80550503 +#define SCE_NP_MANAGER_ERROR_OUT_OF_MEMORY 0x80550504 +#define SCE_NP_MANAGER_ERROR_INVALID_TICKET_SIZE 0x80550505 +#define SCE_NP_MANAGER_ERROR_INVALID_STATE 0x80550506 +#define SCE_NP_MANAGER_ERROR_ABORTED 0x80550507 +#define SCE_NP_MANAGER_ERROR_VARIANT_ACCOUNT_ID 0x80550508 +#define SCE_NP_MANAGER_ERROR_ID_NOT_AVAIL 0x80550509 +#define SCE_NP_MANAGER_ERROR_SIGNOUT 0x8055050a +#define SCE_NP_MANAGER_ERROR_NOT_SIGNIN 0x8055050b + +#define SCE_NP_UTIL_ERROR_INVALID_ARGUMENT 0x80550601 +#define SCE_NP_UTIL_ERROR_INSUFFICIENT 0x80550602 +#define SCE_NP_UTIL_ERROR_PARSER_FAILED 0x80550603 +#define SCE_NP_UTIL_ERROR_INVALID_PROTOCOL_ID 0x80550604 +#define SCE_NP_UTIL_ERROR_INVALID_NP_ID 0x80550605 +#define SCE_NP_UTIL_ERROR_INVALID_NP_ENV 0x80550606 +#define SCE_NP_UTIL_ERROR_INVALID_NPCOMMID 0x80550607 +#define SCE_NP_UTIL_ERROR_INVALID_CHARACTER 0x80550608 +#define SCE_NP_UTIL_ERROR_NOT_MATCH 0x80550609 +#define SCE_NP_UTIL_ERROR_INVALID_TITLEID 0x8055060a +#define SCE_NP_UTIL_ERROR_INVALID_ESCAPE_STRING 0x8055060c +#define SCE_NP_UTIL_ERROR_UNKNOWN_TYPE 0x8055060d +#define SCE_NP_UTIL_ERROR_UNKNOWN 0x8055060e + +#define SCE_NP_COMMUNITY_ERROR_ALREADY_INITIALIZED 0x80550701 +#define SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED 0x80550702 +#define SCE_NP_COMMUNITY_ERROR_OUT_OF_MEMORY 0x80550703 +#define SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT 0x80550704 +#define SCE_NP_COMMUNITY_ERROR_NO_LOGIN 0x80550705 +#define SCE_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS 0x80550706 +#define SCE_NP_COMMUNITY_ERROR_ABORTED 0x80550707 +#define SCE_NP_COMMUNITY_ERROR_BAD_RESPONSE 0x80550708 +#define SCE_NP_COMMUNITY_ERROR_BODY_TOO_LARGE 0x80550709 +#define SCE_NP_COMMUNITY_ERROR_HTTP_SERVER 0x8055070a +#define SCE_NP_COMMUNITY_ERROR_INVALID_SIGNATURE 0x8055070b +#define SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT 0x8055070c +#define SCE_NP_COMMUNITY_ERROR_UNKNOWN_TYPE 0x8055070d +#define SCE_NP_COMMUNITY_ERROR_INVALID_ID 0x8055070e +#define SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID 0x8055070f +#define SCE_NP_COMMUNITY_ERROR_CONNECTION_HANDLE_ALREADY_EXISTS 0x80550710 +#define SCE_NP_COMMUNITY_ERROR_INVALID_TYPE 0x80550711 +#define SCE_NP_COMMUNITY_ERROR_TRANSACTION_ALREADY_END 0x80550712 +#define SCE_NP_COMMUNITY_ERROR_INVALID_PARTITION 0x80550713 +#define SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT 0x80550714 +#define SCE_NP_COMMUNITY_ERROR_CLIENT_HANDLE_ALREADY_EXISTS 0x80550715 +#define SCE_NP_COMMUNITY_ERROR_NO_RESOURCE 0x80550716 + +#define SCE_NP_COMMUNITY_SERVER_ERROR_BAD_REQUEST 0x80550801 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_TICKET 0x80550802 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_SIGNATURE 0x80550803 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_NPID 0x80550805 +#define SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN 0x80550806 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INTERNAL_SERVER_ERROR 0x80550807 +#define SCE_NP_COMMUNITY_SERVER_ERROR_VERSION_NOT_SUPPORTED 0x80550808 +#define SCE_NP_COMMUNITY_SERVER_ERROR_SERVICE_UNAVAILABLE 0x80550809 +#define SCE_NP_COMMUNITY_SERVER_ERROR_PLAYER_BANNED 0x8055080a +#define SCE_NP_COMMUNITY_SERVER_ERROR_CENSORED 0x8055080b +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_RECORD_FORBIDDEN 0x8055080c +#define SCE_NP_COMMUNITY_SERVER_ERROR_USER_PROFILE_NOT_FOUND 0x8055080d +#define SCE_NP_COMMUNITY_SERVER_ERROR_UPLOADER_DATA_NOT_FOUND 0x8055080e +#define SCE_NP_COMMUNITY_SERVER_ERROR_QUOTA_MASTER_NOT_FOUND 0x8055080f +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_TITLE_NOT_FOUND 0x80550810 +#define SCE_NP_COMMUNITY_SERVER_ERROR_BLACKLISTED_USER_ID 0x80550811 +#define SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND 0x80550812 +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_STORE_NOT_FOUND 0x80550814 +#define SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE 0x80550815 +#define SCE_NP_COMMUNITY_SERVER_ERROR_LATEST_UPDATE_NOT_FOUND 0x80550816 +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_BOARD_MASTER_NOT_FOUND 0x80550817 +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_GAME_DATA_MASTER_NOT_FOUND 0x80550818 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_ANTICHEAT_DATA 0x80550819 +#define SCE_NP_COMMUNITY_SERVER_ERROR_TOO_LARGE_DATA 0x8055081a +#define SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_USER_NPID 0x8055081b +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_ENVIRONMENT 0x8055081d +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_ONLINE_NAME_CHARACTER 0x8055081f +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_ONLINE_NAME_LENGTH 0x80550820 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_ABOUT_ME_CHARACTER 0x80550821 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_ABOUT_ME_LENGTH 0x80550822 +#define SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_SCORE 0x80550823 +#define SCE_NP_COMMUNITY_SERVER_ERROR_OVER_THE_RANKING_LIMIT 0x80550824 +#define SCE_NP_COMMUNITY_SERVER_ERROR_FAIL_TO_CREATE_SIGNATURE 0x80550826 +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_MASTER_INFO_NOT_FOUND 0x80550827 +#define SCE_NP_COMMUNITY_SERVER_ERROR_OVER_THE_GAME_DATA_LIMIT 0x80550828 +#define SCE_NP_COMMUNITY_SERVER_ERROR_SELF_DATA_NOT_FOUND 0x8055082a +#define SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED 0x8055082b +#define SCE_NP_COMMUNITY_SERVER_ERROR_GAME_DATA_ALREADY_EXISTS 0x8055082c +#define SCE_NP_COMMUNITY_SERVER_ERROR_MATCHING_BEFORE_SERVICE 0x805508a0 +#define SCE_NP_COMMUNITY_SERVER_ERROR_MATCHING_END_OF_SERVICE 0x805508a1 +#define SCE_NP_COMMUNITY_SERVER_ERROR_MATCHING_MAINTENANCE 0x805508a2 +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_BEFORE_SERVICE 0x805508a3 +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_END_OF_SERVICE 0x805508a4 +#define SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_MAINTENANCE 0x805508a5 +#define SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE 0x805508a6 +#define SCE_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_BEFORE_SERVICE 0x805508aa +#define SCE_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_END_OF_SERVICE 0x805508ab +#define SCE_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_MAINTENANCE 0x805508ac +#define SCE_NP_COMMUNITY_SERVER_ERROR_FSR_BEFORE_SERVICE 0x805508ad +#define SCE_NP_COMMUNITY_SERVER_ERROR_FSR_END_OF_SERVICE 0x805508ae +#define SCE_NP_COMMUNITY_SERVER_ERROR_FSR_MAINTENANCE 0x805508af +#define SCE_NP_COMMUNITY_SERVER_ERROR_UBS_BEFORE_SERVICE 0x805508b1 +#define SCE_NP_COMMUNITY_SERVER_ERROR_UBS_END_OF_SERVICE 0x805508b2 +#define SCE_NP_COMMUNITY_SERVER_ERROR_UBS_MAINTENANCE 0x805508b3 +#define SCE_NP_COMMUNITY_SERVER_ERROR_UNSPECIFIED 0x805508ff + +// Based on https://gist.githubusercontent.com/raw/4140449/PS%20Vita (Might be slightly different with PSP?) +#define SCE_NP_TROPHY_ERROR_UNKNOWN 0x80551600 +#define SCE_NP_TROPHY_ERROR_NOT_INITIALIZED 0x80551601 +#define SCE_NP_TROPHY_ERROR_ALREADY_INITIALIZED 0x80551602 +#define SCE_NP_TROPHY_ERROR_NO_MEMORY 0x80551603 +#define SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT 0x80551604 +#define SCE_NP_TROPHY_ERROR_INSUFFICIENT_BUFFER 0x80551605 +#define SCE_NP_TROPHY_ERROR_EXCEEDS_MAX 0x80551606 +#define SCE_NP_TROPHY_ERROR_ABORT 0x80551607 +#define SCE_NP_TROPHY_ERROR_INVALID_HANDLE 0x80551608 +#define SCE_NP_TROPHY_ERROR_INVALID_CONTEXT 0x80551609 +#define SCE_NP_TROPHY_ERROR_INVALID_NPCOMMID 0x8055160a +#define SCE_NP_TROPHY_ERROR_INVALID_NPCOMMSIGN 0x8055160b + +#define SCE_NP_BASIC_ERROR_BASE 0x80551d00 +#define SCE_NP_BASIC_ERROR_UNKNOWN 0x80551d01 +#define SCE_NP_BASIC_ERROR_INVALID_ARGUMENT 0x80551d02 +#define SCE_NP_BASIC_ERROR_OUT_OF_MEMORY 0x80551d03 +#define SCE_NP_BASIC_ERROR_NOT_INITIALIZED 0x80551d04 +#define SCE_NP_BASIC_ERROR_ALREADY_INITIALIZED 0x80551d05 +#define SCE_NP_BASIC_ERROR_SIGNED_OUT 0x80551d06 +#define SCE_NP_BASIC_ERROR_NOT_ONLINE 0x80551d07 +#define SCE_NP_BASIC_ERROR_DATA_NOT_FOUND 0x80551d08 +#define SCE_NP_BASIC_ERROR_BUSY 0x80551d09 +#define SCE_NP_BASIC_ERROR_NOT_READY_TO_COMMUNICATE 0x80551d0a +#define SCE_NP_BASIC_ERROR_NO_COMM_ID_SUPPLIED 0x80551d0b // Based on https://github.com/RPCS3/rpcs3/blob/psp2/rpcs3/Emu/PSP2/Modules/sceNpCommon.h enum SceNpServiceState : s32 @@ -82,6 +223,7 @@ struct SceNpCommunicationConfig PSPPointer commSignature; }; +// Part of BCP 47 Code (ie. "fr" for France/French)? struct SceNpCountryCode { char data[2]; @@ -117,6 +259,7 @@ struct SceNpUserInformation u8 reserved[52]; }; +// Language Code (ie. 1033 for "en-US")? struct SceNpMyLanguages { s32_le language1; @@ -172,8 +315,13 @@ struct NpAuthHandler { u32 argument; }; -struct NpAuthArgs { +union NpAuthArgs { u32_le data[3]; // id, result, ArgAddr + struct { + s32_le id; + s32_le result; + u32_le argAddr; + }; }; using SceNpAuthCallback = s32(s32 id, s32 result, PSPPointer arg); @@ -227,9 +375,9 @@ struct SceNpEntitlement // Tickets data are in big-endian based on captured packets struct SceNpTicketParamData { - u16_be type; - u16_be length; - //u8 value[]; // optional data + u16_be type; // 0(NULL), 1(32-bit int), 2(64-bi long), 4(ansi string?), 7(date/timestamp), 8(ascii string) + u16_be length; // size of the following data value + //u8 value[]; // optional data of length size }; struct SceNpTicketHeader @@ -242,7 +390,7 @@ struct SceNpTicketHeader // A section can contain other sections or param data, thus sharing their enum/def? struct SceNpTicketSection { - u16_be type; // section type? ie. 30 XX where known XX are 00, 02, 10, 11 + u16_be type; // section type? ie. 30 XX where known XX are 00(Body), 02(Footer), 10, 11 u16_be size; // total section size (excluding this 4-bytes section delimiter struct) }; @@ -250,10 +398,19 @@ struct SceNpTicket { SceNpTicketHeader header; SceNpTicketSection section; // Body or Parameter sections? - //SceNpTicketParamData parameters[]; // a list of TicketParamData + //SceNpTicketParamData parameters[]; // a list of TicketParamData following the section //u8 unknownBytes[]; // optional data? }; +struct SceNpAuthMemoryStat { + int npMemSize; // Memory allocated by the NP utility. Pool Size? + int npMaxMemSize; // Maximum memory used by the NP utility. + int npFreeMemSize; // Free memory available to use by the NP utility. +}; + +#pragma pack(pop) + + #define PARENTAL_CONTROL_DISABLED 0 #define PARENTAL_CONTROL_ENABLED 1 @@ -261,22 +418,34 @@ struct SceNpTicket #define STATUS_ACCOUNT_CHAT_RESTRICTED 0x100 #define STATUS_ACCOUNT_PARENTAL_CONTROL_ENABLED 0x200 -struct SceNpAuthMemoryStat { - int npMemSize; // Memory allocated by the NP utility. - int npMaxMemSize; // Maximum memory used by the NP utility. - int npFreeMemSize; // Free memory available to use by the NP utility. -}; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif +#define NP_SIGNIN_STATUS_NONE 0 // SIGNEDOUT? +#define NP_SIGNIN_STATUS_SUCCESS 1 +#define NP_SIGNIN_STATUS_CANCELED 2 +#define NP_SIGNIN_STATUS_FAILED 3 // ERROR/ABORTED/SIGNEDOUT? +// Used by PSPNpSigninDialog.cpp +extern int npSigninState; +extern PSPTimeval npSigninTimestamp; +// Used by sceNet.cpp since we're borrowing Apctl's PSPThread to process NP events & callbacks. +// TODO: NP events should be processed on it's own PSPThread extern std::recursive_mutex npAuthEvtMtx; extern std::deque npAuthEvents; extern std::map npAuthHandlers; +// Used by sceNp2.cpp +extern SceNpCommunicationId npTitleId; + +int NpGetNpId(SceNpId* npid); + +int sceNpAuthGetMemoryStat(u32 memStatAddr); +int sceNpAuthCreateStartRequest(u32 paramAddr); +int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length); +int sceNpAuthGetEntitlementById(u32 ticketBufferAddr, u32 ticketLength, u32 entitlementIdAddr, u32 arg4); +int sceNpAuthAbortRequest(int requestId); +int sceNpAuthDestroyRequest(int requestId); + void Register_sceNp(); -void Register_sceNpCommerce2(); void Register_sceNpService(); void Register_sceNpAuth(); +void Register_sceNpCommerce2(); diff --git a/Core/HLE/sceNp2.cpp b/Core/HLE/sceNp2.cpp new file mode 100644 index 0000000000..b0a7565fde --- /dev/null +++ b/Core/HLE/sceNp2.cpp @@ -0,0 +1,568 @@ +// Copyright (c) 2022- 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/. + +// This is pretty much a stub implementation. Doesn't actually do anything, just tries to return values +// to keep games happy anyway. + +#include +#include +#include +#include "Core/MemMapHelpers.h" +#include "Core/CoreTiming.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceNp.h" +#include "Core/HLE/sceNp2.h" + + +bool npMatching2Inited = false; +SceNpAuthMemoryStat npMatching2MemStat = {}; + +std::recursive_mutex npMatching2EvtMtx; +std::deque npMatching2Events; +std::map npMatching2Handlers; +//std::map npMatching2Contexts; + + +// serverId: 0 on 0x0103/0x0104/0x0105/0x0107/0x0108/0x0109/0x010a/0x010b/0x010c/0x010d (ie. when already joined to a server?) +// unk1~unk5 usually 0, +// unk1: 1st 32-bit of LeaveRoom/etc's Arg2 on 0x0103/0x0104/0x0105/0x0107/0x0108/0x0109/0x010a/0x010b/0x010c/0x010d/0x010e +// unk2: 2nd 32-bit of LeaveRoom/etc's Arg2 on 0x0103/0x0104/0x0105/0x0107/0x0108/0x0109/0x010a/0x010b/0x010c/0x010d/0x010e +// unk5: 1 on 0x0002/0x0003/0x0005/0x0006/0x0007/0x0101/0x0102/0x0106 +// unk6 (new state?): 8-bit?(masked with 0xff) 0x01 on 0x0001, 0x03 on 0x0002, 0x04 on 0x0003, 0x05 on 0x0004, 0x06 on 0x0005, 0x07 on 0x0006, 0x08 on 0x0007, +// 0x09 on 0x0101, 0x0A on 0x0102, 0x0C on 0x0103, 0x0D on 0x0104, 0x0E on 0x0105, 0x0F on 0x0106, 0x10 on 0x0107, 0x11 on 0x0108, +// 0x12 on 0x0109, 0x13 on 0x010a, 0x14 on 0x010b, 0x15 on 0x010c, 0x16 on 0x010d, 0x17 on 0x010e, 0x18 on 0xa102 +void notifyNpMatching2Handlers(NpMatching2Args &args, u32 ctxId, u32 serverId, u32 cbFuncAddr, u32 cbArgAddr, u32 unk3, u32 unk4, u32 unk5, u8 unk6) { + std::lock_guard npMatching2Guard(npMatching2EvtMtx); + // TODO: separate/map each list per ctxId + npMatching2Events.push_back(args); +} + +static int sceNpMatching2Init(int poolSize, int threadPriority, int cpuAffinityMask, int threadStackSize) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %d, %d, %d) at %08x", __FUNCTION__, poolSize, threadPriority, cpuAffinityMask, threadStackSize, currentMIPS->pc); + //if (npMatching2Inited) + // return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED); + + npMatching2MemStat.npMemSize = poolSize - 0x20; + npMatching2MemStat.npMaxMemSize = 0x4050; // Dummy maximum foot print + npMatching2MemStat.npFreeMemSize = npMatching2MemStat.npMemSize; + + npMatching2Handlers.clear(); + npMatching2Events.clear(); + npMatching2Inited = true; + return 0; +} + +static int sceNpMatching2Term() +{ + ERROR_LOG(SCENET, "UNIMPL %s() at %08x", __FUNCTION__, currentMIPS->pc); + npMatching2Inited = false; + npMatching2Handlers.clear(); + npMatching2Events.clear(); + + return 0; +} + +static int sceNpMatching2CreateContext(u32 communicationIdPtr, u32 passPhrasePtr, u32 ctxIdPtr, int unknown) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%08x[%s], %08x[%08x], %08x[%d], %d) at %08x", __FUNCTION__, communicationIdPtr, Memory::GetCharPointer(communicationIdPtr), passPhrasePtr, Memory::Read_U32(passPhrasePtr), ctxIdPtr, Memory::Read_U16(ctxIdPtr), unknown, currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(communicationIdPtr) || !Memory::IsValidAddress(passPhrasePtr) || !Memory::IsValidAddress(ctxIdPtr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); + + // FIXME: It seems Context are mapped to TitleID? may return 0x80550C05 or 0x80550C06 when finding an existing context + SceNpCommunicationId* titleid = (SceNpCommunicationId*)Memory::GetCharPointer(communicationIdPtr); + memcpy(npTitleId.data, titleid->data, sizeof(npTitleId)); + + SceNpCommunicationPassphrase* passph = (SceNpCommunicationPassphrase*)Memory::GetCharPointer(passPhrasePtr); + + SceNpId npid{}; + int retval = NpGetNpId(&npid); + if (retval < 0) + return hleLogError(SCENET, retval); + + INFO_LOG(SCENET, "%s - Title ID: %s", __FUNCTION__, titleid->data); + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, npid.handle.data); + std::string datahex; + DataToHexString(npid.opt, sizeof(npid.opt), &datahex); + INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str()); + datahex.clear(); + DataToHexString(10, 0, passph->data, sizeof(passph->data), &datahex); + INFO_LOG(SCENET, "%s - Passphrase: \n%s", __FUNCTION__, datahex.c_str()); + + // TODO: Allocate & zeroed a memory of 68 bytes where npId (36 bytes) is copied to offset 8, offset 44 = 0x00026808, offset 48 = 0 + + // Returning dummy Id, a 16-bit variable according to JPCSP + // FIXME: It seems ctxId need to be in the range of 1 to 7 to be valid ? + Memory::Write_U16(1, ctxIdPtr); + return 0; +} + +static int sceNpMatching2ContextStart(int ctxId) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d) at %08x", __FUNCTION__, ctxId, currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + //if (!npMatching2Ctx) + // return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND); //SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID + + //if (npMatching2Ctx.started) + // return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_ALREADY_STARTED); + + // TODO: use sceNpGetUserProfile and check server availability using sceNpService_76867C01 + //npMatching2Ctx.started = true; + Url url("http://static-resource.np.community.playstation.net/np/resource/psp-title/" + std::string(npTitleId.data) + "_00/matching/" + std::string(npTitleId.data) + "_00-matching.xml"); + http::Client client; + http::RequestProgress progress; + if (!client.Resolve(url.Host().c_str(), url.Port())) { + return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "HTTP failed to resolve %s", url.Resource().c_str()); + } + + client.SetDataTimeout(20.0); + if (client.Connect()) { + char requestHeaders[4096]; + snprintf(requestHeaders, sizeof(requestHeaders), + "User-Agent: PS3Community-agent/1.0.0 libhttp/1.0.0\r\n"); + + DEBUG_LOG(SCENET, "GET URI: %s", url.ToString().c_str()); + http::RequestParams req(url.Resource(), "*/*"); + int err = client.SendRequest("GET", req, requestHeaders, &progress); + if (err < 0) { + client.Disconnect(); + return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "HTTP GET Error = %d", err); + } + + net::Buffer readbuf; + std::vector responseHeaders; + int code = client.ReadResponseHeaders(&readbuf, responseHeaders, &progress); + if (code != 200) { + client.Disconnect(); + return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "HTTP Error Code = %d", code); + } + + net::Buffer output; + int res = client.ReadResponseEntity(&readbuf, responseHeaders, &output, &progress); + if (res != 0) { + WARN_LOG(SCENET, "Unable to read HTTP response entity: %d", res); + } + client.Disconnect(); + + std::string entity; + size_t readBytes = output.size(); + output.Take(readBytes, &entity); + + // TODO: Use XML Parser to get the Tag and it's attributes instead of searching for keywords on the string + std::string text; + size_t ofs = entity.find("titleid="); + if (ofs == std::string::npos) + return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "titleid not found"); + + ofs += 9; + size_t ofs2 = entity.find('"', ofs); + text = entity.substr(ofs, ofs2-ofs); + INFO_LOG(SCENET, "%s - Title ID: %s", __FUNCTION__, text.c_str()); + + int i = 1; + while (true) { + ofs = entity.find("", ++ofs2); + if (ofs == std::string::npos) + return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "agent host not found"); + + ofs2 = entity.find("pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + //if (!npMatching2Ctx) + // return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND); //SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID + + //if (!npMatching2Ctx.started) + // return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED); + + //TODO: Stop any in-progress HTTPClient communication used on sceNpMatching2ContextStart + //npMatching2Ctx.started = false; + + return 0; +} + +static int sceNpMatching2DestroyContext(int ctxId) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d) at %08x", __FUNCTION__, ctxId, currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + //if (!npMatching2Ctx) + // return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND); //SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID + + // Remove callback handler + int handlerID = ctxId - 1; + if (npMatching2Handlers.find(handlerID) != npMatching2Handlers.end()) { + npMatching2Handlers.erase(handlerID); + WARN_LOG(SCENET, "%s: Deleted handler %d", __FUNCTION__, handlerID); + } + else { + ERROR_LOG(SCENET, "%s: Invalid Context ID %d", __FUNCTION__, ctxId); + } + + return 0; +} + +static int sceNpMatching2GetMemoryStat(u32 memStatPtr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%08x) at %08x", __FUNCTION__, memStatPtr, currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(memStatPtr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT); + + Memory::WriteStruct(memStatPtr, &npMatching2MemStat); + + return 0; +} + +static int sceNpMatching2RegisterSignalingCallback(int ctxId, u32 callbackFunctionAddr, u32 callbackArgument) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %08x) at %08x", __FUNCTION__, ctxId, callbackFunctionAddr, callbackArgument, currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (ctxId <= 0) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID); + + int id = ctxId - 1; + if (callbackFunctionAddr != 0) { + bool foundHandler = false; + + struct NpMatching2Handler handler; + memset(&handler, 0, sizeof(handler)); + + handler.entryPoint = callbackFunctionAddr; + handler.argument = callbackArgument; + + for (std::map::iterator it = npMatching2Handlers.begin(); it != npMatching2Handlers.end(); it++) { + if (it->second.entryPoint == handler.entryPoint) { + foundHandler = true; + id = it->first; + break; + } + } + + if (!foundHandler && Memory::IsValidAddress(handler.entryPoint)) { + npMatching2Handlers[id] = handler; + WARN_LOG(SCENET, "%s - Added handler(%08x, %08x) : %d", __FUNCTION__, handler.entryPoint, handler.argument, id); + } + else { + ERROR_LOG(SCENET, "%s - Same handler(%08x, %08x) already exists", __FUNCTION__, handler.entryPoint, handler.argument); + } + + //u32 dataLength = 4097; + //notifyNpMatching2Handlers(retval, dataLength, handler.argument); + + // callback struct have 57 * u32? where [0]=0, [40]=flags, [55]=callbackFunc, and [56]=callbackArgs? + //hleEnqueueCall(callbackFunctionAddr, 7, (u32*)Memory::GetPointer(callbackArgument), nullptr); // 7 args? since the callback handler is trying to use t2 register + } + return 0; +} + +static int sceNpMatching2GetServerIdListLocal(int ctxId, u32 serverIdsPtr, int maxServerIds) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %d) at %08x", __FUNCTION__, ctxId, serverIdsPtr, maxServerIds, currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(serverIdsPtr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT); + + // Returning dummy Id, a 16-bit variable according to JPCSP + for (int i = 0; i < maxServerIds; i++) + Memory::Write_U16(1234+i, serverIdsPtr+(i*2)); + + return maxServerIds; // dummy value +} + +// Unknown1 = optParam, unknown2 = assignedReqId according to https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/sceNp2.cpp ? +static int sceNpMatching2GetServerInfo(int ctxId, u32 serverIdPtr, u32 unknown1Ptr, u32 unknown2Ptr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x[%d], %08x, %08x[%08x]) at %08x", __FUNCTION__, ctxId, serverIdPtr, Memory::Read_U16(serverIdPtr), unknown1Ptr, unknown2Ptr, Memory::Read_U32(unknown2Ptr), currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(serverIdPtr) || !Memory::IsValidAddress(unknown2Ptr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); // Should be SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT ? + + // Server ID is a 16-bit variable according to JPCSP + int serverId = Memory::Read_U16(serverIdPtr); + + if (serverId == 0) + return hleLogError(SCENET, 0x80550CBF); // Should be SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID ? + + // Output to unknown1(infoBuffer)? and unknown2(infoLength or flags)? + // Patapon 3 is using serverId at 09FFF2F4, unknown1 at 09FFF2E4, unknown2 at 09FFF2E0, which mean unknown1's can only fit upto 16-bytes + // Patapon 3 seems to be copying data from unknown1 with a fixed size of 20-bytes? + // input unknown1 struct: based on Fat Princess (US) + // 0000 32-bit function address (callback?) 0x08A08B40 + // 0004 32-bit pointer to a struct? (callback args?) 0x09888158 (contains 32-bit (-1) + 32-bit (1) + 16-bit ctxId(0001) + 32bit 0x06913801? + 16-bit serverId(1234), so on), probably only 2x 32-bit struct? + // 0008 32-bit set to 0 + // 000a 16-bit set to 0 + // + u32 cbFunc = Memory::Read_U32(unknown1Ptr); + u32 cbArg = Memory::Read_U32(unknown1Ptr + 0x04); + + // Notify callback handler + if (Memory::IsValidAddress(cbFunc)) { + // The cbFunc seems to be storing s0~s4(s0 pointing to 0x0996DD58 containing data similar to 0x09888158 above on the 1st 2x 32-bit data, s1 seems to be ctxId, s2~s4=0xdeadbeef) into stack and use a0~t1 (6 args?): + // Arg1(a0) & Arg3(a2) are being masked with 0xffff (16-bit id?) + // This callback tried to load data from address 0x08BD4860+8 (not part of arg? which being set using content of unknown2 not long after returning from sceNpMatching2GetServerInfo, so we may need to give some delay before calling this callback) + // and comparing it with Arg2(a1), repeated by increasing the address 0x08BD4860 by 288 bytes on each loop for 64 times or until it found a matching one. + // When a match is found the callback will process the address further, otherwise exit the callback. + // Matching address struct: (zeroed before calling sceNpMatching2GetServerInfo? and set after returning from sceNpMatching2GetServerInfo?) + // 0000 32-bit func address (another callback?) 0x08A07EF4 + // 0008 32-bit value from unknown2 content, being set not long after returning from sceNpMatching2GetServerInfo + // 000c 32-bit unknown + // 0010 8-bit status to indicate not updated from callback yet? initially 0, set to 1 not long after returning from sceNpMatching2GetServerInfo (along with unknown2 content) + // + // There args are supposed to be constructed in the stack and the data need to be available even after returning from this function, so these args + optional data probably copied to somewhere + NpMatching2Args args = {}; + args.data[0] = PSP_NP_MATCHING2_EVENT_0001; + args.data[1] = PSP_NP_MATCHING2_STATE_1001; // or size of data? + args.data[2] = serverIdPtr; // serverId or was it pointing to optional data at last arg (ie. args[10] where serverId is stored)? + args.data[3] = unknown1Ptr; + //args.data[4] = a boolean(0/1) related to a u8 value from the struct at args[9] (value XOR 0x04 == 0) + args.data[5] = unknown2Ptr; + args.data[6] = 0; + //args.data[8] = 0 or a pointer to a struct related to context? + //args.data[9] = 0 or a pointer to a struct related to context and matched serverId? + //args.data[10] = serverId; + + notifyNpMatching2Handlers(args, ctxId, serverId, 0, 0, 0, 0, 0, 1); + + Memory::Write_U32(args.data[1], unknown2Ptr); // server status or flags? + } + + // After returning, Fat Princess will loop for 64 times (increasing the address by 288 bytes on each loop) or until found a zero status byte (0x08BD4860 + 0x10), looking for empty/available entry to set? + return 0; +} + +static int sceNpMatching2LeaveRoom(int ctxId, u32 reqParamPtr, u32 optParamPtr, u32 assignedReqIdPtr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %08x, %08x[%08x]) at %08x", __FUNCTION__, ctxId, reqParamPtr, optParamPtr, assignedReqIdPtr, Memory::Read_U32(assignedReqIdPtr), currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(reqParamPtr) || !Memory::IsValidAddress(assignedReqIdPtr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); // Should be SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT ? + + u32 cbFunc = Memory::Read_U32(reqParamPtr); + u32 cbArg = Memory::Read_U32(reqParamPtr + 0x04); + + // Notify callback handler + if (Memory::IsValidAddress(cbFunc)) { + // There args are supposed to be constructed in the stack and the data need to be available even after returning from this function, so these args + optional data probably copied to somewhere + NpMatching2Args args = {}; + args.data[0] = PSP_NP_MATCHING2_EVENT_0103; + args.data[1] = PSP_NP_MATCHING2_STATE_3202; + //args.data[2] = pointer to arg[8], where the 1st 20 bytes copied from (reqParamPtr+0x08), the rest of the struct are zeroed + args.data[3] = optParamPtr; + args.data[4] = 0; + args.data[5] = assignedReqIdPtr; + args.data[6] = 0; + //args.data[8] = an initially zeroed struct of 536 bytes where the 1st 20 bytes were taken from reqParam offset 0x08 + + notifyNpMatching2Handlers(args, ctxId, 0, cbFunc, cbArg, 0, 0, 0, 0x0c); + + Memory::Write_U32(args.data[1], assignedReqIdPtr); + } + + // After returning, Fat Princess will loop for 64 times (increasing the address by 288 bytes on each loop) or until found a zero status byte (0x08BD4860 + 0x10), looking for empty/available entry to set? + return 0; +} + +static int sceNpMatching2JoinRoom(int ctxId, u32 reqParamPtr, u32 optParamPtr, u32 unknown1, u32 unknown2, u32 assignedReqIdPtr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %08x, %08x[%08x]) at %08x", __FUNCTION__, ctxId, reqParamPtr, optParamPtr, assignedReqIdPtr, Memory::Read_U32(assignedReqIdPtr), currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(reqParamPtr) || !Memory::IsValidAddress(assignedReqIdPtr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); // Should be SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT ? + + // Server ID is a 16-bit variable according to JPCSP + int serverId = Memory::Read_U16(reqParamPtr + 0x06); + + if (serverId == 0) + return hleLogError(SCENET, 0x80550CBF); // Should be SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID ? + + u32 cbFunc = Memory::Read_U32(reqParamPtr); + u32 cbArg = Memory::Read_U32(reqParamPtr + 0x04); + + // Notify callback handler + if (Memory::IsValidAddress(cbFunc)) { + // There args are supposed to be constructed in the stack and the data need to be available even after returning from this function, so these args + optional data probably copied to somewhere + NpMatching2Args args = {}; + args.data[0] = PSP_NP_MATCHING2_EVENT_0102; + args.data[1] = PSP_NP_MATCHING2_STATE_1209; + //args.data[2] = pointer to arg[8] (optional data?) + args.data[3] = optParamPtr; + args.data[4] = 0; + args.data[5] = assignedReqIdPtr; + args.data[6] = 0; + // Followed by optional data? + args.data[8] = reqParamPtr; // an initially zeroed struct of 1224 bytes, where the 1st 32bit is set to reqParamPtr + args.data[9] = unknown1; + args.data[10] = unknown2; + + notifyNpMatching2Handlers(args, ctxId, serverId, 0, 0, 0, 0, 1, 0x0a); + + Memory::Write_U32(args.data[1], assignedReqIdPtr); + } + + // After returning, Fat Princess will loop for 64 times (increasing the address by 288 bytes on each loop) or until found a zero status byte (0x08BD4860 + 0x10), looking for empty/available entry to set? + return 0; +} + +static int sceNpMatching2SearchRoom(int ctxId, u32 reqParamPtr, u32 optParamPtr, u32 assignedReqIdPtr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %08x, %08x[%08x]) at %08x", __FUNCTION__, ctxId, reqParamPtr, optParamPtr, assignedReqIdPtr, Memory::Read_U32(assignedReqIdPtr), currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(reqParamPtr) || !Memory::IsValidAddress(assignedReqIdPtr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); // Should be SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT ? + + u32 cbFunc = Memory::Read_U32(reqParamPtr); + u32 cbArg = Memory::Read_U32(reqParamPtr + 0x04); + + // Notify callback handler + if (Memory::IsValidAddress(cbFunc)) { + // There args are supposed to be constructed in the stack and the data need to be available even after returning from this function, so these args + optional data probably copied to somewhere + NpMatching2Args args = {}; + // TODO: Set the correct callback args + + Memory::Write_U32(args.data[1], assignedReqIdPtr); // server status or flags? + } + + return 0; +} + +static int sceNpMatching2SendRoomChatMessage(int ctxId, u32 reqParamPtr, u32 optParamPtr, u32 assignedReqIdPtr) +{ + ERROR_LOG(SCENET, "UNIMPL %s(%d, %08x, %08x, %08x[%08x]) at %08x", __FUNCTION__, ctxId, reqParamPtr, optParamPtr, assignedReqIdPtr, Memory::Read_U32(assignedReqIdPtr), currentMIPS->pc); + if (!npMatching2Inited) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); + + if (!Memory::IsValidAddress(reqParamPtr) || !Memory::IsValidAddress(assignedReqIdPtr)) + return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); // Should be SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT ? + + u32 cbFunc = Memory::Read_U32(reqParamPtr); + u32 cbArg = Memory::Read_U32(reqParamPtr + 0x04); + + // Notify callback handler + if (Memory::IsValidAddress(cbFunc)) { + // There args are supposed to be constructed in the stack and the data need to be available even after returning from this function, so these args + optional data probably copied to somewhere + NpMatching2Args args = {}; + args.data[0] = PSP_NP_MATCHING2_EVENT_0107; + args.data[1] = PSP_NP_MATCHING2_STATE_3208; + //args.data[2] = pointer to arg[8] + args.data[3] = optParamPtr; + args.data[4] = 0; + args.data[5] = assignedReqIdPtr; + args.data[6] = 0; + //args.data[8] = reqParamPtr; + + notifyNpMatching2Handlers(args, ctxId, 0, cbFunc, cbArg, 0, 0, 0, 0x10); + + Memory::Write_U32(args.data[1], assignedReqIdPtr); // server status or flags? + } + + // After returning, Fat Princess will loop for 64 times (increasing the address by 288 bytes on each loop) or until found a zero status byte (0x08BD4860 + 0x10), looking for empty/available entry to set? + return 0; +} + +const HLEFunction sceNpMatching2[] = { + {0x2E61F6E1, &WrapI_IIII, "sceNpMatching2Init", 'i', "iiii" }, + {0x8BF37D8C, &WrapI_V, "sceNpMatching2Term", 'i', "" }, + {0x5030CC53, &WrapI_UUUI, "sceNpMatching2CreateContext", 'i', "xxxi" }, + {0x190FF903, &WrapI_I, "sceNpMatching2ContextStart", 'i', "i" }, + {0x2B3892FC, &WrapI_I, "sceNpMatching2ContextStop", 'i', "i" }, + {0x3DE70241, &WrapI_I, "sceNpMatching2DestroyContext", 'i', "i" }, + {0x22F38DAF, &WrapI_U, "sceNpMatching2GetMemoryStat", 'i', "x" }, + {0xA3C298D1, &WrapI_IUU, "sceNpMatching2RegisterSignalingCallback", 'i', "ixx" }, + {0xF47342FC, &WrapI_IUI, "sceNpMatching2GetServerIdListLocal", 'i', "ixi" }, + {0x4EE3A8EC, &WrapI_IUUU, "sceNpMatching2GetServerInfo", 'i', "ixxx" }, + {0xC870535A, &WrapI_IUUU, "sceNpMatching2LeaveRoom", 'i', "ixxx" }, + {0xAAD0946A, &WrapI_IUUUUU, "sceNpMatching2JoinRoom", 'i', "ixxxxx" }, + {0x81C13E6D, &WrapI_IUUU, "sceNpMatching2SearchRoom", 'i', "ixxx" }, + {0x55F7837F, &WrapI_IUUU, "sceNpMatching2SendRoomChatMessage", 'i', "ixxx" }, +}; + +void Register_sceNpMatching2() +{ + RegisterModule("sceNpMatching2", ARRAY_SIZE(sceNpMatching2), sceNpMatching2); +} diff --git a/Core/HLE/sceNp2.h b/Core/HLE/sceNp2.h new file mode 100644 index 0000000000..8b1b6c1c56 --- /dev/null +++ b/Core/HLE/sceNp2.h @@ -0,0 +1,254 @@ +// Copyright (c) 2022- 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 + + +#pragma pack(push,1) + +// Based on https://gist.githubusercontent.com/raw/4140449/PS%20Vita (Might be slightly different with PSP?) +#define SCE_NP_MATCHING2_ERROR_OUT_OF_MEMORY 0x80550c01 +#define SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED 0x80550c02 +#define SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED 0x80550c03 +#define SCE_NP_MATCHING2_ERROR_CONTEXT_MAX 0x80550c04 // might be "Invalid Argument" on PSP? +#define SCE_NP_MATCHING2_ERROR_CONTEXT_ALREADY_EXISTS 0x80550c05 // might be "Context Max/Context Id higher than 7" on PSP? +#define SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND 0x80550c06 +#define SCE_NP_MATCHING2_ERROR_CONTEXT_ALREADY_STARTED 0x80550c07 +#define SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED 0x80550c08 +#define SCE_NP_MATCHING2_ERROR_SERVER_NOT_FOUND 0x80550c09 +#define SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT 0x80550c0a +#define SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID 0x80550c0b +#define SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID 0x80550c0c +#define SCE_NP_MATCHING2_ERROR_INVALID_LOBBY_ID 0x80550c0e +#define SCE_NP_MATCHING2_ERROR_INVALID_ROOM_ID 0x80550c0f +#define SCE_NP_MATCHING2_ERROR_INVALID_MEMBER_ID 0x80550c10 +#define SCE_NP_MATCHING2_ERROR_INVALID_ATTRIBUTE_ID 0x80550c11 +#define SCE_NP_MATCHING2_ERROR_INVALID_CASTTYPE 0x80550c12 +#define SCE_NP_MATCHING2_ERROR_INVALID_SORT_METHOD 0x80550c13 +#define SCE_NP_MATCHING2_ERROR_INVALID_MAX_SLOT 0x80550c14 +#define SCE_NP_MATCHING2_ERROR_INVALID_OPT_SIZE 0x80550c15 +#define SCE_NP_MATCHING2_ERROR_INVALID_MATCHING_SPACE 0x80550c16 +#define SCE_NP_MATCHING2_ERROR_INVALID_BLOCK_KICK_FLAG 0x80550c18 +#define SCE_NP_MATCHING2_ERROR_INVALID_MESSAGE_TARGET 0x80550c19 +#define SCE_NP_MATCHING2_ERROR_RANGE_FILTER_MAX 0x80550c1a +#define SCE_NP_MATCHING2_ERROR_INVALID_ALIGNMENT 0x80550c1e +#define SCE_NP_MATCHING2_ERROR_CONNECTION_CLOSED_BY_SERVER 0x80550c22 +#define SCE_NP_MATCHING2_ERROR_SSL_VERIFY_FAILED 0x80550c23 +#define SCE_NP_MATCHING2_ERROR_SSL_HANDSHAKE 0x80550c24 +#define SCE_NP_MATCHING2_ERROR_SSL_SEND 0x80550c25 +#define SCE_NP_MATCHING2_ERROR_SSL_RECV 0x80550c26 +#define SCE_NP_MATCHING2_ERROR_JOINED_SESSION_MAX 0x80550c27 +#define SCE_NP_MATCHING2_ERROR_ALREADY_JOINED 0x80550c28 +#define SCE_NP_MATCHING2_ERROR_INVALID_SESSION_TYPE 0x80550c29 +#define SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT 0x80550c2b +#define SCE_NP_MATCHING2_ERROR_BUSY 0x80550c2c +#define SCE_NP_MATCHING2_ERROR_SERVER_NOT_AVAILABLE 0x80550c2d +#define SCE_NP_MATCHING2_ERROR_NOT_ALLOWED 0x80550c2e +#define SCE_NP_MATCHING2_ERROR_ABORTED 0x80550c2f +#define SCE_NP_MATCHING2_ERROR_REQUEST_NOT_FOUND 0x80550c30 +#define SCE_NP_MATCHING2_ERROR_SESSION_DESTROYED 0x80550c31 +#define SCE_NP_MATCHING2_ERROR_CONTEXT_STOPPED 0x80550c32 +#define SCE_NP_MATCHING2_ERROR_INVALID_REQUEST_PARAMETER 0x80550c33 +#define SCE_NP_MATCHING2_ERROR_NOT_NP_SIGN_IN 0x80550c34 +#define SCE_NP_MATCHING2_ERROR_ROOM_NOT_FOUND 0x80550c35 +#define SCE_NP_MATCHING2_ERROR_ROOM_MEMBER_NOT_FOUND 0x80550c36 +#define SCE_NP_MATCHING2_ERROR_LOBBY_NOT_FOUND 0x80550c37 +#define SCE_NP_MATCHING2_ERROR_LOBBY_MEMBER_NOT_FOUND 0x80550c38 +#define SCE_NP_MATCHING2_ERROR_KEEPALIVE_TIMEOUT 0x80550c3a +#define SCE_NP_MATCHING2_ERROR_TIMEOUT_TOO_SHORT 0x80550c3b +#define SCE_NP_MATCHING2_ERROR_TIMEDOUT 0x80550c3c +#define SCE_NP_MATCHING2_ERROR_INVALID_SLOTGROUP 0x80550c3d +#define SCE_NP_MATCHING2_ERROR_INVALID_ATTRIBUTE_SIZE 0x80550c3e +#define SCE_NP_MATCHING2_ERROR_CANNOT_ABORT 0x80550c3f +#define SCE_NP_MATCHING2_ERROR_SESSION_NOT_FOUND 0x80550c40 + +#define SCE_NP_MATCHING2_SERVER_ERROR_BAD_REQUEST 0x80550d01 +#define SCE_NP_MATCHING2_SERVER_ERROR_SERVICE_UNAVAILABLE 0x80550d02 +#define SCE_NP_MATCHING2_SERVER_ERROR_BUSY 0x80550d03 +#define SCE_NP_MATCHING2_SERVER_ERROR_END_OF_SERVICE 0x80550d04 +#define SCE_NP_MATCHING2_SERVER_ERROR_INTERNAL_SERVER_ERROR 0x80550d05 +#define SCE_NP_MATCHING2_SERVER_ERROR_PLAYER_BANNED 0x80550d06 +#define SCE_NP_MATCHING2_SERVER_ERROR_FORBIDDEN 0x80550d07 +#define SCE_NP_MATCHING2_SERVER_ERROR_BLOCKED 0x80550d08 +#define SCE_NP_MATCHING2_SERVER_ERROR_UNSUPPORTED_NP_ENV 0x80550d09 +#define SCE_NP_MATCHING2_SERVER_ERROR_INVALID_TICKET 0x80550d0a +#define SCE_NP_MATCHING2_SERVER_ERROR_INVALID_SIGNATURE 0x80550d0b +#define SCE_NP_MATCHING2_SERVER_ERROR_EXPIRED_TICKET 0x80550d0c +#define SCE_NP_MATCHING2_SERVER_ERROR_ENTITLEMENT_REQUIRED 0x80550d0d +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_CONTEXT 0x80550d0e +#define SCE_NP_MATCHING2_SERVER_ERROR_CLOSED 0x80550d0f +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_TITLE 0x80550d10 +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_WORLD 0x80550d11 +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_LOBBY 0x80550d12 +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM 0x80550d13 +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_LOBBY_INSTANCE 0x80550d14 +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM_INSTANCE 0x80550d15 +#define SCE_NP_MATCHING2_SERVER_ERROR_PASSWORD_MISMATCH 0x80550d17 +#define SCE_NP_MATCHING2_SERVER_ERROR_LOBBY_FULL 0x80550d18 +#define SCE_NP_MATCHING2_SERVER_ERROR_ROOM_FULL 0x80550d19 +#define SCE_NP_MATCHING2_SERVER_ERROR_GROUP_FULL 0x80550d1b +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_USER 0x80550d1c +#define SCE_NP_MATCHING2_SERVER_ERROR_TITLE_PASSPHRASE_MISMATCH 0x80550d1e +#define SCE_NP_MATCHING2_SERVER_ERROR_CONSOLE_BANNED 0x80550d28 +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_ROOMGROUP 0x80550d29 +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_GROUP 0x80550d2a +#define SCE_NP_MATCHING2_SERVER_ERROR_NO_PASSWORD 0x80550d2b +#define SCE_NP_MATCHING2_SERVER_ERROR_INVALID_GROUP_SLOT_NUM 0x80550d2c +#define SCE_NP_MATCHING2_SERVER_ERROR_INVALID_PASSWORD_SLOT_MASK 0x80550d2d +#define SCE_NP_MATCHING2_SERVER_ERROR_DUPLICATE_GROUP_LABEL 0x80550d2e +#define SCE_NP_MATCHING2_SERVER_ERROR_REQUEST_OVERFLOW 0x80550d2f +#define SCE_NP_MATCHING2_SERVER_ERROR_ALREADY_JOINED 0x80550d30 +#define SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH 0x80550d31 +#define SCE_NP_MATCHING2_SERVER_ERROR_ROOM_INCONSISTENCY 0x80550d32 + +#define SCE_NP_MATCHING2_SIGNALING_ERROR_NOT_INITIALIZED 0x80550e01 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_ALREADY_INITIALIZED 0x80550e02 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_OUT_OF_MEMORY 0x80550e03 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_CTXID_NOT_AVAILABLE 0x80550e04 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_CTX_NOT_FOUND 0x80550e05 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_REQID_NOT_AVAILABLE 0x80550e06 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_REQ_NOT_FOUND 0x80550e07 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_PARSER_CREATE_FAILED 0x80550e08 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_PARSER_FAILED 0x80550e09 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_INVALID_NAMESPACE 0x80550e0a +#define SCE_NP_MATCHING2_SIGNALING_ERROR_NETINFO_NOT_AVAILABLE 0x80550e0b +#define SCE_NP_MATCHING2_SIGNALING_ERROR_PEER_NOT_RESPONDING 0x80550e0c +#define SCE_NP_MATCHING2_SIGNALING_ERROR_CONNID_NOT_AVAILABLE 0x80550e0d +#define SCE_NP_MATCHING2_SIGNALING_ERROR_CONN_NOT_FOUND 0x80550e0e +#define SCE_NP_MATCHING2_SIGNALING_ERROR_PEER_UNREACHABLE 0x80550e0f +#define SCE_NP_MATCHING2_SIGNALING_ERROR_TERMINATED_BY_PEER 0x80550e10 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_TIMEOUT 0x80550e11 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_CTX_MAX 0x80550e12 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_RESULT_NOT_FOUND 0x80550e13 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_CONN_IN_PROGRESS 0x80550e14 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_INVALID_ARGUMENT 0x80550e15 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_OWN_NP_ID 0x80550e16 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_TOO_MANY_CONN 0x80550e17 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_TERMINATED_BY_MYSELF 0x80550e18 +#define SCE_NP_MATCHING2_SIGNALING_ERROR_MATCHING2_PEER_NOT_FOUND 0x80550e19 + +// Based on https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/sceNp2.h (Just as reference, might be slightly different than PSP) +// Event of request functions +enum PS3Matching2RequestEvent +{ + SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo = 0x0001, + SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList = 0x0002, + SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomMemberDataExternalList = 0x0003, + SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal = 0x0004, + SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList = 0x0005, + SCE_NP_MATCHING2_REQUEST_EVENT_GetLobbyInfoList = 0x0006, + SCE_NP_MATCHING2_REQUEST_EVENT_SetUserInfo = 0x0007, + SCE_NP_MATCHING2_REQUEST_EVENT_GetUserInfoList = 0x0008, + SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext = 0x0009, + SCE_NP_MATCHING2_REQUEST_EVENT_DeleteServerContext = 0x000a, + SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom = 0x0101, + SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom = 0x0102, + SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom = 0x0103, + SCE_NP_MATCHING2_REQUEST_EVENT_GrantRoomOwner = 0x0104, + SCE_NP_MATCHING2_REQUEST_EVENT_KickoutRoomMember = 0x0105, + SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom = 0x0106, + SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomChatMessage = 0x0107, + SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomMessage = 0x0108, + SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal = 0x0109, + SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal = 0x010a, + SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomMemberDataInternal = 0x010b, + SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomMemberDataInternal = 0x010c, + SCE_NP_MATCHING2_REQUEST_EVENT_SetSignalingOptParam = 0x010d, + SCE_NP_MATCHING2_REQUEST_EVENT_JoinLobby = 0x0201, + SCE_NP_MATCHING2_REQUEST_EVENT_LeaveLobby = 0x0202, + SCE_NP_MATCHING2_REQUEST_EVENT_SendLobbyChatMessage = 0x0203, + SCE_NP_MATCHING2_REQUEST_EVENT_SendLobbyInvitation = 0x0204, + SCE_NP_MATCHING2_REQUEST_EVENT_SetLobbyMemberDataInternal = 0x0205, + SCE_NP_MATCHING2_REQUEST_EVENT_GetLobbyMemberDataInternal = 0x0206, + SCE_NP_MATCHING2_REQUEST_EVENT_GetLobbyMemberDataInternalList = 0x0207, + SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo = 0x0e01, +}; + +// Based on decompiled np_matching2.prx, commented with syscalls where the event id is being used +#define PSP_NP_MATCHING2_EVENT_0001 0x0001 // GetServerInfo +#define PSP_NP_MATCHING2_EVENT_0002 0x0002 // GetWorldInfoList +#define PSP_NP_MATCHING2_EVENT_0003 0x0003 // SetUserInfo +#define PSP_NP_MATCHING2_EVENT_0004 0x0004 // GetUserInfoList +#define PSP_NP_MATCHING2_EVENT_0005 0x0005 // GetRoomMemberDataExternalList +#define PSP_NP_MATCHING2_EVENT_0006 0x0006 // SetRoomDataExternal +#define PSP_NP_MATCHING2_EVENT_0007 0x0007 // GetRoomDataExternalList + +#define PSP_NP_MATCHING2_EVENT_0101 0x0101 // CreateJoinRoom +#define PSP_NP_MATCHING2_EVENT_0102 0x0102 // JoinRoom +#define PSP_NP_MATCHING2_EVENT_0103 0x0103 // LeaveRoom +#define PSP_NP_MATCHING2_EVENT_0104 0x0104 // GrantRoomOwner +#define PSP_NP_MATCHING2_EVENT_0105 0x0105 // KickoutRoomMember +#define PSP_NP_MATCHING2_EVENT_0106 0x0106 // SearchRoom +#define PSP_NP_MATCHING2_EVENT_0107 0x0107 // SendRoomChatMessage +#define PSP_NP_MATCHING2_EVENT_0108 0x0108 // SendRoomMessage, also used on various places (internal function) +#define PSP_NP_MATCHING2_EVENT_0109 0x0109 // SetRoomDataInternal +#define PSP_NP_MATCHING2_EVENT_010A 0x010A // GetRoomDataInternal +#define PSP_NP_MATCHING2_EVENT_010B 0x010B // SetRoomMemberDataInternal +#define PSP_NP_MATCHING2_EVENT_010C 0x010C // GetRoomMemberDataInternal +#define PSP_NP_MATCHING2_EVENT_010D 0x010D // GetRoomMemberDataInternalList +#define PSP_NP_MATCHING2_EVENT_010E 0x010E // SetSignalingOptParam + +#define PSP_NP_MATCHING2_EVENT_A102 0xA102 // Used on various places (internal function) + +// Either this is an ID, state/status, flags, or might be size of data? +#define PSP_NP_MATCHING2_STATE_1001 0x1001 +#define PSP_NP_MATCHING2_STATE_1006 0x1006 +#define PSP_NP_MATCHING2_STATE_1007 0x1007 +#define PSP_NP_MATCHING2_STATE_1008 0x1008 + +#define PSP_NP_MATCHING2_STATE_1200 0x1200 +#define PSP_NP_MATCHING2_STATE_1206 0x1206 +#define PSP_NP_MATCHING2_STATE_1207 0x1207 +#define PSP_NP_MATCHING2_STATE_1208 0x1208 +#define PSP_NP_MATCHING2_STATE_1209 0x1209 +#define PSP_NP_MATCHING2_STATE_120B 0x120B +#define PSP_NP_MATCHING2_STATE_120C 0x120C + +#define PSP_NP_MATCHING2_STATE_3202 0x3202 +#define PSP_NP_MATCHING2_STATE_3203 0x3203 +#define PSP_NP_MATCHING2_STATE_3204 0x3204 +#define PSP_NP_MATCHING2_STATE_3205 0x3205 +#define PSP_NP_MATCHING2_STATE_3206 0x3206 +#define PSP_NP_MATCHING2_STATE_3207 0x3207 +#define PSP_NP_MATCHING2_STATE_3208 0x3208 +#define PSP_NP_MATCHING2_STATE_320A 0x320A +#define PSP_NP_MATCHING2_STATE_3210 0x3210 +#define PSP_NP_MATCHING2_STATE_3211 0x3211 + +#define PSP_NP_MATCHING2_MAX_CONTEXTID 7; + +struct NpMatching2Handler { + u32 entryPoint; + u32 argument; +}; + +// Arg1 and Arg2 seems to be a pair and predefined: 0x0001 with 0x1001, 0x0002 with 0x1008, 0x0003 with 0x1006, 0x0004 with 0x1007, +// 0x0005 with 0x1206, 0x0006 with 0x1207, 0x0007 with 0x1208, 0x0101 with 0x1209, 0x0102 with 0x1209, 0x0103 with 0x3202, +// 0x0104 with 0x3210, 0x0105 with 0x3211, 0x0106 with 2 possibilities (0x1200 and 0x120c), 0x0107 with 0x3208, 0x0108 with 0x320a, +// 0x0109 with 0x3204, 0x010a with 0x3205, 0x010b with 0x3206, 0x010c with 0x3207, 0x010d with 0x3203, 0x010e with 0x3204, +// 0xa102 with 0x120b. +// Arg5 seems to be boolean (0/1), mostly 0, conditional when Arg1=0x0001 +// Arg7 seems to be integer/state? (0..2), mostly 0, conditional when Arg1=0x0108 (0 on SendRoomMessage, 2 on others), 1 when Arg1=0xa102 +struct NpMatching2Args { + u32_le data[11]; // 7 elements (excluding optional data)? or may be 11 elements (including optional data)? + // May be followed by optional data? since these Args usually created on the stack +}; + +#pragma pack(pop) + + +void Register_sceNpMatching2(); diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index c5bdde62f8..e6a5124600 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -47,6 +47,7 @@ #include "Core/Dialog/PSPOskDialog.h" #include "Core/Dialog/PSPGamedataInstallDialog.h" #include "Core/Dialog/PSPNetconfDialog.h" +#include "Core/Dialog/PSPNpSigninDialog.h" #include "Core/Dialog/PSPScreenshotDialog.h" #define PSP_AV_MODULE_AVCODEC 0 @@ -132,6 +133,7 @@ static PSPOskDialog *oskDialog; static PSPNetconfDialog *netDialog; static PSPScreenshotDialog *screenshotDialog; static PSPGamedataInstallDialog *gamedataInstallDialog; +static PSPNpSigninDialog *npSigninDialog; static int oldStatus = -1; static std::map currentlyLoadedModules; @@ -195,6 +197,8 @@ static PSPDialog *CurrentDialog(UtilityDialogType type) { break; case UtilityDialogType::GAMEDATAINSTALL: return gamedataInstallDialog; + case UtilityDialogType::NPSIGNIN: + return npSigninDialog; } return nullptr; } @@ -212,6 +216,7 @@ void __UtilityInit() { netDialog = new PSPNetconfDialog(UtilityDialogType::NET); screenshotDialog = new PSPScreenshotDialog(UtilityDialogType::SCREENSHOT); gamedataInstallDialog = new PSPGamedataInstallDialog(UtilityDialogType::GAMEDATAINSTALL); + npSigninDialog = new PSPNpSigninDialog(UtilityDialogType::NPSIGNIN); currentDialogType = UtilityDialogType::NONE; DeactivateDialog(); @@ -221,7 +226,7 @@ void __UtilityInit() { } void __UtilityDoState(PointerWrap &p) { - auto s = p.Section("sceUtility", 1, 5); + auto s = p.Section("sceUtility", 1, 6); if (!s) { return; } @@ -267,6 +272,10 @@ void __UtilityDoState(PointerWrap &p) { if (s >= 5) Do(p, accessThreadFinished); + if (s >= 6) { + npSigninDialog->DoState(p); + } + if (!hasAccessThread && accessThread) { accessThread->Forget(); delete accessThread; @@ -282,6 +291,7 @@ void __UtilityShutdown() { netDialog->Shutdown(true); screenshotDialog->Shutdown(true); gamedataInstallDialog->Shutdown(true); + npSigninDialog->Shutdown(true); if (accessThread) { delete accessThread; @@ -296,6 +306,7 @@ void __UtilityShutdown() { delete netDialog; delete screenshotDialog; delete gamedataInstallDialog; + delete npSigninDialog; } void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int priority) { @@ -884,15 +895,43 @@ static u32 sceUtilityUnloadNetModule(u32 module) } static int sceUtilityNpSigninInitStart(u32 paramsPtr) { - return hleLogError(SCEUTILITY, 0, "not implemented"); + if (currentDialogActive && currentDialogType != UtilityDialogType::NPSIGNIN) { + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); + } + + ActivateDialog(UtilityDialogType::NPSIGNIN); + return hleLogSuccessInfoI(SCEUTILITY, npSigninDialog->Init(paramsPtr)); +} + +static int sceUtilityNpSigninShutdownStart() { + if (currentDialogType != UtilityDialogType::NPSIGNIN) { + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); + } + + DeactivateDialog(); + return hleLogSuccessI(SCEUTILITY, npSigninDialog->Shutdown()); } static int sceUtilityNpSigninUpdate(int animSpeed) { - return hleLogError(SCEUTILITY, 0, "not implemented"); + if (currentDialogType != UtilityDialogType::NPSIGNIN) { + return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); + } + + return hleLogSuccessI(SCEUTILITY, npSigninDialog->Update(animSpeed)); } static int sceUtilityNpSigninGetStatus() { - return hleLogError(SCEUTILITY, 0, "not implemented"); + if (currentDialogType != UtilityDialogType::NPSIGNIN) { + return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type"); + } + + int status = npSigninDialog->GetStatus(); + CleanupDialogThreads(); + if (oldStatus != status) { + oldStatus = status; + return hleLogSuccessI(SCEUTILITY, status); + } + return hleLogSuccessVerboseI(SCEUTILITY, status); } static void sceUtilityInstallInitStart(u32 unknown) @@ -1066,7 +1105,7 @@ const HLEFunction sceUtility[] = {0X180F7B62, &WrapI_V, "sceUtilityGamedataInstallAbort", 'i', "" }, {0X16D02AF0, &WrapI_U, "sceUtilityNpSigninInitStart", 'i', "x" }, - {0XE19C97D6, nullptr, "sceUtilityNpSigninShutdownStart", 'i', "" }, + {0XE19C97D6, &WrapI_V, "sceUtilityNpSigninShutdownStart", 'i', "" }, {0XF3FBC572, &WrapI_I, "sceUtilityNpSigninUpdate", 'i', "i" }, {0X86ABDB1B, &WrapI_V, "sceUtilityNpSigninGetStatus", 'i', "" }, diff --git a/Core/HLE/sceUtility.h b/Core/HLE/sceUtility.h index d7358a9ae5..9943fc8399 100644 --- a/Core/HLE/sceUtility.h +++ b/Core/HLE/sceUtility.h @@ -85,6 +85,7 @@ enum class UtilityDialogType { SCREENSHOT, GAMESHARING, GAMEDATAINSTALL, + NPSIGNIN, }; void __UtilityInit(); diff --git a/GPU/Common/Draw2D.cpp b/GPU/Common/Draw2D.cpp new file mode 100644 index 0000000000..a9ba00ab4f --- /dev/null +++ b/GPU/Common/Draw2D.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2014- 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 "Common/GPU/Shader.h" +#include "Common/GPU/ShaderWriter.h" +#include "Common/GPU/thin3d.h" +#include "Core/Config.h" +#include "Core/ConfigValues.h" +#include "Core/Reporting.h" +#include "GPU/Common/Draw2D.h" +#include "GPU/Common/DrawEngineCommon.h" +#include "GPU/Common/FramebufferManagerCommon.h" +#include "GPU/Common/TextureCacheCommon.h" + +static const InputDef inputs[2] = { + { "vec2", "a_position", Draw::SEM_POSITION }, + { "vec2", "a_texcoord", Draw::SEM_TEXCOORD0 }, +}; + +static const VaryingDef varyings[1] = { + { "vec2", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" }, +}; + +void GenerateDraw2DFs(char *buffer, const ShaderLanguageDesc &lang, const Draw::Bugs &bugs) { + ShaderWriter writer(buffer, lang, ShaderStage::Fragment, nullptr, 0); + writer.DeclareSampler2D("samp", 0); + writer.DeclareTexture2D("tex", 0); + writer.BeginFSMain(Slice::empty(), varyings); + writer.C(" vec4 outColor = ").SampleTexture2D("tex", "samp", "v_texcoord.xy").C(";\n"); + writer.EndFSMain("outColor"); +} + +void GenerateDraw2DVS(char *buffer, const ShaderLanguageDesc &lang) { + ShaderWriter writer(buffer, lang, ShaderStage::Vertex, nullptr, 0); + + writer.BeginVSMain(inputs, Slice::empty(), varyings); + + writer.C(" v_texcoord = a_texcoord;\n"); // yes, this should be right. Should be 2.0 in the far corners. + writer.C(" gl_Position = vec4(a_position, 0.0, 1.0);\n"); + writer.F(" gl_Position.y *= %s1.0;\n", lang.viewportYSign); + + writer.EndVSMain(varyings); +} + +// verts have positions in clip coordinates. +void FramebufferManagerCommon::DrawStrip2D(Draw::Texture *tex, Draw2DVertex *verts, int vertexCount, bool linearFilter) { + using namespace Draw; + + if (!draw2DPipelineLinear_) { + const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc(); + + char *fsCode = new char[4000]; + char *vsCode = new char[4000]; + GenerateDraw2DFs(fsCode, shaderLanguageDesc, draw_->GetBugs()); + GenerateDraw2DVS(vsCode, shaderLanguageDesc); + + draw2DFs_ = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), "draw2d_fs"); + draw2DVs_ = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "draw2d_vs"); + + _assert_(draw2DFs_ && draw2DVs_); + + InputLayoutDesc desc = { + { + { 16, false }, + }, + { + { 0, SEM_POSITION, DataFormat::R32G32_FLOAT, 0 }, + { 0, SEM_TEXCOORD0, DataFormat::R32G32_FLOAT, 8 }, + }, + }; + InputLayout *inputLayout = draw_->CreateInputLayout(desc); + + BlendState *blendOff = draw_->CreateBlendState({ false, 0xF }); + DepthStencilStateDesc dsDesc{}; + DepthStencilState *noDepthStencil = draw_->CreateDepthStencilState(dsDesc); + RasterState *rasterNoCull = draw_->CreateRasterState({}); + + PipelineDesc stencilWriteDesc{ + Primitive::TRIANGLE_STRIP, + { draw2DVs_, draw2DFs_ }, + inputLayout, noDepthStencil, blendOff, rasterNoCull, nullptr, + }; + draw2DPipelineLinear_ = draw_->CreateGraphicsPipeline(stencilWriteDesc); + _assert_(draw2DPipelineLinear_); + + delete[] fsCode; + delete[] vsCode; + + rasterNoCull->Release(); + blendOff->Release(); + noDepthStencil->Release(); + inputLayout->Release(); + + SamplerStateDesc descLinear{}; + descLinear.magFilter = TextureFilter::LINEAR; + descLinear.minFilter = TextureFilter::LINEAR; + descLinear.mipFilter = TextureFilter::LINEAR; + descLinear.wrapU = TextureAddressMode::CLAMP_TO_EDGE; + descLinear.wrapV = TextureAddressMode::CLAMP_TO_EDGE; + draw2DSamplerLinear_= draw_->CreateSamplerState(descLinear); + + SamplerStateDesc descNearest{}; + descLinear.magFilter = TextureFilter::NEAREST; + descLinear.minFilter = TextureFilter::NEAREST; + descLinear.mipFilter = TextureFilter::NEAREST; + descLinear.wrapU = TextureAddressMode::CLAMP_TO_EDGE; + descLinear.wrapV = TextureAddressMode::CLAMP_TO_EDGE; + draw2DSamplerNearest_ = draw_->CreateSamplerState(descNearest); + } + + draw_->BindPipeline(draw2DPipelineLinear_); + if (tex) { + draw_->BindTextures(TEX_SLOT_PSP_TEXTURE, 1, &tex); + } + draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, linearFilter ? &draw2DSamplerLinear_ : &draw2DSamplerNearest_); + draw_->DrawUP(verts, vertexCount); +} diff --git a/GPU/Common/Draw2D.h b/GPU/Common/Draw2D.h new file mode 100644 index 0000000000..34c171e7a0 --- /dev/null +++ b/GPU/Common/Draw2D.h @@ -0,0 +1,9 @@ +#pragma once + +// For framebuffer copies and similar things that just require passthrough. +struct Draw2DVertex { + float x; + float y; + float u; + float v; +}; diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index e5b7f952da..65391be14f 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -35,6 +35,16 @@ enum { DECODED_INDEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * 16, }; +enum { + TEX_SLOT_PSP_TEXTURE = 0, + TEX_SLOT_SHADERBLEND_SRC = 1, + TEX_SLOT_ALPHATEST = 2, + TEX_SLOT_CLUT = 3, + TEX_SLOT_SPLINE_POINTS = 4, + TEX_SLOT_SPLINE_WEIGHTS_U = 5, + TEX_SLOT_SPLINE_WEIGHTS_V = 6, +}; + inline uint32_t GetVertTypeID(uint32_t vertType, int uvGenMode) { // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it // into the top of the verttype where there are unused bits. diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 1471f6bcf5..fe23b16825 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -23,6 +23,9 @@ #include "Common/GPU/OpenGL/GLFeatures.h" #include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Text/I18n.h" +#include "Common/Math/lin/matrix4x4.h" +#include "Common/Math/math_util.h" +#include "Common/System/Display.h" #include "Common/CommonTypes.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -44,8 +47,7 @@ #include "GPU/GPUState.h" FramebufferManagerCommon::FramebufferManagerCommon(Draw::DrawContext *draw) - : draw_(draw), - displayFormat_(GE_FORMAT_565) { + : draw_(draw), displayFormat_(GE_FORMAT_565) { presentation_ = new PresentationCommon(draw); } @@ -469,7 +471,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame if (useBufferedRendering_ && !g_Config.bDisableSlowFramebufEffects) { gpu->PerformMemoryUpload(params.fb_address, byteSize); - NotifyStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO); + PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO); // TODO: Is it worth trying to upload the depth buffer (only if it wasn't copied above..?) } @@ -581,15 +583,16 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir int w = std::min(src->renderWidth, dst->renderWidth); int h = std::min(src->renderHeight, dst->renderHeight); - // Note: We prefer Blit ahead of Copy here, since at least on GL, Copy will always also copy stencil which we don't want. - // See #9740. - // TODO: This ordering should probably apply to GL only, since in Vulkan you can totally copy just the depth aspect. - if (gstate_c.Supports(GPU_SUPPORTS_FRAMEBUFFER_BLIT_TO_DEPTH)) { - draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST, "BlitFramebufferDepth"); - RebindFramebuffer("After BlitFramebufferDepth"); - } else if (gstate_c.Supports(GPU_SUPPORTS_COPY_IMAGE)) { + // TODO: It might even be advantageous on some GPUs to do this copy using a fragment shader that writes to Z, that way upcoming commands can just continue that render pass. + + // Some GPUs can copy depth but only if stencil gets to come along for the ride. We only want to use this if there is no blit functionality. + if (draw_->GetDeviceCaps().framebufferSeparateDepthCopySupported || !draw_->GetDeviceCaps().framebufferDepthBlitSupported) { draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, w, h, 1, Draw::FB_DEPTH_BIT, "BlitFramebufferDepth"); RebindFramebuffer("After BlitFramebufferDepth"); + } else if (draw_->GetDeviceCaps().framebufferDepthBlitSupported) { + // We'll accept whether we get a separate depth blit or not... + draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST, "BlitFramebufferDepth"); + RebindFramebuffer("After BlitFramebufferDepth"); } dst->last_frame_depth_updated = gpuStats.numFlips; } @@ -693,7 +696,7 @@ void FramebufferManagerCommon::ReinterpretFramebuffer(VirtualFramebuffer *vfb, G bool doReinterpret = PSP_CoreParameter().compat.flags().ReinterpretFramebuffers && (lang == HLSL_D3D11 || lang == GLSL_VULKAN || lang == GLSL_3xx); // Copy image required for now. - if (!gstate_c.Supports(GPU_SUPPORTS_COPY_IMAGE)) + if (!draw_->GetDeviceCaps().framebufferCopySupported) doReinterpret = false; if (!doReinterpret) { // Fake reinterpret - just clear the way we always did on Vulkan. Just clear color and stencil. @@ -975,7 +978,6 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height); if (pixelsTex) { draw_->BindTextures(0, 1, &pixelsTex); - Bind2DShader(); DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); gpuStats.numUploads++; pixelsTex->Release(); @@ -1457,7 +1459,6 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, if (old.fbo) { INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %dx%dx%s", vfb->fb_address, w, h, GeBufferFormatToString(vfb->format)); if (vfb->fbo) { - // TODO: Swap the order of the below? That way we can avoid the needGLESRebinds_ check below I think. draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO"); if (!skipCopy) { // TODO: In this case, it'll nearly always be better to draw the old framebuffer to the new one than to do an actual blit. @@ -2388,6 +2389,7 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, } } + draw_->InvalidateCachedState(); textureCache_->ForgetLastTexture(); RebindFramebuffer("RebindFramebuffer - ReadFramebufferToMemory"); } @@ -2442,6 +2444,7 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo } void FramebufferManagerCommon::RebindFramebuffer(const char *tag) { + draw_->InvalidateCachedState(); shaderManager_->DirtyLastShader(); if (currentRenderVfb_ && currentRenderVfb_->fbo) { draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag); @@ -2487,6 +2490,15 @@ void FramebufferManagerCommon::DeviceLost() { DoRelease(reinterpretVBuf_); DoRelease(reinterpretSampler_); DoRelease(reinterpretVS_); + DoRelease(stencilUploadFs_); + DoRelease(stencilUploadVs_); + DoRelease(stencilUploadSampler_); + DoRelease(stencilUploadPipeline_); + DoRelease(draw2DPipelineLinear_); + DoRelease(draw2DSamplerNearest_); + DoRelease(draw2DSamplerLinear_); + DoRelease(draw2DVs_); + DoRelease(draw2DFs_); presentation_->DeviceLost(); draw_ = nullptr; } @@ -2495,3 +2507,188 @@ void FramebufferManagerCommon::DeviceRestore(Draw::DrawContext *draw) { draw_ = draw; presentation_->DeviceRestore(draw); } + +void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { + // Will be drawn as a strip. + Draw2DVertex coord[4] = { + {x, y, u0, v0}, + {x + w, y, u1, v0}, + {x + w, y + h, u1, v1}, + {x, y + h, u0, v1}, + }; + + if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { + float temp[8]; + int rotation = 0; + switch (uvRotation) { + case ROTATION_LOCKED_HORIZONTAL180: rotation = 2; break; + case ROTATION_LOCKED_VERTICAL: rotation = 1; break; + case ROTATION_LOCKED_VERTICAL180: rotation = 3; break; + } + for (int i = 0; i < 4; i++) { + temp[i * 2] = coord[((i + rotation) & 3)].u; + temp[i * 2 + 1] = coord[((i + rotation) & 3)].v; + } + + for (int i = 0; i < 4; i++) { + coord[i].u = temp[i * 2]; + coord[i].v = temp[i * 2 + 1]; + } + } + + const float invDestW = 2.0f / destW; + const float invDestH = 2.0f / destH; + for (int i = 0; i < 4; i++) { + coord[i].x = coord[i].x * invDestW - 1.0f; + coord[i].y = coord[i].y * invDestH - 1.0f; + } + + if ((flags & DRAWTEX_TO_BACKBUFFER) && g_display_rotation != DisplayRotation::ROTATE_0) { + for (int i = 0; i < 4; i++) { + // backwards notation, should fix that... + Lin::Vec3 pos = Lin::Vec3(coord[i].x, coord[i].y, 0.0); + pos = pos * g_display_rot_matrix; + coord[i].x = pos.x; + coord[i].y = pos.y; + } + } + + // Rearrange to strip form. + std::swap(coord[2], coord[3]); + + DrawStrip2D(nullptr, coord, 4, (flags & DRAWTEX_LINEAR) != 0); + + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); +} + +void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) { + if (!dst->fbo || !src->fbo || !useBufferedRendering_) { + // This can happen if they recently switched from non-buffered. + if (useBufferedRendering_) { + draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer"); + } + return; + } + + // Perform a little bit of clipping first. + // Block transfer coords are unsigned so I don't think we need to clip on the left side.. Although there are + // other uses for BlitFramebuffer. + if (dstX + w > dst->bufferWidth) { + w -= dstX + w - dst->bufferWidth; + } + if (dstY + h > dst->bufferHeight) { + h -= dstY + h - dst->bufferHeight; + } + if (srcX + w > src->bufferWidth) { + w -= srcX + w - src->bufferWidth; + } + if (srcY + h > src->bufferHeight) { + h -= srcY + h - src->bufferHeight; + } + + if (w <= 0 || h <= 0) { + // The whole rectangle got clipped. + return; + } + + bool useBlit = draw_->GetDeviceCaps().framebufferBlitSupported; + bool useCopy = draw_->GetDeviceCaps().framebufferCopySupported; + if (dst == currentRenderVfb_) { + // If already bound, using either a blit or a copy is unlikely to be an optimization. + // So we're gonna use a raster draw instead. + useBlit = false; + useCopy = false; + } + + float srcXFactor = src->renderScaleFactor; + float srcYFactor = src->renderScaleFactor; + const int srcBpp = src->format == GE_FORMAT_8888 ? 4 : 2; + if (srcBpp != bpp && bpp != 0) { + srcXFactor = (srcXFactor * bpp) / srcBpp; + } + int srcX1 = srcX * srcXFactor; + int srcX2 = (srcX + w) * srcXFactor; + int srcY1 = srcY * srcYFactor; + int srcY2 = (srcY + h) * srcYFactor; + + float dstXFactor = dst->renderScaleFactor; + float dstYFactor = dst->renderScaleFactor; + const int dstBpp = dst->format == GE_FORMAT_8888 ? 4 : 2; + if (dstBpp != bpp && bpp != 0) { + dstXFactor = (dstXFactor * bpp) / dstBpp; + } + int dstX1 = dstX * dstXFactor; + int dstX2 = (dstX + w) * dstXFactor; + int dstY1 = dstY * dstYFactor; + int dstY2 = (dstY + h) * dstYFactor; + + if (src == dst && srcX == dstX && srcY == dstY) { + // Let's just skip a copy where the destination is equal to the source. + WARN_LOG_REPORT_ONCE(blitSame, G3D, "Skipped blit with equal dst and src"); + return; + } + + if (useCopy) { + // glBlitFramebuffer can clip, but glCopyImageSubData is more restricted. + // In case the src goes outside, we just skip the optimization in that case. + const bool sameSize = dstX2 - dstX1 == srcX2 - srcX1 && dstY2 - dstY1 == srcY2 - srcY1; + const bool srcInsideBounds = srcX2 <= src->renderWidth && srcY2 <= src->renderHeight; + const bool dstInsideBounds = dstX2 <= dst->renderWidth && dstY2 <= dst->renderHeight; + const bool xOverlap = src == dst && srcX2 > dstX1 && srcX1 < dstX2; + const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2; + if (sameSize && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) { + draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT, tag); + return; + } + } + + if (useBlit) { + draw_->BlitFramebuffer(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST, tag); + } else { + Draw::Framebuffer *srcFBO = src->fbo; + if (src == dst) { + Draw::Framebuffer *tempFBO = GetTempFBO(TempFBO::BLIT, src->renderWidth, src->renderHeight); + BlitUsingRaster(src->fbo, srcX1, srcY1, srcX2, srcY2, tempFBO, dstX1, dstY1, dstX2, dstY2, false); + srcFBO = tempFBO; + } + BlitUsingRaster(srcFBO, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, false); + } + + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); +} + +void FramebufferManagerCommon::BlitUsingRaster( + Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, + Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, + bool linearFilter) { + + int destW, destH, srcW, srcH; + draw_->GetFramebufferDimensions(src, &srcW, &srcH); + draw_->GetFramebufferDimensions(dest, &destW, &destH); + + if (srcW == destW && srcH == destH && destX2 - destX1 == srcX2 - srcX1 && destY2 - destY1 == srcY2 - srcY1) { + // Optimize to a copy + draw_->CopyFramebufferImage(src, 0, (int)srcX1, (int)srcY1, 0, dest, 0, (int)destX1, (int)destY1, 0, (int)(srcX2 - srcX1), (int)(srcY2 - srcY1), 1, Draw::FB_COLOR_BIT, "BlitUsingRaster"); + return; + } + + float dX = 1.0f / (float)destW; + float dY = 1.0f / (float)destH; + float sX = 1.0f / (float)srcW; + float sY = 1.0f / (float)srcH; + Draw2DVertex vtx[4] = { + { -1.0f + 2.0f * dX * destX1, -(1.0f - 2.0f * dY * destY1), sX * srcX1, sY * srcY1 }, + { -1.0f + 2.0f * dX * destX2, -(1.0f - 2.0f * dY * destY1), sX * srcX2, sY * srcY1 }, + { -1.0f + 2.0f * dX * destX1, -(1.0f - 2.0f * dY * destY2), sX * srcX1, sY * srcY2 }, + { -1.0f + 2.0f * dX * destX2, -(1.0f - 2.0f * dY * destY2), sX * srcX2, sY * srcY2 }, + }; + + // Unbind the texture first to avoid the D3D11 hazard check (can't set render target to things bound as textures and vice versa, not even temporarily). + draw_->BindTexture(0, nullptr); + draw_->BindFramebufferAsRenderTarget(dest, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitUsingRaster"); + draw_->BindFramebufferAsTexture(src, 0, Draw::FB_COLOR_BIT, 0); + + DrawStrip2D(nullptr, vtx, 4, linearFilter); + + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); +} diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 04e2683e0f..1cfe42c447 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -19,7 +19,6 @@ // Here's a list of functionality to unify into FramebufferManagerCommon: // * DrawActiveTexture // * BlitFramebuffer -// * StencilBuffer*.cpp // // Also, in TextureCache we should be able to unify texture-based depal. @@ -30,11 +29,11 @@ #include "Common/CommonTypes.h" #include "Common/Log.h" -#include "Core/MemMap.h" +#include "Common/GPU/thin3d.h" #include "GPU/GPU.h" #include "GPU/ge_constants.h" #include "GPU/GPUInterface.h" -#include "Common/GPU/thin3d.h" +#include "GPU/Common/Draw2D.h" enum { FB_USAGE_DISPLAYED_FRAMEBUFFER = 1, @@ -165,7 +164,6 @@ enum BindFramebufferColorFlags { enum DrawTextureFlags { DRAWTEX_NEAREST = 0, DRAWTEX_LINEAR = 1, - DRAWTEX_KEEP_STENCIL_ALPHA = 4, DRAWTEX_TO_BACKBUFFER = 8, }; @@ -253,7 +251,7 @@ public: void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt); void UpdateFromMemory(u32 addr, int size, bool safe); void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor); - virtual bool NotifyStencilUpload(u32 addr, int size, StencilUpload flags = StencilUpload::NEEDS_CLEAR) = 0; + bool PerformStencilUpload(u32 addr, int size, StencilUpload flags = StencilUpload::NEEDS_CLEAR); // Returns true if it's sure this is a direct FBO->FBO transfer and it has already handle it. // In that case we hardly need to actually copy the bytes in VRAM, they will be wrong anyway (unless // read framebuffers is on, in which case this should always return false). @@ -329,7 +327,6 @@ public: SetColorUpdated(currentRenderVfb_, skipDrawReason); } } - void SetRenderSize(VirtualFramebuffer *vfb); void SetSafeSize(u16 w, u16 h); virtual void Resized(); @@ -355,8 +352,9 @@ protected: virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); void SetViewport2D(int x, int y, int w, int h); Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); - virtual void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) = 0; - virtual void Bind2DShader() = 0; + void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags); + + void DrawStrip2D(Draw::Texture *tex, Draw2DVertex *verts, int vertexCount, bool linearFilter); bool UpdateSize(); @@ -364,7 +362,12 @@ protected: virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra // Used by ReadFramebufferToMemory and later framebuffer block copies - virtual void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) = 0; + virtual void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag); + + void BlitUsingRaster( + Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, + Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, bool linearFilter); + void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags); void EstimateDrawingSize(u32 fb_address, GEBufferFormat fb_format, int viewport_width, int viewport_height, int region_width, int region_height, int scissor_width, int scissor_height, int fb_stride, int &drawing_width, int &drawing_height); @@ -442,7 +445,6 @@ protected: int pixelHeight_; int bloomHack_ = 0; - bool needGLESRebinds_ = false; Draw::DataFormat preferredPixelsFormat_ = Draw::DataFormat::R8G8B8A8_UNORM; struct TempFBOInfo { @@ -467,4 +469,18 @@ protected: Draw::ShaderModule *reinterpretVS_ = nullptr; Draw::SamplerState *reinterpretSampler_ = nullptr; Draw::Buffer *reinterpretVBuf_ = nullptr; + + // Common implementation of stencil buffer upload. Also not 100% optimal, but not perforamnce + // critical either. + Draw::Pipeline *stencilUploadPipeline_ = nullptr; + Draw::ShaderModule *stencilUploadVs_ = nullptr; + Draw::ShaderModule *stencilUploadFs_ = nullptr; + Draw::SamplerState *stencilUploadSampler_ = nullptr; + + // Draw2D pipelines + Draw::Pipeline *draw2DPipelineLinear_ = nullptr; + Draw::SamplerState *draw2DSamplerLinear_ = nullptr; + Draw::SamplerState *draw2DSamplerNearest_ = nullptr; + Draw::ShaderModule *draw2DVs_ = nullptr; + Draw::ShaderModule *draw2DFs_ = nullptr; }; diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 97e84ba138..6b21b66785 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -807,13 +807,11 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u draw_->DrawIndexed(6, 0); } - draw_->BindIndexBuffer(nullptr, 0); - DoRelease(srcFramebuffer_); DoRelease(srcTexture_); // Unbinds all textures and samplers too, needed since sometimes a MakePixelTexture is deleted etc. - draw_->BindPipeline(nullptr); + draw_->InvalidateCachedState(); previousUniforms_ = uniforms; } diff --git a/GPU/Common/ReinterpretFramebuffer.cpp b/GPU/Common/ReinterpretFramebuffer.cpp index da2f0ef7b1..aa1cd89a1b 100644 --- a/GPU/Common/ReinterpretFramebuffer.cpp +++ b/GPU/Common/ReinterpretFramebuffer.cpp @@ -3,10 +3,11 @@ #include "Common/GPU/Shader.h" #include "Common/GPU/ShaderWriter.h" #include "Common/Log.h" +#include "Common/GPU/thin3d.h" #include "GPU/Common/ReinterpretFramebuffer.h" static const VaryingDef varyings[1] = { - { "vec2", "v_texcoord", "TEXCOORD0", 0, "highp" }, + { "vec2", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" }, }; // TODO: We could possibly have an option to preserve any extra color precision? But gonna start without it. @@ -79,9 +80,10 @@ bool GenerateReinterpretVertexShader(char *buffer, const ShaderLanguageDesc &lan writer.C(" float x = -1.0 + float((gl_VertexIndex & 1) << 2);\n"); writer.C(" float y = -1.0 + float((gl_VertexIndex & 2) << 1);\n"); writer.C(" v_texcoord = (vec2(x, y) + vec2(1.0, 1.0)) * 0.5;\n"); - writer.F(" y *= %s1.0;\n", lang.viewportYSign); + if (strlen(lang.viewportYSign)) { + writer.F(" y *= %s1.0;\n", lang.viewportYSign); + } writer.C(" gl_Position = vec4(x, y, 0.0, 1.0);\n"); - writer.EndVSMain(varyings); return true; } diff --git a/GPU/Common/StencilCommon.cpp b/GPU/Common/StencilCommon.cpp index 29af15c020..91583ea24f 100644 --- a/GPU/Common/StencilCommon.cpp +++ b/GPU/Common/StencilCommon.cpp @@ -15,9 +15,17 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/GPU/Shader.h" +#include "Common/GPU/ShaderWriter.h" +#include "Core/Config.h" +#include "Core/ConfigValues.h" +#include "Core/Reporting.h" #include "GPU/Common/StencilCommon.h" +#include "GPU/Common/DrawEngineCommon.h" +#include "GPU/Common/FramebufferManagerCommon.h" +#include "GPU/Common/TextureCacheCommon.h" -u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { +static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { const u32 *ptr = (const u32 *)ptr8; for (u32 i = 0; i < numPixels / 2; ++i) { @@ -28,7 +36,7 @@ u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { return 0; } -u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { +static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { const u32 *ptr = (const u32 *)ptr8; u32 bits = 0; @@ -39,7 +47,7 @@ u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { return ((bits >> 12) & 0xF) | (bits >> 28); } -u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { +static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { const u32 *ptr = (const u32 *)ptr8; u32 bits = 0; @@ -49,3 +57,274 @@ u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { return bits >> 24; } + +struct StencilUB { + float stencilValue; +}; + +const UniformBufferDesc stencilUBDesc { sizeof(StencilUB), { + { "stencilValue", -1, 0, UniformType::FLOAT1, 0 }, +} }; + +// TODO: Merge this with UniformBufferDesc +static const UniformDef uniforms[1] = { + { "float", "stencilValue", 0 }, +}; + +static const InputDef inputs[1] = { + { "vec2", "a_position", Draw::SEM_POSITION, } +}; + +static const VaryingDef varyings[1] = { + { "vec2", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" }, +}; + +void GenerateStencilFs(char *buffer, const ShaderLanguageDesc &lang, const Draw::Bugs &bugs) { + ShaderWriter writer(buffer, lang, ShaderStage::Fragment, nullptr, 0); + writer.HighPrecisionFloat(); + + writer.DeclareSampler2D("samp", 0); + writer.DeclareTexture2D("tex", 0); + + if (bugs.Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL)) { + writer.C("layout (depth_unchanged) out float gl_FragDepth;\n"); + } + + writer.C("float roundAndScaleTo255f(in float x) { return floor(x * 255.99); }\n"); + + writer.BeginFSMain(uniforms, varyings); + + writer.C(" vec4 index = ").SampleTexture2D("tex", "samp", "v_texcoord.xy").C(";\n"); + writer.C(" vec4 outColor = index.aaaa;\n"); // Only care about a. + writer.C(" float shifted = roundAndScaleTo255f(index.a) / roundAndScaleTo255f(stencilValue);\n"); + // Bitwise operations on floats, ugh. + writer.C(" if (mod(floor(shifted), 2.0) < 0.99) DISCARD;\n"); + + if (bugs.Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL)) { + writer.C(" gl_FragDepth = gl_FragCoord.z;\n"); + } + + writer.EndFSMain("outColor"); +} + +// This can probably be shared with some other shaders, like reinterpret or the future depth upload. +void GenerateStencilVs(char *buffer, const ShaderLanguageDesc &lang) { + ShaderWriter writer(buffer, lang, ShaderStage::Vertex, nullptr, 0); + + writer.BeginVSMain(lang.vertexIndex ? Slice::empty() : inputs, Slice::empty(), varyings); + + if (lang.vertexIndex) { + writer.C(" float x = float((gl_VertexIndex & 1) << 1);\n"); + writer.C(" float y = float(gl_VertexIndex & 2);\n"); + writer.C(" v_texcoord = vec2(x, y);\n"); + } else { + writer.C(" v_texcoord = a_position * 2.0;\n"); // yes, this should be right. Should be 2.0 in the far corners. + } + writer.C(" gl_Position = vec4(v_texcoord * 2.0 - vec2(1.0, 1.0), 0.0, 1.0);\n"); + writer.F(" gl_Position.y *= %s1.0;\n", lang.viewportYSign); + + writer.EndVSMain(varyings); +} + +bool FramebufferManagerCommon::PerformStencilUpload(u32 addr, int size, StencilUpload flags) { + using namespace Draw; + + addr &= 0x3FFFFFFF; + if (!MayIntersectFramebuffer(addr)) { + return false; + } + + VirtualFramebuffer *dstBuffer = 0; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + if (vfb->fb_address == addr) { + dstBuffer = vfb; + } + } + if (!dstBuffer) { + return false; + } + + int values = 0; + u8 usedBits = 0; + + const u8 *src = Memory::GetPointer(addr); + if (!src) + return false; + + switch (dstBuffer->format) { + case GE_FORMAT_565: + // Well, this doesn't make much sense. + return false; + case GE_FORMAT_5551: + usedBits = StencilBits5551(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 2; + break; + case GE_FORMAT_4444: + usedBits = StencilBits4444(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 16; + break; + case GE_FORMAT_8888: + usedBits = StencilBits8888(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 256; + break; + case GE_FORMAT_INVALID: + case GE_FORMAT_DEPTH16: + // Inconceivable. + _assert_(false); + break; + } + + if (usedBits == 0) { + if (flags == StencilUpload::STENCIL_IS_ZERO) { + // Common when creating buffers, it's already 0. + // We're done. + return false; + } + + shaderManager_->DirtyLastShader(); + + // Let's not bother with the shader if it's just zero. + if (dstBuffer->fbo) { + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "NotifyStencilUpload_Clear"); + } + + // Here we might want to clear destination alpha by using a draw, but we haven't found a need for this yet. + // Will implement when needed... + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE); + return true; + } + + shaderManager_->DirtyLastShader(); + textureCache_->ForgetLastTexture(); + + if (!stencilUploadPipeline_) { + const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc(); + + char *fsCode = new char[4000]; + char *vsCode = new char[4000]; + GenerateStencilFs(fsCode, shaderLanguageDesc, draw_->GetBugs()); + GenerateStencilVs(vsCode, shaderLanguageDesc); + + stencilUploadFs_ = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), "stencil_fs"); + stencilUploadVs_ = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "stencil_vs"); + + _assert_(stencilUploadFs_ && stencilUploadVs_); + + InputLayoutDesc desc = { + { + { 8, false }, + }, + { + { 0, SEM_POSITION, DataFormat::R32G32_FLOAT, 0 }, + }, + }; + InputLayout *inputLayout = draw_->CreateInputLayout(desc); + + BlendState *blendOff = draw_->CreateBlendState({ false, 0x8 }); + DepthStencilStateDesc dsDesc{}; + dsDesc.stencilEnabled = true; + dsDesc.stencil.compareOp = Comparison::ALWAYS; + dsDesc.stencil.depthFailOp = StencilOp::REPLACE; + dsDesc.stencil.failOp = StencilOp::REPLACE; + dsDesc.stencil.passOp = StencilOp::REPLACE; + DepthStencilState *stencilWrite = draw_->CreateDepthStencilState(dsDesc); + RasterState *rasterNoCull = draw_->CreateRasterState({}); + + PipelineDesc stencilWriteDesc{ + Primitive::TRIANGLE_LIST, + { stencilUploadVs_, stencilUploadFs_ }, + inputLayout, stencilWrite, blendOff, rasterNoCull, &stencilUBDesc, + }; + stencilUploadPipeline_ = draw_->CreateGraphicsPipeline(stencilWriteDesc); + _assert_(stencilUploadPipeline_); + + delete[] fsCode; + delete[] vsCode; + + rasterNoCull->Release(); + blendOff->Release(); + stencilWrite->Release(); + inputLayout->Release(); + + SamplerStateDesc descNearest{}; + stencilUploadSampler_ = draw_->CreateSamplerState(descNearest); + } + + // Fullscreen triangle coordinates. + static const float positions[6] = { + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + }; + + bool useBlit = draw_->GetDeviceCaps().framebufferStencilBlitSupported; + + // Our fragment shader (and discard) is slow. Since the source is 1x, we can stencil to 1x. + // Then after we're done, we'll just blit it across and stretch it there. Not worth doing + // if already at 1x size though, of course. + // TODO: This path means that we don't write color alpha... Ugh. + if (dstBuffer->width == dstBuffer->renderWidth || !dstBuffer->fbo) { + useBlit = false; + } + + u16 w = useBlit ? dstBuffer->width : dstBuffer->renderWidth; + u16 h = useBlit ? dstBuffer->height : dstBuffer->renderHeight; + + Draw::Framebuffer *blitFBO = nullptr; + if (useBlit) { + blitFBO = GetTempFBO(TempFBO::STENCIL, w, h); + draw_->BindFramebufferAsRenderTarget(blitFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::CLEAR }, "NotifyStencilUpload_Blit"); + } else if (dstBuffer->fbo) { + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "NotifyStencilUpload_NoBlit"); + } + + Draw::Viewport viewport = { 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f }; + draw_->SetViewports(1, &viewport); + + // TODO: Switch the format to a single channel format? + Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height); + if (!tex) { + // Bad! + return false; + } + + draw_->BindTextures(TEX_SLOT_PSP_TEXTURE, 1, &tex); + draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, &stencilUploadSampler_); + + // We must bind the program after starting the render pass, and set the color mask after clearing. + draw_->SetScissorRect(0, 0, w, h); + draw_->BindPipeline(stencilUploadPipeline_); + + for (int i = 1; i < values; i += i) { + if (!(usedBits & i)) { + // It's already zero, let's skip it. + continue; + } + StencilUB ub{}; + if (dstBuffer->format == GE_FORMAT_4444) { + draw_->SetStencilParams(0xFF, (i << 4) | i, 0xFF); + ub.stencilValue = i * (16.0f / 255.0f); + } else if (dstBuffer->format == GE_FORMAT_5551) { + draw_->SetStencilParams(0xFF, 0xFF, 0xFF); + ub.stencilValue = i * (128.0f / 255.0f); + } else { + draw_->SetStencilParams(0xFF, i, 0xFF); + ub.stencilValue = i * (1.0f / 255.0f); + } + draw_->UpdateDynamicUniformBuffer(&ub, sizeof(ub)); + draw_->DrawUP(positions, 3); + } + + if (useBlit) { + // Note that scissors don't affect blits on other APIs than OpenGL, so might want to try to get rid of this. + draw_->SetScissorRect(0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight); + draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST, "NotifyStencilUpload_Blit"); + RebindFramebuffer("RebindFramebuffer - Stencil"); + } + tex->Release(); + + draw_->InvalidateCachedState(); + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); + return true; +} diff --git a/GPU/Common/StencilCommon.h b/GPU/Common/StencilCommon.h index ee26b4412e..e9746012a6 100644 --- a/GPU/Common/StencilCommon.h +++ b/GPU/Common/StencilCommon.h @@ -2,6 +2,8 @@ #include "Common/CommonTypes.h" -u8 StencilBits8888(const u8 *ptr8, u32 numPixels); -u8 StencilBits4444(const u8 *ptr8, u32 numPixels); -u8 StencilBits5551(const u8 *ptr8, u32 numPixels); +#include "Common/GPU/thin3d.h" + +// Exposed for automated tests +void GenerateStencilFs(char *buffer, const ShaderLanguageDesc &lang, const Draw::Bugs &bugs); +void GenerateStencilVs(char *buffer, const ShaderLanguageDesc &lang); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 6900e115d6..9e59a07d8b 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -21,9 +21,6 @@ #include "Common/Common.h" #include "Common/Data/Convert/ColorConv.h" -#include "Common/System/Display.h" -#include "Common/Math/lin/matrix4x4.h" -#include "Common/Math/math_util.h" #include "Common/GPU/thin3d.h" #include "Core/MemMap.h" @@ -46,97 +43,27 @@ #include #endif -static const char *vscode = - "struct VS_IN {\n" - " float4 ObjPos : POSITION;\n" - " float2 Uv : TEXCOORD0;\n" - "};\n" - "struct VS_OUT {\n" - " float2 Uv : TEXCOORD0;\n" - " float4 ProjPos : SV_Position;\n" - "};\n" - "VS_OUT main(VS_IN In) {\n" - " VS_OUT Out;\n" - " Out.ProjPos = In.ObjPos;\n" - " Out.Uv = In.Uv;\n" - " return Out;\n" - "}\n"; - -static const char *pscode = - "SamplerState samp : register(s0);\n" - "Texture2D tex : register(t0);\n" - "struct PS_IN {\n" - " float2 Uv : TEXCOORD0;\n" - "};\n" - "float4 main( PS_IN In ) : SV_Target {\n" - " float4 c = tex.Sample(samp, In.Uv);\n" - " return c;\n" - "}\n"; - -const D3D11_INPUT_ELEMENT_DESC FramebufferManagerD3D11::g_QuadVertexElements[2] = { - { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, }, -}; - FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw) : FramebufferManagerCommon(draw) { device_ = (ID3D11Device *)draw->GetNativeObject(Draw::NativeObject::DEVICE); context_ = (ID3D11DeviceContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); featureLevel_ = (D3D_FEATURE_LEVEL)draw->GetNativeObject(Draw::NativeObject::FEATURE_LEVEL); - std::vector bytecode; - - std::string errorMsg; - quadVertexShader_ = CreateVertexShaderD3D11(device_, vscode, strlen(vscode), &bytecode, featureLevel_); - quadPixelShader_ = CreatePixelShaderD3D11(device_, pscode, strlen(pscode), featureLevel_); - ASSERT_SUCCESS(device_->CreateInputLayout(g_QuadVertexElements, ARRAY_SIZE(g_QuadVertexElements), bytecode.data(), bytecode.size(), &quadInputLayout_)); - - // STRIP geometry - static const float fsCoord[20] = { - -1.0f,-1.0f, 0.0f, 0.0f, 0.0f, - 1.0f,-1.0f, 0.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - }; D3D11_BUFFER_DESC vb{}; vb.ByteWidth = 20 * 4; vb.Usage = D3D11_USAGE_IMMUTABLE; - vb.CPUAccessFlags = 0; vb.BindFlags = D3D11_BIND_VERTEX_BUFFER; - D3D11_SUBRESOURCE_DATA data{ fsCoord }; - ASSERT_SUCCESS(device_->CreateBuffer(&vb, &data, &fsQuadBuffer_)); vb.Usage = D3D11_USAGE_DYNAMIC; vb.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ASSERT_SUCCESS(device_->CreateBuffer(&vb, nullptr, &quadBuffer_)); - 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); - presentation_->SetLanguage(HLSL_D3D11); preferredPixelsFormat_ = Draw::DataFormat::B8G8R8A8_UNORM; } FramebufferManagerD3D11::~FramebufferManagerD3D11() { // Drawing cleanup - if (quadVertexShader_) - quadVertexShader_->Release(); - if (quadPixelShader_) - quadPixelShader_->Release(); - quadInputLayout_->Release(); quadBuffer_->Release(); - fsQuadBuffer_->Release(); // Stencil cleanup for (int i = 0; i < 256; i++) { @@ -151,11 +78,6 @@ FramebufferManagerD3D11::~FramebufferManagerD3D11() { stencilUploadInputLayout_->Release(); if (stencilValueBuffer_) stencilValueBuffer_->Release(); - - if (nullTextureView_) - nullTextureView_->Release(); - if (nullTexture_) - nullTexture_->Release(); } void FramebufferManagerD3D11::SetTextureCache(TextureCacheD3D11 *tc) { @@ -170,78 +92,6 @@ void FramebufferManagerD3D11::SetDrawEngine(DrawEngineD3D11 *td) { drawEngine_ = td; } -void FramebufferManagerD3D11::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { - struct Coord { - Lin::Vec3 pos; float u, v; - }; - Coord coord[4] = { - {{x, y, 0}, u0, v0}, - {{x + w, y, 0}, u1, v0}, - {{x + w, y + h,0}, u1, v1}, - {{x, y + h, 0}, u0, v1}, - }; - - if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { - float temp[8]; - int rotation = 0; - switch (uvRotation) { - case ROTATION_LOCKED_HORIZONTAL180: rotation = 2; break; - case ROTATION_LOCKED_VERTICAL: rotation = 1; break; - case ROTATION_LOCKED_VERTICAL180: rotation = 3; break; - } - for (int i = 0; i < 4; i++) { - temp[i * 2] = coord[((i + rotation) & 3)].u; - temp[i * 2 + 1] = coord[((i + rotation) & 3)].v; - } - - for (int i = 0; i < 4; i++) { - coord[i].u = temp[i * 2]; - coord[i].v = temp[i * 2 + 1]; - } - } - - float invDestW = 1.0f / (destW * 0.5f); - float invDestH = 1.0f / (destH * 0.5f); - for (int i = 0; i < 4; i++) { - coord[i].pos.x = coord[i].pos.x * invDestW - 1.0f; - coord[i].pos.y = -(coord[i].pos.y * invDestH - 1.0f); - } - - if (g_display_rotation != DisplayRotation::ROTATE_0) { - for (int i = 0; i < 4; i++) { - // backwards notation, should fix that... - coord[i].pos = coord[i].pos * g_display_rot_matrix; - } - } - - // The above code is for FAN geometry but we can only do STRIP. So rearrange it a little. - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(quadBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); - float *dest = (float *)map.pData; - memcpy(dest, coord, sizeof(Coord)); - memcpy(dest + 5, coord + 1, sizeof(Coord)); - memcpy(dest + 10, coord + 3, sizeof(Coord)); - memcpy(dest + 15, coord + 2, sizeof(Coord)); - context_->Unmap(quadBuffer_, 0); - - context_->RSSetState(stockD3D11.rasterStateNoCull); - context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF); - context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0); - context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - context_->PSSetSamplers(0, 1, (flags & DRAWTEX_LINEAR) ? &stockD3D11.samplerLinear2DClamp : &stockD3D11.samplerPoint2DClamp); - UINT stride = 20; - UINT offset = 0; - context_->IASetVertexBuffers(0, 1, &quadBuffer_, &stride, &offset); - context_->Draw(4, 0); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE); -} - -void FramebufferManagerD3D11::Bind2DShader() { - context_->IASetInputLayout(quadInputLayout_); - context_->PSSetShader(quadPixelShader_, 0, 0); - context_->VSSetShader(quadVertexShader_, 0, 0); -} - static void CopyPixelDepthOnly(u32 *dstp, const u32 *srcp, size_t c) { size_t x = 0; @@ -267,106 +117,6 @@ static void CopyPixelDepthOnly(u32 *dstp, const u32 *srcp, size_t c) { } } -void FramebufferManagerD3D11::SimpleBlit( - Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, - Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, bool linearFilter) { - - int destW, destH, srcW, srcH; - draw_->GetFramebufferDimensions(src, &srcW, &srcH); - draw_->GetFramebufferDimensions(dest, &destW, &destH); - - if (srcW == destW && srcH == destH && destX2 - destX1 == srcX2 - srcX1 && destY2 - destY1 == srcY2 - srcY1) { - // Optimize to a copy - draw_->CopyFramebufferImage(src, 0, (int)srcX1, (int)srcY1, 0, dest, 0, (int)destX1, (int)destY1, 0, (int)(srcX2 - srcX1), (int)(srcY2 - srcY1), 1, Draw::FB_COLOR_BIT, "SimpleBlit"); - return; - } - - float dX = 1.0f / (float)destW; - float dY = 1.0f / (float)destH; - float sX = 1.0f / (float)srcW; - float sY = 1.0f / (float)srcH; - struct Vtx { - float x, y, z, u, v; - }; - Vtx vtx[4] = { - { -1.0f + 2.0f * dX * destX1, 1.0f - 2.0f * dY * destY1, 0.0f, sX * srcX1, sY * srcY1 }, - { -1.0f + 2.0f * dX * destX2, 1.0f - 2.0f * dY * destY1, 0.0f, sX * srcX2, sY * srcY1 }, - { -1.0f + 2.0f * dX * destX1, 1.0f - 2.0f * dY * destY2, 0.0f, sX * srcX1, sY * srcY2 }, - { -1.0f + 2.0f * dX * destX2, 1.0f - 2.0f * dY * destY2, 0.0f, sX * srcX2, sY * srcY2 }, - }; - - D3D11_MAPPED_SUBRESOURCE map; - ASSERT_SUCCESS(context_->Map(quadBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map)); - memcpy(map.pData, vtx, 4 * sizeof(Vtx)); - context_->Unmap(quadBuffer_, 0); - - // Unbind the texture first to avoid the D3D11 hazard check (can't set render target to things bound as textures and vice versa, not even temporarily). - draw_->BindTexture(0, nullptr); - draw_->BindFramebufferAsRenderTarget(dest, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "SimpleBlit"); - draw_->BindFramebufferAsTexture(src, 0, Draw::FB_COLOR_BIT, 0); - - Bind2DShader(); - D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)destW, (float)destH, 0.0f, 1.0f }; - context_->RSSetViewports(1, &vp); - context_->RSSetState(stockD3D11.rasterStateNoCull); - context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF); - context_->OMSetDepthStencilState(stockD3D11.depthStencilDisabled, 0); - context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - context_->PSSetSamplers(0, 1, linearFilter ? &stockD3D11.samplerLinear2DClamp : &stockD3D11.samplerPoint2DClamp); - UINT stride = sizeof(Vtx); - UINT offset = 0; - context_->IASetVertexBuffers(0, 1, &quadBuffer_, &stride, &offset); - context_->Draw(4, 0); - - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE); -} - -void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) { - if (!dst->fbo || !src->fbo || !useBufferedRendering_) { - // This can happen if they recently switched from non-buffered. - if (useBufferedRendering_) { - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer_Fail"); - } - return; - } - - float srcXFactor = src->renderScaleFactor; - float srcYFactor = src->renderScaleFactor; - const int srcBpp = src->format == GE_FORMAT_8888 ? 4 : 2; - if (srcBpp != bpp && bpp != 0) { - srcXFactor = (srcXFactor * bpp) / srcBpp; - } - int srcX1 = srcX * srcXFactor; - int srcX2 = (srcX + w) * srcXFactor; - int srcY1 = srcY * srcYFactor; - int srcY2 = (srcY + h) * srcYFactor; - - float dstXFactor = dst->renderScaleFactor; - float dstYFactor = dst->renderScaleFactor; - const int dstBpp = dst->format == GE_FORMAT_8888 ? 4 : 2; - if (dstBpp != bpp && bpp != 0) { - dstXFactor = (dstXFactor * bpp) / dstBpp; - } - int dstX1 = dstX * dstXFactor; - int dstX2 = (dstX + w) * dstXFactor; - int dstY1 = dstY * dstYFactor; - int dstY2 = (dstY + h) * dstYFactor; - - // Direct3D doesn't support rect -> self. - Draw::Framebuffer *srcFBO = src->fbo; - if (src == dst) { - Draw::Framebuffer *tempFBO = GetTempFBO(TempFBO::BLIT, src->renderWidth, src->renderHeight); - SimpleBlit(tempFBO, dstX1, dstY1, dstX2, dstY2, src->fbo, srcX1, srcY1, srcX2, srcY2, false); - srcFBO = tempFBO; - } - SimpleBlit( - dst->fbo, dstX1, dstY1, dstX2, dstY2, - srcFBO, srcX1, srcY1, srcX2, srcY2, - false); - - draw_->BindTexture(0, nullptr); -} - // Nobody calls this yet. void FramebufferManagerD3D11::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 0a389f6dc3..6dd9bbb3ea 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -39,49 +39,27 @@ public: void SetTextureCache(TextureCacheD3D11 *tc); void SetShaderManager(ShaderManagerD3D11 *sm); void SetDrawEngine(DrawEngineD3D11 *td); - void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; void EndFrame(); - virtual bool NotifyStencilUpload(u32 addr, int size, StencilUpload flags = StencilUpload::NEEDS_CLEAR) override; - // TODO: Remove ID3D11Buffer *GetDynamicQuadBuffer() { return quadBuffer_; } -protected: - // Used by ReadFramebufferToMemory and later framebuffer block copies - void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) override; - private: - void Bind2DShader() override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); - void SimpleBlit( - Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, - Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, - bool linearFilter); ID3D11Device *device_; ID3D11DeviceContext *context_; D3D_FEATURE_LEVEL featureLevel_; - ID3D11VertexShader *quadVertexShader_; - ID3D11PixelShader *quadPixelShader_; - ID3D11InputLayout *quadInputLayout_; // Dynamic ID3D11Buffer *quadBuffer_; - ID3D11Buffer *fsQuadBuffer_; - const UINT quadStride_ = 20; - const UINT quadOffset_ = 0; - static const D3D11_INPUT_ELEMENT_DESC g_QuadVertexElements[2]; ID3D11PixelShader *stencilUploadPS_ = nullptr; ID3D11VertexShader *stencilUploadVS_ = nullptr; ID3D11InputLayout *stencilUploadInputLayout_ = nullptr; ID3D11Buffer *stencilValueBuffer_ = nullptr; ID3D11DepthStencilState *stencilMaskStates_[256]{}; - - ID3D11Texture2D *nullTexture_ = nullptr; - ID3D11ShaderResourceView *nullTextureView_ = nullptr; }; diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index f02d93547e..ba68c1bd4d 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -139,7 +139,6 @@ void GPU_D3D11::CheckGPUFeatures() { } } - features |= GPU_SUPPORTS_COPY_IMAGE; features |= GPU_SUPPORTS_TEXTURE_FLOAT; features |= GPU_SUPPORTS_INSTANCE_RENDERING; features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL; diff --git a/GPU/D3D11/StencilBufferD3D11.cpp b/GPU/D3D11/StencilBufferD3D11.cpp deleted file mode 100644 index eee531b55f..0000000000 --- a/GPU/D3D11/StencilBufferD3D11.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (c) 2014- 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 - -#include "Common/GPU/thin3d.h" - -#include "Common/Log.h" -#include "Core/Reporting.h" -#include "GPU/Common/StencilCommon.h" -#include "GPU/D3D11/FramebufferManagerD3D11.h" -#include "GPU/D3D11/ShaderManagerD3D11.h" -#include "GPU/D3D11/TextureCacheD3D11.h" -#include "GPU/D3D11/D3D11Util.h" - -struct StencilValueUB { - uint32_t u_stencilValue[4]; -}; - -static const char *stencil_ps = R"( -SamplerState samp : register(s0); -Texture2D tex : register(t0); -cbuffer base : register(b0) { - int4 u_stencilValue; -}; -struct PS_IN { - float2 v_texcoord0 : TEXCOORD0; -}; -float4 main(PS_IN In) : SV_Target { - float4 index = tex.Sample(samp, In.v_texcoord0); - int indexBits = int(index.a * 255.99); - if ((indexBits & u_stencilValue.x) == 0) - discard; - return index.aaaa; -} -)"; - -// static const char *stencil_ps_fast; - -static const char *stencil_vs = R"( -struct VS_IN { - float4 a_position : POSITION; - float2 a_texcoord0 : TEXCOORD0; -}; -struct VS_OUT { - float2 v_texcoord0 : TEXCOORD0; - float4 position : SV_Position; -}; -VS_OUT main(VS_IN In) { - VS_OUT Out; - Out.position = In.a_position; - Out.v_texcoord0 = In.a_texcoord0; - return Out; -} -)"; - -// TODO : If SV_StencilRef is available (D3D11.3) then this can be done in a single pass. -bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, StencilUpload flags) { - addr &= 0x3FFFFFFF; - if (!MayIntersectFramebuffer(addr)) { - return false; - } - - VirtualFramebuffer *dstBuffer = 0; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - if (vfb->fb_address == addr) { - dstBuffer = vfb; - } - } - if (!dstBuffer) { - return false; - } - - int values = 0; - u8 usedBits = 0; - - const u8 *src = Memory::GetPointer(addr); - if (!src) { - return false; - } - - switch (dstBuffer->format) { - case GE_FORMAT_565: - // Well, this doesn't make much sense. - return false; - case GE_FORMAT_5551: - usedBits = StencilBits5551(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 2; - break; - case GE_FORMAT_4444: - usedBits = StencilBits4444(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 16; - break; - case GE_FORMAT_8888: - usedBits = StencilBits8888(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 256; - break; - case GE_FORMAT_INVALID: - // Impossible. - break; - } - - if (usedBits == 0) { - if (flags == StencilUpload::STENCIL_IS_ZERO) { - // Common when creating buffers, it's already 0. We're done. - return false; - } - - // Clear stencil+alpha but not color. Only way is to draw a quad. - context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0x8], nullptr, 0xFFFFFFFF); - context_->RSSetState(stockD3D11.rasterStateNoCull); - context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0); - context_->IASetVertexBuffers(0, 1, &fsQuadBuffer_, &quadStride_, &quadOffset_); - context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - context_->Draw(4, 0); - - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); - return true; - } - - // TODO: Helper with logging? - if (!stencilUploadPS_) { - std::string errorMessage; - stencilUploadPS_ = CreatePixelShaderD3D11(device_, stencil_ps, strlen(stencil_ps), featureLevel_); - } - if (!stencilUploadVS_) { - std::string errorMessage; - std::vector byteCode; - stencilUploadVS_ = CreateVertexShaderD3D11(device_, stencil_vs, strlen(stencil_vs), &byteCode, featureLevel_); - ASSERT_SUCCESS(device_->CreateInputLayout(g_QuadVertexElements, 2, byteCode.data(), byteCode.size(), &stencilUploadInputLayout_)); - } - if (!stencilValueBuffer_) { - D3D11_BUFFER_DESC desc{}; - desc.ByteWidth = sizeof(StencilValueUB); - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - ASSERT_SUCCESS(device_->CreateBuffer(&desc, nullptr, &stencilValueBuffer_)); - } - - shaderManager_->DirtyLastShader(); - - u16 w = dstBuffer->renderWidth; - u16 h = dstBuffer->renderHeight; - Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight); - if (!tex) - return false; - if (dstBuffer->fbo) { - // Typically, STENCIL_IS_ZERO means it's already bound. - Draw::RPAction stencilAction = flags == StencilUpload::STENCIL_IS_ZERO ? Draw::RPAction::KEEP : Draw::RPAction::CLEAR; - draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, stencilAction }, "Stencil"); - } else { - // something is wrong... - } - D3D11_VIEWPORT vp{ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f }; - context_->RSSetViewports(1, &vp); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); - - float coord[20] = { - -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 1.0, - 1.0f, -1.0f, 0.0f, 1.0, 1.0, - }; - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(quadBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); - memcpy(map.pData, coord, sizeof(float) * 4 * 5); - context_->Unmap(quadBuffer_, 0); - - shaderManager_->DirtyLastShader(); - textureCache_->ForgetLastTexture(); - - context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0], nullptr, 0xFFFFFFFF); - context_->IASetInputLayout(stencilUploadInputLayout_); - context_->PSSetShader(stencilUploadPS_, nullptr, 0); - context_->VSSetShader(stencilUploadVS_, nullptr, 0); - draw_->BindTextures(0, 1, &tex); - context_->RSSetState(stockD3D11.rasterStateNoCull); - context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - context_->IASetVertexBuffers(0, 1, &quadBuffer_, &quadStride_, &quadOffset_); - context_->PSSetSamplers(0, 1, &stockD3D11.samplerPoint2DClamp); - context_->OMSetDepthStencilState(stockD3D11.depthDisabledStencilWrite, 0xFF); - - for (int i = 1; i < values; i += i) { - if (!(usedBits & i)) { - // It's already zero, let's skip it. - continue; - } - uint8_t mask = 0; - uint8_t value = 0; - if (dstBuffer->format == GE_FORMAT_4444) { - mask = i | (i << 4); - value = i * 16; - } else if (dstBuffer->format == GE_FORMAT_5551) { - mask = 0xFF; - value = i * 128; - } else { - mask = i; - value = i; - } - if (!stencilMaskStates_[mask]) { - D3D11_DEPTH_STENCIL_DESC desc{}; - desc.DepthEnable = false; - desc.StencilEnable = true; - desc.StencilReadMask = 0xFF; - desc.StencilWriteMask = mask; - desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_REPLACE; - desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_REPLACE; - desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; - desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; - desc.BackFace = desc.FrontFace; - device_->CreateDepthStencilState(&desc, &stencilMaskStates_[mask]); - } - context_->OMSetDepthStencilState(stencilMaskStates_[mask], 0xFF); - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(stencilValueBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); - StencilValueUB ub{ (uint32_t)value }; - memcpy(map.pData, &ub, sizeof(ub)); - context_->Unmap(stencilValueBuffer_, 0); - context_->PSSetConstantBuffers(0, 1, &stencilValueBuffer_); - context_->Draw(4, 0); - } - - tex->Release(); - RebindFramebuffer("RebindFramebuffer stencil"); - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - return true; -} diff --git a/GPU/Directx9/FramebufferManagerDX9.cpp b/GPU/Directx9/FramebufferManagerDX9.cpp index 47e86fc0a0..0bfe4c0ad1 100644 --- a/GPU/Directx9/FramebufferManagerDX9.cpp +++ b/GPU/Directx9/FramebufferManagerDX9.cpp @@ -47,97 +47,20 @@ namespace DX9 { -static const char *vscode = R"( -struct VS_IN { - float4 ObjPos : POSITION; - float2 Uv : TEXCOORD0; -}; -struct VS_OUT { - float4 ProjPos : POSITION; - float2 Uv : TEXCOORD0; -}; -VS_OUT main( VS_IN In ) { - VS_OUT Out; - Out.ProjPos = In.ObjPos; - Out.Uv = In.Uv; - return Out; -} -)"; - -static const char *pscode = R"( -sampler s: register(s0); -struct PS_IN { - float2 Uv : TEXCOORD0; -}; -float4 main( PS_IN In ) : COLOR { - float4 c = tex2D(s, In.Uv); - return c; -} -)"; - -static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { - { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, - { 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, - D3DDECL_END() -}; - FramebufferManagerDX9::FramebufferManagerDX9(Draw::DrawContext *draw) : FramebufferManagerCommon(draw) { device_ = (LPDIRECT3DDEVICE9)draw->GetNativeObject(Draw::NativeObject::DEVICE); deviceEx_ = (LPDIRECT3DDEVICE9)draw->GetNativeObject(Draw::NativeObject::DEVICE_EX); - std::string errorMsg; - if (!CompileVertexShaderD3D9(device_, vscode, &pFramebufferVertexShader, &errorMsg)) { - OutputDebugStringA(errorMsg.c_str()); - } - - if (!CompilePixelShaderD3D9(device_, pscode, &pFramebufferPixelShader, &errorMsg)) { - OutputDebugStringA(errorMsg.c_str()); - if (pFramebufferVertexShader) { - pFramebufferVertexShader->Release(); - } - } - - 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); presentation_->SetLanguage(HLSL_D3D9); preferredPixelsFormat_ = Draw::DataFormat::B8G8R8A8_UNORM; } FramebufferManagerDX9::~FramebufferManagerDX9() { - if (pFramebufferVertexShader) { - pFramebufferVertexShader->Release(); - pFramebufferVertexShader = nullptr; - } - if (pFramebufferPixelShader) { - pFramebufferPixelShader->Release(); - pFramebufferPixelShader = nullptr; - } - pFramebufferVertexDecl->Release(); for (auto &it : offscreenSurfaces_) { it.second.surface->Release(); } - if (stencilUploadPS_) { - stencilUploadPS_->Release(); - } - if (stencilUploadVS_) { - stencilUploadVS_->Release(); - } - if (nullTex_) - nullTex_->Release(); } void FramebufferManagerDX9::SetTextureCache(TextureCacheDX9 *tc) { @@ -152,72 +75,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { drawEngine_ = td; } - void FramebufferManagerDX9::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { - // TODO: StretchRect instead when possible? - float coord[20] = { - x,y,0, u0,v0, - x+w,y,0, u1,v0, - x+w,y+h,0, u1,v1, - x,y+h,0, u0,v1, - }; - - if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { - float temp[8]; - int rotation = 0; - switch (uvRotation) { - case ROTATION_LOCKED_HORIZONTAL180: rotation = 2; break; - case ROTATION_LOCKED_VERTICAL: rotation = 1; break; - case ROTATION_LOCKED_VERTICAL180: rotation = 3; break; - } - - for (int i = 0; i < 4; i++) { - temp[i * 2] = coord[((i + rotation) & 3) * 5 + 3]; - temp[i * 2 + 1] = coord[((i + rotation) & 3) * 5 + 4]; - } - - for (int i = 0; i < 4; i++) { - coord[i * 5 + 3] = temp[i * 2]; - coord[i * 5 + 4] = temp[i * 2 + 1]; - } - } - - float invDestW = 1.0f / (destW * 0.5f); - float invDestH = 1.0f / (destH * 0.5f); - float halfPixelX = invDestW * 0.5f; - float halfPixelY = invDestH * 0.5f; - for (int i = 0; i < 4; i++) { - coord[i * 5] = coord[i * 5] * invDestW - 1.0f - halfPixelX; - coord[i * 5 + 1] = -(coord[i * 5 + 1] * invDestH - 1.0f - halfPixelY); - } - - if (flags & DRAWTEX_LINEAR) { - dxstate.texMagFilter.set(D3DTEXF_LINEAR); - dxstate.texMinFilter.set(D3DTEXF_LINEAR); - } else { - dxstate.texMagFilter.set(D3DTEXF_POINT); - dxstate.texMinFilter.set(D3DTEXF_POINT); - } - dxstate.texMipLodBias.set(0.0f); - dxstate.texMaxMipLevel.set(0); - dxstate.blend.disable(); - dxstate.cullMode.set(D3DCULL_NONE); - dxstate.depthTest.disable(); - dxstate.scissorTest.disable(); - dxstate.stencilTest.disable(); - dxstate.colorMask.set(0xF); - dxstate.stencilWriteMask.set(0xFF); - HRESULT hr = device_->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coord, 5 * sizeof(float)); - if (FAILED(hr)) { - ERROR_LOG_REPORT(G3D, "DrawActiveTexture() failed: %08x", (uint32_t)hr); - } - } - - void FramebufferManagerDX9::Bind2DShader() { - device_->SetVertexDeclaration(pFramebufferVertexDecl); - device_->SetPixelShader(pFramebufferPixelShader); - device_->SetVertexShader(pFramebufferVertexShader); - } - LPDIRECT3DSURFACE9 FramebufferManagerDX9::GetOffscreenSurface(LPDIRECT3DSURFACE9 similarSurface, VirtualFramebuffer *vfb) { D3DSURFACE_DESC desc = {}; HRESULT hr = similarSurface->GetDesc(&desc); @@ -249,57 +106,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { return offscreen; } - void FramebufferManagerDX9::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) { - if (!dst->fbo || !src->fbo || !useBufferedRendering_) { - // This can happen if we recently switched from non-buffered. - if (useBufferedRendering_) - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer_Fail"); - return; - } - - float srcXFactor = (float)src->renderScaleFactor; - float srcYFactor = (float)src->renderScaleFactor; - const int srcBpp = src->format == GE_FORMAT_8888 ? 4 : 2; - if (srcBpp != bpp && bpp != 0) { - srcXFactor = (srcXFactor * bpp) / srcBpp; - } - int srcX1 = srcX * srcXFactor; - int srcX2 = (srcX + w) * srcXFactor; - int srcY1 = srcY * srcYFactor; - int srcY2 = (srcY + h) * srcYFactor; - - float dstXFactor = (float)dst->renderScaleFactor; - float dstYFactor = (float)dst->renderScaleFactor; - const int dstBpp = dst->format == GE_FORMAT_8888 ? 4 : 2; - if (dstBpp != bpp && bpp != 0) { - dstXFactor = (dstXFactor * bpp) / dstBpp; - } - int dstX1 = dstX * dstXFactor; - int dstX2 = (dstX + w) * dstXFactor; - int dstY1 = dstY * dstYFactor; - int dstY2 = (dstY + h) * dstYFactor; - - // Direct3D 9 doesn't support rect -> self. - Draw::Framebuffer *srcFBO = src->fbo; - if (src == dst) { - Draw::Framebuffer *tempFBO = GetTempFBO(TempFBO::BLIT, src->renderWidth, src->renderHeight); - bool result = draw_->BlitFramebuffer( - src->fbo, srcX1, srcY1, srcX2, srcY2, - tempFBO, dstX1, dstY1, dstX2, dstY2, - Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST, tag); - if (result) { - srcFBO = tempFBO; - } - } - bool result = draw_->BlitFramebuffer( - srcFBO, srcX1, srcY1, srcX2, srcY2, - dst->fbo, dstX1, dstY1, dstX2, dstY2, - Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST, tag); - if (!result) { - ERROR_LOG_REPORT(G3D, "fbo_blit_color failed in blit (%08x -> %08x)", src->fb_address, dst->fb_address); - } - } - void ConvertFromBGRA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. const u32 *src32 = (const u32 *)src; diff --git a/GPU/Directx9/FramebufferManagerDX9.h b/GPU/Directx9/FramebufferManagerDX9.h index 0d715d75f1..fdb5cf1787 100644 --- a/GPU/Directx9/FramebufferManagerDX9.h +++ b/GPU/Directx9/FramebufferManagerDX9.h @@ -42,14 +42,11 @@ public: void SetTextureCache(TextureCacheDX9 *tc); void SetShaderManager(ShaderManagerDX9 *sm); void SetDrawEngine(DrawEngineDX9 *td); - void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; void DestroyAllFBOs() override; void EndFrame(); - virtual bool NotifyStencilUpload(u32 addr, int size, StencilUpload flags = StencilUpload::NEEDS_CLEAR) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; @@ -59,12 +56,8 @@ public: LPDIRECT3DSURFACE9 GetOffscreenSurface(D3DFORMAT fmt, u32 w, u32 h); protected: - void Bind2DShader() override; void DecimateFBOs() override; - // Used by ReadFramebufferToMemory and later framebuffer block copies - void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) override; - private: void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); @@ -73,16 +66,6 @@ private: LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9 deviceEx_; - LPDIRECT3DVERTEXSHADER9 pFramebufferVertexShader = nullptr; - LPDIRECT3DPIXELSHADER9 pFramebufferPixelShader = nullptr; - LPDIRECT3DVERTEXDECLARATION9 pFramebufferVertexDecl = nullptr; - - LPDIRECT3DPIXELSHADER9 stencilUploadPS_ = nullptr; - LPDIRECT3DVERTEXSHADER9 stencilUploadVS_ = nullptr; - bool stencilUploadFailed_ = false; - - LPDIRECT3DTEXTURE9 nullTex_ = nullptr; - struct OffscreenSurface { LPDIRECT3DSURFACE9 surface; int last_frame_used; @@ -91,4 +74,4 @@ private: std::unordered_map offscreenSurfaces_; }; -}; +} diff --git a/GPU/Directx9/StencilBufferDX9.cpp b/GPU/Directx9/StencilBufferDX9.cpp deleted file mode 100644 index 31ae6aa17d..0000000000 --- a/GPU/Directx9/StencilBufferDX9.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2014- 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 - -#include "Common/GPU/D3D9/D3D9StateCache.h" -#include "Common/GPU/thin3d.h" -#include "Core/Reporting.h" -#include "GPU/Common/StencilCommon.h" -#include "GPU/Directx9/FramebufferManagerDX9.h" -#include "GPU/Directx9/ShaderManagerDX9.h" -#include "GPU/Directx9/TextureCacheDX9.h" - -namespace DX9 { - -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - -static const char *stencil_ps = R"( -sampler tex: register(s0); -// TODO: Don't use fixed registers? Or don't overlap? -float4 u_stencilValue : register(c)" STR(CONST_PS_STENCILVALUE) R"(); -struct PS_IN { - float2 v_texcoord0 : TEXCOORD0; -}; -float roundAndScaleTo255f(in float x) { return floor(x * 255.99); } -float4 main(PS_IN In) : COLOR { - float4 index = tex2D(tex, In.v_texcoord0); - float shifted = roundAndScaleTo255f(index.a) / roundAndScaleTo255f(u_stencilValue.x); - clip(fmod(floor(shifted), 2.0) - 0.99); - return index.aaaa; -} -)"; - -static const char *stencil_vs = R"( -struct VS_IN { - float4 a_position : POSITION; - float2 a_texcoord0 : TEXCOORD0; -}; -struct VS_OUT { - float4 position : POSITION; - float2 v_texcoord0 : TEXCOORD0; -}; -VS_OUT main(VS_IN In) { - VS_OUT Out; - Out.position = In.a_position; - Out.v_texcoord0 = In.a_texcoord0; - return Out; -} -)"; - -bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, StencilUpload flags) { - addr &= 0x3FFFFFFF; - if (!MayIntersectFramebuffer(addr)) { - return false; - } - - VirtualFramebuffer *dstBuffer = 0; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - if (vfb->fb_address == addr) { - dstBuffer = vfb; - } - } - if (!dstBuffer) { - return false; - } - - int values = 0; - u8 usedBits = 0; - - const u8 *src = Memory::GetPointer(addr); - if (!src) { - return false; - } - - switch (dstBuffer->format) { - case GE_FORMAT_565: - // Well, this doesn't make much sense. - return false; - case GE_FORMAT_5551: - usedBits = StencilBits5551(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 2; - break; - case GE_FORMAT_4444: - usedBits = StencilBits4444(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 16; - break; - case GE_FORMAT_8888: - usedBits = StencilBits8888(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 256; - break; - case GE_FORMAT_INVALID: - // Impossible. - break; - } - - if (usedBits == 0) { - if (flags == StencilUpload::STENCIL_IS_ZERO) { - // Common when creating buffers, it's already 0. We're done. - return false; - } - - // Let's not bother with the shader if it's just zero. - dxstate.scissorTest.disable(); - dxstate.colorMask.set(0x8); - // TODO: Verify this clears only stencil/alpha. - device_->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_STENCIL, D3DCOLOR_RGBA(0, 0, 0, 0), 0.0f, 0); - - gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE); - return true; - } - - if (stencilUploadFailed_) { - return false; - } - - // TODO: Helper with logging? - if (!stencilUploadPS_) { - std::string errorMessage; - bool success = CompilePixelShaderD3D9(device_, stencil_ps, &stencilUploadPS_, &errorMessage); - if (!errorMessage.empty()) { - if (success) { - ERROR_LOG(G3D, "Warnings in shader compilation!"); - } else { - ERROR_LOG(G3D, "Error in shader compilation!"); - } - ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); - ERROR_LOG(G3D, "Shader source:\n%s", stencil_ps); - OutputDebugStringUTF8("Messages:\n"); - OutputDebugStringUTF8(errorMessage.c_str()); - Reporting::ReportMessage("D3D error in shader compilation: info: %s / code: %s", errorMessage.c_str(), stencil_ps); - } - if (!success) { - if (stencilUploadPS_) { - stencilUploadPS_->Release(); - } - stencilUploadPS_ = nullptr; - } - } - if (!stencilUploadVS_) { - std::string errorMessage; - bool success = CompileVertexShaderD3D9(device_, stencil_vs, &stencilUploadVS_, &errorMessage); - if (!errorMessage.empty()) { - if (success) { - ERROR_LOG(G3D, "Warnings in shader compilation!"); - } else { - ERROR_LOG(G3D, "Error in shader compilation!"); - } - ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); - ERROR_LOG(G3D, "Shader source:\n%s", stencil_vs); - OutputDebugStringUTF8("Messages:\n"); - OutputDebugStringUTF8(errorMessage.c_str()); - Reporting::ReportMessage("D3D error in shader compilation: info: %s / code: %s", errorMessage.c_str(), stencil_vs); - } - if (!success) { - if (stencilUploadVS_) { - stencilUploadVS_->Release(); - } - stencilUploadVS_ = nullptr; - } - } - if (!stencilUploadPS_ || !stencilUploadVS_) { - stencilUploadFailed_ = true; - return false; - } - - shaderManager_->DirtyLastShader(); - - dxstate.colorMask.set(0x8); - dxstate.stencilTest.enable(); - dxstate.stencilOp.set(D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE); - - u16 w = dstBuffer->renderWidth; - u16 h = dstBuffer->renderHeight; - - if (dstBuffer->fbo) { - // Typically, STENCIL_IS_ZERO means it's already bound. - Draw::RPAction stencilAction = flags == StencilUpload::STENCIL_IS_ZERO ? Draw::RPAction::KEEP : Draw::RPAction::CLEAR; - draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, stencilAction }, "StencilUpload"); - } - D3DVIEWPORT9 vp{ 0, 0, w, h, 0.0f, 1.0f }; - device_->SetViewport(&vp); - - Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight); - if (!tex) - return false; - - // TODO: Ideally, we should clear alpha to zero here (but not RGB.) - - dxstate.stencilFunc.set(D3DCMP_ALWAYS); - dxstate.stencilRef.set(0xFF); - dxstate.stencilCompareMask.set(0xFF); - - float coord[20] = { - -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, - }; - - device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - - device_->SetVertexDeclaration(pFramebufferVertexDecl); - device_->SetPixelShader(stencilUploadPS_); - device_->SetVertexShader(stencilUploadVS_); - - draw_->BindTextures(0, 1, &tex); - - shaderManager_->DirtyLastShader(); - textureCache_->ForgetLastTexture(); - - for (int i = 1; i < values; i += i) { - if (!(usedBits & i)) { - // It's already zero, let's skip it. - continue; - } - if (dstBuffer->format == GE_FORMAT_4444) { - dxstate.stencilWriteMask.set(i | (i << 4)); - const float f[4] = {i * (16.0f / 255.0f)}; - device_->SetPixelShaderConstantF(CONST_PS_STENCILVALUE, f, 1); - } else if (dstBuffer->format == GE_FORMAT_5551) { - dxstate.stencilWriteMask.set(0xFF); - const float f[4] = {i * (128.0f / 255.0f)}; - device_->SetPixelShaderConstantF(CONST_PS_STENCILVALUE, f, 1); - } else { - dxstate.stencilWriteMask.set(i); - const float f[4] = {i * (1.0f / 255.0f)}; - device_->SetPixelShaderConstantF(CONST_PS_STENCILVALUE, f, 1); - } - HRESULT hr = device_->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, coord, 5 * sizeof(float)); - if (FAILED(hr)) { - ERROR_LOG_REPORT(G3D, "Failed to draw stencil bit %x: %08x", i, (uint32_t)hr); - } - } - - tex->Release(); - dxstate.stencilWriteMask.set(0xFF); - dxstate.viewport.restore(); - RebindFramebuffer("RebindFramebuffer stencil"); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - return true; -} - -} // namespace DX9 diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 5554fe21e8..c016771e64 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -40,16 +40,6 @@ struct TransformedVertex; struct DecVtxFormat; -enum { - TEX_SLOT_PSP_TEXTURE = 0, - TEX_SLOT_SHADERBLEND_SRC = 1, - TEX_SLOT_ALPHATEST = 2, - TEX_SLOT_CLUT = 3, - TEX_SLOT_SPLINE_POINTS = 4, - TEX_SLOT_SPLINE_WEIGHTS_U = 5, - TEX_SLOT_SPLINE_WEIGHTS_V = 6, -}; - class TessellationDataTransferGLES : public TessellationDataTransfer { private: GLRTexture *data_tex[3]{}; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 5a14ff051e..74ce72e506 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -39,77 +39,20 @@ #include "GPU/GLES/DrawEngineGLES.h" #include "GPU/GLES/ShaderManagerGLES.h" -static const char tex_fs[] = R"( -#if __VERSION__ >= 130 -#define varying in -#define texture2D texture -#define gl_FragColor fragColor0 -out vec4 fragColor0; -#endif -#ifdef GL_ES -precision mediump float; -#endif -uniform sampler2D sampler0; -varying vec2 v_texcoord0; -void main() { - gl_FragColor = texture2D(sampler0, v_texcoord0); -} -)"; - -static const char basic_vs[] = R"( -#if __VERSION__ >= 130 -#define attribute in -#define varying out -#endif -attribute vec4 a_position; -attribute vec2 a_texcoord0; -varying vec2 v_texcoord0; -void main() { - v_texcoord0 = a_texcoord0; - gl_Position = a_position; -} -)"; - -void FramebufferManagerGLES::CompileDraw2DProgram() { - if (!draw2dprogram_) { - std::string errorString; - static std::string vs_code, fs_code; - vs_code = ApplyGLSLPrelude(basic_vs, GL_VERTEX_SHADER); - fs_code = ApplyGLSLPrelude(tex_fs, GL_FRAGMENT_SHADER); - std::vector shaders; - shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "draw2d")); - shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "draw2d")); - - std::vector queries; - queries.push_back({ &u_draw2d_tex, "u_tex" }); - std::vector initializers; - initializers.push_back({ &u_draw2d_tex, 0 }); - std::vector semantics; - semantics.push_back({ 0, "a_position" }); - semantics.push_back({ 1, "a_texcoord0" }); - draw2dprogram_ = render_->CreateProgram(shaders, semantics, queries, initializers, false, false); - for (auto shader : shaders) - render_->DeleteShader(shader); - } -} - -void FramebufferManagerGLES::Bind2DShader() { - render_->BindProgram(draw2dprogram_); -} - FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render) : FramebufferManagerCommon(draw), render_(render) { needBackBufferYSwap_ = true; - needGLESRebinds_ = true; presentation_->SetLanguage(draw_->GetShaderLanguageDesc().shaderLanguage); - CreateDeviceObjects(); +} + +FramebufferManagerGLES::~FramebufferManagerGLES() { + delete[] convBuf_; } void FramebufferManagerGLES::Init() { FramebufferManagerCommon::Init(); - CompileDraw2DProgram(); } void FramebufferManagerGLES::SetTextureCache(TextureCacheGLES *tc) { @@ -125,118 +68,6 @@ void FramebufferManagerGLES::SetDrawEngine(DrawEngineGLES *td) { drawEngine_ = td; } -void FramebufferManagerGLES::CreateDeviceObjects() { - CompileDraw2DProgram(); - - std::vector entries; - entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, pos) }); - entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, uv) }); - simple2DInputLayout_ = render_->CreateInputLayout(entries); -} - -void FramebufferManagerGLES::DestroyDeviceObjects() { - if (simple2DInputLayout_) { - render_->DeleteInputLayout(simple2DInputLayout_); - simple2DInputLayout_ = nullptr; - } - - if (draw2dprogram_) { - render_->DeleteProgram(draw2dprogram_); - draw2dprogram_ = nullptr; - } - if (stencilUploadProgram_) { - render_->DeleteProgram(stencilUploadProgram_); - stencilUploadProgram_ = nullptr; - } - if (depthDownloadProgram_) { - render_->DeleteProgram(depthDownloadProgram_); - depthDownloadProgram_ = nullptr; - } -} - -FramebufferManagerGLES::~FramebufferManagerGLES() { - DestroyDeviceObjects(); - - delete [] convBuf_; -} - -// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive. -// TODO: This could totally use fbo_blit in many cases. -void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { - float texCoords[8] = { - u0,v0, - u1,v0, - u1,v1, - u0,v1, - }; - - if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { - float temp[8]; - int rotation = 0; - // Vertical and Vertical180 needed swapping after we changed the coordinate system. - switch (uvRotation) { - case ROTATION_LOCKED_HORIZONTAL180: rotation = 4; break; - case ROTATION_LOCKED_VERTICAL: rotation = 6; break; - case ROTATION_LOCKED_VERTICAL180: rotation = 2; break; - } - for (int i = 0; i < 8; i++) { - temp[i] = texCoords[(i + rotation) & 7]; - } - memcpy(texCoords, temp, sizeof(temp)); - } - - float pos[12] = { - x,y,0, - x + w,y,0, - x + w,y + h,0, - x,y + h,0 - }; - - float invDestW = 1.0f / (destW * 0.5f); - float invDestH = 1.0f / (destH * 0.5f); - for (int i = 0; i < 4; i++) { - pos[i * 3] = pos[i * 3] * invDestW - 1.0f; - pos[i * 3 + 1] = pos[i * 3 + 1] * invDestH - 1.0f; - } - - // We always want a plain state here, well, except for when it's used by the stencil stuff... - render_->SetDepth(false, false, GL_ALWAYS); - render_->SetRaster(false, GL_CCW, GL_FRONT, GL_FALSE, GL_FALSE); - if (!(flags & DRAWTEX_KEEP_STENCIL_ALPHA)) { - render_->SetNoBlendAndMask(0xF); - render_->SetStencilDisabled(); - } - - // Upscaling postshaders don't look well with linear - if (flags & DRAWTEX_LINEAR) { - render_->SetTextureSampler(0, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f); - } else { - render_->SetTextureSampler(0, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); - } - - Simple2DVertex verts[4]; - memcpy(verts[0].pos, &pos[0], 12); - memcpy(verts[1].pos, &pos[3], 12); - memcpy(verts[3].pos, &pos[6], 12); - memcpy(verts[2].pos, &pos[9], 12); - memcpy(verts[0].uv, &texCoords[0], 8); - memcpy(verts[1].uv, &texCoords[2], 8); - memcpy(verts[3].uv, &texCoords[4], 8); - memcpy(verts[2].uv, &texCoords[6], 8); - - uint32_t bindOffset; - GLRBuffer *buffer; - - // Workaround: This might accidentally get called from ReportScreen screenshot-taking, and in that case - // the push buffer is not mapped. This only happens if framebuffer blit support is not available. - if (drawEngineGL_->GetPushVertexBuffer()->IsReady()) { - void *dest = drawEngineGL_->GetPushVertexBuffer()->Push(sizeof(verts), &bindOffset, &buffer); - memcpy(dest, verts, sizeof(verts)); - render_->BindVertexBuffer(simple2DInputLayout_, buffer, bindOffset); - render_->Draw(GL_TRIANGLE_STRIP, 0, 4); - } -} - void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { _assert_msg_(nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer"); @@ -249,98 +80,20 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) } } -void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) { - if (!dst->fbo || !src->fbo || !useBufferedRendering_) { - // This can happen if they recently switched from non-buffered. - if (useBufferedRendering_) - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer"); - return; - } - - bool useBlit = gstate_c.Supports(GPU_SUPPORTS_FRAMEBUFFER_BLIT); - - float srcXFactor = useBlit ? src->renderScaleFactor : 1.0f; - float srcYFactor = useBlit ? src->renderScaleFactor : 1.0f; - const int srcBpp = src->format == GE_FORMAT_8888 ? 4 : 2; - if (srcBpp != bpp && bpp != 0) { - srcXFactor = (srcXFactor * bpp) / srcBpp; - } - int srcX1 = srcX * srcXFactor; - int srcX2 = (srcX + w) * srcXFactor; - int srcY1 = srcY * srcYFactor; - int srcY2 = (srcY + h) * srcYFactor; - - float dstXFactor = useBlit ? dst->renderScaleFactor : 1.0f; - float dstYFactor = useBlit ? dst->renderScaleFactor : 1.0f; - const int dstBpp = dst->format == GE_FORMAT_8888 ? 4 : 2; - if (dstBpp != bpp && bpp != 0) { - dstXFactor = (dstXFactor * bpp) / dstBpp; - } - int dstX1 = dstX * dstXFactor; - int dstX2 = (dstX + w) * dstXFactor; - int dstY1 = dstY * dstYFactor; - int dstY2 = (dstY + h) * dstYFactor; - - if (src == dst && srcX == dstX && srcY == dstY) { - // Let's just skip a copy where the destination is equal to the source. - WARN_LOG_REPORT_ONCE(blitSame, G3D, "Skipped blit with equal dst and src"); - return; - } - - if (gstate_c.Supports(GPU_SUPPORTS_COPY_IMAGE)) { - // glBlitFramebuffer can clip, but glCopyImageSubData is more restricted. - // In case the src goes outside, we just skip the optimization in that case. - const bool sameSize = dstX2 - dstX1 == srcX2 - srcX1 && dstY2 - dstY1 == srcY2 - srcY1; - const bool srcInsideBounds = srcX2 <= src->renderWidth && srcY2 <= src->renderHeight; - const bool dstInsideBounds = dstX2 <= dst->renderWidth && dstY2 <= dst->renderHeight; - const bool xOverlap = src == dst && srcX2 > dstX1 && srcX1 < dstX2; - const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2; - if (sameSize && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) { - draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT, tag); - return; - } - } - - if (useBlit) { - draw_->BlitFramebuffer(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST, tag); - } else { - // TODO: Move this workaround out into thin3d, instead of dirtying up the code here. - - draw_->BindFramebufferAsRenderTarget(dst->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag); - draw_->BindFramebufferAsTexture(src->fbo, 0, Draw::FB_COLOR_BIT, 0); - - // Make sure our 2D drawing program is ready. Compiles only if not already compiled. - CompileDraw2DProgram(); - - render_->SetViewport({ 0, 0, (float)dst->renderWidth, (float)dst->renderHeight, 0, 1.0f }); - render_->SetStencilDisabled(); - render_->SetDepth(false, false, GL_ALWAYS); - render_->SetNoBlendAndMask(0xF); - - // The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture. - // Should maybe revamp that interface. - float srcW = src->bufferWidth; - float srcH = src->bufferHeight; - render_->BindProgram(draw2dprogram_); - DrawActiveTexture(dstX1, dstY1, w * dstXFactor, h * dstYFactor, dst->bufferWidth, dst->bufferHeight, srcX1 / srcW, srcY1 / srcH, srcX2 / srcW, srcY2 / srcH, ROTATION_LOCKED_HORIZONTAL, DRAWTEX_NEAREST); - textureCache_->ForgetLastTexture(); - } - - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); -} - void FramebufferManagerGLES::EndFrame() { } void FramebufferManagerGLES::DeviceLost() { FramebufferManagerCommon::DeviceLost(); - DestroyDeviceObjects(); + if (depthDownloadProgram_) { + render_->DeleteProgram(depthDownloadProgram_); + depthDownloadProgram_ = nullptr; + } } void FramebufferManagerGLES::DeviceRestore(Draw::DrawContext *draw) { FramebufferManagerCommon::DeviceRestore(draw); render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - CreateDeviceObjects(); } void FramebufferManagerGLES::Resized() { diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 7e558772d2..20ddd1eab8 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -40,9 +40,6 @@ public: void SetShaderManager(ShaderManagerGLES *sm); void SetDrawEngine(DrawEngineGLES *td); - // x,y,w,h are relative to destW, destH which fill out the target completely. - void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; - virtual void Init() override; void EndFrame(); void Resized() override; @@ -50,23 +47,12 @@ public: void DeviceLost() override; void DeviceRestore(Draw::DrawContext *draw) override; - bool NotifyStencilUpload(u32 addr, int size, StencilUpload flags = StencilUpload::NEEDS_CLEAR) override; - bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; protected: - // Used by ReadFramebufferToMemory and later framebuffer block copies - void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) override; - void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: - void CreateDeviceObjects(); - void DestroyDeviceObjects(); - - void Bind2DShader() override; - void CompileDraw2DProgram(); - void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); GLRenderManager *render_; @@ -74,26 +60,11 @@ private: u8 *convBuf_ = nullptr; u32 convBufSize_ = 0; - GLRProgram *draw2dprogram_ = nullptr; - - GLRProgram *stencilUploadProgram_ = nullptr; - int u_stencilUploadTex = -1; - int u_stencilValue = -1; - GLRProgram *depthDownloadProgram_ = nullptr; int u_depthDownloadTex = -1; int u_depthDownloadFactor = -1; int u_depthDownloadShift = -1; int u_depthDownloadTo8 = -1; - // Cached uniform locs - int u_draw2d_tex = -1; - DrawEngineGLES *drawEngineGL_ = nullptr; - - struct Simple2DVertex { - float pos[3]; - float uv[2]; - }; - GLRInputLayout *simple2DInputLayout_ = nullptr; }; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 57937cd9a8..4be07ecb42 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -185,10 +185,6 @@ void GPU_GLES::CheckGPUFeatures() { } } - if (gl_extensions.ARB_framebuffer_object || gl_extensions.NV_framebuffer_blit || gl_extensions.GLES3) { - features |= GPU_SUPPORTS_FRAMEBUFFER_BLIT | GPU_SUPPORTS_FRAMEBUFFER_BLIT_TO_DEPTH; - } - if ((gl_extensions.gpuVendor == GPU_VENDOR_NVIDIA) || (gl_extensions.gpuVendor == GPU_VENDOR_AMD)) features |= GPU_PREFER_REVERSE_COLOR_ORDER; @@ -198,9 +194,6 @@ void GPU_GLES::CheckGPUFeatures() { if (gl_extensions.EXT_blend_minmax) features |= GPU_SUPPORTS_BLEND_MINMAX; - if (gl_extensions.OES_copy_image || gl_extensions.NV_copy_image || gl_extensions.EXT_copy_image || gl_extensions.ARB_copy_image) - features |= GPU_SUPPORTS_COPY_IMAGE; - if (!gl_extensions.IsGLES) features |= GPU_SUPPORTS_LOGIC_OP; diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 4c35fb4967..665f534137 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -158,7 +158,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, StencilUplo shaderManager_->DirtyLastShader(); - bool useBlit = gstate_c.Supports(GPU_SUPPORTS_FRAMEBUFFER_BLIT); + bool useBlit = draw_->GetDeviceCaps().framebufferDepthBlitSupported; // Our fragment shader (and discard) is slow. Since the source is 1x, we can stencil to 1x. // Then after we're done, we'll just blit it across and stretch it there. diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 49db8ec9c4..ca6cd6a03d 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -338,6 +338,7 @@ + @@ -459,6 +460,7 @@ + @@ -513,7 +515,6 @@ - @@ -525,7 +526,6 @@ - @@ -572,12 +572,6 @@ true true - - true - true - true - true - true true @@ -616,7 +610,6 @@ - diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 2f26b61aa6..db9157dde0 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -264,6 +264,9 @@ Software + + Common + @@ -356,9 +359,6 @@ Common - - DirectX9 - Common @@ -416,9 +416,6 @@ GLES - - GLES - GLES @@ -455,9 +452,6 @@ D3D11 - - D3D11 - Common @@ -473,9 +467,6 @@ Debugger - - Vulkan - Common @@ -530,6 +521,9 @@ Software + + Common + diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 9fc627fc75..03ab2629e8 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2933,7 +2933,7 @@ void GPUCommon::NotifyVideoUpload(u32 addr, int size, int width, int format) { bool GPUCommon::PerformStencilUpload(u32 dest, int size) { if (framebufferManager_->MayIntersectFramebuffer(dest)) { - framebufferManager_->NotifyStencilUpload(dest, size); + framebufferManager_->PerformStencilUpload(dest, size); return true; } return false; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 2d8db478b9..bc0ede6d1c 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -479,15 +479,13 @@ enum { GPU_SUPPORTS_32BIT_INT_FSHADER = FLAG_BIT(15), GPU_SUPPORTS_DEPTH_TEXTURE = FLAG_BIT(16), GPU_SUPPORTS_ACCURATE_DEPTH = FLAG_BIT(17), - // Free bit: 18 - GPU_SUPPORTS_COPY_IMAGE = FLAG_BIT(19), + // Free bits: 18-19 GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH = FLAG_BIT(20), GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT = FLAG_BIT(21), GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT = FLAG_BIT(22), GPU_ROUND_DEPTH_TO_16BIT = FLAG_BIT(23), // Can be disabled either per game or if we use a real 16-bit depth buffer GPU_SUPPORTS_TEXTURE_LOD_CONTROL = FLAG_BIT(24), - GPU_SUPPORTS_FRAMEBUFFER_BLIT = FLAG_BIT(26), - GPU_SUPPORTS_FRAMEBUFFER_BLIT_TO_DEPTH = FLAG_BIT(27), + // Free bits: 25-27 GPU_SUPPORTS_TEXTURE_NPOT = FLAG_BIT(28), GPU_SUPPORTS_CLIP_DISTANCE = FLAG_BIT(29), GPU_SUPPORTS_CULL_DISTANCE = FLAG_BIT(30), diff --git a/GPU/Vulkan/FramebufferManagerVulkan.cpp b/GPU/Vulkan/FramebufferManagerVulkan.cpp index 707c20da6e..b3a63848e3 100644 --- a/GPU/Vulkan/FramebufferManagerVulkan.cpp +++ b/GPU/Vulkan/FramebufferManagerVulkan.cpp @@ -49,38 +49,9 @@ using namespace PPSSPP_VK; -static const char tex_fs[] = R"(#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -layout (binding = 0) uniform sampler2D sampler0; -layout (location = 0) in vec2 v_texcoord0; -layout (location = 0) out vec4 fragColor; -void main() { - fragColor = texture(sampler0, v_texcoord0); -} -)"; - -static const char tex_vs[] = R"(#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -layout (location = 0) in vec3 a_position; -layout (location = 1) in vec2 a_texcoord0; -layout (location = 0) out vec2 v_texcoord0; -out gl_PerVertex { vec4 gl_Position; }; -void main() { - v_texcoord0 = a_texcoord0; - gl_Position = vec4(a_position, 1.0); -} -)"; - FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw) : FramebufferManagerCommon(draw) { presentation_->SetLanguage(GLSL_VULKAN); - - InitDeviceObjects(); - - // After a blit we do need to rebind for the VulkanRenderManager to know what to do. - needGLESRebinds_ = true; } FramebufferManagerVulkan::~FramebufferManagerVulkan() { @@ -99,55 +70,6 @@ void FramebufferManagerVulkan::SetDrawEngine(DrawEngineVulkan *td) { drawEngine_ = td; } -void FramebufferManagerVulkan::InitDeviceObjects() { - VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); - std::string fs_errors, vs_errors; - fsBasicTex_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, tex_fs, &fs_errors); - vsBasicTex_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_VERTEX_BIT, tex_vs, &vs_errors); - _assert_(fsBasicTex_ != VK_NULL_HANDLE); - _assert_(vsBasicTex_ != VK_NULL_HANDLE); - - VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samp.magFilter = VK_FILTER_NEAREST; - samp.minFilter = VK_FILTER_NEAREST; - VkResult res = vkCreateSampler(vulkan->GetDevice(), &samp, nullptr, &nearestSampler_); - _assert_(res == VK_SUCCESS); - samp.magFilter = VK_FILTER_LINEAR; - samp.minFilter = VK_FILTER_LINEAR; - res = vkCreateSampler(vulkan->GetDevice(), &samp, nullptr, &linearSampler_); - _assert_(res == VK_SUCCESS); -} - -void FramebufferManagerVulkan::DestroyDeviceObjects() { - if (!draw_) - return; - VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); - if (fsBasicTex_ != VK_NULL_HANDLE) { - vulkan2D_->PurgeFragmentShader(fsBasicTex_); - vulkan->Delete().QueueDeleteShaderModule(fsBasicTex_); - } - if (vsBasicTex_ != VK_NULL_HANDLE) { - vulkan2D_->PurgeVertexShader(vsBasicTex_); - vulkan->Delete().QueueDeleteShaderModule(vsBasicTex_); - } - if (stencilFs_ != VK_NULL_HANDLE) { - vulkan2D_->PurgeFragmentShader(stencilFs_); - vulkan->Delete().QueueDeleteShaderModule(stencilFs_); - } - if (stencilVs_ != VK_NULL_HANDLE) { - vulkan2D_->PurgeVertexShader(stencilVs_); - vulkan->Delete().QueueDeleteShaderModule(stencilVs_); - } - - if (linearSampler_ != VK_NULL_HANDLE) - vulkan->Delete().QueueDeleteSampler(linearSampler_); - if (nearestSampler_ != VK_NULL_HANDLE) - vulkan->Delete().QueueDeleteSampler(nearestSampler_); -} - void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) { int mask = 0; // The Clear detection takes care of doing a regular draw instead if separate masking @@ -170,143 +92,6 @@ void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, boo } } -void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { - float texCoords[8] = { - u0,v0, - u1,v0, - u1,v1, - u0,v1, - }; - - if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { - float temp[8]; - int rotation = 0; - switch (uvRotation) { - case ROTATION_LOCKED_HORIZONTAL180: rotation = 4; break; - case ROTATION_LOCKED_VERTICAL: rotation = 2; break; - case ROTATION_LOCKED_VERTICAL180: rotation = 6; break; - } - for (int i = 0; i < 8; i++) { - temp[i] = texCoords[(i + rotation) & 7]; - } - memcpy(texCoords, temp, sizeof(temp)); - } - - Vulkan2D::Vertex vtx[4] = { - {x, y, 0, texCoords[0], texCoords[1]}, - {x + w, y, 0, texCoords[2], texCoords[3]}, - {x, y + h, 0, texCoords[6], texCoords[7]}, - {x + w, y + h, 0, texCoords[4], texCoords[5]}, - }; - - float invDestW = 1.0f / (destW * 0.5f); - float invDestH = 1.0f / (destH * 0.5f); - for (int i = 0; i < 4; i++) { - vtx[i].x = vtx[i].x * invDestW - 1.0f; - vtx[i].y = vtx[i].y * invDestH - 1.0f; - } - - if ((flags & DRAWTEX_TO_BACKBUFFER) && g_display_rotation != DisplayRotation::ROTATE_0) { - for (int i = 0; i < 4; i++) { - Lin::Vec3 v(vtx[i].x, vtx[i].y, 0.0f); - // backwards notation, should fix that... - v = v * g_display_rot_matrix; - vtx[i].x = v.x; - vtx[i].y = v.y; - } - } - - draw_->FlushState(); - - // TODO: Should probably use draw_ directly and not go low level - - VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - - VkImageView view = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); - VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); - VkBuffer vbuffer; - VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer); - renderManager->BindPipeline(cur2DPipeline_, (PipelineFlags)0); - renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); -} - -void FramebufferManagerVulkan::Bind2DShader() { - VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); - cur2DPipeline_ = vulkan2D_->GetPipeline(rp, vsBasicTex_, fsBasicTex_); -} - -void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) { - if (!dst->fbo || !src->fbo || !useBufferedRendering_) { - // This can happen if they recently switched from non-buffered. - if (useBufferedRendering_) { - draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer_Fail"); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); - } - return; - } - - // Perform a little bit of clipping first. - // Block transfer coords are unsigned so I don't think we need to clip on the left side.. - if (dstX + w > dst->bufferWidth) { - w -= dstX + w - dst->bufferWidth; - } - if (dstY + h > dst->bufferHeight) { - h -= dstY + h - dst->bufferHeight; - } - if (srcX + w > src->bufferWidth) { - w -= srcX + w - src->bufferWidth; - } - if (srcY + h > src->bufferHeight) { - h -= srcY + h - src->bufferHeight; - } - - if (w <= 0 || h <= 0) { - // The whole rectangle got clipped. - return; - } - - float srcXFactor = (float)src->renderScaleFactor; - float srcYFactor = (float)src->renderScaleFactor; - - // Some games use wrong-format block transfers. Simulate that. - const int srcBpp = src->format == GE_FORMAT_8888 ? 4 : 2; - if (srcBpp != bpp && bpp != 0) { - srcXFactor = (srcXFactor * bpp) / srcBpp; - } - int srcX1 = srcX * srcXFactor; - int srcX2 = (srcX + w) * srcXFactor; - int srcY1 = srcY * srcYFactor; - int srcY2 = (srcY + h) * srcYFactor; - - float dstXFactor = (float)dst->renderScaleFactor; - float dstYFactor = (float)dst->renderScaleFactor; - const int dstBpp = dst->format == GE_FORMAT_8888 ? 4 : 2; - if (dstBpp != bpp && bpp != 0) { - dstXFactor = (dstXFactor * bpp) / dstBpp; - } - int dstX1 = dstX * dstXFactor; - int dstX2 = (dstX + w) * dstXFactor; - int dstY1 = dstY * dstYFactor; - int dstY2 = (dstY + h) * dstYFactor; - - if (src == dst && srcX == dstX && srcY == dstY) { - // Let's just skip a copy where the destination is equal to the source. - WARN_LOG_REPORT_ONCE(blitSame, G3D, "Skipped blit with equal dst and src"); - return; - } - - const bool sameSize = dstX2 - dstX1 == srcX2 - srcX1 && dstY2 - dstY1 == srcY2 - srcY1; - const bool srcInsideBounds = srcX2 <= src->renderWidth && srcY2 <= src->renderHeight; - const bool dstInsideBounds = dstX2 <= dst->renderWidth && dstY2 <= dst->renderHeight; - const bool xOverlap = src == dst && srcX2 > dstX1 && srcX1 < dstX2; - const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2; - if (sameSize && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) { - draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT, tag); - } else { - draw_->BlitFramebuffer(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST, tag); - } -} - void FramebufferManagerVulkan::BeginFrameVulkan() { BeginFrame(); } @@ -315,11 +100,9 @@ void FramebufferManagerVulkan::EndFrame() { } void FramebufferManagerVulkan::DeviceLost() { - DestroyDeviceObjects(); FramebufferManagerCommon::DeviceLost(); } void FramebufferManagerVulkan::DeviceRestore(Draw::DrawContext *draw) { FramebufferManagerCommon::DeviceRestore(draw); - InitDeviceObjects(); } diff --git a/GPU/Vulkan/FramebufferManagerVulkan.h b/GPU/Vulkan/FramebufferManagerVulkan.h index 6e8725398a..7974aa6f5b 100644 --- a/GPU/Vulkan/FramebufferManagerVulkan.h +++ b/GPU/Vulkan/FramebufferManagerVulkan.h @@ -41,10 +41,6 @@ public: void SetShaderManager(ShaderManagerVulkan *sm); void SetDrawEngine(DrawEngineVulkan *td); void SetVulkan2D(Vulkan2D *vk2d) { vulkan2D_ = vk2d; } - void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; } - - // x,y,w,h are relative to destW, destH which fill out the target completely. - void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; void BeginFrameVulkan(); // there's a BeginFrame in the base class, which this calls void EndFrame(); @@ -52,44 +48,11 @@ public: void DeviceLost() override; void DeviceRestore(Draw::DrawContext *draw) override; - bool NotifyStencilUpload(u32 addr, int size, StencilUpload flags = StencilUpload::NEEDS_CLEAR) override; - // If within a render pass, this will just issue a regular clear. If beginning a new render pass, // do that. void NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth); -protected: - void Bind2DShader() override; - - // Used by ReadFramebufferToMemory and later framebuffer block copies - void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) override; - private: - void InitDeviceObjects(); - void DestroyDeviceObjects(); - - // Used to keep track of command buffers here but have moved all that into Thin3D. - - VulkanPushBuffer *push_; - - enum { - MAX_COMMAND_BUFFERS = 32, - }; - - VkPipelineCache pipelineCache2D_; - - // Basic shaders - VkShaderModule fsBasicTex_ = VK_NULL_HANDLE; - VkShaderModule vsBasicTex_ = VK_NULL_HANDLE; - - VkShaderModule stencilVs_ = VK_NULL_HANDLE; - VkShaderModule stencilFs_ = VK_NULL_HANDLE; - - VkPipeline cur2DPipeline_ = VK_NULL_HANDLE; - - VkSampler linearSampler_; - VkSampler nearestSampler_; - // Simple 2D drawing engine. Vulkan2D *vulkan2D_; }; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 96c2841f3d..2c5a50aebc 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -232,19 +232,13 @@ void GPU_Vulkan::CheckGPUFeatures() { // Mandatory features on Vulkan, which may be checked in "centralized" code features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL; - features |= GPU_SUPPORTS_FRAMEBUFFER_BLIT; features |= GPU_SUPPORTS_BLEND_MINMAX; - features |= GPU_SUPPORTS_COPY_IMAGE; features |= GPU_SUPPORTS_TEXTURE_NPOT; features |= GPU_SUPPORTS_INSTANCE_RENDERING; features |= GPU_SUPPORTS_VERTEX_TEXTURE_FETCH; features |= GPU_SUPPORTS_TEXTURE_FLOAT; features |= GPU_SUPPORTS_DEPTH_TEXTURE; - if (vulkan->GetDeviceInfo().canBlitToPreferredDepthStencilFormat) { - features |= GPU_SUPPORTS_FRAMEBUFFER_BLIT_TO_DEPTH; - } - auto &enabledFeatures = vulkan->GetDeviceFeatures().enabled; if (enabledFeatures.depthClamp) { features |= GPU_SUPPORTS_DEPTH_CLAMP; @@ -333,7 +327,6 @@ void GPU_Vulkan::BeginHostFrame() { frame.push_->Begin(vulkan); framebufferManagerVulkan_->BeginFrameVulkan(); - framebufferManagerVulkan_->SetPushBuffer(frameData_[curFrame].push_); depalShaderCache_.SetPushBuffer(frameData_[curFrame].push_); textureCacheVulkan_->SetPushBuffer(frameData_[curFrame].push_); diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp deleted file mode 100644 index 6998455aef..0000000000 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) 2014- 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 "Common/GPU/thin3d.h" -#include "Common/GPU/Vulkan/VulkanRenderManager.h" - -#include "Common/Log.h" -#include "Core/Config.h" -#include "Core/Reporting.h" -#include "GPU/Common/StencilCommon.h" -#include "GPU/Vulkan/FramebufferManagerVulkan.h" -#include "GPU/Vulkan/ShaderManagerVulkan.h" -#include "GPU/Vulkan/TextureCacheVulkan.h" -#include "GPU/Vulkan/VulkanUtil.h" - -// This shader references gl_FragDepth to prevent early fragment tests. -// They shouldn't happen since it uses discard, but Adreno detects that incorrectly - see #10634. -// This only affected the 5xx generation and was fixed sometime before driver version 512.384.0.0 (whatever that means). -// Strangely, this same code appears to fix a different issue on Exynos instead so now it's enabled on Mali as well. -static const char *stencil_fs_adreno = R"(#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -#extension GL_ARB_conservative_depth : enable -layout (depth_unchanged) out float gl_FragDepth; -layout (binding = 0) uniform sampler2D tex; -layout (push_constant) uniform params { - int u_stencilValue; -}; -layout (location = 0) in vec2 v_texcoord0; -layout (location = 0) out vec4 fragColor0; - -void main() { - gl_FragDepth = gl_FragCoord.z; - - if (u_stencilValue == 0) { - fragColor0 = vec4(0.0); - } else { - vec4 index = texture(tex, v_texcoord0); - int indexBits = int(floor(index.a * 255.99)) & 0xFF; - if ((indexBits & u_stencilValue) == 0) - discard; - fragColor0 = index.aaaa; - } -} -)"; - -static const char *stencil_fs = R"(#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -layout (binding = 0) uniform sampler2D tex; -layout (push_constant) uniform params { - int u_stencilValue; -}; -layout (location = 0) in vec2 v_texcoord0; -layout (location = 0) out vec4 fragColor0; - -void main() { - if (u_stencilValue == 0) { - fragColor0 = vec4(0.0); - } else { - vec4 index = texture(tex, v_texcoord0); - int indexBits = int(floor(index.a * 255.99)) & 0xFF; - if ((indexBits & u_stencilValue) == 0) - discard; - fragColor0 = index.aaaa; - } -} -)"; - - -static const char stencil_vs[] = R"(#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -layout (location = 0) out vec2 v_texcoord0; -out gl_PerVertex { vec4 gl_Position; }; -void main() { - int id = gl_VertexIndex; - v_texcoord0.x = (id == 2) ? 2.0 : 0.0; - v_texcoord0.y = (id == 1) ? 2.0 : 0.0; - gl_Position = vec4(v_texcoord0 * vec2(2.0, 2.0) + vec2(-1.0, -1.0), 0.0, 1.0); -} -)"; - -// In Vulkan we should be able to simply copy the stencil data directly to a stencil buffer without -// messing about with bitplane textures and the like. Or actually, maybe not... Let's start with -// the traditional approach. -bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, StencilUpload flags) { - addr &= 0x3FFFFFFF; - if (!MayIntersectFramebuffer(addr)) { - return false; - } - - VirtualFramebuffer *dstBuffer = 0; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - if (vfb->fb_address == addr) { - dstBuffer = vfb; - } - } - if (!dstBuffer) { - return false; - } - - int values = 0; - u8 usedBits = 0; - - const u8 *src = Memory::GetPointer(addr); - if (!src) { - return false; - } - - switch (dstBuffer->format) { - case GE_FORMAT_565: - // Well, this doesn't make much sense. - return false; - case GE_FORMAT_5551: - usedBits = StencilBits5551(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 2; - break; - case GE_FORMAT_4444: - usedBits = StencilBits4444(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 16; - break; - case GE_FORMAT_8888: - usedBits = StencilBits8888(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); - values = 256; - break; - case GE_FORMAT_INVALID: - case GE_FORMAT_DEPTH16: - // Inconceivable. - _assert_(false); - break; - } - - std::string error; - if (!stencilVs_) { - VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); - - const char *stencil_fs_source = stencil_fs; - // See comment above the stencil_fs_adreno definition. - u32 vendorID = vulkan->GetPhysicalDeviceProperties().properties.vendorID; - if (g_Config.bVendorBugChecksEnabled && (draw_->GetBugs().Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL) || vendorID == VULKAN_VENDOR_ARM)) - stencil_fs_source = stencil_fs_adreno; - - stencilVs_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_VERTEX_BIT, stencil_vs, &error); - stencilFs_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, stencil_fs_source, &error); - } - VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS); - - VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - - shaderManager_->DirtyLastShader(); - textureCache_->ForgetLastTexture(); - - u16 w = dstBuffer->renderWidth; - u16 h = dstBuffer->renderHeight; - Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight); - if (!tex) - return false; - - if (dstBuffer->fbo) { - // Typically, STENCIL_IS_ZERO means it's already bound, so this bind will be optimized away. - draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::DONT_CARE }, "Stencil"); - } else { - // something is wrong... - } - - VkPipeline pipeline = vulkan2D_->GetPipeline(rp, stencilVs_, stencilFs_, false, Vulkan2D::VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS); - renderManager->BindPipeline(pipeline, PIPELINE_FLAG_USES_DEPTH_STENCIL); - renderManager->SetViewport({ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f }); - renderManager->SetScissor(0, 0, (int)w, (int)h); - - draw_->BindTextures(0, 1, &tex); - VkImageView drawPixelsImageView = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); - VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(drawPixelsImageView, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); - - // Note: Even with skipZero, we don't necessarily start framebuffers at 0 in Vulkan. Clear anyway. - // Not an actual clear, because we need to draw to alpha only as well. - uint32_t value = 0; - renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4, &value); - renderManager->SetStencilParams(0xFF, 0xFF, 0x00); - renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, VK_NULL_HANDLE, 0, 3); // full screen triangle - - for (int i = 1; i < values; i += i) { - if (!(usedBits & i)) { - // It's already zero, let's skip it. - continue; - } - - // These are the stencil bits that will be written. We discard when the bit doesn't match. - uint8_t writeMask = 0; - // This is the value to test the texture alpha against in the shader. - uint32_t value = 0; - if (dstBuffer->format == GE_FORMAT_4444) { - writeMask = i | (i << 4); - value = i * 16; - } else if (dstBuffer->format == GE_FORMAT_5551) { - writeMask = 0xFF; - value = i * 128; - } else { - writeMask = i; - value = i; - } - renderManager->SetStencilParams(writeMask, 0xFF, 0xFF); - // Need to specify both VERTEX and FRAGMENT bits here since that's what we set up in the pipeline layout, and we need - // that for the post shaders. There's probably not really a cost to this. - renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4, &value); - renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, VK_NULL_HANDLE, 0, 3); // full screen triangle - } - - tex->Release(); - RebindFramebuffer("RebindFramebuffer - NotifyStencilUpload"); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - return true; -} diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index dc2d0ecf87..50a2a48cb1 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -597,7 +597,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VkFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.hardwareScaling) { + if (plan.scaleFactor > 1) { + // Whether hardware or software scaling, this is the dest format. dstFmt = VULKAN_8888_FORMAT; } diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index d1e9ec4366..0777e28251 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -415,6 +415,7 @@ + @@ -440,6 +441,7 @@ + @@ -650,6 +652,7 @@ + @@ -675,6 +678,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 466e7867e9..b1f23b19fb 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -539,6 +539,9 @@ Dialog + + Dialog + Dialog @@ -1112,6 +1115,9 @@ + + HLE + @@ -1539,6 +1545,9 @@ Dialog + + Dialog + Dialog @@ -1755,6 +1764,9 @@ + + HLE + diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 0d09bebc50..acda27f60f 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -380,6 +380,7 @@ + @@ -439,6 +440,7 @@ + @@ -470,7 +472,6 @@ - diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters index 84ab7ec547..9dd217c598 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters @@ -30,7 +30,6 @@ - @@ -58,6 +57,7 @@ + @@ -117,5 +117,6 @@ + \ No newline at end of file diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 767f2573fa..6426e6309b 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -184,14 +184,14 @@ - PPSSPP_UWP_TemporaryKey.pfx True False Never - b5e422213fb26042db3327f1102c4f9b86c27b1c + 169879A9D7A20F22B0DD435921035E3E6D5996A4 False 1 OnApplicationRun + True @@ -596,6 +596,13 @@ true Document + + true + Document + + + true + true diff --git a/UWP/UWP.vcxproj.filters b/UWP/UWP.vcxproj.filters index 4fcd4bd2ee..209fd4951f 100644 --- a/UWP/UWP.vcxproj.filters +++ b/UWP/UWP.vcxproj.filters @@ -88,9 +88,6 @@ Content - - Content - Content @@ -103,12 +100,17 @@ Assets + + Assets + + + Content + - Content @@ -365,6 +367,9 @@ Content\themes + + Content + @@ -376,4 +381,4 @@ Content - \ No newline at end of file + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index ae109b91d1..a0c9774d37 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -133,7 +133,6 @@ VULKAN_FILES := \ $(SRC)/GPU/Vulkan/PipelineManagerVulkan.cpp \ $(SRC)/GPU/Vulkan/ShaderManagerVulkan.cpp \ $(SRC)/GPU/Vulkan/StateMappingVulkan.cpp \ - $(SRC)/GPU/Vulkan/StencilBufferVulkan.cpp \ $(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \ $(SRC)/GPU/Vulkan/DepalettizeShaderVulkan.cpp \ $(SRC)/GPU/Vulkan/VulkanUtil.cpp \ @@ -320,6 +319,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/GPUState.cpp \ $(SRC)/GPU/GeConstants.cpp \ $(SRC)/GPU/GeDisasm.cpp \ + $(SRC)/GPU/Common/Draw2D.cpp \ $(SRC)/GPU/Common/DepalettizeShaderCommon.cpp \ $(SRC)/GPU/Common/FragmentShaderGenerator.cpp \ $(SRC)/GPU/Common/FramebufferManagerCommon.cpp \ @@ -351,7 +351,6 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/GLES/DepalettizeShaderGLES.cpp \ $(SRC)/GPU/GLES/DepthBufferGLES.cpp \ $(SRC)/GPU/GLES/GPU_GLES.cpp.arm \ - $(SRC)/GPU/GLES/StencilBufferGLES.cpp \ $(SRC)/GPU/GLES/TextureCacheGLES.cpp.arm \ $(SRC)/GPU/GLES/DrawEngineGLES.cpp.arm \ $(SRC)/GPU/GLES/StateMappingGLES.cpp.arm \ @@ -440,6 +439,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \ $(SRC)/Core/Dialog/PSPMsgDialog.cpp \ $(SRC)/Core/Dialog/PSPNetconfDialog.cpp \ + $(SRC)/Core/Dialog/PSPNpSigninDialog.cpp \ $(SRC)/Core/Dialog/PSPOskDialog.cpp \ $(SRC)/Core/Dialog/PSPScreenshotDialog.cpp \ $(SRC)/Core/Dialog/PSPPlaceholderDialog.cpp \ @@ -518,6 +518,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/scePspNpDrm_user.cpp \ $(SRC)/Core/HLE/sceGameUpdate.cpp \ $(SRC)/Core/HLE/sceNp.cpp \ + $(SRC)/Core/HLE/sceNp2.cpp \ $(SRC)/Core/HLE/scePauth.cpp \ $(SRC)/Core/FileSystems/BlobFileSystem.cpp \ $(SRC)/Core/FileSystems/BlockDevices.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 696987c6fd..9143600e09 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -315,6 +315,7 @@ SOURCES_C +=\ $(COMMONDIR)/GPU/OpenGL/gl3stub.c SOURCES_CXX += \ + $(GPUCOMMONDIR)/Draw2D.cpp \ $(GPUCOMMONDIR)/VertexDecoderCommon.cpp \ $(GPUCOMMONDIR)/GPUStateUtils.cpp \ $(GPUCOMMONDIR)/DrawEngineCommon.cpp \ @@ -369,7 +370,6 @@ SOURCES_CXX += \ $(GPUDIR)/GLES/TextureCacheGLES.cpp \ $(GPUDIR)/GLES/ShaderManagerGLES.cpp \ $(GPUDIR)/GLES/StateMappingGLES.cpp \ - $(GPUDIR)/GLES/StencilBufferGLES.cpp \ $(EXTDIR)/glslang/OGLCompilersDLL/InitializeDll.cpp \ $(EXTDIR)/glslang/glslang/GenericCodeGen/CodeGen.cpp \ $(EXTDIR)/glslang/glslang/GenericCodeGen/Link.cpp \ @@ -459,6 +459,7 @@ SOURCES_CXX += \ $(COREDIR)/Dialog/PSPGamedataInstallDialog.cpp \ $(COREDIR)/Dialog/PSPMsgDialog.cpp \ $(COREDIR)/Dialog/PSPNetconfDialog.cpp \ + $(COREDIR)/Dialog/PSPNpSigninDialog.cpp \ $(COREDIR)/Dialog/PSPOskDialog.cpp \ $(COREDIR)/Dialog/PSPSaveDialog.cpp \ $(COREDIR)/Dialog/PSPScreenshotDialog.cpp \ @@ -546,6 +547,7 @@ SOURCES_CXX += \ $(COREDIR)/HLE/sceVaudio.cpp \ $(COREDIR)/HLE/scePspNpDrm_user.cpp \ $(COREDIR)/HLE/sceNp.cpp \ + $(COREDIR)/HLE/sceNp2.cpp \ $(COREDIR)/HLE/scePauth.cpp \ $(COREDIR)/HLE/sceUsbGps.cpp \ $(COREDIR)/HW/BufferQueue.cpp \ @@ -732,7 +734,6 @@ SOURCES_CXX += \ $(GPUDIR)/Vulkan/PipelineManagerVulkan.cpp \ $(GPUDIR)/Vulkan/ShaderManagerVulkan.cpp \ $(GPUDIR)/Vulkan/StateMappingVulkan.cpp \ - $(GPUDIR)/Vulkan/StencilBufferVulkan.cpp \ $(GPUDIR)/Vulkan/TextureCacheVulkan.cpp \ $(GPUDIR)/Vulkan/VulkanUtil.cpp \ $(LIBRETRODIR)/LibretroVulkanContext.cpp \ @@ -747,7 +748,6 @@ SOURCES_CXX += \ $(GPUDIR)/Directx9/GPU_DX9.cpp \ $(GPUDIR)/Directx9/ShaderManagerDX9.cpp \ $(GPUDIR)/Directx9/StateMappingDX9.cpp \ - $(GPUDIR)/Directx9/StencilBufferDX9.cpp \ $(GPUDIR)/Directx9/TextureCacheDX9.cpp SOURCES_CXX += \ @@ -758,7 +758,6 @@ SOURCES_CXX += \ $(GPUDIR)/D3D11/D3D11Util.cpp \ $(GPUDIR)/D3D11/ShaderManagerD3D11.cpp \ $(GPUDIR)/D3D11/StateMappingD3D11.cpp \ - $(GPUDIR)/D3D11/StencilBufferD3D11.cpp \ $(GPUDIR)/D3D11/TextureCacheD3D11.cpp \ $(LIBRETRODIR)/LibretroD3D11Context.cpp diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp index ef4116cfaf..8583a18c60 100644 --- a/unittest/TestShaderGenerators.cpp +++ b/unittest/TestShaderGenerators.cpp @@ -13,6 +13,7 @@ #include "GPU/Common/FragmentShaderGenerator.h" #include "GPU/Common/VertexShaderGenerator.h" #include "GPU/Common/ReinterpretFramebuffer.h" +#include "GPU/Common/StencilCommon.h" #if PPSSPP_PLATFORM(WINDOWS) #include "GPU/D3D11/D3D11Util.h" @@ -89,18 +90,18 @@ bool GenerateVShader(VShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs } } -bool TestCompileShader(const char *buffer, ShaderLanguage lang, bool vertex, std::string *errorMessage) { +bool TestCompileShader(const char *buffer, ShaderLanguage lang, ShaderStage stage, std::string *errorMessage) { std::vector spirv; switch (lang) { #if PPSSPP_PLATFORM(WINDOWS) case ShaderLanguage::HLSL_D3D11: { - auto output = CompileShaderToBytecodeD3D11(buffer, strlen(buffer), vertex ? "vs_4_0" : "ps_4_0", 0); + auto output = CompileShaderToBytecodeD3D11(buffer, strlen(buffer), stage == ShaderStage::Vertex ? "vs_4_0" : "ps_4_0", 0); return !output.empty(); } case ShaderLanguage::HLSL_D3D9: { - LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, vertex ? "vs_2_0" : "ps_2_0", errorMessage); + LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_2_0" : "ps_2_0", errorMessage); if (blob) { blob->Release(); return true; @@ -111,11 +112,11 @@ bool TestCompileShader(const char *buffer, ShaderLanguage lang, bool vertex, std #endif case ShaderLanguage::GLSL_VULKAN: - return GLSLtoSPV(vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::VULKAN, spirv, errorMessage); + return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::VULKAN, spirv, errorMessage); case ShaderLanguage::GLSL_1xx: - return GLSLtoSPV(vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GL140, spirv, errorMessage); + return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GL140, spirv, errorMessage); case ShaderLanguage::GLSL_3xx: - return GLSLtoSPV(vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GLES300, spirv, errorMessage); + return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GLES300, spirv, errorMessage); default: return false; } @@ -159,6 +160,8 @@ const char *ShaderLanguageToString(ShaderLanguage lang) { } bool TestReinterpretShaders() { + Draw::Bugs bugs; + ShaderLanguage languages[] = { #if PPSSPP_PLATFORM(WINDOWS) ShaderLanguage::HLSL_D3D11, @@ -183,7 +186,7 @@ bool TestReinterpretShaders() { failed = true; } else { std::string errorMessage; - if (!TestCompileShader(buffer, languages[k], true, &errorMessage)) { + if (!TestCompileShader(buffer, languages[k], ShaderStage::Vertex, &errorMessage)) { printf("Error compiling fragment shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str()); failed = true; return false; @@ -197,6 +200,7 @@ bool TestReinterpretShaders() { printf("=== %s ===\n\n", ShaderLanguageToString(languages[k])); ShaderLanguageDesc desc(languages[k]); + std::string errorMessage; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -206,8 +210,7 @@ bool TestReinterpretShaders() { printf("Failed!\n%s\n", buffer); failed = true; } else { - std::string errorMessage; - if (!TestCompileShader(buffer, languages[k], false, &errorMessage)) { + if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) { printf("Error compiling fragment shader %d:\n\n%s\n\n%s\n", (int)j, LineNumberString(buffer).c_str(), errorMessage.c_str()); failed = true; return false; @@ -217,8 +220,55 @@ bool TestReinterpretShaders() { } } } - } + + delete[] buffer; + return !failed; +} + +bool TestStencilShaders() { + Draw::Bugs bugs; + + ShaderLanguage languages[] = { +#if PPSSPP_PLATFORM(WINDOWS) + ShaderLanguage::HLSL_D3D9, + ShaderLanguage::HLSL_D3D11, +#endif + ShaderLanguage::GLSL_VULKAN, + ShaderLanguage::GLSL_3xx, + }; + + char *buffer = new char[65536]; + + bool failed = false; + + for (int k = 0; k < ARRAY_SIZE(languages); k++) { + printf("=== %s ===\n\n", ShaderLanguageToString(languages[k])); + + ShaderLanguageDesc desc(languages[k]); + std::string errorMessage; + + // Generate all despite failures - it's only 6. + GenerateStencilFs(buffer, desc, bugs); + if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) { + printf("Error compiling stencil shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str()); + failed = true; + return false; + } else { + printf("===\n%s\n===\n", buffer); + } + + GenerateStencilVs(buffer, desc); + if (!TestCompileShader(buffer, languages[k], ShaderStage::Vertex, &errorMessage)) { + printf("Error compiling stencil shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str()); + failed = true; + return false; + } else { + printf("===\n%s\n===\n", buffer); + } + } + + delete[] buffer; return !failed; } @@ -281,7 +331,7 @@ bool TestVertexShaders() { for (int j = 0; j < numLanguages; j++) { if (generateSuccess[j]) { std::string errorMessage; - if (!TestCompileShader(buffer[j], languages[j], true, &errorMessage)) { + if (!TestCompileShader(buffer[j], languages[j], ShaderStage::Vertex, &errorMessage)) { printf("Error compiling vertex shader %d:\n\n%s\n\n%s\n", (int)j, LineNumberString(buffer[j]).c_str(), errorMessage.c_str()); return false; } @@ -343,7 +393,7 @@ bool TestFragmentShaders() { for (int j = 0; j < numLanguages; j++) { if (generateSuccess[j]) { std::string errorMessage; - if (!TestCompileShader(buffer[j], languages[j], false, &errorMessage)) { + if (!TestCompileShader(buffer[j], languages[j], ShaderStage::Fragment, &errorMessage)) { printf("Error compiling fragment shader:\n\n%s\n\n%s\n", LineNumberString(buffer[j]).c_str(), errorMessage.c_str()); return false; } @@ -369,7 +419,7 @@ bool TestShaderGenerators() { init_glslang(); #endif - if (!TestFragmentShaders()) { + if (!TestStencilShaders()) { return false; } @@ -377,6 +427,10 @@ bool TestShaderGenerators() { return false; } + if (!TestFragmentShaders()) { + return false; + } + if (!TestVertexShaders()) { return false; }