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/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/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 880d18bd2b..b00ec67a01 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, @@ -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; @@ -1166,6 +1170,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VkPipeline pipeline = c.pipeline.pipeline; if (pipeline != lastGraphicsPipeline) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + pipelineLayout = c.pipeline.pipelineLayout; lastGraphicsPipeline = pipeline; // Reset dynamic state so it gets refreshed with the new pipeline. lastStencilWriteMask = -1; @@ -1187,6 +1192,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } if (pipeline->pipeline != lastGraphicsPipeline && pipeline->pipeline != VK_NULL_HANDLE) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); + pipelineLayout = c.pipeline.pipelineLayout; lastGraphicsPipeline = pipeline->pipeline; // Reset dynamic state so it gets refreshed with the new pipeline. lastStencilWriteMask = -1; @@ -1208,6 +1214,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c } if (pipeline->pipeline != lastComputePipeline && pipeline->pipeline != VK_NULL_HANDLE) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); + pipelineLayout = c.pipeline.pipelineLayout; lastComputePipeline = pipeline->pipeline; } break; @@ -1257,7 +1264,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 +1283,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); } @@ -1793,6 +1803,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..401a125d6b 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.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..ba2c64f758 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -836,6 +836,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 +1189,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..a541bea57e 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -242,29 +242,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 +354,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 +388,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 +404,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..d6cc587944 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1339,7 +1339,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 +1359,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 +1375,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/Core/Compatibility.cpp b/Core/Compatibility.cpp index 84d42df284..0740e41711 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); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index dc22c45e0f..97f2d48075 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; }; class IniFile; diff --git a/Core/Config.cpp b/Core/Config.cpp index 3c24061088..74aae44d36 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -935,7 +935,7 @@ 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("DisableSlowFramebufEffects", &g_Config.bDisableShaderBlending, 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..bed892a751 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -232,7 +232,7 @@ public: float fGameListScrollPosition; int iBloomHack; //0 = off, 1 = safe, 2 = balanced, 3 = aggressive bool bBlockTransferGPU; - bool bDisableSlowFramebufEffects; + bool bDisableShaderBlending; 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/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/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/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index a86deee9fe..cffedaeb46 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()); } @@ -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/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 899230142b..915baa9e81 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -657,8 +657,10 @@ int sceKernelUnlockMutex(SceUID id, int count) if (mutex->nm.lockLevel == 0) { - if (__KernelUnlockMutex(mutex, error)) + if (__KernelUnlockMutex(mutex, error)) { hleReSchedule("mutex unlocked"); + return hleDelayResult(0, "unlock", 150); + } } return 0; diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index 848bfa73b6..e623d9e768 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); 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..b55bb85cbd 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -165,10 +165,10 @@ protected: u32 vertexCount; u8 indexType; s8 prim; + u8 cullMode; u16 indexLowerBound; u16 indexUpperBound; UVScale uvScale; - int cullMode; }; enum { MAX_DEFERRED_DRAW_CALLS = 128 }; diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 77c8042435..8cf705cce5 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -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. diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index c9c49da6bb..00f5df9b07 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); @@ -886,7 +905,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 +916,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 +978,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 +1125,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 +1234,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 +1388,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 +1521,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 +1626,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 +1648,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 +1678,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 +1753,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 +1823,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 +1862,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 +2038,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 +2059,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 +2094,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 +2274,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 +2312,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 +2421,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 +2458,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 +2507,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 +2562,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 +2690,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 +2721,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. @@ -2859,3 +2883,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..a9f99f793f 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -581,8 +581,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 +612,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; 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..faa03ad261 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,27 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } } + if (plan.isVideo || isPPGETexture) { + plan.replaced = &replacer_.FindNone(); + plan.replaceValid = false; + } else { + plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); + plan.replaceValid = plan.replaced->Valid(); + } + + 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 { + 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 +2413,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) { diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 534a8dbbdb..e394923090 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,30 @@ 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; + + 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 +378,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/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index fd4365d34e..c6a4631636 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); diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 33725facb3..b80c18c2dc 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -125,10 +125,13 @@ public: case DEC_FLOAT_3: { const float *f = (const float *)(data_ + decFmt_.posoff); - memcpy(pos, f, 12); - if (isThrough()) { + 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. - const float z = (int)pos[2] * (1.0f / 65535.0f); + const float z = (int)f[2] * (1.0f / 65535.0f); pos[2] = z > 1.0f ? 1.0f : (z < 0.0f ? 0.0f : z); } } @@ -443,8 +446,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 +550,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 diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 561ea414b3..239d8def45 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -142,7 +142,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); // Blend if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { keys_.blend.value = 0; // full wipe keys_.blend.blendEnable = false; diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 7fee999b6f..baa8ee5e04 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; 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/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/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 1e4d1d348c..bcc2d23b72 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -119,8 +119,7 @@ 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); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { dxstate.blend.disable(); // Color Mask diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 9de670732d..0ef616914d 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; 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/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 5f9fe9d135..53fd0711b4 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -145,7 +145,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { // Color Test diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index b24b48283a..cf9a8e13af 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -301,37 +301,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 +358,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..9f7f98df48 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -592,8 +592,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 +621,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/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..60997e582a 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -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"); @@ -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/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index ac51debc6a..96c145e6f7 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -136,7 +136,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool useBufferedRendering = framebufferManager_->UseBufferedRendering(); if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects); + gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending); if (gstate.isModeClear()) { key.logicOpEnable = false; key.logicOp = VK_LOGIC_OP_CLEAR; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 76d2fbe71f..2f82eae187 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)); } @@ -503,7 +503,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { 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 +519,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) { @@ -537,7 +542,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { ReplacedTextureDecodeInfo replacedInfo; bool willSaveTex = false; - if (replacer_.Enabled() && plan.replaced->IsInvalid() && plan.depth == 1) { + if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { + // 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; @@ -563,15 +569,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. @@ -592,9 +597,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { }; 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 +609,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,7 +632,7 @@ 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); @@ -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/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 19ce717bf1..896a74dea5 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -508,7 +508,7 @@ void GameSettingsScreen::CreateViews() { }); texSecondary_->SetDisabledPtr(&g_Config.bSoftwareRendering); - CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableSlowFramebufEffects, gr->T("Disable slower effects (speedup)"))); + CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableShaderBlending, gr->T("Disable slower effects (speedup)"))); framebufferSlowEffects->SetDisabledPtr(&g_Config.bSoftwareRendering); // Seems solid, so we hide the setting. diff --git a/assets/compat.ini b/assets/compat.ini index 27b8fd571b..bbe058a707 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 @@ -1263,3 +1264,12 @@ UCKS45048 = true UCJS18030 = true UCJS18047 = true NPJG00015 = true + +[SplitFramebufferMargin] +# Killzone: Liberation (see issue #6207) +UCES00279 = true +UCKS45041 = true +UCUS98646 = true +UCET00278 = true +UCUS98670 = true +UCUS98646 = true diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index eacbca25e5..a5a720b7a7 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,7 @@ 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_disable_shader_blending("ppsspp_disable_slow_framebuffer_effects", "Disable shader blending (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 +699,7 @@ 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_disable_shader_blending.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 +831,7 @@ 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_disable_shader_blending.Update(&g_Config.bDisableShaderBlending); 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/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[]) {