diff --git a/Common/Data/Collections/TinySet.h b/Common/Data/Collections/TinySet.h index 8a7cf89fa6..5c8c8e78e6 100644 --- a/Common/Data/Collections/TinySet.h +++ b/Common/Data/Collections/TinySet.h @@ -3,10 +3,12 @@ #include // Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded. +// Can also be used as a small vector, then use push_back (or push_in_place) instead of insert. +// Duplicates are thus allowed if you use that, but not if you exclusively use insert. template struct TinySet { ~TinySet() { delete slowLookup_; } - inline void insert(T t) { + inline void insert(const T &t) { // Fast linear scan. for (int i = 0; i < fastCount; i++) { if (fastLookup_[i] == t) @@ -20,6 +22,41 @@ struct TinySet { // Fall back to slow path. insertSlow(t); } + inline void push_back(const T &t) { + if (fastCount < MaxFastSize) { + fastLookup_[fastCount++] = t; + return; + } + if (!slowLookup_) { + slowLookup_ = new std::vector(); + } + slowLookup_->push_back(t); + } + inline T *add_back() { + if (fastCount < MaxFastSize) { + return &fastLookup_[fastCount++]; + } + if (!slowLookup_) { + slowLookup_ = new std::vector(); + } + T t; + slowLookup_->push_back(t); + return slowLookup_->back(); + } + void append(const TinySet &other) { + size_t otherSize = other.size(); + if (size() + otherSize <= MaxFastSize) { + // Fast case + for (int i = 0; i < otherSize; i++) { + fastLookup_[fastCount + i] = other.fastLookup_[i]; + } + fastCount += other.fastCount; + } else { + for (int i = 0; i < otherSize; i++) { + push_back(other[i]); + } + } + } bool contains(T t) const { for (int i = 0; i < fastCount; i++) { if (fastLookup_[i] == t) @@ -52,6 +89,26 @@ struct TinySet { slowLookup_ = nullptr; fastCount = 0; } + bool empty() const { + return fastCount == 0; + } + size_t size() const { + if (!slowLookup_) { + return fastCount; + } else { + return slowLookup_->size() + MaxFastSize; + } + } + const T &operator[] (size_t index) const { + if (index < MaxFastSize) { + return fastLookup_[index]; + } else { + return (*slowLookup_)[index - MaxFastSize]; + } + } + const T &back() const { + return (*this)[size() - 1]; + } private: void insertSlow(T t) { diff --git a/Common/File/Path.cpp b/Common/File/Path.cpp index d0bda53328..ba3372e78a 100644 --- a/Common/File/Path.cpp +++ b/Common/File/Path.cpp @@ -258,8 +258,13 @@ std::wstring Path::ToWString() const { std::string Path::ToVisualString() const { if (type_ == PathType::CONTENT_URI) { return AndroidContentURI(path_).ToVisualString(); +#if PPSSPP_PLATFORM(WINDOWS) + } else if (type_ == PathType::NATIVE) { + return ReplaceAll(path_, "/", "\\"); +#endif + } else { + return path_; } - return path_; } bool Path::CanNavigateUp() const { diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 09ffd4fa6a..0464235eb5 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -275,10 +275,11 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de if (SUCCEEDED(result)) { if (options.OutputMergerLogicOp) { // Actually, need to check that the format supports logic ops as well. - // Which normal UNORM formats don't seem to do. So meh. + // Which normal UNORM formats don't seem to do in D3D11. So meh. We can't enable logicOp support. // caps_.logicOpSupported = true; } } + IDXGIDevice* dxgiDevice = nullptr; IDXGIAdapter* adapter = nullptr; HRESULT hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgiDevice)); diff --git a/Common/GPU/D3D9/D3D9ShaderCompiler.cpp b/Common/GPU/D3D9/D3D9ShaderCompiler.cpp index e8585f15f3..f41ea3911d 100644 --- a/Common/GPU/D3D9/D3D9ShaderCompiler.cpp +++ b/Common/GPU/D3D9/D3D9ShaderCompiler.cpp @@ -54,7 +54,7 @@ LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std: } bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, std::string *errorMessage) { - LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_2_0", errorMessage); + LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_3_0", errorMessage); if (pShaderCode) { // Create pixel shader. device->CreatePixelShader((DWORD*)pShaderCode->GetBufferPointer(), pShader); @@ -66,7 +66,7 @@ bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT } bool CompileVertexShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, std::string *errorMessage) { - LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_2_0", errorMessage); + LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_3_0", errorMessage); if (pShaderCode) { // Create vertex shader. device->CreateVertexShader((DWORD*)pShaderCode->GetBufferPointer(), pShader); diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index cf73de79a3..dc72ed898d 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -1069,11 +1069,7 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si auto compile = [&](const char *profile) -> HRESULT { return dyn_D3DCompile(source, (UINT)strlen(source), nullptr, defines, includes, "main", profile, 0, 0, &codeBuffer, &errorBuffer); }; - HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_2_0" : "vs_2_0"); - if (FAILED(hr) && hr == D3DXERR_INVALIDDATA) { - // Might be a post shader. Let's try using shader model 3. - hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0"); - } + HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0"); if (FAILED(hr)) { const char *error = errorBuffer ? (const char *)errorBuffer->GetBufferPointer() : "(no errorbuffer returned)"; if (hr == ERROR_MOD_NOT_FOUND) { diff --git a/Common/GPU/DataFormat.h b/Common/GPU/DataFormat.h index f1e1687630..b07d0a2f34 100644 --- a/Common/GPU/DataFormat.h +++ b/Common/GPU/DataFormat.h @@ -77,5 +77,6 @@ inline bool DataFormatIsColor(DataFormat fmt) { void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); +void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); } // namespace diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 986167903d..1644018f2e 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -643,7 +643,7 @@ retry_depth: currentReadHandle_ = fbo->handle; } -void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCalls) { +void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps) { if (skipGLCalls) { // Dry run for (size_t i = 0; i < steps.size(); i++) { @@ -725,7 +725,9 @@ void GLQueueRunner::RunSteps(const std::vector &steps, bool skipGLCal glPopDebugGroup(); #endif - delete steps[i]; + if (!keepSteps) { + delete steps[i]; + } } CHECK_GL_ERROR_IF_DEBUG(); } diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index a01fad23ab..1e009787ae 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -363,7 +363,7 @@ public: void RunInitSteps(const std::vector &steps, bool skipGLCalls); - void RunSteps(const std::vector &steps, bool skipGLCalls); + void RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps = false); void LogSteps(const std::vector &steps); void CreateDeviceObjects(); diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index b9173c2beb..7c228f240b 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -5,6 +5,8 @@ #include "Common/Thread/ThreadUtil.h" #include "Common/VR/PPSSPPVR.h" +#include "Core/Config.h" + #include "Common/Log.h" #include "Common/MemoryUtil.h" #include "Common/Math/math_util.h" @@ -233,15 +235,7 @@ bool GLRenderManager::ThreadFrame() { INFO_LOG(G3D, "Running first frame (%d)", threadFrame_); firstFrame = false; } - - if (IsVRBuild()) { - if (PreVRRender()) { - Run(threadFrame_); - PostVRRender(); - } - } else { - Run(threadFrame_); - } + Run(threadFrame_); VLOG("PULL: Finished frame %d", threadFrame_); } while (!nextFrame); @@ -567,6 +561,7 @@ void GLRenderManager::EndSubmitFrame(int frame) { void GLRenderManager::Run(int frame) { BeginSubmitFrame(frame); + FrameData &frameData = frameData_[frame]; auto &stepsOnThread = frameData_[frame].steps; @@ -583,8 +578,22 @@ void GLRenderManager::Run(int frame) { } } - queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); - stepsOnThread.clear(); + if (IsVRBuild()) { + if (PreVRRender()) { + int passes = 1; + if (!IsMultiviewSupported() && g_Config.bEnableStereo) { + passes = 2; + } + for (int i = 0; i < passes; i++) { + PreVRFrameRender(i); + queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1); + PostVRFrameRender(); + } + PostVRRender(); + } + } else { + queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); + } if (!skipGLCalls_) { for (auto iter : frameData.activePushBuffers) { @@ -592,6 +601,8 @@ void GLRenderManager::Run(int frame) { } } + stepsOnThread.clear(); + switch (frameData.type) { case GLRRunType::END: EndSubmitFrame(frame); diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 130ba2d0fe..5283b064ec 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -569,6 +569,9 @@ OpenGLContext::OpenGLContext() { caps_.fragmentShaderDepthWriteSupported = true; } + // GLES has no support for logic framebuffer operations. There doesn't even seem to exist any such extensions. + caps_.logicOpSupported = !gl_extensions.IsGLES; + // Interesting potential hack for emulating GL_DEPTH_CLAMP (use a separate varying, force depth in fragment shader): // This will induce a performance penalty on many architectures though so a blanket enable of this // is probably not a good idea. diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 880d18bd2b..9b7ae11ea5 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -347,6 +347,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st profile->cpuStartTime = time_now_d(); bool emitLabels = vulkan_->Extensions().EXT_debug_utils; + for (size_t i = 0; i < steps.size(); i++) { const VKRStep &step = *steps[i]; @@ -685,7 +686,7 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector &steps) { auto mergeRenderSteps = [](VKRStep *dst, VKRStep *src) { // OK. Now, if it's a render, slurp up all the commands and kill the step. // Also slurp up any pretransitions. - dst->preTransitions.insert(dst->preTransitions.end(), src->preTransitions.begin(), src->preTransitions.end()); + dst->preTransitions.append(src->preTransitions); dst->commands.insert(dst->commands.end(), src->commands.begin(), src->commands.end()); MergeRenderAreaRectInto(&dst->render.renderArea, src->render.renderArea); // So we don't consider it for other things, maybe doesn't matter. @@ -1060,7 +1061,8 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou } void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { - for (const auto &iter : step.preTransitions) { + for (size_t i = 0; i < step.preTransitions.size(); i++) { + const TransitionRequest &iter = step.preTransitions[i]; if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) { recordBarrier_.TransitionImageAuto( iter.fb->color.image, @@ -1142,8 +1144,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VKRFramebuffer *fb = step.render.framebuffer; - VkPipeline lastGraphicsPipeline = VK_NULL_HANDLE; - VkPipeline lastComputePipeline = VK_NULL_HANDLE; + VKRGraphicsPipeline *lastGraphicsPipeline = nullptr; + VKRComputePipeline *lastComputePipeline = nullptr; auto &commands = step.commands; @@ -1151,6 +1153,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c // The stencil ones are very commonly mostly redundant so let's eliminate them where possible. // Might also want to consider scissor and viewport. VkPipeline lastPipeline = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + int lastStencilWriteMask = -1; int lastStencilCompareMask = -1; int lastStencilReference = -1; @@ -1164,52 +1168,44 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::BIND_PIPELINE: { VkPipeline pipeline = c.pipeline.pipeline; - if (pipeline != lastGraphicsPipeline) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - lastGraphicsPipeline = pipeline; - // Reset dynamic state so it gets refreshed with the new pipeline. - lastStencilWriteMask = -1; - lastStencilCompareMask = -1; - lastStencilReference = -1; - } + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; + // Reset dynamic state so it gets refreshed with the new pipeline. + lastStencilWriteMask = -1; + lastStencilCompareMask = -1; + lastStencilReference = -1; break; } case VKRRenderCommand::BIND_GRAPHICS_PIPELINE: { - VKRGraphicsPipeline *pipeline = c.graphics_pipeline.pipeline; - if (pipeline->Pending()) { - // Stall processing, waiting for the compile queue to catch up. - std::unique_lock lock(compileDoneMutex_); - while (!pipeline->pipeline) { - compileDone_.wait(lock); + VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline; + if (graphicsPipeline != lastGraphicsPipeline) { + VkPipeline pipeline = graphicsPipeline->pipeline->BlockUntilReady(); + if (pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; + lastGraphicsPipeline = graphicsPipeline; + // Reset dynamic state so it gets refreshed with the new pipeline. + lastStencilWriteMask = -1; + lastStencilCompareMask = -1; + lastStencilReference = -1; } } - if (pipeline->pipeline != lastGraphicsPipeline && pipeline->pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); - lastGraphicsPipeline = pipeline->pipeline; - // Reset dynamic state so it gets refreshed with the new pipeline. - lastStencilWriteMask = -1; - lastStencilCompareMask = -1; - lastStencilReference = -1; - } break; } case VKRRenderCommand::BIND_COMPUTE_PIPELINE: { - VKRComputePipeline *pipeline = c.compute_pipeline.pipeline; - if (pipeline->Pending()) { - // Stall processing, waiting for the compile queue to catch up. - std::unique_lock lock(compileDoneMutex_); - while (!pipeline->pipeline) { - compileDone_.wait(lock); + VKRComputePipeline *computePipeline = c.compute_pipeline.pipeline; + if (computePipeline != lastComputePipeline) { + VkPipeline pipeline = computePipeline->pipeline->BlockUntilReady(); + if (pipeline != VK_NULL_HANDLE) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; + lastComputePipeline = computePipeline; } } - if (pipeline->pipeline != lastComputePipeline && pipeline->pipeline != VK_NULL_HANDLE) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); - lastComputePipeline = pipeline->pipeline; - } break; } @@ -1257,7 +1253,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } case VKRRenderCommand::PUSH_CONSTANTS: - vkCmdPushConstants(cmd, c.push.pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data); + vkCmdPushConstants(cmd, pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data); break; case VKRRenderCommand::STENCIL: @@ -1276,14 +1272,17 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c break; case VKRRenderCommand::DRAW_INDEXED: - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); - vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, c.drawIndexed.indexType); - vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + { + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, (VkIndexType)c.drawIndexed.indexType); + VkDeviceSize voffset = c.drawIndexed.voffset; + vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset); vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); break; + } case VKRRenderCommand::DRAW: - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); if (c.draw.vbuffer) { vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); } @@ -1325,7 +1324,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } default: ERROR_LOG(G3D, "Unimpl queue command"); - ; } } vkCmdEndRenderPass(cmd); @@ -1793,6 +1791,8 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataForm } } else if (destFormat == Draw::DataFormat::D32F) { ConvertToD32F(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat); + } else if (destFormat == Draw::DataFormat::D16) { + ConvertToD16(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat); } else { // TODO: Maybe a depth conversion or something? ERROR_LOG(G3D, "CopyReadbackBuffer: Unknown format"); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index f80a1ba340..ce6dc9f5eb 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -4,7 +4,7 @@ #include #include - +#include "Common/Thread/Promise.h" #include "Common/Data/Collections/Hashmaps.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanBarrier.h" @@ -52,15 +52,17 @@ struct VkRenderData { union { struct { VkPipeline pipeline; + VkPipelineLayout pipelineLayout; } pipeline; struct { VKRGraphicsPipeline *pipeline; + VkPipelineLayout pipelineLayout; } graphics_pipeline; struct { VKRComputePipeline *pipeline; + VkPipelineLayout pipelineLayout; } compute_pipeline; struct { - VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; uint32_t uboOffsets[3]; @@ -70,17 +72,16 @@ struct VkRenderData { uint32_t offset; } draw; struct { - VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; uint32_t uboOffsets[3]; VkBuffer vbuffer; // might need to increase at some point - VkDeviceSize voffset; VkBuffer ibuffer; - VkDeviceSize ioffset; + uint32_t voffset; + uint32_t ioffset; uint32_t count; int16_t instances; - VkIndexType indexType; + int16_t indexType; } drawIndexed; struct { uint32_t clearColor; @@ -103,7 +104,6 @@ struct VkRenderData { uint32_t color; } blendColor; struct { - VkPipelineLayout pipelineLayout; VkShaderStageFlags stages; uint8_t offset; uint8_t size; @@ -134,8 +134,8 @@ enum class VKRRenderPassStoreAction : uint8_t { }; struct TransitionRequest { - VkImageAspectFlags aspect; // COLOR or DEPTH VKRFramebuffer *fb; + VkImageAspectFlags aspect; // COLOR or DEPTH VkImageLayout targetLayout; }; @@ -153,7 +153,7 @@ struct VKRStep { VKRStepType stepType; std::vector commands; - std::vector preTransitions; + TinySet preTransitions; TinySet dependencies; const char *tag; union { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 8e17483ae9..a4e14a1b8d 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -5,6 +5,7 @@ #include "Common/Log.h" #include "Common/StringUtils.h" +#include "Common/TimeUtil.h" #include "Common/GPU/Vulkan/VulkanAlloc.h" #include "Common/GPU/Vulkan/VulkanContext.h" @@ -29,27 +30,61 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) { // Already failed to create this one. return false; } + + // Fill in the last part of the desc since now it's time to block. + VkShaderModule vs = desc->vertexShader->BlockUntilReady(); + VkShaderModule fs = desc->fragmentShader->BlockUntilReady(); + + if (!vs || !fs) { + ERROR_LOG(G3D, "Failed creating graphics pipeline - missing shader modules"); + // We're kinda screwed here? + return false; + } + + VkPipelineShaderStageCreateInfo ss[2]{}; + ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + ss[0].pSpecializationInfo = nullptr; + ss[0].module = vs; + ss[0].pName = "main"; + ss[0].flags = 0; + ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + ss[1].pSpecializationInfo = nullptr; + ss[1].module = fs; + ss[1].pName = "main"; + ss[1].flags = 0; + + desc->pipe.pStages = ss; + desc->pipe.stageCount = 2; + + double start = time_now_d(); VkPipeline vkpipeline; VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline); + INFO_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0); + bool success = true; if (result == VK_INCOMPLETE) { // Bad (disallowed by spec) return value seen on Adreno in Burnout :( Try to ignore? // Would really like to log more here, we could probably attach more info to desc. // // At least create a null placeholder to avoid creating over and over if something is broken. - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); success = false; } else if (result != VK_SUCCESS) { - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result)); success = false; } else { - pipeline = vkpipeline; + pipeline->Post(vkpipeline); } + // Having the desc stick around can be useful for debugging. +#ifndef _DEBUG delete desc; desc = nullptr; +#endif return success; } @@ -63,11 +98,11 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) { bool success = true; if (result != VK_SUCCESS) { - pipeline = VK_NULL_HANDLE; + pipeline->Post(VK_NULL_HANDLE); ERROR_LOG(G3D, "Failed creating compute pipeline! result='%s'", VulkanResultToString(result)); success = false; } else { - pipeline = vkpipeline; + pipeline->Post(vkpipeline); } delete desc; @@ -449,6 +484,12 @@ void VulkanRenderManager::CompileThreadFunc() { if (!run_) { break; } + + INFO_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size()); + + // TODO: Here we can sort the pending pipelines by vertex and fragment shaders, + // and split up further. + // Those with the same pairs of shaders should be on the same thread. for (auto &entry : toCompile) { switch (entry.type) { case CompileQueueEntry::Type::GRAPHICS: @@ -836,6 +877,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma } else { _assert_(false); } + // Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM. queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels); return true; @@ -1188,7 +1230,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in // We're done. return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView; } else { - curRenderStep_->preTransitions.push_back({ aspectBit, fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); + curRenderStep_->preTransitions.push_back({ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView; } } diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 1df2fc2361..67dff36880 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -12,6 +12,7 @@ #include #include +#include "Common/Thread/Promise.h" #include "Common/System/Display.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/Data/Convert/SmallDataConvert.h" @@ -121,7 +122,11 @@ struct VKRGraphicsPipelineDesc { VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - VkPipelineShaderStageCreateInfo shaderStageInfo[2]{}; + + // Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish. + Promise *vertexShader; + Promise *fragmentShader; + VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; VkVertexInputAttributeDescription attrs[8]{}; VkVertexInputBindingDescription ibd{}; @@ -136,18 +141,19 @@ struct VKRComputePipelineDesc { VkComputePipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; }; -// Wrapped pipeline, which will later allow for background compilation while emulating the rest of the frame. +// Wrapped pipeline. struct VKRGraphicsPipeline { VKRGraphicsPipeline() { - pipeline = VK_NULL_HANDLE; + pipeline = Promise::CreateEmpty(); } - VKRGraphicsPipelineDesc *desc = nullptr; // While non-zero, is pending and pipeline isn't valid. - std::atomic pipeline; + ~VKRGraphicsPipeline() { + delete desc; + } + + VKRGraphicsPipelineDesc *desc = nullptr; + Promise *pipeline; bool Create(VulkanContext *vulkan); - bool Pending() const { - return pipeline == VK_NULL_HANDLE && desc != nullptr; - } }; struct VKRComputePipeline { @@ -155,7 +161,7 @@ struct VKRComputePipeline { pipeline = VK_NULL_HANDLE; } VKRComputePipelineDesc *desc = nullptr; - std::atomic pipeline; + Promise *pipeline; bool Create(VulkanContext *vulkan); bool Pending() const { @@ -242,29 +248,32 @@ public: return pipeline; } - void BindPipeline(VkPipeline pipeline, PipelineFlags flags) { + void BindPipeline(VkPipeline pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != VK_NULL_HANDLE); VkRenderData data{ VKRRenderCommand::BIND_PIPELINE }; data.pipeline.pipeline = pipeline; + data.pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); } - void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags) { + void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE }; data.graphics_pipeline.pipeline = pipeline; + data.graphics_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); } - void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags) { + void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(pipeline != nullptr); VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE }; data.compute_pipeline.pipeline = pipeline; + data.compute_pipeline.pipelineLayout = pipelineLayout; curPipelineFlags_ |= flags; curRenderStep_->commands.push_back(data); } @@ -351,7 +360,6 @@ public: _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_(size + offset < 40); VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS }; - data.push.pipelineLayout = pipelineLayout; data.push.stages = stages; data.push.offset = offset; data.push.size = size; @@ -386,12 +394,11 @@ public: curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE; } - void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) { + void Draw(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_); VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; data.draw.offset = offset; - data.draw.pipelineLayout = layout; data.draw.ds = descSet; data.draw.vbuffer = vbuffer; data.draw.voffset = voffset; @@ -403,12 +410,11 @@ public: curRenderStep_->render.numDraws++; } - void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { + void DrawIndexed(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_); VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.instances = numInstances; - data.drawIndexed.pipelineLayout = layout; data.drawIndexed.ds = descSet; data.drawIndexed.vbuffer = vbuffer; data.drawIndexed.voffset = voffset; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 171decd3eb..8a1112d446 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -795,6 +795,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) caps_.fragmentShaderInt32Supported = true; caps_.textureNPOTFullySupported = true; caps_.fragmentShaderDepthWriteSupported = true; + caps_.logicOpSupported = vulkan->GetDeviceFeatures().enabled.logicOp != 0; auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties; switch (deviceProps.vendorID) { @@ -1339,7 +1340,7 @@ void VKContext::Draw(int vertexCount, int offset) { BindCompatiblePipeline(); ApplyDynamicState(); - renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset); + renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset); } void VKContext::DrawIndexed(int vertexCount, int offset) { @@ -1359,7 +1360,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { BindCompatiblePipeline(); ApplyDynamicState(); - renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16); + renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1375,15 +1376,15 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { BindCompatiblePipeline(); ApplyDynamicState(); - renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount); + renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount); } void VKContext::BindCompatiblePipeline() { VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass(); if (renderPass == renderManager_.GetBackbufferRenderPass()) { - renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags); + renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags, pipelineLayout_); } else { - renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags); + renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags, pipelineLayout_); } } diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index 9dee5898fb..d712195c7b 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -623,6 +623,48 @@ void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_ } } +// TODO: This is missing the conversion to the quarter-range we use if depth clamp is not available. +// That conversion doesn't necessarily belong here in thin3d, though. +void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { + if (format == Draw::DataFormat::D32F) { + const float *src32 = (const float *)src; + uint16_t *dst16 = (uint16_t *)dst; + if (src == dst) { + return; + } else { + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + dst16[x] = (uint16_t)(src32[x] * 65535.0f); + } + src32 += srcStride; + dst16 += dstStride; + } + } + } else if (format == Draw::DataFormat::D16) { + _assert_(src != dst); + const uint16_t *src16 = (const uint16_t *)src; + uint16_t *dst16 = (uint16_t *)dst; + for (uint32_t y = 0; y < height; ++y) { + memcpy(dst16, src16, width * 2); + src16 += srcStride; + dst16 += dstStride; + } + } else if (format == Draw::DataFormat::D24_S8) { + _assert_(src != dst); + const uint32_t *src32 = (const uint32_t *)src; + uint16_t *dst16 = (uint16_t *)dst; + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + dst16[x] = (src32[x] & 0x00FFFFFF) >> 8; + } + src32 += srcStride; + dst16 += dstStride; + } + } else { + assert(false); + } +} + const char *Bugs::GetBugName(uint32_t bug) { switch (bug) { case NO_DEPTH_CANNOT_DISCARD_STENCIL: return "NO_DEPTH_CANNOT_DISCARD_STENCIL"; diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index c4b88c4c3a..f88f6194ab 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -44,15 +44,16 @@ #include "Common/Buffer.h" #include "Common/StringUtils.h" -void truncate_cpy(char *dest, size_t destSize, const char *src) { +size_t truncate_cpy(char *dest, size_t destSize, const char *src) { size_t len = strlen(src); if (len >= destSize - 1) { memcpy(dest, src, destSize - 1); - dest[destSize - 1] = '\0'; + len = destSize - 1; } else { memcpy(dest, src, len); - dest[len] = '\0'; } + dest[len] = '\0'; + return len; } const char* safe_string(const char* s) { diff --git a/Common/StringUtils.h b/Common/StringUtils.h index 989fe8b9fd..16bfaa18e5 100644 --- a/Common/StringUtils.h +++ b/Common/StringUtils.h @@ -78,10 +78,10 @@ std::string ReplaceAll(std::string input, const std::string& src, const std::str void SkipSpace(const char **ptr); -void truncate_cpy(char *dest, size_t destSize, const char *src); +size_t truncate_cpy(char *dest, size_t destSize, const char *src); template -inline void truncate_cpy(char(&out)[Count], const char *src) { - truncate_cpy(out, Count, src); +inline size_t truncate_cpy(char(&out)[Count], const char *src) { + return truncate_cpy(out, Count, src); } const char* safe_string(const char* s); diff --git a/Common/Thread/Channel.h b/Common/Thread/Channel.h index 9ccbbd7096..b6b23fdebe 100644 --- a/Common/Thread/Channel.h +++ b/Common/Thread/Channel.h @@ -19,7 +19,7 @@ struct Mailbox { std::mutex mutex_; std::condition_variable condvar_; - T data_ = nullptr; + T data_{}; T Wait() { std::unique_lock lock(mutex_); @@ -43,7 +43,7 @@ struct Mailbox { std::unique_lock lock(mutex_); if (!data_) { data_ = data; - condvar_.notify_one(); + condvar_.notify_all(); return true; } else { // Already has value. diff --git a/Common/Thread/Promise.h b/Common/Thread/Promise.h index fe2ddb3807..4145b4b6c1 100644 --- a/Common/Thread/Promise.h +++ b/Common/Thread/Promise.h @@ -1,7 +1,9 @@ #pragma once #include +#include +#include "Common/Log.h" #include "Common/Thread/Channel.h" #include "Common/Thread/ThreadManager.h" @@ -33,6 +35,7 @@ public: // Has ownership over the data. Single use. // TODO: Split Mailbox (rx_ and tx_) up into separate proxy objects. // NOTE: Poll/BlockUntilReady should only be used from one thread. +// TODO: Make movable? template class Promise { public: @@ -47,15 +50,36 @@ public: return promise; } + static Promise *AlreadyDone(T data) { + Promise *promise = new Promise(); + promise->data_ = data; + promise->ready_ = true; + return promise; + } + + static Promise *CreateEmpty() { + Mailbox *mailbox = new Mailbox(); + Promise *promise = new Promise(); + promise->rx_ = mailbox; + return promise; + } + + // Allow an empty promise to spawn, too, in case we want to delay it. + void SpawnEmpty(ThreadManager *threadman, std::function fun, TaskType taskType) { + PromiseTask *task = new PromiseTask(fun, rx_, taskType); + threadman->EnqueueTask(task); + } + ~Promise() { + std::lock_guard guard(readyMutex_); // A promise should have been fulfilled before it's destroyed. _assert_(ready_); _assert_(!rx_); - delete data_; } // Returns T if the data is ready, nullptr if it's not. T Poll() { + std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { @@ -71,6 +95,7 @@ public: } T BlockUntilReady() { + std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { @@ -82,10 +107,17 @@ public: } } + // For outside injection of data, when not using Spawn + void Post(T data) { + rx_->Send(data); + } + private: Promise() {} - T data_ = nullptr; + // Promise can only be constructed in Spawn (or AlreadyDone). + T data_{}; bool ready_ = false; - Mailbox *rx_; + std::mutex readyMutex_; + Mailbox *rx_ = nullptr; }; diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 9c6cac4640..2d3562468c 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -1,6 +1,7 @@ #include "Common/VR/PPSSPPVR.h" #include "Common/VR/VRBase.h" #include "Common/VR/VRInput.h" +#include "Common/VR/VRMath.h" #include "Common/VR/VRRenderer.h" #include "Common/VR/VRTweaks.h" @@ -31,6 +32,16 @@ struct ButtonMapping { } }; +struct MouseActivator { + bool activate; + ovrButton ovr; + + MouseActivator(bool activate, ovrButton ovr) { + this->activate = activate; + this->ovr = ovr; + } +}; + static std::vector leftControllerMapping = { ButtonMapping(NKCODE_BUTTON_X, ovrButton_X), ButtonMapping(NKCODE_BUTTON_Y, ovrButton_Y), @@ -61,6 +72,16 @@ static std::vector controllerMapping[2] = { leftControllerMapping, rightControllerMapping }; +static int mouseController = -1; +static bool mousePressed[] = {false, false}; + +static std::vector mouseActivators = { + MouseActivator(true, ovrButton_Trigger), + MouseActivator(false, ovrButton_Up), + MouseActivator(false, ovrButton_Down), + MouseActivator(false, ovrButton_Left), + MouseActivator(false, ovrButton_Right), +}; /* ================================================================================ @@ -90,7 +111,7 @@ void EnterVR(bool firstStart) { VR_EnterVR(VR_GetEngine()); IN_VRInit(VR_GetEngine()); } - VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported()); + VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, false); } void GetVRResolutionPerEye(int* width, int* height) { @@ -99,7 +120,8 @@ void GetVRResolutionPerEye(int* width, int* height) { } } -void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) { +void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) { + //buttons KeyInput keyInput = {}; for (int j = 0; j < 2; j++) { int status = IN_VRGetButtonState(j); @@ -125,6 +147,53 @@ void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) { } } } + + //enable or disable mouse + for (int j = 0; j < 2; j++) { + int status = IN_VRGetButtonState(j); + for (MouseActivator& m : mouseActivators) { + if (status & m.ovr) { + mouseController = m.activate ? j : -1; + } + } + } + + //mouse cursor + if (mouseController >= 0) { + //get position on screen + XrPosef pose = IN_VRGetPose(mouseController); + XrVector3f angles = XrQuaternionf_ToEulerAngles(pose.orientation); + float width = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_WIDTH); + float height = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_HEIGHT); + float cx = width / 2; + float cy = height / 2; + float speed = (cx + cy) / 2; + float x = cx - tan(ToRadians(angles.y - (float)VR_GetConfig(VR_CONFIG_MENU_YAW))) * speed; + float y = cy - tan(ToRadians(angles.x)) * speed; + + //set renderer + VR_SetConfig(VR_CONFIG_MOUSE_X, (int)x); + VR_SetConfig(VR_CONFIG_MOUSE_Y, (int)y); + VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 6 * (int)pow(VR_GetConfig(VR_CONFIG_CANVAS_DISTANCE), 0.25f)); + + //inform engine about the status + TouchInput touch; + touch.id = mouseController; + touch.x = x * dp_xscale; + touch.y = (height - y - 1) * dp_yscale; + bool pressed = IN_VRGetButtonState(mouseController) & ovrButton_Trigger; + if (mousePressed[mouseController] != pressed) { + if (!pressed) { + touch.flags = TOUCH_DOWN; + NativeTouch(touch); + touch.flags = TOUCH_UP; + NativeTouch(touch); + } + mousePressed[mouseController] = pressed; + } + } else { + VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 0); + } } void UpdateVRScreenKey(const KeyInput &key) { @@ -151,7 +220,12 @@ void BindVRFramebuffer() { } bool PreVRRender() { - if (VR_BeginFrame(VR_GetEngine())) { + if (!VR_GetConfig(VR_CONFIG_VIEWPORT_VALID)) { + VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported()); + VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true); + } + + if (VR_InitFrame(VR_GetEngine())) { // Decide if the scene is 3D or not if (g_Config.bEnableVR && !VR_GetConfig(VR_CONFIG_FORCE_2D) && (VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) > 15)) { @@ -171,9 +245,21 @@ bool PreVRRender() { } void PostVRRender() { + VR_FinishFrame(VR_GetEngine()); +} + +void PreVRFrameRender(int fboIndex) { + VR_BeginFrame(VR_GetEngine(), fboIndex); +} + +void PostVRFrameRender() { VR_EndFrame(VR_GetEngine()); } +int GetVRFBOIndex() { + return VR_GetConfig(VR_CONFIG_CURRENT_FBO); +} + bool IsMultiviewSupported() { return false; } diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h index c605980d28..b41b69f1be 100644 --- a/Common/VR/PPSSPPVR.h +++ b/Common/VR/PPSSPPVR.h @@ -10,13 +10,16 @@ bool IsVRBuild(); void InitVROnAndroid(void* vm, void* activity, int version, char* name); void EnterVR(bool firstStart); void GetVRResolutionPerEye(int* width, int* height); -void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics); +void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale); void UpdateVRScreenKey(const KeyInput &key); // VR rendering integration void BindVRFramebuffer(); bool PreVRRender(); void PostVRRender(); +void PreVRFrameRender(int fboIndex); +void PostVRFrameRender(); +int GetVRFBOIndex(); bool IsMultiviewSupported(); bool IsFlatVRScene(); bool Is2DVRObject(float* projMatrix, bool ortho); @@ -30,13 +33,16 @@ inline bool IsVRBuild() { return false; } inline void InitVROnAndroid(void* vm, void* activity, int version, char* name) {} inline void EnterVR(bool firstTime) {} inline void GetVRResolutionPerEye(int* width, int* height) {} -inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) {} +inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) {} inline void UpdateVRScreenKey(const KeyInput &key) {} // VR rendering integration inline void BindVRFramebuffer() {} inline bool PreVRRender() { return false; } inline void PostVRRender() {} +inline void PreVRFrameRender(int fboIndex) {} +inline void PostVRFrameRender() {} +inline int GetVRFBOIndex() { return 0; } inline bool IsMultiviewSupported() { return false; } inline bool IsFlatVRScene() { return true; } inline bool Is2DVRObject(float* projMatrix, bool ortho) { return false; } diff --git a/Common/VR/VRBase.h b/Common/VR/VRBase.h index 138c8f09f4..fffd234868 100644 --- a/Common/VR/VRBase.h +++ b/Common/VR/VRBase.h @@ -92,18 +92,19 @@ typedef struct { typedef struct { int Width; int Height; - bool Multiview; uint32_t TextureSwapChainLength; uint32_t TextureSwapChainIndex; ovrSwapChain ColorSwapChain; + ovrSwapChain DepthSwapChain; XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; - unsigned int* DepthBuffers; + XrSwapchainImageOpenGLESKHR* DepthSwapChainImage; unsigned int* FrameBuffers; bool Acquired; } ovrFramebuffer; typedef struct { - ovrFramebuffer FrameBuffer; + bool Multiview; + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; } ovrRenderer; typedef struct { diff --git a/Common/VR/VRFramebuffer.cpp b/Common/VR/VRFramebuffer.cpp index df9b4eebea..b01775c588 100644 --- a/Common/VR/VRFramebuffer.cpp +++ b/Common/VR/VRFramebuffer.cpp @@ -33,8 +33,12 @@ void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; frameBuffer->ColorSwapChain.Width = 0; frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->DepthSwapChain.Width = 0; + frameBuffer->DepthSwapChain.Height = 0; + frameBuffer->DepthSwapChainImage = NULL; - frameBuffer->DepthBuffers = NULL; frameBuffer->FrameBuffers = NULL; frameBuffer->Acquired = false; } @@ -43,12 +47,10 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w frameBuffer->Width = width; frameBuffer->Height = height; - frameBuffer->Multiview = multiview; if (strstr((const char*)glGetString(GL_EXTENSIONS), "GL_OVR_multiview2") == nullptr) { ALOGE("OpenGL implementation does not support GL_OVR_multiview2 extension.\n"); - exit(EXIT_FAILURE); } typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); @@ -56,14 +58,11 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w if (!glFramebufferTextureMultiviewOVR) { ALOGE("Can not get proc address for glFramebufferTextureMultiviewOVR.\n"); - exit(EXIT_FAILURE); } XrSwapchainCreateInfo swapChainCreateInfo; memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo)); swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO; - swapChainCreateInfo.format = GL_RGBA8; - swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; swapChainCreateInfo.sampleCount = 1; swapChainCreateInfo.width = width; swapChainCreateInfo.height = height; @@ -73,69 +72,55 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + frameBuffer->DepthSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->DepthSwapChain.Height = swapChainCreateInfo.height; // Create the color swapchain. + swapChainCreateInfo.format = GL_RGBA8; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); OXR(xrEnumerateSwapchainImages(frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + // Create the depth swapchain. + swapChainCreateInfo.format = GL_DEPTH24_STENCIL8; + swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->DepthSwapChain.Handle)); + frameBuffer->DepthSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + // Populate the swapchain image array. for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; frameBuffer->ColorSwapChainImage[i].next = NULL; + frameBuffer->DepthSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->DepthSwapChainImage[i].next = NULL; } OXR(xrEnumerateSwapchainImages( frameBuffer->ColorSwapChain.Handle, frameBuffer->TextureSwapChainLength, &frameBuffer->TextureSwapChainLength, (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + OXR(xrEnumerateSwapchainImages( + frameBuffer->DepthSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->DepthSwapChainImage)); - frameBuffer->DepthBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); frameBuffer->FrameBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + const GLuint depthTexture = frameBuffer->DepthSwapChainImage[i].image; + + // Create the frame buffer. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); if (multiview) { - // Create the color buffer texture. - GLenum textureTarget = GL_TEXTURE_2D_ARRAY; - GL(glBindTexture(textureTarget, colorTexture)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL(glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GL(glBindTexture(textureTarget, 0)); - - // Create depth buffer. - GL(glGenTextures(1, &frameBuffer->DepthBuffers[i])); - GL(glBindTexture(textureTarget, frameBuffer->DepthBuffers[i])); - GL(glTexStorage3D(textureTarget, 1, GL_DEPTH_COMPONENT24, width, height, 2)); - GL(glBindTexture(textureTarget, 0)); - - // Create the frame buffer. - GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); - GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); - GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, frameBuffer->DepthBuffers[i], 0, 0, 2)); + GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexture, 0, 0, 2)); + GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 0, 2)); GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0, 0, 2)); } else { - // Create the color buffer texture. - GLenum colorTextureTarget = GL_TEXTURE_2D; - GL(glBindTexture(colorTextureTarget, colorTexture)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GL(glBindTexture(colorTextureTarget, 0)); - - // Create depth buffer. - GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); - GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); - GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height)); - GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); - - // Create the frame buffer. - GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); - GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); - GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); - GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0)); + GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0)); GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); } GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); @@ -151,14 +136,10 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); - if (frameBuffer->Multiview) { - GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); - } else { - GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); - } OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + OXR(xrDestroySwapchain(frameBuffer->DepthSwapChain.Handle)); free(frameBuffer->ColorSwapChainImage); - free(frameBuffer->DepthBuffers); + free(frameBuffer->DepthSwapChainImage); free(frameBuffer->FrameBuffers); ovrFramebuffer_Clear(frameBuffer); @@ -216,15 +197,24 @@ ovrRenderer */ void ovrRenderer_Clear(ovrRenderer* renderer) { - ovrFramebuffer_Clear(&renderer->FrameBuffer); + for (int i = 0; i < ovrMaxNumEyes; i++) { + ovrFramebuffer_Clear(&renderer->FrameBuffer[i]); + } } void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview) { - ovrFramebuffer_Create(session, &renderer->FrameBuffer, width, height, multiview); + renderer->Multiview = multiview; + int instances = renderer->Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer_Create(session, &renderer->FrameBuffer[i], width, height, multiview); + } } void ovrRenderer_Destroy(ovrRenderer* renderer) { - ovrFramebuffer_Destroy(&renderer->FrameBuffer); + int instances = renderer->Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[i]); + } } /* diff --git a/Common/VR/VRInput.cpp b/Common/VR/VRInput.cpp index b25bc4b6ca..63fc97a030 100644 --- a/Common/VR/VRInput.cpp +++ b/Common/VR/VRInput.cpp @@ -506,3 +506,12 @@ uint32_t IN_VRGetButtonState( int controllerIndex ) { XrVector2f IN_VRGetJoystickState( int controllerIndex ) { return moveJoystickState[controllerIndex].currentState; } + +XrPosef IN_VRGetPose( int controllerIndex ) { + engine_t* engine = VR_GetEngine(); + XrSpaceLocation loc = {}; + loc.type = XR_TYPE_SPACE_LOCATION; + XrSpace aimSpace[] = { leftControllerAimSpace, rightControllerAimSpace }; + xrLocateSpace(aimSpace[controllerIndex], engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc); + return loc.pose; +} diff --git a/Common/VR/VRInput.h b/Common/VR/VRInput.h index 241bf2791e..126cc56eaa 100644 --- a/Common/VR/VRInput.h +++ b/Common/VR/VRInput.h @@ -32,4 +32,5 @@ void IN_VRInit( engine_t *engine ); void IN_VRInputFrame( engine_t* engine ); uint32_t IN_VRGetButtonState( int controllerIndex ); XrVector2f IN_VRGetJoystickState( int controllerIndex ); +XrPosef IN_VRGetPose( int controllerIndex ); void INVR_Vibrate( int duration, int chan, float intensity ); diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp index 343a496620..bc066035d2 100644 --- a/Common/VR/VRRenderer.cpp +++ b/Common/VR/VRRenderer.cpp @@ -16,9 +16,6 @@ GLboolean initialized = GL_FALSE; GLboolean stageSupported = GL_FALSE; int vrConfig[VR_CONFIG_MAX] = {}; -float menuPitch = 0; -float menuYaw = 0; -float recenterYaw = 0; XrVector3f hmdorientation; XrVector3f hmdposition; @@ -135,7 +132,8 @@ void VR_Recenter(engine_t* engine) { OXR(xrLocateSpace(engine->appState.HeadSpace, engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc)); hmdorientation = XrQuaternionf_ToEulerAngles(loc.pose.orientation); - recenterYaw += ToRadians(hmdorientation.y); + vrConfig[VR_CONFIG_RECENTER_YAW] += (int)hmdorientation.y; + float recenterYaw = ToRadians((float)vrConfig[VR_CONFIG_RECENTER_YAW]); spaceCreateInfo.poseInReferenceSpace.orientation.x = 0; spaceCreateInfo.poseInReferenceSpace.orientation.y = sin(recenterYaw / 2); spaceCreateInfo.poseInReferenceSpace.orientation.z = 0; @@ -171,8 +169,8 @@ void VR_Recenter(engine_t* engine) { } // Update menu orientation - menuPitch = hmdorientation.x; - menuYaw = 0; + vrConfig[VR_CONFIG_MENU_PITCH] = (int)hmdorientation.x; + vrConfig[VR_CONFIG_MENU_YAW] = 0; } void VR_InitRenderer( engine_t* engine, bool multiview ) { @@ -182,6 +180,8 @@ void VR_InitRenderer( engine_t* engine, bool multiview ) { int eyeW, eyeH; VR_GetResolution(engine, &eyeW, &eyeH); + vrConfig[VR_CONFIG_VIEWPORT_WIDTH] = eyeW; + vrConfig[VR_CONFIG_VIEWPORT_HEIGHT] = eyeH; // Get the viewport configuration info for the chosen viewport configuration type. engine->appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; @@ -235,7 +235,7 @@ void VR_ClearFrameBuffer( int width, int height) { glDisable( GL_SCISSOR_TEST ); } -bool VR_BeginFrame( engine_t* engine ) { +bool VR_InitFrame( engine_t* engine ) { GLboolean stageBoundsDirty = GL_TRUE; if (ovrApp_HandleXrEvents(&engine->appState)) { VR_Recenter(engine); @@ -304,16 +304,21 @@ bool VR_BeginFrame( engine_t* engine ) { engine->appState.LayerCount = 0; memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); - - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer; - ovrFramebuffer_Acquire(frameBuffer); - ovrFramebuffer_SetCurrent(frameBuffer); - VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); return true; } +void VR_BeginFrame( engine_t* engine, int fboIndex ) { + vrConfig[VR_CONFIG_CURRENT_FBO] = fboIndex; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; + ovrFramebuffer_Acquire(frameBuffer); + ovrFramebuffer_SetCurrent(frameBuffer); + VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height); +} + void VR_EndFrame( engine_t* engine ) { + int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO]; + // Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully VR_BindFramebuffer(engine); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); @@ -321,22 +326,38 @@ void VR_EndFrame( engine_t* engine ) { glClear(GL_COLOR_BUFFER_BIT); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer; + // Show mouse cursor + int size = vrConfig[VR_CONFIG_MOUSE_SIZE]; + if ((vrConfig[VR_CONFIG_MODE] == VR_MODE_FLAT_SCREEN) && (size > 0)) { + glEnable(GL_SCISSOR_TEST); + glScissor(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); + glViewport(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size); + glClearColor(1, 1, 1, 1); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; //ovrFramebuffer_Resolve(frameBuffer); ovrFramebuffer_Release(frameBuffer); ovrFramebuffer_SetNone(); +} + +void VR_FinishFrame( engine_t* engine ) { - XrCompositionLayerProjectionView projection_layer_elements[2] = {}; int vrMode = vrConfig[VR_CONFIG_MODE]; + XrCompositionLayerProjectionView projection_layer_elements[2] = {}; if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) { - menuYaw = hmdorientation.y; + vrConfig[VR_CONFIG_MENU_YAW] = (int)hmdorientation.y; for (int eye = 0; eye < ovrMaxNumEyes; eye++) { - int imageLayer = eye; + int imageLayer = engine->appState.Renderer.Multiview ? eye : 0; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[0]; XrFovf fov = projections[eye].fov; if (vrMode == VR_MODE_MONO_6DOF) { fov = projections[0].fov; - imageLayer = 0; + } else if (!engine->appState.Renderer.Multiview) { + frameBuffer = &engine->appState.Renderer.FrameBuffer[eye]; } memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); @@ -365,28 +386,30 @@ void VR_EndFrame( engine_t* engine ) { } else if (vrMode == VR_MODE_FLAT_SCREEN) { // Build the cylinder layer - int width = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Width; - int height = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Height; + int width = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Width; + int height = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Height; XrCompositionLayerCylinderKHR cylinder_layer = {}; cylinder_layer.type = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR; cylinder_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; cylinder_layer.space = engine->appState.CurrentSpace; cylinder_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; memset(&cylinder_layer.subImage, 0, sizeof(XrSwapchainSubImage)); - cylinder_layer.subImage.swapchain = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Handle; + cylinder_layer.subImage.swapchain = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Handle; cylinder_layer.subImage.imageRect.offset.x = 0; cylinder_layer.subImage.imageRect.offset.y = 0; cylinder_layer.subImage.imageRect.extent.width = width; cylinder_layer.subImage.imageRect.extent.height = height; cylinder_layer.subImage.imageArrayIndex = 0; - float distance = vrConfig[VR_CONFIG_CANVAS_DISTANCE]; + float distance = (float)vrConfig[VR_CONFIG_CANVAS_DISTANCE]; + float menuPitch = ToRadians((float)vrConfig[VR_CONFIG_MENU_PITCH]); + float menuYaw = ToRadians((float)vrConfig[VR_CONFIG_MENU_YAW]); XrVector3f pos = { - invViewTransform[0].position.x - sin(ToRadians(menuYaw)) * distance, + invViewTransform[0].position.x - sin(menuYaw) * distance, invViewTransform[0].position.y, - invViewTransform[0].position.z - cos(ToRadians(menuYaw)) * distance + invViewTransform[0].position.z - cos(menuYaw) * distance }; - XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -ToRadians(menuPitch)); - XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, ToRadians(menuYaw)); + XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -menuPitch); + XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, menuYaw); cylinder_layer.pose.orientation = XrQuaternionf_Multiply(pitch, yaw); cylinder_layer.pose.position = pos; cylinder_layer.radius = 12.0f; @@ -412,8 +435,12 @@ void VR_EndFrame( engine_t* engine ) { endFrameInfo.layers = layers; OXR(xrEndFrame(engine->appState.Session, &endFrameInfo)); - frameBuffer->TextureSwapChainIndex++; - frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength; + int instances = engine->appState.Renderer.Multiview ? 1 : ovrMaxNumEyes; + for (int i = 0; i < instances; i++) { + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[instances]; + frameBuffer->TextureSwapChainIndex++; + frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength; + } } int VR_GetConfig( VRConfig config ) { @@ -426,7 +453,8 @@ void VR_SetConfig( VRConfig config, int value) { void VR_BindFramebuffer(engine_t *engine) { if (!initialized) return; - ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer; + int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO]; + ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex]; unsigned int swapchainIndex = frameBuffer->TextureSwapChainIndex; unsigned int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex]; GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glFramebuffer)); diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h index 872bda7772..abec8dfbaf 100644 --- a/Common/VR/VRRenderer.h +++ b/Common/VR/VRRenderer.h @@ -4,19 +4,24 @@ #include "VRMath.h" enum VRConfig { - VR_CONFIG_MODE, - VR_CONFIG_6DOF_ENABLED, - VR_CONFIG_6DOF_SCALE, - VR_CONFIG_MIRROR_AXIS_X, - VR_CONFIG_MIRROR_AXIS_Y, - VR_CONFIG_MIRROR_AXIS_Z, - VR_CONFIG_MIRROR_PITCH, - VR_CONFIG_MIRROR_YAW, - VR_CONFIG_MIRROR_ROLL, - VR_CONFIG_3D_GEOMETRY_COUNT, - VR_CONFIG_FOV_SCALE, - VR_CONFIG_FORCE_2D, - VR_CONFIG_CANVAS_DISTANCE, + //switching between 2D and 3D + VR_CONFIG_MODE, VR_CONFIG_3D_GEOMETRY_COUNT, VR_CONFIG_FORCE_2D, + //camera setup + VR_CONFIG_FOV_SCALE, VR_CONFIG_CANVAS_DISTANCE, + //6DoF + VR_CONFIG_6DOF_ENABLED, VR_CONFIG_6DOF_SCALE, + VR_CONFIG_MIRROR_AXIS_X, VR_CONFIG_MIRROR_AXIS_Y, VR_CONFIG_MIRROR_AXIS_Z, + VR_CONFIG_MIRROR_PITCH, VR_CONFIG_MIRROR_YAW, VR_CONFIG_MIRROR_ROLL, + //2D canvas positioning + VR_CONFIG_MENU_PITCH, VR_CONFIG_MENU_YAW, VR_CONFIG_RECENTER_YAW, + //mouse cursor + VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y, + //viewport setup + VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, VR_CONFIG_VIEWPORT_VALID, + //render status + VR_CONFIG_CURRENT_FBO, + + //end VR_CONFIG_MAX }; @@ -38,8 +43,10 @@ void VR_GetResolution( engine_t* engine, int *pWidth, int *pHeight ); void VR_InitRenderer( engine_t* engine, bool multiview ); void VR_DestroyRenderer( engine_t* engine ); -bool VR_BeginFrame( engine_t* engine ); +bool VR_InitFrame( engine_t* engine ); +void VR_BeginFrame( engine_t* engine, int fboIndex ); void VR_EndFrame( engine_t* engine ); +void VR_FinishFrame( engine_t* engine ); int VR_GetConfig( VRConfig config ); void VR_SetConfig( VRConfig config, int value); diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 4487c3f203..e68629dc2d 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -67,7 +67,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "RequireBufferedRendering", &flags_.RequireBufferedRendering); CheckSetting(iniFile, gameID, "RequireBlockTransfer", &flags_.RequireBlockTransfer); CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock); - CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks); CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth); CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack); CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack); @@ -97,6 +96,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "AllowLargeFBTextureOffsets", &flags_.AllowLargeFBTextureOffsets); CheckSetting(iniFile, gameID, "AtracLoopHack", &flags_.AtracLoopHack); CheckSetting(iniFile, gameID, "DeswizzleDepth", &flags_.DeswizzleDepth); + CheckSetting(iniFile, gameID, "SplitFramebufferMargin", &flags_.SplitFramebufferMargin); CheckSetting(iniFile, gameID, "VRClearFramebuffer", &flags_.VRClearFramebuffer); } diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 941844bd85..3f363adbeb 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -57,7 +57,6 @@ struct CompatFlags { bool RequireBufferedRendering; bool RequireBlockTransfer; bool RequireDefaultCPUClock; - bool DisableReadbacks; bool DisableAccurateDepth; bool MGS2AcidHack; bool SonicRivalsHack; @@ -87,6 +86,7 @@ struct CompatFlags { bool AllowLargeFBTextureOffsets; bool AtracLoopHack; bool DeswizzleDepth; + bool SplitFramebufferMargin; bool VRClearFramebuffer; }; diff --git a/Core/Config.cpp b/Core/Config.cpp index 3c24061088..e75554ae73 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -935,7 +935,6 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("ShaderChainRequires60FPS", &g_Config.bShaderChainRequires60FPS, false, true, true), ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true), - ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false, true, true), ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true), ConfigSetting("GfxDebugOutput", &g_Config.bGfxDebugOutput, false, false, false), diff --git a/Core/Config.h b/Core/Config.h index 60ec8eb96b..6ae172bfba 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -232,7 +232,6 @@ public: float fGameListScrollPosition; int iBloomHack; //0 = off, 1 = safe, 2 = balanced, 3 = aggressive bool bBlockTransferGPU; - bool bDisableSlowFramebufEffects; bool bFragmentTestCache; int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High bool bHardwareTessellation; diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index f0be93ea60..e38b6861b7 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -37,7 +37,8 @@ public: bool Mark(uint32_t addr, uint32_t size, uint64_t ticks, uint32_t pc, bool allocated, const char *tag); bool Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::vector &results); - bool FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size, std::string &result); + // Note that the returned pointer gets invalidated as soon as Mark is called. + const char *FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size); void Reset(); void DoState(PointerWrap &p); @@ -151,17 +152,16 @@ bool MemSlabMap::Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::ve return found; } -bool MemSlabMap::FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size, std::string &result) { +const char *MemSlabMap::FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size) { uint32_t end = addr + size; Slab *slab = FindSlab(addr); while (slab != nullptr && slab->start < end) { if (slab->pc != 0 || slab->tag[0] != '\0') { - result = slab->tag; - return true; + return slab->tag; } slab = slab->next; } - return false; + return nullptr; } void MemSlabMap::Reset() { @@ -485,7 +485,7 @@ std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, return results; } -static std::string FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) { +static const char *FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) { start &= ~0xC0000000; if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start) @@ -493,37 +493,51 @@ static std::string FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint3 if (pendingNotifyMinAddr2 < start + size && pendingNotifyMaxAddr2 >= start) FlushPendingMemInfo(); - std::string tag; if (flags & MemBlockFlags::ALLOC) { - if (allocMap.FastFindWriteTag(MemBlockFlags::ALLOC, start, size, tag)) + const char *tag = allocMap.FastFindWriteTag(MemBlockFlags::ALLOC, start, size); + if (tag) return tag; } if (flags & MemBlockFlags::SUB_ALLOC) { - if (suballocMap.FastFindWriteTag(MemBlockFlags::SUB_ALLOC, start, size, tag)) + const char *tag = suballocMap.FastFindWriteTag(MemBlockFlags::SUB_ALLOC, start, size); + if (tag) return tag; } if (flags & MemBlockFlags::WRITE) { - if (writeMap.FastFindWriteTag(MemBlockFlags::WRITE, start, size, tag)) + const char *tag = writeMap.FastFindWriteTag(MemBlockFlags::WRITE, start, size); + if (tag) return tag; } if (flags & MemBlockFlags::TEXTURE) { - if (textureMap.FastFindWriteTag(MemBlockFlags::TEXTURE, start, size, tag)) + const char *tag = textureMap.FastFindWriteTag(MemBlockFlags::TEXTURE, start, size); + if (tag) return tag; } - return ""; + return nullptr; } -std::string GetMemWriteTagAt(uint32_t start, uint32_t size) { - std::string tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size); - if (!tag.empty() && tag != "MemInit") - return tag; - +std::string GetMemWriteTagAt(const char *prefix, uint32_t start, uint32_t size) { + const char *tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size); + if (tag && strcmp(tag, "MemInit") != 0) + return std::string(prefix) + tag; // Fall back to alloc and texture, especially for VRAM. We prefer write above. tag = FindWriteTagByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size); - if (!tag.empty()) - return tag; + if (tag) + return std::string(prefix) + tag; + return StringFromFormat("%s%08x_size_%08x", prefix, start, size); +} - return StringFromFormat("%08x_size_%08x", start, size); +size_t FormatMemWriteTagAt(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size) { + const char *tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size); + if (tag && strcmp(tag, "MemInit") != 0) { + return snprintf(buf, sz, "%s%s", prefix, tag); + } + // Fall back to alloc and texture, especially for VRAM. We prefer write above. + tag = FindWriteTagByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size); + if (tag) { + return snprintf(buf, sz, "%s%s", prefix, tag); + } + return snprintf(buf, sz, "%s%08x_size_%08x", prefix, start, size); } void MemBlockInfoInit() { diff --git a/Core/Debugger/MemBlockInfo.h b/Core/Debugger/MemBlockInfo.h index 50e3bfae53..839249f3a2 100644 --- a/Core/Debugger/MemBlockInfo.h +++ b/Core/Debugger/MemBlockInfo.h @@ -68,7 +68,9 @@ inline void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, co std::vector FindMemInfo(uint32_t start, uint32_t size); std::vector FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, uint32_t size); -std::string GetMemWriteTagAt(uint32_t start, uint32_t size); +std::string GetMemWriteTagAt(const char *prefix, uint32_t start, uint32_t size); +// Same as above but allocation-free. +size_t FormatMemWriteTagAt(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size); void MemBlockInfoInit(); void MemBlockInfoShutdown(); diff --git a/Core/Dialog/PSPDialog.h b/Core/Dialog/PSPDialog.h index 636fc20334..673bd54f9b 100644 --- a/Core/Dialog/PSPDialog.h +++ b/Core/Dialog/PSPDialog.h @@ -129,6 +129,6 @@ protected: private: DialogStatus status = SCE_UTILITY_STATUS_NONE; - UtilityDialogType dialogType_; + UtilityDialogType dialogType_ = UtilityDialogType::NONE; bool volatileLocked_ = false; }; diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index 0e550a9d2e..9fe30db3c8 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -116,9 +116,9 @@ int PSPGamedataInstallDialog::Update(int animSpeed) { WriteSfoFile(); // TODO: What is this? Should one of these update per file or anything? - request.unknownResult1 = readFiles; - request.unknownResult2 = readFiles; - Memory::WriteStruct(param.ptr, &request); + param->unknownResult1 = readFiles; + param->unknownResult2 = readFiles; + param.NotifyWrite("DialogResult"); ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); } @@ -250,8 +250,9 @@ void PSPGamedataInstallDialog::UpdateProgress() { progressValue = (int)((allReadSize * 100) / allFilesSize); else progressValue = 100; - request.progress = progressValue; - Memory::WriteStruct(param.ptr, &request); + + param->progress = progressValue; + param.NotifyWrite("DialogResult"); } void PSPGamedataInstallDialog::DoState(PointerWrap &p) { diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index e7bdf9ad46..8599a1428d 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -285,9 +285,6 @@ struct SaveFileInfo PPGeImage *texture = nullptr; - SaveFileInfo() { - } - void DoState(PointerWrap &p); }; diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 57603ccf2c..462bad0a7e 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -159,7 +159,7 @@ static int Replace_memcpy() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpy/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); @@ -211,7 +211,7 @@ static int Replace_memcpy_jak() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpy/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); @@ -249,7 +249,7 @@ static int Replace_memcpy16() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemcpy16/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpy16/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); } @@ -290,7 +290,7 @@ static int Replace_memcpy_swizzled() { RETURN(0); if (MemBlockInfoDetailed(pitch * h)) { - const std::string tag = "ReplaceMemcpySwizzle/" + GetMemWriteTagAt(srcPtr, pitch * h); + const std::string tag = GetMemWriteTagAt("ReplaceMemcpySwizzle/", srcPtr, pitch * h); NotifyMemInfo(MemBlockFlags::READ, srcPtr, pitch * h, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, pitch * h, tag.c_str(), tag.size()); } @@ -321,7 +321,7 @@ static int Replace_memmove() { RETURN(destPtr); if (MemBlockInfoDetailed(bytes)) { - const std::string tag = "ReplaceMemmove/" + GetMemWriteTagAt(srcPtr, bytes); + const std::string tag = GetMemWriteTagAt("ReplaceMemmove/", srcPtr, bytes); NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size()); } diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 9632d90e0b..f05ad9dfca 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -64,8 +64,8 @@ int srcFrequency = 0; const int hwSampleRate = 44100; -int hwBlockSize = 64; -int hostAttemptBlockSize = 512; +const int hwBlockSize = 64; +const int hostAttemptBlockSize = 512; static int audioIntervalCycles; static int audioHostIntervalCycles; @@ -82,6 +82,11 @@ static bool m_logAudio; static int chanQueueMaxSizeFactor; static int chanQueueMinSizeFactor; +// Accessor for libretro +int __AudioGetHostAttemptBlockSize() { + return hostAttemptBlockSize; +} + static void hleAudioUpdate(u64 userdata, int cyclesLate) { // Schedule the next cycle first. __AudioUpdate() may consume cycles. CoreTiming::ScheduleEvent(audioIntervalCycles - cyclesLate, eventAudioUpdate, 0); @@ -110,8 +115,6 @@ void __AudioInit() { chanQueueMaxSizeFactor = 2; chanQueueMinSizeFactor = 1; - hwBlockSize = 64; - hostAttemptBlockSize = 512; __AudioCPUMHzChange(); @@ -338,7 +341,8 @@ void __AudioUpdate(bool resetRecording) { // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio // if the buffer somehow gets full. bool firstChannel = true; - std::vector srcBuffer; + const int16_t srcBufferSize = hwBlockSize * 2; + int16_t srcBuffer[srcBufferSize]; for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) { if (!chans[i].reserved) @@ -351,7 +355,7 @@ void __AudioUpdate(bool resetRecording) { } bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency; - size_t sz = needsResample ? (hwBlockSize * 2 * srcFrequency) / mixFrequency : hwBlockSize * 2; + size_t sz = needsResample ? (srcBufferSize * srcFrequency) / mixFrequency : srcBufferSize; if (sz > chanSampleQueues[i].size()) { ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chanSampleQueues[i].size() / 2, (int)sz / 2); } @@ -372,13 +376,11 @@ void __AudioUpdate(bool resetRecording) { return buf1[sz1 - 1]; }; - srcBuffer.resize(hwBlockSize * 2); - // TODO: This is terrible, since it's doing it by small chunk and discarding frac. const uint32_t ratio = (uint32_t)(65536.0 * srcFrequency / (double)mixFrequency); uint32_t frac = 0; size_t readIndex = 0; - for (size_t outIndex = 0; readIndex < sz && outIndex < srcBuffer.size(); outIndex += 2) { + for (size_t outIndex = 0; readIndex < sz && outIndex < srcBufferSize; outIndex += 2) { size_t readIndex2 = readIndex + 2; int16_t l1 = read(readIndex); int16_t r1 = read(readIndex + 1); @@ -393,8 +395,8 @@ void __AudioUpdate(bool resetRecording) { frac &= 0xffff; } - buf1 = srcBuffer.data(); - sz1 = srcBuffer.size(); + buf1 = srcBuffer; + sz1 = srcBufferSize; buf2 = nullptr; sz2 = 0; } diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index ae9ca35126..f26ae0dc77 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -50,6 +50,8 @@ int __AudioMix(short *outstereo, int numSamples, int sampleRate); void __AudioGetDebugStats(char *buf, size_t bufSize); void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu! +int __AudioGetHostAttemptBlockSize(); + // Audio Dumping stuff void __StartLogAudio(const Path &filename); void __StopLogAudio(); diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 6ee643fe5a..b22c439bc1 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -1268,7 +1268,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 if (outbufPtr != 0) { u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16); if (packetAddr != 0 && MemBlockInfoDetailed()) { - const std::string tag = "AtracDecode/" + GetMemWriteTagAt(packetAddr, packetSize); + const std::string tag = GetMemWriteTagAt("AtracDecode/", packetAddr, packetSize); NotifyMemInfo(MemBlockFlags::READ, packetAddr, packetSize, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tag.c_str(), tag.size()); } else { diff --git a/Core/HLE/sceChnnlsv.cpp b/Core/HLE/sceChnnlsv.cpp index 82e063b344..fc73837687 100644 --- a/Core/HLE/sceChnnlsv.cpp +++ b/Core/HLE/sceChnnlsv.cpp @@ -214,13 +214,12 @@ static int sub_17A8(u8* data) return -261; } -static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) -{ - pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx, &ctx); - int res = sceSdGetLastIndex_(ctx, Memory::GetPointerWrite(addressHash), Memory::GetPointerWrite(addressKey)); - Memory::WriteStruct(addressCtx, &ctx); - return res; +static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) { + auto ctx = PSPPointer::Create(addressCtx); + u8 *hash = Memory::GetPointerWrite(addressHash); + if (!ctx.IsValid() || !hash) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceSdGetLastIndex_(*ctx, hash, Memory::GetPointerWrite(addressKey))); } int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) @@ -325,13 +324,11 @@ int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) return 0; } -static int sceSdSetIndex(u32 addressCtx, int value) -{ - pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx,&ctx); - int res = sceSdSetIndex_(ctx, value); - Memory::WriteStruct(addressCtx,&ctx); - return res; +static int sceSdSetIndex(u32 addressCtx, int value) { + auto ctx = PSPPointer::Create(addressCtx); + if (!ctx.IsValid()) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceSdSetIndex_(*ctx, value)); } int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value) @@ -344,14 +341,11 @@ int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value) } -static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) -{ - pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx, &ctx); - int res = sceSdRemoveValue_(ctx, Memory::GetPointerWrite(addressData), length); - Memory::WriteStruct(addressCtx, &ctx); - - return res; +static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) { + auto ctx = PSPPointer::Create(addressCtx); + if (!ctx.IsValid() || !Memory::IsValidAddress(addressData)) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceSdRemoveValue_(*ctx, Memory::GetPointerWrite(addressData), length)); } int sceSdRemoveValue_(pspChnnlsvContext1& ctx, u8* data, int length) @@ -396,18 +390,14 @@ int sceSdRemoveValue_(pspChnnlsvContext1& ctx, u8* data, int length) return 0; } -static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr) -{ - pspChnnlsvContext2 ctx2; - Memory::ReadStruct(ctx2Addr, &ctx2); +static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr) { + auto ctx2 = PSPPointer::Create(ctx2Addr); u8* data = Memory::GetPointerWrite(dataAddr); u8* cryptkey = Memory::GetPointerWrite(cryptkeyAddr); + if (!ctx2.IsValid() || !data) + return hleLogError(SCEMISC, 0, "Invalid pointer"); - int res = sceSdCreateList_(ctx2, mode, unkwn, data, cryptkey); - - Memory::WriteStruct(ctx2Addr, &ctx2); - - return res; + return hleLogSuccessI(SCEMISC, sceSdCreateList_(*ctx2, mode, unkwn, data, cryptkey)); } int sceSdCreateList_(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, u8* cryptkey) @@ -464,17 +454,13 @@ int sceSdCreateList_(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, u8* return 0; } -static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen) -{ - pspChnnlsvContext2 ctx; - Memory::ReadStruct(ctxAddr, &ctx); - u8* data = Memory::GetPointerWrite(dataAddr); +static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen) { + auto ctx = PSPPointer::Create(ctxAddr); + u8 *data = Memory::GetPointerWrite(dataAddr); + if (!ctx.IsValid() || !data) + return hleLogError(SCEMISC, 0, "Invalid pointer"); - int res = sceSdSetMember_(ctx, data, alignedLen); - - Memory::WriteStruct(ctxAddr, &ctx); - - return res; + return hleLogSuccessI(SCEMISC, sceSdSetMember_(*ctx, data, alignedLen)); } int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen) @@ -511,15 +497,11 @@ int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen) return res; } -static int sceChnnlsv_21BE78B4(u32 ctxAddr) -{ - pspChnnlsvContext2 ctx; - Memory::ReadStruct(ctxAddr, &ctx); - - int res = sceChnnlsv_21BE78B4_(ctx); - - Memory::WriteStruct(ctxAddr, &ctx); - return res; +static int sceChnnlsv_21BE78B4(u32 ctxAddr) { + auto ctx = PSPPointer::Create(ctxAddr); + if (!ctx.IsValid()) + return hleLogError(SCEMISC, 0, "Invalid pointer"); + return hleLogSuccessI(SCEMISC, sceChnnlsv_21BE78B4_(*ctx)); } int sceChnnlsv_21BE78B4_(pspChnnlsvContext2& ctx) diff --git a/Core/HLE/sceDeflt.cpp b/Core/HLE/sceDeflt.cpp index d5580dcc40..985b89d543 100644 --- a/Core/HLE/sceDeflt.cpp +++ b/Core/HLE/sceDeflt.cpp @@ -59,7 +59,7 @@ static int CommonDecompress(int windowBits, u32 OutBuffer, int OutBufferLength, } if (MemBlockInfoDetailed(stream.total_in, stream.total_out)) { - const std::string tag = "sceDeflt/" + GetMemWriteTagAt(InBuffer, stream.total_in); + const std::string tag = GetMemWriteTagAt("sceDeflt/", InBuffer, stream.total_in); NotifyMemInfo(MemBlockFlags::READ, InBuffer, stream.total_in, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, OutBuffer, stream.total_out, tag.c_str(), tag.size()); } diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 7db5998ff7..d0577d46f2 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -823,7 +823,10 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) s64 delayCycles = 0; // Don't count transitions between display off and display on. - if (topaddr != 0 && topaddr != framebuf.topaddr && framebuf.topaddr != 0 && PSP_CoreParameter().compat.flags().ForceMax60FPS) { + if (topaddr != 0 && + (topaddr != framebuf.topaddr || PSP_CoreParameter().compat.flags().SplitFramebufferMargin) && + framebuf.topaddr != 0 && + PSP_CoreParameter().compat.flags().ForceMax60FPS) { // sceDisplaySetFramebuf() isn't supposed to delay threads at all. This is a hack. // So let's only delay when it's more than 1ms. const s64 FLIP_DELAY_CYCLES_MIN = usToCycles(1000); diff --git a/Core/HLE/sceDmac.cpp b/Core/HLE/sceDmac.cpp index 86c8e60a1e..5fba8f9b94 100644 --- a/Core/HLE/sceDmac.cpp +++ b/Core/HLE/sceDmac.cpp @@ -52,7 +52,7 @@ static int __DmacMemcpy(u32 dst, u32 src, u32 size) { if (!skip) { currentMIPS->InvalidateICache(src, size); if (MemBlockInfoDetailed(size)) { - const std::string tag = "DmacMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("DmacMemcpy/", src, size); Memory::Memcpy(dst, src, size, tag.c_str(), tag.size()); } else { Memory::Memcpy(dst, src, size, "DmacMemcpy"); diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index e91eeef88b..d55ce61514 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -486,8 +486,8 @@ public: // For save states only. } - FontLib(u32 paramPtr, u32 errorCodePtr) { - Memory::ReadStruct(paramPtr, ¶ms_); + FontLib(FontNewLibParams *params, u32 errorCodePtr) { + params_ = *params; if (params_.numFonts > 9) { params_.numFonts = 9; } @@ -998,7 +998,7 @@ static u32 sceFontNewLib(u32 paramPtr, u32 errorCodePtr) { INFO_LOG(SCEFONT, "sceFontNewLib(%08x, %08x)", paramPtr, errorCodePtr); *errorCode = 0; - FontLib *newLib = new FontLib(paramPtr, errorCodePtr); + FontLib *newLib = new FontLib(params, errorCodePtr); fontLibList.push_back(newLib); // The game should never see this value, the return value is replaced // by the action. Except if we disable the alloc, in this case we return diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index e56f631c16..46230373d2 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -427,8 +427,6 @@ static int sceGeBreak(u32 mode, u32 unknownPtr) { } static u32 sceGeSetCallback(u32 structAddr) { - DEBUG_LOG(SCEGE, "sceGeSetCallback(struct=%08x)", structAddr); - int cbID = -1; for (size_t i = 0; i < ARRAY_SIZE(ge_used_callbacks); ++i) { if (!ge_used_callbacks[i]) { @@ -438,12 +436,13 @@ static u32 sceGeSetCallback(u32 structAddr) { } if (cbID == -1) { - WARN_LOG(SCEGE, "sceGeSetCallback(): out of callback ids"); - return SCE_KERNEL_ERROR_OUT_OF_MEMORY; + return hleLogWarning(SCEGE, SCE_KERNEL_ERROR_OUT_OF_MEMORY, "out of callback ids"); } ge_used_callbacks[cbID] = true; - Memory::ReadStruct(structAddr, &ge_callback_data[cbID]); + auto callbackData = PSPPointer::Create(structAddr); + ge_callback_data[cbID] = *callbackData; + callbackData.NotifyRead("GeSetCallback"); int subIntrBase = __GeSubIntrBase(cbID); @@ -458,7 +457,7 @@ static u32 sceGeSetCallback(u32 structAddr) { sceKernelEnableSubIntr(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL); } - return cbID; + return hleLogSuccessI(SCEGE, cbID); } static int sceGeUnsetCallback(u32 cbID) { @@ -603,7 +602,7 @@ const HLEFunction sceGe_user[] = { {0XB287BD61, &WrapU_U, "sceGeDrawSync", 'x', "x" }, {0XB448EC0D, &WrapI_UU, "sceGeBreak", 'i', "xx" }, {0X4C06E472, &WrapI_V, "sceGeContinue", 'i', "" }, - {0XA4FC06A4, &WrapU_U, "sceGeSetCallback", 'x', "x" }, + {0XA4FC06A4, &WrapU_U, "sceGeSetCallback", 'i', "p" }, {0X05DB22CE, &WrapI_U, "sceGeUnsetCallback", 'i', "x" }, {0X1F6752AD, &WrapU_V, "sceGeEdramGetSize", 'x', "" }, {0XB77905EA, &WrapU_I, "sceGeEdramSetAddrTranslation", 'x', "i" }, diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 575f4be959..9e161828a8 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -931,12 +931,12 @@ static u32 sceIoGetstat(const char *filename, u32 addr) { // TODO: Improve timing (although this seems normally slow..) int usec = 1000; - SceIoStat stat; + auto stat = PSPPointer::Create(addr); PSPFileInfo info = pspFileSystem.GetFileInfo(filename); if (info.exists) { - __IoGetStat(&stat, info); - if (Memory::IsValidAddress(addr)) { - Memory::WriteStruct(addr, &stat); + if (stat.IsValid()) { + __IoGetStat(stat, info); + stat.NotifyWrite("IoGetstat"); DEBUG_LOG(SCEIO, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr, info.startSector); return hleDelayResult(0, "io getstat", usec); } else { @@ -1798,19 +1798,23 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; } // TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace? - if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr - u32 pointer = Memory::Read_U32(argAddr); + if (Memory::IsValidRange(argAddr, 4) && argLen >= 4) { // NOTE: not outPtr + u32 pointer = Memory::ReadUnchecked_U32(argAddr); u32 sectorSize = 0x200; u32 memStickSectorSize = 32 * 1024; u32 sectorCount = memStickSectorSize / sectorSize; u64 freeSize = 1 * 1024 * 1024 * 1024; - DeviceSize deviceSize; - deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); - deviceSize.freeClusters = deviceSize.maxClusters; - deviceSize.maxSectors = deviceSize.maxClusters; - deviceSize.sectorSize = sectorSize; - deviceSize.sectorCount = sectorCount; - Memory::WriteStruct(pointer, &deviceSize); + + auto deviceSize = PSPPointer::Create(pointer); + if (deviceSize.IsValid()) { + deviceSize->maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); + deviceSize->freeClusters = deviceSize->maxClusters; + deviceSize->maxSectors = deviceSize->maxClusters; + deviceSize->sectorSize = sectorSize; + deviceSize->sectorCount = sectorCount; + deviceSize.NotifyWrite("ms0:02425818"); + } + return 0; } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; @@ -1821,8 +1825,8 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) { return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; } - if (Memory::IsValidAddress(outPtr) && outLen == 4) { - Memory::Write_U32(0, outPtr); + if (Memory::IsValidRange(outPtr, 4) && outLen == 4) { + Memory::WriteUnchecked_U32(0, outPtr); return 0; } else { ERROR_LOG(SCEIO, "Failed 0x02425824 fat"); @@ -1939,13 +1943,16 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o u32 memStickSectorSize = 32 * 1024; u32 sectorCount = memStickSectorSize / sectorSize; u64 freeSize = 1 * 1024 * 1024 * 1024; - DeviceSize deviceSize; - deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); - deviceSize.freeClusters = deviceSize.maxClusters; - deviceSize.maxSectors = deviceSize.maxClusters; - deviceSize.sectorSize = sectorSize; - deviceSize.sectorCount = sectorCount; - Memory::WriteStruct(pointer, &deviceSize); + + auto deviceSize = PSPPointer::Create(pointer); + if (deviceSize.IsValid()) { + deviceSize->maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount)); + deviceSize->freeClusters = deviceSize->maxClusters; + deviceSize->maxSectors = deviceSize->maxClusters; + deviceSize->sectorSize = sectorSize; + deviceSize->sectorCount = sectorCount; + deviceSize.NotifyWrite("fatms0:02425818"); + } return 0; } else { return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS; diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 738ce25303..cb858c0e90 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -646,12 +646,12 @@ struct SystemStatus { static int sceKernelReferSystemStatus(u32 statusPtr) { DEBUG_LOG(SCEKERNEL, "sceKernelReferSystemStatus(%08x)", statusPtr); - if (Memory::IsValidAddress(statusPtr)) { - SystemStatus status; - memset(&status, 0, sizeof(SystemStatus)); - status.size = sizeof(SystemStatus); + auto status = PSPPointer::Create(statusPtr); + if (status.IsValid()) { + memset((SystemStatus *)status, 0, sizeof(SystemStatus)); + status->size = sizeof(SystemStatus); // TODO: Fill in the struct! - Memory::WriteStruct(statusPtr, &status); + status.NotifyWrite("SystemStatus"); } return 0; } @@ -684,11 +684,11 @@ static u32 sceKernelReferThreadProfiler(u32 statusPtr) { // Can we confirm that the struct above is the right struct? // If so, re-enable this code. - //DebugProfilerRegs regs; - //memset(®s, 0, sizeof(regs)); + //auto regs = PSPPointer::Create(statusPtr); // TODO: fill the struct. - //if (Memory::IsValidAddress(statusPtr)) { - // Memory::WriteStruct(statusPtr, ®s); + //if (regs.IsValid()) { + // memset((DebugProfilerRegs *)regs, 0, sizeof(DebugProfilerRegs)); + // regs.NotifyWrite("ThreadProfiler"); //} return 0; } @@ -715,14 +715,14 @@ const HLEFunction ThreadManForUser[] = {0XD6DA4BA1, &WrapI_CUIIU, "sceKernelCreateSema", 'i', "sxiix" }, {0X28B6489C, &WrapI_I, "sceKernelDeleteSema", 'i', "i" }, {0X58B1F937, &WrapI_II, "sceKernelPollSema", 'i', "ii" }, - {0XBC6FEBC5, &WrapI_IU, "sceKernelReferSemaStatus", 'i', "ix" }, + {0XBC6FEBC5, &WrapI_IU, "sceKernelReferSemaStatus", 'i', "ip" }, {0X3F53E640, &WrapI_II, "sceKernelSignalSema", 'i', "ii" }, {0X4E3A1105, &WrapI_IIU, "sceKernelWaitSema", 'i', "iix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X6D212BAC, &WrapI_IIU, "sceKernelWaitSemaCB", 'i', "iix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X60107536, &WrapI_U, "sceKernelDeleteLwMutex", 'i', "x" }, {0X19CFF145, &WrapI_UCUIU, "sceKernelCreateLwMutex", 'i', "xsxix" }, - {0X4C145944, &WrapI_IU, "sceKernelReferLwMutexStatusByID", 'i', "ix" }, + {0X4C145944, &WrapI_IU, "sceKernelReferLwMutexStatusByID", 'i', "xp" }, // NOTE: LockLwMutex, UnlockLwMutex, and ReferLwMutexStatus are in Kernel_Library, see sceKernelInterrupt.cpp. // The below should not be called directly. //{0x71040D5C, nullptr, "_sceKernelTryLockLwMutex", '?', "" }, @@ -736,7 +736,7 @@ const HLEFunction ThreadManForUser[] = {0X6B30100F, &WrapI_II, "sceKernelUnlockMutex", 'i', "ii" }, {0XB7D098C6, &WrapI_CUIU, "sceKernelCreateMutex", 'i', "sxix" }, {0X0DDCD2C9, &WrapI_II, "sceKernelTryLockMutex", 'i', "ii" }, - {0XA9C2CB9A, &WrapI_IU, "sceKernelReferMutexStatus", 'i', "ix" }, + {0XA9C2CB9A, &WrapI_IU, "sceKernelReferMutexStatus", 'i', "ip" }, {0X87D9223C, &WrapI_IIU, "sceKernelCancelMutex", 'i', "iix" }, {0XFCCFAD26, &WrapI_I, "sceKernelCancelWakeupThread", 'i', "i" }, @@ -818,7 +818,7 @@ const HLEFunction ThreadManForUser[] = {0XF3986382, &WrapI_IUU, "sceKernelReceiveMbxCB", 'i', "ixx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X0D81716A, &WrapI_IU, "sceKernelPollMbx", 'i', "ix" }, {0X87D4DD36, &WrapI_IU, "sceKernelCancelReceiveMbx", 'i', "ix" }, - {0XA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ix" }, + {0XA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ip" }, {0X7C0DC2A0, &WrapI_CIUUU, "sceKernelCreateMsgPipe", 'i', "sixxx" }, {0XF0B7DA1C, &WrapI_I, "sceKernelDeleteMsgPipe", 'i', "i" }, @@ -829,7 +829,7 @@ const HLEFunction ThreadManForUser[] = {0XFBFA697D, &WrapI_IUUUUU, "sceKernelReceiveMsgPipeCB", 'i', "ixxxxx" }, {0XDF52098F, &WrapI_IUUUU, "sceKernelTryReceiveMsgPipe", 'i', "ixxxx" }, {0X349B864D, &WrapI_IUU, "sceKernelCancelMsgPipe", 'i', "ixx" }, - {0X33BE4024, &WrapI_IU, "sceKernelReferMsgPipeStatus", 'i', "ix" }, + {0X33BE4024, &WrapI_IU, "sceKernelReferMsgPipeStatus", 'i', "ip" }, {0X56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxx" }, {0X89B3D48C, &WrapI_I, "sceKernelDeleteVpl", 'i', "i" }, @@ -838,7 +838,7 @@ const HLEFunction ThreadManForUser[] = {0XAF36D708, &WrapI_IUU, "sceKernelTryAllocateVpl", 'i', "ixx" }, {0XB736E9FF, &WrapI_IU, "sceKernelFreeVpl", 'i', "ix" }, {0X1D371B8A, &WrapI_IU, "sceKernelCancelVpl", 'i', "ix" }, - {0X39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ix" }, + {0X39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ip" }, {0XC07BB470, &WrapI_CUUUUU, "sceKernelCreateFpl", 'i', "sxxxxx" }, {0XED1410E0, &WrapI_I, "sceKernelDeleteFpl", 'i', "i" }, @@ -847,7 +847,7 @@ const HLEFunction ThreadManForUser[] = {0X623AE665, &WrapI_IU, "sceKernelTryAllocateFpl", 'i', "ix" }, {0XF6414A71, &WrapI_IU, "sceKernelFreeFpl", 'i', "ix" }, {0XA8AA591F, &WrapI_IU, "sceKernelCancelFpl", 'i', "ix" }, - {0XD8199E4C, &WrapI_IU, "sceKernelReferFplStatus", 'i', "ix" }, + {0XD8199E4C, &WrapI_IU, "sceKernelReferFplStatus", 'i', "ip" }, {0X20FFF560, &WrapU_CU, "sceKernelCreateVTimer", 'x', "sx", HLE_NOT_IN_INTERRUPT }, {0X328F9E52, &WrapU_I, "sceKernelDeleteVTimer", 'x', "i", HLE_NOT_IN_INTERRUPT }, @@ -866,7 +866,7 @@ const HLEFunction ThreadManForUser[] = {0X8DAFF657, &WrapI_CUUUUU, "sceKernelCreateTlspl", 'i', "sxxxxx" }, {0X32BF938E, &WrapI_I, "sceKernelDeleteTlspl", 'i', "i" }, - {0X721067F3, &WrapI_IU, "sceKernelReferTlsplStatus", 'i', "ix" }, + {0X721067F3, &WrapI_IU, "sceKernelReferTlsplStatus", 'i', "xp" }, // Not completely certain about args. {0X4A719FB2, &WrapI_I, "sceKernelFreeTlspl", 'i', "i" }, // Internal. Takes (uid, &addr) as parameters... probably. @@ -931,7 +931,7 @@ const HLEFunction ThreadManForKernel[] = {0xF3986382, &WrapI_IUU, "sceKernelReceiveMbxCB", 'i', "ixx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0x0D81716A, &WrapI_IU, "sceKernelPollMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, {0x87D4DD36, &WrapI_IU, "sceKernelCancelReceiveMbx", 'i', "ix", HLE_KERNEL_SYSCALL }, - {0xA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0xA8E8C846, &WrapI_IU, "sceKernelReferMbxStatus", 'i', "ip", HLE_KERNEL_SYSCALL }, {0x56C039B5, &WrapI_CIUUU, "sceKernelCreateVpl", 'i', "sixxx", HLE_KERNEL_SYSCALL }, {0x89B3D48C, &WrapI_I, "sceKernelDeleteVpl", 'i', "i", HLE_KERNEL_SYSCALL }, {0xBED27435, &WrapI_IUUU, "sceKernelAllocateVpl", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, @@ -939,7 +939,7 @@ const HLEFunction ThreadManForKernel[] = {0xAF36D708, &WrapI_IUU, "sceKernelTryAllocateVpl", 'i', "ixx", HLE_KERNEL_SYSCALL }, {0xB736E9FF, &WrapI_IU, "sceKernelFreeVpl", 'i', "ix", HLE_KERNEL_SYSCALL }, {0x1D371B8A, &WrapI_IU, "sceKernelCancelVpl", 'i', "ix", HLE_KERNEL_SYSCALL }, - {0x39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ix", HLE_KERNEL_SYSCALL }, + {0x39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ip", HLE_KERNEL_SYSCALL }, }; void Register_ThreadManForUser() diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 9ea935f06d..b903f950d3 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -522,14 +522,17 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr) { u32 error; EventFlag *e = kernelObjects.Get(id, error); if (e) { - if (!Memory::IsValidAddress(statusPtr)) + auto status = PSPPointer::Create(statusPtr); + if (!status.IsValid()) return hleLogWarning(SCEKERNEL, -1, "invalid ptr"); HLEKernel::CleanupWaitingThreads(WAITTYPE_EVENTFLAG, id, e->waitingThreads); e->nef.numWaitThreads = (int) e->waitingThreads.size(); - if (Memory::Read_U32(statusPtr) != 0) - Memory::WriteStruct(statusPtr, &e->nef); + if (status->size != 0) { + *status = e->nef; + status.NotifyWrite("EventFlagStatus"); + } return hleLogSuccessI(SCEKERNEL, 0); } else { return hleLogDebug(SCEKERNEL, error, "invalid event flag"); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index a86deee9fe..4c91ffe529 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -656,7 +656,7 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) } if (MemBlockInfoDetailed(size)) { - const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); } @@ -677,7 +677,7 @@ const HLEFunction Kernel_Library[] = {0XBEA46419, &WrapI_UIU, "sceKernelLockLwMutex", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X1FC64E09, &WrapI_UIU, "sceKernelLockLwMutexCB", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X15B6446B, &WrapI_UI, "sceKernelUnlockLwMutex", 'i', "xi" }, - {0XC1734599, &WrapI_UU, "sceKernelReferLwMutexStatus", 'i', "xx" }, + {0XC1734599, &WrapI_UU, "sceKernelReferLwMutexStatus", 'i', "xp" }, {0X293B45B8, &WrapI_V, "sceKernelGetThreadId", 'i', "" }, {0XD13BDE95, &WrapI_V, "sceKernelCheckThreadStack", 'i', "" }, {0X1839852A, &WrapU_UUU, "sceKernelMemcpy", 'x', "xxx" }, @@ -691,7 +691,7 @@ static u32 sysclib_memcpy(u32 dst, u32 src, u32 size) { memcpy(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size); } if (MemBlockInfoDetailed(size)) { - const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); } @@ -794,7 +794,7 @@ static u32 sysclib_memmove(u32 dst, u32 src, u32 size) { memmove(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size); } if (MemBlockInfoDetailed(size)) { - const std::string tag = "KernelMemmove/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("KernelMemmove/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size()); } diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 412c70188d..0a5f132f01 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -552,19 +552,17 @@ int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr) return 0; } -int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) -{ +int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) { u32 error; Mbx *m = kernelObjects.Get(id, error); - if (!m) - { - ERROR_LOG(SCEKERNEL, "sceKernelReferMbxStatus(%i, %08x): invalid mbx id", id, infoAddr); - return error; + if (!m) { + return hleLogError(SCEKERNEL, error, "invalid mbx id"); } // Should we crash the thread somehow? - if (!Memory::IsValidAddress(infoAddr)) - return -1; + auto info = PSPPointer::Create(infoAddr); + if (!info.IsValid()) + return hleLogError(SCEKERNEL, -1, "invalid pointer"); for (int i = 0, n = m->nmb.numMessages; i < n; ++i) m->nmb.packetListHead = Memory::Read_U32(m->nmb.packetListHead); @@ -572,10 +570,10 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) HLEKernel::CleanupWaitingThreads(WAITTYPE_MBX, id, m->waitingThreads); // For whatever reason, it won't write if the size (first member) is 0. - if (Memory::Read_U32(infoAddr) != 0) - { + if (info->size != 0) { m->nmb.numWaitThreads = (int) m->waitingThreads.size(); - Memory::WriteStruct(infoAddr, &m->nmb); + *info = m->nmb; + info.NotifyWrite("MbxStatus"); } return 0; diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 3c5cce3a9f..ac853d5bb3 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -881,30 +881,26 @@ int sceKernelCancelFpl(SceUID uid, u32 numWaitThreadsPtr) } } -int sceKernelReferFplStatus(SceUID uid, u32 statusPtr) -{ +int sceKernelReferFplStatus(SceUID uid, u32 statusPtr) { u32 error; FPL *fpl = kernelObjects.Get(uid, error); - if (fpl) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x)", uid, statusPtr); + if (fpl) { // Refresh waiting threads and free block count. __KernelSortFplThreads(fpl); fpl->nf.numWaitThreads = (int) fpl->waitingThreads.size(); fpl->nf.numFreeBlocks = 0; - for (int i = 0; i < (int)fpl->nf.numBlocks; ++i) - { + for (int i = 0; i < (int)fpl->nf.numBlocks; ++i) { if (!fpl->blocks[i]) ++fpl->nf.numFreeBlocks; } - if (Memory::Read_U32(statusPtr) != 0) - Memory::WriteStruct(statusPtr, &fpl->nf); - return 0; - } - else - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x): invalid fpl", uid, statusPtr); - return error; + auto status = PSPPointer::Create(statusPtr); + if (status.IsValid() && status->size != 0) { + *status = fpl->nf; + status.NotifyWrite("FplStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error, "invalid fpl"); } } @@ -1782,8 +1778,6 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr) { u32 error; VPL *vpl = kernelObjects.Get(uid, error); if (vpl) { - DEBUG_LOG(SCEKERNEL, "sceKernelReferVplStatus(%i, %08x)", uid, infoPtr); - __KernelSortVplThreads(vpl); vpl->nv.numWaitThreads = (int) vpl->waitingThreads.size(); if (vpl->header.IsValid()) { @@ -1791,12 +1785,14 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr) { } else { vpl->nv.freeSize = vpl->alloc.GetTotalFreeBytes(); } - if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) { - Memory::WriteStruct(infoPtr, &vpl->nv); + auto info = PSPPointer::Create(infoPtr); + if (info.IsValid() && info->size != 0) { + *info = vpl->nv; + info.NotifyWrite("VplStatus"); } - return 0; + return hleLogSuccessI(SCEKERNEL, 0); } else { - return error; + return hleLogError(SCEKERNEL, error, "invalid vpl"); } } @@ -2270,23 +2266,23 @@ int sceKernelFreeTlspl(SceUID uid) return error; } -int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr) -{ - DEBUG_LOG(SCEKERNEL, "sceKernelReferTlsplStatus(%08x, %08x)", uid, infoPtr); +int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr) { u32 error; TLSPL *tls = kernelObjects.Get(uid, error); - if (tls) - { + if (tls) { // Update the waiting threads in case of deletions, etc. __KernelSortTlsplThreads(tls); tls->ntls.numWaitThreads = (int) tls->waitingThreads.size(); - if (Memory::Read_U32(infoPtr) != 0) - Memory::WriteStruct(infoPtr, &tls->ntls); - return 0; + auto info = PSPPointer::Create(infoPtr); + if (info.IsValid() && info->size != 0) { + *info = tls->ntls; + info.NotifyWrite("TlsplStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error, "invalid tlspl"); } - else - return error; } const HLEFunction SysMemUserForUser[] = { diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 502c48fc2e..a32604e928 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -247,6 +247,10 @@ enum NativeModuleStatus { class PSPModule : public KernelObject { public: + PSPModule() { + modulePtr.ptr = 0; + } + ~PSPModule() { if (memoryBlockAddr) { // If it's either below user memory, or using a high kernel bit, it's in kernel. @@ -258,9 +262,9 @@ public: g_symbolMap->UnloadModule(memoryBlockAddr, memoryBlockSize); } - if (modulePtr) { + if (modulePtr.ptr) { //Only alloc at kernel memory. - kernelMemory.Free(modulePtr); + kernelMemory.Free(modulePtr.ptr); } } const char *GetName() override { return nm.name; } @@ -320,7 +324,7 @@ public: } if (s >= 5) { - Do(p, modulePtr); + Do(p, modulePtr.ptr); } ModuleWaitingThread mwt = {0}; @@ -454,7 +458,7 @@ public: u32 memoryBlockAddr = 0; u32 memoryBlockSize = 0; - u32 modulePtr = 0; + PSPPointer modulePtr; bool isFake = false; }; @@ -1616,11 +1620,13 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load u32 moduleSize = sizeof(module->nm); char tag[32]; snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid); - module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag); + module->modulePtr.ptr = kernelMemory.Alloc(moduleSize, true, tag); // Fill the struct. - if (Memory::IsValidAddress(module->modulePtr)) - Memory::WriteStruct(module->modulePtr, &module->nm); + if (module->modulePtr.IsValid()) { + *module->modulePtr = module->nm; + module->modulePtr.NotifyWrite("KernelModule"); + } error = 0; return module; @@ -1763,17 +1769,18 @@ void __KernelLoadReset() { } bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string) { - SceKernelLoadExecParam param; + SceKernelLoadExecParam param{}; PSP_SetLoading("Loading exec..."); - if (paramPtr) - Memory::ReadStruct(paramPtr, ¶m); - else - memset(¶m, 0, sizeof(SceKernelLoadExecParam)); + auto paramData = PSPPointer::Create(paramPtr); + if (paramData.IsValid()) { + param = *paramData; + paramData.NotifyRead("KernelLoadExec"); + } - u8 *param_argp = 0; - u8 *param_key = 0; + u8 *param_argp = nullptr; + u8 *param_key = nullptr; if (param.args > 0) { u32 argpAddr = param.argp; param_argp = new u8[param.args]; @@ -1792,10 +1799,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str if (!info.exists) { ERROR_LOG(LOADER, "Failed to load executable %s - file doesn't exist", filename); *error_string = StringFromFormat("Could not find executable %s", filename); - if (paramPtr) { - if (param_argp) delete[] param_argp; - if (param_key) delete[] param_key; - } + delete[] param_argp; + delete[] param_key; __KernelShutdown(); return false; } @@ -1817,10 +1822,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str ERROR_LOG(LOADER, "Failed to load module %s", filename); *error_string = "Failed to load executable: " + *error_string; delete [] temp; - if (paramPtr) { - if (param_argp) delete[] param_argp; - if (param_key) delete[] param_key; - } + delete[] param_argp; + delete[] param_key; return false; } @@ -1857,8 +1860,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str __KernelStartIdleThreads(module->GetUID()); - if (param_argp) delete[] param_argp; - if (param_key) delete[] param_key; + delete[] param_argp; + delete[] param_key; hleSkipDeadbeef(); return true; @@ -1989,11 +1992,13 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) { u32 moduleSize = sizeof(module->nm); char tag[32]; snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid); - module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag); + module->modulePtr.ptr = kernelMemory.Alloc(moduleSize, true, tag); // Fill the struct. - if(Memory::IsValidAddress(module->modulePtr)) - Memory::WriteStruct(module->modulePtr, &module->nm); + if (module->modulePtr.IsValid()) { + *module->modulePtr = module->nm; + module->modulePtr.NotifyWrite("KernelModule"); + } // TODO: It would be more ideal to allocate memory for this module. @@ -2147,10 +2152,7 @@ int KernelStartModule(SceUID moduleId, u32 argsize, u32 argAddr, u32 returnValue static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) { - SceKernelSMOption smoption = {0}; - if (optionAddr) { - Memory::ReadStruct(optionAddr, &smoption); - } + auto smoption = PSPPointer::Create(optionAddr); u32 error; PSPModule *module = kernelObjects.Get(moduleId, error); if (!module) { @@ -2176,7 +2178,7 @@ static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 ret moduleId,argsize,argAddr,returnValueAddr,optionAddr); bool needsWait; - int ret = KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, optionAddr ? &smoption : nullptr, &needsWait); + int ret = KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, smoption.PtrOrNull(), &needsWait); if (needsWait) { __KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "started module"); @@ -2464,8 +2466,8 @@ u32 sceKernelFindModuleByUID(u32 uid) ERROR_LOG(SCEMODULE, "0 = sceKernelFindModuleByUID(%d): Module Not Found or Fake", uid); return 0; } - INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByUID(%d)", module->modulePtr, uid); - return module->modulePtr; + INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByUID(%d)", module->modulePtr.ptr, uid); + return module->modulePtr.ptr; } u32 sceKernelFindModuleByName(const char *name) @@ -2477,8 +2479,8 @@ u32 sceKernelFindModuleByName(const char *name) continue; if (strcmp(name, module->nm.name) == 0) { if (!module->isFake) { - INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr, name); - return module->modulePtr; + INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr.ptr, name); + return module->modulePtr.ptr; } else { WARN_LOG(SCEMODULE, "0 = sceKernelFindModuleByName(%s): Module Fake", name); diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index 253bf9ae65..b16e8060e2 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -1006,33 +1006,27 @@ int sceKernelCancelMsgPipe(SceUID uid, u32 numSendThreadsAddr, u32 numReceiveThr return 0; } -int sceKernelReferMsgPipeStatus(SceUID uid, u32 statusPtr) -{ +int sceKernelReferMsgPipeStatus(SceUID uid, u32 statusPtr) { u32 error; MsgPipe *m = kernelObjects.Get(uid, error); - if (m) - { - if (!Memory::IsValidAddress(statusPtr)) - { - ERROR_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x): invalid address", uid, statusPtr); - return -1; + if (m) { + auto status = PSPPointer::Create(statusPtr); + if (!status.IsValid()) { + return hleLogError(SCEKERNEL, -1, "invalid address"); } - DEBUG_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x)", uid, statusPtr); - // Clean up any that have timed out. m->SortReceiveThreads(); m->SortSendThreads(); m->nmp.numSendWaitThreads = (int) m->sendWaitingThreads.size(); m->nmp.numReceiveWaitThreads = (int) m->receiveWaitingThreads.size(); - if (Memory::Read_U32(statusPtr) != 0) - Memory::WriteStruct(statusPtr, &m->nmp); - return 0; - } - else - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x): bad message pipe", uid, statusPtr); - return error; + if (status->size != 0) { + *status = m->nmp; + status.NotifyWrite("MsgPipeStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error, "bad message pipe"); } } diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 899230142b..2cba7b5ca5 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -664,31 +664,27 @@ int sceKernelUnlockMutex(SceUID id, int count) return 0; } -int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) -{ +int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) { u32 error; PSPMutex *m = kernelObjects.Get(id, error); - if (!m) - { - ERROR_LOG(SCEKERNEL, "sceKernelReferMutexStatus(%i, %08x): invalid mutex id", id, infoAddr); - return error; + if (!m) { + return hleLogError(SCEKERNEL, error, "invalid mutex id"); } - DEBUG_LOG(SCEKERNEL, "sceKernelReferMutexStatus(%08x, %08x)", id, infoAddr); - // Should we crash the thread somehow? - if (!Memory::IsValidAddress(infoAddr)) - return -1; + auto info = PSPPointer::Create(infoAddr); + if (!info.IsValid()) + return hleLogError(SCEKERNEL, -1, "invalid pointer"); // Don't write if the size is 0. Anything else is A-OK, though, apparently. - if (Memory::Read_U32(infoAddr) != 0) - { + if (info->size != 0) { HLEKernel::CleanupWaitingThreads(WAITTYPE_MUTEX, id, m->waitingThreads); m->nm.numWaitThreads = (int) m->waitingThreads.size(); - Memory::WriteStruct(infoAddr, &m->nm); + *info = m->nm; + info.NotifyWrite("MutexStatus"); } - return 0; + return hleLogSuccessI(SCEKERNEL, 0); } int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr) @@ -1075,18 +1071,18 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count) return 0; } -static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) -{ +static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) { u32 error; LwMutex *m = kernelObjects.Get(uid, error); if (!m) - return error; + return hleLogError(SCEKERNEL, error, "invalid id"); // Should we crash the thread somehow? - if (!Memory::IsValidAddress(infoPtr)) - return -1; + auto info = PSPPointer::Create(infoPtr); + if (!info.IsValid()) + return hleLogError(SCEKERNEL, -1, "invalid pointer"); - if (Memory::Read_U32(infoPtr) != 0) + if (info->size != 0) { auto workarea = m->nm.workarea; @@ -1096,44 +1092,21 @@ static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) m->nm.currentCount = workarea->lockLevel; m->nm.lockThread = workarea->lockThread == 0 ? SceUID_le(-1) : workarea->lockThread; m->nm.numWaitThreads = (int) m->waitingThreads.size(); - Memory::WriteStruct(infoPtr, &m->nm); + *info = m->nm; + info.NotifyWrite("LwMutexStatus"); } - return 0; + return hleLogSuccessI(SCEKERNEL, 0); } -int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr) -{ - int error = __KernelReferLwMutexStatus(uid, infoPtr); - if (error >= 0) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferLwMutexStatusByID(%08x, %08x)", uid, infoPtr); - return error; - } - else - { - ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferLwMutexStatusByID(%08x, %08x)", error, uid, infoPtr); - return error; - } +int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr) { + return __KernelReferLwMutexStatus(uid, infoPtr); } -int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr) -{ - if (!Memory::IsValidAddress(workareaPtr)) { - ERROR_LOG(SCEKERNEL, "Bad workarea pointer for LwMutex"); - return SCE_KERNEL_ERROR_ACCESS_ERROR; - } - +int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr) { auto workarea = PSPPointer::Create(workareaPtr); + if (!workarea.IsValid()) { + return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_ACCESS_ERROR, "bad workarea pointer for LwMutex"); + } - int error = __KernelReferLwMutexStatus(workarea->uid, infoPtr); - if (error >= 0) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferLwMutexStatus(%08x, %08x)", workareaPtr, infoPtr); - return error; - } - else - { - ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferLwMutexStatus(%08x, %08x)", error, workareaPtr, infoPtr); - return error; - } + return __KernelReferLwMutexStatus(workarea->uid, infoPtr); } diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index d6ebbaf2c1..fffb16c20f 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -263,28 +263,24 @@ int sceKernelDeleteSema(SceUID id) } } -int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) -{ +int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) { u32 error; PSPSemaphore *s = kernelObjects.Get(id, error); - if (s) - { - DEBUG_LOG(SCEKERNEL, "sceKernelReferSemaStatus(%i, %08x)", id, infoPtr); - - if (!Memory::IsValidAddress(infoPtr)) - return -1; + if (s) { + auto info = PSPPointer::Create(infoPtr); + if (!info.IsValid()) + return hleLogWarning(SCEKERNEL, -1, "invalid pointer"); HLEKernel::CleanupWaitingThreads(WAITTYPE_SEMA, id, s->waitingThreads); s->ns.numWaitThreads = (int) s->waitingThreads.size(); - if (Memory::Read_U32(infoPtr) != 0) - Memory::WriteStruct(infoPtr, &s->ns); - return 0; - } - else - { - ERROR_LOG(SCEKERNEL, "sceKernelReferSemaStatus: error %08x", error); - return error; + if (info->size != 0) { + *info = s->ns; + info.NotifyWrite("SemaStatus"); + } + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogError(SCEKERNEL, error); } } diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 86a234cd3b..ea9caaba1e 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2902,13 +2902,14 @@ int sceKernelGetCallbackCount(SceUID cbId) } } -int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr) -{ +int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr) { u32 error; PSPCallback *c = kernelObjects.Get(cbId, error); if (c) { - if (Memory::IsValidAddress(statusAddr) && Memory::Read_U32(statusAddr) != 0) { - Memory::WriteStruct(statusAddr, &c->nc); + auto status = PSPPointer::Create(statusAddr); + if (status.IsValid() && status->size != 0) { + *status = c->nc; + status.NotifyWrite("CallbackStatus"); return hleLogSuccessI(SCEKERNEL, 0); } else { return hleLogDebug(SCEKERNEL, 0, "struct size was 0"); @@ -3789,8 +3790,10 @@ int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr) { return hleReportError(SCEKERNEL, error, "bad handler id"); } - if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) { - Memory::WriteStruct(infoPtr, &teh->nteh); + auto info = PSPPointer::Create(infoPtr); + if (info.IsValid() && info->size != 0) { + *info = teh->nteh; + info.NotifyWrite("ThreadEventHandlerStatus"); return hleLogSuccessI(SCEKERNEL, 0); } else { return hleLogDebug(SCEKERNEL, 0, "struct size was 0"); diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 5a3cd77d35..8aa429ba1d 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -125,7 +125,7 @@ struct SceMpegLLI }; void SceMpegAu::read(u32 addr) { - Memory::ReadStruct(addr, this); + Memory::Memcpy(this, addr, sizeof(this), "SceMpegAu"); pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32); dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32); } @@ -133,7 +133,7 @@ void SceMpegAu::read(u32 addr) { void SceMpegAu::write(u32 addr) { pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32); dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32); - Memory::WriteStruct(addr, this); + Memory::Memcpy(addr, this, sizeof(this), "SceMpegAu"); } /* @@ -2410,15 +2410,15 @@ static u32 sceMpegBasePESpacketCopy(u32 p) { pmp_videoSource = p; pmp_nBlocks = 0; - SceMpegLLI lli; - while (1){ - Memory::ReadStruct(p, &lli); + + auto lli = PSPPointer::Create(p); + while (lli.IsValid()) { pmp_nBlocks++; // lli.Next ==0 for last block - if (lli.Next == 0){ + if (lli->Next == 0){ break; } - p = p + sizeof(SceMpegLLI); + ++lli; } DEBUG_LOG(ME, "sceMpegBasePESpacketCopy(%08x), received %d block(s)", pmp_videoSource, pmp_nBlocks); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index ff2cb8ed7e..c2a8987408 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -785,11 +785,12 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) { // Write static data since we don't actually manage any memory for sceNet* yet. static int sceNetGetMallocStat(u32 statPtr) { VERBOSE_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x) at %08x", statPtr, currentMIPS->pc); - if(Memory::IsValidAddress(statPtr)) - Memory::WriteStruct(statPtr, &netMallocStat); - else - ERROR_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x): tried to request invalid address!", statPtr); + auto stat = PSPPointer::Create(statPtr); + if (!stat.IsValid()) + return hleLogError(SCENET, 0, "invalid address"); + *stat = netMallocStat; + stat.NotifyWrite("sceNetGetMallocStat"); return 0; } @@ -893,77 +894,128 @@ static int sceNetApctlGetInfo(int code, u32 pInfoAddr) { if (!netApctlInited) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_IN_BSS, "apctl not in bss"); // Only have valid info after joining an AP and got an IP, right? - if (!Memory::IsValidAddress(pInfoAddr)) - return hleLogError(SCENET, -1, "apctl invalid arg"); - - u8* info = Memory::GetPointerWrite(pInfoAddr); // FIXME: Points to a union instead of a struct thus each field have the same address - switch (code) { case PSP_NET_APCTL_INFO_PROFILE_NAME: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.name); + if (!Memory::IsValidRange(pInfoAddr, APCTL_PROFILENAME_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.name, APCTL_PROFILENAME_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_PROFILENAME_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - ProfileName: %s", netApctlInfo.name); break; case PSP_NET_APCTL_INFO_BSSID: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.bssid); + if (!Memory::IsValidRange(pInfoAddr, ETHER_ADDR_LEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.bssid, ETHER_ADDR_LEN); DEBUG_LOG(SCENET, "ApctlInfo - BSSID: %s", mac2str((SceNetEtherAddr*)&netApctlInfo.bssid).c_str()); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, ETHER_ADDR_LEN, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_SSID: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssid); + if (!Memory::IsValidRange(pInfoAddr, APCTL_SSID_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.ssid, APCTL_SSID_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_SSID_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - SSID: %s", netApctlInfo.ssid); break; case PSP_NET_APCTL_INFO_SSID_LENGTH: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssidLength); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.ssidLength, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_SECURITY_TYPE: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.securityType); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.securityType, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_STRENGTH: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.strength); + if (!Memory::IsValidRange(pInfoAddr, 1)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U8(netApctlInfo.strength, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_CHANNEL: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.channel); + if (!Memory::IsValidRange(pInfoAddr, 1)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U8(netApctlInfo.channel, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_POWER_SAVE: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.powerSave); + if (!Memory::IsValidRange(pInfoAddr, 1)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U8(netApctlInfo.powerSave, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_IP: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.ip); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.ip, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - IP: %s", netApctlInfo.ip); break; case PSP_NET_APCTL_INFO_SUBNETMASK: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.subNetMask); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.subNetMask, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - SubNet Mask: %s", netApctlInfo.subNetMask); break; case PSP_NET_APCTL_INFO_GATEWAY: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.gateway); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.gateway, APCTL_IPADDR_MAXLEN); DEBUG_LOG(SCENET, "ApctlInfo - Gateway IP: %s", netApctlInfo.gateway); break; case PSP_NET_APCTL_INFO_PRIMDNS: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.primaryDns); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.primaryDns, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - Primary DNS: %s", netApctlInfo.primaryDns); break; case PSP_NET_APCTL_INFO_SECDNS: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.secondaryDns); + if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.secondaryDns, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - Secondary DNS: %s", netApctlInfo.secondaryDns); break; case PSP_NET_APCTL_INFO_USE_PROXY: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.useProxy); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.useProxy, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_PROXY_URL: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyUrl); + if (!Memory::IsValidRange(pInfoAddr, APCTL_URL_MAXLEN)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.proxyUrl, APCTL_URL_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_URL_MAXLEN, "NetApctlGetInfo"); DEBUG_LOG(SCENET, "ApctlInfo - Proxy URL: %s", netApctlInfo.proxyUrl); break; case PSP_NET_APCTL_INFO_PROXY_PORT: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyPort); + if (!Memory::IsValidRange(pInfoAddr, 2)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U16(netApctlInfo.proxyPort, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 2, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_8021_EAP_TYPE: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.eapType); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.eapType, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_START_BROWSER: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.startBrowser); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.startBrowser, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; case PSP_NET_APCTL_INFO_WIFISP: - Memory::WriteStruct(pInfoAddr, &netApctlInfo.wifisp); + if (!Memory::IsValidRange(pInfoAddr, 4)) + return hleLogError(SCENET, -1, "apctl invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.wifisp, pInfoAddr); + NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo"); break; default: return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "apctl invalid code"); @@ -1234,28 +1286,28 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { switch (infoId) { case PSP_NET_APCTL_DESC_IBSS: // IBSS, 6 bytes if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.bssid); + Memory::Memcpy(resultAddr, netApctlInfo.bssid, sizeof(netApctlInfo.bssid), "GetBSSDescEntryUser"); else { // Generate a BSSID/MAC address char dummyMAC[ETHER_ADDR_LEN]; memset(dummyMAC, entryId, sizeof(dummyMAC)); // Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo) dummyMAC[0] &= 0xfc; - Memory::WriteStruct(resultAddr, &dummyMAC); + Memory::Memcpy(resultAddr, dummyMAC, sizeof(dummyMAC), "GetBSSDescEntryUser"); } break; case PSP_NET_APCTL_DESC_SSID_NAME: // Return 32 bytes if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.ssid); + Memory::Memcpy(resultAddr, netApctlInfo.ssid, sizeof(netApctlInfo.ssid), "GetBSSDescEntryUser"); else { - Memory::WriteStruct(resultAddr, &dummySSID); + Memory::Memcpy(resultAddr, dummySSID, sizeof(dummySSID), "GetBSSDescEntryUser"); } break; case PSP_NET_APCTL_DESC_SSID_NAME_LENGTH: // Return one 32-bit value if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.ssidLength); + Memory::Write_U32(netApctlInfo.ssidLength, resultAddr); else { // Calculate the SSID length Memory::Write_U32((u32)strlen(dummySSID), resultAddr); @@ -1264,7 +1316,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { case PSP_NET_APCTL_DESC_CHANNEL: // FIXME: Return one 1 byte value or may be 32-bit if this is not a channel? if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.channel); + Memory::Write_U8(netApctlInfo.channel, resultAddr); else { // Generate channel for testing purposes, not even sure whether this is channel or not, MGS:PW seems to treat the data as u8 Memory::Write_U8(entryId, resultAddr); @@ -1273,7 +1325,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { case PSP_NET_APCTL_DESC_SIGNAL_STRENGTH: // Return 1 byte if (entryId == 0) - Memory::WriteStruct(resultAddr, &netApctlInfo.strength); + Memory::Write_U8(netApctlInfo.strength, resultAddr); else { // Randomize signal strength between 1%~99% since games like MGS:PW are using signal strength to determine the strength of the recruit Memory::Write_U8((int)(((float)rand() / (float)RAND_MAX) * 99.0 + 1.0), resultAddr); @@ -1281,7 +1333,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { break; case PSP_NET_APCTL_DESC_SECURITY: // Return one 32-bit value - Memory::WriteStruct(resultAddr, &netApctlInfo.securityType); + Memory::Write_U32(netApctlInfo.securityType, resultAddr); break; default: return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "unknown info id"); @@ -1432,7 +1484,7 @@ const HLEFunction sceNet[] = { {0XD27961C9, &WrapV_UU, "sceNetEtherStrton", 'v', "xx" }, {0X0BF0A3AE, &WrapU_U, "sceNetGetLocalEtherAddr", 'x', "x" }, {0X50647530, &WrapI_I, "sceNetFreeThreadinfo", 'i', "i" }, - {0XCC393E48, &WrapI_U, "sceNetGetMallocStat", 'i', "x" }, + {0XCC393E48, &WrapI_U, "sceNetGetMallocStat", 'i', "p" }, {0XAD6844C6, &WrapI_I, "sceNetThreadAbort", 'i', "i" }, }; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index ec3cced95e..5e5da07fd1 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1303,8 +1303,10 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { if (netAdhocctlInited) return ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED; - if (Memory::IsValidAddress(productAddr)) { - Memory::ReadStruct(productAddr, &product_code); + auto product = PSPPointer::Create(productAddr); + if (product.IsValid()) { + product_code = *product; + product.NotifyRead("NetAdhocctlInit"); } adhocctlEvents.clear(); @@ -1539,21 +1541,17 @@ static int sceNetAdhocctlGetParameter(u32 paramAddr) { } // Library initialized - if (netAdhocctlInited) { - // Valid Arguments - if (Memory::IsValidAddress(paramAddr)) { - // Copy Parameter - Memory::WriteStruct(paramAddr,¶meter); - // Return Success - return 0; - } - - // Invalid Arguments - return ERROR_NET_ADHOCCTL_INVALID_ARG; + if (!netAdhocctlInited) { + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED); } - // Library uninitialized - return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; + auto ptr = PSPPointer::Create(paramAddr); + if (!ptr.IsValid()) + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG); + + *ptr = parameter; + ptr.NotifyWrite("NetAdhocctlGetParameter"); + return 0; } /** @@ -2258,25 +2256,17 @@ static int sceNetAdhocPdpDelete(int id, int unknown) { static int sceNetAdhocctlGetAdhocId(u32 productStructAddr) { INFO_LOG(SCENET, "sceNetAdhocctlGetAdhocId(%08x) at %08x", productStructAddr, currentMIPS->pc); - // Library initialized - if (netAdhocctlInited) - { - // Valid Arguments - if (Memory::IsValidAddress(productStructAddr)) - { - // Copy Product ID - Memory::WriteStruct(productStructAddr, &product_code); + if (!netAdhocctlInited) + return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); - // Return Success - return hleLogDebug(SCENET, 0, "type = %d, code = %s", product_code.type, product_code.data); - } - - // Invalid Arguments + auto productStruct = PSPPointer::Create(productStructAddr); + if (!productStruct.IsValid()) return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); - } - // Library uninitialized - return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); + *productStruct = product_code; + productStruct.NotifyWrite("NetAdhocctlGetAdhocId"); + + return hleLogDebug(SCENET, 0, "type = %d, code = %s", product_code.type, product_code.data); } // FIXME: Scan probably not a blocking function since there is ADHOCCTL_STATE_SCANNING state that can be polled by the game, right? But apparently it need to be delayed for Naruto Shippuden Ultimate Ninja Heroes 3 diff --git a/Core/HLE/sceNp.cpp b/Core/HLE/sceNp.cpp index 50bb3f317b..c5ca4be4c6 100644 --- a/Core/HLE/sceNp.cpp +++ b/Core/HLE/sceNp.cpp @@ -154,15 +154,15 @@ static int sceNpGetOnlineId(u32 idPtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr); - if (!Memory::IsValidAddress(idPtr)) + auto id = PSPPointer::Create(idPtr); + if (!id.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - SceNpOnlineId dummyOnlineId{}; - truncate_cpy(dummyOnlineId.data, sizeof(dummyOnlineId.data), npOnlineId.c_str()); + memset((SceNpOnlineId *)id, 0, sizeof(SceNpOnlineId)); + truncate_cpy(id->data, sizeof(id->data), npOnlineId.c_str()); + id.NotifyWrite("NpGetOnlineId"); - INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyOnlineId.data); - - Memory::WriteStruct(idPtr, &dummyOnlineId); + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, id->data); return 0; } @@ -177,20 +177,21 @@ static int sceNpGetNpId(u32 idPtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr); - if (!Memory::IsValidAddress(idPtr)) + auto id = PSPPointer::Create(idPtr); + if (!id.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); SceNpId dummyNpId{}; - int retval = NpGetNpId(&dummyNpId); + memset((SceNpId *)id, 0, sizeof(SceNpId)); + int retval = NpGetNpId(id); if (retval < 0) return hleLogError(SCENET, retval); - INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyNpId.handle.data); + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, id->handle.data); std::string datahex; - DataToHexString(dummyNpId.opt, sizeof(dummyNpId.opt), &datahex); + DataToHexString(id->opt, sizeof(id->opt), &datahex); INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str()); - - Memory::WriteStruct(idPtr, &dummyNpId); + id.NotifyWrite("NpGetNpId"); return 0; } @@ -199,19 +200,21 @@ static int sceNpGetAccountRegion(u32 countryCodePtr, u32 regionCodePtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, countryCodePtr, regionCodePtr); - if (!Memory::IsValidAddress(countryCodePtr) || !Memory::IsValidAddress(regionCodePtr)) + auto countryCode = PSPPointer::Create(countryCodePtr); + auto regionCode = PSPPointer::Create(regionCodePtr); + if (!countryCode.IsValid() || !regionCode.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - SceNpCountryCode dummyCountryCode{}; - memcpy(dummyCountryCode.data, npCountryCode, sizeof(dummyCountryCode.data)); - SceNpCountryCode dummyRegionCode{}; - memcpy(dummyRegionCode.data, npRegionCode, sizeof(dummyRegionCode.data)); + memset((SceNpCountryCode *)countryCode, 0, sizeof(SceNpCountryCode)); + memcpy(countryCode->data, npCountryCode, sizeof(countryCode->data)); + memset((SceNpCountryCode *)regionCode, 0, sizeof(SceNpCountryCode)); + memcpy(regionCode->data, npRegionCode, sizeof(regionCode->data)); - INFO_LOG(SCENET, "%s - Country Code: %s", __FUNCTION__, dummyCountryCode.data); - INFO_LOG(SCENET, "%s - Region? Code: %s", __FUNCTION__, dummyRegionCode.data); + INFO_LOG(SCENET, "%s - Country Code: %s", __FUNCTION__, countryCode->data); + INFO_LOG(SCENET, "%s - Region? Code: %s", __FUNCTION__, regionCode->data); - Memory::WriteStruct(countryCodePtr, &dummyCountryCode); - Memory::WriteStruct(regionCodePtr, &dummyRegionCode); + countryCode.NotifyWrite("NpGetAccountRegion"); + regionCode.NotifyWrite("NpGetAccountRegion"); return 0; } @@ -220,14 +223,16 @@ static int sceNpGetMyLanguages(u32 langListPtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, langListPtr); - if (!Memory::IsValidAddress(langListPtr)) + auto langList = PSPPointer::Create(langListPtr); + if (!langList.IsValid()) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); INFO_LOG(SCENET, "%s - Language1 Code: %d", __FUNCTION__, npMyLangList.language1); INFO_LOG(SCENET, "%s - Language2 Code: %d", __FUNCTION__, npMyLangList.language2); INFO_LOG(SCENET, "%s - Language3 Code: %d", __FUNCTION__, npMyLangList.language3); - Memory::WriteStruct(langListPtr, &npMyLangList); + *langList = npMyLangList; + langList.NotifyWrite("NpGetMyLanguages"); return 0; } @@ -236,20 +241,21 @@ static int sceNpGetUserProfile(u32 profilePtr) { WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, profilePtr); + auto profile = PSPPointer::Create(profilePtr); if (!Memory::IsValidAddress(profilePtr)) return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - SceNpUserInformation dummyProfile{}; - truncate_cpy(dummyProfile.userId.handle.data, sizeof(dummyProfile.userId.handle.data), npOnlineId.c_str()); - truncate_cpy(dummyProfile.icon.data, sizeof(dummyProfile.icon.data), npAvatarUrl.c_str()); + memset((SceNpUserInformation *)profile, 0, sizeof(SceNpUserInformation)); + truncate_cpy(profile->userId.handle.data, sizeof(profile->userId.handle.data), npOnlineId.c_str()); + truncate_cpy(profile->icon.data, sizeof(profile->icon.data), npAvatarUrl.c_str()); - INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyProfile.userId.handle.data); + INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, profile->userId.handle.data); std::string datahex; - DataToHexString(dummyProfile.userId.opt, sizeof(dummyProfile.userId.opt), &datahex); + DataToHexString(profile->userId.opt, sizeof(profile->userId.opt), &datahex); INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str()); - INFO_LOG(SCENET, "%s - Avatar URL: %s", __FUNCTION__, dummyProfile.icon.data); + INFO_LOG(SCENET, "%s - Avatar URL: %s", __FUNCTION__, profile->icon.data); - Memory::WriteStruct(profilePtr, &dummyProfile); + profile.NotifyWrite("NpGetUserProfile"); return 0; } @@ -295,10 +301,12 @@ int sceNpAuthGetMemoryStat(u32 memStatAddr) { ERROR_LOG(SCENET, "UNIMPL %s(%08x)", __FUNCTION__, memStatAddr); - if (!Memory::IsValidAddress(memStatAddr)) + auto memStat = PSPPointer::Create(memStatAddr); + if (!memStat.IsValid()) return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); - Memory::WriteStruct(memStatAddr, &npAuthMemStat); + *memStat = npAuthMemStat; + memStat.NotifyWrite("NpAuthGetMemoryStat"); return 0; } @@ -386,7 +394,7 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); int result = length; - Memory::Memset(bufferAddr, 0, length); + Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket"); SceNpTicket ticket = {}; // Dummy Login ticket returned as Login response. Dummy ticket contents were taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket ticket.header.version = TICKET_VER_2_1; @@ -415,14 +423,14 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL); ticket.section.type = SECTION_TYPE_BODY; ticket.section.size = ofs; - Memory::WriteStruct(bufferAddr, &ticket); + Memory::Memcpy(bufferAddr, &ticket, sizeof(SceNpTicket), "NpAuthGetTicket"); SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on version 2.1 containing 4-chars ASCII + 20-chars ASCII - Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, &footer); + Memory::Memcpy(bufferAddr + sizeof(ticket) + ofs, &footer, sizeof(SceNpTicketSection), "NpAuthGetTicket"); ofs += sizeof(footer); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x34\xcd\x3c\xa9", 4); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x3a\x4b\x42\x66\x92\xda\x6b\x7c\xb7\x4c\xe8\xd9\x4f\x2b\x77\x15\x91\xb8\xa4\xa9", 20); // 20 random letters, token key or SceNpSignature? - u8 unknownBytes[36] = {}; // includes Language list? - Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, unknownBytes); + // includes Language list? + Memory::Memset(bufferAddr + sizeof(ticket) + ofs, 0, 36); result = ticket.header.size + sizeof(ticket.header); // dummy ticket is 248 bytes diff --git a/Core/HLE/sceNp2.cpp b/Core/HLE/sceNp2.cpp index 51a1ada775..59f7b0cfdf 100644 --- a/Core/HLE/sceNp2.cpp +++ b/Core/HLE/sceNp2.cpp @@ -280,10 +280,12 @@ static int sceNpMatching2GetMemoryStat(u32 memStatPtr) if (!npMatching2Inited) return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); - if (!Memory::IsValidAddress(memStatPtr)) + auto memStat = PSPPointer::Create(memStatPtr); + if (!memStat.IsValid()) return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT); - Memory::WriteStruct(memStatPtr, &npMatching2MemStat); + *memStat = npMatching2MemStat; + memStat.NotifyWrite("NpMatching2GetMemoryStat"); return 0; } diff --git a/Core/HLE/sceOpenPSID.cpp b/Core/HLE/sceOpenPSID.cpp index 43665f530f..9d47b48374 100644 --- a/Core/HLE/sceOpenPSID.cpp +++ b/Core/HLE/sceOpenPSID.cpp @@ -38,9 +38,10 @@ static int sceOpenPSIDGetOpenPSID(u32 OpenPSIDPtr) { WARN_LOG(HLE, "UNTESTED %s(%08x)", __FUNCTION__, OpenPSIDPtr); - if (Memory::IsValidAddress(OpenPSIDPtr)) - { - Memory::WriteStruct(OpenPSIDPtr, &dummyOpenPSID); + auto ptr = PSPPointer::Create(OpenPSIDPtr); + if (ptr.IsValid()) { + *ptr = dummyOpenPSID; + ptr.NotifyWrite("OpenPSIDGetOpenPSID"); } return 0; } @@ -49,9 +50,10 @@ static int sceOpenPSIDGetPSID(u32 OpenPSIDPtr,u32 unknown) { WARN_LOG(HLE, "UNTESTED %s(%08x, %08x)", __FUNCTION__, OpenPSIDPtr, unknown); - if (Memory::IsValidAddress(OpenPSIDPtr)) - { - Memory::WriteStruct(OpenPSIDPtr, &dummyOpenPSID); + auto ptr = PSPPointer::Create(OpenPSIDPtr); + if (ptr.IsValid()) { + *ptr = dummyOpenPSID; + ptr.NotifyWrite("OpenPSIDGetPSID"); } return 0; } diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index 020ca8a808..b2aefa9a1a 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -766,20 +766,21 @@ static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { // Note: this structure changes between versions. // TODO: These values are not right, but games probably don't read them. - PsmfData data = {0}; - data.version = psmf->version; - data.headerSize = 0x800; - data.streamSize = psmf->streamSize; + auto data = PSPPointer::Create(psmfStruct); + memset((PsmfData *)data, 0, sizeof(PsmfData)); + data->version = psmf->version; + data->headerSize = 0x800; + data->streamSize = psmf->streamSize; // This should be and needs to be the current stream. - data.streamNum = psmf->currentStreamNum; - data.headerOffset = psmf->headerOffset; - Memory::WriteStruct(psmfStruct, &data); + data->streamNum = psmf->currentStreamNum; + data->headerOffset = psmf->headerOffset; + data.NotifyWrite("PsmfSetPsmf"); // Because the Psmf struct is sometimes copied, we use a value inside as an id. - auto iter = psmfMap.find(data.headerOffset); + auto iter = psmfMap.find(data->headerOffset); if (iter != psmfMap.end()) delete iter->second; - psmfMap[data.headerOffset] = psmf; + psmfMap[data->headerOffset] = psmf; return hleLogSuccessI(ME, 0); } @@ -1046,45 +1047,43 @@ static u32 scePsmfGetEPWithId(u32 psmfStruct, int epid, u32 entryAddr) { Psmf *psmf = getPsmf(psmfStruct); if (!psmf) { - ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x): invalid psmf", psmfStruct, epid, entryAddr); - return ERROR_PSMF_NOT_INITIALIZED; + return hleLogError(ME, ERROR_PSMF_NOT_INITIALIZED, "invalid psmf"); } - DEBUG_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x)", psmfStruct, epid, entryAddr); if (epid < 0 || epid >= (int)psmf->EPMap.size()) { - ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i): invalid id", psmfStruct, epid); - return ERROR_PSMF_NOT_FOUND; + return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid id"); } - if (Memory::IsValidAddress(entryAddr)) { - Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]); + + auto entry = PSPPointer::Create(entryAddr); + if (entry.IsValid()) { + *entry = psmf->EPMap[epid]; + entry.NotifyWrite("PsmfGetEPWithId"); } - return 0; + return hleLogSuccessI(ME, 0); } static u32 scePsmfGetEPWithTimestamp(u32 psmfStruct, u32 ts, u32 entryAddr) { Psmf *psmf = getPsmf(psmfStruct); if (!psmf) { - ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x): invalid psmf", psmfStruct, ts, entryAddr); - return ERROR_PSMF_NOT_INITIALIZED; + return hleLogError(ME, ERROR_PSMF_NOT_INITIALIZED, "invalid psmf"); } - DEBUG_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x)", psmfStruct, ts, entryAddr); if (ts < psmf->presentationStartTime) { - ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid timestamp", psmfStruct, ts); - return ERROR_PSMF_NOT_FOUND; + return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid timestamp"); } int epid = psmf->FindEPWithTimestamp(ts); if (epid < 0 || epid >= (int)psmf->EPMap.size()) { - ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid id", psmfStruct, epid); - return ERROR_PSMF_NOT_FOUND; + return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid id"); } - if (Memory::IsValidAddress(entryAddr)) { - Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]); + auto entry = PSPPointer::Create(entryAddr); + if (entry.IsValid()) { + *entry = psmf->EPMap[epid]; + entry.NotifyWrite("PsmfGetEPWithTimestamp"); } - return 0; + return hleLogSuccessI(ME, 0); } static u32 scePsmfGetEPidWithTimestamp(u32 psmfStruct, u32 ts) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 18417bb9c2..c8d43a5027 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -113,11 +113,12 @@ static int getCameraResolution(Camera::ConfigType type, int *width, int *height) static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) { - INFO_LOG(HLE, "sceUsbCamSetupMic"); - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) { - Memory::ReadStruct(paramAddr, &config->micParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->micParam = *param; + param.NotifyRead("UsbCamSetupMic"); } - return 0; + return hleLogSuccessInfoI(SCEMISC, 0); } static int sceUsbCamStartMic() { @@ -155,16 +156,20 @@ static int sceUsbCamGetMicDataLength() { } static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoParam))) { - Memory::ReadStruct(paramAddr, &config->videoParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->videoParam = *param; + param.NotifyRead("UsbCamSetupVideo"); } config->type = Camera::ConfigType::CfVideo; return 0; } static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) { - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoExParam))) { - Memory::ReadStruct(paramAddr, &config->videoExParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->videoExParam = *param; + param.NotifyRead("UsbCamSetupVideoEx"); } config->type = Camera::ConfigType::CfVideoEx; return 0; @@ -222,8 +227,10 @@ static int sceUsbCamPollReadVideoFrameEnd() { static int sceUsbCamSetupStill(u32 paramAddr) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStill"); - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillParam))) { - Memory::ReadStruct(paramAddr, &config->stillParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->stillParam = *param; + param.NotifyRead("UsbCamSetupStill"); } config->type = Camera::ConfigType::CfStill; return 0; @@ -231,8 +238,10 @@ static int sceUsbCamSetupStill(u32 paramAddr) { static int sceUsbCamSetupStillEx(u32 paramAddr) { INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStillEx"); - if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillExParam))) { - Memory::ReadStruct(paramAddr, &config->stillExParam); + auto param = PSPPointer::Create(paramAddr); + if (param.IsValid()) { + config->stillExParam = *param; + param.NotifyRead("UsbCamSetupStillEx"); } config->type = Camera::ConfigType::CfStillEx; return 0; diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index 2c9f320044..020339fe10 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -59,8 +59,8 @@ static int sceUsbGpsGetInitDataLocation(u32 addr) { } static int sceUsbGpsGetState(u32 stateAddr) { - if (Memory::IsValidAddress(stateAddr)) { - Memory::Write_U32(gpsStatus, stateAddr); + if (Memory::IsValidRange(stateAddr, 4)) { + Memory::WriteUnchecked_U32(gpsStatus, stateAddr); } return 0; } @@ -81,11 +81,15 @@ static int sceUsbGpsClose() { } static int sceUsbGpsGetData(u32 gpsDataAddr, u32 satDataAddr) { - if (Memory::IsValidRange(gpsDataAddr, sizeof(GpsData))) { - Memory::WriteStruct(gpsDataAddr, GPS::getGpsData()); + auto gpsData = PSPPointer::Create(gpsDataAddr); + if (gpsData.IsValid()) { + *gpsData = *GPS::getGpsData(); + gpsData.NotifyWrite("UsbGpsGetData"); } - if (Memory::IsValidRange(satDataAddr, sizeof(SatData))) { - Memory::WriteStruct(satDataAddr, GPS::getSatData()); + auto satData = PSPPointer::Create(satDataAddr); + if (satData.IsValid()) { + *satData = *GPS::getSatData(); + gpsData.NotifyWrite("UsbGpsGetData"); } return 0; } diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 46585b69a9..c9811f5c96 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -65,10 +65,15 @@ static inline void DelayBranchTo(u32 where) mipsr4k.inDelaySlot = true; } -static inline void SkipLikely() -{ - PC += 8; - --mipsr4k.downcount; +static inline void SkipLikely() { + MIPSInfo delaySlot = MIPSGetInfo(Memory::Read_Instruction(PC + 4, true)); + // Don't actually skip if it is a jump (seen in Brooktown High.) + if (delaySlot & IS_JUMP) { + PC += 4; + } else { + PC += 8; + --mipsr4k.downcount; + } } int MIPS_SingleStep() @@ -246,17 +251,21 @@ namespace MIPSInt void Int_JumpType(MIPSOpcode op) { if (mipsr4k.inDelaySlot) - _dbg_assert_msg_(false,"Jump in delay slot :("); + ERROR_LOG(CPU, "Jump in delay slot :("); u32 off = ((op & 0x03FFFFFF) << 2); u32 addr = (currentMIPS->pc & 0xF0000000) | off; switch (op>>26) { - case 2: DelayBranchTo(addr); break; //j + case 2: //j + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); + break; case 3: //jal - R(31) = PC + 8; - DelayBranchTo(addr); + R(MIPS_REG_RA) = PC + 8; + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); break; default: _dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted"); @@ -268,11 +277,8 @@ namespace MIPSInt { if (mipsr4k.inDelaySlot) { - // There's one of these in Star Soldier at 0881808c, which seems benign - it should probably be ignored. - if (op == 0x03e00008) - return; + // There's one of these in Star Soldier at 0881808c, which seems benign. ERROR_LOG(CPU, "Jump in delay slot :("); - _dbg_assert_msg_(false,"Jump in delay slot :("); } int rs = _RS; @@ -281,12 +287,15 @@ namespace MIPSInt switch (op & 0x3f) { case 8: //jr - DelayBranchTo(addr); + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); break; case 9: //jalr if (rd != 0) R(rd) = PC + 8; - DelayBranchTo(addr); + // Update rd, but otherwise do not take the branch if we're branching. + if (!mipsr4k.inDelaySlot) + DelayBranchTo(addr); break; } } diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 1a0fd654f6..52ef9ff5f9 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -490,3 +490,12 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength, const char } } // namespace + +void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char * tag, size_t tagLen) { + if (MemBlockInfoDetailed(bytes)) { + if (rw & 1) + NotifyMemInfo(MemBlockFlags::WRITE, ptr, bytes, tag, tagLen); + if (rw & 2) + NotifyMemInfo(MemBlockFlags::READ, ptr, bytes, tag, tagLen); + } +} diff --git a/Core/MemMap.h b/Core/MemMap.h index 97b7fd76e7..b123af4f3a 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -328,6 +328,9 @@ inline bool IsValidRange(const u32 address, const u32 size) { } // namespace Memory +// Avoiding a global include for NotifyMemInfo. +void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char *tag, size_t tagLen); + template struct PSPPointer { @@ -438,9 +441,35 @@ struct PSPPointer #endif } - bool IsValid() const + bool IsValid() const { + return Memory::IsValidRange(ptr, (u32)sizeof(T)); + } + + T *PtrOrNull() { + if (IsValid()) + return (T *)*this; + return nullptr; + } + + const T *PtrOrNull() const { + if (IsValid()) + return (const T *)*this; + return nullptr; + } + + template + void NotifyWrite(const char(&tag)[tagLen]) const { + PSPPointerNotifyRW(1, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1); + } + + template + void NotifyRead(const char(&tag)[tagLen]) const { + PSPPointerNotifyRW(2, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1); + } + + size_t ElementSize() const { - return Memory::IsValidAddress(ptr); + return sizeof(T); } static PSPPointer Create(u32 ptr) { diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index 848bfa73b6..fdee215d03 100644 --- a/Core/MemMapHelpers.h +++ b/Core/MemMapHelpers.h @@ -20,6 +20,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/StringUtils.h" #include "Core/Debugger/MemBlockInfo.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" @@ -70,9 +71,9 @@ inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len, if (MemBlockInfoDetailed(len)) { char tagData[128]; if (!tag) { - const std::string srcTag = GetMemWriteTagAt(from_address, len); + const std::string srcTag = GetMemWriteTagAt("Memcpy/", from_address, len); tag = tagData; - tagLen = snprintf(tagData, sizeof(tagData), "Memcpy/%s", srcTag.c_str()); + tagLen = truncate_cpy(tagData, srcTag.c_str()); } NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag, tagLen); NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag, tagLen); @@ -108,31 +109,4 @@ inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len) void Memset(const u32 _Address, const u8 _Data, const u32 _iLength, const char *tag = "Memset"); -template -void ReadStruct(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - Memcpy(ptr, address, sz); -} - -template -void ReadStructUnchecked(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - MemcpyUnchecked(ptr, address, sz); -} - -template -void WriteStruct(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - Memcpy(address, ptr, sz); -} - -template -void WriteStructUnchecked(u32 address, T *ptr) -{ - const u32 sz = (u32)sizeof(*ptr); - MemcpyUnchecked(address, ptr, sz); -} } diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index c0b965e9b1..fb6b436661 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -573,7 +573,7 @@ public: if (png.warning_or_error >= 2) { ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors."); } else if (success) { - NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfoHash, w, h); + NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str()); } } }; @@ -970,6 +970,7 @@ void ReplacedTexture::PrepareData(int level) { void ReplacedTexture::PurgeIfOlder(double t) { if (lastUsed_ < t && (!threadWaitable_ || threadWaitable_->WaitFor(0.0))) { levelData_.clear(); + initDone_ = false; } } diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 425552a453..13fd62a22f 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -178,27 +178,28 @@ static void BeginVertexData() { static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color = 0xFFFFFFFF) { if (g_RemasterMode) { - PPGeRemasterVertex vtx; - vtx.x = x; vtx.y = y; vtx.z = 0; - vtx.u = u * tw; vtx.v = v * th; - vtx.color = color; - Memory::WriteStruct(dataWritePtr, &vtx); - dataWritePtr += sizeof(vtx); + auto vtx = PSPPointer::Create(dataWritePtr); + vtx->x = x; vtx->y = y; vtx->z = 0; + vtx->u = u * tw; vtx->v = v * th; + vtx->color = color; + dataWritePtr += (u32)vtx.ElementSize(); } else { - PPGeVertex vtx; - vtx.x = x; vtx.y = y; vtx.z = 0; - vtx.u = u * tw; vtx.v = v * th; - vtx.color = color; - Memory::WriteStruct(dataWritePtr, &vtx); - dataWritePtr += sizeof(vtx); + auto vtx = PSPPointer::Create(dataWritePtr); + vtx->x = x; vtx->y = y; vtx->z = 0; + vtx->u = u * tw; vtx->v = v * th; + vtx->color = color; + dataWritePtr += (u32)vtx.ElementSize(); } _dbg_assert_(dataWritePtr <= dataPtr + dataSize); vertexCount++; } static void EndVertexDataAndDraw(int prim) { + _assert_msg_(vertexStart != 0, "Missing matching call to BeginVertexData()"); + NotifyMemInfo(MemBlockFlags::WRITE, vertexStart, dataWritePtr - vertexStart, "PPGe Vertex"); WriteCmdAddrWithBase(GE_CMD_VADDR, vertexStart); WriteCmd(GE_CMD_PRIM, (prim << 16) | vertexCount); + vertexStart = 0; } bool PPGeIsFontTextureAddress(u32 addr) { @@ -272,6 +273,7 @@ void __PPGeInit() { int val = i; palette[i] = (val << 12) | 0xFFF; } + NotifyMemInfo(MemBlockFlags::WRITE, palette.ptr, 16 * sizeof(u16_le), "PPGe Palette"); const u32_le *imagePtr = (u32_le *)imageData[0]; u8 *ramPtr = atlasPtr == 0 ? nullptr : (u8 *)Memory::GetPointer(atlasPtr); @@ -286,7 +288,10 @@ void __PPGeInit() { u8 cval = (a2 << 4) | a1; ramPtr[i] = cval; } - atlasHash = XXH3_64bits(ramPtr, atlasWidth * atlasHeight / 2); + if (atlasPtr != 0) { + atlasHash = XXH3_64bits(ramPtr, atlasSize); + NotifyMemInfo(MemBlockFlags::WRITE, atlasPtr, atlasSize, "PPGe Atlas"); + } free(imageData[0]); @@ -457,6 +462,7 @@ void PPGeEnd() if (dataWritePtr > dataPtr) { // We actually drew something gpu->EnableInterrupts(false); + NotifyMemInfo(MemBlockFlags::WRITE, dlPtr, dlWritePtr - dlPtr, "PPGe ListCmds"); u32 list = sceGeListEnQueue(dlPtr, dlWritePtr, -1, listArgs.ptr); DEBUG_LOG(SCEGE, "PPGe enqueued display list %i", list); gpu->EnableInterrupts(true); diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index cbbf4621be..6bc9d7437f 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -701,12 +701,11 @@ void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimiti DeferredDrawCall &dc = drawCalls[numDrawCalls]; dc.verts = verts; dc.inds = inds; + dc.vertexCount = vertexCount; dc.indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT; dc.prim = prim; - dc.vertexCount = vertexCount; - dc.uvScale = gstate_c.uv; dc.cullMode = cullMode; - + dc.uvScale = gstate_c.uv; if (inds) { GetIndexBounds(inds, vertexCount, vertTypeID, &dc.indexLowerBound, &dc.indexUpperBound); } else { diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index fb1218224b..a8997454d9 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -23,6 +23,7 @@ #include "Common/Data/Collections/Hashmaps.h" #include "GPU/GPUState.h" +#include "GPU/Common/GPUStateUtils.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Common/IndexGenerator.h" #include "GPU/Common/VertexDecoderCommon.h" @@ -165,10 +166,10 @@ protected: u32 vertexCount; u8 indexType; s8 prim; + u8 cullMode; u16 indexLowerBound; u16 indexUpperBound; UVScale uvScale; - int cullMode; }; enum { MAX_DEFERRED_DRAW_CALLS = 128 }; @@ -186,12 +187,13 @@ protected: GEPrimitiveType prevPrim_ = GE_PRIM_INVALID; // Shader blending state - bool fboTexNeedsBind_ = false; bool fboTexBound_ = false; // Sometimes, unusual situations mean we need to reset dirty flags after state calc finishes. uint64_t dirtyRequiresRecheck_ = 0; + ComputedPipelineState pipelineState_; + // Hardware tessellation TessellationDataTransfer *tessDataTransfer; }; diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 77c8042435..2173130295 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -121,7 +121,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool useDiscardStencilBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL); - bool readFramebuffer = replaceBlend == REPLACE_BLEND_COPY_FBO || colorWriteMask; + bool readFramebuffer = replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER || colorWriteMask; bool readFramebufferTex = readFramebuffer && !gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH); bool needFragCoord = readFramebuffer || gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); @@ -132,11 +132,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu return false; } - if (readFramebuffer && compat.shaderLanguage == HLSL_D3D9) { - *errorString = "Framebuffer read not yet supported in HLSL D3D9"; - return false; - } - if (compat.shaderLanguage == ShaderLanguage::GLSL_VULKAN) { if (useDiscardStencilBugWorkaround && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) { WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n"); @@ -227,7 +222,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } if (readFramebufferTex) { // No sampler required, we Load - WRITE(p, "Texture2D fboTex : register(t1);\n"); + WRITE(p, "Texture2D fbotex : register(t1);\n"); } WRITE(p, "cbuffer base : register(b0) {\n%s};\n", ub_baseStr); @@ -264,8 +259,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (enableFog) { WRITE(p, " float v_fogdepth: TEXCOORD1;\n"); } - if (compat.shaderLanguage == HLSL_D3D11 && needFragCoord) { - WRITE(p, " vec4 pixelPos : SV_POSITION;\n"); + if (needFragCoord) { + if (compat.shaderLanguage == HLSL_D3D11) { + WRITE(p, " vec4 pixelPos : SV_POSITION;\n"); + } else if (compat.shaderLanguage == HLSL_D3D9) { + WRITE(p, " vec4 pixelPos : VPOS;\n"); // VPOS is only supported for Shader Model 3.0, but we can probably forget about D3D9 SM2.0 at this point... + } } WRITE(p, "};\n"); @@ -457,7 +456,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } else if (compat.shaderLanguage == HLSL_D3D9) { WRITE(p, "PS_OUT main( PS_IN In ) {\n"); WRITE(p, " PS_OUT outfragment;\n"); - WRITE(p, " vec4 target;\n"); + if (needFragCoord) { + WRITE(p, " vec4 gl_FragCoord = In.pixelPos;\n"); + } } else { WRITE(p, "void main() {\n"); } @@ -477,7 +478,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu // Two things read from the old framebuffer - shader replacement blending and bit-level masking. if (readFramebuffer) { if (compat.shaderLanguage == HLSL_D3D11) { - WRITE(p, " vec4 destColor = fboTex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n"); + WRITE(p, " vec4 destColor = fbotex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n"); + } else if (compat.shaderLanguage == HLSL_D3D9) { + WRITE(p, " vec4 destColor = tex2D(fbotex, gl_FragCoord.xy * u_fbotexSize.xy);\n", compat.texture); } else if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) { // If we have EXT_shader_framebuffer_fetch / ARM_shader_framebuffer_fetch, we skip the blit. // We can just read the prev value more directly. @@ -940,7 +943,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " v.rgb = v.rgb * %s;\n", srcFactor); } - if (replaceBlend == REPLACE_BLEND_COPY_FBO) { + if (replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER) { const char *srcFactor = nullptr; const char *dstFactor = nullptr; @@ -1057,9 +1060,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu return false; } - // TODO: This could support more ops using the shader blending mechanism. - LogicOpReplaceType replaceLogicOpType = (LogicOpReplaceType)id.Bits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2); - switch (replaceLogicOpType) { + // TODO: We could have a separate mechanism to support more ops using the shader blending mechanism, + // on hardware that can do proper bit math in fragment shaders. + SimulateLogicOpType simulateLogicOpType = (SimulateLogicOpType)id.Bits(FS_BIT_SIMULATE_LOGIC_OP_TYPE, 2); + switch (simulateLogicOpType) { case LOGICOPTYPE_ONE: WRITE(p, " %s.rgb = splat3(1.0);\n", compat.fragColor0); break; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index c9c49da6bb..5a74382fb6 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -21,6 +21,7 @@ #include "Common/GPU/thin3d.h" #include "Common/GPU/OpenGL/GLFeatures.h" +#include "Common/Data/Collections/TinySet.h" #include "Common/Data/Convert/ColorConv.h" #include "Common/Data/Text/I18n.h" #include "Common/Math/lin/matrix4x4.h" @@ -126,13 +127,19 @@ VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const { } VirtualFramebuffer *FramebufferManagerCommon::GetExactVFB(u32 addr, int stride, GEBufferFormat format) const { + VirtualFramebuffer *newest = nullptr; for (auto vfb : vfbs_) { if (vfb->fb_address == addr && vfb->fb_stride == stride && vfb->fb_format == format) { - // There'll only be one exact match, we don't allow duplicates with these conditions. - return vfb; + if (newest) { + if (vfb->colorBindSeq > newest->colorBindSeq) { + newest = vfb; + } + } else { + newest = vfb; + } } } - return nullptr; + return newest; } VirtualFramebuffer *FramebufferManagerCommon::ResolveVFB(u32 addr, int stride, GEBufferFormat format) { @@ -167,6 +174,7 @@ u32 FramebufferManagerCommon::ColorBufferByteSize(const VirtualFramebuffer *vfb) } bool FramebufferManagerCommon::ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const { + // Dangan Ronpa hack return PSP_CoreParameter().compat.flags().Force04154000Download && vfb->fb_address == 0x04154000; } @@ -326,15 +334,20 @@ void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPU params->viewportHeight = (int)(fabsf(vpy) * 2.0f); params->regionWidth = gstate.getRegionX2() + 1; params->regionHeight = gstate.getRegionY2() + 1; - params->scissorWidth = gstate.getScissorX2() + 1; - params->scissorHeight = gstate.getScissorY2() + 1; + + params->scissorLeft = gstate.getScissorX1(); + params->scissorTop = gstate.getScissorY1(); + params->scissorRight = gstate.getScissorX2() + 1; + params->scissorBottom = gstate.getScissorY2() + 1; if (gstate.getRegionRateX() != 0x100 || gstate.getRegionRateY() != 0x100) { WARN_LOG_REPORT_ONCE(regionRate, G3D, "Drawing region rate add non-zero: %04x, %04x of %04x, %04x", gstate.getRegionRateX(), gstate.getRegionRateY(), gstate.getRegionX2(), gstate.getRegionY2()); } } -VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason) { +static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width); + +VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason) { gstate_c.Clean(DIRTY_FRAMEBUF); // Collect all parameters. This whole function has really become a cesspool of heuristics... @@ -343,9 +356,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame // As there are no clear "framebuffer width" and "framebuffer height" registers, // we need to infer the size of the current framebuffer somehow. int drawing_width, drawing_height; - EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height); - - gstate_c.SetCurRTOffset(0, 0); + EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorRight, params.scissorBottom, drawing_width, drawing_height); if (params.fb_address == params.z_address) { // Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure) @@ -353,6 +364,13 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame WARN_LOG_ONCE(color_equal_z, G3D, "Framebuffer bound with color addr == z addr, likely will not use Z in this pass: %08x", params.fb_address); } + // Compatibility hack for Killzone, see issue #6207. + if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin && params.fb_format == GE_FORMAT_8888) { + ApplyKillzoneFramebufferSplit(¶ms, &drawing_width); + } else { + gstate_c.SetCurRTOffset(0, 0); + } + // Find a matching framebuffer. VirtualFramebuffer *vfb = nullptr; for (auto v : vfbs_) { @@ -378,7 +396,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame vfb->height = drawing_height; } break; - } else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format) { + } else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format && !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { u32 v_fb_first_line_end_ptr = v->fb_address + v->fb_stride * bpp; u32 v_fb_end_ptr = v->fb_address + v->fb_stride * v->height * bpp; @@ -477,7 +495,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame currentRenderVfb_ = vfb; // Assume that if we're clearing right when switching to a new framebuffer, we don't need to upload. - if (useBufferedRendering_ && !g_Config.bDisableSlowFramebufEffects && params.isDrawing) { + if (useBufferedRendering_ && params.isDrawing) { gpu->PerformMemoryUpload(params.fb_address, byteSize); // Alpha was already done by PerformMemoryUpload. PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA); @@ -659,7 +677,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra // This will result in reinterpret later, if both formats are 16-bit. sources.push_back(CopySource{ src, RASTER_COLOR, 0, 0 }); } else { - // Happens in Prince of Persia - Revelations. Ignoring. + // This shouldn't happen anymore. I think when it happened last, we still had + // lax stride checking when video was incoming, and a resize happened causing a duplicate. } } else if (src->fb_stride == dst->fb_stride && src->fb_format == dst->fb_format) { u32 bytesPerPixel = BufferFormatBytesPerPixel(src->fb_format); @@ -789,7 +808,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra } if (dst != currentRenderVfb_ && tookActions) { - draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After FakeReinterpret"); + // Will probably just change the name of the current renderpass, since one was started by the reinterpret itself. + draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After Reinterpret"); } shaderManager_->DirtyLastShader(); @@ -886,7 +906,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer } void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb) { - // ugly... if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) { gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); } @@ -898,11 +917,12 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) { if (ShouldDownloadFramebuffer(vfb) && !vfb->memoryUpdated) { - ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height); + ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR); vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR; } else { DownloadFramebufferOnSwitch(prevVfb); } + textureCache_->ForgetLastTexture(); shaderManager_->DirtyLastShader(); @@ -959,41 +979,41 @@ void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int stride, } } -void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size, bool safe) { +void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { // Take off the uncached flag from the address. Not to be confused with the start of VRAM. addr &= 0x3FFFFFFF; // TODO: Could go through all FBOs, but probably not important? // TODO: Could also check for inner changes, but video is most important. // TODO: This shouldn't care if it's a display framebuf or not, should work exactly the same. bool isDisplayBuf = addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr(); - if (isDisplayBuf || safe) { - // TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help. - if (!Memory::IsValidAddress(displayFramebufPtr_)) - return; + // TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help. + if (!Memory::IsValidAddress(displayFramebufPtr_)) + return; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - if (vfb->fb_address == addr) { - FlushBeforeCopy(); + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + if (vfb->fb_address == addr) { + FlushBeforeCopy(); - if (useBufferedRendering_ && vfb->fbo) { - GEBufferFormat fmt = vfb->fb_format; - if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) { - // If we're not rendering to it, format may be wrong. Use displayFormat_ instead. - fmt = displayFormat_; - } - DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height); - SetColorUpdated(vfb, gstate_c.skipDrawReason); - } else { - INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->fb_format); - DestroyFramebuf(vfb); - vfbs_.erase(vfbs_.begin() + i--); + if (useBufferedRendering_ && vfb->fbo) { + GEBufferFormat fmt = vfb->fb_format; + if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) { + // If we're not rendering to it, format may be wrong. Use displayFormat_ instead. + // TODO: This doesn't seem quite right anymore. + fmt = displayFormat_; } + DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height); + SetColorUpdated(vfb, gstate_c.skipDrawReason); + } else { + INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format)); + DestroyFramebuf(vfb); + vfbs_.erase(vfbs_.begin() + i--); } } - - RebindFramebuffer("RebindFramebuffer - UpdateFromMemory"); } + + RebindFramebuffer("RebindFramebuffer - UpdateFromMemory"); + // TODO: Necessary? gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } @@ -1106,7 +1126,7 @@ void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer } if (x < src->drawnWidth && y < src->drawnHeight && w > 0 && h > 0) { - BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, "Blit_CopyFramebufferForColorTexture"); + BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, RASTER_COLOR, "Blit_CopyFramebufferForColorTexture"); } } @@ -1215,8 +1235,8 @@ void FramebufferManagerCommon::DownloadFramebufferOnSwitch(VirtualFramebuffer *v // Some games will draw to some memory once, and use it as a render-to-texture later. // To support this, we save the first frame to memory when we have a safe w/h. // Saving each frame would be slow. - if (!g_Config.bDisableSlowFramebufEffects && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) { - ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight); + if (g_Config.bBlockTransferGPU && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) { + ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR); vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR; vfb->safeWidth = 0; vfb->safeHeight = 0; @@ -1369,7 +1389,7 @@ void FramebufferManagerCommon::DecimateFBOs() { int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used); if (ShouldDownloadFramebuffer(vfb) && age == 0 && !vfb->memoryUpdated) { - ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height); + ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR); vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR; } @@ -1502,9 +1522,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, if (vfb->fbo) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO"); if (!skipCopy) { - // TODO: In this case, it'll nearly always be better to draw the old framebuffer to the new one than to do an actual blit. - // Usually hardly a performance issue though. - BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, "Blit_ResizeFramebufFBO"); + BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "Blit_ResizeFramebufFBO"); + // Depth copying is handled by deferred copies later. } } fbosToDelete_.push_back(old.fbo); @@ -1608,7 +1627,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } else { WARN_LOG_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy %08x -> %08x (size: %x)", src, dst, size); // Just do the blit! - BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, "Blit_InterBufferMemcpy"); + BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, RASTER_COLOR, "Blit_InterBufferMemcpy"); SetColorUpdated(dstBuffer, skipDrawReason); RebindFramebuffer("RebindFramebuffer - Inter-buffer memcpy"); } @@ -1630,8 +1649,8 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, FlushBeforeCopy(); if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) { WARN_LOG_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight); - } else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated && !PSP_CoreParameter().compat.flags().DisableReadbacks) { - ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH); + } else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated) { + ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, RASTER_COLOR); srcBuffer->usageFlags = (srcBuffer->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR; } return false; @@ -1660,7 +1679,7 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p int x_bytes = x_pixels * bpp; int w_bytes = w_pixels * bpp; - std::vector candidates; + TinySet candidates; // We work entirely in bytes when we do the matching, because games don't consistently use bpps that match // that of their buffers. Then after matching we try to map the copy to the simplest operation that does @@ -1735,20 +1754,27 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p candidates.push_back(candidate); } + const BlockTransferRect *best = nullptr; // Sort candidates by just recency for now, we might add other. - std::sort(candidates.begin(), candidates.end()); + for (size_t i = 0; i < candidates.size(); i++) { + const BlockTransferRect *candidate = &candidates[i]; + if (!best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq) { + best = candidate; + } + } if (candidates.size() > 1) { - std::string log; - for (auto &candidate : candidates) { - log += " - " + candidate.ToString() + "\n"; + if (Reporting::ShouldLogNTimes("mulblock", 5)) { + std::string log; + for (size_t i = 0; i < candidates.size(); i++) { + log += " - " + candidates[i].ToString() + "\n"; + } + WARN_LOG(G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str()); } - WARN_LOG_N_TIMES(mulblock, 5, G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str()); } if (!candidates.empty()) { - // Pick the last candidate. - *rect = candidates.back(); + *rect = *best; return true; } else { if (Memory::IsVRAMAddress(basePtr) && destination && h >= 128) { @@ -1798,7 +1824,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd } // 1:1 pixel sides buffers, we resize buffers to these before we read them back. -VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb) { +VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel) { // For now we'll keep these on the same struct as the ones that can get displayed // (and blatantly copy work already done above while at it). VirtualFramebuffer *nvfb = nullptr; @@ -1837,12 +1863,13 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram char name[64]; snprintf(name, sizeof(name), "download_temp"); - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, name }); + // TODO: We don't have a way to create a depth-only framebuffer yet. + // Also, at least on Vulkan we always create both depth and color, need to rework how we handle renderpasses. + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, channel == RASTER_DEPTH ? true : false, name }); if (!nvfb->fbo) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight); return nullptr; } - bvfbs_.push_back(nvfb); } else { UpdateDownloadTempBuffer(nvfb); @@ -2012,7 +2039,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst dstBasePtr, dstRect.x_bytes / bpp, dstRect.y, dstStride); FlushBeforeCopy(); // Some backends can handle blitting within a framebuffer. Others will just have to deal with it or ignore it, apparently. - BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, "Blit_IntraBufferBlockTransfer"); + BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, RASTER_COLOR, "Blit_IntraBufferBlockTransfer"); RebindFramebuffer("rebind after intra block transfer"); SetColorUpdated(dstRect.vfb, skipDrawReason); return true; // Skip the memory copy. @@ -2033,7 +2060,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst bpp = buffer_bpp; } FlushBeforeCopy(); - BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, "Blit_InterBufferBlockTransfer"); + BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, RASTER_COLOR, "Blit_InterBufferBlockTransfer"); RebindFramebuffer("RebindFramebuffer - Inter-buffer block transfer"); SetColorUpdated(dstRect.vfb, skipDrawReason); return true; @@ -2068,7 +2095,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst if (tooTall) { WARN_LOG_ONCE(btdheight, G3D, "Block transfer download %08x -> %08x dangerous, %d+%d is taller than %d", srcBasePtr, dstBasePtr, srcRect.y, srcRect.h, srcRect.vfb->bufferHeight); } - ReadFramebufferToMemory(srcRect.vfb, static_cast(srcX * srcXFactor), srcY, static_cast(srcRect.w_bytes * srcXFactor), srcRect.h); + ReadFramebufferToMemory(srcRect.vfb, static_cast(srcX * srcXFactor), srcY, static_cast(srcRect.w_bytes * srcXFactor), srcRect.h, RASTER_COLOR); srcRect.vfb->usageFlags = (srcRect.vfb->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR; } } @@ -2248,7 +2275,7 @@ void FramebufferManagerCommon::ShowScreenResolution() { // We might also want to implement an asynchronous callback-style version of this. Would probably // only be possible to implement optimally on Vulkan, but on GL and D3D11 we could do pixel buffers -// and read on the next frame, then call the callback. PackFramebufferAsync_ on OpenGL already does something similar. +// and read on the next frame, then call the callback. // // The main use cases for this are: // * GE debugging(in practice async will not matter because it will stall anyway.) @@ -2286,7 +2313,7 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB tempVfb.renderWidth = w; tempVfb.renderHeight = h; tempVfb.renderScaleFactor = maxScaleFactor; - BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, "Blit_GetFramebuffer"); + BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, RASTER_COLOR, "Blit_GetFramebuffer"); bound = tempFBO; } else { @@ -2395,33 +2422,33 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return retval; } -// This function takes an already correctly-sized framebuffer and packs it into RAM. +// This function takes an already correctly-sized framebuffer and reads it into emulated PSP VRAM. // Does not need to account for scaling. +// // Color conversion is currently done on CPU but should theoretically be done on GPU. // (Except using the GPU might cause problems because of various implementations' // dithering behavior and games that expect exact colors like Danganronpa, so we // can't entirely be rid of the CPU path.) -- unknown -void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); - return; - } - +void FramebufferManagerCommon::PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) { if (w <= 0 || h <= 0) { - ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync_: %d %d %d %d", x, y, w, h); + ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync: %d %d %d %d", x, y, w, h); return; } const u32 fb_address = vfb->fb_address & 0x3FFFFFFF; - Draw::DataFormat destFormat = GEFormatToThin3D(vfb->fb_format); + Draw::DataFormat destFormat = channel == RASTER_COLOR ? GEFormatToThin3D(vfb->fb_format) : GEFormatToThin3D(GE_FORMAT_DEPTH16); const int dstBpp = (int)DataFormatSizeInBytes(destFormat); - const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; - const int dstSize = (h * vfb->fb_stride + w - 1) * dstBpp; + int stride = channel == RASTER_COLOR ? vfb->fb_stride : vfb->z_stride; + + const int dstByteOffset = (y * stride + x) * dstBpp; + // Leave the gap between the end of the last line and the full stride. + // This is only used for the NotifyMemInfo range. + const int dstSize = (h * stride + w - 1) * dstBpp; if (!Memory::IsValidRange(fb_address + dstByteOffset, dstSize)) { - ERROR_LOG_REPORT(G3D, "PackFramebufferSync_ would write outside of memory, ignoring"); + ERROR_LOG_REPORT(G3D, "PackFramebufferSync would write outside of memory, ignoring"); return; } @@ -2432,18 +2459,18 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x, ptr=%p", fb_address, destPtr); if (destPtr) { - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync_"); + draw_->CopyFramebufferToMemorySync(vfb->fbo, channel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync"); char tag[128]; size_t len = snprintf(tag, sizeof(tag), "FramebufferPack/%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->fb_format)); NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, tag, len); } else { - ERROR_LOG(G3D, "PackFramebufferSync_: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride); + ERROR_LOG(G3D, "PackFramebufferSync: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride); } gpuStats.numReadbacks++; } -void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h) { +void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) { // Clamp to bufferWidth. Sometimes block transfers can cause this to hit. if (x + w >= vfb->bufferWidth) { w = vfb->bufferWidth - x; @@ -2481,13 +2508,13 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, } if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) { - // No need to blit - PackFramebufferSync_(vfb, x, y, w, h); + // No need to stretch-blit + PackFramebufferSync(vfb, x, y, w, h, channel); } else { - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, channel); if (nvfb) { - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_ReadFramebufferToMemory"); - PackFramebufferSync_(nvfb, x, y, w, h); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, channel, "Blit_ReadFramebufferToMemory"); + PackFramebufferSync(nvfb, x, y, w, h, channel); } } @@ -2536,10 +2563,10 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo vfb->clutUpdatedBytes = loadBytes; // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, RASTER_COLOR); if (nvfb) { - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_DownloadFramebufferForClut"); - PackFramebufferSync_(nvfb, x, y, w, h); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, RASTER_COLOR, "Blit_DownloadFramebufferForClut"); + PackFramebufferSync(nvfb, x, y, w, h, RASTER_COLOR); } textureCache_->ForgetLastTexture(); @@ -2664,9 +2691,7 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); } -void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) { - RasterChannel channel = RASTER_COLOR; - +void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag) { if (!dst->fbo || !src->fbo || !useBufferedRendering_) { // This can happen if they recently switched from non-buffered. if (useBufferedRendering_) { @@ -2697,8 +2722,8 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX return; } - bool useBlit = draw_->GetDeviceCaps().framebufferBlitSupported; - bool useCopy = draw_->GetDeviceCaps().framebufferCopySupported; + bool useBlit = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferBlitSupported : false; + bool useCopy = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferCopySupported : false; if (dst == currentRenderVfb_) { // If already bound, using either a blit or a copy is unlikely to be an optimization. // So we're gonna use a raster draw instead. @@ -2767,7 +2792,7 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX draw_->InvalidateCachedState(); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); } // The input is raw pixel coordinates, scale not taken into account. @@ -2859,3 +2884,37 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi vfb->colorBindSeq = GetBindSeqCount(); return vfb; } + +static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width) { + // Detect whether we're rendering to the margin. + bool margin; + if ((params->scissorRight - params->scissorLeft) == 32) { + // Title screen has this easy case. It also uses non-through verts, so lucky for us that we have this. + margin = true; + } else if (params->scissorRight == 480) { + margin = false; + } else { + // Go deep, look at the vertices. Killzone-specific, of course. + margin = false; + if ((gstate.vertType & 0xFFFFFF) == 0x00800102) { // through, u16, s16 + u16 *vdata = (u16 *)Memory::GetPointerUnchecked(gstate_c.vertexAddr); + int v0PosU = vdata[0]; + int v0PosX = vdata[2]; + if (v0PosX >= 480 && v0PosU < 480) { + // Texturing from surface, writing to margin + margin = true; + } + } + } + + if (margin) { + gstate_c.SetCurRTOffset(-480, 0); + // Modify the fb_address and z_address too to avoid matching below. + params->fb_address += 480 * 4; + params->z_address += 480 * 2; + *drawing_width = 32; + } else { + gstate_c.SetCurRTOffset(0, 0); + *drawing_width = 480; + } +} diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 84bcd99b91..f78e52d13e 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -165,10 +165,12 @@ struct FramebufferHeuristicParams { bool isBlending; int viewportWidth; int viewportHeight; - int regionWidth; - int regionHeight; - int scissorWidth; - int scissorHeight; + int16_t regionWidth; + int16_t regionHeight; + int16_t scissorLeft; + int16_t scissorTop; + int16_t scissorRight; + int16_t scissorBottom; }; struct GPUgstate; @@ -206,7 +208,7 @@ enum class TempFBO { STENCIL, }; -inline Draw::DataFormat GEFormatToThin3D(int geFormat) { +inline Draw::DataFormat GEFormatToThin3D(GEBufferFormat geFormat) { switch (geFormat) { case GE_FORMAT_4444: return Draw::DataFormat::A4R4G4B4_UNORM_PACK16; @@ -216,7 +218,10 @@ inline Draw::DataFormat GEFormatToThin3D(int geFormat) { return Draw::DataFormat::R5G6B5_UNORM_PACK16; case GE_FORMAT_8888: return Draw::DataFormat::R8G8B8A8_UNORM; + case GE_FORMAT_DEPTH16: + return Draw::DataFormat::D16; default: + // TODO: Assert? return Draw::DataFormat::UNDEFINED; } } @@ -240,10 +245,6 @@ struct BlockTransferRect { int x_pixels() const { return x_bytes / BufferFormatBytesPerPixel(vfb->fb_format); } - - bool operator < (const BlockTransferRect &other) const { - return vfb->colorBindSeq < other.vfb->colorBindSeq; - } }; namespace Draw { @@ -276,7 +277,7 @@ public: void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format); void DestroyFramebuf(VirtualFramebuffer *v); - VirtualFramebuffer *DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason); + VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason); VirtualFramebuffer *SetRenderFrameBuffer(bool framebufChanged, int skipDrawReason) { // Inlining this part since it's so frequent. if (!framebufChanged && currentRenderVfb_) { @@ -305,7 +306,7 @@ public: bool NotifyFramebufferCopy(u32 src, u32 dest, int size, bool isMemset, u32 skipDrawReason); void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt); - void UpdateFromMemory(u32 addr, int size, bool safe); + void UpdateFromMemory(u32 addr, int size); void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor); bool PerformStencilUpload(u32 addr, int size, StencilUpload flags); @@ -320,7 +321,7 @@ public: void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel); void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes); void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat); @@ -420,7 +421,7 @@ public: VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat); protected: - virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + virtual void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel); void SetViewport2D(int x, int y, int w, int h); Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags); @@ -436,7 +437,7 @@ protected: virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra // Used by ReadFramebufferToMemory and later framebuffer block copies - void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag); + void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag); void BlitUsingRaster( Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, @@ -461,7 +462,7 @@ protected: bool FindTransferFramebuffer(u32 basePtr, int stride, int x, int y, int w, int h, int bpp, bool destination, BlockTransferRect *rect); - VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb); + VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel); virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {} VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format); diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 1272a0d7c0..c0d2df2e5b 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -179,7 +179,7 @@ ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) { return REPLACE_ALPHA_NO; } - if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_COPY_FBO) { + if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_READ_FRAMEBUFFER) { if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) { return REPLACE_ALPHA_YES; } else { @@ -257,7 +257,7 @@ StencilValueType ReplaceAlphaWithStencilType() { return STENCIL_VALUE_KEEP; } -ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferFormat bufferFormat) { +ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat) { if (gstate_c.blueToAlpha) { return REPLACE_BLEND_BLUE_TO_ALPHA; } @@ -270,14 +270,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma // Let's get the non-factor modes out of the way first. switch (eq) { case GE_BLENDMODE_ABSDIFF: - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_BLENDMODE_MIN: case GE_BLENDMODE_MAX: if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) { return REPLACE_BLEND_STANDARD; } else { - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; } default: @@ -300,19 +300,19 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_2X_ALPHA; // Can't double, we need the source color to be correct. // Doubling only alpha would clamp the src alpha incorrectly. - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: if (bufferFormat == GE_FORMAT_565) return REPLACE_BLEND_2X_ALPHA; - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: // We can't technically do this correctly (due to clamping) without reading the dst color. // Using a copy isn't accurate either, though, when there's overlap. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; case GE_DSTBLEND_DOUBLEINVSRCALPHA: @@ -339,7 +339,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_STANDARD; } // Can't double, we need the source color to be correct. - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEDSTALPHA: case GE_DSTBLEND_DOUBLEINVDSTALPHA: @@ -348,7 +348,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma // Doubling will have no effect here. return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: case GE_DSTBLEND_DOUBLEINVSRCALPHA: @@ -357,7 +357,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma } // Double both src (for dst alpha) and alpha (for dst factor.) // But to be accurate (clamping), we need to read the dst color. - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_SRCALPHA: case GE_DSTBLEND_INVSRCALPHA: @@ -369,7 +369,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_STANDARD; } // We can't technically do this correctly (due to clamping) without reading the dst alpha. - return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; } case GE_SRCBLEND_DOUBLEINVDSTALPHA: @@ -383,14 +383,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLESRCALPHA: case GE_DSTBLEND_DOUBLEINVSRCALPHA: if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_2X_ALPHA; } - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_SRCALPHA: case GE_DSTBLEND_INVSRCALPHA: @@ -401,7 +401,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; } case GE_SRCBLEND_FIXA: @@ -409,7 +409,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma switch (funcB) { case GE_DSTBLEND_DOUBLESRCALPHA: // Can't safely double alpha, will clamp. - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_DOUBLEINVSRCALPHA: // Doubling alpha is safe for the inverse, will clamp to zero either way. @@ -420,7 +420,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; case GE_DSTBLEND_FIXB: default: @@ -454,14 +454,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) { // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; } else { // This means dst alpha/color is used in the src factor. // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?) // We will just hope that doubling alpha for the dst factor will not clamp too badly. if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) - return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; return REPLACE_BLEND_2X_ALPHA; } @@ -478,7 +478,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma if (bufferFormat == GE_FORMAT_565) { return REPLACE_BLEND_STANDARD; } - return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO; + return REPLACE_BLEND_READ_FRAMEBUFFER; default: return REPLACE_BLEND_STANDARD; @@ -489,27 +489,6 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma return REPLACE_BLEND_STANDARD; } -LogicOpReplaceType ReplaceLogicOpType() { - if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) { - switch (gstate.getLogicOp()) { - case GE_LOGIC_COPY_INVERTED: - case GE_LOGIC_AND_INVERTED: - case GE_LOGIC_OR_INVERTED: - case GE_LOGIC_NOR: - case GE_LOGIC_NAND: - case GE_LOGIC_EQUIV: - return LOGICOPTYPE_INVERT; - case GE_LOGIC_INVERTED: - return LOGICOPTYPE_ONE; - case GE_LOGIC_SET: - return LOGICOPTYPE_ONE; - default: - return LOGICOPTYPE_NORMAL; - } - } - return LOGICOPTYPE_NORMAL; -} - static const float DEPTH_SLICE_FACTOR_HIGH = 4.0f; static const float DEPTH_SLICE_FACTOR_16BIT = 256.0f; @@ -581,8 +560,11 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo renderHeightFactor = renderHeight / 272.0f; } - renderX = gstate_c.curRTOffsetX; - renderY = gstate_c.curRTOffsetY; + // We take care negative offsets of in the projection matrix. + // These come from split framebuffers (Killzone). + // TODO: Might be safe to do get rid of this here and do the same for positive offsets? + renderX = std::max(gstate_c.curRTOffsetX, 0); + renderY = std::max(gstate_c.curRTOffsetY, 0); // Scissor int scissorX1 = gstate.getScissorX1(); @@ -609,6 +591,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo float offsetY = gstate.getOffsetY(); if (out.throughMode) { + // If renderX/renderY are offset to compensate for a split framebuffer, + // applying the offset to the viewport isn't enough, since the viewport clips. + // We need to apply either directly to the vertices, or to the "through" projection matrix. out.viewportX = renderX * renderWidthFactor + displayOffsetX; out.viewportY = renderY * renderHeightFactor + displayOffsetY; out.viewportW = curRTWidth * renderWidthFactor; @@ -868,8 +853,10 @@ static inline bool blendColorSimilar(uint32_t a, uint32_t b, int margin = 25) { return false; } -// Try to simulate some common logic ops. -static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { +// Try to simulate some common logic ops by using blend, if needed. +// The shader might also need modification, the below function SimulateLogicOpShaderTypeIfNeeded +// takes care of that. +static void SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { // Note: our shader solution applies logic ops BEFORE blending, not correctly after. // This is however fine for the most common ones, like CLEAR/NOOP/SET, etc. if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { @@ -931,7 +918,28 @@ static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq & } } -// Try to simulate some common logic ops. +// Choose the shader part of the above logic op fallback simulation. +SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded() { + if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) { + switch (gstate.getLogicOp()) { + case GE_LOGIC_COPY_INVERTED: + case GE_LOGIC_AND_INVERTED: + case GE_LOGIC_OR_INVERTED: + case GE_LOGIC_NOR: + case GE_LOGIC_NAND: + case GE_LOGIC_EQUIV: + return LOGICOPTYPE_INVERT; + case GE_LOGIC_INVERTED: + return LOGICOPTYPE_ONE; + case GE_LOGIC_SET: + return LOGICOPTYPE_ONE; + default: + return LOGICOPTYPE_NORMAL; + } + } + return LOGICOPTYPE_NORMAL; +} + void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) { StencilValueType stencilType = STENCIL_VALUE_KEEP; if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { @@ -942,7 +950,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS BlendFactor srcBlend = BlendFactor::ONE; BlendFactor dstBlend = BlendFactor::ZERO; BlendEq blendEq = BlendEq::ADD; - ApplyLogicOp(srcBlend, dstBlend, blendEq); + SimulateLogicOpIfNeeded(srcBlend, dstBlend, blendEq); // We're not blending, but we may still want to "blend" for stencil. // This is only useful for INCR/DECR/INVERT. Others can write directly. @@ -950,7 +958,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS case STENCIL_VALUE_INCR_4: case STENCIL_VALUE_INCR_8: // We'll add the incremented value output by the shader. - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE); blendState.setEquation(blendEq, BlendEq::ADD); break; @@ -958,23 +966,23 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS case STENCIL_VALUE_DECR_4: case STENCIL_VALUE_DECR_8: // We'll subtract the incremented value output by the shader. - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE); blendState.setEquation(blendEq, BlendEq::SUBTRACT); break; case STENCIL_VALUE_INVERT: // The shader will output one, and reverse subtracting will essentially invert. - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE); blendState.setEquation(blendEq, BlendEq::REVERSE_SUBTRACT); break; default: if (srcBlend == BlendFactor::ONE && dstBlend == BlendFactor::ZERO && blendEq == BlendEq::ADD) { - blendState.enabled = false; + blendState.blendEnabled = false; } else { - blendState.enabled = true; + blendState.blendEnabled = true; blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ZERO); blendState.setEquation(blendEq, BlendEq::ADD); } @@ -982,86 +990,59 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS } } -bool IsColorWriteMaskComplex(bool allowFramebufferRead) { - // Restrict to Outrun temporarily (by uglily reusing the ReinterpretFramebuffers flag) - // This check must match the one in ConvertMaskState. - if (!allowFramebufferRead || !PSP_CoreParameter().compat.flags().ShaderColorBitmask) { - // Don't have a choice - we'll make do but it won't always be right. - return false; - } - - if (gstate_c.blueToAlpha) { - // We'll generate a simple ___A mask. - return false; - } - - uint32_t colorMask = (gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24); - - for (int i = 0; i < 4; i++) { - switch (colorMask & 0xFF) { - case 0x0: - case 0xFF: - break; - default: - return true; - } - colorMask >>= 8; - } - return false; -} - // If we can we emulate the colorMask by simply toggling the full R G B A masks offered // by modern hardware, we do that. This is 99.9% of the time. // When that's not enough, we fall back on a technique similar to shader blending, // we read from the framebuffer (or a copy of it). -void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) { +// We also prepare uniformMask so that if doing this in the shader gets forced-on, +// we have the right mask already. +void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported) { if (gstate_c.blueToAlpha) { maskState.applyFramebufferRead = false; - maskState.rgba[0] = false; - maskState.rgba[1] = false; - maskState.rgba[2] = false; - maskState.rgba[3] = true; + maskState.uniformMask = 0xFF000000; + maskState.channelMask = 0x8; return; } // Invert to convert masks from the PSP's format where 1 is don't draw to PC where 1 is draw. uint32_t colorMask = ~((gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24)); + maskState.uniformMask = colorMask; maskState.applyFramebufferRead = false; + maskState.channelMask = 0; for (int i = 0; i < 4; i++) { int channelMask = colorMask & 0xFF; switch (channelMask) { case 0x0: - maskState.rgba[i] = false; break; case 0xFF: - maskState.rgba[i] = true; + maskState.channelMask |= 1 << i; break; default: - if (allowFramebufferRead) { - // Instead of just 'true', restrict shader bitmasks to Outrun temporarily. - // TODO: This check must match the one in IsColorWriteMaskComplex. - maskState.applyFramebufferRead = PSP_CoreParameter().compat.flags().ShaderColorBitmask; - maskState.rgba[i] = true; + if (shaderBitOpsSupported && PSP_CoreParameter().compat.flags().ShaderColorBitmask) { + // Shaders can emulate masking accurately. Let's make use of that. + maskState.applyFramebufferRead = true; + maskState.channelMask |= 1 << i; } else { - // Use the old heuristic. - maskState.rgba[i] = channelMask >= 128; + // Use the old inaccurate heuristic. + if (channelMask >= 128) { + maskState.channelMask |= 1 << i; + } } } colorMask >>= 8; } // Let's not write to alpha if stencil isn't enabled. - if (IsStencilTestOutputDisabled()) { - maskState.rgba[3] = false; - } else if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) { - // If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel. - maskState.rgba[3] = false; + // Also if the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel. + if (IsStencilTestOutputDisabled() || ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) { + maskState.channelMask &= ~8; + maskState.uniformMask &= ~0xFF000000; } } // Called even if AlphaBlendEnable == false - it also deals with stencil-related blend state. -void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, bool forceReplaceBlend) { +void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) { // Blending is a bit complex to emulate. This is due to several reasons: // // * Doubled blend modes (src, dst, inversed) aren't supported in OpenGL. @@ -1075,52 +1056,52 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, blendState.applyFramebufferRead = false; blendState.dirtyShaderBlendFixValues = false; blendState.useBlendColor = false; - blendState.replaceAlphaWithStencil = REPLACE_ALPHA_NO; - ReplaceBlendType replaceBlend = ReplaceBlendWithShader(allowFramebufferRead, gstate_c.framebufFormat); + ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.framebufFormat); if (forceReplaceBlend) { - replaceBlend = REPLACE_BLEND_COPY_FBO; + replaceBlend = REPLACE_BLEND_READ_FRAMEBUFFER; } + blendState.replaceBlend = replaceBlend; + + blendState.simulateLogicOpType = SimulateLogicOpShaderTypeIfNeeded(); + ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend); + blendState.replaceAlphaWithStencil = replaceAlphaWithStencil; + bool usePreSrc = false; bool blueToAlpha = false; switch (replaceBlend) { case REPLACE_BLEND_NO: - blendState.resetFramebufferRead = true; // We may still want to do something about stencil -> alpha. ApplyStencilReplaceAndLogicOpIgnoreBlend(replaceAlphaWithStencil, blendState); return; case REPLACE_BLEND_BLUE_TO_ALPHA: blueToAlpha = true; - blendState.enabled = gstate.isAlphaBlendEnabled(); + blendState.blendEnabled = gstate.isAlphaBlendEnabled(); // We'll later convert the color blend to blend in the alpha channel. break; - case REPLACE_BLEND_COPY_FBO: - blendState.enabled = true; + case REPLACE_BLEND_READ_FRAMEBUFFER: + blendState.blendEnabled = true; blendState.applyFramebufferRead = true; - blendState.resetFramebufferRead = false; - blendState.replaceAlphaWithStencil = replaceAlphaWithStencil; break; case REPLACE_BLEND_PRE_SRC: case REPLACE_BLEND_PRE_SRC_2X_ALPHA: - blendState.enabled = true; + blendState.blendEnabled = true; usePreSrc = true; break; case REPLACE_BLEND_STANDARD: case REPLACE_BLEND_2X_ALPHA: case REPLACE_BLEND_2X_SRC: - blendState.enabled = true; + blendState.blendEnabled = true; break; } - blendState.resetFramebufferRead = true; - const GEBlendMode blendFuncEq = gstate.getBlendEq(); GEBlendSrcFactor blendFuncA = gstate.getBlendFuncA(); GEBlendDstFactor blendFuncB = gstate.getBlendFuncB(); @@ -1269,8 +1250,8 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, colorEq = eqLookupNoMinMax[blendFuncEq]; } - // Attempt to apply the logic op, if any. - ApplyLogicOp(glBlendFuncA, glBlendFuncB, colorEq); + // Attempt to apply simulated logic ops, if any and if needed. + SimulateLogicOpIfNeeded(glBlendFuncA, glBlendFuncB, colorEq); // The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't // do any blending in the alpha channel as that doesn't seem to happen on PSP. So, we attempt to @@ -1485,6 +1466,19 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) { } } +void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported) { + // Just do the non-shader case for now. + if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { + logicOpState.logicOpEnabled = gstate.isLogicOpEnabled() && logicSupported; + logicOpState.logicOp = gstate.isLogicOpEnabled() ? gstate.getLogicOp() : GE_LOGIC_COPY; + logicOpState.applyFramebufferRead = false; + } else { + logicOpState.logicOpEnabled = false; + logicOpState.logicOp = GE_LOGIC_COPY; + logicOpState.applyFramebufferRead = false; // true later? + } +} + static void ConvertStencilMask5551(GenericStencilFuncState &state) { state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00; } @@ -1532,3 +1526,18 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) { break; } } + +void GenericMaskState::Log() { + WARN_LOG(G3D, "Mask: %01X readfb=%d", uniformMask, channelMask, applyFramebufferRead); +} + +void GenericBlendState::Log() { + WARN_LOG(G3D, "Blend: hwenable=%d readfb=%d replblend=%d replalpha=%d", + blendEnabled, applyFramebufferRead, replaceBlend, (int)replaceAlphaWithStencil); +} + +void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) { + ConvertMaskState(maskState, shaderBitOpsSuppported); + ConvertLogicOpState(logicState, gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP), shaderBitOpsSuppported); + ConvertBlendState(blendState, maskState.applyFramebufferRead); +} diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index 1bdd7cce5b..b91311207d 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -39,13 +39,13 @@ enum ReplaceBlendType { // Full blend equation runs in shader. // We might have to make a copy of the framebuffer target to read from. - REPLACE_BLEND_COPY_FBO, + REPLACE_BLEND_READ_FRAMEBUFFER, // Color blend mode and color gets copied to alpha blend mode. REPLACE_BLEND_BLUE_TO_ALPHA, }; -enum LogicOpReplaceType { +enum SimulateLogicOpType { LOGICOPTYPE_NORMAL, LOGICOPTYPE_ONE, LOGICOPTYPE_INVERT, @@ -60,9 +60,10 @@ bool IsStencilTestOutputDisabled(); StencilValueType ReplaceAlphaWithStencilType(); ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend); -ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bufferFormat); +ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat); -LogicOpReplaceType ReplaceLogicOpType(); +// This is for the fallback path if real logic ops are not available. +SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded(); // Common representation, should be able to set this directly with any modern API. struct ViewportAndScissor { @@ -137,12 +138,18 @@ enum class BlendEq : uint8_t { COUNT }; +// Computed blend setup, including shader stuff. struct GenericBlendState { - bool enabled; - bool resetFramebufferRead; bool applyFramebufferRead; bool dirtyShaderBlendFixValues; + + // Shader generation state ReplaceAlphaType replaceAlphaWithStencil; + ReplaceBlendType replaceBlend; + SimulateLogicOpType simulateLogicOpType; + + // Resulting hardware blend state + bool blendEnabled; BlendFactor srcColor; BlendFactor dstColor; @@ -173,19 +180,31 @@ struct GenericBlendState { blendColor = 0xFFFFFF | ((uint32_t)alpha << 24); useBlendColor = true; } + + void Log(); }; -void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend, bool forceReplaceBlend); +void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend); void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState); struct GenericMaskState { bool applyFramebufferRead; uint32_t uniformMask; // For each bit, opposite to the PSP. - bool rgba[4]; // true = draw, false = don't draw this channel + + // The hardware channel masks, 1 bit per color component. From bit 0, order is RGBA like in all APIs! + uint8_t channelMask; + + void ConvertToShaderBlend() { + // If we have to do it in the shader, we simply pass through all channels but mask only in the shader instead. + // Some GPUs have minor penalties for masks that are not all-channels-on or all-channels-off. + channelMask = 0xF; + applyFramebufferRead = true; + } + + void Log(); }; -void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead); -bool IsColorWriteMaskComplex(bool allowFramebufferRead); +void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported); struct GenericStencilFuncState { bool enabled; @@ -197,9 +216,40 @@ struct GenericStencilFuncState { GEStencilOp zFail; GEStencilOp zPass; }; - void ConvertStencilFuncState(GenericStencilFuncState &stencilFuncState); +struct GenericLogicState { + // If set, logic op is applied in the shader INSTEAD of in hardware. + // In this case, simulateLogicOpType and all that should be off. + bool applyFramebufferRead; + + // Hardware + bool logicOpEnabled; + + // Hardware and shader generation + GELogicOp logicOp; + + void ConvertToShaderBlend() { + if (logicOp != GE_LOGIC_COPY) { + logicOpEnabled = false; + applyFramebufferRead = true; + // Same logicOp is kept. + } + } + + void Log(); +}; + +void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported); + +struct ComputedPipelineState { + GenericBlendState blendState; + GenericMaskState maskState; + GenericLogicState logicState; + + void Convert(bool shaderBitOpsSupported); +}; + // See issue #15898 inline bool SpongebobDepthInverseConditions(const GenericStencilFuncState &stencilState) { // Check that the depth/stencil state matches the conditions exactly. diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 00c39e4b71..53176ebcba 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -246,7 +246,7 @@ std::string FragmentShaderDesc(const FShaderID &id) { // Here we must take all the bits of the gstate that determine what the fragment shader will // look like, and concatenate them together into an ID. -void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) { +void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs) { FShaderID id; if (gstate.isModeClear()) { // We only need one clear shader, so let's ignore the rest of the bits. @@ -263,20 +263,17 @@ void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) { bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT; bool useShaderDepal = gstate_c.useShaderDepal; bool useSmoothedDepal = gstate_c.useSmoothedShaderDepal; - bool colorWriteMask = IsColorWriteMaskComplex(gstate_c.allowFramebufferRead); + bool colorWriteMask = pipelineState.maskState.applyFramebufferRead; - // Note how we here recompute some of the work already done in state mapping. - // Not ideal! At least we share the code. - ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowFramebufferRead, gstate_c.framebufFormat); - if (colorWriteMask) { - replaceBlend = REPLACE_BLEND_COPY_FBO; - } - ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend); + ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend; + ReplaceAlphaType stencilToAlpha = pipelineState.blendState.replaceAlphaWithStencil; + SimulateLogicOpType simulateLogicOpType = pipelineState.blendState.simulateLogicOpType; // All texfuncs except replace are the same for RGB as for RGBA with full alpha. // Note that checking this means that we must dirty the fragment shader ID whenever textureFullAlpha changes. - if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE) + if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE) { doTextureAlpha = false; + } if (gstate.isTextureMapEnabled()) { id.SetBit(FS_BIT_DO_TEXTURE); @@ -326,7 +323,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) { } // 2 bits. - id.SetBits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2, ReplaceLogicOpType()); + id.SetBits(FS_BIT_SIMULATE_LOGIC_OP_TYPE, 2, simulateLogicOpType); // If replaceBlend == REPLACE_BLEND_STANDARD (or REPLACE_BLEND_NO) nothing is done, so we kill these bits. if (replaceBlend == REPLACE_BLEND_BLUE_TO_ALPHA) { diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index a105af6746..914824d6bc 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -83,7 +83,7 @@ enum FShaderBit : uint8_t { FS_BIT_COLOR_DOUBLE = 23, FS_BIT_STENCIL_TO_ALPHA = 24, // 2 bits FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE = 26, // 4 bits (ReplaceAlphaType) - FS_BIT_REPLACE_LOGIC_OP_TYPE = 30, // 2 bits + FS_BIT_SIMULATE_LOGIC_OP_TYPE = 30, // 2 bits FS_BIT_REPLACE_BLEND = 32, // 3 bits (ReplaceBlendType) FS_BIT_BLENDEQ = 35, // 3 bits FS_BIT_BLENDFUNC_A = 38, // 4 bits @@ -226,11 +226,11 @@ namespace Draw { class Bugs; } - void ComputeVertexShaderID(VShaderID *id, uint32_t vertexType, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); // Generates a compact string that describes the shader. Useful in a list to get an overview // of the current flora of shaders. std::string VertexShaderDesc(const VShaderID &id); -void ComputeFragmentShaderID(FShaderID *id, const Draw::Bugs &bugs); +struct ComputedPipelineState; +void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs); std::string FragmentShaderDesc(const FShaderID &id); diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 0a8888321b..c3480e6fef 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -152,6 +152,13 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView if (!useBufferedRendering && g_display_rotation != DisplayRotation::ROTATE_0) { proj_through = proj_through * g_display_rot_matrix; } + + // Negative RT offsets come from split framebuffers (Killzone) + if (gstate_c.curRTOffsetX < 0 || gstate_c.curRTOffsetY < 0) { + proj_through.wx += 2.0f * (float)gstate_c.curRTOffsetX / (float)gstate_c.curRTWidth; + proj_through.wy += 2.0f * (float)gstate_c.curRTOffsetY / (float)gstate_c.curRTHeight; + } + CopyMatrix4x4(ub->proj_through, proj_through.getReadPtr()); ub->rotation = useBufferedRendering ? 0 : (float)g_display_rotation; } diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index b2ce179863..599c39c9db 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -21,6 +21,7 @@ #include "Common/Common.h" #include "Common/Data/Convert/ColorConv.h" +#include "Common/Data/Collections/TinySet.h" #include "Common/Profiler/Profiler.h" #include "Common/MemoryUtil.h" #include "Common/StringUtils.h" @@ -532,21 +533,17 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { def.format = texFormat; def.bufw = bufw; - std::vector candidates = GetFramebufferCandidates(def, 0); - if (candidates.size() > 0) { - int index = GetBestCandidateIndex(candidates); - if (index != -1) { - // If we had a texture entry here, let's get rid of it. - if (entryIter != cache_.end()) { - DeleteTexture(entryIter); - } - - const AttachCandidate &candidate = candidates[index]; - nextTexture_ = nullptr; - nextNeedsRebuild_ = false; - SetTextureFramebuffer(candidate); // sets curTexture3D - return nullptr; + AttachCandidate bestCandidate; + if (GetBestFramebufferCandidate(def, 0, &bestCandidate)) { + // If we had a texture entry here, let's get rid of it. + if (entryIter != cache_.end()) { + DeleteTexture(entryIter); } + + nextTexture_ = nullptr; + nextNeedsRebuild_ = false; + SetTextureFramebuffer(bestCandidate); // sets curTexture3D + return nullptr; } // Didn't match a framebuffer, keep going. @@ -617,55 +614,69 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { return entry; } -std::vector TextureCacheCommon::GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset) { +bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const { gpuStats.numFramebufferEvaluations++; - std::vector candidates; + TinySet candidates; const std::vector &framebuffers = framebufferManager_->Framebuffers(); for (VirtualFramebuffer *framebuffer : framebuffers) { FramebufferMatchInfo match{}; if (MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_COLOR, &match)) { - candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_COLOR, framebuffer->colorBindSeq }); + candidates.push_back(AttachCandidate{ framebuffer, match, RASTER_COLOR }); } match = {}; if (MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_DEPTH, &match)) { - candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_DEPTH, framebuffer->depthBindSeq }); + candidates.push_back(AttachCandidate{ framebuffer, match, RASTER_DEPTH }); } } - if (candidates.size() > 1) { + if (candidates.size() == 0) { + return false; + } else if (candidates.size() == 1) { + *bestCandidate = candidates[0]; + return true; + } + + if (Reporting::ShouldLogNTimes("multifbcandidate", 5)) { std::string cands; - for (auto &candidate : candidates) { - cands += candidate.ToString() + "\n"; + for (size_t i = 0; i < candidates.size(); i++) { + cands += candidates[i].ToString() + "\n"; } - WARN_LOG_REPORT_ONCE(multifbcandidate, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", + WARN_LOG(G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s", (int)candidates.size(), entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format), cands.c_str() ); } - return candidates; -} - -int TextureCacheCommon::GetBestCandidateIndex(const std::vector &candidates) { - _dbg_assert_(!candidates.empty()); - - if (candidates.size() == 1) { - return 0; - } - // OK, multiple possible candidates. Will need to figure out which one is the most relevant. int bestRelevancy = -1; - int bestIndex = -1; + size_t bestIndex = -1; + + bool kzCompat = PSP_CoreParameter().compat.flags().SplitFramebufferMargin; // We simply use the sequence counter as relevancy nowadays. - for (int i = 0; i < (int)candidates.size(); i++) { + for (size_t i = 0; i < candidates.size(); i++) { const AttachCandidate &candidate = candidates[i]; - int relevancy = candidate.seqCount; + int relevancy = candidate.channel == RASTER_COLOR ? candidate.fb->colorBindSeq : candidate.fb->depthBindSeq; + + // Add a small negative penalty if the texture is currently bound as a framebuffer, and offset is not zero. + // Should avoid problems when pingponging two nearby buffers, like in Wipeout Pure in #15927. + if (candidate.channel == RASTER_COLOR && + (candidate.match.yOffset != 0 || candidate.match.xOffset != 0) && + (candidate.fb->fb_address & 0x1FFFFF) == (gstate.getFrameBufAddress() & 0x1FFFFF)) { + relevancy -= 2; + } + + // Avoid binding as texture the framebuffer we're rendering to. + // In Killzone, we split the framebuffer but the matching algorithm can still pick the wrong one, + // which this avoids completely. + if (kzCompat && candidate.fb == framebufferManager_->GetCurrentRenderVFB()) { + continue; + } if (relevancy > bestRelevancy) { bestRelevancy = relevancy; @@ -673,7 +684,12 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector } } - return bestIndex; + if (bestIndex != -1) { + *bestCandidate = candidates[bestIndex]; + return true; + } else { + return false; + } } // Removes old textures. @@ -850,7 +866,7 @@ bool TextureCacheCommon::MatchFramebuffer( uint32_t fb_stride = channel == RASTER_DEPTH ? framebuffer->z_stride : framebuffer->fb_stride; GEBufferFormat fb_format = channel == RASTER_DEPTH ? GE_FORMAT_DEPTH16 : framebuffer->fb_format; - if (channel == RASTER_DEPTH && framebuffer->z_address == framebuffer->fb_address) { + if (channel == RASTER_DEPTH && (framebuffer->z_address == framebuffer->fb_address || framebuffer->z_address == 0)) { // Try to avoid silly matches to somewhat malformed buffers. return false; } @@ -920,15 +936,23 @@ bool TextureCacheCommon::MatchFramebuffer( (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT32) || (fb_format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16); - const u32 bitOffset = (texaddr - addr) * 8; + const int bitOffset = (texaddr - addr) * 8; if (bitOffset != 0) { - const u32 pixelOffset = bitOffset / std::max(1U, (u32)textureBitsPerPixel[entry.format]); + const int pixelOffset = bitOffset / (int)std::max(1U, (u32)textureBitsPerPixel[entry.format]); - matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / entry.bufw; - matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % entry.bufw; + if (pixelOffset > 0) { + matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / (int)entry.bufw; + matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % (int)entry.bufw; + } else if (pixelOffset < 0) { + // We don't support negative Y offsets, and negative X offsets are only for the Killzone workaround. + if (pixelOffset < -(int)entry.bufw || !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { + return false; + } + matchInfo->xOffset = entry.bufw == 0 ? 0 : -(-pixelOffset % (int)entry.bufw); + } } - if (matchInfo->yOffset + minSubareaHeight >= framebuffer->height) { + if (matchInfo->yOffset > 0 && matchInfo->yOffset + minSubareaHeight >= framebuffer->height) { // Can't be inside the framebuffer. return false; } @@ -1082,15 +1106,13 @@ bool TextureCacheCommon::SetOffsetTexture(u32 yOffset) { def.bufw = GetTextureBufw(0, texaddr, fmt); def.dim = gstate.getTextureDimension(0); - std::vector candidates = GetFramebufferCandidates(def, texaddrOffset); - if (candidates.size() > 0) { - int index = GetBestCandidateIndex(candidates); - if (index != -1) { - SetTextureFramebuffer(candidates[index]); - return true; - } + AttachCandidate bestCandidate; + if (GetBestFramebufferCandidate(def, texaddrOffset, &bestCandidate)) { + SetTextureFramebuffer(bestCandidate); + return true; + } else { + return false; } - return false; } void TextureCacheCommon::NotifyConfigChanged() { @@ -1164,7 +1186,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { // It's possible for a game to (successfully) access outside valid memory. u32 bytes = Memory::ValidSize(clutAddr, loadBytes); - if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bDisableSlowFramebufEffects) { + if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bBlockTransferGPU) { framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes); Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes); if (bytes < loadBytes) { @@ -1903,7 +1925,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer bool smoothedDepal = false; u32 depthUpperBits = 0; - if (need_depalettize && !g_Config.bDisableSlowFramebufEffects) { + if (need_depalettize) { clutTexture = textureShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBufRaw_); smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->fb_format, clutTexture.rampLength); @@ -2227,7 +2249,7 @@ void TextureCacheCommon::ClearNextFrame() { std::string AttachCandidate::ToString() const { return StringFromFormat("[%s seq:%d C:%08x/%d(%s) Z:%08x/%d X:%d Y:%d reint: %s]", this->channel == RASTER_COLOR ? "COLOR" : "DEPTH", - this->seqCount, + this->channel == RASTER_COLOR ? this->fb->colorBindSeq : this->fb->depthBindSeq, this->fb->fb_address, this->fb->fb_stride, GeBufferFormatToString(this->fb->fb_format), this->fb->z_address, this->fb->z_stride, this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false"); @@ -2319,16 +2341,10 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.w = gstate.getTextureWidth(0); plan.h = gstate.getTextureHeight(0); - plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); - if (plan.replaced->Valid()) { - // We're replacing, so we won't scale. - plan.scaleFactor = 1; - plan.levelsToLoad = plan.replaced->NumLevels(); - plan.badMipSizes = false; - } + bool isPPGETexture = entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd(); // Don't scale the PPGe texture. - if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd()) { + if (isPPGETexture) { plan.scaleFactor = 1; } @@ -2364,6 +2380,40 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } } + if (isPPGETexture) { + plan.replaced = &replacer_.FindNone(); + plan.replaceValid = false; + } else { + plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); + plan.replaceValid = plan.replaced->Valid(); + } + + plan.saveTexture = false; + if (plan.replaceValid) { + // We're replacing, so we won't scale. + plan.scaleFactor = 1; + plan.levelsToLoad = plan.replaced->NumLevels(); + plan.levelsToCreate = std::min(plan.levelsToLoad, plan.levelsToCreate); + plan.badMipSizes = false; + // But, we still need to create the texture at a larger size. + plan.replaced->GetSize(0, plan.createW, plan.createH); + } else { + if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { + ReplacedTextureDecodeInfo replacedInfo; + // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? + replacedInfo.cachekey = entry->CacheKey(); + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = entry->addr; + replacedInfo.isVideo = plan.isVideo; + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = plan.scaleFactor; + replacedInfo.fmt = Draw::DataFormat::R8G8B8A8_UNORM; + plan.saveTexture = replacer_.WillSave(replacedInfo); + } + plan.createW = plan.w * plan.scaleFactor; + plan.createH = plan.h * plan.scaleFactor; + } + // Always load base level texture here plan.baseLevelSrc = 0; if (IsFakeMipmapChange()) { @@ -2376,7 +2426,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt if (plan.isVideo || plan.depth != 1) { plan.maxPossibleLevels = 1; } else { - plan.maxPossibleLevels = log2i(std::min(plan.w * plan.scaleFactor, plan.h * plan.scaleFactor)) + 1; + plan.maxPossibleLevels = log2i(std::min(plan.createW, plan.createH)) + 1; } if (plan.levelsToCreate == 1) { @@ -2417,7 +2467,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i decPitch = stride; } - bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1; + bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM; CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, reverseColors, expand32); entry.SetAlphaStatus(alphaResult, srcLevel); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 534a8dbbdb..7cb79d44ce 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -93,12 +93,11 @@ class VulkanTexture; // Enough information about a texture to match it to framebuffers. struct TextureDefinition { u32 addr; + u16 bufw; + u16 dim; GETextureFormat format; - u32 dim; - u32 bufw; }; - // TODO: Shrink this struct. There is some fluff. // NOTE: These only handle textures loaded directly from PSP memory contents. @@ -209,18 +208,16 @@ typedef std::map> TexCache; #endif struct FramebufferMatchInfo { - u32 xOffset; - u32 yOffset; + int16_t xOffset; + int16_t yOffset; bool reinterpret; GEBufferFormat reinterpretTo; }; struct AttachCandidate { - FramebufferMatchInfo match; - TextureDefinition entry; VirtualFramebuffer *fb; + FramebufferMatchInfo match; RasterChannel channel; - int seqCount; std::string ToString() const; }; @@ -264,11 +261,31 @@ struct BuildTexturePlan { int w; int h; + // Scaled (or replaced) size of the 0-mip of the final texture. + int createW; + int createH; + // Used for 3D textures only. If not a 3D texture, will be 1. int depth; // The replacement for the texture. ReplacedTexture *replaced; + // Need to only check once since it can change during the load! + bool replaceValid; + bool saveTexture; + + void GetMipSize(int level, int *w, int *h) const { + if (replaceValid) { + replaced->GetSize(level, *w, *h); + } else if (depth == 1) { + *w = createW >> level; + *h = createH >> level; + } else { + // 3D texture, we look for layers instead of levels. + *w = createW; + *h = createH; + } + } }; class TextureCacheCommon { @@ -362,8 +379,7 @@ protected: bool MatchFramebuffer(const TextureDefinition &entry, VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel, FramebufferMatchInfo *matchInfo) const; - std::vector GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset); - int GetBestCandidateIndex(const std::vector &candidates); + bool GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const; void SetTextureFramebuffer(const AttachCandidate &candidate); diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index 52cefc0d71..c2f6ae46fd 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -136,7 +136,6 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through}, {&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through}, - {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat}, {&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8}, {&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16}, @@ -882,7 +881,7 @@ void VertexDecoderJitCache::Jit_PosS8Through() { // TODO: SIMD LDRSB(tempReg1, srcReg, dec_->posoff); LDRSB(tempReg2, srcReg, dec_->posoff + 1); - LDRSB(tempReg3, srcReg, dec_->posoff + 2); // signed? + LDRB(tempReg3, srcReg, dec_->posoff + 2); static const ARMReg tr[3] = { tempReg1, tempReg2, tempReg3 }; static const ARMReg fr[3] = { fpScratchReg, fpScratchReg2, fpScratchReg3 }; ADD(scratchReg, dstReg, dec_->decFmt.posoff); diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index 305ba3ed90..32cc5bc291 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -113,7 +113,7 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through}, {&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through}, - {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat}, + {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloatThrough}, {&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8}, {&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16}, @@ -670,7 +670,7 @@ void VertexDecoderJitCache::Jit_PosFloat() { void VertexDecoderJitCache::Jit_PosS8Through() { LDRSB(INDEX_UNSIGNED, tempReg1, srcReg, dec_->posoff); LDRSB(INDEX_UNSIGNED, tempReg2, srcReg, dec_->posoff + 1); - LDRSB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->posoff + 2); // signed? + LDRB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->posoff + 2); fp.SCVTF(fpScratchReg, tempReg1); fp.SCVTF(fpScratchReg2, tempReg2); fp.SCVTF(fpScratchReg3, tempReg3); @@ -691,6 +691,25 @@ void VertexDecoderJitCache::Jit_PosS16Through() { STR(INDEX_UNSIGNED, src[1], dstReg, dec_->decFmt.posoff + 8); } +void VertexDecoderJitCache::Jit_PosFloatThrough() { + // Instead of just copying 12 bytes, we copy 8 and clamp Z. + if ((dec_->posoff & 7) == 0 && (dec_->decFmt.posoff & 7) == 0) { + LDR(INDEX_UNSIGNED, EncodeRegTo64(tempReg1), srcReg, dec_->posoff); + STR(INDEX_UNSIGNED, EncodeRegTo64(tempReg1), dstReg, dec_->decFmt.posoff); + } else { + LDP(INDEX_SIGNED, tempReg1, tempReg2, srcReg, dec_->posoff); + STP(INDEX_SIGNED, tempReg1, tempReg2, dstReg, dec_->decFmt.posoff); + } + + fp.LDUR(32, neonScratchRegD, srcReg, dec_->posoff + 8); + fp.FCVTZU(32, neonScratchRegD, neonScratchRegD); + // Narrow to 16 bit, saturating meanwhile. + fp.UQXTN(16, neonScratchRegD, neonScratchRegD); + fp.UXTL(16, neonScratchRegD, neonScratchRegD); + fp.UCVTF(32, neonScratchRegD, neonScratchRegD); + fp.STUR(32, neonScratchRegD, dstReg, dec_->decFmt.posoff + 8); +} + void VertexDecoderJitCache::Jit_NormalS8() { LDURH(tempReg1, srcReg, dec_->nrmoff); LDRB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->nrmoff + 2); diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index fd4365d34e..6ac9806c01 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -176,9 +176,6 @@ void PrintDecodedVertex(VertexReader &vtx) { printf("P: %f %f %f\n", pos[0], pos[1], pos[2]); } -VertexDecoder::VertexDecoder() : decoded_(nullptr), ptr_(nullptr), jitted_(0), jittedSize_(0) { -} - void VertexDecoder::Step_WeightsU8() const { u8 *wt = (u8 *)(decoded_ + decFmt.w0off); @@ -779,10 +776,11 @@ void VertexDecoder::Step_PosFloatSkin() const void VertexDecoder::Step_PosS8Through() const { float *v = (float *)(decoded_ + decFmt.posoff); - const s8 *sv = (const s8*)(ptr_ + posoff); + const s8 *sv = (const s8 *)(ptr_ + posoff); + const u8 *uv = (const u8 *)(ptr_ + posoff); v[0] = sv[0]; v[1] = sv[1]; - v[2] = sv[2]; + v[2] = uv[2]; } void VertexDecoder::Step_PosS16Through() const @@ -797,9 +795,10 @@ void VertexDecoder::Step_PosS16Through() const void VertexDecoder::Step_PosFloatThrough() const { - u8 *v = (u8 *)(decoded_ + decFmt.posoff); - const u8 *fv = (const u8 *)(ptr_ + posoff); - memcpy(v, fv, 12); + float *v = (float *)(decoded_ + decFmt.posoff); + const float *fv = (const float *)(ptr_ + posoff); + memcpy(v, fv, 8); + v[2] = fv[2] > 65535.0f ? 65535.0f : (fv[2] < 0.0f ? 0.0f : fv[2]); } void VertexDecoder::Step_PosS8Morph() const @@ -1265,6 +1264,9 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, ERROR_LOG_REPORT(G3D, "Vertices without position found: (%08x) %s", fmt_, temp); } + _assert_msg_(decFmt.posfmt == DEC_FLOAT_3, "Reader only supports float pos"); + _assert_msg_(decFmt.uvfmt == DEC_FLOAT_2 || decFmt.uvfmt == DEC_NONE, "Reader only supports float UV"); + // Attempt to JIT as well. But only do that if the main CPU JIT is enabled, in order to aid // debugging attempts - if the main JIT doesn't work, this one won't do any better, probably. if (jitCache && g_Config.bVertexDecoderJit && g_Config.iCpuCore == (int)CPUCore::JIT) { diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 33725facb3..3d6377a86c 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -121,103 +121,22 @@ public: VertexReader(u8 *base, const DecVtxFormat &decFmt, int vtype) : base_(base), data_(base), decFmt_(decFmt), vtype_(vtype) {} void ReadPos(float pos[3]) const { - switch (decFmt_.posfmt) { - case DEC_FLOAT_3: - { - const float *f = (const float *)(data_ + decFmt_.posoff); - memcpy(pos, f, 12); - if (isThrough()) { - // Integer value passed in a float. Clamped to 0, 65535. - const float z = (int)pos[2] * (1.0f / 65535.0f); - pos[2] = z > 1.0f ? 1.0f : (z < 0.0f ? 0.0f : z); - } - } - break; - case DEC_S16_3: - { - // X and Y are signed 16 bit, Z is unsigned 16 bit - const s16 *s = (const s16 *)(data_ + decFmt_.posoff); - const u16 *u = (const u16 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = s[i]; - pos[2] = u[2] * (1.0f / 65535.0f); - } else { - for (int i = 0; i < 3; i++) - pos[i] = s[i] * (1.0f / 32768.0f); - } - } - break; - case DEC_S8_3: - { - // X and Y are signed 8 bit, Z is unsigned 8 bit - const s8 *b = (const s8 *)(data_ + decFmt_.posoff); - const u8 *u = (const u8 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = b[i]; - pos[2] = u[2] * (1.0f / 255.0f); - } else { - for (int i = 0; i < 3; i++) - pos[i] = b[i] * (1.0f / 128.0f); - } - } - break; - default: - ERROR_LOG_REPORT_ONCE(fmtpos, G3D, "Reader: Unsupported Pos Format %d", decFmt_.posfmt); - memset(pos, 0, sizeof(float) * 3); - break; + // Only DEC_FLOAT_3 is supported. + const float *f = (const float *)(data_ + decFmt_.posoff); + pos[0] = f[0]; + pos[1] = f[1]; + if (!isThrough()) { + pos[2] = f[2]; + } else { + // Integer value passed in a float. Clamped to 0, 65535. + pos[2] = (int)f[2] * (1.0f / 65535.0f); } } void ReadPosThroughZ16(float pos[3]) const { - switch (decFmt_.posfmt) { - case DEC_FLOAT_3: - { - const float *f = (const float *)(data_ + decFmt_.posoff); - memcpy(pos, f, 12); - if (isThrough()) { - // Integer value passed in a float. Clamped to 0, 65535. - const float z = (int)pos[2]; - pos[2] = z > 65535.0f ? 65535.0f : (z < 0.0f ? 0.0f : z); - } - } - break; - case DEC_S16_3: - { - // X and Y are signed 16 bit, Z is unsigned 16 bit - const s16 *s = (const s16 *)(data_ + decFmt_.posoff); - const u16 *u = (const u16 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = s[i]; - pos[2] = u[2]; - } else { - for (int i = 0; i < 3; i++) - pos[i] = s[i] * (1.0f / 32768.0f); - } - } - break; - case DEC_S8_3: - { - // X and Y are signed 8 bit, Z is unsigned 8 bit - const s8 *b = (const s8 *)(data_ + decFmt_.posoff); - const u8 *u = (const u8 *)(data_ + decFmt_.posoff); - if (isThrough()) { - for (int i = 0; i < 2; i++) - pos[i] = b[i]; - pos[2] = u[2]; - } else { - for (int i = 0; i < 3; i++) - pos[i] = b[i] * (1.0f / 128.0f); - } - } - break; - default: - ERROR_LOG_REPORT_ONCE(fmtz16, G3D, "Reader: Unsupported Pos Format %d", decFmt_.posfmt); - memset(pos, 0, sizeof(float) * 3); - break; - } + // Only DEC_FLOAT_3 is supported. + const float *f = (const float *)(data_ + decFmt_.posoff); + memcpy(pos, f, 12); } void ReadNrm(float nrm[3]) const { @@ -252,36 +171,10 @@ public: } void ReadUV(float uv[2]) const { - switch (decFmt_.uvfmt) { - case DEC_U8_2: - { - const u8 *b = (const u8 *)(data_ + decFmt_.uvoff); - uv[0] = b[0] * (1.f / 128.f); - uv[1] = b[1] * (1.f / 128.f); - } - break; - - case DEC_U16_2: - { - const u16 *s = (const u16 *)(data_ + decFmt_.uvoff); - uv[0] = s[0] * (1.f / 32768.f); - uv[1] = s[1] * (1.f / 32768.f); - } - break; - - case DEC_FLOAT_2: - { - const float *f = (const float *)(data_ + decFmt_.uvoff); - uv[0] = f[0]; - uv[1] = f[1]; - } - break; - - default: - ERROR_LOG_REPORT_ONCE(fmtuv, G3D, "Reader: Unsupported UV Format %d", decFmt_.uvfmt); - memset(uv, 0, sizeof(float) * 2); - break; - } + // Only DEC_FLOAT_2 is supported. + const float *f = (const float *)(data_ + decFmt_.uvoff); + uv[0] = f[0]; + uv[1] = f[1]; } void ReadColor0(float color[4]) const { @@ -443,8 +336,6 @@ struct VertexDecoderOptions { class VertexDecoder { public: - VertexDecoder(); - // A jit cache is not mandatory. void SetVertexType(u32 vtype, const VertexDecoderOptions &options, VertexDecoderJitCache *jitCache = nullptr); @@ -549,11 +440,10 @@ public: int ToString(char *output) const; // Mutable decoder state - mutable u8 *decoded_; - mutable const u8 *ptr_; - - JittedVertexDecoder jitted_; - int32_t jittedSize_; + mutable u8 *decoded_ = nullptr; + mutable const u8 *ptr_ = nullptr; + JittedVertexDecoder jitted_ = 0; + int32_t jittedSize_ = 0; // "Immutable" state, set at startup @@ -666,6 +556,7 @@ public: void Jit_PosFloat(); void Jit_PosS8Through(); void Jit_PosS16Through(); + void Jit_PosFloatThrough(); void Jit_PosS8Skin(); void Jit_PosS16Skin(); diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index d16c3fd047..611bf56c21 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -138,7 +138,7 @@ static const JitLookup jitLookup[] = { {&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through}, {&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through}, - {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat}, + {&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloatThrough}, {&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8}, {&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16}, @@ -1348,7 +1348,10 @@ void VertexDecoderJitCache::Jit_PosS8Through() { DEBUG_LOG_REPORT_ONCE(vertexS8Through, G3D, "Using S8 positions in throughmode"); // SIMD doesn't really matter since this isn't useful on hardware. for (int i = 0; i < 3; i++) { - MOVSX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); + if (i == 2) + MOVZX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); + else + MOVSX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i)); CVTSI2SS(fpScratchReg, R(tempReg1)); MOVSS(MDisp(dstReg, dec_->decFmt.posoff + i * 4), fpScratchReg); } @@ -1377,6 +1380,29 @@ void VertexDecoderJitCache::Jit_PosS16Through() { } } +void VertexDecoderJitCache::Jit_PosFloatThrough() { + PXOR(fpScratchReg2, R(fpScratchReg2)); + if (cpu_info.Mode64bit) { + MOV(64, R(tempReg1), MDisp(srcReg, dec_->posoff)); + MOVSS(fpScratchReg, MDisp(srcReg, dec_->posoff + 8)); + MOV(64, MDisp(dstReg, dec_->decFmt.posoff), R(tempReg1)); + } else { + MOV(32, R(tempReg1), MDisp(srcReg, dec_->posoff)); + MOV(32, R(tempReg2), MDisp(srcReg, dec_->posoff + 4)); + MOVSS(fpScratchReg, MDisp(srcReg, dec_->posoff + 8)); + MOV(32, MDisp(dstReg, dec_->decFmt.posoff), R(tempReg1)); + MOV(32, MDisp(dstReg, dec_->decFmt.posoff + 4), R(tempReg2)); + } + + CVTTPS2DQ(fpScratchReg, R(fpScratchReg)); + // Use pack to saturate to 0,65535. + PACKUSDW(fpScratchReg, R(fpScratchReg)); + PUNPCKLWD(fpScratchReg, R(fpScratchReg2)); + CVTDQ2PS(fpScratchReg, R(fpScratchReg)); + + MOVSS(MDisp(dstReg, dec_->decFmt.posoff + 8), fpScratchReg); +} + void VertexDecoderJitCache::Jit_PosS8() { Jit_AnyS8ToFloat(dec_->posoff); MOVUPS(MDisp(dstReg, dec_->decFmt.posoff), XMM3); diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 1822fc5256..bc3c67da1d 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -538,7 +538,7 @@ rotateVBO: D3D11VertexShader *vshader; D3D11FragmentShader *fshader; - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform, useHWTessellation_, decOptions_.expandAllWeightsToFloat); + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, useHWTransform, useHWTessellation_, decOptions_.expandAllWeightsToFloat); ID3D11InputLayout *inputLayout = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType()); context_->PSSetShader(fshader->GetShader(), nullptr, 0); context_->VSSetShader(vshader->GetShader(), nullptr, 0); @@ -648,7 +648,7 @@ rotateVBO: if (result.action == SW_DRAW_PRIMITIVES) { D3D11VertexShader *vshader; D3D11FragmentShader *fshader; - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat); + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); context_->PSSetShader(fshader->GetShader(), nullptr, 0); context_->VSSetShader(vshader->GetShader(), nullptr, 0); shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering()); diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index acfbc01be3..ca7fb04dbb 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -159,7 +159,6 @@ private: void ApplyDrawState(int prim); void ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRef); - void ResetFramebufferRead(); ID3D11InputLayout *SetupDecFmtForDraw(D3D11VertexShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt); diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index c141c1ed5b..34c8980ad0 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -177,7 +177,7 @@ void ShaderManagerD3D11::BindUniforms() { context_->PSSetConstantBuffers(0, 1, ps_cbs); } -void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { +void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { VShaderID VSID; FShaderID FSID; @@ -190,7 +190,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader ** if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/D3D11/ShaderManagerD3D11.h b/GPU/D3D11/ShaderManagerD3D11.h index df8f7e4809..0f7a297965 100644 --- a/GPU/D3D11/ShaderManagerD3D11.h +++ b/GPU/D3D11/ShaderManagerD3D11.h @@ -85,7 +85,7 @@ public: ShaderManagerD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context, D3D_FEATURE_LEVEL featureLevel); ~ShaderManagerD3D11(); - void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); + void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); void ClearShaders(); void DirtyLastShader() override; diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 561ea414b3..e525b3f765 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -122,12 +122,6 @@ static const D3D11_LOGIC_OP logicOps[] = { D3D11_LOGIC_OP_SET, }; -void DrawEngineD3D11::ResetFramebufferRead() { - if (fboTexBound_) { - fboTexBound_ = false; - } -} - class FramebufferManagerD3D11; class ShaderManagerD3D11; @@ -142,7 +136,6 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); // Blend if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); if (gstate.isModeClear()) { keys_.blend.value = 0; // full wipe keys_.blend.blendEnable = false; @@ -154,23 +147,21 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } else { keys_.blend.value = 0; - GenericMaskState maskState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - // Set blend - unless we need to do it in the shader. - GenericBlendState blendState; - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; + // We ignore the logicState on D3D since there's no support, the emulation of it is blend-and-shader only. if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { - ApplyFramebufferRead(&fboTexNeedsBind_); + bool fboTexNeedsBind = false; + ApplyFramebufferRead(&fboTexNeedsBind); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); - if (fboTexNeedsBind_) { + if (fboTexNeedsBind) { framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // No sampler required, we do a plain Load in the pixel shader. fboTexBound_ = true; - fboTexNeedsBind_ = false; framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState"); // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. @@ -180,13 +171,15 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); - dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; - gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } else { + if (fboTexBound_) { + fboTexBound_ = false; + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } } - if (blendState.enabled) { + if (blendState.blendEnabled) { keys_.blend.blendEnable = true; keys_.blend.logicOpEnable = false; keys_.blend.blendOpColor = d3d11BlendEqLookup[(size_t)blendState.eqColor]; @@ -219,7 +212,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } } - keys_.blend.colorWriteMask = (maskState.rgba[0] ? 1 : 0) | (maskState.rgba[1] ? 2 : 0) | (maskState.rgba[2] ? 4 : 0) | (maskState.rgba[3] ? 8 : 0); + keys_.blend.colorWriteMask = maskState.channelMask; } } diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 7fee999b6f..5cc42adee6 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -276,26 +276,27 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { return; } - int tw = plan.w; - int th = plan.h; - DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) { + if (plan.replaceValid) { dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc)); - } else if (plan.scaleFactor > 1) { - tw *= plan.scaleFactor; - th *= plan.scaleFactor; + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; } - // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. - int levels;; + int levels; ID3D11ShaderResourceView *view; ID3D11Resource *texture = DxTex(entry); _assert_(texture == nullptr); + int tw; + int th; + plan.GetMipSize(0, &tw, &th); + if (plan.depth == 1) { + // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. + levels = std::min(plan.levelsToCreate, plan.levelsToLoad); + ID3D11Texture2D *tex; D3D11_TEXTURE2D_DESC desc{}; desc.CPUAccessFlags = 0; @@ -305,13 +306,11 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { desc.Width = tw; desc.Height = th; desc.Format = dstFmt; - desc.MipLevels = plan.levelsToCreate; + desc.MipLevels = levels; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &tex)); texture = tex; - - levels = std::min(plan.levelsToCreate, plan.levelsToLoad); } else { ID3D11Texture3D *tex; D3D11_TEXTURE3D_DESC desc{}; @@ -338,45 +337,43 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { for (int i = 0; i < levels; i++) { int srcLevel = (i == 0) ? plan.baseLevelSrc : i; - int w = gstate.getTextureWidth(srcLevel); - int h = gstate.getTextureHeight(srcLevel); + int mipWidth; + int mipHeight; + plan.GetMipSize(i, &mipWidth, &mipHeight); u8 *data = nullptr; int stride = 0; + int bpp = 0; // For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :( // NOTE: Could reuse it between levels or textures! - if (plan.replaced->GetSize(srcLevel, w, h)) { - int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); - stride = w * bpp; - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + if (plan.replaceValid) { + bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); } else { if (plan.scaleFactor > 1) { - data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16); - stride = w * plan.scaleFactor * 4; + bpp = 4; } else { - int bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2; - - stride = std::max(w * bpp, 16); - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2; } } + stride = std::max(mipWidth * bpp, 16); + data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16); + if (!data) { - ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h); + ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight); return; } LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, false); - if (plan.depth == 1) { context_->UpdateSubresource(texture, i, nullptr, data, stride, 0); } else { D3D11_BOX box{}; box.front = i; box.back = i + 1; - box.right = w * plan.scaleFactor; - box.bottom = h * plan.scaleFactor; + box.right = mipWidth; + box.bottom = mipHeight; context_->UpdateSubresource(texture, 0, &box, data, stride, 0); } FreeAlignedMemory(data); @@ -393,7 +390,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { entry->status &= ~TexCacheEntry::STATUS_NO_MIPS; } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 1ab4da1a63..e05df6c98c 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -522,7 +522,7 @@ rotateVBO: ApplyDrawState(prim); ApplyDrawStateLate(); - VSShader *vshader = shaderManager_->ApplyShader(true, useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat); + VSShader *vshader = shaderManager_->ApplyShader(true, useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat, pipelineState_); IDirect3DVertexDeclaration9 *pHardwareVertexDecl = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType()); if (pHardwareVertexDecl) { @@ -613,7 +613,7 @@ rotateVBO: ApplyDrawStateLate(); - VSShader *vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat); + VSShader *vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat, pipelineState_); if (result.action == SW_DRAW_PRIMITIVES) { if (result.setStencil) { diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index 616620a412..a0ee23e60d 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -148,7 +148,6 @@ private: void ApplyDrawState(int prim); void ApplyDrawStateLate(); - void ResetFramebufferRead(); IDirect3DVertexDeclaration9 *SetupDecFmtForDraw(VSShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt); @@ -172,4 +171,6 @@ private: TessellationDataTransferDX9 *tessDataTransferDX9; int lastRenderStepId_ = -1; + + bool fboTexNeedsBind_ = false; }; diff --git a/GPU/Directx9/FramebufferManagerDX9.cpp b/GPU/Directx9/FramebufferManagerDX9.cpp index 8de6f438a9..f828336edd 100644 --- a/GPU/Directx9/FramebufferManagerDX9.cpp +++ b/GPU/Directx9/FramebufferManagerDX9.cpp @@ -133,9 +133,10 @@ } } - void FramebufferManagerDX9::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferDirectx9_: vfb->fbo == 0"); + void FramebufferManagerDX9::PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) { + if (channel != RASTER_COLOR) { + // Unsupported + WARN_LOG_ONCE(d3ddepthreadback, G3D, "Not yet supporting depth readbacks on DX9"); return; } @@ -175,11 +176,6 @@ } void FramebufferManagerDX9::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0"); - return; - } - // We always read the depth buffer in 24_8 format. const u32 z_address = vfb->z_address; diff --git a/GPU/Directx9/FramebufferManagerDX9.h b/GPU/Directx9/FramebufferManagerDX9.h index 4835ded4d1..0f4f7d7ce1 100644 --- a/GPU/Directx9/FramebufferManagerDX9.h +++ b/GPU/Directx9/FramebufferManagerDX9.h @@ -51,7 +51,7 @@ protected: void DecimateFBOs() override; private: - void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; + void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer); diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 7cae12bb53..1700206a02 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -544,7 +544,7 @@ void ShaderManagerDX9::DirtyLastShader() { // disables vertex arrays lastPShader_ = nullptr; } -VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat) { +VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, const ComputedPipelineState &pipelineState) { VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { gstate_c.Clean(DIRTY_VERTEXSHADER_STATE); @@ -556,7 +556,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/Directx9/ShaderManagerDX9.h b/GPU/Directx9/ShaderManagerDX9.h index 04cdf79544..9c7404d856 100644 --- a/GPU/Directx9/ShaderManagerDX9.h +++ b/GPU/Directx9/ShaderManagerDX9.h @@ -78,7 +78,7 @@ public: ~ShaderManagerDX9(); void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected - VSShader *ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat); + VSShader *ApplyShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, const ComputedPipelineState &pipelineState); void DirtyShader(); void DirtyLastShader() override; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 1e4d1d348c..7a324219a5 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -88,10 +88,6 @@ static const D3DSTENCILOP stencilOps[] = { D3DSTENCILOP_KEEP, // reserved }; -inline void DrawEngineDX9::ResetFramebufferRead() { - fboTexBound_ = false; -} - void DrawEngineDX9::ApplyDrawState(int prim) { if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { // nothing to do @@ -119,8 +115,6 @@ void DrawEngineDX9::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - // Unfortunately, this isn't implemented on DX9 yet. - gstate_c.SetAllowFramebufferRead(false); if (gstate.isModeClear()) { dxstate.blend.disable(); // Color Mask @@ -133,37 +127,39 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } dxstate.colorMask.set(mask); } else { - GenericMaskState maskState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - // Set blend - unless we need to do it in the shader. - GenericBlendState blendState; - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; + // We ignore the logicState on D3D since there's no support, the emulation of it is blend-and-shader only. if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { - ApplyFramebufferRead(&fboTexNeedsBind_); + bool fboTexNeedsBind = false; + ApplyFramebufferRead(&fboTexNeedsBind); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); - if (fboTexNeedsBind_) { + if (fboTexNeedsBind) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); fboTexBound_ = true; - fboTexNeedsBind_ = false; dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE; gstate_c.Dirty(DIRTY_BLEND_STATE); } dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); + } else { + if (fboTexBound_) { + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + fboTexBound_ = false; + } } - if (blendState.enabled) { + if (blendState.blendEnabled) { dxstate.blend.enable(); dxstate.blendSeparate.enable(); dxstate.blendEquation.set(dxBlendEqLookup[(size_t)blendState.eqColor], dxBlendEqLookup[(size_t)blendState.eqAlpha]); @@ -181,12 +177,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { dxstate.blend.disable(); } - u32 mask = 0; - for (int i = 0; i < 4; i++) { - if (maskState.rgba[i]) - mask |= 1 << i; - } - dxstate.colorMask.set(mask); + dxstate.colorMask.set(maskState.channelMask); } } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 9de670732d..289d52d3b7 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -238,27 +238,28 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { return; } - int tw = plan.w; - int th = plan.h; - D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) { + if (plan.replaceValid) { dstFmt = ToD3D9Format(plan.replaced->Format(plan.baseLevelSrc)); - } else if (plan.scaleFactor > 1) { - tw *= plan.scaleFactor; - th *= plan.scaleFactor; + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = D3DFMT_A8R8G8B8; } - // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. - int levels = std::min(plan.levelsToCreate, plan.levelsToLoad); + int levels; LPDIRECT3DBASETEXTURE9 &texture = DxTex(entry); D3DPOOL pool = D3DPOOL_DEFAULT; int usage = D3DUSAGE_DYNAMIC; + int tw; + int th; + plan.GetMipSize(0, &tw, &th); + HRESULT hr; if (plan.depth == 1) { + // We don't yet have mip generation, so clamp the number of levels to the ones we can load directly. + levels = std::min(plan.levelsToCreate, plan.levelsToLoad); + LPDIRECT3DTEXTURE9 tex; hr = device_->CreateTexture(tw, th, levels, usage, dstFmt, pool, &tex, nullptr); texture = tex; @@ -266,6 +267,8 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { LPDIRECT3DVOLUMETEXTURE9 tex; hr = device_->CreateVolumeTexture(tw, th, plan.depth, 1, usage, dstFmt, pool, &tex, nullptr); texture = tex; + + levels = 1; } if (FAILED(hr)) { @@ -322,7 +325,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { entry->status |= TexCacheEntry::STATUS_3D; } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index ff3eb56a0f..4eb23cd4d4 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -315,7 +315,7 @@ void DrawEngineGLES::DoFlush() { ApplyDrawState(prim); ApplyDrawStateLate(false, 0); - LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, framebufferManager_->UseBufferedRendering()); + LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, lastVType_, framebufferManager_->UseBufferedRendering()); GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt()); render_->BindVertexBuffer(inputLayout, vertexBuffer, vertexBufferOffset); if (useElements) { @@ -405,7 +405,7 @@ void DrawEngineGLES::DoFlush() { ApplyDrawStateLate(result.setStencil, result.stencilValue); - shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, framebufferManager_->UseBufferedRendering()); + shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, lastVType_, framebufferManager_->UseBufferedRendering()); if (result.action == SW_DRAW_PRIMITIVES) { if (result.drawIndexed) { diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 8d45ff0d84..d843c83ff4 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -123,7 +123,6 @@ private: void DoFlush(); void ApplyDrawState(int prim); void ApplyDrawStateLate(bool setStencil, int stencilValue); - void ResetFramebufferRead(); GLRInputLayout *SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index bb3ba416c4..63395d7509 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -410,7 +410,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu if (IsMultiviewSupported()) { render_->SetUniformM4x4Stereo("u_proj", &u_proj, leftEyeMatrix.m, rightEyeMatrix.m); } else { - render_->SetUniformM4x4(&u_proj, leftEyeMatrix.m); + render_->SetUniformM4x4(&u_proj, GetVRFBOIndex() == 0 ? leftEyeMatrix.m : rightEyeMatrix.m); } } else { Matrix4x4 flippedMatrix; @@ -537,7 +537,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu if (IsMultiviewSupported()) { render_->SetUniformM4x4Stereo("u_view", &u_view, leftEyeView, rightEyeView); } else { - render_->SetUniformM4x4(&u_view, leftEyeView); + render_->SetUniformM4x4(&u_view, GetVRFBOIndex() == 0 ? leftEyeView : rightEyeView); } } else { SetMatrix4x3(render_, &u_view, gstate.viewMatrix); @@ -784,7 +784,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess return vs; } -LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, u32 vertType, bool useBufferedRendering) { +LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, u32 vertType, bool useBufferedRendering) { uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty) { if (lastShader_) @@ -796,7 +796,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index b6f6272d70..b91ba8e7c9 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -159,7 +159,7 @@ public: // This is the old ApplyShader split into two parts, because of annoying information dependencies. // If you call ApplyVertexShader, you MUST call ApplyFragmentShader soon afterwards. Shader *ApplyVertexShader(bool useHWTransform, bool useHWTessellation, u32 vertType, bool weightsAsFloat, VShaderID *VSID); - LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, u32 vertType, bool useBufferedRendering); + LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, u32 vertType, bool useBufferedRendering); void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 5f9fe9d135..007c280832 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -122,14 +122,6 @@ static const GLushort logicOps[] = { }; #endif -inline void DrawEngineGLES::ResetFramebufferRead() { - if (fboTexBound_) { - GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr); - fboTexBound_ = false; - } -} - void DrawEngineGLES::ApplyDrawState(int prim) { GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); @@ -145,36 +137,30 @@ void DrawEngineGLES::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); - if (gstate.isModeClear()) { // Color Test bool colorMask = gstate.isClearModeColorMask(); bool alphaMask = gstate.isClearModeAlphaMask(); renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0)); } else { - // Do the large chunks of state conversion. We might be able to hide these two behind a dirty-flag each, - // to avoid recomputing heavy stuff unnecessarily every draw call. - - GenericMaskState maskState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - GenericBlendState blendState; - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; + GenericLogicState &logicState = pipelineState_.logicState; if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { - ApplyFramebufferRead(&fboTexNeedsBind_); + bool fboTexNeedsBind = false; + ApplyFramebufferRead(&fboTexNeedsBind); // The shader takes over the responsibility for blending, so recompute. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); // We copy the framebuffer here, as doing so will wipe any blend state if we do it later. - if (fboTexNeedsBind_) { + if (fboTexNeedsBind) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f); fboTexBound_ = true; - fboTexNeedsBind_ = false; framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState"); // Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects. @@ -183,13 +169,17 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); - dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; - gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } else { + if (fboTexBound_) { + GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr); + fboTexBound_ = false; + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } } - if (blendState.enabled) { + if (blendState.blendEnabled) { if (blendState.dirtyShaderBlendFixValues) { // Not quite sure how necessary this is. dirtyRequiresRecheck_ |= DIRTY_SHADERBLEND; @@ -203,19 +193,20 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } } - int mask = (int)maskState.rgba[0] | ((int)maskState.rgba[1] << 1) | ((int)maskState.rgba[2] << 2) | ((int)maskState.rgba[3] << 3); - if (blendState.enabled) { - renderManager->SetBlendAndMask(mask, blendState.enabled, + int mask = (int)maskState.channelMask; + if (blendState.blendEnabled) { + renderManager->SetBlendAndMask(mask, blendState.blendEnabled, glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor], glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha], glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]); } else { renderManager->SetNoBlendAndMask(mask); } + + // TODO: Get rid of the ifdef #ifndef USING_GLES2 if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { - renderManager->SetLogicOp(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY, - logicOps[gstate.getLogicOp()]); + renderManager->SetLogicOp(logicState.logicOpEnabled, logicOps[(int)logicState.logicOp]); } #endif } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index b24b48283a..928854f1e2 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -258,15 +258,13 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { // the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not // be as good quality as the game's own (might even be better in some cases though). - int tw = plan.w; - int th = plan.h; + int tw = plan.createW; + int th = plan.createH; Draw::DataFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) { dstFmt = plan.replaced->Format(plan.baseLevelSrc); - } else if (plan.scaleFactor > 1) { - tw *= plan.scaleFactor; - th *= plan.scaleFactor; + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = Draw::DataFormat::R8G8B8A8_UNORM; } @@ -301,37 +299,36 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { for (int i = 0; i < plan.levelsToLoad; i++) { int srcLevel = i == 0 ? plan.baseLevelSrc : i; - int w = gstate.getTextureWidth(srcLevel); - int h = gstate.getTextureHeight(srcLevel); + int mipWidth; + int mipHeight; + plan.GetMipSize(i, &mipWidth, &mipHeight); u8 *data = nullptr; int stride = 0; + int bpp; - if (plan.replaced->GetSize(srcLevel, w, h)) { - int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); - stride = w * bpp; - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + if (plan.replaceValid) { + bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel)); } else { if (plan.scaleFactor > 1) { - data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16); - stride = w * plan.scaleFactor * 4; + bpp = 4; } else { - int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; - - stride = std::max(w * bpp, 4); - data = (u8 *)AllocateAlignedMemory(stride * h, 16); + bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; } } + stride = mipWidth * bpp; + data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16); + if (!data) { - ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h); + ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight); return; } LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true); // NOTE: TextureImage takes ownership of data, so we don't free it afterwards. - render_->TextureImage(entry->textureName, i, w * plan.scaleFactor, h * plan.scaleFactor, 1, dstFmt, data, GLRAllocType::ALIGNED); + render_->TextureImage(entry->textureName, i, mipWidth, mipHeight, 1, dstFmt, data, GLRAllocType::ALIGNED); } bool genMips = plan.levelsToCreate > plan.levelsToLoad; @@ -359,7 +356,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { render_->FinalizeTexture(entry->textureName, 1, false); } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 18f1cab9d4..60383b6c86 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1686,6 +1686,16 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { } } + if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin) { + switch (gstate.vertType & 0xFFFFFF) { + case 0x00800102: // through, u16 uv, u16 pos (used for the framebuffer effect in-game) + case 0x0080011c: // through, 8888 color, s16 pos (used for clearing in the margin of the title screen) + case 0x00000183: // float uv, float pos (used for drawing in the margin of the title screen) + // Need to re-check the framebuffer every one of these draws, to update the split if needed. + gstate_c.Dirty(DIRTY_FRAMEBUF); + } + } + // This also makes skipping drawing very effective. VirtualFramebuffer *vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); if (blueToAlpha) { @@ -2854,9 +2864,10 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { if (MemBlockInfoDetailed(numBytes, numBytes)) { const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp; const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp; - const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize); - NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); - NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); + char tag[128]; + size_t tagSize = FormatMemWriteTagAt(tag, sizeof(tag), "GPUBlockTransfer/", src, srcSize); + NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag, tagSize); + NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag, tagSize); } // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. @@ -2871,7 +2882,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { // Since they're identical we don't need to copy. if (!Memory::IsVRAMAddress(dest) || (dest ^ 0x00400000) != src) { if (MemBlockInfoDetailed(size)) { - const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("GPUMemcpy/", src, size); Memory::Memcpy(dest, src, size, tag.c_str(), tag.size()); } else { Memory::Memcpy(dest, src, size, "GPUMemcpy"); @@ -2883,7 +2894,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) { } if (MemBlockInfoDetailed(size)) { - const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size); + const std::string tag = GetMemWriteTagAt("GPUMemcpy/", src, size); NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dest, size, tag.c_str(), tag.size()); } @@ -2938,7 +2949,7 @@ void GPUCommon::InvalidateCache(u32 addr, int size, GPUInvalidationType type) { // Vempire invalidates (with writeback) after drawing, but before blitting. // TODO: Investigate whether we can get this to work some other way. if (type == GPU_INVALIDATE_SAFE) { - framebufferManager_->UpdateFromMemory(addr, size, type == GPU_INVALIDATE_SAFE); + framebufferManager_->UpdateFromMemory(addr, size); } } } diff --git a/GPU/GPUState.h b/GPU/GPUState.h index f75a09fdad..5b011374ac 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -550,12 +550,6 @@ struct GPUStateCache { Dirty(DIRTY_TEXCLAMP); } } - void SetAllowFramebufferRead(bool allow) { - if (allowFramebufferRead != allow) { - allowFramebufferRead = allow; - Dirty(DIRTY_FRAGMENTSHADER_STATE); - } - } void SetTextureIs3D(bool is3D) { if (is3D != curTextureIs3D) { curTextureIs3D = is3D; @@ -583,7 +577,6 @@ struct GPUStateCache { bool bgraTexture; bool needShaderTexClamp; - bool allowFramebufferRead; float morphWeights[8]; u32 deferredVertTypeDirty; @@ -592,8 +585,8 @@ struct GPUStateCache { u32 curTextureHeight; u32 actualTextureHeight; // Only applied when needShaderTexClamp = true. - u32 curTextureXOffset; - u32 curTextureYOffset; + int curTextureXOffset; + int curTextureYOffset; bool curTextureIs3D; float vpWidth; @@ -621,15 +614,15 @@ struct GPUStateCache { u32 curRTRenderWidth; u32 curRTRenderHeight; - void SetCurRTOffset(u32 xoff, u32 yoff) { + void SetCurRTOffset(int xoff, int yoff) { if (xoff != curRTOffsetX || yoff != curRTOffsetY) { curRTOffsetX = xoff; curRTOffsetY = yoff; - Dirty(DIRTY_VIEWPORTSCISSOR_STATE); + Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_PROJTHROUGHMATRIX); } } - u32 curRTOffsetX; - u32 curRTOffsetY; + int curRTOffsetX; + int curRTOffsetY; // Set if we are doing hardware bezier/spline. SubmitType submitType; diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp index ef04d7c606..4f4bdb0efd 100644 --- a/GPU/GeDisasm.cpp +++ b/GPU/GeDisasm.cpp @@ -16,11 +16,12 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include - #include "Core/MemMap.h" #include "GPU/ge_constants.h" #include "GPU/GPU.h" +#include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +#include "GPU/GeDisasm.h" void GeDescribeVertexType(u32 op, char *buffer, int len) { bool through = (op & GE_VTYPE_THROUGH_MASK) == GE_VTYPE_THROUGH; @@ -113,7 +114,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_BASE: - snprintf(buffer, bufsize, "BASE: %06x", data); + if ((data & 0x000F0000) == data) + snprintf(buffer, bufsize, "BASE: high=%02x", data >> 16); + else + snprintf(buffer, bufsize, "BASE: high=%02x (extra %06x)", data >> 16, data & ~0x000F0000); break; case GE_CMD_VADDR: @@ -171,16 +175,16 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_JUMP: { - u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; - snprintf(buffer, bufsize, "JUMP: %08x to %08x", pc, target); + u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); + snprintf(buffer, bufsize, "JUMP: %08x to %08x (%06x)", pc, target, data); } break; case GE_CMD_CALL: { u32 retval = pc + 4; - u32 target = gstate_c.getRelativeAddress(op & 0xFFFFFF); - snprintf(buffer, bufsize, "CALL: %08x to %08x, ret=%08x", pc, target, retval); + u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); + snprintf(buffer, bufsize, "CALL: %08x to %08x (%06x), ret=%08x", pc, target, data, retval); } break; @@ -210,28 +214,45 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { int signal = prev & 0xFFFF; int enddata = data & 0xFFFFFF; // We should probably defer to sceGe here, no sense in implementing this stuff in every GPU + u32 target = (((signal << 16) | (enddata & 0xFFFF)) & 0xFFFFFFFC); switch (behaviour) { - case 1: + case PSP_GE_SIGNAL_HANDLER_SUSPEND: snprintf(buffer, bufsize, "Signal with wait. signal/end: %04x %04x", signal, enddata); break; - case 2: + case PSP_GE_SIGNAL_HANDLER_CONTINUE: snprintf(buffer, bufsize, "Signal without wait. signal/end: %04x %04x", signal, enddata); break; - case 3: + case PSP_GE_SIGNAL_HANDLER_PAUSE: snprintf(buffer, bufsize, "Signal with pause. signal/end: %04x %04x", signal, enddata); break; - case 8: + case PSP_GE_SIGNAL_SYNC: snprintf(buffer, bufsize, "Signal with sync. signal/end: %04x %04x", signal, enddata); break; - case 0x10: - snprintf(buffer, bufsize, "Signal with jump. signal/end: %04x %04x", signal, enddata); + case PSP_GE_SIGNAL_JUMP: + snprintf(buffer, bufsize, "Signal with jump. signal/end: %04x %04x, target: %08x", signal, enddata, target); break; - case 0x11: - snprintf(buffer, bufsize, "Signal with call. signal/end: %04x %04x", signal, enddata); + case PSP_GE_SIGNAL_CALL: + snprintf(buffer, bufsize, "Signal with call. signal/end: %04x %04x, target: %08x", signal, enddata, target); break; - case 0x12: + case PSP_GE_SIGNAL_RET: snprintf(buffer, bufsize, "Signal with return. signal/end: %04x %04x", signal, enddata); break; + case PSP_GE_SIGNAL_RJUMP: + target += pc; + snprintf(buffer, bufsize, "Signal with jump (relative.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; + case PSP_GE_SIGNAL_RCALL: + target += pc; + snprintf(buffer, bufsize, "Signal with call (relative.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; + case PSP_GE_SIGNAL_OJUMP: + target = gstate_c.getRelativeAddress(target); + snprintf(buffer, bufsize, "Signal with jump (offset.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; + case PSP_GE_SIGNAL_OCALL: + target = gstate_c.getRelativeAddress(target); + snprintf(buffer, bufsize, "Signal with call (offset.) signal/end: %04x %04x, target: %08x", signal, enddata, target); + break; default: snprintf(buffer, bufsize, "UNKNOWN Signal UNIMPLEMENTED %i! signal/end: %04x %04x", behaviour, signal, enddata); break; @@ -262,7 +283,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_ORIGIN: - snprintf(buffer, bufsize, "ORIGIN: %06x", data); + if (data) + snprintf(buffer, bufsize, "ORIGIN offset=%08x (extra %06x)", pc, data); + else + snprintf(buffer, bufsize, "ORIGIN offset=%08x", pc); break; case GE_CMD_VERTEXTYPE: @@ -273,24 +297,24 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_OFFSETADDR: - snprintf(buffer, bufsize, "OffsetAddr: %06x", data); + snprintf(buffer, bufsize, "OffsetAddr: %06x (offset=%08x)", data, data << 8); break; case GE_CMD_REGION1: { int x1 = data & 0x3ff; - int y1 = data >> 10; + int y1 = (data >> 10) & 0x3ff; if (data & 0xF00000) - snprintf(buffer, bufsize, "Region TL: %d %d (extra %x)", x1, y1, data >> 20); + snprintf(buffer, bufsize, "Region Rate: %d %d (extra %x)", x1, y1, data >> 20); else - snprintf(buffer, bufsize, "Region TL: %d %d", x1, y1); + snprintf(buffer, bufsize, "Region Rate: %d %d", x1, y1); } break; case GE_CMD_REGION2: { int x2 = data & 0x3ff; - int y2 = data >> 10; + int y2 = (data >> 10) & 0x3ff; if (data & 0xF00000) snprintf(buffer, bufsize, "Region BR: %d %d (extra %x)", x2, y2, data >> 20); else @@ -349,7 +373,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_SCISSOR1: { int x1 = data & 0x3ff; - int y1 = data >> 10; + int y1 = (data >> 10) & 0x3ff; if (data & 0xF00000) snprintf(buffer, bufsize, "Scissor TL: %i, %i (extra %x)", x1, y1, data >> 20); else @@ -359,7 +383,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_SCISSOR2: { int x2 = data & 0x3ff; - int y2 = data >> 10; + int y2 = (data >> 10) & 0x3ff; if (data & 0xF00000) snprintf(buffer, bufsize, "Scissor BR: %i, %i (extra %x)", x2, y2, data >> 20); else @@ -368,33 +392,37 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_MINZ: - { - float zMin = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "MinZ: %f", zMin); - } + if (data & 0xFF0000) + snprintf(buffer, bufsize, "MinZ: 0x%04x / %f (extra %02x)", data & 0xFFFF, (float)(data & 0xFFFF) / 65535.0f, data >> 16); + else + snprintf(buffer, bufsize, "MinZ: 0x%04x / %f", data, (float)data / 65535.0f); break; case GE_CMD_MAXZ: - { - float zMax = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "MaxZ: %f", zMax); - } + if (data & 0xFF0000) + snprintf(buffer, bufsize, "MaxZ: 0x%04x / %f (extra %02x)", data & 0xFFFF, (float)(data & 0xFFFF) / 65535.0f, data >> 16); + else + snprintf(buffer, bufsize, "MaxZ: 0x%04x / %f", data, (float)data / 65535.0f); break; case GE_CMD_FRAMEBUFPTR: { - snprintf(buffer, bufsize, "FramebufPtr: %08x", data); + snprintf(buffer, bufsize, "Framebuf ptr: 0x04%06x", data); } break; case GE_CMD_FRAMEBUFWIDTH: - { - snprintf(buffer, bufsize, "FramebufWidth: %x, address high %02x", data & 0xFFFF, data >> 16); - } + if (data & ~0x07FC) + snprintf(buffer, bufsize, "Framebuf stride: 0x%x (extra %06x)", data & 0x07FC, data & ~0x07FC); + else + snprintf(buffer, bufsize, "Framebuf stride: %04x", data); break; case GE_CMD_FRAMEBUFPIXFORMAT: - snprintf(buffer, bufsize, "FramebufPixelFormat: %i", data); + if (data <= 3) + snprintf(buffer, bufsize, "Framebuf PixelFormat: %s", GeBufferFormatToString((GEBufferFormat)data)); + else + snprintf(buffer, bufsize, "Framebuf PixelFormat: invalid %x", data); break; case GE_CMD_TEXADDR0: @@ -405,7 +433,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TEXADDR5: case GE_CMD_TEXADDR6: case GE_CMD_TEXADDR7: - snprintf(buffer, bufsize, "Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data); + snprintf(buffer, bufsize, "Texture address %d: low=%06x", cmd - GE_CMD_TEXADDR0, data); break; case GE_CMD_TEXBUFWIDTH0: @@ -416,31 +444,53 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TEXBUFWIDTH5: case GE_CMD_TEXBUFWIDTH6: case GE_CMD_TEXBUFWIDTH7: - snprintf(buffer, bufsize, "Texture BUFWIDTH %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data); + snprintf(buffer, bufsize, "Texture stride %d: 0x%04x, address high=%02x", cmd - GE_CMD_TEXBUFWIDTH0, data & 0xFFFF, data >> 16); break; case GE_CMD_CLUTADDR: - snprintf(buffer, bufsize, "CLUT base addr: %06x", data); + snprintf(buffer, bufsize, "CLUT addr: low=%06x", data); break; case GE_CMD_CLUTADDRUPPER: - snprintf(buffer, bufsize, "CLUT addr upper %08x", data); + if ((data & 0x000F0000) == data) + snprintf(buffer, bufsize, "CLUT addr: high=%02x", data >> 16); + else + snprintf(buffer, bufsize, "CLUT addr: high=%02x (extra %06x)", data >> 16, data & ~0x000F0000); break; case GE_CMD_LOADCLUT: - // This could be used to "dirty" textures with clut. - if (data) - snprintf(buffer, bufsize, "Clut load: %08x, %d bytes, %06x", gstate.getClutAddress(), (data & 0x3F) << 5, data & 0xFFFFC0); + if ((data & 0xFFFFC0) != 0) + snprintf(buffer, bufsize, "Clut load: %08x, %d bytes (extra %06x)", gstate.getClutAddress(), (data & 0x3F) << 5, data & 0xFFFFC0); else - snprintf(buffer, bufsize, "Clut load"); + snprintf(buffer, bufsize, "Clut load: %08x, %d bytes", gstate.getClutAddress(), (data & 0x3F) << 5); break; case GE_CMD_TEXMAPMODE: - snprintf(buffer, bufsize, "Tex map mode: %06x", data); + { + static const char *uvgen[] = { + "texcoords", + "texgen matrix", + "env map", + "invalid" + }; + static const char *uvproj[] = { + "pos", + "uv", + "normalized normal", + "normal", + }; + if ((data & 0x000303) == data) + snprintf(buffer, bufsize, "Tex map mode: uvgen=%s, uvproj=%s", uvgen[data & 3], uvproj[(data >> 8) & 3]); + else + snprintf(buffer, bufsize, "Tex map mode: uvgen=%s, uvproj=%s (extra %06x)", uvgen[data & 3], uvproj[(data >> 8) & 3], data & ~0x000303); + } break; case GE_CMD_TEXSHADELS: - snprintf(buffer, bufsize, "Tex shade light sources: %06x", data); + if ((data & 0x000303) == data) + snprintf(buffer, bufsize, "Tex shade light sources: %d, %d", data & 3, (data >> 8) & 3); + else + snprintf(buffer, bufsize, "Tex shade light sources: %d, %d (extra %06x)", data & 3, (data >> 8) & 3, data & ~0x000303); break; case GE_CMD_CLUTFORMAT: @@ -458,9 +508,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TRANSFERSRC: { if (data & 0xF) - snprintf(buffer, bufsize, "Block transfer src: %06x (extra: %x)", data & ~0xF, data & 0xF); + snprintf(buffer, bufsize, "Block transfer src: low=%06x (extra: %x)", data & ~0xF, data & 0xF); else - snprintf(buffer, bufsize, "Block transfer src: %06x", data); + snprintf(buffer, bufsize, "Block transfer src: low=%06x", data); // Nothing to do, the next one prints } break; @@ -470,9 +520,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { u32 xferSrc = (gstate.transfersrc & 0x00FFFFFF) | ((data & 0xFF0000) << 8); u32 xferSrcW = data & 0x3FF; if (data & ~0xFF03FF) - snprintf(buffer, bufsize, "Block transfer src: %08x W: %i (extra %x)", xferSrc, xferSrcW, data); + snprintf(buffer, bufsize, "Block transfer src: high=%02x, w=%d (addr %08x, extra %x)", data >> 16, xferSrcW, xferSrc, data & ~0xFF03FF); else - snprintf(buffer, bufsize, "Block transfer src: %08x W: %i", xferSrc, xferSrcW); + snprintf(buffer, bufsize, "Block transfer src: high=%02x, w=%d (addr %08x)", data >> 16, xferSrcW, xferSrc); break; } @@ -480,9 +530,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { { // Nothing to do, the next one prints if (data & 0xF) - snprintf(buffer, bufsize, "Block transfer dst: %06x (extra: %x)", data & ~0xF, data & 0xF); + snprintf(buffer, bufsize, "Block transfer dst: low=%06x (extra: %x)", data & ~0xF, data & 0xF); else - snprintf(buffer, bufsize, "Block transfer dst: %06x", data); + snprintf(buffer, bufsize, "Block transfer dst: low=%06x", data); } break; @@ -491,9 +541,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { u32 xferDst = (gstate.transferdst & 0x00FFFFFF) | ((data & 0xFF0000) << 8); u32 xferDstW = data & 0x3FF; if (data & ~0xFF03FF) - snprintf(buffer, bufsize, "Block transfer dest: %08x W: %i (extra %x)", xferDst, xferDstW, data); + snprintf(buffer, bufsize, "Block transfer dst: high=%02x, w=%d (addr %08x, extra %x)", data >> 16, xferDstW, xferDst, data & ~0xFF03FF); else - snprintf(buffer, bufsize, "Block transfer dest: %08x W: %i", xferDst, xferDstW); + snprintf(buffer, bufsize, "Block transfer dst: high=%02x, w=%d (addr %08x)", data >> 16, xferDstW, xferDst); break; } @@ -513,9 +563,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { u32 x = (data & 1023); u32 y = ((data>>10) & 1023); if (data & 0xF00000) - snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i (extra %x)", x, y, data >> 20); + snprintf(buffer, bufsize, "Block transfer dst rect TL: %d, %d (extra %x)", x, y, data >> 20); else - snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i", x, y); + snprintf(buffer, bufsize, "Block transfer dst rect TL: %d, %d", x, y); break; } @@ -532,9 +582,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TRANSFERSTART: if (data & ~1) - snprintf(buffer, bufsize, "Block transfer start: %d (extra %x)", data & 1, data & ~1); + snprintf(buffer, bufsize, "Block transfer start: bpp=%d (extra %x)", (data & 1) ? 4 : 2, data & ~1); else - snprintf(buffer, bufsize, "Block transfer start: %d", data); + snprintf(buffer, bufsize, "Block transfer start: bpp=%d", (data & 1) ? 4 : 2); break; case GE_CMD_TEXSIZE0: @@ -548,7 +598,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { { int w = 1 << (data & 0xf); int h = 1 << ((data>>8) & 0xf); - snprintf(buffer, bufsize, "Texture size %i: %06x, width : %d, height : %d", cmd - GE_CMD_TEXSIZE0, data, w, h); + if ((data & ~0x0F0F) && w <= 512 && h <= 512) + snprintf(buffer, bufsize, "Texture size %d: %dx%d", cmd - GE_CMD_TEXSIZE0, w, h); + else + snprintf(buffer, bufsize, "Texture size %d: %dx%d (extra %06x)", cmd - GE_CMD_TEXSIZE0, w, h, data); } break; @@ -559,7 +612,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_ZBUFWIDTH: - snprintf(buffer, bufsize, "Zbuf width: %06x", data); + if (data & ~0x07FC) + snprintf(buffer, bufsize, "Zbuf stride: 0x%x (extra %06x)", data & 0x07FC, data & ~0x07FC); + else + snprintf(buffer, bufsize, "Zbuf stride: %04x", data); break; case GE_CMD_AMBIENTCOLOR: @@ -567,7 +623,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_AMBIENTALPHA: - snprintf(buffer, bufsize, "Ambient alpha: %06x", data); + if (data & ~0xFF) + snprintf(buffer, bufsize, "Ambient alpha: %02x (extra %04x)", data & 0xFF, data >> 8); + else + snprintf(buffer, bufsize, "Ambient alpha: %02x", data); break; case GE_CMD_MATERIALAMBIENT: @@ -587,7 +646,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_MATERIALALPHA: - snprintf(buffer, bufsize, "Material alpha color: %06x", data); + if (data & ~0xFF) + snprintf(buffer, bufsize, "Material alpha: %02x (extra %04x)", data & 0xFF, data >> 8); + else + snprintf(buffer, bufsize, "Material alpha: %02x", data); break; case GE_CMD_MATERIALSPECULARCOEF: @@ -596,23 +658,43 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_SHADEMODE: if (data & ~1) - snprintf(buffer, bufsize, "Shade: %06x (%s, extra %x)", data, data ? "gouraud" : "flat", data); + snprintf(buffer, bufsize, "Shade: %d (%s, extra %x)", data & 1, (data & 1) ? "gouraud" : "flat", data & ~1); else - snprintf(buffer, bufsize, "Shade: %06x (%s)", data, data ? "gouraud" : "flat"); + snprintf(buffer, bufsize, "Shade: %d (%s)", data & 1, (data & 1) ? "gouraud" : "flat"); break; case GE_CMD_LIGHTMODE: if (data & ~1) - snprintf(buffer, bufsize, "Lightmode: %06x (%s, extra %x)", data, data ? "separate spec" : "single color", data); + snprintf(buffer, bufsize, "Lightmode: %d (%s, extra %x)", data & 1, (data & 1) ? "separate spec" : "single color", data & ~1); else - snprintf(buffer, bufsize, "Lightmode: %06x (%s)", data, data ? "separate spec" : "single color"); + snprintf(buffer, bufsize, "Lightmode: %d (%s)", data & 1, (data & 1) ? "separate spec" : "single color"); break; case GE_CMD_LIGHTTYPE0: case GE_CMD_LIGHTTYPE1: case GE_CMD_LIGHTTYPE2: case GE_CMD_LIGHTTYPE3: - snprintf(buffer, bufsize, "Light %i type: %06x", cmd-GE_CMD_LIGHTTYPE0, data); + { + static const char *lightComputations[] = { + "diffuse", + "diffuse + spec", + "pow(diffuse)", + "unknown (diffuse?)", + }; + static const char *lightTypes[] = { + "directional", + "point", + "spot", + "unknown (directional?)", + }; + const int comp = (data & 0x0003) >> 0; + const int type = (data & 0x0300) >> 8; + + if (data & ~0x0303) + snprintf(buffer, bufsize, "Light %d type: %s, comp: %s (extra %06x)", cmd - GE_CMD_LIGHTTYPE0, lightTypes[type], lightComputations[comp], data & ~0x0303); + else + snprintf(buffer, bufsize, "Light %d type: %s, comp: %s", cmd - GE_CMD_LIGHTTYPE0, lightTypes[type], lightComputations[comp]); + } break; case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0: @@ -669,22 +751,22 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_VIEWPORTXSCALE: + snprintf(buffer, bufsize, "Viewport X scale: %f", getFloat24(data)); + break; case GE_CMD_VIEWPORTYSCALE: - case GE_CMD_VIEWPORTXCENTER: - case GE_CMD_VIEWPORTYCENTER: - snprintf(buffer, bufsize, "Viewport param %i: %f", cmd-GE_CMD_VIEWPORTXSCALE, getFloat24(data)); + snprintf(buffer, bufsize, "Viewport Y scale: %f", getFloat24(data)); break; case GE_CMD_VIEWPORTZSCALE: - { - float zScale = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "Viewport Z scale: %f", zScale); - } + snprintf(buffer, bufsize, "Viewport Z scale: %f", getFloat24(data)); + break; + case GE_CMD_VIEWPORTXCENTER: + snprintf(buffer, bufsize, "Viewport X center: %f", getFloat24(data)); + break; + case GE_CMD_VIEWPORTYCENTER: + snprintf(buffer, bufsize, "Viewport Y center: %f", getFloat24(data)); break; case GE_CMD_VIEWPORTZCENTER: - { - float zOff = getFloat24(data) / 65535.f; - snprintf(buffer, bufsize, "Viewport Z pos: %f", zOff); - } + snprintf(buffer, bufsize, "Viewport Z center: %f", getFloat24(data)); break; case GE_CMD_LIGHTENABLE0: @@ -695,34 +777,69 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_CULL: - snprintf(buffer, bufsize, "Cull: %06x", data); + if (data & ~1) + snprintf(buffer, bufsize, "Cull: %s (extra %06x)", (data & 1) ? "back (CCW)" : "front (CW)", data & ~1); + else + snprintf(buffer, bufsize, "Cull: %s", (data & 1) ? "back (CCW)" : "front (CW)"); break; case GE_CMD_PATCHDIVISION: { - int patch_div_s = data & 0xFF; - int patch_div_t = (data >> 8) & 0xFF; - if (data & 0xFF0000) - snprintf(buffer, bufsize, "Patch subdivision: %i x %i (extra %x)", patch_div_s, patch_div_t, data & 0xFF0000); + int patch_div_s = data & 0x7F; + int patch_div_t = (data >> 8) & 0x7F; + if (data & 0xFF8080) + snprintf(buffer, bufsize, "Patch subdivision: %d x %d (extra %x)", patch_div_s, patch_div_t, data & 0xFF8080); else - snprintf(buffer, bufsize, "Patch subdivision: %i x %i", patch_div_s, patch_div_t); + snprintf(buffer, bufsize, "Patch subdivision: %d x %d", patch_div_s, patch_div_t); } break; case GE_CMD_PATCHPRIMITIVE: - snprintf(buffer, bufsize, "Patch Primitive: %d", data); + { + static const char *patchPrims[] = { + "triangles", + "lines", + "points", + "unknown/points", + }; + if (data & ~3) + snprintf(buffer, bufsize, "Patch Primitive: %s (extra %06x)", patchPrims[data & 3], data & ~3); + else + snprintf(buffer, bufsize, "Patch Primitive: %s", patchPrims[data & 3]); + } break; case GE_CMD_PATCHFACING: - snprintf(buffer, bufsize, "Patch Facing: %d", data); + if (data & ~1) + snprintf(buffer, bufsize, "Patch Facing: %s (extra %06x)", (data & 1) ? "reversed normals" : "standard normals", data & ~1); + else + snprintf(buffer, bufsize, "Patch Facing: %s", (data & 1) ? "reversed normals" : "standard normals"); break; case GE_CMD_REVERSENORMAL: - snprintf(buffer, bufsize, "Reverse normal: %d", data); + if (data & ~1) + snprintf(buffer, bufsize, "Reverse normal: %s (extra %06x)", (data & 1) ? "reversed" : "standard", data & ~1); + else + snprintf(buffer, bufsize, "Reverse normal: %s", (data & 1) ? "reversed" : "standard"); break; case GE_CMD_MATERIALUPDATE: - snprintf(buffer, bufsize, "Material update: %d", data); + { + static const char *materialTypes[] = { + "none", + "ambient", + "diffuse", + "ambient, diffuse", + "specular", + "ambient, specular", + "diffuse, specular", + "ambient, diffuse, specular", + }; + if (data & ~7) + snprintf(buffer, bufsize, "Material update: %s (extra %06x)", materialTypes[data & 7], data & ~7); + else + snprintf(buffer, bufsize, "Material update: %s", materialTypes[data & 7]); + } break; @@ -747,7 +864,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { mode = clearModes[(data >> 8) & 7]; else mode = "off"; - snprintf(buffer, bufsize, "Clear mode: %06x (%s)", data, mode); + if (data & ~0x0701) + snprintf(buffer, bufsize, "Clear mode: %s (extra %06x)", mode, data & ~0x0701); + else + snprintf(buffer, bufsize, "Clear mode: %s", mode); } break; @@ -832,7 +952,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_ALPHATEST: { const char *alphaTestFuncs[] = { " NEVER ", " ALWAYS ", " == ", " != ", " < ", " <= ", " > ", " >= " }; - snprintf(buffer, bufsize, "Alpha test settings: %06x ((c & %02x)%s%02x)", data, (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF); + if (data & ~0xFFFF07) + snprintf(buffer, bufsize, "Alpha test: (src.a & %02x)%s%02x (extra %06x)", (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF, data & ~0xFFFF07); + else + snprintf(buffer, bufsize, "Alpha test: (src.a & %02x)%s%02x", (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF); } break; @@ -860,25 +983,35 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { "blend", "replace", "add", - "unsupported1", - "unsupported2", - "unsupported3", + "add2", + "add3", + "add4", }; if (data & ~0x10107) - snprintf(buffer, bufsize, "TexFunc %i %s %s%s (extra %x)", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : "", data); + snprintf(buffer, bufsize, "TexFunc %d %s %s%s (extra %x)", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? ", color double" : "", data & ~0x10107); else - snprintf(buffer, bufsize, "TexFunc %i %s %s%s", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : ""); + snprintf(buffer, bufsize, "TexFunc %d %s %s%s", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? ", color double" : ""); } break; case GE_CMD_TEXFILTER: { + static const char *textureFilters[] = { + "nearest", + "linear", + "nearest, invalid", + "linear, invalid", + "nearest, mipmap nearest", + "linear, mipmap nearest", + "nearest, mipmap linear", + "linear, mipmap linear", + }; int min = data & 7; int mag = (data >> 8) & 1; if (data & ~0x107) - snprintf(buffer, bufsize, "TexFilter min: %i mag: %i (extra %x)", min, mag, data); + snprintf(buffer, bufsize, "TexFilter min: %s, mag: %s (extra %x)", textureFilters[min], textureFilters[mag], data & ~0x107); else - snprintf(buffer, bufsize, "TexFilter min: %i mag: %i", min, mag); + snprintf(buffer, bufsize, "TexFilter min: %s, mag: %s", textureFilters[min], textureFilters[mag]); } break; @@ -887,12 +1020,15 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_TEXMODE: - snprintf(buffer, bufsize, "TexMode %06x (%s, %d levels, %s)", data, data & 1 ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut"); + if (data & ~0x070101) + snprintf(buffer, bufsize, "TexMode %s, %d levels, %s (extra %06x)", (data & 1) ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut", data & ~0x070101); + else + snprintf(buffer, bufsize, "TexMode %s, %d levels, %s", (data & 1) ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut"); break; case GE_CMD_TEXFORMAT: { - const char *texformats[] = { + static const char *texformats[] = { "5650", "5551", "4444", @@ -910,7 +1046,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { "unsupported4", "unsupported5", }; - snprintf(buffer, bufsize, "TexFormat %06x (%s)", data, texformats[data & 0xF]); + if (data & ~0xF) + snprintf(buffer, bufsize, "TexFormat %s (extra %06x)", texformats[data & 0xF], data & ~0xF); + else + snprintf(buffer, bufsize, "TexFormat %s", texformats[data & 0xF]); } break; @@ -930,24 +1069,34 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TEXWRAP: if (data & ~0x0101) - snprintf(buffer, bufsize, "TexWrap %s s, %s t (extra %x)", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap", data); + snprintf(buffer, bufsize, "TexWrap %s s, %s t (extra %x)", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap", data & ~0x0101); else snprintf(buffer, bufsize, "TexWrap %s s, %s t", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap"); break; case GE_CMD_TEXLEVEL: - if (data & ~0xFF0003) - snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i (extra %x)", data&3, data >> 16, data); - else - snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i", data&3, data >> 16); + { + static const char *mipLevelModes[] = { + "auto + bias", + "bias", + "slope + bias", + "invalid + bias", + }; + const int biasFixed = (s8)(data >> 16); + const float bias = (float)biasFixed / 16.0f; + if (data & ~0xFF0003) + snprintf(buffer, bufsize, "TexLevel mode: %s Offset: %f / %d (extra %x)", mipLevelModes[data & 3], bias, biasFixed, data & ~0xFF0003); + else + snprintf(buffer, bufsize, "TexLevel mode: %s Offset: %f / %d", mipLevelModes[data & 3], bias, biasFixed); + } break; case GE_CMD_FOG1: - snprintf(buffer, bufsize, "Fog1 %f", getFloat24(data)); + snprintf(buffer, bufsize, "Fog end %f", getFloat24(data)); break; case GE_CMD_FOG2: - snprintf(buffer, bufsize, "Fog2 %f", getFloat24(data)); + snprintf(buffer, bufsize, "Fog slope %f", getFloat24(data)); break; case GE_CMD_FOGCOLOR: @@ -955,7 +1104,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_TEXLODSLOPE: - snprintf(buffer, bufsize, "TexLodSlope %06x", data); + snprintf(buffer, bufsize, "TexLodSlope %f", getFloat24(data)); break; ////////////////////////////////////////////////////////////////// @@ -971,15 +1120,18 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_STENCILOP: { - const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT", "unsupported1", "unsupported2" }; + static const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT", "unsupported1", "unsupported2" }; snprintf(buffer, bufsize, "Stencil op: fail=%s, pass/depthfail=%s, pass=%s", stencilOps[data & 7], stencilOps[(data >> 8) & 7], stencilOps[(data >> 16) & 7]); } break; case GE_CMD_STENCILTEST: { - const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " }; - snprintf(buffer, bufsize, "Stencil test: %06x (%02x %s (c & %02x))", data, (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF); + static const char *zTestFuncs[] = { " NEVER ", " ALWAYS ", " == ", " != ", " < ", " <= ", " > ", " >= " }; + if (data & ~0xFFFF07) + snprintf(buffer, bufsize, "Stencil test: %02x%s(dst.a & %02x) (extra %06x)", (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF, data & ~0xFFFF07); + else + snprintf(buffer, bufsize, "Stencil test: %02x%s(dst.a & %02x)", (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF); } break; @@ -989,8 +1141,11 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_ZTEST: { - const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " }; - snprintf(buffer, bufsize, "Z test mode: %i (%s)", data, zTestFuncs[data & 7]); + static const char *zTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" }; + if (data & ~7) + snprintf(buffer, bufsize, "Z test mode: %s (extra %06x)", zTestFuncs[data & 7], data & ~7); + else + snprintf(buffer, bufsize, "Z test mode: %s", zTestFuncs[data & 7]); } break; @@ -1036,18 +1191,27 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { "negated and", "set", }; - snprintf(buffer, bufsize, "LogicOp: %06x (%s)", data, logicOps[data & 0xF]); + if (data & ~0xF) + snprintf(buffer, bufsize, "LogicOp: %s (%06x)", logicOps[data & 0xF], data & ~0xF); + else + snprintf(buffer, bufsize, "LogicOp: %s", logicOps[data & 0xF]); } break; case GE_CMD_ZWRITEDISABLE: - snprintf(buffer, bufsize, "ZMask: %06x", data); + if (data & ~1) + snprintf(buffer, bufsize, "ZMask: %s (extra %06x)", data & 1 ? "disable write" : "allow write", data & ~1); + else + snprintf(buffer, bufsize, "ZMask: %s", data & 1 ? "disable write" : "allow write"); break; case GE_CMD_COLORTEST: { - const char *colorTests[] = {"NEVER", "ALWAYS", " == ", " != "}; - snprintf(buffer, bufsize, "ColorTest: %06x (ref%s(c & cmask))", data, colorTests[data & 3]); + const char *colorTests[] = {" NEVER ", " ALWAYS ", " == ", " != "}; + if (data & ~3) + snprintf(buffer, bufsize, "ColorTest: (src.rgb & cmask)%s(dst.rgb & cmask) (extra %06x)", colorTests[data & 3], data & ~3); + else + snprintf(buffer, bufsize, "ColorTest: (src.rgb & cmask)%s(dst.rgb & cmask)", colorTests[data & 3]); } break; @@ -1060,18 +1224,21 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { break; case GE_CMD_MASKRGB: - snprintf(buffer, bufsize, "MaskRGB: %06x", data); + snprintf(buffer, bufsize, "MaskRGB: %06x (bits not to write)", data); break; case GE_CMD_MASKALPHA: - snprintf(buffer, bufsize, "MaskAlpha: %06x", data); + if (data & ~0xFF) + snprintf(buffer, bufsize, "MaskAlpha: %02x (bits not to write) (extra %04x)", data & 0xFF, data >> 8); + else + snprintf(buffer, bufsize, "MaskAlpha: %02x (bits not to write)", data & 0xFF); break; case GE_CMD_WORLDMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "World # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "World # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "World # %i", data & 0xF); + snprintf(buffer, bufsize, "World # %d", data & 0xF); break; case GE_CMD_WORLDMATRIXDATA: @@ -1080,9 +1247,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_VIEWMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "VIEW # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "VIEW # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "VIEW # %i", data & 0xF); + snprintf(buffer, bufsize, "VIEW # %d", data & 0xF); break; case GE_CMD_VIEWMATRIXDATA: @@ -1091,9 +1258,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_PROJMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "PROJECTION # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "PROJECTION # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "PROJECTION # %i", data & 0xF); + snprintf(buffer, bufsize, "PROJECTION # %d", data & 0xF); break; case GE_CMD_PROJMATRIXDATA: @@ -1102,9 +1269,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_TGENMATRIXNUMBER: if (data & ~0xF) - snprintf(buffer, bufsize, "TGEN # %i (extra %x)", data & 0xF, data); + snprintf(buffer, bufsize, "TGEN # %d (extra %x)", data & 0xF, data & ~0xF); else - snprintf(buffer, bufsize, "TGEN # %i", data & 0xF); + snprintf(buffer, bufsize, "TGEN # %d", data & 0xF); break; case GE_CMD_TGENMATRIXDATA: @@ -1113,13 +1280,13 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { case GE_CMD_BONEMATRIXNUMBER: if (data & ~0x7F) - snprintf(buffer, bufsize, "BONE #%i (extra %x)", data & 0x7F, data); + snprintf(buffer, bufsize, "BONE #%d (extra %x)", data & 0x7F, data & ~0x7F); else - snprintf(buffer, bufsize, "BONE #%i", data & 0x7F); + snprintf(buffer, bufsize, "BONE #%d", data & 0x7F); break; case GE_CMD_BONEMATRIXDATA: - snprintf(buffer, bufsize, "BONE data #%i %f", gstate.boneMatrixNumber & 0x7f, getFloat24(data)); + snprintf(buffer, bufsize, "BONE data # %f", getFloat24(data)); break; default: diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index bf24c39192..96e7a7f0fd 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -78,6 +78,27 @@ static inline Vec4IntResult SOFTRAST_CALL ModulateRGBA(Vec4IntArg prim_in, Vec4I return ToVec4IntResult(out); } +// Check if we can safely ignore the alpha test. +static inline bool AlphaTestIsNeedless(const PixelFuncID &pixelID) { + switch (pixelID.AlphaTestFunc()) { + case GE_COMP_NEVER: + case GE_COMP_EQUAL: + case GE_COMP_LESS: + case GE_COMP_LEQUAL: + return false; + + case GE_COMP_ALWAYS: + return true; + + case GE_COMP_NOTEQUAL: + case GE_COMP_GREATER: + case GE_COMP_GEQUAL: + return pixelID.alphaBlend && pixelID.alphaTestRef == 0 && !pixelID.hasAlphaTestMask; + } + + return false; +} + void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state) { const u8 *texptr = state.texptr[0]; @@ -132,11 +153,8 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran !pixelID.applyLogicOp && !pixelID.colorTest && !pixelID.dithering && - // TODO: Safe? - pixelID.AlphaTestFunc() != GE_COMP_ALWAYS && - pixelID.alphaTestRef == 0 && - !pixelID.hasAlphaTestMask && pixelID.alphaBlend && + AlphaTestIsNeedless(pixelID) && samplerID.useTextureAlpha && samplerID.TexFunc() == GE_TEXFUNC_MODULATE && !pixelID.applyColorWriteMask && @@ -205,13 +223,8 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran !pixelID.applyLogicOp && !pixelID.colorTest && !pixelID.dithering && - // TODO: Safe? - pixelID.AlphaTestFunc() != GE_COMP_ALWAYS && - pixelID.alphaTestRef == 0 && - !pixelID.hasAlphaTestMask && pixelID.alphaBlend && - samplerID.useTextureAlpha && - samplerID.TexFunc() == GE_TEXFUNC_MODULATE && + AlphaTestIsNeedless(pixelID) && !pixelID.applyColorWriteMask && pixelID.FBFormat() == GE_FORMAT_5551) { if (v1.color0.a() == 0) @@ -272,7 +285,9 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b bool orient_check = xdiff >= 0 && ydiff >= 0; // We already have a fast path for clear in ClearRectangle. bool state_check = !state.pixelID.clearMode && !state.samplerID.hasAnyMips && NoClampOrWrap(state, v0.texturecoords) && NoClampOrWrap(state, v1.texturecoords); - if ((coord_check || !state.enableTextures) && orient_check && state_check) { + // This doesn't work well with offset drawing, see #15876. Through never has a subpixel offset. + bool subpixel_check = ((v0.screenpos.x | v0.screenpos.y | v1.screenpos.x | v1.screenpos.y) & 0xF) != 0; + if ((coord_check || !state.enableTextures) && orient_check && state_check && subpixel_check) { binner.AddSprite(v0, v1); return true; } diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index dd4b71b166..8c26eac9b1 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -813,7 +813,7 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) { } if (MemBlockInfoDetailed(srcSize, dstSize)) { - const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize); + const std::string tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize); NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size()); NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size()); } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 37243cac85..0ada8b9b06 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -761,7 +761,7 @@ void DrawEngineVulkan::DoFlush() { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform if (!vshader) { // We're screwed. return; @@ -787,7 +787,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags); + renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -814,9 +814,9 @@ void DrawEngineVulkan::DoFlush() { if (!ibuf) { ibOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); } - renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16); } else { - renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount); + renderManager->Draw(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } } else { PROFILE_THIS_SCOPE("soft"); @@ -890,11 +890,11 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform - _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform + _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false); @@ -917,7 +917,7 @@ void DrawEngineVulkan::DoFlush() { lastRenderStepId_ = curRenderStepId; } - renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags); + renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -949,11 +949,11 @@ void DrawEngineVulkan::DoFlush() { VkBuffer vbuf, ibuf; vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); ibOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(short) * result.drawNumTrans, &ibuf); - renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, result.drawNumTrans * sizeof(TransformedVertex), &vbuf); - renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans); + renderManager->Draw(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans); } } else if (result.action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index f89a28b7f9..531e05c4ed 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -195,7 +195,6 @@ private: void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); void BindShaderBlendTex(); - void ResetFramebufferRead(); void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -282,6 +281,7 @@ private: VulkanDynamicState dynState_{}; int tessOffset_ = 0; + bool fboTexNeedsBind_ = false; // Hardware tessellation TessellationDataTransferVulkan *tessDataTransferVulkan; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 17f6f01aca..6cf56b17f3 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -254,7 +254,7 @@ void GPU_Vulkan::CheckGPUFeatures() { features |= GPU_SUPPORTS_DUALSOURCE_BLEND; } } - if (enabledFeatures.logicOp) { + if (draw_->GetDeviceCaps().logicOpSupported) { features |= GPU_SUPPORTS_LOGIC_OP; } if (draw_->GetDeviceCaps().anisoSupported) { diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 86fa279b9c..8909283884 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -36,7 +36,7 @@ void PipelineManagerVulkan::Clear() { pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) { if (value->pipeline) { - VkPipeline pipeline = value->pipeline->pipeline; + VkPipeline pipeline = value->pipeline->pipeline->BlockUntilReady(); vulkan_->Delete().QueueDeletePipeline(pipeline); vulkan_->Delete().QueueCallback([](void *p) { VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p; @@ -256,28 +256,8 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, ms.pSampleMask = nullptr; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - VkPipelineShaderStageCreateInfo *ss = &desc->shaderStageInfo[0]; - ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - ss[0].pSpecializationInfo = nullptr; - ss[0].module = vs->GetModule(); - ss[0].pName = "main"; - ss[0].flags = 0; - ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - ss[1].pSpecializationInfo = nullptr; - ss[1].module = fs->GetModule(); - ss[1].pName = "main"; - ss[1].flags = 0; - - if (!ss[0].module || !ss[1].module) { - ERROR_LOG(G3D, "Failed creating graphics pipeline - bad shaders"); - // Create a placeholder to avoid creating over and over if shader compiler broken. - VulkanPipeline *nullPipeline = new VulkanPipeline(); - nullPipeline->pipeline = VK_NULL_HANDLE; - nullPipeline->flags = 0; - return nullPipeline; - } + desc->fragmentShader = fs->GetModule(); + desc->vertexShader = vs->GetModule(); VkPipelineInputAssemblyStateCreateInfo &inputAssembly = desc->inputAssembly; inputAssembly.flags = 0; @@ -321,7 +301,6 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkGraphicsPipelineCreateInfo &pipe = desc->pipe; pipe.flags = 0; pipe.stageCount = 2; - pipe.pStages = ss; pipe.basePipelineIndex = 0; pipe.pColorBlendState = &desc->cbs; @@ -633,8 +612,8 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) { if (failed) return; - VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader); - VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader); + VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader->BlockUntilReady()); + VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader->BlockUntilReady()); if (!vshader || !fshader) { failed = true; return; diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 37d542e9a0..a6ab695cef 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -18,7 +18,9 @@ #pragma once #include + #include "Common/Data/Collections/Hashmaps.h" +#include "Common/Thread/Promise.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/ShaderId.h" @@ -33,8 +35,8 @@ class VulkanRenderManager; struct VulkanPipelineKey { VulkanPipelineRasterStateKey raster; // prim is included here VkRenderPass renderPass; - VkShaderModule vShader; - VkShaderModule fShader; + Promise *vShader; + Promise *fShader; uint32_t vtxFmtId; bool useHWTransform; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index a7fe4b252c..52c2103a88 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -42,62 +42,69 @@ #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/FramebufferManagerVulkan.h" -VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { - PROFILE_THIS_SCOPE("shadercomp"); +// Most drivers treat vkCreateShaderModule as pretty much a memcpy. What actually +// takes time here, and makes this worthy of parallelization, is GLSLtoSPV. +Promise *CompileShaderModuleAsync(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code) { + auto compile = [=] { + PROFILE_THIS_SCOPE("shadercomp"); - std::string errorMessage; - std::vector spirv; + std::string errorMessage; + std::vector spirv; - bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage); + bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, &errorMessage); - VkShaderModule shaderModule = VK_NULL_HANDLE; - - if (!errorMessage.empty()) { - if (success) { - ERROR_LOG(G3D, "Warnings in shader compilation!"); - } else { - ERROR_LOG(G3D, "Error in shader compilation!"); + if (!errorMessage.empty()) { + if (success) { + ERROR_LOG(G3D, "Warnings in shader compilation!"); + } else { + ERROR_LOG(G3D, "Error in shader compilation!"); + } + ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); + ERROR_LOG(G3D, "Shader source:\n%s", code); +#ifdef SHADERLOG + OutputDebugStringA(LineNumberString(code).c_str()); + OutputDebugStringA("Error messages:\n"); + OutputDebugStringA(errorMessage.c_str()); +#endif + Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } - ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); - ERROR_LOG(G3D, "Shader source:\n%s", code); -#ifdef SHADERLOG - OutputDebugStringA(LineNumberString(code).c_str()); - OutputDebugStringA("Error messages:\n"); - OutputDebugStringA(errorMessage.c_str()); -#endif - Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); - } else { -#ifdef SHADERLOG - OutputDebugStringA(LineNumberString(code).c_str()); -#endif -#ifdef SHADERLOG - OutputDebugStringA("OK\n"); -#endif - } - if (success) { - vulkan->CreateShaderModule(spirv, &shaderModule); - } + VkShaderModule shaderModule = VK_NULL_HANDLE; + if (success) { + success = vulkan->CreateShaderModule(spirv, &shaderModule); +#ifdef SHADERLOG + OutputDebugStringA("OK"); +#endif + } - return shaderModule; + return shaderModule; + }; + +#ifdef _DEBUG + // Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator. + return Promise::AlreadyDone(compile()); +#else + return Promise::Spawn(&g_threadManager, compile, TaskType::CPU_COMPUTE); +#endif } VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code) : vulkan_(vulkan), id_(id) { source_ = code; - module_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str()); if (!module_) { failed_ = true; - return; } else { VERBOSE_LOG(G3D, "Compiled fragment shader:\n%s\n", (const char *)code); } } VulkanFragmentShader::~VulkanFragmentShader() { - if (module_ != VK_NULL_HANDLE) { - vulkan_->Delete().QueueDeleteShaderModule(module_); + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; } } @@ -115,18 +122,19 @@ std::string VulkanFragmentShader::GetShaderString(DebugShaderStringType type) co VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, const char *code, bool useHWTransform) : vulkan_(vulkan), useHWTransform_(useHWTransform), id_(id) { source_ = code; - module_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str()); + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_VERTEX_BIT, source_.c_str()); if (!module_) { failed_ = true; - return; } else { VERBOSE_LOG(G3D, "Compiled vertex shader:\n%s\n", (const char *)code); } } VulkanVertexShader::~VulkanVertexShader() { - if (module_ != VK_NULL_HANDLE) { - vulkan_->Delete().QueueDeleteShaderModule(module_); + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; } } @@ -217,7 +225,7 @@ uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) { return dirty; } -void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { +void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { gstate_c.Clean(DIRTY_VERTEXSHADER_STATE); @@ -229,7 +237,7 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, draw_->GetBugs()); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs()); } else { FSID = lastFSID_; } @@ -334,7 +342,9 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade VulkanVertexShader *ShaderManagerVulkan::GetVertexShaderFromModule(VkShaderModule module) { VulkanVertexShader *vs = nullptr; vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *shader) { - if (shader->GetModule() == module) + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) vs = shader; }); return vs; @@ -343,7 +353,9 @@ VulkanVertexShader *ShaderManagerVulkan::GetVertexShaderFromModule(VkShaderModul VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderModule module) { VulkanFragmentShader *fs = nullptr; fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *shader) { - if (shader->GetModule() == module) + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) fs = shader; }); return fs; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 4b167e002b..50915be6a2 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -20,6 +20,7 @@ #include #include +#include "Common/Thread/Promise.h" #include "Common/Data/Collections/Hashmaps.h" #include "Common/GPU/Vulkan/VulkanMemory.h" #include "GPU/Common/ShaderCommon.h" @@ -43,11 +44,11 @@ public: bool Failed() const { return failed_; } std::string GetShaderString(DebugShaderStringType type) const; - VkShaderModule GetModule() const { return module_; } + Promise *GetModule() { return module_; } const FShaderID &GetID() { return id_; } protected: - VkShaderModule module_ = VK_NULL_HANDLE; + Promise *module_ = nullptr; VulkanContext *vulkan_; std::string source_; @@ -66,11 +67,11 @@ public: bool UseHWTransform() const { return useHWTransform_; } std::string GetShaderString(DebugShaderStringType type) const; - VkShaderModule GetModule() const { return module_; } + Promise *GetModule() { return module_; } const VShaderID &GetID() { return id_; } protected: - VkShaderModule module_ = VK_NULL_HANDLE; + Promise *module_ = nullptr; VulkanContext *vulkan_; std::string source_; @@ -89,7 +90,7 @@ public: void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); - void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); + void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); void ClearShaders(); void DirtyShader(); void DirtyLastShader() override; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index ac51debc6a..5c7f7b77af 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -122,12 +122,6 @@ static const VkLogicOp logicOps[] = { VK_LOGIC_OP_SET, }; -void DrawEngineVulkan::ResetFramebufferRead() { - boundSecondary_ = VK_NULL_HANDLE; - fboTexBound_ = false; -} - -// TODO: Do this more progressively. No need to compute the entire state if the entire state hasn't changed. // In Vulkan, we simply collect all the state together into a "pipeline key" - we don't actually set any state here // (the caller is responsible for setting the little dynamic state that is supported, dynState). void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState) { @@ -136,7 +130,6 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); if (gstate.isModeClear()) { key.logicOpEnable = false; key.logicOp = VK_LOGIC_OP_CLEAR; @@ -154,34 +147,28 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool alphaMask = gstate.isClearModeAlphaMask(); key.colorWriteMask = (colorMask ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT) : 0) | (alphaMask ? VK_COLOR_COMPONENT_A_BIT : 0); } else { - if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) { - key.logicOpEnable = true; - key.logicOp = logicOps[gstate.getLogicOp()]; - } else { - key.logicOpEnable = false; - key.logicOp = VK_LOGIC_OP_CLEAR; - } - - GenericMaskState maskState; - ConvertMaskState(maskState, gstate_c.allowFramebufferRead); - - // Set blend - unless we need to do it in the shader. - GenericBlendState blendState; - ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead); + pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported); + GenericMaskState &maskState = pipelineState_.maskState; + GenericBlendState &blendState = pipelineState_.blendState; + GenericLogicState &logicState = pipelineState_.logicState; if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) { ApplyFramebufferRead(&fboTexNeedsBind_); // The shader takes over the responsibility for blending, so recompute. + // We might still end up using blend to write something to alpha. ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState); dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); - } else if (blendState.resetFramebufferRead) { - ResetFramebufferRead(); - dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; - gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } else { + if (fboTexBound_) { + boundSecondary_ = VK_NULL_HANDLE; + fboTexBound_ = false; + dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } } - if (blendState.enabled) { + if (blendState.blendEnabled) { key.blendEnable = true; key.blendOpColor = vkBlendEqLookup[(size_t)blendState.eqColor]; key.blendOpAlpha = vkBlendEqLookup[(size_t)blendState.eqAlpha]; @@ -208,11 +195,15 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag dynState.useBlendColor = false; } - key.colorWriteMask = - (maskState.rgba[0] ? VK_COLOR_COMPONENT_R_BIT : 0) | - (maskState.rgba[1] ? VK_COLOR_COMPONENT_G_BIT : 0) | - (maskState.rgba[2] ? VK_COLOR_COMPONENT_B_BIT : 0) | - (maskState.rgba[3] ? VK_COLOR_COMPONENT_A_BIT : 0); + key.colorWriteMask = maskState.channelMask; // flags match + + if (logicState.logicOpEnabled) { + key.logicOpEnable = true; + key.logicOp = logicOps[(int)logicState.logicOp]; + } else { + key.logicOpEnable = false; + key.logicOp = VK_LOGIC_OP_COPY; + } // Workaround proposed in #10421, for bug where the color write mask is not applied correctly on Adreno. if ((gstate.pmskc & 0x00FFFFFF) == 0x00FFFFFF && g_Config.bVendorBugChecksEnabled && draw_->GetBugs().Has(Draw::Bugs::COLORWRITEMASK_BROKEN_WITH_DEPTHTEST)) { @@ -374,7 +365,8 @@ void DrawEngineVulkan::BindShaderBlendTex() { // Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { if (fboTexNeedsBind_) { - framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); + bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); + _dbg_assert_(bindResult); boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW); fboTexBound_ = true; fboTexNeedsBind_ = false; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 76d2fbe71f..d11e92ba05 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -464,7 +464,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { // Any texture scaling is gonna move away from the original 16-bit format, if any. VkFormat actualFmt = plan.scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt; - if (plan.replaced->Valid()) { + if (plan.replaceValid) { actualFmt = ToVulkanFormat(plan.replaced->Format(plan.baseLevelSrc)); } @@ -499,11 +499,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { imageLayout = VK_IMAGE_LAYOUT_GENERAL; } + if (plan.saveTexture) { + actualFmt = VULKAN_8888_FORMAT; + } + char texName[128]{}; snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); image->SetTag(texName); - bool allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); + bool allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; @@ -519,10 +523,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f); } + // Turn off texture replacement for this texture. + plan.replaced = &replacer_.FindNone(); + + plan.createW /= plan.scaleFactor; + plan.createH /= plan.scaleFactor; plan.scaleFactor = 1; actualFmt = dstFmt; - allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); } if (!allocSuccess) { @@ -535,19 +544,6 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { return; } - ReplacedTextureDecodeInfo replacedInfo; - bool willSaveTex = false; - if (replacer_.Enabled() && plan.replaced->IsInvalid() && plan.depth == 1) { - replacedInfo.cachekey = entry->CacheKey(); - replacedInfo.hash = entry->fullhash; - replacedInfo.addr = entry->addr; - replacedInfo.isVideo = plan.isVideo; - replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; - replacedInfo.scaleFactor = plan.scaleFactor; - replacedInfo.fmt = FromVulkanFormat(actualFmt); - willSaveTex = replacer_.WillSave(replacedInfo); - } - VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "Texture Upload (%08x) video=%d", entry->addr, plan.isVideo); @@ -563,15 +559,14 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { int mipUnscaledWidth = gstate.getTextureWidth(i); int mipUnscaledHeight = gstate.getTextureHeight(i); - int mipWidth = mipUnscaledWidth * plan.scaleFactor; - int mipHeight = mipUnscaledHeight * plan.scaleFactor; - if (plan.replaced->Valid()) { - plan.replaced->GetSize(plan.baseLevelSrc + i, mipWidth, mipHeight); - } + int mipWidth; + int mipHeight; + plan.GetMipSize(i, &mipWidth, &mipHeight); int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp int stride = (mipWidth * bpp + 15) & ~15; // output stride - int size = stride * mipHeight; + int uploadSize = stride * mipHeight; + uint32_t bufferOffset; VkBuffer texBuf; // NVIDIA reports a min alignment of 1 but that can't be healthy... let's align by 16 as a minimum. @@ -580,21 +575,21 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { std::vector saveData; auto loadLevel = [&](int sz, int srcLevel, int lstride, int lfactor) { - if (willSaveTex) { + if (plan.saveTexture) { saveData.resize(sz); data = &saveData[0]; } else { data = drawEngine_->GetPushBufferForTextureData()->PushAligned(sz, &bufferOffset, &texBuf, pushAlignment); } - LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, dstFmt); - if (willSaveTex) + LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, actualFmt); + if (plan.saveTexture) bufferOffset = drawEngine_->GetPushBufferForTextureData()->PushAligned(&saveData[0], sz, pushAlignment, &texBuf); }; bool dataScaled = true; - if (plan.replaced->Valid()) { + if (plan.replaceValid) { // Directly load the replaced image. - data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment); + data = drawEngine_->GetPushBufferForTextureData()->PushAligned(uploadSize, &bufferOffset, &texBuf, pushAlignment); double replaceStart = time_now_d(); plan.replaced->Load(plan.baseLevelSrc + i, data, stride); // if it fails, it'll just be garbage data... OK for now. replacementTimeThisFrame_ += time_now_d() - replaceStart; @@ -604,7 +599,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT); } else { if (plan.depth != 1) { - loadLevel(size, i, stride, plan.scaleFactor); + // 3D texturing. + loadLevel(uploadSize, i, stride, plan.scaleFactor); entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, stride / bpp); } else if (computeUpload) { int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2; @@ -626,17 +622,27 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); vulkan->Delete().QueueDeleteImageView(view); } else { - loadLevel(size, i == 0 ? plan.baseLevelSrc : i, stride, plan.scaleFactor); + loadLevel(uploadSize, i == 0 ? plan.baseLevelSrc : i, stride, plan.scaleFactor); VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT, "Copy Upload: %dx%d", mipWidth, mipHeight); entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, stride / bpp); VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT); } - if (replacer_.Enabled()) { + // Format might be wrong in lowMemoryMode_, so don't save. + if (replacer_.Enabled() && plan.replaced->IsInvalid() && !lowMemoryMode_) { // When hardware texture scaling is enabled, this saves the original. int w = dataScaled ? mipWidth : mipUnscaledWidth; int h = dataScaled ? mipHeight : mipUnscaledHeight; // At this point, data should be saveData, and not slow. + ReplacedTextureDecodeInfo replacedInfo; + replacedInfo.cachekey = entry->CacheKey(); + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = entry->addr; + replacedInfo.isVideo = IsVideo(entry->addr); + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = plan.scaleFactor; + replacedInfo.fmt = FromVulkanFormat(actualFmt); + replacer_.NotifyTextureDecoded(replacedInfo, data, stride, plan.baseLevelSrc + i, w, h); } } @@ -663,7 +669,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { entry->status |= TexCacheEntry::STATUS_3D; } - if (plan.replaced->Valid()) { + if (plan.replaceValid) { entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus())); } } diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index 8cd455ba79..3559282c57 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -405,7 +405,7 @@ enum GELightComputation GE_LIGHTCOMP_ONLYPOWDIFFUSE = 2, }; -enum GETextureFormat +enum GETextureFormat : uint8_t { GE_TFMT_5650 = 0, GE_TFMT_5551 = 1, diff --git a/Tools/langtool/Cargo.lock b/Tools/langtool/Cargo.lock index e837567757..6f7ad9ef41 100644 --- a/Tools/langtool/Cargo.lock +++ b/Tools/langtool/Cargo.lock @@ -76,9 +76,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "proc-macro-error" @@ -106,18 +106,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -174,9 +174,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-segmentation" diff --git a/Tools/langtool/src/inifile.rs b/Tools/langtool/src/inifile.rs index 8669175c9b..ac109af0f7 100644 --- a/Tools/langtool/src/inifile.rs +++ b/Tools/langtool/src/inifile.rs @@ -1,6 +1,6 @@ -use std::path::{Path, PathBuf}; -use std::io::{self, Write}; use std::fs::File; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; use crate::section::Section; diff --git a/Tools/langtool/src/main.rs b/Tools/langtool/src/main.rs index d3ffa95193..6b99a272d3 100644 --- a/Tools/langtool/src/main.rs +++ b/Tools/langtool/src/main.rs @@ -18,12 +18,20 @@ enum Command { CopyMissingLines {}, CommentUnknownLines {}, RemoveUnknownLines {}, - MoveKey { old: String, new: String, key: String }, + MoveKey { + old: String, + new: String, + key: String, + }, + RemoveKey { + section: String, + key: String, + }, } fn copy_missing_lines(reference_ini: &IniFile, target_ini: &mut IniFile) -> io::Result<()> { - // Insert any missing full sections. for reference_section in &reference_ini.sections { + // Insert any missing full sections. if !target_ini.insert_section_if_missing(reference_section) { if let Some(target_section) = target_ini.get_section_mut(&reference_section.name) { for line in &reference_section.lines { @@ -43,7 +51,6 @@ fn deal_with_unknown_lines( target_ini: &mut IniFile, remove: bool, ) -> io::Result<()> { - // Insert any missing full sections. for reference_section in &reference_ini.sections { if let Some(target_section) = target_ini.get_section_mut(&reference_section.name) { if remove { @@ -56,13 +63,7 @@ fn deal_with_unknown_lines( Ok(()) } -fn move_key( - target_ini: &mut IniFile, - old: &str, - new: &str, - key: &str, -) -> io::Result<()> { - // Insert any missing full sections. +fn move_key(target_ini: &mut IniFile, old: &str, new: &str, key: &str) -> io::Result<()> { if let Some(old_section) = target_ini.get_section_mut(old) { if let Some(line) = old_section.remove_line(key) { if let Some(new_section) = target_ini.get_section_mut(new) { @@ -79,6 +80,15 @@ fn move_key( Ok(()) } +fn remove_key(target_ini: &mut IniFile, section: &str, key: &str) -> io::Result<()> { + if let Some(old_section) = target_ini.get_section_mut(section) { + let _ = old_section.remove_line(key); + } else { + println!("No section {}", section); + } + Ok(()) +} + fn main() { let opt = Opt::from_args(); @@ -127,8 +137,18 @@ fn main() { Command::RemoveUnknownLines {} => { deal_with_unknown_lines(&reference_ini, &mut target_ini, true).unwrap(); } - Command::MoveKey { ref old, ref new,ref key, } => { - move_key(&mut target_ini, &old, &new, &key).unwrap(); + Command::MoveKey { + ref old, + ref new, + ref key, + } => { + move_key(&mut target_ini, &old, &new, &key).unwrap(); + } + Command::RemoveKey { + ref section, + ref key, + } => { + remove_key(&mut target_ini, section, key).unwrap(); } } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 19ce717bf1..e8af977e6c 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -508,9 +508,6 @@ void GameSettingsScreen::CreateViews() { }); texSecondary_->SetDisabledPtr(&g_Config.bSoftwareRendering); - CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableSlowFramebufEffects, gr->T("Disable slower effects (speedup)"))); - framebufferSlowEffects->SetDisabledPtr(&g_Config.bSoftwareRendering); - // Seems solid, so we hide the setting. /*CheckBox *vtxJit = graphicsSettings->Add(new CheckBox(&g_Config.bVertexDecoderJit, gr->T("Vertex Decoder JIT"))); @@ -1120,10 +1117,8 @@ void GameSettingsScreen::CreateViews() { vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Enable virtual reality"))); CheckBox *vr6DoF = vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("Enable 6 degrees of freedom movement"))); vr6DoF->SetEnabledPtr(&g_Config.bEnableVR); - if (IsMultiviewSupported()) { - CheckBox *vrStereo = vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Enable stereoscopic vision"))); - vrStereo->SetEnabledPtr(&g_Config.bEnableVR); - } + CheckBox *vrStereo = vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Enable stereoscopic vision"))); + vrStereo->SetEnabledPtr(&g_Config.bEnableVR); PopupSliderChoice *vrFieldOfView = vrSettings->Add(new PopupSliderChoice(&g_Config.iFieldOfViewPercentage, 100, 150, vr->T("Field of view scale", "Headset's field of view scale"), 10, screenManager(), vr->T("% of native FoV"))); vrFieldOfView->SetEnabledPtr(&g_Config.bEnableVR); vrSettings->Add(new PopupSliderChoice(&g_Config.iCanvasDistance, 1, 10, vr->T("Distance to 2D menus and scenes", "Distance to 2D menus and scenes"), 1, screenManager(), "")); diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 1b38c93093..06d3d2f9f7 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -3,6 +3,7 @@ #include "UI/OnScreenDisplay.h" #include "Common/Data/Color/RGBAUtil.h" +#include "Common/Data/Encoding/Utf8.h" #include "Common/Render/TextureAtlas.h" #include "Common/Render/DrawBuffer.h" @@ -33,9 +34,10 @@ void OnScreenMessagesView::Draw(UIContext &dc) { // Messages that are wider than the screen are left-aligned instead of centered. int align = 0; - // Simple heuristic: Draw messages that contain line breaks with ASCII_FONT (likely debug output) + // If we have newlines, we may be looking at ASCII debug output. But let's verify. if (iter->text.find('\n') != 0) { - align |= FLAG_DYNAMIC_ASCII; + if (!UTF8StringHasNonASCII(iter->text.c_str())) + align |= FLAG_DYNAMIC_ASCII; } float tw, th; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 6cc3ea0885..482b864ea5 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1066,7 +1066,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, } if (IsVRBuild()) { - UpdateVRInput(NativeKey, g_Config.bHapticFeedback); + UpdateVRInput(NativeKey, NativeTouch, g_Config.bHapticFeedback, dp_xscale, dp_yscale); } } diff --git a/assets/compat.ini b/assets/compat.ini index 692173597f..bc86e5c4e5 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -287,13 +287,6 @@ UCKS45022 = true ULJS19009 = true NPJH50141 = true -[DisableReadbacks] -# MotoGP copies the framebuffer to RAM every frame. We have a hack to display it directly, -# which means we don't also need a readback. -ULJS00078 = true -ULUS10153 = true -UCES00373 = true - [DrawSyncEatCycles] # This replaced Crash Tag Team Racing hack to also fix Gundam games # It makes sceGeDrawSync eat a lot of cycles which can affect timing in lots of games, @@ -535,7 +528,7 @@ ULJM08033 = true NPJH50373 = true NPUH10191 = true NPUH10197 = true -# Grand Knights History need it to fix blackboxes on characters and flickering texture . See issues #2135 , #6099 +# Grand Knights History need it to fix blackboxes on characters and flickering texture . See issues #2135, #6099 ULJS00394 = true ULJS19068 = true NPJH50518 = true @@ -1011,6 +1004,14 @@ ULUS10317 = true ULUS10598 = true ULES01578 = true +# Killzone: Liberation (see issue #6207) +UCES00279 = true +UCKS45041 = true +UCUS98646 = true +UCET00278 = true +UCUS98670 = true +UCUS98646 = true + [JitInvalidationHack] # This is an absolutely awful hack that somehow prevents issues when clearing the JIT, # if the game has copied code with EmuHack opcodes or something. Hopefully will be able @@ -1264,6 +1265,15 @@ UCJS18030 = true UCJS18047 = true NPJG00015 = true +[SplitFramebufferMargin] +# Killzone: Liberation (see issue #6207) +UCES00279 = true +UCKS45041 = true +UCUS98646 = true +UCET00278 = true +UCUS98670 = true +UCUS98646 = true + [VRClearFramebuffer] #Grand Theft Auto: Liberty City Stories ULUS10041 = true diff --git a/assets/lang/ar_AE.ini b/assets/lang/ar_AE.ini index 05c6b51ce8..20d525747b 100644 --- a/assets/lang/ar_AE.ini +++ b/assets/lang/ar_AE.ini @@ -498,7 +498,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1207,7 +1206,7 @@ YYYYMMDD = ‎سنة شهر يوم [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/az_AZ.ini b/assets/lang/az_AZ.ini index 669ddeabd8..26b022eb25 100644 --- a/assets/lang/az_AZ.ini +++ b/assets/lang/az_AZ.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/bg_BG.ini b/assets/lang/bg_BG.ini index b730dd6242..1c2ba43a4e 100644 --- a/assets/lang/bg_BG.ini +++ b/assets/lang/bg_BG.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = ггггммдд [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ca_ES.ini b/assets/lang/ca_ES.ini index 5c222da16f..1f823575e8 100644 --- a/assets/lang/ca_ES.ini +++ b/assets/lang/ca_ES.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/cz_CZ.ini b/assets/lang/cz_CZ.ini index 6f3cbd0efe..6682c46ee5 100644 --- a/assets/lang/cz_CZ.ini +++ b/assets/lang/cz_CZ.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Zakázat pomalejší efekty (zrychlení) Disabled = Disabled Display layout editor = Zobrazit editor rozvržení Display Resolution (HW scaler) = Rozlišení obrazovky (Hardwarové zvětšení) @@ -1199,7 +1198,7 @@ YYYYMMDD = RRRRMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/da_DK.ini b/assets/lang/da_DK.ini index 23d3c0a24d..406dee1abd 100644 --- a/assets/lang/da_DK.ini +++ b/assets/lang/da_DK.ini @@ -490,7 +490,6 @@ Deposterize Tip = Retter visuel banding fejl in opskalerede textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Deaktiver langsommere effekter (hurtigere) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Skærmopløsning (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/de_DE.ini b/assets/lang/de_DE.ini index 0cba36ee2e..0903a464a1 100644 --- a/assets/lang/de_DE.ini +++ b/assets/lang/de_DE.ini @@ -57,7 +57,7 @@ Gesture = Geste Gesture mapping = Gesten mapping Glowing borders = Leuchtende Ränder HapticFeedback = Haptisches Feedback (Vibration) -Hide touch analog stick background circle = Verstecke Kreis des touch analog Stick Hintergrundes +Hide touch analog stick background circle = Verstecke Kreis des touch analog Stick Hintergrundes Icon = Symbol Ignore gamepads when not focused = Ignoriere Gamepads falls nicht fokusiert Ignore Windows Key = Ignoriere Windowstaste @@ -490,7 +490,6 @@ Deposterize Tip = Behebt visuelle Artifakte von hochskalierten Texturen Device = Gerät Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Deaktiviere langsame Effekte (schneller) Disabled = Deaktiviert Display layout editor = Bildschirmlayout Editor Display Resolution (HW scaler) = Bildschirmauflösung (HW Skalierung) @@ -539,7 +538,7 @@ Postprocessing effect = Nachbearbeitungs-Effekte Postprocessing Shader = Nachbearbeitungs-Shader Recreate Activity = Recreate activity Render duplicate frames to 60hz = Render duplicate frames to 60 Hz -RenderDuplicateFrames Tip = Kann Framerate smoother in Spielen machen, die eine niedrige Framerate haben. +RenderDuplicateFrames Tip = Kann Framerate smoother in Spielen machen, die eine niedrige Framerate haben. Rendering Mode = Rendermodus Rendering Resolution = Renderauflösung RenderingMode NonBuffered Tip = Schneller, aber keine Anzeige in einigen Spielen @@ -720,9 +719,9 @@ Wlan = WLAN Already contains PSP data = Enthält bereits PSP Daten Create or Choose a PSP folder = Create or Choose a PSP folder Current = Current -DataCanBeShared = Daten können mit PPSSPP Regulär/Gold geteilt werden! -DataCannotBeShared = Daten können NICHT mit Regulär/Gold geteilt werden! -DataWillBeLostOnUninstall = Warnung! Dateien gehen verloren beim deinstallieren von PPSSPP. +DataCanBeShared = Daten können mit PPSSPP Regulär/Gold geteilt werden! +DataCannotBeShared = Daten können NICHT mit Regulär/Gold geteilt werden! +DataWillBeLostOnUninstall = Warnung! Dateien gehen verloren beim deinstallieren von PPSSPP. DataWillStay = Dateien gehen NICHT verloren beim deinstallieren von PPSSPP. Done! = Done! EasyUSBAccess = Easy USB access @@ -1199,7 +1198,7 @@ YYYYMMDD = JJJJMMTT [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/dr_ID.ini b/assets/lang/dr_ID.ini index 7b429a9621..d0176fe3a9 100644 --- a/assets/lang/dr_ID.ini +++ b/assets/lang/dr_ID.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index 2d696f4ee4..b3389a229b 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -514,7 +514,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) diff --git a/assets/lang/es_ES.ini b/assets/lang/es_ES.ini index 7111db4d95..a251e5cbb7 100644 --- a/assets/lang/es_ES.ini +++ b/assets/lang/es_ES.ini @@ -490,7 +490,6 @@ Deposterize Tip = Arregla pequeños errores en las texturas causadas por el esca Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desactivar efectos más lentos (rápido) Disabled = Desactivado Display layout editor = Editor del área de pantalla Display Resolution (HW scaler) = Resolución de pantalla (escalado por hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = Año/Mes/Día [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/es_LA.ini b/assets/lang/es_LA.ini index 0ec79ab54e..46ad0c39e3 100644 --- a/assets/lang/es_LA.ini +++ b/assets/lang/es_LA.ini @@ -29,7 +29,7 @@ Auto = Automática Auto-centering analog stick = Auto-centrado del análogo Auto-hide buttons after seconds = Ocultar botones después de (en segundos) Auto-switch = Auto-switch -Base tilt position = Posición de inclinación de la base +Base tilt position = Posición de inclinación de la base Binds = Asignaciones Button Binding = Vincular botones Button Opacity = Transparencia de botones @@ -90,7 +90,7 @@ Screen Rotation = Rotación de pantalla seconds, 0 : off = Segundos, 0 = No Sensitivity (scale) = Sensibilidad (escala) Sensitivity = Sensibilidad -Shape = Forma +Shape = Forma Show Touch Pause Menu Button = Mostrar botón del menú Swipe = Deslizar hacia Swipe sensitivity = Sensibilidad del deslizado @@ -101,7 +101,7 @@ Tilt Input Type = Tipo de control del acelerómetro Tilt Sensitivity along X axis = Sensibilidad de inclinación del eje X Tilt Sensitivity along Y axis = Sensibilidad de inclinación del eje Y To Calibrate = Para calibrar, pon el aparato en una superficie plana y presiona "Calibrar". -Toggle mode = Modo de alternancia +Toggle mode = Modo de alternancia Touch Control Visibility = Visibilidad de pantalla táctil Use custom right analog = Usar Analog derecho personalizado Use Mouse Control = Usar el control del ratón @@ -490,7 +490,6 @@ Deposterize Tip = Arregla glitches de banda visual en texturas causadas por el e Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desactivar efectos más lentos (rápido) Disabled = Apagado Display layout editor = Editor del área de pantalla Display Resolution (HW scaler) = Resolución de pantalla (escalado por HW) @@ -895,7 +894,7 @@ tools = Software libre utilizado: # Add translators1-6 for up to 6 lines of translator credits. # Leave extra lines blank. 4 contributors per line seems to look best. translators1 = VR0, KAGE-008 -translators2 = +translators2 = translators3 = translators4 = translators5 = @@ -1199,7 +1198,7 @@ YYYYMMDD = Año/Mes/Día [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/fa_IR.ini b/assets/lang/fa_IR.ini index 6f1d4f06ad..4ea5d675dc 100644 --- a/assets/lang/fa_IR.ini +++ b/assets/lang/fa_IR.ini @@ -490,7 +490,6 @@ Deposterize Tip = ‎مشکل نواری شدن در تکسچر های تغیی Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = ‎غیر فعال کردن افکت های کند (افزایش سرعت) Disabled = Disabled Display layout editor = ‎ویرایشگر چیدمان صفحه Display Resolution (HW scaler) = ‎رزولوشن صفحه @@ -1199,7 +1198,7 @@ YYYYMMDD = ‎روز/ماه/سال [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/fi_FI.ini b/assets/lang/fi_FI.ini index c0d6038c50..94dd50c642 100644 --- a/assets/lang/fi_FI.ini +++ b/assets/lang/fi_FI.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/fr_FR.ini b/assets/lang/fr_FR.ini index eb2beec75c..6c28c420e0 100644 --- a/assets/lang/fr_FR.ini +++ b/assets/lang/fr_FR.ini @@ -490,7 +490,6 @@ Deposterize Tip = Corrige le bug graphique qui fait apparaître des bandes dans Device = Appareil Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Désactiver les effets les plus lourds (gain de vitesse) Disabled = Désactivé Display layout editor = Éditeur d'affichage Display Resolution (HW scaler) = Définition d'affichage (mise à l'échelle matérielle) @@ -1190,7 +1189,7 @@ YYYYMMDD = AAAAMMJJ [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/gl_ES.ini b/assets/lang/gl_ES.ini index 767b24ea0a..f644af1246 100644 --- a/assets/lang/gl_ES.ini +++ b/assets/lang/gl_ES.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desactivar efectos máis lentos (rápido) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Resolución de pantalla (escalado por hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = AAAA/MM/DD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/gr_EL.ini b/assets/lang/gr_EL.ini index e8f8413fe9..51df0a1cd9 100644 --- a/assets/lang/gr_EL.ini +++ b/assets/lang/gr_EL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Διορθώνει μικρές ατέλειες υφών με Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Απενεργοποίηση χρονοβόρων εφέ (άυξηση ταχύτητας) Disabled = Απενεργοποιημένο Display layout editor = Επεξεργασία διάταξης οθόνης Display Resolution (HW scaler) = Ανάλυση οθόνης (Κλιμακοτής hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = ΧΧΧΧΜΜΗΗ [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/he_IL.ini b/assets/lang/he_IL.ini index ceee0141a9..95cda4cb26 100644 --- a/assets/lang/he_IL.ini +++ b/assets/lang/he_IL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = יום/חודש/שנה [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/he_IL_invert.ini b/assets/lang/he_IL_invert.ini index e93e0eedbc..ee1ed7aced 100644 --- a/assets/lang/he_IL_invert.ini +++ b/assets/lang/he_IL_invert.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = הנש/שדוח/םוי [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/hr_HR.ini b/assets/lang/hr_HR.ini index c2ecfa8ffa..8238d63b17 100644 --- a/assets/lang/hr_HR.ini +++ b/assets/lang/hr_HR.ini @@ -490,7 +490,6 @@ Deposterize Tip = Popravlja vizualne banding glitcheve u podignutim teksturama Device = Uređaj Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Isključite sporije efekte (ubrzava) Disabled = Isključeno Display layout editor = Prikaz layout uređivača Display Resolution (HW scaler) = Prikaz rezolucije (HW mjeritelj) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/hu_HU.ini b/assets/lang/hu_HU.ini index dabbdcc906..13078c4f7f 100644 --- a/assets/lang/hu_HU.ini +++ b/assets/lang/hu_HU.ini @@ -490,7 +490,6 @@ Deposterize Tip = Skálázásból eredő kisebb textúra hibák megszüntetése. Device = Eszköz Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Lassú effektek letiltása (gyorsítás) Disabled = Letiltva Display layout editor = Elrendezés szerkesztő Display Resolution (HW scaler) = Megjelenítési felbontás (HW skálázó) @@ -1199,7 +1198,7 @@ YYYYMMDD = ÉÉÉÉHHNN [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/id_ID.ini b/assets/lang/id_ID.ini index fb6c9b376b..00af881bf1 100644 --- a/assets/lang/id_ID.ini +++ b/assets/lang/id_ID.ini @@ -490,7 +490,6 @@ Deposterize Tip = Memperbaiki gangguan pita visual pada tekstur yang ditingkatka Device = Perangkat Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Matikan efek lambat (mempercepat) Disabled = Nonaktif Display layout editor = Penyesuaian tata letak tampilan Display Resolution (HW scaler) = Resolusi tampilan (penskala HW) @@ -725,7 +724,7 @@ DataCannotBeShared = Data tidak bisa dibagi antar PPSSPP reguler/emas! DataWillBeLostOnUninstall = Peringatan! Data akan hilang ketika Anda menghapus PPSSPP! DataWillStay = Data akan tetap ada meskipun Anda menghapus PPSSPP. Done! = Selesai! -EasyUSBAccess = Akses mudah USB +EasyUSBAccess = Akses mudah USB Failed to move some files! = Gagal memindahkan beberapa berkas! Failed to save config = Gagal menyimpan konfigurasi Free space = Penyimpanan bebas @@ -746,7 +745,7 @@ Welcome to PPSSPP! = Selamat Datang di PPSSPP! WhatsThis = Apa ini? [Networking] -AdHoc Server = Server Ad Hoc +AdHoc Server = Server Ad Hoc AdhocServer Failed to Bind Port = Server Ad Hoc gagal memuat port AM: Data from Unknown Port = AM: Data dari port tidak dikenal Auto = Otomatis @@ -791,7 +790,7 @@ Network Initialized = Jaringan terinisiasi None = Tidak ada Please change your Port Offset = Harap ubah letak port Anda Port offset = Letak port (0 = kecocokan dengan PSP) -Open PPSSPP Multiplayer Wiki Page = Halaman Wiki PPSSPP Ad-Hoc +Open PPSSPP Multiplayer Wiki Page = Halaman Wiki PPSSPP Ad-Hoc proAdhocServer Address: = Ad Hoc alamat server: Quick Chat 1 = Obrolan cepat 1 Quick Chat 2 = Obrolan cepat 2 @@ -842,7 +841,7 @@ Cartoon = Kartun CatmullRom = Bicubic (Catmull-Rom) Upscaler ColorCorrection = Koreksi Warna Contrast = Kontras -CRT = CRT +CRT = CRT FXAA = Mengurangi Artifak Distorsi (FXAA) Gamma = Gamma Grayscale = Abu-abu @@ -853,7 +852,7 @@ Natural = Warna Natural NaturalA = Warna Natural (tidak kabur) Off = Mati Power = Daya -PSPColor = Warna PSP +PSPColor = Warna PSP Saturation = Saturasi Scanlines = Pindai Garis (CRT) Sharpen = Pertajam @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTT/BB/HH [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/it_IT.ini b/assets/lang/it_IT.ini index ab827b38b7..849b469c39 100644 --- a/assets/lang/it_IT.ini +++ b/assets/lang/it_IT.ini @@ -49,7 +49,7 @@ Customize tilt = Personalizza Inclinazione D-PAD = Croce direzionale Deadzone radius = Raggio della zona morta Disable D-Pad diagonals (4-way touch) = Disattiva Diagonali del D-Pad (Tocco a 4-Vie) -Disable diagonal input = Disabilita input diagonale +Disable diagonal input = Disabilita input diagonale Double tap = Doppio tocco Enable gesture control = Abilita controllo gesti Enable standard shortcut keys = Abilita scorciatoie standard @@ -490,7 +490,6 @@ Deposterize Tip = Sistema i possibili problemi visivi nelle texture upscalate Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disattiva gli effetti più lenti (veloce) Disabled = Disabilitato Display layout editor = Editor Display Display Resolution (HW scaler) = Risoluzione Display (scaler HW) @@ -1199,7 +1198,7 @@ YYYYMMDD = AAAAMMGG [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ja_JP.ini b/assets/lang/ja_JP.ini index b4fdb34460..10c66c47f1 100644 --- a/assets/lang/ja_JP.ini +++ b/assets/lang/ja_JP.ini @@ -490,7 +490,6 @@ Deposterize Tip = アップスケールされたテクスチャのバンディ Device = デバイス Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 重いエフェクトを無効にする (高速化) Disabled = 無効 Display layout editor = 画面のレイアウトを編集する Display Resolution (HW scaler) = 画面解像度 (HWスケーラー) @@ -1199,7 +1198,7 @@ YYYYMMDD = 年/月/日 [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/jv_ID.ini b/assets/lang/jv_ID.ini index 0e9d0b5bc4..016ae0e857 100644 --- a/assets/lang/jv_ID.ini +++ b/assets/lang/jv_ID.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Mateni efek alon (luwih cepet) Disabled = Disabled Display layout editor = Tata letak Tampilan editor Display Resolution (HW scaler) = Resolusi tampilan (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTTBBHH [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ko_KR.ini b/assets/lang/ko_KR.ini index b83bb8a381..d08d55dd14 100644 --- a/assets/lang/ko_KR.ini +++ b/assets/lang/ko_KR.ini @@ -490,7 +490,6 @@ Deposterize Tip = 업스케일링된 텍스처의 시각적 밴딩 결함 수정 Device = 장치 Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 느린 효과 비활성화 (속도 향상) Disabled = 비활성화 Display layout editor = 화면 레이아웃 편집기 Display Resolution (HW scaler) = 화면 해상도(HW 스케일러) diff --git a/assets/lang/lo_LA.ini b/assets/lang/lo_LA.ini index 805339cc3f..dad15456b5 100644 --- a/assets/lang/lo_LA.ini +++ b/assets/lang/lo_LA.ini @@ -490,7 +490,6 @@ Deposterize Tip = ແກ້ໄຂການສະແດງຜົນຜິດພ Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = ປິດເອັບເຟກທີ່ເຮັດໃຫ້ເກມຊ້າ (ໄວຂຶ້ນ) Disabled = Disabled Display layout editor = ແກ້ໄຂຮູບແບບໜ້າຈໍ Display Resolution (HW scaler) = ຄວາມລະອຽດໜ້າຈໍ (ຕາມຮາດແວຣ໌) @@ -1199,7 +1198,7 @@ YYYYMMDD = ປີ/ເດືອນ/ວັນ [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/lt-LT.ini b/assets/lang/lt-LT.ini index 2c99319418..917e171242 100644 --- a/assets/lang/lt-LT.ini +++ b/assets/lang/lt-LT.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Išjungti lėtesnius efektus (greičio didintojas) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Ekrano rezoliucija ("HW" ištiesinimas) @@ -1199,7 +1198,7 @@ YYYYMMDD = MMMMMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ms_MY.ini b/assets/lang/ms_MY.ini index 598c4bdf18..5a6687e035 100644 --- a/assets/lang/ms_MY.ini +++ b/assets/lang/ms_MY.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTTBBHH [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/nl_NL.ini b/assets/lang/nl_NL.ini index 60d35500c7..ad38f550cb 100644 --- a/assets/lang/nl_NL.ini +++ b/assets/lang/nl_NL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Verhelpt visuele streepglitches in opgeschaalde textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Tragere effecten uitschakelen (sneller) Disabled = Disabled Display layout editor = Schermweergave bewerken Display Resolution (HW scaler) = Schermresolutie (hardware) @@ -1199,7 +1198,7 @@ YYYYMMDD = JJJJMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/no_NO.ini b/assets/lang/no_NO.ini index 603cf5280f..70163c25c0 100644 --- a/assets/lang/no_NO.ini +++ b/assets/lang/no_NO.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Disable slower effects (speedup) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/pl_PL.ini b/assets/lang/pl_PL.ini index 38810bf550..4493f6db83 100644 --- a/assets/lang/pl_PL.ini +++ b/assets/lang/pl_PL.ini @@ -490,7 +490,6 @@ Deposterize Tip = Poprawia banding koloru na przeskalowanych teksturach Device = Urządzenie Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Wył. wolniejsze efekty (przyśpieszenie) Disabled = Wył. Display layout editor = Edytor położenia obrazu Display Resolution (HW scaler) = Rozdzielczość ekranu (skaler sprz.) @@ -1199,7 +1198,7 @@ YYYYMMDD = RRRR/MM/DD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/pt_BR.ini b/assets/lang/pt_BR.ini index 2e1796deb0..6d5b63cdcf 100644 --- a/assets/lang/pt_BR.ini +++ b/assets/lang/pt_BR.ini @@ -514,7 +514,6 @@ Deposterize Tip = Conserta erros gráficos visuais das faixas nas texturas ampli Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desativa efeitos mais lentos (mais rápido) Disabled = Desativado Display layout editor = Exibe o editor dos esquemas Display Resolution (HW scaler) = Resolução da tela (Dimensionador do hardware) @@ -891,10 +890,10 @@ tools = Ferramentas grátis usadas: # Leave extra lines blank. 4 contributors per line seems to look best. translators1 = Papel, gabrielmop, Efraim Lopes, AkiraJkr translators2 = Felipe -translators3 = -translators4 = -translators5 = -translators6 = +translators3 = +translators4 = +translators5 = +translators6 = Twitter @PPSSPP_emu = Twitter @PPSSPP_emu website = Verifique o site da web: written = Escrito em C++ pela velocidade e portabilidade diff --git a/assets/lang/pt_PT.ini b/assets/lang/pt_PT.ini index 54bc9d92f4..b50d6f0d95 100644 --- a/assets/lang/pt_PT.ini +++ b/assets/lang/pt_PT.ini @@ -514,7 +514,6 @@ Deposterize Tip = Conserta erros gráficos visuais das faixas nas texturas ampli Device = Dispositivo Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Desativa efeitos mais lentos (mais rápido) Disabled = Desativado Display layout editor = Mostrar o editor dos esquemas Display Resolution (HW scaler) = Resolução da tela (Dimensionador do hardware) @@ -890,11 +889,11 @@ tools = Ferramentas gratuitas usadas: # Add translators1-6 for up to 6 lines of translator credits. # Leave extra lines blank. 4 contributors per line seems to look best. translators1 = MicSuit -translators2 = -translators3 = -translators4 = -translators5 = -translators6 = +translators2 = +translators3 = +translators4 = +translators5 = +translators6 = Twitter @PPSSPP_emu = Twitter @PPSSPP_emu website = Verifique o website: written = PPSSPP foi escrito em C++ pela sua velocidade e portabilidade diff --git a/assets/lang/ro_RO.ini b/assets/lang/ro_RO.ini index 46e5b6e9d4..0ec247f441 100644 --- a/assets/lang/ro_RO.ini +++ b/assets/lang/ro_RO.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Dezactivare efecte lente (mărire viteză) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Rezoluție ecran (scalare HW) @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/ru_RU.ini b/assets/lang/ru_RU.ini index 09f1af9598..35b73386d5 100644 --- a/assets/lang/ru_RU.ini +++ b/assets/lang/ru_RU.ini @@ -490,7 +490,6 @@ Deposterize Tip = Исправляет полосатость в масштаб Device = Устройство Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Отключить медленные эффекты (быстрее) Disabled = Отключено Display layout editor = Редактор расположения экрана Display Resolution (HW scaler) = Разрешение экрана (аппаратное) @@ -1200,7 +1199,7 @@ YYYYMMDD = ГГГГММДД [TextureShaders] Off = Выкл. TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index ba6b4e043a..fbacbb1219 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Enhet Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Stäng av långsamma effects (speedup) Disabled = Avstängd Display layout editor = Skärmlayouteditor Display Resolution (HW scaler) = Skärmupplösning (HW scaler) @@ -1095,8 +1094,8 @@ AVI Dump started. = AVI dump started AVI Dump stopped. = AVI dump stopped Cache ISO in RAM = Cachea hela ISO:n i RAM Change CPU Clock = Ändra CPU-frekvens (kan orsaka instabilitet) -Color Saturation = Color Saturation -Color Tint = Color Tint +Color Saturation = Färgmättnad +Color Tint = Färgförskjutning Error: load undo state is from a different game = Error: load undo state is from a different game Failed to load state for load undo. Error in the file system. = Failed to load state for load undo. Error in the file system. Floating symbols = Floating symbols @@ -1199,7 +1198,7 @@ YYYYMMDD = YYYYMMDD [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/tg_PH.ini b/assets/lang/tg_PH.ini index 9ad12dc737..5afd25561b 100644 --- a/assets/lang/tg_PH.ini +++ b/assets/lang/tg_PH.ini @@ -18,7 +18,7 @@ Use global volume = Paggamit ng pangkalahatang tunog WASAPI (fast) = WASAPI (Mabilis) [Controls] -Analog Binding = Bigkisin ang analog +Analog Binding = Bigkisin ang analog Analog Limiter = Taga limit ng analog Analog Settings = Ayusin ang analog Analog Stick = Analog stick @@ -490,7 +490,6 @@ Deposterize Tip = Fixes visual banding glitches in upscaled textures Device = Device Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Wag paganahin ang slower effects (pampa-bilis) Disabled = Disabled Display layout editor = Display layout editor Display Resolution (HW scaler) = Display resolution (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = TTTTBBPP [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/th_TH.ini b/assets/lang/th_TH.ini index c4340eae45..c63fa3125b 100644 --- a/assets/lang/th_TH.ini +++ b/assets/lang/th_TH.ini @@ -490,7 +490,6 @@ Deposterize Tip = แก้ไขขอบภาพเบลอ หรือภ Device = การ์ดจอ Direct3D 9 = ไดเร็คท์ 3D 9 Direct3D 11 = ไดเร็คท์ 3D 11 -Disable slower effects (speedup) = ปิดเอฟเฟ็คท์ที่ทำให้เกมช้า (เร่งความเร็ว) Disabled = ปิดการใช้งาน Display layout editor = แก้ไขรูปแบบของหน้าจอ Display Resolution (HW scaler) = ความละเอียดหน้าจอ (ตามฮาร์ดแวร์) diff --git a/assets/lang/tr_TR.ini b/assets/lang/tr_TR.ini index 761b6598bf..d6c04817fd 100644 --- a/assets/lang/tr_TR.ini +++ b/assets/lang/tr_TR.ini @@ -492,7 +492,6 @@ Deposterize Tip = Yeniden ölçeklenen dokulardaki görsel şerit hatalarını d Device = Cihaz Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Daha yavaş efektleri kapatın (daha hızlı) Disabled = Devre dışı Display layout editor = Görüntü Düzeni Düzenleyicisi Display Resolution (HW scaler) = Görüntü Çözünürlüğü (HW scaler) @@ -1200,7 +1199,7 @@ YYYYMMDD = YYYYAAGG [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/uk_UA.ini b/assets/lang/uk_UA.ini index c25ba59a58..b6ec199493 100644 --- a/assets/lang/uk_UA.ini +++ b/assets/lang/uk_UA.ini @@ -490,7 +490,6 @@ Deposterize Tip = Виправляє візуальні глюки в масшт Device = Пристрій Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Вимкнути повільні ефекти (прискорення) Disabled = Вимкнено Display layout editor = Редактор розташування екрану Display Resolution (HW scaler) = Розширення екрану (HW масштабування) @@ -1199,7 +1198,7 @@ YYYYMMDD = РРРРММДД [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/vi_VN.ini b/assets/lang/vi_VN.ini index 040f2f8540..40e6bb7b30 100644 --- a/assets/lang/vi_VN.ini +++ b/assets/lang/vi_VN.ini @@ -490,7 +490,6 @@ Deposterize Tip = Sửa lỗi trục trặc hình ảnh trong textures Device = Thiết bị Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = Tắt hiệu ứng làm chậm (tăng tốc) Disabled = Vô hiệu hóa Display layout editor = Chỉnh bố trí hiển thị Display Resolution (HW scaler) = Độ phân giải màn hình (HW scaler) @@ -1199,7 +1198,7 @@ YYYYMMDD = NamThangNgay [TextureShaders] Off = Off TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] diff --git a/assets/lang/zh_CN.ini b/assets/lang/zh_CN.ini index 93ba6ebe35..486a2d5230 100644 --- a/assets/lang/zh_CN.ini +++ b/assets/lang/zh_CN.ini @@ -490,7 +490,6 @@ Deposterize Tip = 修复纹理被放大时可见的带状错误 Device = 设备 Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 停用较慢的渲染效果(提速) Disabled = 禁用 Display layout editor = 显示布局编辑器 Display Resolution (HW scaler) = 显示分辨率(硬件缩放) @@ -1193,7 +1192,7 @@ Show Memory Stick folder = 显示记忆棒文件夹 [TextureShaders] Off = 关闭 TexMMPX = TexMMPX -Tex2xBRZ = Tex2xBRZ +Tex2xBRZ = Tex2xBRZ Tex4xBRZ = Tex4xBRZ [Themes] @@ -1222,4 +1221,4 @@ Filtering settings by '%1' = 设置筛选'%1' Find settings = 搜索设置 Filter = 筛选 Clear filter = 清除筛选结果 -No settings matched '%1' = 未找到设置'%1' +No settings matched '%1' = 未找到设置'%1' diff --git a/assets/lang/zh_TW.ini b/assets/lang/zh_TW.ini index 86670da9e3..3559284b19 100644 --- a/assets/lang/zh_TW.ini +++ b/assets/lang/zh_TW.ini @@ -494,7 +494,6 @@ Deposterize Tip = 修復在紋理被放大時可能發生的小紋理內部毛 Device = 設備 Direct3D 9 = Direct3D 9 Direct3D 11 = Direct3D 11 -Disable slower effects (speedup) = 停用較慢的效果(提升速度) Disabled = 已禁用 Display layout editor = 自訂版面 Display Resolution (HW scaler) = 顯示解析度(硬體縮放) diff --git a/ext/gason/gason.h b/ext/gason/gason.h index 922751099f..cddb3615a7 100644 --- a/ext/gason/gason.h +++ b/ext/gason/gason.h @@ -114,10 +114,10 @@ public: JsonAllocator() : head(nullptr) {}; JsonAllocator(const JsonAllocator &) = delete; JsonAllocator &operator=(const JsonAllocator &) = delete; - JsonAllocator(JsonAllocator &&x) : head(x.head) { + JsonAllocator(JsonAllocator &&x) noexcept : head(x.head) { x.head = nullptr; } - JsonAllocator &operator=(JsonAllocator &&x) { + JsonAllocator &operator=(JsonAllocator &&x) noexcept { head = x.head; x.head = nullptr; return *this; diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index eacbca25e5..9b4a20bd97 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -379,7 +379,7 @@ class LibretroHost : public Host void InitSound() override {} void UpdateSound() override { - extern int hostAttemptBlockSize; + int hostAttemptBlockSize = __AudioGetHostAttemptBlockSize(); const int blockSizeMax = 512; static int16_t audio[blockSizeMax * 2]; assert(hostAttemptBlockSize <= blockSizeMax); @@ -563,7 +563,6 @@ static RetroOption ppsspp_lazy_texture_caching("ppsspp_lazy_texture_cachin static RetroOption ppsspp_retain_changed_textures("ppsspp_retain_changed_textures", "Retain changed textures (Speedup, mem hog)", false); static RetroOption ppsspp_force_lag_sync("ppsspp_force_lag_sync", "Force real clock sync (Slower, less lag)", false); static RetroOption ppsspp_spline_quality("ppsspp_spline_quality", "Spline/Bezier curves quality", { {"Low", 0}, {"Medium", 1}, {"High", 2} }); -static RetroOption ppsspp_disable_slow_framebuffer_effects("ppsspp_disable_slow_framebuffer_effects", "Disable slower effects (Speedup)", false); static RetroOption ppsspp_enable_wlan("ppsspp_enable_wlan", "Enable Networking/WLAN (beta, may break games)", false); static RetroOption ppsspp_change_mac_address[] = { {"ppsspp_change_mac_address01", "MAC address Pt 1: X-:--:--:--:--:--", MAC_INITIALIZER_LIST}, @@ -699,7 +698,6 @@ void retro_set_environment(retro_environment_t cb) vars.push_back(ppsspp_lazy_texture_caching.GetOptions()); vars.push_back(ppsspp_retain_changed_textures.GetOptions()); vars.push_back(ppsspp_force_lag_sync.GetOptions()); - vars.push_back(ppsspp_disable_slow_framebuffer_effects.GetOptions()); vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions()); vars.push_back(ppsspp_texture_scaling_level.GetOptions()); vars.push_back(ppsspp_texture_scaling_type.GetOptions()); @@ -831,7 +829,6 @@ static void check_variables(CoreParameter &coreParam) ppsspp_retain_changed_textures.Update(&g_Config.bTextureSecondaryCache); ppsspp_force_lag_sync.Update(&g_Config.bForceLagSync); ppsspp_spline_quality.Update(&g_Config.iSplineBezierQuality); - ppsspp_disable_slow_framebuffer_effects.Update(&g_Config.bDisableSlowFramebufEffects); ppsspp_inflight_frames.Update(&g_Config.iInflightFrames); const bool do_scaling_type_update = ppsspp_texture_scaling_type.Update(&g_Config.iTexScalingType); const bool do_scaling_level_update = ppsspp_texture_scaling_level.Update(&g_Config.iTexScalingLevel); diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp index 28ab16e2c4..d90d893ff3 100644 --- a/unittest/TestShaderGenerators.cpp +++ b/unittest/TestShaderGenerators.cpp @@ -102,7 +102,7 @@ bool TestCompileShader(const char *buffer, ShaderLanguage lang, ShaderStage stag } case ShaderLanguage::HLSL_D3D9: { - LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_2_0" : "ps_2_0", errorMessage); + LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_3_0" : "ps_3_0", errorMessage); if (blob) { blob->Release(); return true; diff --git a/unittest/TestSoftwareGPUJit.cpp b/unittest/TestSoftwareGPUJit.cpp index 23292ed214..593ac69f41 100644 --- a/unittest/TestSoftwareGPUJit.cpp +++ b/unittest/TestSoftwareGPUJit.cpp @@ -50,6 +50,8 @@ static bool TestSamplerJit() { u8 **tptr = new u8 *[8]; int *bufw = new int[8]; u8 *clut = new u8[1024]; + memset(clut, 0, 1024); + for (int i = 0; i < 8; ++i) { tptr[i] = new u8[1024 * 1024 * 4]; memset(tptr[i], 0, 1024 * 1024 * 4); @@ -119,6 +121,8 @@ static bool TestPixelJit() { u16 *zb_data = new u16[512 * 2]; fb.as32 = fb_data; depthbuf.as16 = zb_data; + memset(fb_data, 0, sizeof(u32) * 512 * 2); + memset(zb_data, 0, sizeof(u16) * 512 * 2); for (int i = 0; i < count; ) { PixelFuncID id; diff --git a/unittest/TestVertexJit.cpp b/unittest/TestVertexJit.cpp index 17e63651d2..573247ba38 100644 --- a/unittest/TestVertexJit.cpp +++ b/unittest/TestVertexJit.cpp @@ -45,8 +45,8 @@ public: gstate_c.uv.vScale = 1.0f; } ~VertexDecoderTestHarness() { - delete src_; - delete dst_; + delete [] src_; + delete [] dst_; delete cache_; delete dec_; } @@ -400,7 +400,7 @@ static bool TestVertexFloatThrough() { dec.Execute(vtype, 0, jit == 1); dec.AssertFloat("TestVertexFloatThrough-TC", 1.0f, -1.0f); dec.AssertFloat("TestVertexFloatThrough-Nrm", 1.0f, 0.5f, -1.0f); - dec.AssertFloat("TestVertexFloatThrough-Pos", 1.0f, 0.5f, -1.0f); + dec.AssertFloat("TestVertexFloatThrough-Pos", 1.0f, 0.5f, 0.0f); } return !dec.HasFailed(); diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 78a08515d6..4f8239a11b 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -40,6 +40,7 @@ #include #endif +#include "Common/Data/Collections/TinySet.h" #include "Common/Data/Text/Parsers.h" #include "Common/Data/Text/WrapText.h" #include "Common/Data/Encoding/Utf8.h" @@ -308,6 +309,44 @@ bool TestParsers() { return true; } +bool TestTinySet() { + TinySet a; + EXPECT_EQ_INT((int)a.size(), 0); + a.push_back(1); + EXPECT_EQ_INT((int)a.size(), 1); + a.push_back(2); + EXPECT_EQ_INT((int)a.size(), 2); + TinySet b; + b.push_back(8); + b.push_back(9); + b.push_back(10); + EXPECT_EQ_INT((int)b.size(), 3); + + a.append(b); + EXPECT_EQ_INT((int)a.size(), 5); + EXPECT_EQ_INT((int)b.size(), 3); + + b.append(b); + EXPECT_EQ_INT((int)b.size(), 6); + + EXPECT_EQ_INT(a[0], 1); + EXPECT_EQ_INT(a[1], 2); + EXPECT_EQ_INT(a[2], 8); + EXPECT_EQ_INT(a[3], 9); + EXPECT_EQ_INT(a[4], 10); + a.append(a); + EXPECT_EQ_INT(a.size(), 10); + EXPECT_EQ_INT(a[9], 10); + + b.push_back(11); + EXPECT_EQ_INT((int)b.size(), 7); + b.push_back(12); + EXPECT_EQ_INT((int)b.size(), 8); + b.push_back(13); + EXPECT_EQ_INT(b.size(), 9); + return true; +} + bool TestVFPUSinCos() { float sine, cosine; InitVFPUSinCos(); @@ -792,6 +831,7 @@ TestItem availableTests[] = { TEST_ITEM(AndroidContentURI), TEST_ITEM(ThreadManager), TEST_ITEM(WrapText), + TEST_ITEM(TinySet), }; int main(int argc, const char *argv[]) {