mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-19 09:19:05 +02:00
Merge branch 'feature_openxr_stereo' into compat_openxr_gta
This commit is contained in:
commit
93a2817062
181 files changed
+2593
-1898
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -129,6 +129,6 @@ protected:
|
||||
|
||||
private:
|
||||
DialogStatus status = SCE_UTILITY_STATUS_NONE;
|
||||
UtilityDialogType dialogType_;
|
||||
UtilityDialogType dialogType_ = UtilityDialogType::NONE;
|
||||
bool volatileLocked_ = false;
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
@@ -285,9 +285,6 @@ struct SaveFileInfo
|
||||
|
||||
PPGeImage *texture = nullptr;
|
||||
|
||||
SaveFileInfo() {
|
||||
}
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -486,8 +486,8 @@ public:
|
||||
// For save states only.
|
||||
}
|
||||
|
||||
FontLib(u32 paramPtr, u32 errorCodePtr) {
|
||||
Memory::ReadStruct(paramPtr, ¶ms_);
|
||||
FontLib(FontNewLibParams *params, u32 errorCodePtr) {
|
||||
params_ = *params;
|
||||
if (params_.numFonts > 9) {
|
||||
params_.numFonts = 9;
|
||||
}
|
||||
@@ -998,7 +998,7 @@ static u32 sceFontNewLib(u32 paramPtr, u32 errorCodePtr) {
|
||||
INFO_LOG(SCEFONT, "sceFontNewLib(%08x, %08x)", paramPtr, errorCodePtr);
|
||||
*errorCode = 0;
|
||||
|
||||
FontLib *newLib = new FontLib(paramPtr, errorCodePtr);
|
||||
FontLib *newLib = new FontLib(params, errorCodePtr);
|
||||
fontLibList.push_back(newLib);
|
||||
// The game should never see this value, the return value is replaced
|
||||
// by the action. Except if we disable the alloc, in this case we return
|
||||
|
||||
+6
-7
@@ -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
@@ -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
@@ -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(®s, 0, sizeof(regs));
|
||||
//auto regs = PSPPointer<DebugProfilerRegs>::Create(statusPtr);
|
||||
// TODO: fill the struct.
|
||||
//if (Memory::IsValidAddress(statusPtr)) {
|
||||
// Memory::WriteStruct(statusPtr, ®s);
|
||||
//if (regs.IsValid()) {
|
||||
// memset((DebugProfilerRegs *)regs, 0, sizeof(DebugProfilerRegs));
|
||||
// regs.NotifyWrite("ThreadProfiler");
|
||||
//}
|
||||
return 0;
|
||||
}
|
||||
@@ -715,14 +715,14 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0XD6DA4BA1, &WrapI_CUIIU<sceKernelCreateSema>, "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()
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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, ¶m);
|
||||
else
|
||||
memset(¶m, 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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
@@ -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,¶meter);
|
||||
// Return Success
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invalid Arguments
|
||||
return ERROR_NET_ADHOCCTL_INVALID_ARG;
|
||||
if (!netAdhocctlInited) {
|
||||
return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED);
|
||||
}
|
||||
|
||||
// Library uninitialized
|
||||
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
|
||||
auto ptr = PSPPointer<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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/GPU/OpenGL/GLFeatures.h"
|
||||
#include "Common/Data/Collections/TinySet.h"
|
||||
#include "Common/Data/Convert/ColorConv.h"
|
||||
#include "Common/Data/Text/I18n.h"
|
||||
#include "Common/Math/lin/matrix4x4.h"
|
||||
@@ -126,13 +127,19 @@ VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const {
|
||||
}
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::GetExactVFB(u32 addr, int stride, GEBufferFormat format) const {
|
||||
VirtualFramebuffer *newest = nullptr;
|
||||
for (auto vfb : vfbs_) {
|
||||
if (vfb->fb_address == addr && vfb->fb_stride == stride && vfb->fb_format == format) {
|
||||
// There'll only be one exact match, we don't allow duplicates with these conditions.
|
||||
return vfb;
|
||||
if (newest) {
|
||||
if (vfb->colorBindSeq > newest->colorBindSeq) {
|
||||
newest = vfb;
|
||||
}
|
||||
} else {
|
||||
newest = vfb;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return newest;
|
||||
}
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::ResolveVFB(u32 addr, int stride, GEBufferFormat format) {
|
||||
@@ -167,6 +174,7 @@ u32 FramebufferManagerCommon::ColorBufferByteSize(const VirtualFramebuffer *vfb)
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const {
|
||||
// Dangan Ronpa hack
|
||||
return PSP_CoreParameter().compat.flags().Force04154000Download && vfb->fb_address == 0x04154000;
|
||||
}
|
||||
|
||||
@@ -326,15 +334,20 @@ void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPU
|
||||
params->viewportHeight = (int)(fabsf(vpy) * 2.0f);
|
||||
params->regionWidth = gstate.getRegionX2() + 1;
|
||||
params->regionHeight = gstate.getRegionY2() + 1;
|
||||
params->scissorWidth = gstate.getScissorX2() + 1;
|
||||
params->scissorHeight = gstate.getScissorY2() + 1;
|
||||
|
||||
params->scissorLeft = gstate.getScissorX1();
|
||||
params->scissorTop = gstate.getScissorY1();
|
||||
params->scissorRight = gstate.getScissorX2() + 1;
|
||||
params->scissorBottom = gstate.getScissorY2() + 1;
|
||||
|
||||
if (gstate.getRegionRateX() != 0x100 || gstate.getRegionRateY() != 0x100) {
|
||||
WARN_LOG_REPORT_ONCE(regionRate, G3D, "Drawing region rate add non-zero: %04x, %04x of %04x, %04x", gstate.getRegionRateX(), gstate.getRegionRateY(), gstate.getRegionX2(), gstate.getRegionY2());
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason) {
|
||||
static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width);
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason) {
|
||||
gstate_c.Clean(DIRTY_FRAMEBUF);
|
||||
|
||||
// Collect all parameters. This whole function has really become a cesspool of heuristics...
|
||||
@@ -343,9 +356,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
// As there are no clear "framebuffer width" and "framebuffer height" registers,
|
||||
// we need to infer the size of the current framebuffer somehow.
|
||||
int drawing_width, drawing_height;
|
||||
EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height);
|
||||
|
||||
gstate_c.SetCurRTOffset(0, 0);
|
||||
EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorRight, params.scissorBottom, drawing_width, drawing_height);
|
||||
|
||||
if (params.fb_address == params.z_address) {
|
||||
// Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure)
|
||||
@@ -353,6 +364,13 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
WARN_LOG_ONCE(color_equal_z, G3D, "Framebuffer bound with color addr == z addr, likely will not use Z in this pass: %08x", params.fb_address);
|
||||
}
|
||||
|
||||
// Compatibility hack for Killzone, see issue #6207.
|
||||
if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin && params.fb_format == GE_FORMAT_8888) {
|
||||
ApplyKillzoneFramebufferSplit(¶ms, &drawing_width);
|
||||
} else {
|
||||
gstate_c.SetCurRTOffset(0, 0);
|
||||
}
|
||||
|
||||
// Find a matching framebuffer.
|
||||
VirtualFramebuffer *vfb = nullptr;
|
||||
for (auto v : vfbs_) {
|
||||
@@ -378,7 +396,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
vfb->height = drawing_height;
|
||||
}
|
||||
break;
|
||||
} else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format) {
|
||||
} else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format && !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) {
|
||||
u32 v_fb_first_line_end_ptr = v->fb_address + v->fb_stride * bpp;
|
||||
u32 v_fb_end_ptr = v->fb_address + v->fb_stride * v->height * bpp;
|
||||
|
||||
@@ -477,7 +495,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
currentRenderVfb_ = vfb;
|
||||
|
||||
// Assume that if we're clearing right when switching to a new framebuffer, we don't need to upload.
|
||||
if (useBufferedRendering_ && !g_Config.bDisableSlowFramebufEffects && params.isDrawing) {
|
||||
if (useBufferedRendering_ && params.isDrawing) {
|
||||
gpu->PerformMemoryUpload(params.fb_address, byteSize);
|
||||
// Alpha was already done by PerformMemoryUpload.
|
||||
PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA);
|
||||
@@ -659,7 +677,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
|
||||
// This will result in reinterpret later, if both formats are 16-bit.
|
||||
sources.push_back(CopySource{ src, RASTER_COLOR, 0, 0 });
|
||||
} else {
|
||||
// Happens in Prince of Persia - Revelations. Ignoring.
|
||||
// This shouldn't happen anymore. I think when it happened last, we still had
|
||||
// lax stride checking when video was incoming, and a resize happened causing a duplicate.
|
||||
}
|
||||
} else if (src->fb_stride == dst->fb_stride && src->fb_format == dst->fb_format) {
|
||||
u32 bytesPerPixel = BufferFormatBytesPerPixel(src->fb_format);
|
||||
@@ -789,7 +808,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
|
||||
}
|
||||
|
||||
if (dst != currentRenderVfb_ && tookActions) {
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After FakeReinterpret");
|
||||
// Will probably just change the name of the current renderpass, since one was started by the reinterpret itself.
|
||||
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After Reinterpret");
|
||||
}
|
||||
|
||||
shaderManager_->DirtyLastShader();
|
||||
@@ -886,7 +906,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb) {
|
||||
// ugly...
|
||||
if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) {
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
|
||||
}
|
||||
@@ -898,11 +917,12 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) {
|
||||
if (ShouldDownloadFramebuffer(vfb) && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height);
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
} else {
|
||||
DownloadFramebufferOnSwitch(prevVfb);
|
||||
}
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
@@ -959,41 +979,41 @@ void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int stride,
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size, bool safe) {
|
||||
void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) {
|
||||
// Take off the uncached flag from the address. Not to be confused with the start of VRAM.
|
||||
addr &= 0x3FFFFFFF;
|
||||
// TODO: Could go through all FBOs, but probably not important?
|
||||
// TODO: Could also check for inner changes, but video is most important.
|
||||
// TODO: This shouldn't care if it's a display framebuf or not, should work exactly the same.
|
||||
bool isDisplayBuf = addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr();
|
||||
if (isDisplayBuf || safe) {
|
||||
// TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help.
|
||||
if (!Memory::IsValidAddress(displayFramebufPtr_))
|
||||
return;
|
||||
// TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help.
|
||||
if (!Memory::IsValidAddress(displayFramebufPtr_))
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
if (vfb->fb_address == addr) {
|
||||
FlushBeforeCopy();
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
if (vfb->fb_address == addr) {
|
||||
FlushBeforeCopy();
|
||||
|
||||
if (useBufferedRendering_ && vfb->fbo) {
|
||||
GEBufferFormat fmt = vfb->fb_format;
|
||||
if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) {
|
||||
// If we're not rendering to it, format may be wrong. Use displayFormat_ instead.
|
||||
fmt = displayFormat_;
|
||||
}
|
||||
DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height);
|
||||
SetColorUpdated(vfb, gstate_c.skipDrawReason);
|
||||
} else {
|
||||
INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->fb_format);
|
||||
DestroyFramebuf(vfb);
|
||||
vfbs_.erase(vfbs_.begin() + i--);
|
||||
if (useBufferedRendering_ && vfb->fbo) {
|
||||
GEBufferFormat fmt = vfb->fb_format;
|
||||
if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) {
|
||||
// If we're not rendering to it, format may be wrong. Use displayFormat_ instead.
|
||||
// TODO: This doesn't seem quite right anymore.
|
||||
fmt = displayFormat_;
|
||||
}
|
||||
DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height);
|
||||
SetColorUpdated(vfb, gstate_c.skipDrawReason);
|
||||
} else {
|
||||
INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format));
|
||||
DestroyFramebuf(vfb);
|
||||
vfbs_.erase(vfbs_.begin() + i--);
|
||||
}
|
||||
}
|
||||
|
||||
RebindFramebuffer("RebindFramebuffer - UpdateFromMemory");
|
||||
}
|
||||
|
||||
RebindFramebuffer("RebindFramebuffer - UpdateFromMemory");
|
||||
|
||||
// TODO: Necessary?
|
||||
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
|
||||
}
|
||||
@@ -1106,7 +1126,7 @@ void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer
|
||||
}
|
||||
|
||||
if (x < src->drawnWidth && y < src->drawnHeight && w > 0 && h > 0) {
|
||||
BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, "Blit_CopyFramebufferForColorTexture");
|
||||
BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, RASTER_COLOR, "Blit_CopyFramebufferForColorTexture");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1215,8 +1235,8 @@ void FramebufferManagerCommon::DownloadFramebufferOnSwitch(VirtualFramebuffer *v
|
||||
// Some games will draw to some memory once, and use it as a render-to-texture later.
|
||||
// To support this, we save the first frame to memory when we have a safe w/h.
|
||||
// Saving each frame would be slow.
|
||||
if (!g_Config.bDisableSlowFramebufEffects && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight);
|
||||
if (g_Config.bBlockTransferGPU && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
vfb->safeWidth = 0;
|
||||
vfb->safeHeight = 0;
|
||||
@@ -1369,7 +1389,7 @@ void FramebufferManagerCommon::DecimateFBOs() {
|
||||
int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used);
|
||||
|
||||
if (ShouldDownloadFramebuffer(vfb) && age == 0 && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height);
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
|
||||
@@ -1502,9 +1522,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
|
||||
if (!skipCopy) {
|
||||
// TODO: In this case, it'll nearly always be better to draw the old framebuffer to the new one than to do an actual blit.
|
||||
// Usually hardly a performance issue though.
|
||||
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, "Blit_ResizeFramebufFBO");
|
||||
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "Blit_ResizeFramebufFBO");
|
||||
// Depth copying is handled by deferred copies later.
|
||||
}
|
||||
}
|
||||
fbosToDelete_.push_back(old.fbo);
|
||||
@@ -1608,7 +1627,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
||||
} else {
|
||||
WARN_LOG_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy %08x -> %08x (size: %x)", src, dst, size);
|
||||
// Just do the blit!
|
||||
BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, "Blit_InterBufferMemcpy");
|
||||
BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, RASTER_COLOR, "Blit_InterBufferMemcpy");
|
||||
SetColorUpdated(dstBuffer, skipDrawReason);
|
||||
RebindFramebuffer("RebindFramebuffer - Inter-buffer memcpy");
|
||||
}
|
||||
@@ -1630,8 +1649,8 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
||||
FlushBeforeCopy();
|
||||
if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) {
|
||||
WARN_LOG_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight);
|
||||
} else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated && !PSP_CoreParameter().compat.flags().DisableReadbacks) {
|
||||
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH);
|
||||
} else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated) {
|
||||
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, RASTER_COLOR);
|
||||
srcBuffer->usageFlags = (srcBuffer->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
return false;
|
||||
@@ -1660,7 +1679,7 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p
|
||||
int x_bytes = x_pixels * bpp;
|
||||
int w_bytes = w_pixels * bpp;
|
||||
|
||||
std::vector<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;
|
||||
}
|
||||
}
|
||||
@@ -165,10 +165,12 @@ struct FramebufferHeuristicParams {
|
||||
bool isBlending;
|
||||
int viewportWidth;
|
||||
int viewportHeight;
|
||||
int regionWidth;
|
||||
int regionHeight;
|
||||
int scissorWidth;
|
||||
int scissorHeight;
|
||||
int16_t regionWidth;
|
||||
int16_t regionHeight;
|
||||
int16_t scissorLeft;
|
||||
int16_t scissorTop;
|
||||
int16_t scissorRight;
|
||||
int16_t scissorBottom;
|
||||
};
|
||||
|
||||
struct GPUgstate;
|
||||
@@ -206,7 +208,7 @@ enum class TempFBO {
|
||||
STENCIL,
|
||||
};
|
||||
|
||||
inline Draw::DataFormat GEFormatToThin3D(int geFormat) {
|
||||
inline Draw::DataFormat GEFormatToThin3D(GEBufferFormat geFormat) {
|
||||
switch (geFormat) {
|
||||
case GE_FORMAT_4444:
|
||||
return Draw::DataFormat::A4R4G4B4_UNORM_PACK16;
|
||||
@@ -216,7 +218,10 @@ inline Draw::DataFormat GEFormatToThin3D(int geFormat) {
|
||||
return Draw::DataFormat::R5G6B5_UNORM_PACK16;
|
||||
case GE_FORMAT_8888:
|
||||
return Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
case GE_FORMAT_DEPTH16:
|
||||
return Draw::DataFormat::D16;
|
||||
default:
|
||||
// TODO: Assert?
|
||||
return Draw::DataFormat::UNDEFINED;
|
||||
}
|
||||
}
|
||||
@@ -240,10 +245,6 @@ struct BlockTransferRect {
|
||||
int x_pixels() const {
|
||||
return x_bytes / BufferFormatBytesPerPixel(vfb->fb_format);
|
||||
}
|
||||
|
||||
bool operator < (const BlockTransferRect &other) const {
|
||||
return vfb->colorBindSeq < other.vfb->colorBindSeq;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Draw {
|
||||
@@ -276,7 +277,7 @@ public:
|
||||
void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format);
|
||||
void DestroyFramebuf(VirtualFramebuffer *v);
|
||||
|
||||
VirtualFramebuffer *DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason);
|
||||
VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason);
|
||||
VirtualFramebuffer *SetRenderFrameBuffer(bool framebufChanged, int skipDrawReason) {
|
||||
// Inlining this part since it's so frequent.
|
||||
if (!framebufChanged && currentRenderVfb_) {
|
||||
@@ -305,7 +306,7 @@ public:
|
||||
|
||||
bool NotifyFramebufferCopy(u32 src, u32 dest, int size, bool isMemset, u32 skipDrawReason);
|
||||
void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt);
|
||||
void UpdateFromMemory(u32 addr, int size, bool safe);
|
||||
void UpdateFromMemory(u32 addr, int size);
|
||||
void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor);
|
||||
bool PerformStencilUpload(u32 addr, int size, StencilUpload flags);
|
||||
|
||||
@@ -320,7 +321,7 @@ public:
|
||||
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
|
||||
|
||||
bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
|
||||
|
||||
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
|
||||
void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat);
|
||||
@@ -420,7 +421,7 @@ public:
|
||||
VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat);
|
||||
|
||||
protected:
|
||||
virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
virtual void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
|
||||
void SetViewport2D(int x, int y, int w, int h);
|
||||
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
|
||||
void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags);
|
||||
@@ -436,7 +437,7 @@ protected:
|
||||
virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra
|
||||
|
||||
// Used by ReadFramebufferToMemory and later framebuffer block copies
|
||||
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag);
|
||||
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag);
|
||||
|
||||
void BlitUsingRaster(
|
||||
Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2,
|
||||
@@ -461,7 +462,7 @@ protected:
|
||||
|
||||
bool FindTransferFramebuffer(u32 basePtr, int stride, int x, int y, int w, int h, int bpp, bool destination, BlockTransferRect *rect);
|
||||
|
||||
VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb);
|
||||
VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel);
|
||||
virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {}
|
||||
|
||||
VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format);
|
||||
|
||||
+123
-114
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
Reference in new issue
Block a user