Merge branch 'feature_openxr_stereo' into compat_openxr_gta

This commit is contained in:
Lubos committed 2022-09-04 16:33:33 +02:00
commit 93a2817062
181 files changed
+2593 -1898

No files matched your search

+58 -1
View File
@@ -3,10 +3,12 @@
#include <vector>
// 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 <class T, int MaxFastSize>
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<T>();
}
slowLookup_->push_back(t);
}
inline T *add_back() {
if (fastCount < MaxFastSize) {
return &fastLookup_[fastCount++];
}
if (!slowLookup_) {
slowLookup_ = new std::vector<T>();
}
T t;
slowLookup_->push_back(t);
return slowLookup_->back();
}
void append(const TinySet<T, MaxFastSize> &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) {
+6 -1
View File
@@ -258,8 +258,13 @@ std::wstring Path::ToWString() const {
std::string Path::ToVisualString() const {
if (type_ == PathType::CONTENT_URI) {
return AndroidContentURI(path_).ToVisualString();
#if PPSSPP_PLATFORM(WINDOWS)
} else if (type_ == PathType::NATIVE) {
return ReplaceAll(path_, "/", "\\");
#endif
} else {
return path_;
}
return path_;
}
bool Path::CanNavigateUp() const {
+2 -1
View File
@@ -275,10 +275,11 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
if (SUCCEEDED(result)) {
if (options.OutputMergerLogicOp) {
// Actually, need to check that the format supports logic ops as well.
// Which normal UNORM formats don't seem to do. So meh.
// Which normal UNORM formats don't seem to do in D3D11. So meh. We can't enable logicOp support.
// caps_.logicOpSupported = true;
}
}
IDXGIDevice* dxgiDevice = nullptr;
IDXGIAdapter* adapter = nullptr;
HRESULT hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
+2 -2
View File
@@ -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);
+1 -5
View File
@@ -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) {
+1
View File
@@ -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
+4 -2
View File
@@ -643,7 +643,7 @@ retry_depth:
currentReadHandle_ = fbo->handle;
}
void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls) {
void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps) {
if (skipGLCalls) {
// Dry run
for (size_t i = 0; i < steps.size(); i++) {
@@ -725,7 +725,9 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
glPopDebugGroup();
#endif
delete steps[i];
if (!keepSteps) {
delete steps[i];
}
}
CHECK_GL_ERROR_IF_DEBUG();
}
+1 -1
View File
@@ -363,7 +363,7 @@ public:
void RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls);
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls);
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls, bool keepSteps = false);
void LogSteps(const std::vector<GLRStep *> &steps);
void CreateDeviceObjects();
+22 -11
View File
@@ -5,6 +5,8 @@
#include "Common/Thread/ThreadUtil.h"
#include "Common/VR/PPSSPPVR.h"
#include "Core/Config.h"
#include "Common/Log.h"
#include "Common/MemoryUtil.h"
#include "Common/Math/math_util.h"
@@ -233,15 +235,7 @@ bool GLRenderManager::ThreadFrame() {
INFO_LOG(G3D, "Running first frame (%d)", threadFrame_);
firstFrame = false;
}
if (IsVRBuild()) {
if (PreVRRender()) {
Run(threadFrame_);
PostVRRender();
}
} else {
Run(threadFrame_);
}
Run(threadFrame_);
VLOG("PULL: Finished frame %d", threadFrame_);
} while (!nextFrame);
@@ -567,6 +561,7 @@ void GLRenderManager::EndSubmitFrame(int frame) {
void GLRenderManager::Run(int frame) {
BeginSubmitFrame(frame);
FrameData &frameData = frameData_[frame];
auto &stepsOnThread = frameData_[frame].steps;
@@ -583,8 +578,22 @@ void GLRenderManager::Run(int frame) {
}
}
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_);
stepsOnThread.clear();
if (IsVRBuild()) {
if (PreVRRender()) {
int passes = 1;
if (!IsMultiviewSupported() && g_Config.bEnableStereo) {
passes = 2;
}
for (int i = 0; i < passes; i++) {
PreVRFrameRender(i);
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1);
PostVRFrameRender();
}
PostVRRender();
}
} else {
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_);
}
if (!skipGLCalls_) {
for (auto iter : frameData.activePushBuffers) {
@@ -592,6 +601,8 @@ void GLRenderManager::Run(int frame) {
}
}
stepsOnThread.clear();
switch (frameData.type) {
case GLRRunType::END:
EndSubmitFrame(frame);
+3
View File
@@ -569,6 +569,9 @@ OpenGLContext::OpenGLContext() {
caps_.fragmentShaderDepthWriteSupported = true;
}
// GLES has no support for logic framebuffer operations. There doesn't even seem to exist any such extensions.
caps_.logicOpSupported = !gl_extensions.IsGLES;
// Interesting potential hack for emulating GL_DEPTH_CLAMP (use a separate varying, force depth in fragment shader):
// This will induce a performance penalty on many architectures though so a blanket enable of this
// is probably not a good idea.
+42 -42
View File
@@ -347,6 +347,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &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<VKRStep *> &steps) {
auto mergeRenderSteps = [](VKRStep *dst, VKRStep *src) {
// OK. Now, if it's a render, slurp up all the commands and kill the step.
// Also slurp up any pretransitions.
dst->preTransitions.insert(dst->preTransitions.end(), src->preTransitions.begin(), src->preTransitions.end());
dst->preTransitions.append(src->preTransitions);
dst->commands.insert(dst->commands.end(), src->commands.begin(), src->commands.end());
MergeRenderAreaRectInto(&dst->render.renderArea, src->render.renderArea);
// So we don't consider it for other things, maybe doesn't matter.
@@ -1060,7 +1061,8 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
}
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) {
for (const auto &iter : step.preTransitions) {
for (size_t i = 0; i < step.preTransitions.size(); i++) {
const TransitionRequest &iter = step.preTransitions[i];
if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) {
recordBarrier_.TransitionImageAuto(
iter.fb->color.image,
@@ -1142,8 +1144,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
VKRFramebuffer *fb = step.render.framebuffer;
VkPipeline lastGraphicsPipeline = VK_NULL_HANDLE;
VkPipeline lastComputePipeline = VK_NULL_HANDLE;
VKRGraphicsPipeline *lastGraphicsPipeline = nullptr;
VKRComputePipeline *lastComputePipeline = nullptr;
auto &commands = step.commands;
@@ -1151,6 +1153,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
// The stencil ones are very commonly mostly redundant so let's eliminate them where possible.
// Might also want to consider scissor and viewport.
VkPipeline lastPipeline = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
int lastStencilWriteMask = -1;
int lastStencilCompareMask = -1;
int lastStencilReference = -1;
@@ -1164,52 +1168,44 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
case VKRRenderCommand::BIND_PIPELINE:
{
VkPipeline pipeline = c.pipeline.pipeline;
if (pipeline != lastGraphicsPipeline) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
lastGraphicsPipeline = pipeline;
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
}
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
pipelineLayout = c.pipeline.pipelineLayout;
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
break;
}
case VKRRenderCommand::BIND_GRAPHICS_PIPELINE:
{
VKRGraphicsPipeline *pipeline = c.graphics_pipeline.pipeline;
if (pipeline->Pending()) {
// Stall processing, waiting for the compile queue to catch up.
std::unique_lock<std::mutex> lock(compileDoneMutex_);
while (!pipeline->pipeline) {
compileDone_.wait(lock);
VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline;
if (graphicsPipeline != lastGraphicsPipeline) {
VkPipeline pipeline = graphicsPipeline->pipeline->BlockUntilReady();
if (pipeline != VK_NULL_HANDLE) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
pipelineLayout = c.pipeline.pipelineLayout;
lastGraphicsPipeline = graphicsPipeline;
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
}
}
if (pipeline->pipeline != lastGraphicsPipeline && pipeline->pipeline != VK_NULL_HANDLE) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline);
lastGraphicsPipeline = pipeline->pipeline;
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
}
break;
}
case VKRRenderCommand::BIND_COMPUTE_PIPELINE:
{
VKRComputePipeline *pipeline = c.compute_pipeline.pipeline;
if (pipeline->Pending()) {
// Stall processing, waiting for the compile queue to catch up.
std::unique_lock<std::mutex> lock(compileDoneMutex_);
while (!pipeline->pipeline) {
compileDone_.wait(lock);
VKRComputePipeline *computePipeline = c.compute_pipeline.pipeline;
if (computePipeline != lastComputePipeline) {
VkPipeline pipeline = computePipeline->pipeline->BlockUntilReady();
if (pipeline != VK_NULL_HANDLE) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
pipelineLayout = c.pipeline.pipelineLayout;
lastComputePipeline = computePipeline;
}
}
if (pipeline->pipeline != lastComputePipeline && pipeline->pipeline != VK_NULL_HANDLE) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline);
lastComputePipeline = pipeline->pipeline;
}
break;
}
@@ -1257,7 +1253,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
}
case VKRRenderCommand::PUSH_CONSTANTS:
vkCmdPushConstants(cmd, c.push.pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data);
vkCmdPushConstants(cmd, pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data);
break;
case VKRRenderCommand::STENCIL:
@@ -1276,14 +1272,17 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
break;
case VKRRenderCommand::DRAW_INDEXED:
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, c.drawIndexed.indexType);
vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset);
{
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, (VkIndexType)c.drawIndexed.indexType);
VkDeviceSize voffset = c.drawIndexed.voffset;
vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset);
vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0);
break;
}
case VKRRenderCommand::DRAW:
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets);
if (c.draw.vbuffer) {
vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset);
}
@@ -1325,7 +1324,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
}
default:
ERROR_LOG(G3D, "Unimpl queue command");
;
}
}
vkCmdEndRenderPass(cmd);
@@ -1793,6 +1791,8 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataForm
}
} else if (destFormat == Draw::DataFormat::D32F) {
ConvertToD32F(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat);
} else if (destFormat == Draw::DataFormat::D16) {
ConvertToD16(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat);
} else {
// TODO: Maybe a depth conversion or something?
ERROR_LOG(G3D, "CopyReadbackBuffer: Unknown format");
+9 -9
View File
@@ -4,7 +4,7 @@
#include <mutex>
#include <condition_variable>
#include "Common/Thread/Promise.h"
#include "Common/Data/Collections/Hashmaps.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/Vulkan/VulkanBarrier.h"
@@ -52,15 +52,17 @@ struct VkRenderData {
union {
struct {
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
} pipeline;
struct {
VKRGraphicsPipeline *pipeline;
VkPipelineLayout pipelineLayout;
} graphics_pipeline;
struct {
VKRComputePipeline *pipeline;
VkPipelineLayout pipelineLayout;
} compute_pipeline;
struct {
VkPipelineLayout pipelineLayout;
VkDescriptorSet ds;
int numUboOffsets;
uint32_t uboOffsets[3];
@@ -70,17 +72,16 @@ struct VkRenderData {
uint32_t offset;
} draw;
struct {
VkPipelineLayout pipelineLayout;
VkDescriptorSet ds;
int numUboOffsets;
uint32_t uboOffsets[3];
VkBuffer vbuffer; // might need to increase at some point
VkDeviceSize voffset;
VkBuffer ibuffer;
VkDeviceSize ioffset;
uint32_t voffset;
uint32_t ioffset;
uint32_t count;
int16_t instances;
VkIndexType indexType;
int16_t indexType;
} drawIndexed;
struct {
uint32_t clearColor;
@@ -103,7 +104,6 @@ struct VkRenderData {
uint32_t color;
} blendColor;
struct {
VkPipelineLayout pipelineLayout;
VkShaderStageFlags stages;
uint8_t offset;
uint8_t size;
@@ -134,8 +134,8 @@ enum class VKRRenderPassStoreAction : uint8_t {
};
struct TransitionRequest {
VkImageAspectFlags aspect; // COLOR or DEPTH
VKRFramebuffer *fb;
VkImageAspectFlags aspect; // COLOR or DEPTH
VkImageLayout targetLayout;
};
@@ -153,7 +153,7 @@ struct VKRStep {
VKRStepType stepType;
std::vector<VkRenderData> commands;
std::vector<TransitionRequest> preTransitions;
TinySet<TransitionRequest, 4> preTransitions;
TinySet<VKRFramebuffer *, 8> dependencies;
const char *tag;
union {
+48 -6
View File
@@ -5,6 +5,7 @@
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/TimeUtil.h"
#include "Common/GPU/Vulkan/VulkanAlloc.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
@@ -29,27 +30,61 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) {
// Already failed to create this one.
return false;
}
// Fill in the last part of the desc since now it's time to block.
VkShaderModule vs = desc->vertexShader->BlockUntilReady();
VkShaderModule fs = desc->fragmentShader->BlockUntilReady();
if (!vs || !fs) {
ERROR_LOG(G3D, "Failed creating graphics pipeline - missing shader modules");
// We're kinda screwed here?
return false;
}
VkPipelineShaderStageCreateInfo ss[2]{};
ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
ss[0].pSpecializationInfo = nullptr;
ss[0].module = vs;
ss[0].pName = "main";
ss[0].flags = 0;
ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
ss[1].pSpecializationInfo = nullptr;
ss[1].module = fs;
ss[1].pName = "main";
ss[1].flags = 0;
desc->pipe.pStages = ss;
desc->pipe.stageCount = 2;
double start = time_now_d();
VkPipeline vkpipeline;
VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline);
INFO_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0);
bool success = true;
if (result == VK_INCOMPLETE) {
// Bad (disallowed by spec) return value seen on Adreno in Burnout :( Try to ignore?
// Would really like to log more here, we could probably attach more info to desc.
//
// At least create a null placeholder to avoid creating over and over if something is broken.
pipeline = VK_NULL_HANDLE;
pipeline->Post(VK_NULL_HANDLE);
success = false;
} else if (result != VK_SUCCESS) {
pipeline = VK_NULL_HANDLE;
pipeline->Post(VK_NULL_HANDLE);
ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
success = false;
} else {
pipeline = vkpipeline;
pipeline->Post(vkpipeline);
}
// Having the desc stick around can be useful for debugging.
#ifndef _DEBUG
delete desc;
desc = nullptr;
#endif
return success;
}
@@ -63,11 +98,11 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) {
bool success = true;
if (result != VK_SUCCESS) {
pipeline = VK_NULL_HANDLE;
pipeline->Post(VK_NULL_HANDLE);
ERROR_LOG(G3D, "Failed creating compute pipeline! result='%s'", VulkanResultToString(result));
success = false;
} else {
pipeline = vkpipeline;
pipeline->Post(vkpipeline);
}
delete desc;
@@ -449,6 +484,12 @@ void VulkanRenderManager::CompileThreadFunc() {
if (!run_) {
break;
}
INFO_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size());
// TODO: Here we can sort the pending pipelines by vertex and fragment shaders,
// and split up further.
// Those with the same pairs of shaders should be on the same thread.
for (auto &entry : toCompile) {
switch (entry.type) {
case CompileQueueEntry::Type::GRAPHICS:
@@ -836,6 +877,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
} else {
_assert_(false);
}
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
return true;
@@ -1188,7 +1230,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in
// We're done.
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView;
} else {
curRenderStep_->preTransitions.push_back({ aspectBit, fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL });
curRenderStep_->preTransitions.push_back({ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL });
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView;
}
}
+23 -17
View File
@@ -12,6 +12,7 @@
#include <thread>
#include <queue>
#include "Common/Thread/Promise.h"
#include "Common/System/Display.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/Data/Convert/SmallDataConvert.h"
@@ -121,7 +122,11 @@ struct VKRGraphicsPipelineDesc {
VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
VkPipelineShaderStageCreateInfo shaderStageInfo[2]{};
// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.
Promise<VkShaderModule> *vertexShader;
Promise<VkShaderModule> *fragmentShader;
VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
VkVertexInputAttributeDescription attrs[8]{};
VkVertexInputBindingDescription ibd{};
@@ -136,18 +141,19 @@ struct VKRComputePipelineDesc {
VkComputePipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
};
// Wrapped pipeline, which will later allow for background compilation while emulating the rest of the frame.
// Wrapped pipeline.
struct VKRGraphicsPipeline {
VKRGraphicsPipeline() {
pipeline = VK_NULL_HANDLE;
pipeline = Promise<VkPipeline>::CreateEmpty();
}
VKRGraphicsPipelineDesc *desc = nullptr; // While non-zero, is pending and pipeline isn't valid.
std::atomic<VkPipeline> pipeline;
~VKRGraphicsPipeline() {
delete desc;
}
VKRGraphicsPipelineDesc *desc = nullptr;
Promise<VkPipeline> *pipeline;
bool Create(VulkanContext *vulkan);
bool Pending() const {
return pipeline == VK_NULL_HANDLE && desc != nullptr;
}
};
struct VKRComputePipeline {
@@ -155,7 +161,7 @@ struct VKRComputePipeline {
pipeline = VK_NULL_HANDLE;
}
VKRComputePipelineDesc *desc = nullptr;
std::atomic<VkPipeline> pipeline;
Promise<VkPipeline> *pipeline;
bool Create(VulkanContext *vulkan);
bool Pending() const {
@@ -242,29 +248,32 @@ public:
return pipeline;
}
void BindPipeline(VkPipeline pipeline, PipelineFlags flags) {
void BindPipeline(VkPipeline pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != VK_NULL_HANDLE);
VkRenderData data{ VKRRenderCommand::BIND_PIPELINE };
data.pipeline.pipeline = pipeline;
data.pipeline.pipelineLayout = pipelineLayout;
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags) {
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != nullptr);
VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE };
data.graphics_pipeline.pipeline = pipeline;
data.graphics_pipeline.pipelineLayout = pipelineLayout;
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags) {
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != nullptr);
VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE };
data.compute_pipeline.pipeline = pipeline;
data.compute_pipeline.pipelineLayout = pipelineLayout;
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
@@ -351,7 +360,6 @@ public:
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(size + offset < 40);
VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS };
data.push.pipelineLayout = pipelineLayout;
data.push.stages = stages;
data.push.offset = offset;
data.push.size = size;
@@ -386,12 +394,11 @@ public:
curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;
}
void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
void Draw(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
VkRenderData data{ VKRRenderCommand::DRAW };
data.draw.count = count;
data.draw.offset = offset;
data.draw.pipelineLayout = layout;
data.draw.ds = descSet;
data.draw.vbuffer = vbuffer;
data.draw.voffset = voffset;
@@ -403,12 +410,11 @@ public:
curRenderStep_->render.numDraws++;
}
void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) {
void DrawIndexed(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
VkRenderData data{ VKRRenderCommand::DRAW_INDEXED };
data.drawIndexed.count = count;
data.drawIndexed.instances = numInstances;
data.drawIndexed.pipelineLayout = layout;
data.drawIndexed.ds = descSet;
data.drawIndexed.vbuffer = vbuffer;
data.drawIndexed.voffset = voffset;
+6 -5
View File
@@ -795,6 +795,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
caps_.fragmentShaderInt32Supported = true;
caps_.textureNPOTFullySupported = true;
caps_.fragmentShaderDepthWriteSupported = true;
caps_.logicOpSupported = vulkan->GetDeviceFeatures().enabled.logicOp != 0;
auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties;
switch (deviceProps.vendorID) {
@@ -1339,7 +1340,7 @@ void VKContext::Draw(int vertexCount, int offset) {
BindCompatiblePipeline();
ApplyDynamicState();
renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
}
void VKContext::DrawIndexed(int vertexCount, int offset) {
@@ -1359,7 +1360,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) {
BindCompatiblePipeline();
ApplyDynamicState();
renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16);
renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16);
}
void VKContext::DrawUP(const void *vdata, int vertexCount) {
@@ -1375,15 +1376,15 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
BindCompatiblePipeline();
ApplyDynamicState();
renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
}
void VKContext::BindCompatiblePipeline() {
VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass();
if (renderPass == renderManager_.GetBackbufferRenderPass()) {
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags);
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags, pipelineLayout_);
} else {
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags);
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags, pipelineLayout_);
}
}
+42
View File
@@ -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";
+4 -3
View File
@@ -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) {
+3 -3
View File
@@ -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<size_t Count>
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);
+2 -2
View File
@@ -19,7 +19,7 @@ struct Mailbox {
std::mutex mutex_;
std::condition_variable condvar_;
T data_ = nullptr;
T data_{};
T Wait() {
std::unique_lock<std::mutex> lock(mutex_);
@@ -43,7 +43,7 @@ struct Mailbox {
std::unique_lock<std::mutex> lock(mutex_);
if (!data_) {
data_ = data;
condvar_.notify_one();
condvar_.notify_all();
return true;
} else {
// Already has value.
+35 -3
View File
@@ -1,7 +1,9 @@
#pragma once
#include <functional>
#include <mutex>
#include "Common/Log.h"
#include "Common/Thread/Channel.h"
#include "Common/Thread/ThreadManager.h"
@@ -33,6 +35,7 @@ public:
// Has ownership over the data. Single use.
// TODO: Split Mailbox (rx_ and tx_) up into separate proxy objects.
// NOTE: Poll/BlockUntilReady should only be used from one thread.
// TODO: Make movable?
template<class T>
class Promise {
public:
@@ -47,15 +50,36 @@ public:
return promise;
}
static Promise<T> *AlreadyDone(T data) {
Promise<T> *promise = new Promise<T>();
promise->data_ = data;
promise->ready_ = true;
return promise;
}
static Promise<T> *CreateEmpty() {
Mailbox<T> *mailbox = new Mailbox<T>();
Promise<T> *promise = new Promise<T>();
promise->rx_ = mailbox;
return promise;
}
// Allow an empty promise to spawn, too, in case we want to delay it.
void SpawnEmpty(ThreadManager *threadman, std::function<T()> fun, TaskType taskType) {
PromiseTask<T> *task = new PromiseTask<T>(fun, rx_, taskType);
threadman->EnqueueTask(task);
}
~Promise() {
std::lock_guard<std::mutex> guard(readyMutex_);
// A promise should have been fulfilled before it's destroyed.
_assert_(ready_);
_assert_(!rx_);
delete data_;
}
// Returns T if the data is ready, nullptr if it's not.
T Poll() {
std::lock_guard<std::mutex> guard(readyMutex_);
if (ready_) {
return data_;
} else {
@@ -71,6 +95,7 @@ public:
}
T BlockUntilReady() {
std::lock_guard<std::mutex> guard(readyMutex_);
if (ready_) {
return data_;
} else {
@@ -82,10 +107,17 @@ public:
}
}
// For outside injection of data, when not using Spawn
void Post(T data) {
rx_->Send(data);
}
private:
Promise() {}
T data_ = nullptr;
// Promise can only be constructed in Spawn (or AlreadyDone).
T data_{};
bool ready_ = false;
Mailbox<T> *rx_;
std::mutex readyMutex_;
Mailbox<T> *rx_ = nullptr;
};
+89 -3
View File
@@ -1,6 +1,7 @@
#include "Common/VR/PPSSPPVR.h"
#include "Common/VR/VRBase.h"
#include "Common/VR/VRInput.h"
#include "Common/VR/VRMath.h"
#include "Common/VR/VRRenderer.h"
#include "Common/VR/VRTweaks.h"
@@ -31,6 +32,16 @@ struct ButtonMapping {
}
};
struct MouseActivator {
bool activate;
ovrButton ovr;
MouseActivator(bool activate, ovrButton ovr) {
this->activate = activate;
this->ovr = ovr;
}
};
static std::vector<ButtonMapping> leftControllerMapping = {
ButtonMapping(NKCODE_BUTTON_X, ovrButton_X),
ButtonMapping(NKCODE_BUTTON_Y, ovrButton_Y),
@@ -61,6 +72,16 @@ static std::vector<ButtonMapping> controllerMapping[2] = {
leftControllerMapping,
rightControllerMapping
};
static int mouseController = -1;
static bool mousePressed[] = {false, false};
static std::vector<MouseActivator> mouseActivators = {
MouseActivator(true, ovrButton_Trigger),
MouseActivator(false, ovrButton_Up),
MouseActivator(false, ovrButton_Down),
MouseActivator(false, ovrButton_Left),
MouseActivator(false, ovrButton_Right),
};
/*
================================================================================
@@ -90,7 +111,7 @@ void EnterVR(bool firstStart) {
VR_EnterVR(VR_GetEngine());
IN_VRInit(VR_GetEngine());
}
VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported());
VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, false);
}
void GetVRResolutionPerEye(int* width, int* height) {
@@ -99,7 +120,8 @@ void GetVRResolutionPerEye(int* width, int* height) {
}
}
void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) {
void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) {
//buttons
KeyInput keyInput = {};
for (int j = 0; j < 2; j++) {
int status = IN_VRGetButtonState(j);
@@ -125,6 +147,53 @@ void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) {
}
}
}
//enable or disable mouse
for (int j = 0; j < 2; j++) {
int status = IN_VRGetButtonState(j);
for (MouseActivator& m : mouseActivators) {
if (status & m.ovr) {
mouseController = m.activate ? j : -1;
}
}
}
//mouse cursor
if (mouseController >= 0) {
//get position on screen
XrPosef pose = IN_VRGetPose(mouseController);
XrVector3f angles = XrQuaternionf_ToEulerAngles(pose.orientation);
float width = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_WIDTH);
float height = (float)VR_GetConfig(VR_CONFIG_VIEWPORT_HEIGHT);
float cx = width / 2;
float cy = height / 2;
float speed = (cx + cy) / 2;
float x = cx - tan(ToRadians(angles.y - (float)VR_GetConfig(VR_CONFIG_MENU_YAW))) * speed;
float y = cy - tan(ToRadians(angles.x)) * speed;
//set renderer
VR_SetConfig(VR_CONFIG_MOUSE_X, (int)x);
VR_SetConfig(VR_CONFIG_MOUSE_Y, (int)y);
VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 6 * (int)pow(VR_GetConfig(VR_CONFIG_CANVAS_DISTANCE), 0.25f));
//inform engine about the status
TouchInput touch;
touch.id = mouseController;
touch.x = x * dp_xscale;
touch.y = (height - y - 1) * dp_yscale;
bool pressed = IN_VRGetButtonState(mouseController) & ovrButton_Trigger;
if (mousePressed[mouseController] != pressed) {
if (!pressed) {
touch.flags = TOUCH_DOWN;
NativeTouch(touch);
touch.flags = TOUCH_UP;
NativeTouch(touch);
}
mousePressed[mouseController] = pressed;
}
} else {
VR_SetConfig(VR_CONFIG_MOUSE_SIZE, 0);
}
}
void UpdateVRScreenKey(const KeyInput &key) {
@@ -151,7 +220,12 @@ void BindVRFramebuffer() {
}
bool PreVRRender() {
if (VR_BeginFrame(VR_GetEngine())) {
if (!VR_GetConfig(VR_CONFIG_VIEWPORT_VALID)) {
VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported());
VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true);
}
if (VR_InitFrame(VR_GetEngine())) {
// Decide if the scene is 3D or not
if (g_Config.bEnableVR && !VR_GetConfig(VR_CONFIG_FORCE_2D) && (VR_GetConfig(VR_CONFIG_3D_GEOMETRY_COUNT) > 15)) {
@@ -171,9 +245,21 @@ bool PreVRRender() {
}
void PostVRRender() {
VR_FinishFrame(VR_GetEngine());
}
void PreVRFrameRender(int fboIndex) {
VR_BeginFrame(VR_GetEngine(), fboIndex);
}
void PostVRFrameRender() {
VR_EndFrame(VR_GetEngine());
}
int GetVRFBOIndex() {
return VR_GetConfig(VR_CONFIG_CURRENT_FBO);
}
bool IsMultiviewSupported() {
return false;
}
+8 -2
View File
@@ -10,13 +10,16 @@ bool IsVRBuild();
void InitVROnAndroid(void* vm, void* activity, int version, char* name);
void EnterVR(bool firstStart);
void GetVRResolutionPerEye(int* width, int* height);
void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics);
void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale);
void UpdateVRScreenKey(const KeyInput &key);
// VR rendering integration
void BindVRFramebuffer();
bool PreVRRender();
void PostVRRender();
void PreVRFrameRender(int fboIndex);
void PostVRFrameRender();
int GetVRFBOIndex();
bool IsMultiviewSupported();
bool IsFlatVRScene();
bool Is2DVRObject(float* projMatrix, bool ortho);
@@ -30,13 +33,16 @@ inline bool IsVRBuild() { return false; }
inline void InitVROnAndroid(void* vm, void* activity, int version, char* name) {}
inline void EnterVR(bool firstTime) {}
inline void GetVRResolutionPerEye(int* width, int* height) {}
inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool haptics) {}
inline void UpdateVRInput(bool(*NativeKey)(const KeyInput &key), bool(*NativeTouch)(const TouchInput &touch), bool haptics, float dp_xscale, float dp_yscale) {}
inline void UpdateVRScreenKey(const KeyInput &key) {}
// VR rendering integration
inline void BindVRFramebuffer() {}
inline bool PreVRRender() { return false; }
inline void PostVRRender() {}
inline void PreVRFrameRender(int fboIndex) {}
inline void PostVRFrameRender() {}
inline int GetVRFBOIndex() { return 0; }
inline bool IsMultiviewSupported() { return false; }
inline bool IsFlatVRScene() { return true; }
inline bool Is2DVRObject(float* projMatrix, bool ortho) { return false; }
+4 -3
View File
@@ -92,18 +92,19 @@ typedef struct {
typedef struct {
int Width;
int Height;
bool Multiview;
uint32_t TextureSwapChainLength;
uint32_t TextureSwapChainIndex;
ovrSwapChain ColorSwapChain;
ovrSwapChain DepthSwapChain;
XrSwapchainImageOpenGLESKHR* ColorSwapChainImage;
unsigned int* DepthBuffers;
XrSwapchainImageOpenGLESKHR* DepthSwapChainImage;
unsigned int* FrameBuffers;
bool Acquired;
} ovrFramebuffer;
typedef struct {
ovrFramebuffer FrameBuffer;
bool Multiview;
ovrFramebuffer FrameBuffer[ovrMaxNumEyes];
} ovrRenderer;
typedef struct {
+45 -55
View File
@@ -33,8 +33,12 @@ void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) {
frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE;
frameBuffer->ColorSwapChain.Width = 0;
frameBuffer->ColorSwapChain.Height = 0;
frameBuffer->ColorSwapChainImage = NULL;
frameBuffer->DepthSwapChain.Handle = XR_NULL_HANDLE;
frameBuffer->DepthSwapChain.Width = 0;
frameBuffer->DepthSwapChain.Height = 0;
frameBuffer->DepthSwapChainImage = NULL;
frameBuffer->DepthBuffers = NULL;
frameBuffer->FrameBuffers = NULL;
frameBuffer->Acquired = false;
}
@@ -43,12 +47,10 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w
frameBuffer->Width = width;
frameBuffer->Height = height;
frameBuffer->Multiview = multiview;
if (strstr((const char*)glGetString(GL_EXTENSIONS), "GL_OVR_multiview2") == nullptr)
{
ALOGE("OpenGL implementation does not support GL_OVR_multiview2 extension.\n");
exit(EXIT_FAILURE);
}
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei);
@@ -56,14 +58,11 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w
if (!glFramebufferTextureMultiviewOVR)
{
ALOGE("Can not get proc address for glFramebufferTextureMultiviewOVR.\n");
exit(EXIT_FAILURE);
}
XrSwapchainCreateInfo swapChainCreateInfo;
memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo));
swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
swapChainCreateInfo.format = GL_RGBA8;
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
swapChainCreateInfo.sampleCount = 1;
swapChainCreateInfo.width = width;
swapChainCreateInfo.height = height;
@@ -73,69 +72,55 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w
frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width;
frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height;
frameBuffer->DepthSwapChain.Width = swapChainCreateInfo.width;
frameBuffer->DepthSwapChain.Height = swapChainCreateInfo.height;
// Create the color swapchain.
swapChainCreateInfo.format = GL_RGBA8;
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle));
OXR(xrEnumerateSwapchainImages(frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL));
frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
// Create the depth swapchain.
swapChainCreateInfo.format = GL_DEPTH24_STENCIL8;
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->DepthSwapChain.Handle));
frameBuffer->DepthSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
// Populate the swapchain image array.
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
frameBuffer->ColorSwapChainImage[i].next = NULL;
frameBuffer->DepthSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
frameBuffer->DepthSwapChainImage[i].next = NULL;
}
OXR(xrEnumerateSwapchainImages(
frameBuffer->ColorSwapChain.Handle,
frameBuffer->TextureSwapChainLength,
&frameBuffer->TextureSwapChainLength,
(XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage));
OXR(xrEnumerateSwapchainImages(
frameBuffer->DepthSwapChain.Handle,
frameBuffer->TextureSwapChainLength,
&frameBuffer->TextureSwapChainLength,
(XrSwapchainImageBaseHeader*)frameBuffer->DepthSwapChainImage));
frameBuffer->DepthBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
frameBuffer->FrameBuffers = (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image;
const GLuint depthTexture = frameBuffer->DepthSwapChainImage[i].image;
// Create the frame buffer.
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
if (multiview) {
// Create the color buffer texture.
GLenum textureTarget = GL_TEXTURE_2D_ARRAY;
GL(glBindTexture(textureTarget, colorTexture));
GL(glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL(glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL(glBindTexture(textureTarget, 0));
// Create depth buffer.
GL(glGenTextures(1, &frameBuffer->DepthBuffers[i]));
GL(glBindTexture(textureTarget, frameBuffer->DepthBuffers[i]));
GL(glTexStorage3D(textureTarget, 1, GL_DEPTH_COMPONENT24, width, height, 2));
GL(glBindTexture(textureTarget, 0));
// Create the frame buffer.
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, frameBuffer->DepthBuffers[i], 0, 0, 2));
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexture, 0, 0, 2));
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 0, 2));
GL(glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0, 0, 2));
} else {
// Create the color buffer texture.
GLenum colorTextureTarget = GL_TEXTURE_2D;
GL(glBindTexture(colorTextureTarget, colorTexture));
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL(glBindTexture(colorTextureTarget, 0));
// Create depth buffer.
GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i]));
GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height));
GL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
// Create the frame buffer.
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0));
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0));
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0));
}
GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
@@ -151,14 +136,10 @@ bool ovrFramebuffer_Create(XrSession session, ovrFramebuffer* frameBuffer, int w
void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) {
GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers));
if (frameBuffer->Multiview) {
GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers));
} else {
GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers));
}
OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle));
OXR(xrDestroySwapchain(frameBuffer->DepthSwapChain.Handle));
free(frameBuffer->ColorSwapChainImage);
free(frameBuffer->DepthBuffers);
free(frameBuffer->DepthSwapChainImage);
free(frameBuffer->FrameBuffers);
ovrFramebuffer_Clear(frameBuffer);
@@ -216,15 +197,24 @@ ovrRenderer
*/
void ovrRenderer_Clear(ovrRenderer* renderer) {
ovrFramebuffer_Clear(&renderer->FrameBuffer);
for (int i = 0; i < ovrMaxNumEyes; i++) {
ovrFramebuffer_Clear(&renderer->FrameBuffer[i]);
}
}
void ovrRenderer_Create(XrSession session, ovrRenderer* renderer, int width, int height, bool multiview) {
ovrFramebuffer_Create(session, &renderer->FrameBuffer, width, height, multiview);
renderer->Multiview = multiview;
int instances = renderer->Multiview ? 1 : ovrMaxNumEyes;
for (int i = 0; i < instances; i++) {
ovrFramebuffer_Create(session, &renderer->FrameBuffer[i], width, height, multiview);
}
}
void ovrRenderer_Destroy(ovrRenderer* renderer) {
ovrFramebuffer_Destroy(&renderer->FrameBuffer);
int instances = renderer->Multiview ? 1 : ovrMaxNumEyes;
for (int i = 0; i < instances; i++) {
ovrFramebuffer_Destroy(&renderer->FrameBuffer[i]);
}
}
/*
+9
View File
@@ -506,3 +506,12 @@ uint32_t IN_VRGetButtonState( int controllerIndex ) {
XrVector2f IN_VRGetJoystickState( int controllerIndex ) {
return moveJoystickState[controllerIndex].currentState;
}
XrPosef IN_VRGetPose( int controllerIndex ) {
engine_t* engine = VR_GetEngine();
XrSpaceLocation loc = {};
loc.type = XR_TYPE_SPACE_LOCATION;
XrSpace aimSpace[] = { leftControllerAimSpace, rightControllerAimSpace };
xrLocateSpace(aimSpace[controllerIndex], engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc);
return loc.pose;
}
+1
View File
@@ -32,4 +32,5 @@ void IN_VRInit( engine_t *engine );
void IN_VRInputFrame( engine_t* engine );
uint32_t IN_VRGetButtonState( int controllerIndex );
XrVector2f IN_VRGetJoystickState( int controllerIndex );
XrPosef IN_VRGetPose( int controllerIndex );
void INVR_Vibrate( int duration, int chan, float intensity );
+56 -28
View File
@@ -16,9 +16,6 @@ GLboolean initialized = GL_FALSE;
GLboolean stageSupported = GL_FALSE;
int vrConfig[VR_CONFIG_MAX] = {};
float menuPitch = 0;
float menuYaw = 0;
float recenterYaw = 0;
XrVector3f hmdorientation;
XrVector3f hmdposition;
@@ -135,7 +132,8 @@ void VR_Recenter(engine_t* engine) {
OXR(xrLocateSpace(engine->appState.HeadSpace, engine->appState.CurrentSpace, engine->predictedDisplayTime, &loc));
hmdorientation = XrQuaternionf_ToEulerAngles(loc.pose.orientation);
recenterYaw += ToRadians(hmdorientation.y);
vrConfig[VR_CONFIG_RECENTER_YAW] += (int)hmdorientation.y;
float recenterYaw = ToRadians((float)vrConfig[VR_CONFIG_RECENTER_YAW]);
spaceCreateInfo.poseInReferenceSpace.orientation.x = 0;
spaceCreateInfo.poseInReferenceSpace.orientation.y = sin(recenterYaw / 2);
spaceCreateInfo.poseInReferenceSpace.orientation.z = 0;
@@ -171,8 +169,8 @@ void VR_Recenter(engine_t* engine) {
}
// Update menu orientation
menuPitch = hmdorientation.x;
menuYaw = 0;
vrConfig[VR_CONFIG_MENU_PITCH] = (int)hmdorientation.x;
vrConfig[VR_CONFIG_MENU_YAW] = 0;
}
void VR_InitRenderer( engine_t* engine, bool multiview ) {
@@ -182,6 +180,8 @@ void VR_InitRenderer( engine_t* engine, bool multiview ) {
int eyeW, eyeH;
VR_GetResolution(engine, &eyeW, &eyeH);
vrConfig[VR_CONFIG_VIEWPORT_WIDTH] = eyeW;
vrConfig[VR_CONFIG_VIEWPORT_HEIGHT] = eyeH;
// Get the viewport configuration info for the chosen viewport configuration type.
engine->appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES;
@@ -235,7 +235,7 @@ void VR_ClearFrameBuffer( int width, int height) {
glDisable( GL_SCISSOR_TEST );
}
bool VR_BeginFrame( engine_t* engine ) {
bool VR_InitFrame( engine_t* engine ) {
GLboolean stageBoundsDirty = GL_TRUE;
if (ovrApp_HandleXrEvents(&engine->appState)) {
VR_Recenter(engine);
@@ -304,16 +304,21 @@ bool VR_BeginFrame( engine_t* engine ) {
engine->appState.LayerCount = 0;
memset(engine->appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount);
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer;
ovrFramebuffer_Acquire(frameBuffer);
ovrFramebuffer_SetCurrent(frameBuffer);
VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height);
return true;
}
void VR_BeginFrame( engine_t* engine, int fboIndex ) {
vrConfig[VR_CONFIG_CURRENT_FBO] = fboIndex;
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex];
ovrFramebuffer_Acquire(frameBuffer);
ovrFramebuffer_SetCurrent(frameBuffer);
VR_ClearFrameBuffer(frameBuffer->ColorSwapChain.Width, frameBuffer->ColorSwapChain.Height);
}
void VR_EndFrame( engine_t* engine ) {
int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO];
// Clear the alpha channel, other way OpenXR would not transfer the framebuffer fully
VR_BindFramebuffer(engine);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
@@ -321,22 +326,38 @@ void VR_EndFrame( engine_t* engine ) {
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer;
// Show mouse cursor
int size = vrConfig[VR_CONFIG_MOUSE_SIZE];
if ((vrConfig[VR_CONFIG_MODE] == VR_MODE_FLAT_SCREEN) && (size > 0)) {
glEnable(GL_SCISSOR_TEST);
glScissor(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size);
glViewport(vrConfig[VR_CONFIG_MOUSE_X], vrConfig[VR_CONFIG_MOUSE_Y], size, size);
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
}
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex];
//ovrFramebuffer_Resolve(frameBuffer);
ovrFramebuffer_Release(frameBuffer);
ovrFramebuffer_SetNone();
}
void VR_FinishFrame( engine_t* engine ) {
XrCompositionLayerProjectionView projection_layer_elements[2] = {};
int vrMode = vrConfig[VR_CONFIG_MODE];
XrCompositionLayerProjectionView projection_layer_elements[2] = {};
if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) {
menuYaw = hmdorientation.y;
vrConfig[VR_CONFIG_MENU_YAW] = (int)hmdorientation.y;
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
int imageLayer = eye;
int imageLayer = engine->appState.Renderer.Multiview ? eye : 0;
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[0];
XrFovf fov = projections[eye].fov;
if (vrMode == VR_MODE_MONO_6DOF) {
fov = projections[0].fov;
imageLayer = 0;
} else if (!engine->appState.Renderer.Multiview) {
frameBuffer = &engine->appState.Renderer.FrameBuffer[eye];
}
memset(&projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView));
@@ -365,28 +386,30 @@ void VR_EndFrame( engine_t* engine ) {
} else if (vrMode == VR_MODE_FLAT_SCREEN) {
// Build the cylinder layer
int width = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Width;
int height = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Height;
int width = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Width;
int height = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Height;
XrCompositionLayerCylinderKHR cylinder_layer = {};
cylinder_layer.type = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR;
cylinder_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
cylinder_layer.space = engine->appState.CurrentSpace;
cylinder_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
memset(&cylinder_layer.subImage, 0, sizeof(XrSwapchainSubImage));
cylinder_layer.subImage.swapchain = engine->appState.Renderer.FrameBuffer.ColorSwapChain.Handle;
cylinder_layer.subImage.swapchain = engine->appState.Renderer.FrameBuffer[0].ColorSwapChain.Handle;
cylinder_layer.subImage.imageRect.offset.x = 0;
cylinder_layer.subImage.imageRect.offset.y = 0;
cylinder_layer.subImage.imageRect.extent.width = width;
cylinder_layer.subImage.imageRect.extent.height = height;
cylinder_layer.subImage.imageArrayIndex = 0;
float distance = vrConfig[VR_CONFIG_CANVAS_DISTANCE];
float distance = (float)vrConfig[VR_CONFIG_CANVAS_DISTANCE];
float menuPitch = ToRadians((float)vrConfig[VR_CONFIG_MENU_PITCH]);
float menuYaw = ToRadians((float)vrConfig[VR_CONFIG_MENU_YAW]);
XrVector3f pos = {
invViewTransform[0].position.x - sin(ToRadians(menuYaw)) * distance,
invViewTransform[0].position.x - sin(menuYaw) * distance,
invViewTransform[0].position.y,
invViewTransform[0].position.z - cos(ToRadians(menuYaw)) * distance
invViewTransform[0].position.z - cos(menuYaw) * distance
};
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -ToRadians(menuPitch));
XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, ToRadians(menuYaw));
XrQuaternionf pitch = XrQuaternionf_CreateFromVectorAngle({1, 0, 0}, -menuPitch);
XrQuaternionf yaw = XrQuaternionf_CreateFromVectorAngle({0, 1, 0}, menuYaw);
cylinder_layer.pose.orientation = XrQuaternionf_Multiply(pitch, yaw);
cylinder_layer.pose.position = pos;
cylinder_layer.radius = 12.0f;
@@ -412,8 +435,12 @@ void VR_EndFrame( engine_t* engine ) {
endFrameInfo.layers = layers;
OXR(xrEndFrame(engine->appState.Session, &endFrameInfo));
frameBuffer->TextureSwapChainIndex++;
frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength;
int instances = engine->appState.Renderer.Multiview ? 1 : ovrMaxNumEyes;
for (int i = 0; i < instances; i++) {
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[instances];
frameBuffer->TextureSwapChainIndex++;
frameBuffer->TextureSwapChainIndex %= frameBuffer->TextureSwapChainLength;
}
}
int VR_GetConfig( VRConfig config ) {
@@ -426,7 +453,8 @@ void VR_SetConfig( VRConfig config, int value) {
void VR_BindFramebuffer(engine_t *engine) {
if (!initialized) return;
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer;
int fboIndex = vrConfig[VR_CONFIG_CURRENT_FBO];
ovrFramebuffer* frameBuffer = &engine->appState.Renderer.FrameBuffer[fboIndex];
unsigned int swapchainIndex = frameBuffer->TextureSwapChainIndex;
unsigned int glFramebuffer = frameBuffer->FrameBuffers[swapchainIndex];
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glFramebuffer));
+21 -14
View File
@@ -4,19 +4,24 @@
#include "VRMath.h"
enum VRConfig {
VR_CONFIG_MODE,
VR_CONFIG_6DOF_ENABLED,
VR_CONFIG_6DOF_SCALE,
VR_CONFIG_MIRROR_AXIS_X,
VR_CONFIG_MIRROR_AXIS_Y,
VR_CONFIG_MIRROR_AXIS_Z,
VR_CONFIG_MIRROR_PITCH,
VR_CONFIG_MIRROR_YAW,
VR_CONFIG_MIRROR_ROLL,
VR_CONFIG_3D_GEOMETRY_COUNT,
VR_CONFIG_FOV_SCALE,
VR_CONFIG_FORCE_2D,
VR_CONFIG_CANVAS_DISTANCE,
//switching between 2D and 3D
VR_CONFIG_MODE, VR_CONFIG_3D_GEOMETRY_COUNT, VR_CONFIG_FORCE_2D,
//camera setup
VR_CONFIG_FOV_SCALE, VR_CONFIG_CANVAS_DISTANCE,
//6DoF
VR_CONFIG_6DOF_ENABLED, VR_CONFIG_6DOF_SCALE,
VR_CONFIG_MIRROR_AXIS_X, VR_CONFIG_MIRROR_AXIS_Y, VR_CONFIG_MIRROR_AXIS_Z,
VR_CONFIG_MIRROR_PITCH, VR_CONFIG_MIRROR_YAW, VR_CONFIG_MIRROR_ROLL,
//2D canvas positioning
VR_CONFIG_MENU_PITCH, VR_CONFIG_MENU_YAW, VR_CONFIG_RECENTER_YAW,
//mouse cursor
VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y,
//viewport setup
VR_CONFIG_VIEWPORT_WIDTH, VR_CONFIG_VIEWPORT_HEIGHT, VR_CONFIG_VIEWPORT_VALID,
//render status
VR_CONFIG_CURRENT_FBO,
//end
VR_CONFIG_MAX
};
@@ -38,8 +43,10 @@ void VR_GetResolution( engine_t* engine, int *pWidth, int *pHeight );
void VR_InitRenderer( engine_t* engine, bool multiview );
void VR_DestroyRenderer( engine_t* engine );
bool VR_BeginFrame( engine_t* engine );
bool VR_InitFrame( engine_t* engine );
void VR_BeginFrame( engine_t* engine, int fboIndex );
void VR_EndFrame( engine_t* engine );
void VR_FinishFrame( engine_t* engine );
int VR_GetConfig( VRConfig config );
void VR_SetConfig( VRConfig config, int value);
+1 -1
View File
@@ -67,7 +67,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
CheckSetting(iniFile, gameID, "RequireBufferedRendering", &flags_.RequireBufferedRendering);
CheckSetting(iniFile, gameID, "RequireBlockTransfer", &flags_.RequireBlockTransfer);
CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock);
CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks);
CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth);
CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack);
CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack);
@@ -97,6 +96,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
CheckSetting(iniFile, gameID, "AllowLargeFBTextureOffsets", &flags_.AllowLargeFBTextureOffsets);
CheckSetting(iniFile, gameID, "AtracLoopHack", &flags_.AtracLoopHack);
CheckSetting(iniFile, gameID, "DeswizzleDepth", &flags_.DeswizzleDepth);
CheckSetting(iniFile, gameID, "SplitFramebufferMargin", &flags_.SplitFramebufferMargin);
CheckSetting(iniFile, gameID, "VRClearFramebuffer", &flags_.VRClearFramebuffer);
}
+1 -1
View File
@@ -57,7 +57,6 @@ struct CompatFlags {
bool RequireBufferedRendering;
bool RequireBlockTransfer;
bool RequireDefaultCPUClock;
bool DisableReadbacks;
bool DisableAccurateDepth;
bool MGS2AcidHack;
bool SonicRivalsHack;
@@ -87,6 +86,7 @@ struct CompatFlags {
bool AllowLargeFBTextureOffsets;
bool AtracLoopHack;
bool DeswizzleDepth;
bool SplitFramebufferMargin;
bool VRClearFramebuffer;
};
-1
View File
@@ -935,7 +935,6 @@ static ConfigSetting graphicsSettings[] = {
ConfigSetting("ShaderChainRequires60FPS", &g_Config.bShaderChainRequires60FPS, false, true, true),
ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true),
ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false, true, true),
ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true),
ConfigSetting("GfxDebugOutput", &g_Config.bGfxDebugOutput, false, false, false),
-1
View File
@@ -232,7 +232,6 @@ public:
float fGameListScrollPosition;
int iBloomHack; //0 = off, 1 = safe, 2 = balanced, 3 = aggressive
bool bBlockTransferGPU;
bool bDisableSlowFramebufEffects;
bool bFragmentTestCache;
int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High
bool bHardwareTessellation;
+34 -20
View File
@@ -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<MemBlockInfo> &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<MemBlockInfo> 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() {
+3 -1
View File
@@ -68,7 +68,9 @@ inline void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, co
std::vector<MemBlockInfo> FindMemInfo(uint32_t start, uint32_t size);
std::vector<MemBlockInfo> 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();
+1 -1
View File
@@ -129,6 +129,6 @@ protected:
private:
DialogStatus status = SCE_UTILITY_STATUS_NONE;
UtilityDialogType dialogType_;
UtilityDialogType dialogType_ = UtilityDialogType::NONE;
bool volatileLocked_ = false;
};
+6 -5
View File
@@ -116,9 +116,9 @@ int PSPGamedataInstallDialog::Update(int animSpeed) {
WriteSfoFile();
// TODO: What is this? Should one of these update per file or anything?
request.unknownResult1 = readFiles;
request.unknownResult2 = readFiles;
Memory::WriteStruct(param.ptr, &request);
param->unknownResult1 = readFiles;
param->unknownResult2 = readFiles;
param.NotifyWrite("DialogResult");
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
}
@@ -250,8 +250,9 @@ void PSPGamedataInstallDialog::UpdateProgress() {
progressValue = (int)((allReadSize * 100) / allFilesSize);
else
progressValue = 100;
request.progress = progressValue;
Memory::WriteStruct(param.ptr, &request);
param->progress = progressValue;
param.NotifyWrite("DialogResult");
}
void PSPGamedataInstallDialog::DoState(PointerWrap &p) {
-3
View File
@@ -285,9 +285,6 @@ struct SaveFileInfo
PPGeImage *texture = nullptr;
SaveFileInfo() {
}
void DoState(PointerWrap &p);
};
+5 -5
View File
@@ -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());
}
+13 -11
View File
@@ -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<int16_t> 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;
}
+2
View File
@@ -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();
+1 -1
View File
@@ -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 {
+32 -50
View File
@@ -214,13 +214,12 @@ static int sub_17A8(u8* data)
return -261;
}
static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey)
{
pspChnnlsvContext1 ctx;
Memory::ReadStruct(addressCtx, &ctx);
int res = sceSdGetLastIndex_(ctx, Memory::GetPointerWrite(addressHash), Memory::GetPointerWrite(addressKey));
Memory::WriteStruct(addressCtx, &ctx);
return res;
static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) {
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
u8 *hash = Memory::GetPointerWrite(addressHash);
if (!ctx.IsValid() || !hash)
return hleLogError(SCEMISC, 0, "Invalid pointer");
return hleLogSuccessI(SCEMISC, sceSdGetLastIndex_(*ctx, hash, Memory::GetPointerWrite(addressKey)));
}
int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key)
@@ -325,13 +324,11 @@ int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key)
return 0;
}
static int sceSdSetIndex(u32 addressCtx, int value)
{
pspChnnlsvContext1 ctx;
Memory::ReadStruct(addressCtx,&ctx);
int res = sceSdSetIndex_(ctx, value);
Memory::WriteStruct(addressCtx,&ctx);
return res;
static int sceSdSetIndex(u32 addressCtx, int value) {
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
if (!ctx.IsValid())
return hleLogError(SCEMISC, 0, "Invalid pointer");
return hleLogSuccessI(SCEMISC, sceSdSetIndex_(*ctx, value));
}
int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value)
@@ -344,14 +341,11 @@ int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value)
}
static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length)
{
pspChnnlsvContext1 ctx;
Memory::ReadStruct(addressCtx, &ctx);
int res = sceSdRemoveValue_(ctx, Memory::GetPointerWrite(addressData), length);
Memory::WriteStruct(addressCtx, &ctx);
return res;
static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) {
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
if (!ctx.IsValid() || !Memory::IsValidAddress(addressData))
return hleLogError(SCEMISC, 0, "Invalid pointer");
return hleLogSuccessI(SCEMISC, sceSdRemoveValue_(*ctx, Memory::GetPointerWrite(addressData), length));
}
int sceSdRemoveValue_(pspChnnlsvContext1& ctx, u8* data, int length)
@@ -396,18 +390,14 @@ int sceSdRemoveValue_(pspChnnlsvContext1& ctx, u8* data, int length)
return 0;
}
static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr)
{
pspChnnlsvContext2 ctx2;
Memory::ReadStruct(ctx2Addr, &ctx2);
static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr) {
auto ctx2 = PSPPointer<pspChnnlsvContext2>::Create(ctx2Addr);
u8* data = Memory::GetPointerWrite(dataAddr);
u8* cryptkey = Memory::GetPointerWrite(cryptkeyAddr);
if (!ctx2.IsValid() || !data)
return hleLogError(SCEMISC, 0, "Invalid pointer");
int res = sceSdCreateList_(ctx2, mode, unkwn, data, cryptkey);
Memory::WriteStruct(ctx2Addr, &ctx2);
return res;
return hleLogSuccessI(SCEMISC, sceSdCreateList_(*ctx2, mode, unkwn, data, cryptkey));
}
int sceSdCreateList_(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, u8* cryptkey)
@@ -464,17 +454,13 @@ int sceSdCreateList_(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, u8*
return 0;
}
static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen)
{
pspChnnlsvContext2 ctx;
Memory::ReadStruct(ctxAddr, &ctx);
u8* data = Memory::GetPointerWrite(dataAddr);
static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen) {
auto ctx = PSPPointer<pspChnnlsvContext2>::Create(ctxAddr);
u8 *data = Memory::GetPointerWrite(dataAddr);
if (!ctx.IsValid() || !data)
return hleLogError(SCEMISC, 0, "Invalid pointer");
int res = sceSdSetMember_(ctx, data, alignedLen);
Memory::WriteStruct(ctxAddr, &ctx);
return res;
return hleLogSuccessI(SCEMISC, sceSdSetMember_(*ctx, data, alignedLen));
}
int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen)
@@ -511,15 +497,11 @@ int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen)
return res;
}
static int sceChnnlsv_21BE78B4(u32 ctxAddr)
{
pspChnnlsvContext2 ctx;
Memory::ReadStruct(ctxAddr, &ctx);
int res = sceChnnlsv_21BE78B4_(ctx);
Memory::WriteStruct(ctxAddr, &ctx);
return res;
static int sceChnnlsv_21BE78B4(u32 ctxAddr) {
auto ctx = PSPPointer<pspChnnlsvContext2>::Create(ctxAddr);
if (!ctx.IsValid())
return hleLogError(SCEMISC, 0, "Invalid pointer");
return hleLogSuccessI(SCEMISC, sceChnnlsv_21BE78B4_(*ctx));
}
int sceChnnlsv_21BE78B4_(pspChnnlsvContext2& ctx)
+1 -1
View File
@@ -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());
}
+4 -1
View File
@@ -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);
+1 -1
View File
@@ -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");
+3 -3
View File
@@ -486,8 +486,8 @@ public:
// For save states only.
}
FontLib(u32 paramPtr, u32 errorCodePtr) {
Memory::ReadStruct(paramPtr, &params_);
FontLib(FontNewLibParams *params, u32 errorCodePtr) {
params_ = *params;
if (params_.numFonts > 9) {
params_.numFonts = 9;
}
@@ -998,7 +998,7 @@ static u32 sceFontNewLib(u32 paramPtr, u32 errorCodePtr) {
INFO_LOG(SCEFONT, "sceFontNewLib(%08x, %08x)", paramPtr, errorCodePtr);
*errorCode = 0;
FontLib *newLib = new FontLib(paramPtr, errorCodePtr);
FontLib *newLib = new FontLib(params, errorCodePtr);
fontLibList.push_back(newLib);
// The game should never see this value, the return value is replaced
// by the action. Except if we disable the alloc, in this case we return
+6 -7
View File
@@ -427,8 +427,6 @@ static int sceGeBreak(u32 mode, u32 unknownPtr) {
}
static u32 sceGeSetCallback(u32 structAddr) {
DEBUG_LOG(SCEGE, "sceGeSetCallback(struct=%08x)", structAddr);
int cbID = -1;
for (size_t i = 0; i < ARRAY_SIZE(ge_used_callbacks); ++i) {
if (!ge_used_callbacks[i]) {
@@ -438,12 +436,13 @@ static u32 sceGeSetCallback(u32 structAddr) {
}
if (cbID == -1) {
WARN_LOG(SCEGE, "sceGeSetCallback(): out of callback ids");
return SCE_KERNEL_ERROR_OUT_OF_MEMORY;
return hleLogWarning(SCEGE, SCE_KERNEL_ERROR_OUT_OF_MEMORY, "out of callback ids");
}
ge_used_callbacks[cbID] = true;
Memory::ReadStruct(structAddr, &ge_callback_data[cbID]);
auto callbackData = PSPPointer<PspGeCallbackData>::Create(structAddr);
ge_callback_data[cbID] = *callbackData;
callbackData.NotifyRead("GeSetCallback");
int subIntrBase = __GeSubIntrBase(cbID);
@@ -458,7 +457,7 @@ static u32 sceGeSetCallback(u32 structAddr) {
sceKernelEnableSubIntr(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL);
}
return cbID;
return hleLogSuccessI(SCEGE, cbID);
}
static int sceGeUnsetCallback(u32 cbID) {
@@ -603,7 +602,7 @@ const HLEFunction sceGe_user[] = {
{0XB287BD61, &WrapU_U<sceGeDrawSync>, "sceGeDrawSync", 'x', "x" },
{0XB448EC0D, &WrapI_UU<sceGeBreak>, "sceGeBreak", 'i', "xx" },
{0X4C06E472, &WrapI_V<sceGeContinue>, "sceGeContinue", 'i', "" },
{0XA4FC06A4, &WrapU_U<sceGeSetCallback>, "sceGeSetCallback", 'x', "x" },
{0XA4FC06A4, &WrapU_U<sceGeSetCallback>, "sceGeSetCallback", 'i', "p" },
{0X05DB22CE, &WrapI_U<sceGeUnsetCallback>, "sceGeUnsetCallback", 'i', "x" },
{0X1F6752AD, &WrapU_V<sceGeEdramGetSize>, "sceGeEdramGetSize", 'x', "" },
{0XB77905EA, &WrapU_I<sceGeEdramSetAddrTranslation>, "sceGeEdramSetAddrTranslation", 'x', "i" },
+29 -22
View File
@@ -931,12 +931,12 @@ static u32 sceIoGetstat(const char *filename, u32 addr) {
// TODO: Improve timing (although this seems normally slow..)
int usec = 1000;
SceIoStat stat;
auto stat = PSPPointer<SceIoStat>::Create(addr);
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
if (info.exists) {
__IoGetStat(&stat, info);
if (Memory::IsValidAddress(addr)) {
Memory::WriteStruct(addr, &stat);
if (stat.IsValid()) {
__IoGetStat(stat, info);
stat.NotifyWrite("IoGetstat");
DEBUG_LOG(SCEIO, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr, info.startSector);
return hleDelayResult(0, "io getstat", usec);
} else {
@@ -1798,19 +1798,23 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o
return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND;
}
// TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace?
if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr
u32 pointer = Memory::Read_U32(argAddr);
if (Memory::IsValidRange(argAddr, 4) && argLen >= 4) { // NOTE: not outPtr
u32 pointer = Memory::ReadUnchecked_U32(argAddr);
u32 sectorSize = 0x200;
u32 memStickSectorSize = 32 * 1024;
u32 sectorCount = memStickSectorSize / sectorSize;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize.freeClusters = deviceSize.maxClusters;
deviceSize.maxSectors = deviceSize.maxClusters;
deviceSize.sectorSize = sectorSize;
deviceSize.sectorCount = sectorCount;
Memory::WriteStruct(pointer, &deviceSize);
auto deviceSize = PSPPointer<DeviceSize>::Create(pointer);
if (deviceSize.IsValid()) {
deviceSize->maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize->freeClusters = deviceSize->maxClusters;
deviceSize->maxSectors = deviceSize->maxClusters;
deviceSize->sectorSize = sectorSize;
deviceSize->sectorCount = sectorCount;
deviceSize.NotifyWrite("ms0:02425818");
}
return 0;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
@@ -1821,8 +1825,8 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o
if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) {
return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND;
}
if (Memory::IsValidAddress(outPtr) && outLen == 4) {
Memory::Write_U32(0, outPtr);
if (Memory::IsValidRange(outPtr, 4) && outLen == 4) {
Memory::WriteUnchecked_U32(0, outPtr);
return 0;
} else {
ERROR_LOG(SCEIO, "Failed 0x02425824 fat");
@@ -1939,13 +1943,16 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o
u32 memStickSectorSize = 32 * 1024;
u32 sectorCount = memStickSectorSize / sectorSize;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize.freeClusters = deviceSize.maxClusters;
deviceSize.maxSectors = deviceSize.maxClusters;
deviceSize.sectorSize = sectorSize;
deviceSize.sectorCount = sectorCount;
Memory::WriteStruct(pointer, &deviceSize);
auto deviceSize = PSPPointer<DeviceSize>::Create(pointer);
if (deviceSize.IsValid()) {
deviceSize->maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize->freeClusters = deviceSize->maxClusters;
deviceSize->maxSectors = deviceSize->maxClusters;
deviceSize->sectorSize = sectorSize;
deviceSize->sectorCount = sectorCount;
deviceSize.NotifyWrite("fatms0:02425818");
}
return 0;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
+19 -19
View File
@@ -646,12 +646,12 @@ struct SystemStatus {
static int sceKernelReferSystemStatus(u32 statusPtr) {
DEBUG_LOG(SCEKERNEL, "sceKernelReferSystemStatus(%08x)", statusPtr);
if (Memory::IsValidAddress(statusPtr)) {
SystemStatus status;
memset(&status, 0, sizeof(SystemStatus));
status.size = sizeof(SystemStatus);
auto status = PSPPointer<SystemStatus>::Create(statusPtr);
if (status.IsValid()) {
memset((SystemStatus *)status, 0, sizeof(SystemStatus));
status->size = sizeof(SystemStatus);
// TODO: Fill in the struct!
Memory::WriteStruct(statusPtr, &status);
status.NotifyWrite("SystemStatus");
}
return 0;
}
@@ -684,11 +684,11 @@ static u32 sceKernelReferThreadProfiler(u32 statusPtr) {
// Can we confirm that the struct above is the right struct?
// If so, re-enable this code.
//DebugProfilerRegs regs;
//memset(&regs, 0, sizeof(regs));
//auto regs = PSPPointer<DebugProfilerRegs>::Create(statusPtr);
// TODO: fill the struct.
//if (Memory::IsValidAddress(statusPtr)) {
// Memory::WriteStruct(statusPtr, &regs);
//if (regs.IsValid()) {
// memset((DebugProfilerRegs *)regs, 0, sizeof(DebugProfilerRegs));
// regs.NotifyWrite("ThreadProfiler");
//}
return 0;
}
@@ -715,14 +715,14 @@ const HLEFunction ThreadManForUser[] =
{0XD6DA4BA1, &WrapI_CUIIU<sceKernelCreateSema>, "sceKernelCreateSema", 'i', "sxiix" },
{0X28B6489C, &WrapI_I<sceKernelDeleteSema>, "sceKernelDeleteSema", 'i', "i" },
{0X58B1F937, &WrapI_II<sceKernelPollSema>, "sceKernelPollSema", 'i', "ii" },
{0XBC6FEBC5, &WrapI_IU<sceKernelReferSemaStatus>, "sceKernelReferSemaStatus", 'i', "ix" },
{0XBC6FEBC5, &WrapI_IU<sceKernelReferSemaStatus>, "sceKernelReferSemaStatus", 'i', "ip" },
{0X3F53E640, &WrapI_II<sceKernelSignalSema>, "sceKernelSignalSema", 'i', "ii" },
{0X4E3A1105, &WrapI_IIU<sceKernelWaitSema>, "sceKernelWaitSema", 'i', "iix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
{0X6D212BAC, &WrapI_IIU<sceKernelWaitSemaCB>, "sceKernelWaitSemaCB", 'i', "iix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
{0X60107536, &WrapI_U<sceKernelDeleteLwMutex>, "sceKernelDeleteLwMutex", 'i', "x" },
{0X19CFF145, &WrapI_UCUIU<sceKernelCreateLwMutex>, "sceKernelCreateLwMutex", 'i', "xsxix" },
{0X4C145944, &WrapI_IU<sceKernelReferLwMutexStatusByID>, "sceKernelReferLwMutexStatusByID", 'i', "ix" },
{0X4C145944, &WrapI_IU<sceKernelReferLwMutexStatusByID>, "sceKernelReferLwMutexStatusByID", 'i', "xp" },
// NOTE: LockLwMutex, UnlockLwMutex, and ReferLwMutexStatus are in Kernel_Library, see sceKernelInterrupt.cpp.
// The below should not be called directly.
//{0x71040D5C, nullptr, "_sceKernelTryLockLwMutex", '?', "" },
@@ -736,7 +736,7 @@ const HLEFunction ThreadManForUser[] =
{0X6B30100F, &WrapI_II<sceKernelUnlockMutex>, "sceKernelUnlockMutex", 'i', "ii" },
{0XB7D098C6, &WrapI_CUIU<sceKernelCreateMutex>, "sceKernelCreateMutex", 'i', "sxix" },
{0X0DDCD2C9, &WrapI_II<sceKernelTryLockMutex>, "sceKernelTryLockMutex", 'i', "ii" },
{0XA9C2CB9A, &WrapI_IU<sceKernelReferMutexStatus>, "sceKernelReferMutexStatus", 'i', "ix" },
{0XA9C2CB9A, &WrapI_IU<sceKernelReferMutexStatus>, "sceKernelReferMutexStatus", 'i', "ip" },
{0X87D9223C, &WrapI_IIU<sceKernelCancelMutex>, "sceKernelCancelMutex", 'i', "iix" },
{0XFCCFAD26, &WrapI_I<sceKernelCancelWakeupThread>, "sceKernelCancelWakeupThread", 'i', "i" },
@@ -818,7 +818,7 @@ const HLEFunction ThreadManForUser[] =
{0XF3986382, &WrapI_IUU<sceKernelReceiveMbxCB>, "sceKernelReceiveMbxCB", 'i', "ixx", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
{0X0D81716A, &WrapI_IU<sceKernelPollMbx>, "sceKernelPollMbx", 'i', "ix" },
{0X87D4DD36, &WrapI_IU<sceKernelCancelReceiveMbx>, "sceKernelCancelReceiveMbx", 'i', "ix" },
{0XA8E8C846, &WrapI_IU<sceKernelReferMbxStatus>, "sceKernelReferMbxStatus", 'i', "ix" },
{0XA8E8C846, &WrapI_IU<sceKernelReferMbxStatus>, "sceKernelReferMbxStatus", 'i', "ip" },
{0X7C0DC2A0, &WrapI_CIUUU<sceKernelCreateMsgPipe>, "sceKernelCreateMsgPipe", 'i', "sixxx" },
{0XF0B7DA1C, &WrapI_I<sceKernelDeleteMsgPipe>, "sceKernelDeleteMsgPipe", 'i', "i" },
@@ -829,7 +829,7 @@ const HLEFunction ThreadManForUser[] =
{0XFBFA697D, &WrapI_IUUUUU<sceKernelReceiveMsgPipeCB>, "sceKernelReceiveMsgPipeCB", 'i', "ixxxxx" },
{0XDF52098F, &WrapI_IUUUU<sceKernelTryReceiveMsgPipe>, "sceKernelTryReceiveMsgPipe", 'i', "ixxxx" },
{0X349B864D, &WrapI_IUU<sceKernelCancelMsgPipe>, "sceKernelCancelMsgPipe", 'i', "ixx" },
{0X33BE4024, &WrapI_IU<sceKernelReferMsgPipeStatus>, "sceKernelReferMsgPipeStatus", 'i', "ix" },
{0X33BE4024, &WrapI_IU<sceKernelReferMsgPipeStatus>, "sceKernelReferMsgPipeStatus", 'i', "ip" },
{0X56C039B5, &WrapI_CIUUU<sceKernelCreateVpl>, "sceKernelCreateVpl", 'i', "sixxx" },
{0X89B3D48C, &WrapI_I<sceKernelDeleteVpl>, "sceKernelDeleteVpl", 'i', "i" },
@@ -838,7 +838,7 @@ const HLEFunction ThreadManForUser[] =
{0XAF36D708, &WrapI_IUU<sceKernelTryAllocateVpl>, "sceKernelTryAllocateVpl", 'i', "ixx" },
{0XB736E9FF, &WrapI_IU<sceKernelFreeVpl>, "sceKernelFreeVpl", 'i', "ix" },
{0X1D371B8A, &WrapI_IU<sceKernelCancelVpl>, "sceKernelCancelVpl", 'i', "ix" },
{0X39810265, &WrapI_IU<sceKernelReferVplStatus>, "sceKernelReferVplStatus", 'i', "ix" },
{0X39810265, &WrapI_IU<sceKernelReferVplStatus>, "sceKernelReferVplStatus", 'i', "ip" },
{0XC07BB470, &WrapI_CUUUUU<sceKernelCreateFpl>, "sceKernelCreateFpl", 'i', "sxxxxx" },
{0XED1410E0, &WrapI_I<sceKernelDeleteFpl>, "sceKernelDeleteFpl", 'i', "i" },
@@ -847,7 +847,7 @@ const HLEFunction ThreadManForUser[] =
{0X623AE665, &WrapI_IU<sceKernelTryAllocateFpl>, "sceKernelTryAllocateFpl", 'i', "ix" },
{0XF6414A71, &WrapI_IU<sceKernelFreeFpl>, "sceKernelFreeFpl", 'i', "ix" },
{0XA8AA591F, &WrapI_IU<sceKernelCancelFpl>, "sceKernelCancelFpl", 'i', "ix" },
{0XD8199E4C, &WrapI_IU<sceKernelReferFplStatus>, "sceKernelReferFplStatus", 'i', "ix" },
{0XD8199E4C, &WrapI_IU<sceKernelReferFplStatus>, "sceKernelReferFplStatus", 'i', "ip" },
{0X20FFF560, &WrapU_CU<sceKernelCreateVTimer>, "sceKernelCreateVTimer", 'x', "sx", HLE_NOT_IN_INTERRUPT },
{0X328F9E52, &WrapU_I<sceKernelDeleteVTimer>, "sceKernelDeleteVTimer", 'x', "i", HLE_NOT_IN_INTERRUPT },
@@ -866,7 +866,7 @@ const HLEFunction ThreadManForUser[] =
{0X8DAFF657, &WrapI_CUUUUU<sceKernelCreateTlspl>, "sceKernelCreateTlspl", 'i', "sxxxxx" },
{0X32BF938E, &WrapI_I<sceKernelDeleteTlspl>, "sceKernelDeleteTlspl", 'i', "i" },
{0X721067F3, &WrapI_IU<sceKernelReferTlsplStatus>, "sceKernelReferTlsplStatus", 'i', "ix" },
{0X721067F3, &WrapI_IU<sceKernelReferTlsplStatus>, "sceKernelReferTlsplStatus", 'i', "xp" },
// Not completely certain about args.
{0X4A719FB2, &WrapI_I<sceKernelFreeTlspl>, "sceKernelFreeTlspl", 'i', "i" },
// Internal. Takes (uid, &addr) as parameters... probably.
@@ -931,7 +931,7 @@ const HLEFunction ThreadManForKernel[] =
{0xF3986382, &WrapI_IUU<sceKernelReceiveMbxCB>, "sceKernelReceiveMbxCB", 'i', "ixx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
{0x0D81716A, &WrapI_IU<sceKernelPollMbx>, "sceKernelPollMbx", 'i', "ix", HLE_KERNEL_SYSCALL },
{0x87D4DD36, &WrapI_IU<sceKernelCancelReceiveMbx>, "sceKernelCancelReceiveMbx", 'i', "ix", HLE_KERNEL_SYSCALL },
{0xA8E8C846, &WrapI_IU<sceKernelReferMbxStatus>, "sceKernelReferMbxStatus", 'i', "ix", HLE_KERNEL_SYSCALL },
{0xA8E8C846, &WrapI_IU<sceKernelReferMbxStatus>, "sceKernelReferMbxStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
{0x56C039B5, &WrapI_CIUUU<sceKernelCreateVpl>, "sceKernelCreateVpl", 'i', "sixxx", HLE_KERNEL_SYSCALL },
{0x89B3D48C, &WrapI_I<sceKernelDeleteVpl>, "sceKernelDeleteVpl", 'i', "i", HLE_KERNEL_SYSCALL },
{0xBED27435, &WrapI_IUUU<sceKernelAllocateVpl>, "sceKernelAllocateVpl", 'i', "ixxx", HLE_KERNEL_SYSCALL | HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
@@ -939,7 +939,7 @@ const HLEFunction ThreadManForKernel[] =
{0xAF36D708, &WrapI_IUU<sceKernelTryAllocateVpl>, "sceKernelTryAllocateVpl", 'i', "ixx", HLE_KERNEL_SYSCALL },
{0xB736E9FF, &WrapI_IU<sceKernelFreeVpl>, "sceKernelFreeVpl", 'i', "ix", HLE_KERNEL_SYSCALL },
{0x1D371B8A, &WrapI_IU<sceKernelCancelVpl>, "sceKernelCancelVpl", 'i', "ix", HLE_KERNEL_SYSCALL },
{0x39810265, &WrapI_IU<sceKernelReferVplStatus>, "sceKernelReferVplStatus", 'i', "ix", HLE_KERNEL_SYSCALL },
{0x39810265, &WrapI_IU<sceKernelReferVplStatus>, "sceKernelReferVplStatus", 'i', "ip", HLE_KERNEL_SYSCALL },
};
void Register_ThreadManForUser()
+6 -3
View File
@@ -522,14 +522,17 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr) {
u32 error;
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
if (e) {
if (!Memory::IsValidAddress(statusPtr))
auto status = PSPPointer<NativeEventFlag>::Create(statusPtr);
if (!status.IsValid())
return hleLogWarning(SCEKERNEL, -1, "invalid ptr");
HLEKernel::CleanupWaitingThreads(WAITTYPE_EVENTFLAG, id, e->waitingThreads);
e->nef.numWaitThreads = (int) e->waitingThreads.size();
if (Memory::Read_U32(statusPtr) != 0)
Memory::WriteStruct(statusPtr, &e->nef);
if (status->size != 0) {
*status = e->nef;
status.NotifyWrite("EventFlagStatus");
}
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return hleLogDebug(SCEKERNEL, error, "invalid event flag");
+4 -4
View File
@@ -656,7 +656,7 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size)
}
if (MemBlockInfoDetailed(size)) {
const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size);
const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size);
NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size());
NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size());
}
@@ -677,7 +677,7 @@ const HLEFunction Kernel_Library[] =
{0XBEA46419, &WrapI_UIU<sceKernelLockLwMutex>, "sceKernelLockLwMutex", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
{0X1FC64E09, &WrapI_UIU<sceKernelLockLwMutexCB>, "sceKernelLockLwMutexCB", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
{0X15B6446B, &WrapI_UI<sceKernelUnlockLwMutex>, "sceKernelUnlockLwMutex", 'i', "xi" },
{0XC1734599, &WrapI_UU<sceKernelReferLwMutexStatus>, "sceKernelReferLwMutexStatus", 'i', "xx" },
{0XC1734599, &WrapI_UU<sceKernelReferLwMutexStatus>, "sceKernelReferLwMutexStatus", 'i', "xp" },
{0X293B45B8, &WrapI_V<sceKernelGetThreadId>, "sceKernelGetThreadId", 'i', "" },
{0XD13BDE95, &WrapI_V<sceKernelCheckThreadStack>, "sceKernelCheckThreadStack", 'i', "" },
{0X1839852A, &WrapU_UUU<sceKernelMemcpy>, "sceKernelMemcpy", 'x', "xxx" },
@@ -691,7 +691,7 @@ static u32 sysclib_memcpy(u32 dst, u32 src, u32 size) {
memcpy(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size);
}
if (MemBlockInfoDetailed(size)) {
const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size);
const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size);
NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size());
NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size());
}
@@ -794,7 +794,7 @@ static u32 sysclib_memmove(u32 dst, u32 src, u32 size) {
memmove(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size);
}
if (MemBlockInfoDetailed(size)) {
const std::string tag = "KernelMemmove/" + GetMemWriteTagAt(src, size);
const std::string tag = GetMemWriteTagAt("KernelMemmove/", src, size);
NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size());
NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size());
}
+9 -11
View File
@@ -552,19 +552,17 @@ int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr)
return 0;
}
int sceKernelReferMbxStatus(SceUID id, u32 infoAddr)
{
int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) {
u32 error;
Mbx *m = kernelObjects.Get<Mbx>(id, error);
if (!m)
{
ERROR_LOG(SCEKERNEL, "sceKernelReferMbxStatus(%i, %08x): invalid mbx id", id, infoAddr);
return error;
if (!m) {
return hleLogError(SCEKERNEL, error, "invalid mbx id");
}
// Should we crash the thread somehow?
if (!Memory::IsValidAddress(infoAddr))
return -1;
auto info = PSPPointer<NativeMbx>::Create(infoAddr);
if (!info.IsValid())
return hleLogError(SCEKERNEL, -1, "invalid pointer");
for (int i = 0, n = m->nmb.numMessages; i < n; ++i)
m->nmb.packetListHead = Memory::Read_U32(m->nmb.packetListHead);
@@ -572,10 +570,10 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr)
HLEKernel::CleanupWaitingThreads(WAITTYPE_MBX, id, m->waitingThreads);
// For whatever reason, it won't write if the size (first member) is 0.
if (Memory::Read_U32(infoAddr) != 0)
{
if (info->size != 0) {
m->nmb.numWaitThreads = (int) m->waitingThreads.size();
Memory::WriteStruct<NativeMbx>(infoAddr, &m->nmb);
*info = m->nmb;
info.NotifyWrite("MbxStatus");
}
return 0;
+27 -31
View File
@@ -881,30 +881,26 @@ int sceKernelCancelFpl(SceUID uid, u32 numWaitThreadsPtr)
}
}
int sceKernelReferFplStatus(SceUID uid, u32 statusPtr)
{
int sceKernelReferFplStatus(SceUID uid, u32 statusPtr) {
u32 error;
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
if (fpl)
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x)", uid, statusPtr);
if (fpl) {
// Refresh waiting threads and free block count.
__KernelSortFplThreads(fpl);
fpl->nf.numWaitThreads = (int) fpl->waitingThreads.size();
fpl->nf.numFreeBlocks = 0;
for (int i = 0; i < (int)fpl->nf.numBlocks; ++i)
{
for (int i = 0; i < (int)fpl->nf.numBlocks; ++i) {
if (!fpl->blocks[i])
++fpl->nf.numFreeBlocks;
}
if (Memory::Read_U32(statusPtr) != 0)
Memory::WriteStruct(statusPtr, &fpl->nf);
return 0;
}
else
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x): invalid fpl", uid, statusPtr);
return error;
auto status = PSPPointer<NativeFPL>::Create(statusPtr);
if (status.IsValid() && status->size != 0) {
*status = fpl->nf;
status.NotifyWrite("FplStatus");
}
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return hleLogError(SCEKERNEL, error, "invalid fpl");
}
}
@@ -1782,8 +1778,6 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr) {
u32 error;
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
if (vpl) {
DEBUG_LOG(SCEKERNEL, "sceKernelReferVplStatus(%i, %08x)", uid, infoPtr);
__KernelSortVplThreads(vpl);
vpl->nv.numWaitThreads = (int) vpl->waitingThreads.size();
if (vpl->header.IsValid()) {
@@ -1791,12 +1785,14 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr) {
} else {
vpl->nv.freeSize = vpl->alloc.GetTotalFreeBytes();
}
if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) {
Memory::WriteStruct(infoPtr, &vpl->nv);
auto info = PSPPointer<SceKernelVplInfo>::Create(infoPtr);
if (info.IsValid() && info->size != 0) {
*info = vpl->nv;
info.NotifyWrite("VplStatus");
}
return 0;
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return error;
return hleLogError(SCEKERNEL, error, "invalid vpl");
}
}
@@ -2270,23 +2266,23 @@ int sceKernelFreeTlspl(SceUID uid)
return error;
}
int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr)
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferTlsplStatus(%08x, %08x)", uid, infoPtr);
int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr) {
u32 error;
TLSPL *tls = kernelObjects.Get<TLSPL>(uid, error);
if (tls)
{
if (tls) {
// Update the waiting threads in case of deletions, etc.
__KernelSortTlsplThreads(tls);
tls->ntls.numWaitThreads = (int) tls->waitingThreads.size();
if (Memory::Read_U32(infoPtr) != 0)
Memory::WriteStruct(infoPtr, &tls->ntls);
return 0;
auto info = PSPPointer<NativeTlspl>::Create(infoPtr);
if (info.IsValid() && info->size != 0) {
*info = tls->ntls;
info.NotifyWrite("TlsplStatus");
}
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return hleLogError(SCEKERNEL, error, "invalid tlspl");
}
else
return error;
}
const HLEFunction SysMemUserForUser[] = {
+38 -36
View File
@@ -247,6 +247,10 @@ enum NativeModuleStatus {
class PSPModule : public KernelObject {
public:
PSPModule() {
modulePtr.ptr = 0;
}
~PSPModule() {
if (memoryBlockAddr) {
// If it's either below user memory, or using a high kernel bit, it's in kernel.
@@ -258,9 +262,9 @@ public:
g_symbolMap->UnloadModule(memoryBlockAddr, memoryBlockSize);
}
if (modulePtr) {
if (modulePtr.ptr) {
//Only alloc at kernel memory.
kernelMemory.Free(modulePtr);
kernelMemory.Free(modulePtr.ptr);
}
}
const char *GetName() override { return nm.name; }
@@ -320,7 +324,7 @@ public:
}
if (s >= 5) {
Do(p, modulePtr);
Do(p, modulePtr.ptr);
}
ModuleWaitingThread mwt = {0};
@@ -454,7 +458,7 @@ public:
u32 memoryBlockAddr = 0;
u32 memoryBlockSize = 0;
u32 modulePtr = 0;
PSPPointer<NativeModule> modulePtr;
bool isFake = false;
};
@@ -1616,11 +1620,13 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
u32 moduleSize = sizeof(module->nm);
char tag[32];
snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid);
module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag);
module->modulePtr.ptr = kernelMemory.Alloc(moduleSize, true, tag);
// Fill the struct.
if (Memory::IsValidAddress(module->modulePtr))
Memory::WriteStruct(module->modulePtr, &module->nm);
if (module->modulePtr.IsValid()) {
*module->modulePtr = module->nm;
module->modulePtr.NotifyWrite("KernelModule");
}
error = 0;
return module;
@@ -1763,17 +1769,18 @@ void __KernelLoadReset() {
}
bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string) {
SceKernelLoadExecParam param;
SceKernelLoadExecParam param{};
PSP_SetLoading("Loading exec...");
if (paramPtr)
Memory::ReadStruct(paramPtr, &param);
else
memset(&param, 0, sizeof(SceKernelLoadExecParam));
auto paramData = PSPPointer<SceKernelLoadExecParam>::Create(paramPtr);
if (paramData.IsValid()) {
param = *paramData;
paramData.NotifyRead("KernelLoadExec");
}
u8 *param_argp = 0;
u8 *param_key = 0;
u8 *param_argp = nullptr;
u8 *param_key = nullptr;
if (param.args > 0) {
u32 argpAddr = param.argp;
param_argp = new u8[param.args];
@@ -1792,10 +1799,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str
if (!info.exists) {
ERROR_LOG(LOADER, "Failed to load executable %s - file doesn't exist", filename);
*error_string = StringFromFormat("Could not find executable %s", filename);
if (paramPtr) {
if (param_argp) delete[] param_argp;
if (param_key) delete[] param_key;
}
delete[] param_argp;
delete[] param_key;
__KernelShutdown();
return false;
}
@@ -1817,10 +1822,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str
ERROR_LOG(LOADER, "Failed to load module %s", filename);
*error_string = "Failed to load executable: " + *error_string;
delete [] temp;
if (paramPtr) {
if (param_argp) delete[] param_argp;
if (param_key) delete[] param_key;
}
delete[] param_argp;
delete[] param_key;
return false;
}
@@ -1857,8 +1860,8 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str
__KernelStartIdleThreads(module->GetUID());
if (param_argp) delete[] param_argp;
if (param_key) delete[] param_key;
delete[] param_argp;
delete[] param_key;
hleSkipDeadbeef();
return true;
@@ -1989,11 +1992,13 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) {
u32 moduleSize = sizeof(module->nm);
char tag[32];
snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid);
module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag);
module->modulePtr.ptr = kernelMemory.Alloc(moduleSize, true, tag);
// Fill the struct.
if(Memory::IsValidAddress(module->modulePtr))
Memory::WriteStruct(module->modulePtr, &module->nm);
if (module->modulePtr.IsValid()) {
*module->modulePtr = module->nm;
module->modulePtr.NotifyWrite("KernelModule");
}
// TODO: It would be more ideal to allocate memory for this module.
@@ -2147,10 +2152,7 @@ int KernelStartModule(SceUID moduleId, u32 argsize, u32 argAddr, u32 returnValue
static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
SceKernelSMOption smoption = {0};
if (optionAddr) {
Memory::ReadStruct(optionAddr, &smoption);
}
auto smoption = PSPPointer<SceKernelSMOption>::Create(optionAddr);
u32 error;
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module) {
@@ -2176,7 +2178,7 @@ static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 ret
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
bool needsWait;
int ret = KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, optionAddr ? &smoption : nullptr, &needsWait);
int ret = KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, smoption.PtrOrNull(), &needsWait);
if (needsWait) {
__KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "started module");
@@ -2464,8 +2466,8 @@ u32 sceKernelFindModuleByUID(u32 uid)
ERROR_LOG(SCEMODULE, "0 = sceKernelFindModuleByUID(%d): Module Not Found or Fake", uid);
return 0;
}
INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByUID(%d)", module->modulePtr, uid);
return module->modulePtr;
INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByUID(%d)", module->modulePtr.ptr, uid);
return module->modulePtr.ptr;
}
u32 sceKernelFindModuleByName(const char *name)
@@ -2477,8 +2479,8 @@ u32 sceKernelFindModuleByName(const char *name)
continue;
if (strcmp(name, module->nm.name) == 0) {
if (!module->isFake) {
INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr, name);
return module->modulePtr;
INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr.ptr, name);
return module->modulePtr.ptr;
}
else {
WARN_LOG(SCEMODULE, "0 = sceKernelFindModuleByName(%s): Module Fake", name);
+12 -18
View File
@@ -1006,33 +1006,27 @@ int sceKernelCancelMsgPipe(SceUID uid, u32 numSendThreadsAddr, u32 numReceiveThr
return 0;
}
int sceKernelReferMsgPipeStatus(SceUID uid, u32 statusPtr)
{
int sceKernelReferMsgPipeStatus(SceUID uid, u32 statusPtr) {
u32 error;
MsgPipe *m = kernelObjects.Get<MsgPipe>(uid, error);
if (m)
{
if (!Memory::IsValidAddress(statusPtr))
{
ERROR_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x): invalid address", uid, statusPtr);
return -1;
if (m) {
auto status = PSPPointer<NativeMsgPipe>::Create(statusPtr);
if (!status.IsValid()) {
return hleLogError(SCEKERNEL, -1, "invalid address");
}
DEBUG_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x)", uid, statusPtr);
// Clean up any that have timed out.
m->SortReceiveThreads();
m->SortSendThreads();
m->nmp.numSendWaitThreads = (int) m->sendWaitingThreads.size();
m->nmp.numReceiveWaitThreads = (int) m->receiveWaitingThreads.size();
if (Memory::Read_U32(statusPtr) != 0)
Memory::WriteStruct(statusPtr, &m->nmp);
return 0;
}
else
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferMsgPipeStatus(%i, %08x): bad message pipe", uid, statusPtr);
return error;
if (status->size != 0) {
*status = m->nmp;
status.NotifyWrite("MsgPipeStatus");
}
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return hleLogError(SCEKERNEL, error, "bad message pipe");
}
}
+26 -53
View File
@@ -664,31 +664,27 @@ int sceKernelUnlockMutex(SceUID id, int count)
return 0;
}
int sceKernelReferMutexStatus(SceUID id, u32 infoAddr)
{
int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) {
u32 error;
PSPMutex *m = kernelObjects.Get<PSPMutex>(id, error);
if (!m)
{
ERROR_LOG(SCEKERNEL, "sceKernelReferMutexStatus(%i, %08x): invalid mutex id", id, infoAddr);
return error;
if (!m) {
return hleLogError(SCEKERNEL, error, "invalid mutex id");
}
DEBUG_LOG(SCEKERNEL, "sceKernelReferMutexStatus(%08x, %08x)", id, infoAddr);
// Should we crash the thread somehow?
if (!Memory::IsValidAddress(infoAddr))
return -1;
auto info = PSPPointer<NativeMutex>::Create(infoAddr);
if (!info.IsValid())
return hleLogError(SCEKERNEL, -1, "invalid pointer");
// Don't write if the size is 0. Anything else is A-OK, though, apparently.
if (Memory::Read_U32(infoAddr) != 0)
{
if (info->size != 0) {
HLEKernel::CleanupWaitingThreads(WAITTYPE_MUTEX, id, m->waitingThreads);
m->nm.numWaitThreads = (int) m->waitingThreads.size();
Memory::WriteStruct(infoAddr, &m->nm);
*info = m->nm;
info.NotifyWrite("MutexStatus");
}
return 0;
return hleLogSuccessI(SCEKERNEL, 0);
}
int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr)
@@ -1075,18 +1071,18 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count)
return 0;
}
static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr)
{
static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) {
u32 error;
LwMutex *m = kernelObjects.Get<LwMutex>(uid, error);
if (!m)
return error;
return hleLogError(SCEKERNEL, error, "invalid id");
// Should we crash the thread somehow?
if (!Memory::IsValidAddress(infoPtr))
return -1;
auto info = PSPPointer<NativeLwMutex>::Create(infoPtr);
if (!info.IsValid())
return hleLogError(SCEKERNEL, -1, "invalid pointer");
if (Memory::Read_U32(infoPtr) != 0)
if (info->size != 0)
{
auto workarea = m->nm.workarea;
@@ -1096,44 +1092,21 @@ static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr)
m->nm.currentCount = workarea->lockLevel;
m->nm.lockThread = workarea->lockThread == 0 ? SceUID_le(-1) : workarea->lockThread;
m->nm.numWaitThreads = (int) m->waitingThreads.size();
Memory::WriteStruct(infoPtr, &m->nm);
*info = m->nm;
info.NotifyWrite("LwMutexStatus");
}
return 0;
return hleLogSuccessI(SCEKERNEL, 0);
}
int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr)
{
int error = __KernelReferLwMutexStatus(uid, infoPtr);
if (error >= 0)
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferLwMutexStatusByID(%08x, %08x)", uid, infoPtr);
return error;
}
else
{
ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferLwMutexStatusByID(%08x, %08x)", error, uid, infoPtr);
return error;
}
int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr) {
return __KernelReferLwMutexStatus(uid, infoPtr);
}
int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr)
{
if (!Memory::IsValidAddress(workareaPtr)) {
ERROR_LOG(SCEKERNEL, "Bad workarea pointer for LwMutex");
return SCE_KERNEL_ERROR_ACCESS_ERROR;
}
int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr) {
auto workarea = PSPPointer<NativeLwMutexWorkarea>::Create(workareaPtr);
if (!workarea.IsValid()) {
return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_ACCESS_ERROR, "bad workarea pointer for LwMutex");
}
int error = __KernelReferLwMutexStatus(workarea->uid, infoPtr);
if (error >= 0)
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferLwMutexStatus(%08x, %08x)", workareaPtr, infoPtr);
return error;
}
else
{
ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferLwMutexStatus(%08x, %08x)", error, workareaPtr, infoPtr);
return error;
}
return __KernelReferLwMutexStatus(workarea->uid, infoPtr);
}
+12 -16
View File
@@ -263,28 +263,24 @@ int sceKernelDeleteSema(SceUID id)
}
}
int sceKernelReferSemaStatus(SceUID id, u32 infoPtr)
{
int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) {
u32 error;
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
if (s)
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferSemaStatus(%i, %08x)", id, infoPtr);
if (!Memory::IsValidAddress(infoPtr))
return -1;
if (s) {
auto info = PSPPointer<NativeSemaphore>::Create(infoPtr);
if (!info.IsValid())
return hleLogWarning(SCEKERNEL, -1, "invalid pointer");
HLEKernel::CleanupWaitingThreads(WAITTYPE_SEMA, id, s->waitingThreads);
s->ns.numWaitThreads = (int) s->waitingThreads.size();
if (Memory::Read_U32(infoPtr) != 0)
Memory::WriteStruct(infoPtr, &s->ns);
return 0;
}
else
{
ERROR_LOG(SCEKERNEL, "sceKernelReferSemaStatus: error %08x", error);
return error;
if (info->size != 0) {
*info = s->ns;
info.NotifyWrite("SemaStatus");
}
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return hleLogError(SCEKERNEL, error);
}
}
+9 -6
View File
@@ -2902,13 +2902,14 @@ int sceKernelGetCallbackCount(SceUID cbId)
}
}
int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr)
{
int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr) {
u32 error;
PSPCallback *c = kernelObjects.Get<PSPCallback>(cbId, error);
if (c) {
if (Memory::IsValidAddress(statusAddr) && Memory::Read_U32(statusAddr) != 0) {
Memory::WriteStruct(statusAddr, &c->nc);
auto status = PSPPointer<NativeCallback>::Create(statusAddr);
if (status.IsValid() && status->size != 0) {
*status = c->nc;
status.NotifyWrite("CallbackStatus");
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return hleLogDebug(SCEKERNEL, 0, "struct size was 0");
@@ -3789,8 +3790,10 @@ int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr) {
return hleReportError(SCEKERNEL, error, "bad handler id");
}
if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) {
Memory::WriteStruct(infoPtr, &teh->nteh);
auto info = PSPPointer<NativeThreadEventHandler>::Create(infoPtr);
if (info.IsValid() && info->size != 0) {
*info = teh->nteh;
info.NotifyWrite("ThreadEventHandlerStatus");
return hleLogSuccessI(SCEKERNEL, 0);
} else {
return hleLogDebug(SCEKERNEL, 0, "struct size was 0");
+7 -7
View File
@@ -125,7 +125,7 @@ struct SceMpegLLI
};
void SceMpegAu::read(u32 addr) {
Memory::ReadStruct(addr, this);
Memory::Memcpy(this, addr, sizeof(this), "SceMpegAu");
pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32);
dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32);
}
@@ -133,7 +133,7 @@ void SceMpegAu::read(u32 addr) {
void SceMpegAu::write(u32 addr) {
pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32);
dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32);
Memory::WriteStruct(addr, this);
Memory::Memcpy(addr, this, sizeof(this), "SceMpegAu");
}
/*
@@ -2410,15 +2410,15 @@ static u32 sceMpegBasePESpacketCopy(u32 p)
{
pmp_videoSource = p;
pmp_nBlocks = 0;
SceMpegLLI lli;
while (1){
Memory::ReadStruct(p, &lli);
auto lli = PSPPointer<SceMpegLLI>::Create(p);
while (lli.IsValid()) {
pmp_nBlocks++;
// lli.Next ==0 for last block
if (lli.Next == 0){
if (lli->Next == 0){
break;
}
p = p + sizeof(SceMpegLLI);
++lli;
}
DEBUG_LOG(ME, "sceMpegBasePESpacketCopy(%08x), received %d block(s)", pmp_videoSource, pmp_nBlocks);
+89 -37
View File
@@ -785,11 +785,12 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) {
// Write static data since we don't actually manage any memory for sceNet* yet.
static int sceNetGetMallocStat(u32 statPtr) {
VERBOSE_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x) at %08x", statPtr, currentMIPS->pc);
if(Memory::IsValidAddress(statPtr))
Memory::WriteStruct(statPtr, &netMallocStat);
else
ERROR_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x): tried to request invalid address!", statPtr);
auto stat = PSPPointer<SceNetMallocStat>::Create(statPtr);
if (!stat.IsValid())
return hleLogError(SCENET, 0, "invalid address");
*stat = netMallocStat;
stat.NotifyWrite("sceNetGetMallocStat");
return 0;
}
@@ -893,77 +894,128 @@ static int sceNetApctlGetInfo(int code, u32 pInfoAddr) {
if (!netApctlInited)
return hleLogError(SCENET, ERROR_NET_APCTL_NOT_IN_BSS, "apctl not in bss"); // Only have valid info after joining an AP and got an IP, right?
if (!Memory::IsValidAddress(pInfoAddr))
return hleLogError(SCENET, -1, "apctl invalid arg");
u8* info = Memory::GetPointerWrite(pInfoAddr); // FIXME: Points to a union instead of a struct thus each field have the same address
switch (code) {
case PSP_NET_APCTL_INFO_PROFILE_NAME:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.name);
if (!Memory::IsValidRange(pInfoAddr, APCTL_PROFILENAME_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.name, APCTL_PROFILENAME_MAXLEN);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_PROFILENAME_MAXLEN, "NetApctlGetInfo");
DEBUG_LOG(SCENET, "ApctlInfo - ProfileName: %s", netApctlInfo.name);
break;
case PSP_NET_APCTL_INFO_BSSID:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.bssid);
if (!Memory::IsValidRange(pInfoAddr, ETHER_ADDR_LEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.bssid, ETHER_ADDR_LEN);
DEBUG_LOG(SCENET, "ApctlInfo - BSSID: %s", mac2str((SceNetEtherAddr*)&netApctlInfo.bssid).c_str());
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, ETHER_ADDR_LEN, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_SSID:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssid);
if (!Memory::IsValidRange(pInfoAddr, APCTL_SSID_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.ssid, APCTL_SSID_MAXLEN);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_SSID_MAXLEN, "NetApctlGetInfo");
DEBUG_LOG(SCENET, "ApctlInfo - SSID: %s", netApctlInfo.ssid);
break;
case PSP_NET_APCTL_INFO_SSID_LENGTH:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssidLength);
if (!Memory::IsValidRange(pInfoAddr, 4))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U32(netApctlInfo.ssidLength, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_SECURITY_TYPE:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.securityType);
if (!Memory::IsValidRange(pInfoAddr, 4))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U32(netApctlInfo.securityType, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_STRENGTH:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.strength);
if (!Memory::IsValidRange(pInfoAddr, 1))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U8(netApctlInfo.strength, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_CHANNEL:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.channel);
if (!Memory::IsValidRange(pInfoAddr, 1))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U8(netApctlInfo.channel, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_POWER_SAVE:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.powerSave);
if (!Memory::IsValidRange(pInfoAddr, 1))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U8(netApctlInfo.powerSave, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 1, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_IP:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.ip);
if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.ip, APCTL_IPADDR_MAXLEN);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo");
DEBUG_LOG(SCENET, "ApctlInfo - IP: %s", netApctlInfo.ip);
break;
case PSP_NET_APCTL_INFO_SUBNETMASK:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.subNetMask);
if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.subNetMask, APCTL_IPADDR_MAXLEN);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo");
DEBUG_LOG(SCENET, "ApctlInfo - SubNet Mask: %s", netApctlInfo.subNetMask);
break;
case PSP_NET_APCTL_INFO_GATEWAY:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.gateway);
if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.gateway, APCTL_IPADDR_MAXLEN);
DEBUG_LOG(SCENET, "ApctlInfo - Gateway IP: %s", netApctlInfo.gateway);
break;
case PSP_NET_APCTL_INFO_PRIMDNS:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.primaryDns);
if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.primaryDns, APCTL_IPADDR_MAXLEN);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo");
DEBUG_LOG(SCENET, "ApctlInfo - Primary DNS: %s", netApctlInfo.primaryDns);
break;
case PSP_NET_APCTL_INFO_SECDNS:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.secondaryDns);
if (!Memory::IsValidRange(pInfoAddr, APCTL_IPADDR_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.secondaryDns, APCTL_IPADDR_MAXLEN);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_IPADDR_MAXLEN, "NetApctlGetInfo");
DEBUG_LOG(SCENET, "ApctlInfo - Secondary DNS: %s", netApctlInfo.secondaryDns);
break;
case PSP_NET_APCTL_INFO_USE_PROXY:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.useProxy);
if (!Memory::IsValidRange(pInfoAddr, 4))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U32(netApctlInfo.useProxy, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_PROXY_URL:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyUrl);
if (!Memory::IsValidRange(pInfoAddr, APCTL_URL_MAXLEN))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::MemcpyUnchecked(pInfoAddr, netApctlInfo.proxyUrl, APCTL_URL_MAXLEN);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, APCTL_URL_MAXLEN, "NetApctlGetInfo");
DEBUG_LOG(SCENET, "ApctlInfo - Proxy URL: %s", netApctlInfo.proxyUrl);
break;
case PSP_NET_APCTL_INFO_PROXY_PORT:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyPort);
if (!Memory::IsValidRange(pInfoAddr, 2))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U16(netApctlInfo.proxyPort, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 2, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_8021_EAP_TYPE:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.eapType);
if (!Memory::IsValidRange(pInfoAddr, 4))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U32(netApctlInfo.eapType, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_START_BROWSER:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.startBrowser);
if (!Memory::IsValidRange(pInfoAddr, 4))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U32(netApctlInfo.startBrowser, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo");
break;
case PSP_NET_APCTL_INFO_WIFISP:
Memory::WriteStruct(pInfoAddr, &netApctlInfo.wifisp);
if (!Memory::IsValidRange(pInfoAddr, 4))
return hleLogError(SCENET, -1, "apctl invalid arg");
Memory::WriteUnchecked_U32(netApctlInfo.wifisp, pInfoAddr);
NotifyMemInfo(MemBlockFlags::WRITE, pInfoAddr, 4, "NetApctlGetInfo");
break;
default:
return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "apctl invalid code");
@@ -1234,28 +1286,28 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) {
switch (infoId) {
case PSP_NET_APCTL_DESC_IBSS: // IBSS, 6 bytes
if (entryId == 0)
Memory::WriteStruct(resultAddr, &netApctlInfo.bssid);
Memory::Memcpy(resultAddr, netApctlInfo.bssid, sizeof(netApctlInfo.bssid), "GetBSSDescEntryUser");
else {
// Generate a BSSID/MAC address
char dummyMAC[ETHER_ADDR_LEN];
memset(dummyMAC, entryId, sizeof(dummyMAC));
// Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo)
dummyMAC[0] &= 0xfc;
Memory::WriteStruct(resultAddr, &dummyMAC);
Memory::Memcpy(resultAddr, dummyMAC, sizeof(dummyMAC), "GetBSSDescEntryUser");
}
break;
case PSP_NET_APCTL_DESC_SSID_NAME:
// Return 32 bytes
if (entryId == 0)
Memory::WriteStruct(resultAddr, &netApctlInfo.ssid);
Memory::Memcpy(resultAddr, netApctlInfo.ssid, sizeof(netApctlInfo.ssid), "GetBSSDescEntryUser");
else {
Memory::WriteStruct(resultAddr, &dummySSID);
Memory::Memcpy(resultAddr, dummySSID, sizeof(dummySSID), "GetBSSDescEntryUser");
}
break;
case PSP_NET_APCTL_DESC_SSID_NAME_LENGTH:
// Return one 32-bit value
if (entryId == 0)
Memory::WriteStruct(resultAddr, &netApctlInfo.ssidLength);
Memory::Write_U32(netApctlInfo.ssidLength, resultAddr);
else {
// Calculate the SSID length
Memory::Write_U32((u32)strlen(dummySSID), resultAddr);
@@ -1264,7 +1316,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) {
case PSP_NET_APCTL_DESC_CHANNEL:
// FIXME: Return one 1 byte value or may be 32-bit if this is not a channel?
if (entryId == 0)
Memory::WriteStruct(resultAddr, &netApctlInfo.channel);
Memory::Write_U8(netApctlInfo.channel, resultAddr);
else {
// Generate channel for testing purposes, not even sure whether this is channel or not, MGS:PW seems to treat the data as u8
Memory::Write_U8(entryId, resultAddr);
@@ -1273,7 +1325,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) {
case PSP_NET_APCTL_DESC_SIGNAL_STRENGTH:
// Return 1 byte
if (entryId == 0)
Memory::WriteStruct(resultAddr, &netApctlInfo.strength);
Memory::Write_U8(netApctlInfo.strength, resultAddr);
else {
// Randomize signal strength between 1%~99% since games like MGS:PW are using signal strength to determine the strength of the recruit
Memory::Write_U8((int)(((float)rand() / (float)RAND_MAX) * 99.0 + 1.0), resultAddr);
@@ -1281,7 +1333,7 @@ int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) {
break;
case PSP_NET_APCTL_DESC_SECURITY:
// Return one 32-bit value
Memory::WriteStruct(resultAddr, &netApctlInfo.securityType);
Memory::Write_U32(netApctlInfo.securityType, resultAddr);
break;
default:
return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "unknown info id");
@@ -1432,7 +1484,7 @@ const HLEFunction sceNet[] = {
{0XD27961C9, &WrapV_UU<sceNetEtherStrton>, "sceNetEtherStrton", 'v', "xx" },
{0X0BF0A3AE, &WrapU_U<sceNetGetLocalEtherAddr>, "sceNetGetLocalEtherAddr", 'x', "x" },
{0X50647530, &WrapI_I<sceNetFreeThreadinfo>, "sceNetFreeThreadinfo", 'i', "i" },
{0XCC393E48, &WrapI_U<sceNetGetMallocStat>, "sceNetGetMallocStat", 'i', "x" },
{0XCC393E48, &WrapI_U<sceNetGetMallocStat>, "sceNetGetMallocStat", 'i', "p" },
{0XAD6844C6, &WrapI_I<sceNetThreadAbort>, "sceNetThreadAbort", 'i', "i" },
};
+21 -31
View File
@@ -1303,8 +1303,10 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) {
if (netAdhocctlInited)
return ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED;
if (Memory::IsValidAddress(productAddr)) {
Memory::ReadStruct(productAddr, &product_code);
auto product = PSPPointer<SceNetAdhocctlAdhocId>::Create(productAddr);
if (product.IsValid()) {
product_code = *product;
product.NotifyRead("NetAdhocctlInit");
}
adhocctlEvents.clear();
@@ -1539,21 +1541,17 @@ static int sceNetAdhocctlGetParameter(u32 paramAddr) {
}
// Library initialized
if (netAdhocctlInited) {
// Valid Arguments
if (Memory::IsValidAddress(paramAddr)) {
// Copy Parameter
Memory::WriteStruct(paramAddr,&parameter);
// Return Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOCCTL_INVALID_ARG;
if (!netAdhocctlInited) {
return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED);
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
auto ptr = PSPPointer<SceNetAdhocctlParameter>::Create(paramAddr);
if (!ptr.IsValid())
return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG);
*ptr = parameter;
ptr.NotifyWrite("NetAdhocctlGetParameter");
return 0;
}
/**
@@ -2258,25 +2256,17 @@ static int sceNetAdhocPdpDelete(int id, int unknown) {
static int sceNetAdhocctlGetAdhocId(u32 productStructAddr) {
INFO_LOG(SCENET, "sceNetAdhocctlGetAdhocId(%08x) at %08x", productStructAddr, currentMIPS->pc);
// Library initialized
if (netAdhocctlInited)
{
// Valid Arguments
if (Memory::IsValidAddress(productStructAddr))
{
// Copy Product ID
Memory::WriteStruct(productStructAddr, &product_code);
if (!netAdhocctlInited)
return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
// Return Success
return hleLogDebug(SCENET, 0, "type = %d, code = %s", product_code.type, product_code.data);
}
// Invalid Arguments
auto productStruct = PSPPointer<SceNetAdhocctlAdhocId>::Create(productStructAddr);
if (!productStruct.IsValid())
return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
}
// Library uninitialized
return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
*productStruct = product_code;
productStruct.NotifyWrite("NetAdhocctlGetAdhocId");
return hleLogDebug(SCENET, 0, "type = %d, code = %s", product_code.type, product_code.data);
}
// FIXME: Scan probably not a blocking function since there is ADHOCCTL_STATE_SCANNING state that can be polled by the game, right? But apparently it need to be delayed for Naruto Shippuden Ultimate Ninja Heroes 3
+45 -37
View File
@@ -154,15 +154,15 @@ static int sceNpGetOnlineId(u32 idPtr)
{
WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr);
if (!Memory::IsValidAddress(idPtr))
auto id = PSPPointer<SceNpOnlineId>::Create(idPtr);
if (!id.IsValid())
return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
SceNpOnlineId dummyOnlineId{};
truncate_cpy(dummyOnlineId.data, sizeof(dummyOnlineId.data), npOnlineId.c_str());
memset((SceNpOnlineId *)id, 0, sizeof(SceNpOnlineId));
truncate_cpy(id->data, sizeof(id->data), npOnlineId.c_str());
id.NotifyWrite("NpGetOnlineId");
INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyOnlineId.data);
Memory::WriteStruct(idPtr, &dummyOnlineId);
INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, id->data);
return 0;
}
@@ -177,20 +177,21 @@ static int sceNpGetNpId(u32 idPtr)
{
WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, idPtr);
if (!Memory::IsValidAddress(idPtr))
auto id = PSPPointer<SceNpId>::Create(idPtr);
if (!id.IsValid())
return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
SceNpId dummyNpId{};
int retval = NpGetNpId(&dummyNpId);
memset((SceNpId *)id, 0, sizeof(SceNpId));
int retval = NpGetNpId(id);
if (retval < 0)
return hleLogError(SCENET, retval);
INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyNpId.handle.data);
INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, id->handle.data);
std::string datahex;
DataToHexString(dummyNpId.opt, sizeof(dummyNpId.opt), &datahex);
DataToHexString(id->opt, sizeof(id->opt), &datahex);
INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str());
Memory::WriteStruct(idPtr, &dummyNpId);
id.NotifyWrite("NpGetNpId");
return 0;
}
@@ -199,19 +200,21 @@ static int sceNpGetAccountRegion(u32 countryCodePtr, u32 regionCodePtr)
{
WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, countryCodePtr, regionCodePtr);
if (!Memory::IsValidAddress(countryCodePtr) || !Memory::IsValidAddress(regionCodePtr))
auto countryCode = PSPPointer<SceNpCountryCode>::Create(countryCodePtr);
auto regionCode = PSPPointer<SceNpCountryCode>::Create(regionCodePtr);
if (!countryCode.IsValid() || !regionCode.IsValid())
return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
SceNpCountryCode dummyCountryCode{};
memcpy(dummyCountryCode.data, npCountryCode, sizeof(dummyCountryCode.data));
SceNpCountryCode dummyRegionCode{};
memcpy(dummyRegionCode.data, npRegionCode, sizeof(dummyRegionCode.data));
memset((SceNpCountryCode *)countryCode, 0, sizeof(SceNpCountryCode));
memcpy(countryCode->data, npCountryCode, sizeof(countryCode->data));
memset((SceNpCountryCode *)regionCode, 0, sizeof(SceNpCountryCode));
memcpy(regionCode->data, npRegionCode, sizeof(regionCode->data));
INFO_LOG(SCENET, "%s - Country Code: %s", __FUNCTION__, dummyCountryCode.data);
INFO_LOG(SCENET, "%s - Region? Code: %s", __FUNCTION__, dummyRegionCode.data);
INFO_LOG(SCENET, "%s - Country Code: %s", __FUNCTION__, countryCode->data);
INFO_LOG(SCENET, "%s - Region? Code: %s", __FUNCTION__, regionCode->data);
Memory::WriteStruct(countryCodePtr, &dummyCountryCode);
Memory::WriteStruct(regionCodePtr, &dummyRegionCode);
countryCode.NotifyWrite("NpGetAccountRegion");
regionCode.NotifyWrite("NpGetAccountRegion");
return 0;
}
@@ -220,14 +223,16 @@ static int sceNpGetMyLanguages(u32 langListPtr)
{
WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, langListPtr);
if (!Memory::IsValidAddress(langListPtr))
auto langList = PSPPointer<SceNpMyLanguages>::Create(langListPtr);
if (!langList.IsValid())
return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
INFO_LOG(SCENET, "%s - Language1 Code: %d", __FUNCTION__, npMyLangList.language1);
INFO_LOG(SCENET, "%s - Language2 Code: %d", __FUNCTION__, npMyLangList.language2);
INFO_LOG(SCENET, "%s - Language3 Code: %d", __FUNCTION__, npMyLangList.language3);
Memory::WriteStruct(langListPtr, &npMyLangList);
*langList = npMyLangList;
langList.NotifyWrite("NpGetMyLanguages");
return 0;
}
@@ -236,20 +241,21 @@ static int sceNpGetUserProfile(u32 profilePtr)
{
WARN_LOG(SCENET, "UNTESTED %s(%08x)", __FUNCTION__, profilePtr);
auto profile = PSPPointer<SceNpUserInformation>::Create(profilePtr);
if (!Memory::IsValidAddress(profilePtr))
return hleLogError(SCENET, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg");
SceNpUserInformation dummyProfile{};
truncate_cpy(dummyProfile.userId.handle.data, sizeof(dummyProfile.userId.handle.data), npOnlineId.c_str());
truncate_cpy(dummyProfile.icon.data, sizeof(dummyProfile.icon.data), npAvatarUrl.c_str());
memset((SceNpUserInformation *)profile, 0, sizeof(SceNpUserInformation));
truncate_cpy(profile->userId.handle.data, sizeof(profile->userId.handle.data), npOnlineId.c_str());
truncate_cpy(profile->icon.data, sizeof(profile->icon.data), npAvatarUrl.c_str());
INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, dummyProfile.userId.handle.data);
INFO_LOG(SCENET, "%s - Online ID: %s", __FUNCTION__, profile->userId.handle.data);
std::string datahex;
DataToHexString(dummyProfile.userId.opt, sizeof(dummyProfile.userId.opt), &datahex);
DataToHexString(profile->userId.opt, sizeof(profile->userId.opt), &datahex);
INFO_LOG(SCENET, "%s - Options?: %s", __FUNCTION__, datahex.c_str());
INFO_LOG(SCENET, "%s - Avatar URL: %s", __FUNCTION__, dummyProfile.icon.data);
INFO_LOG(SCENET, "%s - Avatar URL: %s", __FUNCTION__, profile->icon.data);
Memory::WriteStruct(profilePtr, &dummyProfile);
profile.NotifyWrite("NpGetUserProfile");
return 0;
}
@@ -295,10 +301,12 @@ int sceNpAuthGetMemoryStat(u32 memStatAddr)
{
ERROR_LOG(SCENET, "UNIMPL %s(%08x)", __FUNCTION__, memStatAddr);
if (!Memory::IsValidAddress(memStatAddr))
auto memStat = PSPPointer<SceNpAuthMemoryStat>::Create(memStatAddr);
if (!memStat.IsValid())
return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
Memory::WriteStruct(memStatAddr, &npAuthMemStat);
*memStat = npAuthMemStat;
memStat.NotifyWrite("NpAuthGetMemoryStat");
return 0;
}
@@ -386,7 +394,7 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length)
return hleLogError(SCENET, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
int result = length;
Memory::Memset(bufferAddr, 0, length);
Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket");
SceNpTicket ticket = {};
// Dummy Login ticket returned as Login response. Dummy ticket contents were taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket
ticket.header.version = TICKET_VER_2_1;
@@ -415,14 +423,14 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length)
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL);
ticket.section.type = SECTION_TYPE_BODY;
ticket.section.size = ofs;
Memory::WriteStruct(bufferAddr, &ticket);
Memory::Memcpy(bufferAddr, &ticket, sizeof(SceNpTicket), "NpAuthGetTicket");
SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on version 2.1 containing 4-chars ASCII + 20-chars ASCII
Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, &footer);
Memory::Memcpy(bufferAddr + sizeof(ticket) + ofs, &footer, sizeof(SceNpTicketSection), "NpAuthGetTicket");
ofs += sizeof(footer);
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x34\xcd\x3c\xa9", 4);
ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x3a\x4b\x42\x66\x92\xda\x6b\x7c\xb7\x4c\xe8\xd9\x4f\x2b\x77\x15\x91\xb8\xa4\xa9", 20); // 20 random letters, token key or SceNpSignature?
u8 unknownBytes[36] = {}; // includes Language list?
Memory::WriteStruct(bufferAddr + sizeof(ticket) + ofs, unknownBytes);
// includes Language list?
Memory::Memset(bufferAddr + sizeof(ticket) + ofs, 0, 36);
result = ticket.header.size + sizeof(ticket.header); // dummy ticket is 248 bytes
+4 -2
View File
@@ -280,10 +280,12 @@ static int sceNpMatching2GetMemoryStat(u32 memStatPtr)
if (!npMatching2Inited)
return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED);
if (!Memory::IsValidAddress(memStatPtr))
auto memStat = PSPPointer<SceNpAuthMemoryStat>::Create(memStatPtr);
if (!memStat.IsValid())
return hleLogError(SCENET, SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT);
Memory::WriteStruct(memStatPtr, &npMatching2MemStat);
*memStat = npMatching2MemStat;
memStat.NotifyWrite("NpMatching2GetMemoryStat");
return 0;
}
+8 -6
View File
@@ -38,9 +38,10 @@ static int sceOpenPSIDGetOpenPSID(u32 OpenPSIDPtr)
{
WARN_LOG(HLE, "UNTESTED %s(%08x)", __FUNCTION__, OpenPSIDPtr);
if (Memory::IsValidAddress(OpenPSIDPtr))
{
Memory::WriteStruct(OpenPSIDPtr, &dummyOpenPSID);
auto ptr = PSPPointer<SceOpenPSID>::Create(OpenPSIDPtr);
if (ptr.IsValid()) {
*ptr = dummyOpenPSID;
ptr.NotifyWrite("OpenPSIDGetOpenPSID");
}
return 0;
}
@@ -49,9 +50,10 @@ static int sceOpenPSIDGetPSID(u32 OpenPSIDPtr,u32 unknown)
{
WARN_LOG(HLE, "UNTESTED %s(%08x, %08x)", __FUNCTION__, OpenPSIDPtr, unknown);
if (Memory::IsValidAddress(OpenPSIDPtr))
{
Memory::WriteStruct(OpenPSIDPtr, &dummyOpenPSID);
auto ptr = PSPPointer<SceOpenPSID>::Create(OpenPSIDPtr);
if (ptr.IsValid()) {
*ptr = dummyOpenPSID;
ptr.NotifyWrite("OpenPSIDGetPSID");
}
return 0;
}
+26 -27
View File
@@ -766,20 +766,21 @@ static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
// Note: this structure changes between versions.
// TODO: These values are not right, but games probably don't read them.
PsmfData data = {0};
data.version = psmf->version;
data.headerSize = 0x800;
data.streamSize = psmf->streamSize;
auto data = PSPPointer<PsmfData>::Create(psmfStruct);
memset((PsmfData *)data, 0, sizeof(PsmfData));
data->version = psmf->version;
data->headerSize = 0x800;
data->streamSize = psmf->streamSize;
// This should be and needs to be the current stream.
data.streamNum = psmf->currentStreamNum;
data.headerOffset = psmf->headerOffset;
Memory::WriteStruct(psmfStruct, &data);
data->streamNum = psmf->currentStreamNum;
data->headerOffset = psmf->headerOffset;
data.NotifyWrite("PsmfSetPsmf");
// Because the Psmf struct is sometimes copied, we use a value inside as an id.
auto iter = psmfMap.find(data.headerOffset);
auto iter = psmfMap.find(data->headerOffset);
if (iter != psmfMap.end())
delete iter->second;
psmfMap[data.headerOffset] = psmf;
psmfMap[data->headerOffset] = psmf;
return hleLogSuccessI(ME, 0);
}
@@ -1046,45 +1047,43 @@ static u32 scePsmfGetEPWithId(u32 psmfStruct, int epid, u32 entryAddr)
{
Psmf *psmf = getPsmf(psmfStruct);
if (!psmf) {
ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x): invalid psmf", psmfStruct, epid, entryAddr);
return ERROR_PSMF_NOT_INITIALIZED;
return hleLogError(ME, ERROR_PSMF_NOT_INITIALIZED, "invalid psmf");
}
DEBUG_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x)", psmfStruct, epid, entryAddr);
if (epid < 0 || epid >= (int)psmf->EPMap.size()) {
ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i): invalid id", psmfStruct, epid);
return ERROR_PSMF_NOT_FOUND;
return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid id");
}
if (Memory::IsValidAddress(entryAddr)) {
Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]);
auto entry = PSPPointer<PsmfEntry>::Create(entryAddr);
if (entry.IsValid()) {
*entry = psmf->EPMap[epid];
entry.NotifyWrite("PsmfGetEPWithId");
}
return 0;
return hleLogSuccessI(ME, 0);
}
static u32 scePsmfGetEPWithTimestamp(u32 psmfStruct, u32 ts, u32 entryAddr)
{
Psmf *psmf = getPsmf(psmfStruct);
if (!psmf) {
ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x): invalid psmf", psmfStruct, ts, entryAddr);
return ERROR_PSMF_NOT_INITIALIZED;
return hleLogError(ME, ERROR_PSMF_NOT_INITIALIZED, "invalid psmf");
}
DEBUG_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x)", psmfStruct, ts, entryAddr);
if (ts < psmf->presentationStartTime) {
ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid timestamp", psmfStruct, ts);
return ERROR_PSMF_NOT_FOUND;
return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid timestamp");
}
int epid = psmf->FindEPWithTimestamp(ts);
if (epid < 0 || epid >= (int)psmf->EPMap.size()) {
ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid id", psmfStruct, epid);
return ERROR_PSMF_NOT_FOUND;
return hleLogError(ME, ERROR_PSMF_NOT_FOUND, "invalid id");
}
if (Memory::IsValidAddress(entryAddr)) {
Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]);
auto entry = PSPPointer<PsmfEntry>::Create(entryAddr);
if (entry.IsValid()) {
*entry = psmf->EPMap[epid];
entry.NotifyWrite("PsmfGetEPWithTimestamp");
}
return 0;
return hleLogSuccessI(ME, 0);
}
static u32 scePsmfGetEPidWithTimestamp(u32 psmfStruct, u32 ts)
+21 -12
View File
@@ -113,11 +113,12 @@ static int getCameraResolution(Camera::ConfigType type, int *width, int *height)
static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) {
INFO_LOG(HLE, "sceUsbCamSetupMic");
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) {
Memory::ReadStruct(paramAddr, &config->micParam);
auto param = PSPPointer<PspUsbCamSetupMicParam>::Create(paramAddr);
if (param.IsValid()) {
config->micParam = *param;
param.NotifyRead("UsbCamSetupMic");
}
return 0;
return hleLogSuccessInfoI(SCEMISC, 0);
}
static int sceUsbCamStartMic() {
@@ -155,16 +156,20 @@ static int sceUsbCamGetMicDataLength() {
}
static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) {
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoParam))) {
Memory::ReadStruct(paramAddr, &config->videoParam);
auto param = PSPPointer<PspUsbCamSetupVideoParam>::Create(paramAddr);
if (param.IsValid()) {
config->videoParam = *param;
param.NotifyRead("UsbCamSetupVideo");
}
config->type = Camera::ConfigType::CfVideo;
return 0;
}
static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) {
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoExParam))) {
Memory::ReadStruct(paramAddr, &config->videoExParam);
auto param = PSPPointer<PspUsbCamSetupVideoExParam>::Create(paramAddr);
if (param.IsValid()) {
config->videoExParam = *param;
param.NotifyRead("UsbCamSetupVideoEx");
}
config->type = Camera::ConfigType::CfVideoEx;
return 0;
@@ -222,8 +227,10 @@ static int sceUsbCamPollReadVideoFrameEnd() {
static int sceUsbCamSetupStill(u32 paramAddr) {
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStill");
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillParam))) {
Memory::ReadStruct(paramAddr, &config->stillParam);
auto param = PSPPointer<PspUsbCamSetupStillParam>::Create(paramAddr);
if (param.IsValid()) {
config->stillParam = *param;
param.NotifyRead("UsbCamSetupStill");
}
config->type = Camera::ConfigType::CfStill;
return 0;
@@ -231,8 +238,10 @@ static int sceUsbCamSetupStill(u32 paramAddr) {
static int sceUsbCamSetupStillEx(u32 paramAddr) {
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStillEx");
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillExParam))) {
Memory::ReadStruct(paramAddr, &config->stillExParam);
auto param = PSPPointer<PspUsbCamSetupStillExParam>::Create(paramAddr);
if (param.IsValid()) {
config->stillExParam = *param;
param.NotifyRead("UsbCamSetupStillEx");
}
config->type = Camera::ConfigType::CfStillEx;
return 0;
+10 -6
View File
@@ -59,8 +59,8 @@ static int sceUsbGpsGetInitDataLocation(u32 addr) {
}
static int sceUsbGpsGetState(u32 stateAddr) {
if (Memory::IsValidAddress(stateAddr)) {
Memory::Write_U32(gpsStatus, stateAddr);
if (Memory::IsValidRange(stateAddr, 4)) {
Memory::WriteUnchecked_U32(gpsStatus, stateAddr);
}
return 0;
}
@@ -81,11 +81,15 @@ static int sceUsbGpsClose() {
}
static int sceUsbGpsGetData(u32 gpsDataAddr, u32 satDataAddr) {
if (Memory::IsValidRange(gpsDataAddr, sizeof(GpsData))) {
Memory::WriteStruct(gpsDataAddr, GPS::getGpsData());
auto gpsData = PSPPointer<GpsData>::Create(gpsDataAddr);
if (gpsData.IsValid()) {
*gpsData = *GPS::getGpsData();
gpsData.NotifyWrite("UsbGpsGetData");
}
if (Memory::IsValidRange(satDataAddr, sizeof(SatData))) {
Memory::WriteStruct(satDataAddr, GPS::getSatData());
auto satData = PSPPointer<SatData>::Create(satDataAddr);
if (satData.IsValid()) {
*satData = *GPS::getSatData();
gpsData.NotifyWrite("UsbGpsGetData");
}
return 0;
}
+23 -14
View File
@@ -65,10 +65,15 @@ static inline void DelayBranchTo(u32 where)
mipsr4k.inDelaySlot = true;
}
static inline void SkipLikely()
{
PC += 8;
--mipsr4k.downcount;
static inline void SkipLikely() {
MIPSInfo delaySlot = MIPSGetInfo(Memory::Read_Instruction(PC + 4, true));
// Don't actually skip if it is a jump (seen in Brooktown High.)
if (delaySlot & IS_JUMP) {
PC += 4;
} else {
PC += 8;
--mipsr4k.downcount;
}
}
int MIPS_SingleStep()
@@ -246,17 +251,21 @@ namespace MIPSInt
void Int_JumpType(MIPSOpcode op)
{
if (mipsr4k.inDelaySlot)
_dbg_assert_msg_(false,"Jump in delay slot :(");
ERROR_LOG(CPU, "Jump in delay slot :(");
u32 off = ((op & 0x03FFFFFF) << 2);
u32 addr = (currentMIPS->pc & 0xF0000000) | off;
switch (op>>26)
{
case 2: DelayBranchTo(addr); break; //j
case 2: //j
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
case 3: //jal
R(31) = PC + 8;
DelayBranchTo(addr);
R(MIPS_REG_RA) = PC + 8;
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
@@ -268,11 +277,8 @@ namespace MIPSInt
{
if (mipsr4k.inDelaySlot)
{
// There's one of these in Star Soldier at 0881808c, which seems benign - it should probably be ignored.
if (op == 0x03e00008)
return;
// There's one of these in Star Soldier at 0881808c, which seems benign.
ERROR_LOG(CPU, "Jump in delay slot :(");
_dbg_assert_msg_(false,"Jump in delay slot :(");
}
int rs = _RS;
@@ -281,12 +287,15 @@ namespace MIPSInt
switch (op & 0x3f)
{
case 8: //jr
DelayBranchTo(addr);
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
case 9: //jalr
if (rd != 0)
R(rd) = PC + 8;
DelayBranchTo(addr);
// Update rd, but otherwise do not take the branch if we're branching.
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
}
}
+9
View File
@@ -490,3 +490,12 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength, const char
}
} // namespace
void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char * tag, size_t tagLen) {
if (MemBlockInfoDetailed(bytes)) {
if (rw & 1)
NotifyMemInfo(MemBlockFlags::WRITE, ptr, bytes, tag, tagLen);
if (rw & 2)
NotifyMemInfo(MemBlockFlags::READ, ptr, bytes, tag, tagLen);
}
}
+31 -2
View File
@@ -328,6 +328,9 @@ inline bool IsValidRange(const u32 address, const u32 size) {
} // namespace Memory
// Avoiding a global include for NotifyMemInfo.
void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char *tag, size_t tagLen);
template <typename T>
struct PSPPointer
{
@@ -438,9 +441,35 @@ struct PSPPointer
#endif
}
bool IsValid() const
bool IsValid() const {
return Memory::IsValidRange(ptr, (u32)sizeof(T));
}
T *PtrOrNull() {
if (IsValid())
return (T *)*this;
return nullptr;
}
const T *PtrOrNull() const {
if (IsValid())
return (const T *)*this;
return nullptr;
}
template <size_t tagLen>
void NotifyWrite(const char(&tag)[tagLen]) const {
PSPPointerNotifyRW(1, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1);
}
template <size_t tagLen>
void NotifyRead(const char(&tag)[tagLen]) const {
PSPPointerNotifyRW(2, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1);
}
size_t ElementSize() const
{
return Memory::IsValidAddress(ptr);
return sizeof(T);
}
static PSPPointer<T> Create(u32 ptr) {
+3 -29
View File
@@ -20,6 +20,7 @@
#include <cstring>
#include "Common/CommonTypes.h"
#include "Common/StringUtils.h"
#include "Core/Debugger/MemBlockInfo.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
@@ -70,9 +71,9 @@ inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len,
if (MemBlockInfoDetailed(len)) {
char tagData[128];
if (!tag) {
const std::string srcTag = GetMemWriteTagAt(from_address, len);
const std::string srcTag = GetMemWriteTagAt("Memcpy/", from_address, len);
tag = tagData;
tagLen = snprintf(tagData, sizeof(tagData), "Memcpy/%s", srcTag.c_str());
tagLen = truncate_cpy(tagData, srcTag.c_str());
}
NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag, tagLen);
NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag, tagLen);
@@ -108,31 +109,4 @@ inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len)
void Memset(const u32 _Address, const u8 _Data, const u32 _iLength, const char *tag = "Memset");
template<class T>
void ReadStruct(u32 address, T *ptr)
{
const u32 sz = (u32)sizeof(*ptr);
Memcpy(ptr, address, sz);
}
template<class T>
void ReadStructUnchecked(u32 address, T *ptr)
{
const u32 sz = (u32)sizeof(*ptr);
MemcpyUnchecked(ptr, address, sz);
}
template<class T>
void WriteStruct(u32 address, T *ptr)
{
const u32 sz = (u32)sizeof(*ptr);
Memcpy(address, ptr, sz);
}
template<class T>
void WriteStructUnchecked(u32 address, T *ptr)
{
const u32 sz = (u32)sizeof(*ptr);
MemcpyUnchecked(address, ptr, sz);
}
}
+2 -1
View File
@@ -573,7 +573,7 @@ public:
if (png.warning_or_error >= 2) {
ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors.");
} else if (success) {
NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfoHash, w, h);
NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str());
}
}
};
@@ -970,6 +970,7 @@ void ReplacedTexture::PrepareData(int level) {
void ReplacedTexture::PurgeIfOlder(double t) {
if (lastUsed_ < t && (!threadWaitable_ || threadWaitable_->WaitFor(0.0))) {
levelData_.clear();
initDone_ = false;
}
}
+19 -13
View File
@@ -178,27 +178,28 @@ static void BeginVertexData() {
static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color = 0xFFFFFFFF) {
if (g_RemasterMode) {
PPGeRemasterVertex vtx;
vtx.x = x; vtx.y = y; vtx.z = 0;
vtx.u = u * tw; vtx.v = v * th;
vtx.color = color;
Memory::WriteStruct(dataWritePtr, &vtx);
dataWritePtr += sizeof(vtx);
auto vtx = PSPPointer<PPGeRemasterVertex>::Create(dataWritePtr);
vtx->x = x; vtx->y = y; vtx->z = 0;
vtx->u = u * tw; vtx->v = v * th;
vtx->color = color;
dataWritePtr += (u32)vtx.ElementSize();
} else {
PPGeVertex vtx;
vtx.x = x; vtx.y = y; vtx.z = 0;
vtx.u = u * tw; vtx.v = v * th;
vtx.color = color;
Memory::WriteStruct(dataWritePtr, &vtx);
dataWritePtr += sizeof(vtx);
auto vtx = PSPPointer<PPGeVertex>::Create(dataWritePtr);
vtx->x = x; vtx->y = y; vtx->z = 0;
vtx->u = u * tw; vtx->v = v * th;
vtx->color = color;
dataWritePtr += (u32)vtx.ElementSize();
}
_dbg_assert_(dataWritePtr <= dataPtr + dataSize);
vertexCount++;
}
static void EndVertexDataAndDraw(int prim) {
_assert_msg_(vertexStart != 0, "Missing matching call to BeginVertexData()");
NotifyMemInfo(MemBlockFlags::WRITE, vertexStart, dataWritePtr - vertexStart, "PPGe Vertex");
WriteCmdAddrWithBase(GE_CMD_VADDR, vertexStart);
WriteCmd(GE_CMD_PRIM, (prim << 16) | vertexCount);
vertexStart = 0;
}
bool PPGeIsFontTextureAddress(u32 addr) {
@@ -272,6 +273,7 @@ void __PPGeInit() {
int val = i;
palette[i] = (val << 12) | 0xFFF;
}
NotifyMemInfo(MemBlockFlags::WRITE, palette.ptr, 16 * sizeof(u16_le), "PPGe Palette");
const u32_le *imagePtr = (u32_le *)imageData[0];
u8 *ramPtr = atlasPtr == 0 ? nullptr : (u8 *)Memory::GetPointer(atlasPtr);
@@ -286,7 +288,10 @@ void __PPGeInit() {
u8 cval = (a2 << 4) | a1;
ramPtr[i] = cval;
}
atlasHash = XXH3_64bits(ramPtr, atlasWidth * atlasHeight / 2);
if (atlasPtr != 0) {
atlasHash = XXH3_64bits(ramPtr, atlasSize);
NotifyMemInfo(MemBlockFlags::WRITE, atlasPtr, atlasSize, "PPGe Atlas");
}
free(imageData[0]);
@@ -457,6 +462,7 @@ void PPGeEnd()
if (dataWritePtr > dataPtr) {
// We actually drew something
gpu->EnableInterrupts(false);
NotifyMemInfo(MemBlockFlags::WRITE, dlPtr, dlWritePtr - dlPtr, "PPGe ListCmds");
u32 list = sceGeListEnQueue(dlPtr, dlWritePtr, -1, listArgs.ptr);
DEBUG_LOG(SCEGE, "PPGe enqueued display list %i", list);
gpu->EnableInterrupts(true);
+2 -3
View File
@@ -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 {
+4 -2
View File
@@ -23,6 +23,7 @@
#include "Common/Data/Collections/Hashmaps.h"
#include "GPU/GPUState.h"
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Common/IndexGenerator.h"
#include "GPU/Common/VertexDecoderCommon.h"
@@ -165,10 +166,10 @@ protected:
u32 vertexCount;
u8 indexType;
s8 prim;
u8 cullMode;
u16 indexLowerBound;
u16 indexUpperBound;
UVScale uvScale;
int cullMode;
};
enum { MAX_DEFERRED_DRAW_CALLS = 128 };
@@ -186,12 +187,13 @@ protected:
GEPrimitiveType prevPrim_ = GE_PRIM_INVALID;
// Shader blending state
bool fboTexNeedsBind_ = false;
bool fboTexBound_ = false;
// Sometimes, unusual situations mean we need to reset dirty flags after state calc finishes.
uint64_t dirtyRequiresRecheck_ = 0;
ComputedPipelineState pipelineState_;
// Hardware tessellation
TessellationDataTransfer *tessDataTransfer;
};
+19 -15
View File
@@ -121,7 +121,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
bool useDiscardStencilBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL);
bool readFramebuffer = replaceBlend == REPLACE_BLEND_COPY_FBO || colorWriteMask;
bool readFramebuffer = replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER || colorWriteMask;
bool readFramebufferTex = readFramebuffer && !gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH);
bool needFragCoord = readFramebuffer || gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT);
@@ -132,11 +132,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
return false;
}
if (readFramebuffer && compat.shaderLanguage == HLSL_D3D9) {
*errorString = "Framebuffer read not yet supported in HLSL D3D9";
return false;
}
if (compat.shaderLanguage == ShaderLanguage::GLSL_VULKAN) {
if (useDiscardStencilBugWorkaround && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) {
WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n");
@@ -227,7 +222,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
}
if (readFramebufferTex) {
// No sampler required, we Load
WRITE(p, "Texture2D<vec4> fboTex : register(t1);\n");
WRITE(p, "Texture2D<vec4> fbotex : register(t1);\n");
}
WRITE(p, "cbuffer base : register(b0) {\n%s};\n", ub_baseStr);
@@ -264,8 +259,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
if (enableFog) {
WRITE(p, " float v_fogdepth: TEXCOORD1;\n");
}
if (compat.shaderLanguage == HLSL_D3D11 && needFragCoord) {
WRITE(p, " vec4 pixelPos : SV_POSITION;\n");
if (needFragCoord) {
if (compat.shaderLanguage == HLSL_D3D11) {
WRITE(p, " vec4 pixelPos : SV_POSITION;\n");
} else if (compat.shaderLanguage == HLSL_D3D9) {
WRITE(p, " vec4 pixelPos : VPOS;\n"); // VPOS is only supported for Shader Model 3.0, but we can probably forget about D3D9 SM2.0 at this point...
}
}
WRITE(p, "};\n");
@@ -457,7 +456,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
} else if (compat.shaderLanguage == HLSL_D3D9) {
WRITE(p, "PS_OUT main( PS_IN In ) {\n");
WRITE(p, " PS_OUT outfragment;\n");
WRITE(p, " vec4 target;\n");
if (needFragCoord) {
WRITE(p, " vec4 gl_FragCoord = In.pixelPos;\n");
}
} else {
WRITE(p, "void main() {\n");
}
@@ -477,7 +478,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
// Two things read from the old framebuffer - shader replacement blending and bit-level masking.
if (readFramebuffer) {
if (compat.shaderLanguage == HLSL_D3D11) {
WRITE(p, " vec4 destColor = fboTex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n");
WRITE(p, " vec4 destColor = fbotex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n");
} else if (compat.shaderLanguage == HLSL_D3D9) {
WRITE(p, " vec4 destColor = tex2D(fbotex, gl_FragCoord.xy * u_fbotexSize.xy);\n", compat.texture);
} else if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) {
// If we have EXT_shader_framebuffer_fetch / ARM_shader_framebuffer_fetch, we skip the blit.
// We can just read the prev value more directly.
@@ -940,7 +943,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
WRITE(p, " v.rgb = v.rgb * %s;\n", srcFactor);
}
if (replaceBlend == REPLACE_BLEND_COPY_FBO) {
if (replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER) {
const char *srcFactor = nullptr;
const char *dstFactor = nullptr;
@@ -1057,9 +1060,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
return false;
}
// TODO: This could support more ops using the shader blending mechanism.
LogicOpReplaceType replaceLogicOpType = (LogicOpReplaceType)id.Bits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2);
switch (replaceLogicOpType) {
// TODO: We could have a separate mechanism to support more ops using the shader blending mechanism,
// on hardware that can do proper bit math in fragment shaders.
SimulateLogicOpType simulateLogicOpType = (SimulateLogicOpType)id.Bits(FS_BIT_SIMULATE_LOGIC_OP_TYPE, 2);
switch (simulateLogicOpType) {
case LOGICOPTYPE_ONE:
WRITE(p, " %s.rgb = splat3(1.0);\n", compat.fragColor0);
break;
+152 -93
View File
@@ -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 &params, u32 skipDrawReason) {
static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width);
VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(FramebufferHeuristicParams &params, 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(&params, &drawing_width);
} else {
gstate_c.SetCurRTOffset(0, 0);
}
// Find a matching framebuffer.
VirtualFramebuffer *vfb = nullptr;
for (auto v : vfbs_) {
@@ -378,7 +396,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
vfb->height = drawing_height;
}
break;
} else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format) {
} else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format && !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) {
u32 v_fb_first_line_end_ptr = v->fb_address + v->fb_stride * bpp;
u32 v_fb_end_ptr = v->fb_address + v->fb_stride * v->height * bpp;
@@ -477,7 +495,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
currentRenderVfb_ = vfb;
// Assume that if we're clearing right when switching to a new framebuffer, we don't need to upload.
if (useBufferedRendering_ && !g_Config.bDisableSlowFramebufEffects && params.isDrawing) {
if (useBufferedRendering_ && params.isDrawing) {
gpu->PerformMemoryUpload(params.fb_address, byteSize);
// Alpha was already done by PerformMemoryUpload.
PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA);
@@ -659,7 +677,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
// This will result in reinterpret later, if both formats are 16-bit.
sources.push_back(CopySource{ src, RASTER_COLOR, 0, 0 });
} else {
// Happens in Prince of Persia - Revelations. Ignoring.
// This shouldn't happen anymore. I think when it happened last, we still had
// lax stride checking when video was incoming, and a resize happened causing a duplicate.
}
} else if (src->fb_stride == dst->fb_stride && src->fb_format == dst->fb_format) {
u32 bytesPerPixel = BufferFormatBytesPerPixel(src->fb_format);
@@ -789,7 +808,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
}
if (dst != currentRenderVfb_ && tookActions) {
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After FakeReinterpret");
// Will probably just change the name of the current renderpass, since one was started by the reinterpret itself.
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After Reinterpret");
}
shaderManager_->DirtyLastShader();
@@ -886,7 +906,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer
}
void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb) {
// ugly...
if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) {
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
}
@@ -898,11 +917,12 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer
void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) {
if (ShouldDownloadFramebuffer(vfb) && !vfb->memoryUpdated) {
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height);
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
} else {
DownloadFramebufferOnSwitch(prevVfb);
}
textureCache_->ForgetLastTexture();
shaderManager_->DirtyLastShader();
@@ -959,41 +979,41 @@ void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int stride,
}
}
void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size, bool safe) {
void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) {
// Take off the uncached flag from the address. Not to be confused with the start of VRAM.
addr &= 0x3FFFFFFF;
// TODO: Could go through all FBOs, but probably not important?
// TODO: Could also check for inner changes, but video is most important.
// TODO: This shouldn't care if it's a display framebuf or not, should work exactly the same.
bool isDisplayBuf = addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr();
if (isDisplayBuf || safe) {
// TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help.
if (!Memory::IsValidAddress(displayFramebufPtr_))
return;
// TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help.
if (!Memory::IsValidAddress(displayFramebufPtr_))
return;
for (size_t i = 0; i < vfbs_.size(); ++i) {
VirtualFramebuffer *vfb = vfbs_[i];
if (vfb->fb_address == addr) {
FlushBeforeCopy();
for (size_t i = 0; i < vfbs_.size(); ++i) {
VirtualFramebuffer *vfb = vfbs_[i];
if (vfb->fb_address == addr) {
FlushBeforeCopy();
if (useBufferedRendering_ && vfb->fbo) {
GEBufferFormat fmt = vfb->fb_format;
if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) {
// If we're not rendering to it, format may be wrong. Use displayFormat_ instead.
fmt = displayFormat_;
}
DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height);
SetColorUpdated(vfb, gstate_c.skipDrawReason);
} else {
INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->fb_format);
DestroyFramebuf(vfb);
vfbs_.erase(vfbs_.begin() + i--);
if (useBufferedRendering_ && vfb->fbo) {
GEBufferFormat fmt = vfb->fb_format;
if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) {
// If we're not rendering to it, format may be wrong. Use displayFormat_ instead.
// TODO: This doesn't seem quite right anymore.
fmt = displayFormat_;
}
DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height);
SetColorUpdated(vfb, gstate_c.skipDrawReason);
} else {
INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format));
DestroyFramebuf(vfb);
vfbs_.erase(vfbs_.begin() + i--);
}
}
RebindFramebuffer("RebindFramebuffer - UpdateFromMemory");
}
RebindFramebuffer("RebindFramebuffer - UpdateFromMemory");
// TODO: Necessary?
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
}
@@ -1106,7 +1126,7 @@ void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer
}
if (x < src->drawnWidth && y < src->drawnHeight && w > 0 && h > 0) {
BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, "Blit_CopyFramebufferForColorTexture");
BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, RASTER_COLOR, "Blit_CopyFramebufferForColorTexture");
}
}
@@ -1215,8 +1235,8 @@ void FramebufferManagerCommon::DownloadFramebufferOnSwitch(VirtualFramebuffer *v
// Some games will draw to some memory once, and use it as a render-to-texture later.
// To support this, we save the first frame to memory when we have a safe w/h.
// Saving each frame would be slow.
if (!g_Config.bDisableSlowFramebufEffects && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) {
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight);
if (g_Config.bBlockTransferGPU && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) {
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR);
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
vfb->safeWidth = 0;
vfb->safeHeight = 0;
@@ -1369,7 +1389,7 @@ void FramebufferManagerCommon::DecimateFBOs() {
int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used);
if (ShouldDownloadFramebuffer(vfb) && age == 0 && !vfb->memoryUpdated) {
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height);
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
}
@@ -1502,9 +1522,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
if (vfb->fbo) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
if (!skipCopy) {
// TODO: In this case, it'll nearly always be better to draw the old framebuffer to the new one than to do an actual blit.
// Usually hardly a performance issue though.
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, "Blit_ResizeFramebufFBO");
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "Blit_ResizeFramebufFBO");
// Depth copying is handled by deferred copies later.
}
}
fbosToDelete_.push_back(old.fbo);
@@ -1608,7 +1627,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
} else {
WARN_LOG_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy %08x -> %08x (size: %x)", src, dst, size);
// Just do the blit!
BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, "Blit_InterBufferMemcpy");
BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, RASTER_COLOR, "Blit_InterBufferMemcpy");
SetColorUpdated(dstBuffer, skipDrawReason);
RebindFramebuffer("RebindFramebuffer - Inter-buffer memcpy");
}
@@ -1630,8 +1649,8 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
FlushBeforeCopy();
if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) {
WARN_LOG_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight);
} else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated && !PSP_CoreParameter().compat.flags().DisableReadbacks) {
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH);
} else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated) {
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, RASTER_COLOR);
srcBuffer->usageFlags = (srcBuffer->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR;
}
return false;
@@ -1660,7 +1679,7 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p
int x_bytes = x_pixels * bpp;
int w_bytes = w_pixels * bpp;
std::vector<BlockTransferRect> candidates;
TinySet<BlockTransferRect, 4> candidates;
// We work entirely in bytes when we do the matching, because games don't consistently use bpps that match
// that of their buffers. Then after matching we try to map the copy to the simplest operation that does
@@ -1735,20 +1754,27 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p
candidates.push_back(candidate);
}
const BlockTransferRect *best = nullptr;
// Sort candidates by just recency for now, we might add other.
std::sort(candidates.begin(), candidates.end());
for (size_t i = 0; i < candidates.size(); i++) {
const BlockTransferRect *candidate = &candidates[i];
if (!best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq) {
best = candidate;
}
}
if (candidates.size() > 1) {
std::string log;
for (auto &candidate : candidates) {
log += " - " + candidate.ToString() + "\n";
if (Reporting::ShouldLogNTimes("mulblock", 5)) {
std::string log;
for (size_t i = 0; i < candidates.size(); i++) {
log += " - " + candidates[i].ToString() + "\n";
}
WARN_LOG(G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str());
}
WARN_LOG_N_TIMES(mulblock, 5, G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str());
}
if (!candidates.empty()) {
// Pick the last candidate.
*rect = candidates.back();
*rect = *best;
return true;
} else {
if (Memory::IsVRAMAddress(basePtr) && destination && h >= 128) {
@@ -1798,7 +1824,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
}
// 1:1 pixel sides buffers, we resize buffers to these before we read them back.
VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb) {
VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel) {
// For now we'll keep these on the same struct as the ones that can get displayed
// (and blatantly copy work already done above while at it).
VirtualFramebuffer *nvfb = nullptr;
@@ -1837,12 +1863,13 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
char name[64];
snprintf(name, sizeof(name), "download_temp");
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, name });
// TODO: We don't have a way to create a depth-only framebuffer yet.
// Also, at least on Vulkan we always create both depth and color, need to rework how we handle renderpasses.
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, channel == RASTER_DEPTH ? true : false, name });
if (!nvfb->fbo) {
ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight);
return nullptr;
}
bvfbs_.push_back(nvfb);
} else {
UpdateDownloadTempBuffer(nvfb);
@@ -2012,7 +2039,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
dstBasePtr, dstRect.x_bytes / bpp, dstRect.y, dstStride);
FlushBeforeCopy();
// Some backends can handle blitting within a framebuffer. Others will just have to deal with it or ignore it, apparently.
BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, "Blit_IntraBufferBlockTransfer");
BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, RASTER_COLOR, "Blit_IntraBufferBlockTransfer");
RebindFramebuffer("rebind after intra block transfer");
SetColorUpdated(dstRect.vfb, skipDrawReason);
return true; // Skip the memory copy.
@@ -2033,7 +2060,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
bpp = buffer_bpp;
}
FlushBeforeCopy();
BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, "Blit_InterBufferBlockTransfer");
BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, RASTER_COLOR, "Blit_InterBufferBlockTransfer");
RebindFramebuffer("RebindFramebuffer - Inter-buffer block transfer");
SetColorUpdated(dstRect.vfb, skipDrawReason);
return true;
@@ -2068,7 +2095,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
if (tooTall) {
WARN_LOG_ONCE(btdheight, G3D, "Block transfer download %08x -> %08x dangerous, %d+%d is taller than %d", srcBasePtr, dstBasePtr, srcRect.y, srcRect.h, srcRect.vfb->bufferHeight);
}
ReadFramebufferToMemory(srcRect.vfb, static_cast<int>(srcX * srcXFactor), srcY, static_cast<int>(srcRect.w_bytes * srcXFactor), srcRect.h);
ReadFramebufferToMemory(srcRect.vfb, static_cast<int>(srcX * srcXFactor), srcY, static_cast<int>(srcRect.w_bytes * srcXFactor), srcRect.h, RASTER_COLOR);
srcRect.vfb->usageFlags = (srcRect.vfb->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR;
}
}
@@ -2248,7 +2275,7 @@ void FramebufferManagerCommon::ShowScreenResolution() {
// We might also want to implement an asynchronous callback-style version of this. Would probably
// only be possible to implement optimally on Vulkan, but on GL and D3D11 we could do pixel buffers
// and read on the next frame, then call the callback. PackFramebufferAsync_ on OpenGL already does something similar.
// and read on the next frame, then call the callback.
//
// The main use cases for this are:
// * GE debugging(in practice async will not matter because it will stall anyway.)
@@ -2286,7 +2313,7 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB
tempVfb.renderWidth = w;
tempVfb.renderHeight = h;
tempVfb.renderScaleFactor = maxScaleFactor;
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, "Blit_GetFramebuffer");
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, RASTER_COLOR, "Blit_GetFramebuffer");
bound = tempFBO;
} else {
@@ -2395,33 +2422,33 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
return retval;
}
// This function takes an already correctly-sized framebuffer and packs it into RAM.
// This function takes an already correctly-sized framebuffer and reads it into emulated PSP VRAM.
// Does not need to account for scaling.
//
// Color conversion is currently done on CPU but should theoretically be done on GPU.
// (Except using the GPU might cause problems because of various implementations'
// dithering behavior and games that expect exact colors like Danganronpa, so we
// can't entirely be rid of the CPU path.) -- unknown
void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
if (!vfb->fbo) {
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0");
return;
}
void FramebufferManagerCommon::PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
if (w <= 0 || h <= 0) {
ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync_: %d %d %d %d", x, y, w, h);
ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync: %d %d %d %d", x, y, w, h);
return;
}
const u32 fb_address = vfb->fb_address & 0x3FFFFFFF;
Draw::DataFormat destFormat = GEFormatToThin3D(vfb->fb_format);
Draw::DataFormat destFormat = channel == RASTER_COLOR ? GEFormatToThin3D(vfb->fb_format) : GEFormatToThin3D(GE_FORMAT_DEPTH16);
const int dstBpp = (int)DataFormatSizeInBytes(destFormat);
const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp;
const int dstSize = (h * vfb->fb_stride + w - 1) * dstBpp;
int stride = channel == RASTER_COLOR ? vfb->fb_stride : vfb->z_stride;
const int dstByteOffset = (y * stride + x) * dstBpp;
// Leave the gap between the end of the last line and the full stride.
// This is only used for the NotifyMemInfo range.
const int dstSize = (h * stride + w - 1) * dstBpp;
if (!Memory::IsValidRange(fb_address + dstByteOffset, dstSize)) {
ERROR_LOG_REPORT(G3D, "PackFramebufferSync_ would write outside of memory, ignoring");
ERROR_LOG_REPORT(G3D, "PackFramebufferSync would write outside of memory, ignoring");
return;
}
@@ -2432,18 +2459,18 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int
DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x, ptr=%p", fb_address, destPtr);
if (destPtr) {
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync_");
draw_->CopyFramebufferToMemorySync(vfb->fbo, channel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync");
char tag[128];
size_t len = snprintf(tag, sizeof(tag), "FramebufferPack/%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->fb_format));
NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, tag, len);
} else {
ERROR_LOG(G3D, "PackFramebufferSync_: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride);
ERROR_LOG(G3D, "PackFramebufferSync: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride);
}
gpuStats.numReadbacks++;
}
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
// Clamp to bufferWidth. Sometimes block transfers can cause this to hit.
if (x + w >= vfb->bufferWidth) {
w = vfb->bufferWidth - x;
@@ -2481,13 +2508,13 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb,
}
if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) {
// No need to blit
PackFramebufferSync_(vfb, x, y, w, h);
// No need to stretch-blit
PackFramebufferSync(vfb, x, y, w, h, channel);
} else {
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, channel);
if (nvfb) {
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_ReadFramebufferToMemory");
PackFramebufferSync_(nvfb, x, y, w, h);
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, channel, "Blit_ReadFramebufferToMemory");
PackFramebufferSync(nvfb, x, y, w, h, channel);
}
}
@@ -2536,10 +2563,10 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo
vfb->clutUpdatedBytes = loadBytes;
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, RASTER_COLOR);
if (nvfb) {
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_DownloadFramebufferForClut");
PackFramebufferSync_(nvfb, x, y, w, h);
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, RASTER_COLOR, "Blit_DownloadFramebufferForClut");
PackFramebufferSync(nvfb, x, y, w, h, RASTER_COLOR);
}
textureCache_->ForgetLastTexture();
@@ -2664,9 +2691,7 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
}
void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) {
RasterChannel channel = RASTER_COLOR;
void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag) {
if (!dst->fbo || !src->fbo || !useBufferedRendering_) {
// This can happen if they recently switched from non-buffered.
if (useBufferedRendering_) {
@@ -2697,8 +2722,8 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
return;
}
bool useBlit = draw_->GetDeviceCaps().framebufferBlitSupported;
bool useCopy = draw_->GetDeviceCaps().framebufferCopySupported;
bool useBlit = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferBlitSupported : false;
bool useCopy = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferCopySupported : false;
if (dst == currentRenderVfb_) {
// If already bound, using either a blit or a copy is unlikely to be an optimization.
// So we're gonna use a raster draw instead.
@@ -2767,7 +2792,7 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
draw_->InvalidateCachedState();
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE);
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
}
// The input is raw pixel coordinates, scale not taken into account.
@@ -2859,3 +2884,37 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi
vfb->colorBindSeq = GetBindSeqCount();
return vfb;
}
static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width) {
// Detect whether we're rendering to the margin.
bool margin;
if ((params->scissorRight - params->scissorLeft) == 32) {
// Title screen has this easy case. It also uses non-through verts, so lucky for us that we have this.
margin = true;
} else if (params->scissorRight == 480) {
margin = false;
} else {
// Go deep, look at the vertices. Killzone-specific, of course.
margin = false;
if ((gstate.vertType & 0xFFFFFF) == 0x00800102) { // through, u16, s16
u16 *vdata = (u16 *)Memory::GetPointerUnchecked(gstate_c.vertexAddr);
int v0PosU = vdata[0];
int v0PosX = vdata[2];
if (v0PosX >= 480 && v0PosU < 480) {
// Texturing from surface, writing to margin
margin = true;
}
}
}
if (margin) {
gstate_c.SetCurRTOffset(-480, 0);
// Modify the fb_address and z_address too to avoid matching below.
params->fb_address += 480 * 4;
params->z_address += 480 * 2;
*drawing_width = 32;
} else {
gstate_c.SetCurRTOffset(0, 0);
*drawing_width = 480;
}
}
+16 -15
View File
@@ -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 &params, u32 skipDrawReason);
VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams &params, 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);
+123 -114
View File
@@ -179,7 +179,7 @@ ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend) {
return REPLACE_ALPHA_NO;
}
if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_COPY_FBO) {
if (replaceBlend != REPLACE_BLEND_NO && replaceBlend != REPLACE_BLEND_READ_FRAMEBUFFER) {
if (nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncB()]) {
return REPLACE_ALPHA_YES;
} else {
@@ -257,7 +257,7 @@ StencilValueType ReplaceAlphaWithStencilType() {
return STENCIL_VALUE_KEEP;
}
ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferFormat bufferFormat) {
ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat) {
if (gstate_c.blueToAlpha) {
return REPLACE_BLEND_BLUE_TO_ALPHA;
}
@@ -270,14 +270,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
// Let's get the non-factor modes out of the way first.
switch (eq) {
case GE_BLENDMODE_ABSDIFF:
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_BLENDMODE_MIN:
case GE_BLENDMODE_MAX:
if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) {
return REPLACE_BLEND_STANDARD;
} else {
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
}
default:
@@ -300,19 +300,19 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
return REPLACE_BLEND_2X_ALPHA;
// Can't double, we need the source color to be correct.
// Doubling only alpha would clamp the src alpha incorrectly.
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
if (bufferFormat == GE_FORMAT_565)
return REPLACE_BLEND_2X_ALPHA;
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLESRCALPHA:
// We can't technically do this correctly (due to clamping) without reading the dst color.
// Using a copy isn't accurate either, though, when there's overlap.
if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH))
return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
@@ -339,7 +339,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
return REPLACE_BLEND_STANDARD;
}
// Can't double, we need the source color to be correct.
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
@@ -348,7 +348,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
// Doubling will have no effect here.
return REPLACE_BLEND_STANDARD;
}
return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLESRCALPHA:
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
@@ -357,7 +357,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
}
// Double both src (for dst alpha) and alpha (for dst factor.)
// But to be accurate (clamping), we need to read the dst color.
return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
@@ -369,7 +369,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
return REPLACE_BLEND_STANDARD;
}
// We can't technically do this correctly (due to clamping) without reading the dst alpha.
return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
}
case GE_SRCBLEND_DOUBLEINVDSTALPHA:
@@ -383,14 +383,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLESRCALPHA:
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_2X_ALPHA;
}
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
@@ -401,7 +401,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
}
case GE_SRCBLEND_FIXA:
@@ -409,7 +409,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
switch (funcB) {
case GE_DSTBLEND_DOUBLESRCALPHA:
// Can't safely double alpha, will clamp.
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
// Doubling alpha is safe for the inverse, will clamp to zero either way.
@@ -420,7 +420,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
case GE_DSTBLEND_FIXB:
default:
@@ -454,14 +454,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) {
// Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap.
if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH))
return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
} else {
// This means dst alpha/color is used in the src factor.
// Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?)
// We will just hope that doubling alpha for the dst factor will not clamp too badly.
if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH))
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
return REPLACE_BLEND_2X_ALPHA;
}
@@ -478,7 +478,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_READ_FRAMEBUFFER;
default:
return REPLACE_BLEND_STANDARD;
@@ -489,27 +489,6 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferForma
return REPLACE_BLEND_STANDARD;
}
LogicOpReplaceType ReplaceLogicOpType() {
if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) {
switch (gstate.getLogicOp()) {
case GE_LOGIC_COPY_INVERTED:
case GE_LOGIC_AND_INVERTED:
case GE_LOGIC_OR_INVERTED:
case GE_LOGIC_NOR:
case GE_LOGIC_NAND:
case GE_LOGIC_EQUIV:
return LOGICOPTYPE_INVERT;
case GE_LOGIC_INVERTED:
return LOGICOPTYPE_ONE;
case GE_LOGIC_SET:
return LOGICOPTYPE_ONE;
default:
return LOGICOPTYPE_NORMAL;
}
}
return LOGICOPTYPE_NORMAL;
}
static const float DEPTH_SLICE_FACTOR_HIGH = 4.0f;
static const float DEPTH_SLICE_FACTOR_16BIT = 256.0f;
@@ -581,8 +560,11 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
renderHeightFactor = renderHeight / 272.0f;
}
renderX = gstate_c.curRTOffsetX;
renderY = gstate_c.curRTOffsetY;
// We take care negative offsets of in the projection matrix.
// These come from split framebuffers (Killzone).
// TODO: Might be safe to do get rid of this here and do the same for positive offsets?
renderX = std::max(gstate_c.curRTOffsetX, 0);
renderY = std::max(gstate_c.curRTOffsetY, 0);
// Scissor
int scissorX1 = gstate.getScissorX1();
@@ -609,6 +591,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
float offsetY = gstate.getOffsetY();
if (out.throughMode) {
// If renderX/renderY are offset to compensate for a split framebuffer,
// applying the offset to the viewport isn't enough, since the viewport clips.
// We need to apply either directly to the vertices, or to the "through" projection matrix.
out.viewportX = renderX * renderWidthFactor + displayOffsetX;
out.viewportY = renderY * renderHeightFactor + displayOffsetY;
out.viewportW = curRTWidth * renderWidthFactor;
@@ -868,8 +853,10 @@ static inline bool blendColorSimilar(uint32_t a, uint32_t b, int margin = 25) {
return false;
}
// Try to simulate some common logic ops.
static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) {
// Try to simulate some common logic ops by using blend, if needed.
// The shader might also need modification, the below function SimulateLogicOpShaderTypeIfNeeded
// takes care of that.
static void SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) {
// Note: our shader solution applies logic ops BEFORE blending, not correctly after.
// This is however fine for the most common ones, like CLEAR/NOOP/SET, etc.
if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
@@ -931,7 +918,28 @@ static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &
}
}
// Try to simulate some common logic ops.
// Choose the shader part of the above logic op fallback simulation.
SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded() {
if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP) && gstate.isLogicOpEnabled()) {
switch (gstate.getLogicOp()) {
case GE_LOGIC_COPY_INVERTED:
case GE_LOGIC_AND_INVERTED:
case GE_LOGIC_OR_INVERTED:
case GE_LOGIC_NOR:
case GE_LOGIC_NAND:
case GE_LOGIC_EQUIV:
return LOGICOPTYPE_INVERT;
case GE_LOGIC_INVERTED:
return LOGICOPTYPE_ONE;
case GE_LOGIC_SET:
return LOGICOPTYPE_ONE;
default:
return LOGICOPTYPE_NORMAL;
}
}
return LOGICOPTYPE_NORMAL;
}
void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) {
StencilValueType stencilType = STENCIL_VALUE_KEEP;
if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) {
@@ -942,7 +950,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS
BlendFactor srcBlend = BlendFactor::ONE;
BlendFactor dstBlend = BlendFactor::ZERO;
BlendEq blendEq = BlendEq::ADD;
ApplyLogicOp(srcBlend, dstBlend, blendEq);
SimulateLogicOpIfNeeded(srcBlend, dstBlend, blendEq);
// We're not blending, but we may still want to "blend" for stencil.
// This is only useful for INCR/DECR/INVERT. Others can write directly.
@@ -950,7 +958,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS
case STENCIL_VALUE_INCR_4:
case STENCIL_VALUE_INCR_8:
// We'll add the incremented value output by the shader.
blendState.enabled = true;
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE);
blendState.setEquation(blendEq, BlendEq::ADD);
break;
@@ -958,23 +966,23 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS
case STENCIL_VALUE_DECR_4:
case STENCIL_VALUE_DECR_8:
// We'll subtract the incremented value output by the shader.
blendState.enabled = true;
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE);
blendState.setEquation(blendEq, BlendEq::SUBTRACT);
break;
case STENCIL_VALUE_INVERT:
// The shader will output one, and reverse subtracting will essentially invert.
blendState.enabled = true;
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ONE);
blendState.setEquation(blendEq, BlendEq::REVERSE_SUBTRACT);
break;
default:
if (srcBlend == BlendFactor::ONE && dstBlend == BlendFactor::ZERO && blendEq == BlendEq::ADD) {
blendState.enabled = false;
blendState.blendEnabled = false;
} else {
blendState.enabled = true;
blendState.blendEnabled = true;
blendState.setFactors(srcBlend, dstBlend, BlendFactor::ONE, BlendFactor::ZERO);
blendState.setEquation(blendEq, BlendEq::ADD);
}
@@ -982,86 +990,59 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS
}
}
bool IsColorWriteMaskComplex(bool allowFramebufferRead) {
// Restrict to Outrun temporarily (by uglily reusing the ReinterpretFramebuffers flag)
// This check must match the one in ConvertMaskState.
if (!allowFramebufferRead || !PSP_CoreParameter().compat.flags().ShaderColorBitmask) {
// Don't have a choice - we'll make do but it won't always be right.
return false;
}
if (gstate_c.blueToAlpha) {
// We'll generate a simple ___A mask.
return false;
}
uint32_t colorMask = (gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24);
for (int i = 0; i < 4; i++) {
switch (colorMask & 0xFF) {
case 0x0:
case 0xFF:
break;
default:
return true;
}
colorMask >>= 8;
}
return false;
}
// If we can we emulate the colorMask by simply toggling the full R G B A masks offered
// by modern hardware, we do that. This is 99.9% of the time.
// When that's not enough, we fall back on a technique similar to shader blending,
// we read from the framebuffer (or a copy of it).
void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) {
// We also prepare uniformMask so that if doing this in the shader gets forced-on,
// we have the right mask already.
void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported) {
if (gstate_c.blueToAlpha) {
maskState.applyFramebufferRead = false;
maskState.rgba[0] = false;
maskState.rgba[1] = false;
maskState.rgba[2] = false;
maskState.rgba[3] = true;
maskState.uniformMask = 0xFF000000;
maskState.channelMask = 0x8;
return;
}
// Invert to convert masks from the PSP's format where 1 is don't draw to PC where 1 is draw.
uint32_t colorMask = ~((gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24));
maskState.uniformMask = colorMask;
maskState.applyFramebufferRead = false;
maskState.channelMask = 0;
for (int i = 0; i < 4; i++) {
int channelMask = colorMask & 0xFF;
switch (channelMask) {
case 0x0:
maskState.rgba[i] = false;
break;
case 0xFF:
maskState.rgba[i] = true;
maskState.channelMask |= 1 << i;
break;
default:
if (allowFramebufferRead) {
// Instead of just 'true', restrict shader bitmasks to Outrun temporarily.
// TODO: This check must match the one in IsColorWriteMaskComplex.
maskState.applyFramebufferRead = PSP_CoreParameter().compat.flags().ShaderColorBitmask;
maskState.rgba[i] = true;
if (shaderBitOpsSupported && PSP_CoreParameter().compat.flags().ShaderColorBitmask) {
// Shaders can emulate masking accurately. Let's make use of that.
maskState.applyFramebufferRead = true;
maskState.channelMask |= 1 << i;
} else {
// Use the old heuristic.
maskState.rgba[i] = channelMask >= 128;
// Use the old inaccurate heuristic.
if (channelMask >= 128) {
maskState.channelMask |= 1 << i;
}
}
}
colorMask >>= 8;
}
// Let's not write to alpha if stencil isn't enabled.
if (IsStencilTestOutputDisabled()) {
maskState.rgba[3] = false;
} else if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) {
// If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel.
maskState.rgba[3] = false;
// Also if the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel.
if (IsStencilTestOutputDisabled() || ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) {
maskState.channelMask &= ~8;
maskState.uniformMask &= ~0xFF000000;
}
}
// Called even if AlphaBlendEnable == false - it also deals with stencil-related blend state.
void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead, bool forceReplaceBlend) {
void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) {
// Blending is a bit complex to emulate. This is due to several reasons:
//
// * Doubled blend modes (src, dst, inversed) aren't supported in OpenGL.
@@ -1075,52 +1056,52 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead,
blendState.applyFramebufferRead = false;
blendState.dirtyShaderBlendFixValues = false;
blendState.useBlendColor = false;
blendState.replaceAlphaWithStencil = REPLACE_ALPHA_NO;
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(allowFramebufferRead, gstate_c.framebufFormat);
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.framebufFormat);
if (forceReplaceBlend) {
replaceBlend = REPLACE_BLEND_COPY_FBO;
replaceBlend = REPLACE_BLEND_READ_FRAMEBUFFER;
}
blendState.replaceBlend = replaceBlend;
blendState.simulateLogicOpType = SimulateLogicOpShaderTypeIfNeeded();
ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend);
blendState.replaceAlphaWithStencil = replaceAlphaWithStencil;
bool usePreSrc = false;
bool blueToAlpha = false;
switch (replaceBlend) {
case REPLACE_BLEND_NO:
blendState.resetFramebufferRead = true;
// We may still want to do something about stencil -> alpha.
ApplyStencilReplaceAndLogicOpIgnoreBlend(replaceAlphaWithStencil, blendState);
return;
case REPLACE_BLEND_BLUE_TO_ALPHA:
blueToAlpha = true;
blendState.enabled = gstate.isAlphaBlendEnabled();
blendState.blendEnabled = gstate.isAlphaBlendEnabled();
// We'll later convert the color blend to blend in the alpha channel.
break;
case REPLACE_BLEND_COPY_FBO:
blendState.enabled = true;
case REPLACE_BLEND_READ_FRAMEBUFFER:
blendState.blendEnabled = true;
blendState.applyFramebufferRead = true;
blendState.resetFramebufferRead = false;
blendState.replaceAlphaWithStencil = replaceAlphaWithStencil;
break;
case REPLACE_BLEND_PRE_SRC:
case REPLACE_BLEND_PRE_SRC_2X_ALPHA:
blendState.enabled = true;
blendState.blendEnabled = true;
usePreSrc = true;
break;
case REPLACE_BLEND_STANDARD:
case REPLACE_BLEND_2X_ALPHA:
case REPLACE_BLEND_2X_SRC:
blendState.enabled = true;
blendState.blendEnabled = true;
break;
}
blendState.resetFramebufferRead = true;
const GEBlendMode blendFuncEq = gstate.getBlendEq();
GEBlendSrcFactor blendFuncA = gstate.getBlendFuncA();
GEBlendDstFactor blendFuncB = gstate.getBlendFuncB();
@@ -1269,8 +1250,8 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead,
colorEq = eqLookupNoMinMax[blendFuncEq];
}
// Attempt to apply the logic op, if any.
ApplyLogicOp(glBlendFuncA, glBlendFuncB, colorEq);
// Attempt to apply simulated logic ops, if any and if needed.
SimulateLogicOpIfNeeded(glBlendFuncA, glBlendFuncB, colorEq);
// The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't
// do any blending in the alpha channel as that doesn't seem to happen on PSP. So, we attempt to
@@ -1485,6 +1466,19 @@ static void ConvertStencilFunc5551(GenericStencilFuncState &state) {
}
}
void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported) {
// Just do the non-shader case for now.
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
logicOpState.logicOpEnabled = gstate.isLogicOpEnabled() && logicSupported;
logicOpState.logicOp = gstate.isLogicOpEnabled() ? gstate.getLogicOp() : GE_LOGIC_COPY;
logicOpState.applyFramebufferRead = false;
} else {
logicOpState.logicOpEnabled = false;
logicOpState.logicOp = GE_LOGIC_COPY;
logicOpState.applyFramebufferRead = false; // true later?
}
}
static void ConvertStencilMask5551(GenericStencilFuncState &state) {
state.writeMask = state.writeMask >= 0x80 ? 0xff : 0x00;
}
@@ -1532,3 +1526,18 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) {
break;
}
}
void GenericMaskState::Log() {
WARN_LOG(G3D, "Mask: %01X readfb=%d", uniformMask, channelMask, applyFramebufferRead);
}
void GenericBlendState::Log() {
WARN_LOG(G3D, "Blend: hwenable=%d readfb=%d replblend=%d replalpha=%d",
blendEnabled, applyFramebufferRead, replaceBlend, (int)replaceAlphaWithStencil);
}
void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) {
ConvertMaskState(maskState, shaderBitOpsSuppported);
ConvertLogicOpState(logicState, gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP), shaderBitOpsSuppported);
ConvertBlendState(blendState, maskState.applyFramebufferRead);
}
+61 -11
View File
@@ -39,13 +39,13 @@ enum ReplaceBlendType {
// Full blend equation runs in shader.
// We might have to make a copy of the framebuffer target to read from.
REPLACE_BLEND_COPY_FBO,
REPLACE_BLEND_READ_FRAMEBUFFER,
// Color blend mode and color gets copied to alpha blend mode.
REPLACE_BLEND_BLUE_TO_ALPHA,
};
enum LogicOpReplaceType {
enum SimulateLogicOpType {
LOGICOPTYPE_NORMAL,
LOGICOPTYPE_ONE,
LOGICOPTYPE_INVERT,
@@ -60,9 +60,10 @@ bool IsStencilTestOutputDisabled();
StencilValueType ReplaceAlphaWithStencilType();
ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend);
ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bufferFormat);
ReplaceBlendType ReplaceBlendWithShader(GEBufferFormat bufferFormat);
LogicOpReplaceType ReplaceLogicOpType();
// This is for the fallback path if real logic ops are not available.
SimulateLogicOpType SimulateLogicOpShaderTypeIfNeeded();
// Common representation, should be able to set this directly with any modern API.
struct ViewportAndScissor {
@@ -137,12 +138,18 @@ enum class BlendEq : uint8_t {
COUNT
};
// Computed blend setup, including shader stuff.
struct GenericBlendState {
bool enabled;
bool resetFramebufferRead;
bool applyFramebufferRead;
bool dirtyShaderBlendFixValues;
// Shader generation state
ReplaceAlphaType replaceAlphaWithStencil;
ReplaceBlendType replaceBlend;
SimulateLogicOpType simulateLogicOpType;
// Resulting hardware blend state
bool blendEnabled;
BlendFactor srcColor;
BlendFactor dstColor;
@@ -173,19 +180,31 @@ struct GenericBlendState {
blendColor = 0xFFFFFF | ((uint32_t)alpha << 24);
useBlendColor = true;
}
void Log();
};
void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend, bool forceReplaceBlend);
void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend);
void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState);
struct GenericMaskState {
bool applyFramebufferRead;
uint32_t uniformMask; // For each bit, opposite to the PSP.
bool rgba[4]; // true = draw, false = don't draw this channel
// The hardware channel masks, 1 bit per color component. From bit 0, order is RGBA like in all APIs!
uint8_t channelMask;
void ConvertToShaderBlend() {
// If we have to do it in the shader, we simply pass through all channels but mask only in the shader instead.
// Some GPUs have minor penalties for masks that are not all-channels-on or all-channels-off.
channelMask = 0xF;
applyFramebufferRead = true;
}
void Log();
};
void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead);
bool IsColorWriteMaskComplex(bool allowFramebufferRead);
void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported);
struct GenericStencilFuncState {
bool enabled;
@@ -197,9 +216,40 @@ struct GenericStencilFuncState {
GEStencilOp zFail;
GEStencilOp zPass;
};
void ConvertStencilFuncState(GenericStencilFuncState &stencilFuncState);
struct GenericLogicState {
// If set, logic op is applied in the shader INSTEAD of in hardware.
// In this case, simulateLogicOpType and all that should be off.
bool applyFramebufferRead;
// Hardware
bool logicOpEnabled;
// Hardware and shader generation
GELogicOp logicOp;
void ConvertToShaderBlend() {
if (logicOp != GE_LOGIC_COPY) {
logicOpEnabled = false;
applyFramebufferRead = true;
// Same logicOp is kept.
}
}
void Log();
};
void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported);
struct ComputedPipelineState {
GenericBlendState blendState;
GenericMaskState maskState;
GenericLogicState logicState;
void Convert(bool shaderBitOpsSupported);
};
// See issue #15898
inline bool SpongebobDepthInverseConditions(const GenericStencilFuncState &stencilState) {
// Check that the depth/stencil state matches the conditions exactly.
+8 -11
View File
@@ -246,7 +246,7 @@ std::string FragmentShaderDesc(const FShaderID &id) {
// Here we must take all the bits of the gstate that determine what the fragment shader will
// look like, and concatenate them together into an ID.
void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) {
void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs) {
FShaderID id;
if (gstate.isModeClear()) {
// We only need one clear shader, so let's ignore the rest of the bits.
@@ -263,20 +263,17 @@ void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) {
bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT;
bool useShaderDepal = gstate_c.useShaderDepal;
bool useSmoothedDepal = gstate_c.useSmoothedShaderDepal;
bool colorWriteMask = IsColorWriteMaskComplex(gstate_c.allowFramebufferRead);
bool colorWriteMask = pipelineState.maskState.applyFramebufferRead;
// Note how we here recompute some of the work already done in state mapping.
// Not ideal! At least we share the code.
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowFramebufferRead, gstate_c.framebufFormat);
if (colorWriteMask) {
replaceBlend = REPLACE_BLEND_COPY_FBO;
}
ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend);
ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend;
ReplaceAlphaType stencilToAlpha = pipelineState.blendState.replaceAlphaWithStencil;
SimulateLogicOpType simulateLogicOpType = pipelineState.blendState.simulateLogicOpType;
// All texfuncs except replace are the same for RGB as for RGBA with full alpha.
// Note that checking this means that we must dirty the fragment shader ID whenever textureFullAlpha changes.
if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE)
if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE) {
doTextureAlpha = false;
}
if (gstate.isTextureMapEnabled()) {
id.SetBit(FS_BIT_DO_TEXTURE);
@@ -326,7 +323,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) {
}
// 2 bits.
id.SetBits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2, ReplaceLogicOpType());
id.SetBits(FS_BIT_SIMULATE_LOGIC_OP_TYPE, 2, simulateLogicOpType);
// If replaceBlend == REPLACE_BLEND_STANDARD (or REPLACE_BLEND_NO) nothing is done, so we kill these bits.
if (replaceBlend == REPLACE_BLEND_BLUE_TO_ALPHA) {
+3 -3
View File
@@ -83,7 +83,7 @@ enum FShaderBit : uint8_t {
FS_BIT_COLOR_DOUBLE = 23,
FS_BIT_STENCIL_TO_ALPHA = 24, // 2 bits
FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE = 26, // 4 bits (ReplaceAlphaType)
FS_BIT_REPLACE_LOGIC_OP_TYPE = 30, // 2 bits
FS_BIT_SIMULATE_LOGIC_OP_TYPE = 30, // 2 bits
FS_BIT_REPLACE_BLEND = 32, // 3 bits (ReplaceBlendType)
FS_BIT_BLENDEQ = 35, // 3 bits
FS_BIT_BLENDFUNC_A = 38, // 4 bits
@@ -226,11 +226,11 @@ namespace Draw {
class Bugs;
}
void ComputeVertexShaderID(VShaderID *id, uint32_t vertexType, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
// Generates a compact string that describes the shader. Useful in a list to get an overview
// of the current flora of shaders.
std::string VertexShaderDesc(const VShaderID &id);
void ComputeFragmentShaderID(FShaderID *id, const Draw::Bugs &bugs);
struct ComputedPipelineState;
void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs);
std::string FragmentShaderDesc(const FShaderID &id);
+7
View File
@@ -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;
}
+113 -63
View File
@@ -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<AttachCandidate> 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<AttachCandidate> TextureCacheCommon::GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset) {
bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const {
gpuStats.numFramebufferEvaluations++;
std::vector<AttachCandidate> candidates;
TinySet<AttachCandidate, 6> candidates;
const std::vector<VirtualFramebuffer *> &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<AttachCandidate> &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<AttachCandidate>
}
}
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<AttachCandidate> candidates = GetFramebufferCandidates(def, texaddrOffset);
if (candidates.size() > 0) {
int index = GetBestCandidateIndex(candidates);
if (index != -1) {
SetTextureFramebuffer(candidates[index]);
return true;
}
AttachCandidate bestCandidate;
if (GetBestFramebufferCandidate(def, texaddrOffset, &bestCandidate)) {
SetTextureFramebuffer(bestCandidate);
return true;
} else {
return false;
}
return false;
}
void TextureCacheCommon::NotifyConfigChanged() {
@@ -1164,7 +1186,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) {
// It's possible for a game to (successfully) access outside valid memory.
u32 bytes = Memory::ValidSize(clutAddr, loadBytes);
if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bDisableSlowFramebufEffects) {
if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bBlockTransferGPU) {
framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes);
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
if (bytes < loadBytes) {
@@ -1903,7 +1925,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
bool smoothedDepal = false;
u32 depthUpperBits = 0;
if (need_depalettize && !g_Config.bDisableSlowFramebufEffects) {
if (need_depalettize) {
clutTexture = textureShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBufRaw_);
smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->fb_format, clutTexture.rampLength);
@@ -2227,7 +2249,7 @@ void TextureCacheCommon::ClearNextFrame() {
std::string AttachCandidate::ToString() const {
return StringFromFormat("[%s seq:%d C:%08x/%d(%s) Z:%08x/%d X:%d Y:%d reint: %s]",
this->channel == RASTER_COLOR ? "COLOR" : "DEPTH",
this->seqCount,
this->channel == RASTER_COLOR ? this->fb->colorBindSeq : this->fb->depthBindSeq,
this->fb->fb_address, this->fb->fb_stride, GeBufferFormatToString(this->fb->fb_format),
this->fb->z_address, this->fb->z_stride,
this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false");
@@ -2319,16 +2341,10 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
plan.w = gstate.getTextureWidth(0);
plan.h = gstate.getTextureHeight(0);
plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth);
if (plan.replaced->Valid()) {
// We're replacing, so we won't scale.
plan.scaleFactor = 1;
plan.levelsToLoad = plan.replaced->NumLevels();
plan.badMipSizes = false;
}
bool isPPGETexture = entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd();
// Don't scale the PPGe texture.
if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd()) {
if (isPPGETexture) {
plan.scaleFactor = 1;
}
@@ -2364,6 +2380,40 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
}
}
if (isPPGETexture) {
plan.replaced = &replacer_.FindNone();
plan.replaceValid = false;
} else {
plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth);
plan.replaceValid = plan.replaced->Valid();
}
plan.saveTexture = false;
if (plan.replaceValid) {
// We're replacing, so we won't scale.
plan.scaleFactor = 1;
plan.levelsToLoad = plan.replaced->NumLevels();
plan.levelsToCreate = std::min(plan.levelsToLoad, plan.levelsToCreate);
plan.badMipSizes = false;
// But, we still need to create the texture at a larger size.
plan.replaced->GetSize(0, plan.createW, plan.createH);
} else {
if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) {
ReplacedTextureDecodeInfo replacedInfo;
// TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save?
replacedInfo.cachekey = entry->CacheKey();
replacedInfo.hash = entry->fullhash;
replacedInfo.addr = entry->addr;
replacedInfo.isVideo = plan.isVideo;
replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0;
replacedInfo.scaleFactor = plan.scaleFactor;
replacedInfo.fmt = Draw::DataFormat::R8G8B8A8_UNORM;
plan.saveTexture = replacer_.WillSave(replacedInfo);
}
plan.createW = plan.w * plan.scaleFactor;
plan.createH = plan.h * plan.scaleFactor;
}
// Always load base level texture here
plan.baseLevelSrc = 0;
if (IsFakeMipmapChange()) {
@@ -2376,7 +2426,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
if (plan.isVideo || plan.depth != 1) {
plan.maxPossibleLevels = 1;
} else {
plan.maxPossibleLevels = log2i(std::min(plan.w * plan.scaleFactor, plan.h * plan.scaleFactor)) + 1;
plan.maxPossibleLevels = log2i(std::min(plan.createW, plan.createH)) + 1;
}
if (plan.levelsToCreate == 1) {
@@ -2417,7 +2467,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i
decPitch = stride;
}
bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1;
bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM;
CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, reverseColors, expand32);
entry.SetAlphaStatus(alphaResult, srcLevel);
+26 -10
View File
@@ -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<u64, std::unique_ptr<TexCacheEntry>> TexCache;
#endif
struct FramebufferMatchInfo {
u32 xOffset;
u32 yOffset;
int16_t xOffset;
int16_t yOffset;
bool reinterpret;
GEBufferFormat reinterpretTo;
};
struct AttachCandidate {
FramebufferMatchInfo match;
TextureDefinition entry;
VirtualFramebuffer *fb;
FramebufferMatchInfo match;
RasterChannel channel;
int seqCount;
std::string ToString() const;
};
@@ -264,11 +261,31 @@ struct BuildTexturePlan {
int w;
int h;
// Scaled (or replaced) size of the 0-mip of the final texture.
int createW;
int createH;
// Used for 3D textures only. If not a 3D texture, will be 1.
int depth;
// The replacement for the texture.
ReplacedTexture *replaced;
// Need to only check once since it can change during the load!
bool replaceValid;
bool saveTexture;
void GetMipSize(int level, int *w, int *h) const {
if (replaceValid) {
replaced->GetSize(level, *w, *h);
} else if (depth == 1) {
*w = createW >> level;
*h = createH >> level;
} else {
// 3D texture, we look for layers instead of levels.
*w = createW;
*h = createH;
}
}
};
class TextureCacheCommon {
@@ -362,8 +379,7 @@ protected:
bool MatchFramebuffer(const TextureDefinition &entry, VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel, FramebufferMatchInfo *matchInfo) const;
std::vector<AttachCandidate> GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset);
int GetBestCandidateIndex(const std::vector<AttachCandidate> &candidates);
bool GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const;
void SetTextureFramebuffer(const AttachCandidate &candidate);
+1 -2
View File
@@ -136,7 +136,6 @@ static const JitLookup jitLookup[] = {
{&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through},
{&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through},
{&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat},
{&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8},
{&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16},
@@ -882,7 +881,7 @@ void VertexDecoderJitCache::Jit_PosS8Through() {
// TODO: SIMD
LDRSB(tempReg1, srcReg, dec_->posoff);
LDRSB(tempReg2, srcReg, dec_->posoff + 1);
LDRSB(tempReg3, srcReg, dec_->posoff + 2); // signed?
LDRB(tempReg3, srcReg, dec_->posoff + 2);
static const ARMReg tr[3] = { tempReg1, tempReg2, tempReg3 };
static const ARMReg fr[3] = { fpScratchReg, fpScratchReg2, fpScratchReg3 };
ADD(scratchReg, dstReg, dec_->decFmt.posoff);
+21 -2
View File
@@ -113,7 +113,7 @@ static const JitLookup jitLookup[] = {
{&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through},
{&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through},
{&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat},
{&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloatThrough},
{&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8},
{&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16},
@@ -670,7 +670,7 @@ void VertexDecoderJitCache::Jit_PosFloat() {
void VertexDecoderJitCache::Jit_PosS8Through() {
LDRSB(INDEX_UNSIGNED, tempReg1, srcReg, dec_->posoff);
LDRSB(INDEX_UNSIGNED, tempReg2, srcReg, dec_->posoff + 1);
LDRSB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->posoff + 2); // signed?
LDRB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->posoff + 2);
fp.SCVTF(fpScratchReg, tempReg1);
fp.SCVTF(fpScratchReg2, tempReg2);
fp.SCVTF(fpScratchReg3, tempReg3);
@@ -691,6 +691,25 @@ void VertexDecoderJitCache::Jit_PosS16Through() {
STR(INDEX_UNSIGNED, src[1], dstReg, dec_->decFmt.posoff + 8);
}
void VertexDecoderJitCache::Jit_PosFloatThrough() {
// Instead of just copying 12 bytes, we copy 8 and clamp Z.
if ((dec_->posoff & 7) == 0 && (dec_->decFmt.posoff & 7) == 0) {
LDR(INDEX_UNSIGNED, EncodeRegTo64(tempReg1), srcReg, dec_->posoff);
STR(INDEX_UNSIGNED, EncodeRegTo64(tempReg1), dstReg, dec_->decFmt.posoff);
} else {
LDP(INDEX_SIGNED, tempReg1, tempReg2, srcReg, dec_->posoff);
STP(INDEX_SIGNED, tempReg1, tempReg2, dstReg, dec_->decFmt.posoff);
}
fp.LDUR(32, neonScratchRegD, srcReg, dec_->posoff + 8);
fp.FCVTZU(32, neonScratchRegD, neonScratchRegD);
// Narrow to 16 bit, saturating meanwhile.
fp.UQXTN(16, neonScratchRegD, neonScratchRegD);
fp.UXTL(16, neonScratchRegD, neonScratchRegD);
fp.UCVTF(32, neonScratchRegD, neonScratchRegD);
fp.STUR(32, neonScratchRegD, dstReg, dec_->decFmt.posoff + 8);
}
void VertexDecoderJitCache::Jit_NormalS8() {
LDURH(tempReg1, srcReg, dec_->nrmoff);
LDRB(INDEX_UNSIGNED, tempReg3, srcReg, dec_->nrmoff + 2);
+10 -8
View File
@@ -176,9 +176,6 @@ void PrintDecodedVertex(VertexReader &vtx) {
printf("P: %f %f %f\n", pos[0], pos[1], pos[2]);
}
VertexDecoder::VertexDecoder() : decoded_(nullptr), ptr_(nullptr), jitted_(0), jittedSize_(0) {
}
void VertexDecoder::Step_WeightsU8() const
{
u8 *wt = (u8 *)(decoded_ + decFmt.w0off);
@@ -779,10 +776,11 @@ void VertexDecoder::Step_PosFloatSkin() const
void VertexDecoder::Step_PosS8Through() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
const s8 *sv = (const s8*)(ptr_ + posoff);
const s8 *sv = (const s8 *)(ptr_ + posoff);
const u8 *uv = (const u8 *)(ptr_ + posoff);
v[0] = sv[0];
v[1] = sv[1];
v[2] = sv[2];
v[2] = uv[2];
}
void VertexDecoder::Step_PosS16Through() const
@@ -797,9 +795,10 @@ void VertexDecoder::Step_PosS16Through() const
void VertexDecoder::Step_PosFloatThrough() const
{
u8 *v = (u8 *)(decoded_ + decFmt.posoff);
const u8 *fv = (const u8 *)(ptr_ + posoff);
memcpy(v, fv, 12);
float *v = (float *)(decoded_ + decFmt.posoff);
const float *fv = (const float *)(ptr_ + posoff);
memcpy(v, fv, 8);
v[2] = fv[2] > 65535.0f ? 65535.0f : (fv[2] < 0.0f ? 0.0f : fv[2]);
}
void VertexDecoder::Step_PosS8Morph() const
@@ -1265,6 +1264,9 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options,
ERROR_LOG_REPORT(G3D, "Vertices without position found: (%08x) %s", fmt_, temp);
}
_assert_msg_(decFmt.posfmt == DEC_FLOAT_3, "Reader only supports float pos");
_assert_msg_(decFmt.uvfmt == DEC_FLOAT_2 || decFmt.uvfmt == DEC_NONE, "Reader only supports float UV");
// Attempt to JIT as well. But only do that if the main CPU JIT is enabled, in order to aid
// debugging attempts - if the main JIT doesn't work, this one won't do any better, probably.
if (jitCache && g_Config.bVertexDecoderJit && g_Config.iCpuCore == (int)CPUCore::JIT) {
+21 -130
View File
@@ -121,103 +121,22 @@ public:
VertexReader(u8 *base, const DecVtxFormat &decFmt, int vtype) : base_(base), data_(base), decFmt_(decFmt), vtype_(vtype) {}
void ReadPos(float pos[3]) const {
switch (decFmt_.posfmt) {
case DEC_FLOAT_3:
{
const float *f = (const float *)(data_ + decFmt_.posoff);
memcpy(pos, f, 12);
if (isThrough()) {
// Integer value passed in a float. Clamped to 0, 65535.
const float z = (int)pos[2] * (1.0f / 65535.0f);
pos[2] = z > 1.0f ? 1.0f : (z < 0.0f ? 0.0f : z);
}
}
break;
case DEC_S16_3:
{
// X and Y are signed 16 bit, Z is unsigned 16 bit
const s16 *s = (const s16 *)(data_ + decFmt_.posoff);
const u16 *u = (const u16 *)(data_ + decFmt_.posoff);
if (isThrough()) {
for (int i = 0; i < 2; i++)
pos[i] = s[i];
pos[2] = u[2] * (1.0f / 65535.0f);
} else {
for (int i = 0; i < 3; i++)
pos[i] = s[i] * (1.0f / 32768.0f);
}
}
break;
case DEC_S8_3:
{
// X and Y are signed 8 bit, Z is unsigned 8 bit
const s8 *b = (const s8 *)(data_ + decFmt_.posoff);
const u8 *u = (const u8 *)(data_ + decFmt_.posoff);
if (isThrough()) {
for (int i = 0; i < 2; i++)
pos[i] = b[i];
pos[2] = u[2] * (1.0f / 255.0f);
} else {
for (int i = 0; i < 3; i++)
pos[i] = b[i] * (1.0f / 128.0f);
}
}
break;
default:
ERROR_LOG_REPORT_ONCE(fmtpos, G3D, "Reader: Unsupported Pos Format %d", decFmt_.posfmt);
memset(pos, 0, sizeof(float) * 3);
break;
// Only DEC_FLOAT_3 is supported.
const float *f = (const float *)(data_ + decFmt_.posoff);
pos[0] = f[0];
pos[1] = f[1];
if (!isThrough()) {
pos[2] = f[2];
} else {
// Integer value passed in a float. Clamped to 0, 65535.
pos[2] = (int)f[2] * (1.0f / 65535.0f);
}
}
void ReadPosThroughZ16(float pos[3]) const {
switch (decFmt_.posfmt) {
case DEC_FLOAT_3:
{
const float *f = (const float *)(data_ + decFmt_.posoff);
memcpy(pos, f, 12);
if (isThrough()) {
// Integer value passed in a float. Clamped to 0, 65535.
const float z = (int)pos[2];
pos[2] = z > 65535.0f ? 65535.0f : (z < 0.0f ? 0.0f : z);
}
}
break;
case DEC_S16_3:
{
// X and Y are signed 16 bit, Z is unsigned 16 bit
const s16 *s = (const s16 *)(data_ + decFmt_.posoff);
const u16 *u = (const u16 *)(data_ + decFmt_.posoff);
if (isThrough()) {
for (int i = 0; i < 2; i++)
pos[i] = s[i];
pos[2] = u[2];
} else {
for (int i = 0; i < 3; i++)
pos[i] = s[i] * (1.0f / 32768.0f);
}
}
break;
case DEC_S8_3:
{
// X and Y are signed 8 bit, Z is unsigned 8 bit
const s8 *b = (const s8 *)(data_ + decFmt_.posoff);
const u8 *u = (const u8 *)(data_ + decFmt_.posoff);
if (isThrough()) {
for (int i = 0; i < 2; i++)
pos[i] = b[i];
pos[2] = u[2];
} else {
for (int i = 0; i < 3; i++)
pos[i] = b[i] * (1.0f / 128.0f);
}
}
break;
default:
ERROR_LOG_REPORT_ONCE(fmtz16, G3D, "Reader: Unsupported Pos Format %d", decFmt_.posfmt);
memset(pos, 0, sizeof(float) * 3);
break;
}
// Only DEC_FLOAT_3 is supported.
const float *f = (const float *)(data_ + decFmt_.posoff);
memcpy(pos, f, 12);
}
void ReadNrm(float nrm[3]) const {
@@ -252,36 +171,10 @@ public:
}
void ReadUV(float uv[2]) const {
switch (decFmt_.uvfmt) {
case DEC_U8_2:
{
const u8 *b = (const u8 *)(data_ + decFmt_.uvoff);
uv[0] = b[0] * (1.f / 128.f);
uv[1] = b[1] * (1.f / 128.f);
}
break;
case DEC_U16_2:
{
const u16 *s = (const u16 *)(data_ + decFmt_.uvoff);
uv[0] = s[0] * (1.f / 32768.f);
uv[1] = s[1] * (1.f / 32768.f);
}
break;
case DEC_FLOAT_2:
{
const float *f = (const float *)(data_ + decFmt_.uvoff);
uv[0] = f[0];
uv[1] = f[1];
}
break;
default:
ERROR_LOG_REPORT_ONCE(fmtuv, G3D, "Reader: Unsupported UV Format %d", decFmt_.uvfmt);
memset(uv, 0, sizeof(float) * 2);
break;
}
// Only DEC_FLOAT_2 is supported.
const float *f = (const float *)(data_ + decFmt_.uvoff);
uv[0] = f[0];
uv[1] = f[1];
}
void ReadColor0(float color[4]) const {
@@ -443,8 +336,6 @@ struct VertexDecoderOptions {
class VertexDecoder {
public:
VertexDecoder();
// A jit cache is not mandatory.
void SetVertexType(u32 vtype, const VertexDecoderOptions &options, VertexDecoderJitCache *jitCache = nullptr);
@@ -549,11 +440,10 @@ public:
int ToString(char *output) const;
// Mutable decoder state
mutable u8 *decoded_;
mutable const u8 *ptr_;
JittedVertexDecoder jitted_;
int32_t jittedSize_;
mutable u8 *decoded_ = nullptr;
mutable const u8 *ptr_ = nullptr;
JittedVertexDecoder jitted_ = 0;
int32_t jittedSize_ = 0;
// "Immutable" state, set at startup
@@ -666,6 +556,7 @@ public:
void Jit_PosFloat();
void Jit_PosS8Through();
void Jit_PosS16Through();
void Jit_PosFloatThrough();
void Jit_PosS8Skin();
void Jit_PosS16Skin();
+28 -2
View File
@@ -138,7 +138,7 @@ static const JitLookup jitLookup[] = {
{&VertexDecoder::Step_PosS8Through, &VertexDecoderJitCache::Jit_PosS8Through},
{&VertexDecoder::Step_PosS16Through, &VertexDecoderJitCache::Jit_PosS16Through},
{&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloat},
{&VertexDecoder::Step_PosFloatThrough, &VertexDecoderJitCache::Jit_PosFloatThrough},
{&VertexDecoder::Step_PosS8, &VertexDecoderJitCache::Jit_PosS8},
{&VertexDecoder::Step_PosS16, &VertexDecoderJitCache::Jit_PosS16},
@@ -1348,7 +1348,10 @@ void VertexDecoderJitCache::Jit_PosS8Through() {
DEBUG_LOG_REPORT_ONCE(vertexS8Through, G3D, "Using S8 positions in throughmode");
// SIMD doesn't really matter since this isn't useful on hardware.
for (int i = 0; i < 3; i++) {
MOVSX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i));
if (i == 2)
MOVZX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i));
else
MOVSX(32, 8, tempReg1, MDisp(srcReg, dec_->posoff + i));
CVTSI2SS(fpScratchReg, R(tempReg1));
MOVSS(MDisp(dstReg, dec_->decFmt.posoff + i * 4), fpScratchReg);
}
@@ -1377,6 +1380,29 @@ void VertexDecoderJitCache::Jit_PosS16Through() {
}
}
void VertexDecoderJitCache::Jit_PosFloatThrough() {
PXOR(fpScratchReg2, R(fpScratchReg2));
if (cpu_info.Mode64bit) {
MOV(64, R(tempReg1), MDisp(srcReg, dec_->posoff));
MOVSS(fpScratchReg, MDisp(srcReg, dec_->posoff + 8));
MOV(64, MDisp(dstReg, dec_->decFmt.posoff), R(tempReg1));
} else {
MOV(32, R(tempReg1), MDisp(srcReg, dec_->posoff));
MOV(32, R(tempReg2), MDisp(srcReg, dec_->posoff + 4));
MOVSS(fpScratchReg, MDisp(srcReg, dec_->posoff + 8));
MOV(32, MDisp(dstReg, dec_->decFmt.posoff), R(tempReg1));
MOV(32, MDisp(dstReg, dec_->decFmt.posoff + 4), R(tempReg2));
}
CVTTPS2DQ(fpScratchReg, R(fpScratchReg));
// Use pack to saturate to 0,65535.
PACKUSDW(fpScratchReg, R(fpScratchReg));
PUNPCKLWD(fpScratchReg, R(fpScratchReg2));
CVTDQ2PS(fpScratchReg, R(fpScratchReg));
MOVSS(MDisp(dstReg, dec_->decFmt.posoff + 8), fpScratchReg);
}
void VertexDecoderJitCache::Jit_PosS8() {
Jit_AnyS8ToFloat(dec_->posoff);
MOVUPS(MDisp(dstReg, dec_->decFmt.posoff), XMM3);
+2 -2
View File
@@ -538,7 +538,7 @@ rotateVBO:
D3D11VertexShader *vshader;
D3D11FragmentShader *fshader;
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform, useHWTessellation_, decOptions_.expandAllWeightsToFloat);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, useHWTransform, useHWTessellation_, decOptions_.expandAllWeightsToFloat);
ID3D11InputLayout *inputLayout = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType());
context_->PSSetShader(fshader->GetShader(), nullptr, 0);
context_->VSSetShader(vshader->GetShader(), nullptr, 0);
@@ -648,7 +648,7 @@ rotateVBO:
if (result.action == SW_DRAW_PRIMITIVES) {
D3D11VertexShader *vshader;
D3D11FragmentShader *fshader;
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false, false, decOptions_.expandAllWeightsToFloat);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat);
context_->PSSetShader(fshader->GetShader(), nullptr, 0);
context_->VSSetShader(vshader->GetShader(), nullptr, 0);
shaderManager_->UpdateUniforms(framebufferManager_->UseBufferedRendering());
-1
View File
@@ -159,7 +159,6 @@ private:
void ApplyDrawState(int prim);
void ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRef);
void ResetFramebufferRead();
ID3D11InputLayout *SetupDecFmtForDraw(D3D11VertexShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt);
+2 -2
View File
@@ -177,7 +177,7 @@ void ShaderManagerD3D11::BindUniforms() {
context_->PSSetConstantBuffers(0, 1, ps_cbs);
}
void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) {
void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) {
VShaderID VSID;
FShaderID FSID;
@@ -190,7 +190,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **
if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) {
gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE);
ComputeFragmentShaderID(&FSID, draw_->GetBugs());
ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs());
} else {
FSID = lastFSID_;
}
+1 -1
View File
@@ -85,7 +85,7 @@ public:
ShaderManagerD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context, D3D_FEATURE_LEVEL featureLevel);
~ShaderManagerD3D11();
void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
void GetShaders(int prim, u32 vertType, D3D11VertexShader **vshader, D3D11FragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat);
void ClearShaders();
void DirtyLastShader() override;
+15 -22
View File
@@ -122,12 +122,6 @@ static const D3D11_LOGIC_OP logicOps[] = {
D3D11_LOGIC_OP_SET,
};
void DrawEngineD3D11::ResetFramebufferRead() {
if (fboTexBound_) {
fboTexBound_ = false;
}
}
class FramebufferManagerD3D11;
class ShaderManagerD3D11;
@@ -142,7 +136,6 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
// Blend
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects);
if (gstate.isModeClear()) {
keys_.blend.value = 0; // full wipe
keys_.blend.blendEnable = false;
@@ -154,23 +147,21 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
} else {
keys_.blend.value = 0;
GenericMaskState maskState;
ConvertMaskState(maskState, gstate_c.allowFramebufferRead);
// Set blend - unless we need to do it in the shader.
GenericBlendState blendState;
ConvertBlendState(blendState, gstate_c.allowFramebufferRead, maskState.applyFramebufferRead);
pipelineState_.Convert(draw_->GetDeviceCaps().fragmentShaderInt32Supported);
GenericMaskState &maskState = pipelineState_.maskState;
GenericBlendState &blendState = pipelineState_.blendState;
// We ignore the logicState on D3D since there's no support, the emulation of it is blend-and-shader only.
if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) {
ApplyFramebufferRead(&fboTexNeedsBind_);
bool fboTexNeedsBind = false;
ApplyFramebufferRead(&fboTexNeedsBind);
// The shader takes over the responsibility for blending, so recompute.
ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState);
if (fboTexNeedsBind_) {
if (fboTexNeedsBind) {
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
// No sampler required, we do a plain Load in the pixel shader.
fboTexBound_ = true;
fboTexNeedsBind_ = false;
framebufferManager_->RebindFramebuffer("RebindFramebuffer - ApplyDrawState");
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
@@ -180,13 +171,15 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
} else if (blendState.resetFramebufferRead) {
ResetFramebufferRead();
dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
} else {
if (fboTexBound_) {
fboTexBound_ = false;
dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
}
}
if (blendState.enabled) {
if (blendState.blendEnabled) {
keys_.blend.blendEnable = true;
keys_.blend.logicOpEnable = false;
keys_.blend.blendOpColor = d3d11BlendEqLookup[(size_t)blendState.eqColor];
@@ -219,7 +212,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
}
}
keys_.blend.colorWriteMask = (maskState.rgba[0] ? 1 : 0) | (maskState.rgba[1] ? 2 : 0) | (maskState.rgba[2] ? 4 : 0) | (maskState.rgba[3] ? 8 : 0);
keys_.blend.colorWriteMask = maskState.channelMask;
}
}
+26 -29
View File
@@ -276,26 +276,27 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
return;
}
int tw = plan.w;
int th = plan.h;
DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) {
if (plan.replaceValid) {
dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc));
} else if (plan.scaleFactor > 1) {
tw *= plan.scaleFactor;
th *= plan.scaleFactor;
} else if (plan.scaleFactor > 1 || plan.saveTexture) {
dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM;
}
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
int levels;;
int levels;
ID3D11ShaderResourceView *view;
ID3D11Resource *texture = DxTex(entry);
_assert_(texture == nullptr);
int tw;
int th;
plan.GetMipSize(0, &tw, &th);
if (plan.depth == 1) {
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
ID3D11Texture2D *tex;
D3D11_TEXTURE2D_DESC desc{};
desc.CPUAccessFlags = 0;
@@ -305,13 +306,11 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
desc.Width = tw;
desc.Height = th;
desc.Format = dstFmt;
desc.MipLevels = plan.levelsToCreate;
desc.MipLevels = levels;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &tex));
texture = tex;
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
} else {
ID3D11Texture3D *tex;
D3D11_TEXTURE3D_DESC desc{};
@@ -338,45 +337,43 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
for (int i = 0; i < levels; i++) {
int srcLevel = (i == 0) ? plan.baseLevelSrc : i;
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
int mipWidth;
int mipHeight;
plan.GetMipSize(i, &mipWidth, &mipHeight);
u8 *data = nullptr;
int stride = 0;
int bpp = 0;
// For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :(
// NOTE: Could reuse it between levels or textures!
if (plan.replaced->GetSize(srcLevel, w, h)) {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
stride = w * bpp;
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
if (plan.replaceValid) {
bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
} else {
if (plan.scaleFactor > 1) {
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
stride = w * plan.scaleFactor * 4;
bpp = 4;
} else {
int bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2;
stride = std::max(w * bpp, 16);
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2;
}
}
stride = std::max(mipWidth * bpp, 16);
data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16);
if (!data) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight);
return;
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, false);
if (plan.depth == 1) {
context_->UpdateSubresource(texture, i, nullptr, data, stride, 0);
} else {
D3D11_BOX box{};
box.front = i;
box.back = i + 1;
box.right = w * plan.scaleFactor;
box.bottom = h * plan.scaleFactor;
box.right = mipWidth;
box.bottom = mipHeight;
context_->UpdateSubresource(texture, 0, &box, data, stride, 0);
}
FreeAlignedMemory(data);
@@ -393,7 +390,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
entry->status &= ~TexCacheEntry::STATUS_NO_MIPS;
}
if (plan.replaced->Valid()) {
if (plan.replaceValid) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}
}
+2 -2
View File
@@ -522,7 +522,7 @@ rotateVBO:
ApplyDrawState(prim);
ApplyDrawStateLate();
VSShader *vshader = shaderManager_->ApplyShader(true, useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat);
VSShader *vshader = shaderManager_->ApplyShader(true, useHWTessellation_, lastVType_, decOptions_.expandAllWeightsToFloat, pipelineState_);
IDirect3DVertexDeclaration9 *pHardwareVertexDecl = SetupDecFmtForDraw(vshader, dec_->GetDecVtxFmt(), dec_->VertexType());
if (pHardwareVertexDecl) {
@@ -613,7 +613,7 @@ rotateVBO:
ApplyDrawStateLate();
VSShader *vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat);
VSShader *vshader = shaderManager_->ApplyShader(false, false, lastVType_, decOptions_.expandAllWeightsToFloat, pipelineState_);
if (result.action == SW_DRAW_PRIMITIVES) {
if (result.setStencil) {
+2 -1
View File
@@ -148,7 +148,6 @@ private:
void ApplyDrawState(int prim);
void ApplyDrawStateLate();
void ResetFramebufferRead();
IDirect3DVertexDeclaration9 *SetupDecFmtForDraw(VSShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt);
@@ -172,4 +171,6 @@ private:
TessellationDataTransferDX9 *tessDataTransferDX9;
int lastRenderStepId_ = -1;
bool fboTexNeedsBind_ = false;
};
+4 -8
View File
@@ -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;
+1 -1
View File
@@ -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);
Loaded 100 of 181 files, more files were not shown because too many files have changed in this diff. Show more