Merge branch 'master' into compat_openxr_gta

This commit is contained in:
Lubos committed 2022-09-07 14:58:18 +02:00
commit 4ff3e496c5
71 files changed
+14257 -14080

No files matched your search

-3
View File
@@ -628,10 +628,7 @@ add_library(Common STATIC
Common/Input/KeyCodes.h
Common/Input/InputState.cpp
Common/Input/InputState.h
Common/Math/fast/fast_math.c
Common/Math/fast/fast_matrix.c
Common/Math/fast/fast_matrix_neon.S
Common/Math/fast/fast_matrix_sse.c
Common/Math/curves.cpp
Common/Math/curves.h
Common/Math/expression_parser.cpp
-7
View File
@@ -452,7 +452,6 @@
<ClInclude Include="Input\KeyCodes.h" />
<ClInclude Include="Math\curves.h" />
<ClInclude Include="Math\expression_parser.h" />
<ClInclude Include="Math\fast\fast_math.h" />
<ClInclude Include="Math\fast\fast_matrix.h" />
<ClInclude Include="Math\geom2d.h" />
<ClInclude Include="Math\lin\matrix4x4.h" />
@@ -873,9 +872,7 @@
<ClCompile Include="Log.cpp" />
<ClCompile Include="Math\curves.cpp" />
<ClCompile Include="Math\expression_parser.cpp" />
<ClCompile Include="Math\fast\fast_math.c" />
<ClCompile Include="Math\fast\fast_matrix.c" />
<ClCompile Include="Math\fast\fast_matrix_sse.c" />
<ClCompile Include="Math\lin\matrix4x4.cpp" />
<ClCompile Include="Math\lin\vec3.cpp" />
<ClCompile Include="Math\math_util.cpp" />
@@ -991,10 +988,6 @@
<Project>{f761046e-6c38-4428-a5f1-38391a37bb34}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Math\fast\fast_matrix_neon.S" />
<None Include="Math\lin\matrix_neon.s" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-17
View File
@@ -161,9 +161,6 @@
<ClInclude Include="Math\lin\vec3.h">
<Filter>Math\lin</Filter>
</ClInclude>
<ClInclude Include="Math\fast\fast_math.h">
<Filter>Math\fast</Filter>
</ClInclude>
<ClInclude Include="Math\fast\fast_matrix.h">
<Filter>Math\fast</Filter>
</ClInclude>
@@ -568,15 +565,9 @@
<ClCompile Include="Math\lin\vec3.cpp">
<Filter>Math\lin</Filter>
</ClCompile>
<ClCompile Include="Math\fast\fast_math.c">
<Filter>Math\fast</Filter>
</ClCompile>
<ClCompile Include="Math\fast\fast_matrix.c">
<Filter>Math\fast</Filter>
</ClCompile>
<ClCompile Include="Math\fast\fast_matrix_sse.c">
<Filter>Math\fast</Filter>
</ClCompile>
<ClCompile Include="Data\Format\RIFF.cpp">
<Filter>Data\Format</Filter>
</ClCompile>
@@ -910,12 +901,4 @@
<Filter>ext\libpng17</Filter>
</Text>
</ItemGroup>
<ItemGroup>
<None Include="Math\lin\matrix_neon.s">
<Filter>Math\lin</Filter>
</None>
<None Include="Math\fast\fast_matrix_neon.S">
<Filter>Math\fast</Filter>
</None>
</ItemGroup>
</Project>
+7
View File
@@ -99,6 +99,13 @@ struct TinySet {
return slowLookup_->size() + MaxFastSize;
}
}
T &operator[] (size_t index) {
if (index < MaxFastSize) {
return fastLookup_[index];
} else {
return (*slowLookup_)[index - MaxFastSize];
}
}
const T &operator[] (size_t index) const {
if (index < MaxFastSize) {
return fastLookup_[index];
+25 -11
View File
@@ -110,13 +110,13 @@ static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) {
int GLQueueRunner::GetStereoBufferIndex(const char *uniformName) {
if (!uniformName) return -1;
else if (strcmp(uniformName, "u_view") == 0) return 0;
else if (strcmp(uniformName, "u_proj") == 0) return 1;
else if (strcmp(uniformName, "u_proj_lens") == 0) return 1;
else return -1;
}
std::string GLQueueRunner::GetStereoBufferLayout(const char *uniformName) {
if (strcmp(uniformName, "u_view") == 0) return "ViewMatrices";
else if (strcmp(uniformName, "u_proj") == 0) return "ProjectionMatrix";
else if (strcmp(uniformName, "u_proj_lens") == 0) return "ProjectionMatrix";
else return "undefined";
}
@@ -1037,15 +1037,29 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
case GLRRenderCommand::UNIFORMSTEREOMATRIX:
{
_dbg_assert_(curProgram);
int layout = GetStereoBufferIndex(c.uniformMatrix4.name);
if (layout >= 0) {
int size = 2 * 16 * sizeof(float);
glBindBufferBase(GL_UNIFORM_BUFFER, layout, *c.uniformMatrix4.loc);
glBindBuffer(GL_UNIFORM_BUFFER, *c.uniformMatrix4.loc);
void *viewMatrices = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(viewMatrices, c.uniformMatrix4.m, size);
glUnmapBuffer(GL_UNIFORM_BUFFER);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
if (IsMultiviewSupported()) {
int layout = GetStereoBufferIndex(c.uniformMatrix4.name);
if (layout >= 0) {
int size = 2 * 16 * sizeof(float);
glBindBufferBase(GL_UNIFORM_BUFFER, layout, *c.uniformMatrix4.loc);
glBindBuffer(GL_UNIFORM_BUFFER, *c.uniformMatrix4.loc);
void *matrices = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(matrices, c.uniformMatrix4.m, size);
glUnmapBuffer(GL_UNIFORM_BUFFER);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
} else {
int loc = c.uniformMatrix4.loc ? *c.uniformMatrix4.loc : -1;
if (c.uniformMatrix4.name) {
loc = curProgram->GetUniformLoc(c.uniformMatrix4.name);
}
if (loc >= 0) {
if (GetVRFBOIndex() == 0) {
glUniformMatrix4fv(loc, 1, false, c.uniformMatrix4.m);
} else {
glUniformMatrix4fv(loc, 1, false, &c.uniformMatrix4.m[16]);
}
}
}
CHECK_GL_ERROR_IF_DEBUG();
break;
+1 -2
View File
@@ -594,6 +594,7 @@ void GLRenderManager::Run(int frame) {
} else {
queueRunner_.RunSteps(stepsOnThread, skipGLCalls_);
}
stepsOnThread.clear();
if (!skipGLCalls_) {
for (auto iter : frameData.activePushBuffers) {
@@ -601,8 +602,6 @@ void GLRenderManager::Run(int frame) {
}
}
stepsOnThread.clear();
switch (frameData.type) {
case GLRRunType::END:
EndSubmitFrame(frame);
+4
View File
@@ -66,8 +66,10 @@ PFN_vkBindImageMemory vkBindImageMemory;
PFN_vkBindImageMemory2 vkBindImageMemory2;
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements;
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements;
PFN_vkCreateFence vkCreateFence;
PFN_vkDestroyFence vkDestroyFence;
PFN_vkGetFenceStatus vkGetFenceStatus;
@@ -604,8 +606,10 @@ void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledE
LOAD_DEVICE_FUNC(device, vkBindImageMemory2);
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements2);
LOAD_DEVICE_FUNC(device, vkGetDeviceBufferMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements2);
LOAD_DEVICE_FUNC(device, vkGetDeviceImageMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkCreateFence);
LOAD_DEVICE_FUNC(device, vkDestroyFence);
LOAD_DEVICE_FUNC(device, vkResetFences);
+2
View File
@@ -71,8 +71,10 @@ extern PFN_vkBindImageMemory vkBindImageMemory;
extern PFN_vkBindImageMemory2 vkBindImageMemory2;
extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
extern PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements;
extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
extern PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements;
extern PFN_vkQueueBindSparse vkQueueBindSparse;
extern PFN_vkCreateFence vkCreateFence;
extern PFN_vkDestroyFence vkDestroyFence;
-12
View File
@@ -1,12 +0,0 @@
#include "ppsspp_config.h"
#include "fast_math.h"
#include "fast_matrix.h"
void InitFastMath() {
#ifndef _MSC_VER
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
fast_matrix_mul_4x4 = &fast_matrix_mul_4x4_neon;
#endif
#endif
}
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Fast Math
// A mini library of math kernels. These should be large enough to be worth calling
// as functions, and generic enough to fit in the "native" library (not PSP specific stuff).
// NEON versions are dynamically selected at runtime, when you call InitFastMath.
// SSE versions are hard linked at compile time.
// See fast_matrix.h for the first set of functions.
void InitFastMath();
#ifdef __cplusplus
}
#endif
+106 -4
View File
@@ -1,6 +1,110 @@
#include "fast_math.h"
#include "ppsspp_config.h"
#include "fast_matrix.h"
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#include <emmintrin.h>
#include "fast_matrix.h"
void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b) {
int i;
__m128 a_col_1 = _mm_loadu_ps(a);
__m128 a_col_2 = _mm_loadu_ps(&a[4]);
__m128 a_col_3 = _mm_loadu_ps(&a[8]);
__m128 a_col_4 = _mm_loadu_ps(&a[12]);
for (i = 0; i < 16; i += 4) {
__m128 r_col = _mm_mul_ps(a_col_1, _mm_set1_ps(b[i]));
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_2, _mm_set1_ps(b[i + 1])));
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_3, _mm_set1_ps(b[i + 2])));
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_4, _mm_set1_ps(b[i + 3])));
_mm_storeu_ps(&dest[i], r_col);
}
}
#elif PPSSPP_ARCH(ARM_NEON)
#if defined(_MSC_VER) && PPSSPP_ARCH(ARM64)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#if PPSSPP_ARCH(ARM)
static inline float32x4_t vfmaq_laneq_f32(float32x4_t _s, float32x4_t _a, float32x4_t _b, int lane) {
if (lane == 0) return vmlaq_lane_f32(_s, _a, vget_low_f32(_b), 0);
else if (lane == 1) return vmlaq_lane_f32(_s, _a, vget_low_f32(_b), 1);
else if (lane == 2) return vmlaq_lane_f32(_s, _a, vget_high_f32(_b), 0);
else if (lane == 3) return vmlaq_lane_f32(_s, _a, vget_high_f32(_b), 1);
else return vdupq_n_f32(0.f);
}
#endif
// From https://developer.arm.com/documentation/102467/0100/Matrix-multiplication-example
void fast_matrix_mul_4x4_neon(float *C, const float *A, const float *B) {
// these are the columns A
float32x4_t A0;
float32x4_t A1;
float32x4_t A2;
float32x4_t A3;
// these are the columns B
float32x4_t B0;
float32x4_t B1;
float32x4_t B2;
float32x4_t B3;
// these are the columns C
float32x4_t C0;
float32x4_t C1;
float32x4_t C2;
float32x4_t C3;
A0 = vld1q_f32(A);
A1 = vld1q_f32(A + 4);
A2 = vld1q_f32(A + 8);
A3 = vld1q_f32(A + 12);
// Zero accumulators for C values
C0 = vmovq_n_f32(0);
C1 = vmovq_n_f32(0);
C2 = vmovq_n_f32(0);
C3 = vmovq_n_f32(0);
// Multiply accumulate in 4x1 blocks, i.e. each column in C
B0 = vld1q_f32(B);
C0 = vfmaq_laneq_f32(C0, A0, B0, 0);
C0 = vfmaq_laneq_f32(C0, A1, B0, 1);
C0 = vfmaq_laneq_f32(C0, A2, B0, 2);
C0 = vfmaq_laneq_f32(C0, A3, B0, 3);
vst1q_f32(C, C0);
B1 = vld1q_f32(B + 4);
C1 = vfmaq_laneq_f32(C1, A0, B1, 0);
C1 = vfmaq_laneq_f32(C1, A1, B1, 1);
C1 = vfmaq_laneq_f32(C1, A2, B1, 2);
C1 = vfmaq_laneq_f32(C1, A3, B1, 3);
vst1q_f32(C + 4, C1);
B2 = vld1q_f32(B + 8);
C2 = vfmaq_laneq_f32(C2, A0, B2, 0);
C2 = vfmaq_laneq_f32(C2, A1, B2, 1);
C2 = vfmaq_laneq_f32(C2, A2, B2, 2);
C2 = vfmaq_laneq_f32(C2, A3, B2, 3);
vst1q_f32(C + 8, C2);
B3 = vld1q_f32(B + 12);
C3 = vfmaq_laneq_f32(C3, A0, B3, 0);
C3 = vfmaq_laneq_f32(C3, A1, B3, 1);
C3 = vfmaq_laneq_f32(C3, A2, B3, 2);
C3 = vfmaq_laneq_f32(C3, A3, B3, 3);
vst1q_f32(C + 12, C3);
}
#else
#define xx 0
#define xy 1
#define xz 2
@@ -40,6 +144,4 @@ void fast_matrix_mul_4x4_c(float *dest, const float *a, const float *b) {
dest[ww] = b[wx] * a[xw] + b[wy] * a[yw] + b[wz] * a[zw] + b[ww] * a[ww];
}
#ifndef fast_matrix_mul_4x4
fptr_fast_matrix_mul_4x4 fast_matrix_mul_4x4 = &fast_matrix_mul_4x4_c;
#endif
#endif
+4 -8
View File
@@ -6,11 +6,8 @@
extern "C" {
#endif
// A mini library of matrix math kernels.
// A mini library of 4x4 matrix muls.
// TODO: Really need to wrap this block in a macro or something, will get repetitive.
typedef void(*fptr_fast_matrix_mul_4x4)(float *dest, const float *a, const float *b);
extern void fast_matrix_mul_4x4_c(float *dest, const float *a, const float *b);
extern void fast_matrix_mul_4x4_neon(float *dest, const float *a, const float *b);
extern void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b);
@@ -18,13 +15,12 @@ extern void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b)
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
// Hard link to SSE implementations on x86/amd64
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_sse
#elif PPSSPP_ARCH(ARM64)
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_c
#elif PPSSPP_ARCH(ARM_NEON)
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_neon
#else
extern fptr_fast_matrix_mul_4x4 fast_matrix_mul_4x4;
#define fast_matrix_mul_4x4 fast_matrix_mul_4x4_c
#endif
#ifdef __cplusplus
} // extern "C"
#endif
-54
View File
@@ -1,54 +0,0 @@
#include "ppsspp_config.h"
#if PPSSPP_ARCH(ARM_NEON) && !PPSSPP_ARCH(ARM64)
.syntax unified // Allow both ARM and Thumb-2 instructions
.text
.align 2 // Align the function code to a 4-byte (2^n) word boundary.
.arm // Use ARM instructions instead of Thumb.
@ From ARM samples
@
@ matrix_mul_float:
@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix.
@ matrix 0, matrix 1 and result pointers can be the same,
@ ie. my_matrix = my_matrix * my_matrix is possible.
@
@ r0 = pointer to 4x4 result matrix, single precision floats, column major order
@ r1 = pointer to 4x4 matrix 0, single precision floats, column major order
@ r2 = pointer to 4x4 matrix 1, single precision floats, column major order
@
.globl _fast_matrix_mul_4x4_neon
_fast_matrix_mul_4x4_neon:
.globl fast_matrix_mul_4x4_neon
fast_matrix_mul_4x4_neon:
vld1.32 {d16-d19}, [r1]! @ load first eight elements of matrix 0
vld1.32 {d20-d23}, [r1]! @ load second eight elements of matrix 0
vld1.32 {d0-d3}, [r2]! @ load first eight elements of matrix 1
vld1.32 {d4-d7}, [r2]! @ load second eight elements of matrix 1
vmul.f32 q12, q8, d0[0] @ rslt col0 = (mat0 col0) * (mat1 col0 elt0)
vmul.f32 q13, q8, d2[0] @ rslt col1 = (mat0 col0) * (mat1 col1 elt0)
vmul.f32 q14, q8, d4[0] @ rslt col2 = (mat0 col0) * (mat1 col2 elt0)
vmul.f32 q15, q8, d6[0] @ rslt col3 = (mat0 col0) * (mat1 col3 elt0)
vmla.f32 q12, q9, d0[1] @ rslt col0 += (mat0 col1) * (mat1 col0 elt1)
vmla.f32 q13, q9, d2[1] @ rslt col1 += (mat0 col1) * (mat1 col1 elt1)
vmla.f32 q14, q9, d4[1] @ rslt col2 += (mat0 col1) * (mat1 col2 elt1)
vmla.f32 q15, q9, d6[1] @ rslt col3 += (mat0 col1) * (mat1 col3 elt1)
vmla.f32 q12, q10, d1[0] @ rslt col0 += (mat0 col2) * (mat1 col0 elt2)
vmla.f32 q13, q10, d3[0] @ rslt col1 += (mat0 col2) * (mat1 col1 elt2)
vmla.f32 q14, q10, d5[0] @ rslt col2 += (mat0 col2) * (mat1 col2 elt2)
vmla.f32 q15, q10, d7[0] @ rslt col3 += (mat0 col2) * (mat1 col2 elt2)
vmla.f32 q12, q11, d1[1] @ rslt col0 += (mat0 col3) * (mat1 col0 elt3)
vmla.f32 q13, q11, d3[1] @ rslt col1 += (mat0 col3) * (mat1 col1 elt3)
vmla.f32 q14, q11, d5[1] @ rslt col2 += (mat0 col3) * (mat1 col2 elt3)
vmla.f32 q15, q11, d7[1] @ rslt col3 += (mat0 col3) * (mat1 col3 elt3)
vst1.32 {d24-d27}, [r0]! @ store first eight elements of result
vst1.32 {d28-d31}, [r0]! @ store second eight elements of result
bx lr
#endif
-25
View File
@@ -1,25 +0,0 @@
#include "ppsspp_config.h"
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#include <emmintrin.h>
#include "fast_matrix.h"
void fast_matrix_mul_4x4_sse(float *dest, const float *a, const float *b) {
int i;
__m128 a_col_1 = _mm_loadu_ps(a);
__m128 a_col_2 = _mm_loadu_ps(&a[4]);
__m128 a_col_3 = _mm_loadu_ps(&a[8]);
__m128 a_col_4 = _mm_loadu_ps(&a[12]);
for (i = 0; i < 16; i += 4) {
__m128 r_col = _mm_mul_ps(a_col_1, _mm_set1_ps(b[i]));
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_2, _mm_set1_ps(b[i + 1])));
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_3, _mm_set1_ps(b[i + 2])));
r_col = _mm_add_ps(r_col, _mm_mul_ps(a_col_4, _mm_set1_ps(b[i + 3])));
_mm_storeu_ps(&dest[i], r_col);
}
}
#endif
-106
View File
@@ -1,106 +0,0 @@
@
@ NEON matrix multiplication examples
@
.syntax unified
@
@ matrix_mul_float:
@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix.
@ matrix 0, matrix 1 and result pointers can be the same,
@ ie. my_matrix = my_matrix * my_matrix is possible.
@
@ r0 = pointer to 4x4 result matrix, single precision floats, column major order
@ r1 = pointer to 4x4 matrix 0, single precision floats, column major order
@ r2 = pointer to 4x4 matrix 1, single precision floats, column major order
@
.global matrix_mul_float
matrix_mul_float:
vld1.32 {d16-d19}, [r1]! @ load first eight elements of matrix 0
vld1.32 {d20-d23}, [r1]! @ load second eight elements of matrix 0
vld1.32 {d0-d3}, [r2]! @ load first eight elements of matrix 1
vld1.32 {d4-d7}, [r2]! @ load second eight elements of matrix 1
vmul.f32 q12, q8, d0[0] @ rslt col0 = (mat0 col0) * (mat1 col0 elt0)
vmul.f32 q13, q8, d2[0] @ rslt col1 = (mat0 col0) * (mat1 col1 elt0)
vmul.f32 q14, q8, d4[0] @ rslt col2 = (mat0 col0) * (mat1 col2 elt0)
vmul.f32 q15, q8, d6[0] @ rslt col3 = (mat0 col0) * (mat1 col3 elt0)
vmla.f32 q12, q9, d0[1] @ rslt col0 += (mat0 col1) * (mat1 col0 elt1)
vmla.f32 q13, q9, d2[1] @ rslt col1 += (mat0 col1) * (mat1 col1 elt1)
vmla.f32 q14, q9, d4[1] @ rslt col2 += (mat0 col1) * (mat1 col2 elt1)
vmla.f32 q15, q9, d6[1] @ rslt col3 += (mat0 col1) * (mat1 col3 elt1)
vmla.f32 q12, q10, d1[0] @ rslt col0 += (mat0 col2) * (mat1 col0 elt2)
vmla.f32 q13, q10, d3[0] @ rslt col1 += (mat0 col2) * (mat1 col1 elt2)
vmla.f32 q14, q10, d5[0] @ rslt col2 += (mat0 col2) * (mat1 col2 elt2)
vmla.f32 q15, q10, d7[0] @ rslt col3 += (mat0 col2) * (mat1 col2 elt2)
vmla.f32 q12, q11, d1[1] @ rslt col0 += (mat0 col3) * (mat1 col0 elt3)
vmla.f32 q13, q11, d3[1] @ rslt col1 += (mat0 col3) * (mat1 col1 elt3)
vmla.f32 q14, q11, d5[1] @ rslt col2 += (mat0 col3) * (mat1 col2 elt3)
vmla.f32 q15, q11, d7[1] @ rslt col3 += (mat0 col3) * (mat1 col3 elt3)
vst1.32 {d24-d27}, [r0]! @ store first eight elements of result
vst1.32 {d28-d31}, [r0]! @ store second eight elements of result
mov pc, lr @ return to caller
@ Macro: mul_col_s16
@
@ Multiply a four s16 element column of a matrix by the columns of a second matrix
@ to give a column of results. Elements are assumed to be in Q1.14 format.
@ Inputs: col_d - d register containing a column of the matrix
@ Outputs: res_d - d register containing the column of results
@ Corrupts: register q12
@ Assumes: the second matrix columns are in registers d16-d19 in column major order
@
.macro mul_col_s16 res_d, col_d
vmull.s16 q12, d16, \col_d[0] @ multiply col element 0 by matrix col 0
vmlal.s16 q12, d17, \col_d[1] @ multiply-acc col element 1 by matrix col 1
vmlal.s16 q12, d18, \col_d[2] @ multiply-acc col element 2 by matrix col 2
vmlal.s16 q12, d19, \col_d[3] @ multiply-acc col element 3 by matrix col 3
vqrshrn.s32 \res_d, q12, #14 @ shift right and narrow accumulator into
@ Q1.14 fixed point format, with saturation
.endm
@
@ matrix_mul_fixed:
@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix.
@ matrix 0, matrix 1 and result pointers can be the same,
@ ie. my_matrix = my_matrix * my_matrix is possible
@
@ r0 = pointer to 4x4 result matrix, Q1.14 fixed point, column major order
@ r1 = pointer to 4x4 matrix 0, Q1.14 fixed point, column major order
@ r2 = pointer to 4x4 matrix 1, Q1.14 fixed point, column major order
@
.global matrix_mul_fixed
matrix_mul_fixed:
vld1.16 {d16-d19}, [r1] @ load sixteen elements of matrix 0
vld1.16 {d0-d3}, [r2] @ load sixteen elements of matrix 1
mul_col_s16 d4, d0 @ matrix 0 * matrix 1 col 0
mul_col_s16 d5, d1 @ matrix 0 * matrix 1 col 1
mul_col_s16 d6, d2 @ matrix 0 * matrix 1 col 2
mul_col_s16 d7, d3 @ matrix 0 * matrix 1 col 3
vst1.16 {d4-d7}, [r0] @ store sixteen elements of result
mov pc, lr @ return to caller
+1
View File
@@ -239,6 +239,7 @@ bool PreVRRender() {
VR_SetConfig(VR_CONFIG_6DOF_ENABLED, g_Config.bEnable6DoF);
VR_SetConfig(VR_CONFIG_CANVAS_DISTANCE, g_Config.iCanvasDistance);
VR_SetConfig(VR_CONFIG_FOV_SCALE, g_Config.iFieldOfViewPercentage);
VR_SetConfig(VR_CONFIG_STEREO_SEPARATION, g_Config.iStereoSeparation);
return true;
}
return false;
+1 -1
View File
@@ -166,7 +166,7 @@ void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) {
XrSwapchainImageWaitInfo waitInfo;
waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
waitInfo.next = NULL;
waitInfo.timeout = 1000; /* timeout in nanoseconds */
waitInfo.timeout = 1000000; /* timeout in nanoseconds */
XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo);
int i = 0;
while ((res != XR_SUCCESS) && (i < 10)) {
+1 -1
View File
@@ -17,7 +17,7 @@ float ToRadians(float deg);
// ovrMatrix4f
float ovrMatrix4f_Minor(const ovrMatrix4f* m, int r0, int r1, int r2, int c0, int c1, int c2);
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q);
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(const float fovDegreesX, const float fovDegreesY, const float offsetX, const float offsetY, const float nearZ, const float farZ);
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(const float angleLeft, const float angleRight, const float angleUp, const float angleDown, const float nearZ, const float farZ);
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ);
ovrMatrix4f ovrMatrix4f_Inverse(const ovrMatrix4f* m);
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b);
+11 -12
View File
@@ -462,19 +462,12 @@ void VR_BindFramebuffer(engine_t *engine) {
ovrMatrix4f VR_GetMatrix( VRMatrix matrix ) {
ovrMatrix4f output;
if (matrix == VR_PROJECTION_MATRIX_HUD) {
float hudScale = ToRadians(15.0f);
output = ovrMatrix4f_CreateProjectionFov(-hudScale, hudScale, hudScale, -hudScale, 1.0f, 0.0f );
} else if ((matrix == VR_PROJECTION_MATRIX_LEFT_EYE) || (matrix == VR_PROJECTION_MATRIX_RIGHT_EYE)) {
if ((matrix == VR_PROJECTION_MATRIX_LEFT_EYE) || (matrix == VR_PROJECTION_MATRIX_RIGHT_EYE)) {
XrFovf fov = matrix == VR_PROJECTION_MATRIX_LEFT_EYE ? projections[0].fov : projections[1].fov;
float fovScale = vrConfig[VR_CONFIG_FOV_SCALE] * 0.01f;
fov.angleLeft *= fovScale;
fov.angleRight *= fovScale;
fov.angleUp *= fovScale;
fov.angleDown *= fovScale;
output = ovrMatrix4f_CreateProjectionFov(fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown, 1.0f, 0.0f );
float near = (float)vrConfig[VR_CONFIG_FOV_SCALE] / 200.0f;
output = ovrMatrix4f_CreateProjectionFov(fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown, near, 0.0f );
} else if ((matrix == VR_VIEW_MATRIX_LEFT_EYE) || (matrix == VR_VIEW_MATRIX_RIGHT_EYE)) {
XrPosef invView = matrix == VR_VIEW_MATRIX_LEFT_EYE ? invViewTransform[0] : invViewTransform[1];
XrPosef invView = invViewTransform[0];
// get axis mirroring configuration
float mx = vrConfig[VR_CONFIG_MIRROR_PITCH] ? -1 : 1;
@@ -500,12 +493,18 @@ ovrMatrix4f VR_GetMatrix( VRMatrix matrix ) {
}
output = ovrMatrix4f_CreateFromQuaternion(&invView.orientation);
float scale = (float)VR_GetConfig(VR_CONFIG_6DOF_SCALE) * 0.001f;
if (vrConfig[VR_CONFIG_6DOF_ENABLED]) {
float scale = (float)VR_GetConfig(VR_CONFIG_6DOF_SCALE) * 0.001f;
output.M[0][3] -= hmdposition.x * (vrConfig[VR_CONFIG_MIRROR_AXIS_X] ? -1.0f : 1.0f) * scale;
output.M[1][3] -= hmdposition.y * (vrConfig[VR_CONFIG_MIRROR_AXIS_Y] ? -1.0f : 1.0f) * scale;
output.M[2][3] -= hmdposition.z * (vrConfig[VR_CONFIG_MIRROR_AXIS_Z] ? -1.0f : 1.0f) * scale;
}
if (matrix == VR_VIEW_MATRIX_RIGHT_EYE) {
float ipdScale = (float)vrConfig[VR_CONFIG_STEREO_SEPARATION] * 0.1f * scale;
output.M[0][3] += (invViewTransform[1].position.x - invViewTransform[0].position.x) * ipdScale;
output.M[1][3] += (invViewTransform[1].position.y - invViewTransform[0].position.y) * ipdScale;
output.M[2][3] += (invViewTransform[1].position.z - invViewTransform[0].position.z) * ipdScale;
}
} else {
assert(false);
}
+5 -6
View File
@@ -7,7 +7,7 @@ enum VRConfig {
//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,
VR_CONFIG_FOV_SCALE, VR_CONFIG_CANVAS_DISTANCE, VR_CONFIG_STEREO_SEPARATION,
//6DoF
VR_CONFIG_6DOF_ENABLED, VR_CONFIG_6DOF_SCALE,
VR_CONFIG_MIRROR_AXIS_X, VR_CONFIG_MIRROR_AXIS_Y, VR_CONFIG_MIRROR_AXIS_Z,
@@ -26,11 +26,10 @@ enum VRConfig {
};
enum VRMatrix {
VR_PROJECTION_MATRIX_HUD = 0,
VR_PROJECTION_MATRIX_LEFT_EYE = 1,
VR_PROJECTION_MATRIX_RIGHT_EYE = 2,
VR_VIEW_MATRIX_LEFT_EYE = 3,
VR_VIEW_MATRIX_RIGHT_EYE = 4
VR_PROJECTION_MATRIX_LEFT_EYE = 0,
VR_PROJECTION_MATRIX_RIGHT_EYE = 1,
VR_VIEW_MATRIX_LEFT_EYE = 2,
VR_VIEW_MATRIX_RIGHT_EYE = 3
};
enum VRMode {
+8 -3
View File
@@ -68,10 +68,15 @@ void VR_TweakMirroring(float* projMatrix) {
}
void VR_TweakProjection(float* src, float* dst, VRMatrix matrix) {
memcpy(dst, src, 16 * sizeof(float));
ovrMatrix4f hmdProjection = VR_GetMatrix(matrix);
dst[0] = (dst[0] > 0 ? 1.0f : -1.0f) * hmdProjection.M[0][0];
dst[5] = (dst[5] > 0 ? 1.0f : -1.0f) * hmdProjection.M[1][1];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if ((hmdProjection.M[i][j] > 0) != (src[i * 4 + j] > 0)) {
hmdProjection.M[i][j] *= -1.0f;
}
}
}
memcpy(dst, hmdProjection.M, 16 * sizeof(float));
}
void VR_TweakView(float* view, float* projMatrix, VRMatrix matrix) {
+6
View File
@@ -97,11 +97,17 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
CheckSetting(iniFile, gameID, "AtracLoopHack", &flags_.AtracLoopHack);
CheckSetting(iniFile, gameID, "DeswizzleDepth", &flags_.DeswizzleDepth);
CheckSetting(iniFile, gameID, "SplitFramebufferMargin", &flags_.SplitFramebufferMargin);
CheckSetting(iniFile, gameID, "ForceLowerResolutionForEffectsOn", &flags_.ForceLowerResolutionForEffectsOn);
CheckSetting(iniFile, gameID, "VRClearFramebuffer", &flags_.VRClearFramebuffer);
}
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
if (ignored_.find(option) == ignored_.end()) {
iniFile.Get(option, gameID.c_str(), flag, *flag);
// Shortcut for debugging, sometimes useful to globally enable compat flags.
bool all = false;
iniFile.Get(option, "ALL", &all, false);
*flag |= all;
}
}
+1
View File
@@ -87,6 +87,7 @@ struct CompatFlags {
bool AtracLoopHack;
bool DeswizzleDepth;
bool SplitFramebufferMargin;
bool ForceLowerResolutionForEffectsOn;
bool VRClearFramebuffer;
};
+2 -1
View File
@@ -1205,9 +1205,10 @@ static ConfigSetting themeSettings[] = {
static ConfigSetting vrSettings[] = {
ConfigSetting("VREnable", &g_Config.bEnableVR, true),
ConfigSetting("VREnable6DoF", &g_Config.bEnable6DoF, true),
ConfigSetting("VREnableStereo", &g_Config.bEnableStereo, true),
ConfigSetting("VREnableStereo", &g_Config.bEnableStereo, false),
ConfigSetting("VRCanvasDistance", &g_Config.iCanvasDistance, 6),
ConfigSetting("VRFieldOfView", &g_Config.iFieldOfViewPercentage, 100),
ConfigSetting("VRStereoSeparation", &g_Config.iStereoSeparation, 10),
ConfigSetting(false),
};
+1
View File
@@ -458,6 +458,7 @@ public:
bool bEnableStereo;
int iCanvasDistance;
int iFieldOfViewPercentage;
int iStereoSeparation;
// Debugger
int iDisasmWindowX;
+26 -2
View File
@@ -565,8 +565,32 @@ static int sceGeGetMtx(int type, u32 matrixPtr) {
static u32 sceGeGetCmd(int cmd) {
if (cmd >= 0 && cmd < (int)ARRAY_SIZE(gstate.cmdmem)) {
// Does not mask away the high bits.
return hleLogSuccessInfoX(SCEGE, gstate.cmdmem[cmd]);
// Does not mask away the high bits. But matrix regs don't read back.
u32 val = gstate.cmdmem[cmd];
switch (cmd) {
case GE_CMD_BONEMATRIXDATA:
case GE_CMD_WORLDMATRIXDATA:
case GE_CMD_VIEWMATRIXDATA:
case GE_CMD_PROJMATRIXDATA:
case GE_CMD_TGENMATRIXDATA:
val &= 0xFF000000;
break;
case GE_CMD_BONEMATRIXNUMBER:
val &= 0xFF00007F;
break;
case GE_CMD_WORLDMATRIXNUMBER:
case GE_CMD_VIEWMATRIXNUMBER:
case GE_CMD_PROJMATRIXNUMBER:
case GE_CMD_TGENMATRIXNUMBER:
val &= 0xFF00000F;
break;
default:
break;
}
return hleLogSuccessInfoX(SCEGE, val);
}
return hleLogError(SCEGE, SCE_KERNEL_ERROR_INVALID_INDEX);
}
+84 -40
View File
@@ -55,7 +55,7 @@
#define LOOPOPTIMIZATION 0
// We can disable nice delay slots.
// #define CONDITIONAL_NICE_DELAYSLOT delaySlotIsNice = false;
// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;
#define CONDITIONAL_NICE_DELAYSLOT ;
using namespace MIPSAnalyst;
@@ -76,9 +76,13 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
CONDITIONAL_NICE_DELAYSLOT;
bool immBranch = false;
bool immBranchTaken = false;
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
// The cc flags are opposites: when NOT to take the branch.
bool immBranchNotTaken;
s32 rsImm = (s32)gpr.GetImm(rs);
@@ -112,11 +116,9 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
return;
}
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs);
CONDITIONAL_NICE_DELAYSLOT;
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
if (immBranch) {
// Continuing is handled above, this is just static jumping.
if (immBranchTaken || !likely)
@@ -124,10 +126,10 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
else
FlushAll();
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
WriteExit(destAddr, js.nextExit++);
} else {
if (!likely && delaySlotIsNice)
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
// We might be able to flip the condition (EQ/NEQ are easy.)
@@ -156,7 +158,7 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
ArmGen::FixupBranch ptr;
if (!likely) {
if (!delaySlotIsNice)
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
@@ -164,7 +166,18 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
} else {
FlushAll();
ptr = B_CC(cc);
CompileDelaySlot(DELAYSLOT_FLUSH);
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
@@ -172,7 +185,7 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
SetJumpTarget(ptr);
// Not taken
WriteExit(GetCompilerPC() + 8, js.nextExit++);
WriteExit(notTakenTarget, js.nextExit++);
}
js.compiling = false;
@@ -189,9 +202,13 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
CONDITIONAL_NICE_DELAYSLOT;
bool immBranch = false;
bool immBranchTaken = false;
if (gpr.IsImm(rs)) {
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
// The cc flags are opposites: when NOT to take the branch.
bool immBranchNotTaken;
s32 imm = (s32)gpr.GetImm(rs);
@@ -231,11 +248,9 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
return;
}
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
CONDITIONAL_NICE_DELAYSLOT;
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
if (immBranch) {
// Continuing is handled above, this is just static jumping.
if (andLink)
@@ -245,10 +260,10 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
else
FlushAll();
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
WriteExit(destAddr, js.nextExit++);
} else {
if (!likely && delaySlotIsNice)
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
gpr.MapReg(rs);
@@ -260,7 +275,7 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
ArmGen::FixupBranch ptr;
if (!likely)
{
if (!delaySlotIsNice)
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
@@ -270,7 +285,18 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
{
FlushAll();
ptr = B_CC(cc);
CompileDelaySlot(DELAYSLOT_FLUSH);
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
@@ -278,7 +304,7 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
SetJumpTarget(ptr);
// Not taken
WriteExit(GetCompilerPC() + 8, js.nextExit++);
WriteExit(notTakenTarget, js.nextExit++);
}
js.compiling = false;
}
@@ -335,11 +361,12 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
CONDITIONAL_NICE_DELAYSLOT;
if (!likely && delaySlotIsNice)
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
gpr.MapReg(MIPS_REG_FPCOND);
@@ -348,7 +375,7 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
ArmGen::FixupBranch ptr;
if (!likely)
{
if (!delaySlotIsNice)
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
@@ -358,7 +385,18 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
{
FlushAll();
ptr = B_CC(cc);
CompileDelaySlot(DELAYSLOT_FLUSH);
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
@@ -366,7 +404,7 @@ void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)
SetJumpTarget(ptr);
// Not taken
WriteExit(GetCompilerPC() + 8, js.nextExit++);
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
js.compiling = false;
}
@@ -394,19 +432,16 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
// However, it does consistently try each branch, which these games seem to expect.
bool delaySlotIsBranch = MIPSCodeUtils::IsVFPUBranch(delaySlotOp);
bool delaySlotIsNice = !delaySlotIsBranch && IsDelaySlotNiceVFPU(op, delaySlotOp);
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
CONDITIONAL_NICE_DELAYSLOT;
if (!likely && delaySlotIsNice)
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
if (!likely && branchInfo.delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_NICE);
if (delaySlotIsBranch && (signed short)(delaySlotOp & 0xFFFF) != (signed short)(op & 0xFFFF) - 1)
ERROR_LOG_REPORT(JIT, "VFPU branch in VFPU delay slot at %08x with different target", GetCompilerPC());
int imm3 = (op >> 18) & 7;
@@ -417,7 +452,7 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)
js.inDelaySlot = true;
if (!likely)
{
if (!delaySlotIsNice && !delaySlotIsBranch)
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
@@ -427,18 +462,27 @@ void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)
{
FlushAll();
ptr = B_CC(cc);
if (!delaySlotIsBranch)
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_FLUSH);
}
js.inDelaySlot = false;
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
WriteExit(targetAddr, js.nextExit++);
SetJumpTarget(ptr);
// Not taken
u32 notTakenTarget = GetCompilerPC() + (delaySlotIsBranch ? 4 : 8);
WriteExit(notTakenTarget, js.nextExit++);
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
js.compiling = false;
}
+87 -43
View File
@@ -55,7 +55,7 @@
#define LOOPOPTIMIZATION 0
// We can disable nice delay slots.
// #define CONDITIONAL_NICE_DELAYSLOT delaySlotIsNice = false;
// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;
#define CONDITIONAL_NICE_DELAYSLOT ;
using namespace MIPSAnalyst;
@@ -76,9 +76,13 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
CONDITIONAL_NICE_DELAYSLOT;
bool immBranch = false;
bool immBranchTaken = false;
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
// The cc flags are opposites: when NOT to take the branch.
bool immBranchNotTaken;
s32 rsImm = (s32)gpr.GetImm(rs);
@@ -112,11 +116,9 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
return;
}
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs);
CONDITIONAL_NICE_DELAYSLOT;
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
if (immBranch) {
// Continuing is handled above, this is just static jumping.
if (immBranchTaken || !likely)
@@ -124,10 +126,10 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
else
FlushAll();
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
WriteExit(destAddr, js.nextExit++);
} else {
if (!likely && delaySlotIsNice)
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
// We might be able to flip the condition (EQ/NEQ are easy.)
@@ -136,7 +138,7 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
const bool rtIsZero = gpr.IsImm(rt) && gpr.GetImm(rt) == 0;
Arm64Gen::FixupBranch ptr;
if ((likely || delaySlotIsNice) && (rsIsZero || rtIsZero) && canFlip) {
if ((likely || branchInfo.delaySlotIsNice) && (rsIsZero || rtIsZero) && canFlip) {
// Special case, we can just use CBZ/CBNZ directly.
MIPSGPReg r = rsIsZero ? rt : rs;
gpr.MapReg(r);
@@ -169,7 +171,7 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
}
if (!likely) {
if (!delaySlotIsNice)
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
@@ -180,17 +182,27 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
}
}
if (likely) {
if (likely && !branchInfo.delaySlotIsBranch) {
// Only executed when taking the branch.
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
WriteExit(targetAddr, js.nextExit++);
SetJumpTarget(ptr);
// Not taken
WriteExit(GetCompilerPC() + 8, js.nextExit++);
WriteExit(notTakenTarget, js.nextExit++);
}
js.compiling = false;
@@ -207,9 +219,13 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
CONDITIONAL_NICE_DELAYSLOT;
bool immBranch = false;
bool immBranchTaken = false;
if (gpr.IsImm(rs)) {
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
// The cc flags are opposites: when NOT to take the branch.
bool immBranchNotTaken;
s32 imm = (s32)gpr.GetImm(rs);
@@ -249,11 +265,9 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
return;
}
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
CONDITIONAL_NICE_DELAYSLOT;
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
if (immBranch) {
// Continuing is handled above, this is just static jumping.
if (andLink)
@@ -263,10 +277,10 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
else
FlushAll();
const u32 destAddr = immBranchTaken ? targetAddr : GetCompilerPC() + 8;
const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;
WriteExit(destAddr, js.nextExit++);
} else {
if (!likely && delaySlotIsNice)
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
gpr.MapReg(rs);
@@ -278,7 +292,7 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
Arm64Gen::FixupBranch ptr;
if (!likely)
{
if (!delaySlotIsNice)
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
@@ -288,7 +302,18 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
{
FlushAll();
ptr = B(cc);
CompileDelaySlot(DELAYSLOT_FLUSH);
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
@@ -296,7 +321,7 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
SetJumpTarget(ptr);
// Not taken
WriteExit(GetCompilerPC() + 8, js.nextExit++);
WriteExit(notTakenTarget, js.nextExit++);
}
js.compiling = false;
}
@@ -352,16 +377,17 @@ void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) {
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
CONDITIONAL_NICE_DELAYSLOT;
if (!likely && delaySlotIsNice)
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
gpr.MapReg(MIPS_REG_FPCOND);
Arm64Gen::FixupBranch ptr;
if (likely || delaySlotIsNice) {
if (likely || branchInfo.delaySlotIsNice) {
// FlushAll() won't actually change the reg.
ARM64Reg ar = gpr.R(MIPS_REG_FPCOND);
FlushAll();
@@ -372,20 +398,31 @@ void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) {
}
} else {
TSTI2R(gpr.R(MIPS_REG_FPCOND), 1, SCRATCH1);
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
ptr = B(cc);
}
if (likely) {
if (likely && !branchInfo.delaySlotIsBranch) {
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
WriteExit(targetAddr, js.nextExit++);
SetJumpTarget(ptr);
// Not taken
WriteExit(GetCompilerPC() + 8, js.nextExit++);
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
js.compiling = false;
}
@@ -410,25 +447,22 @@ void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) {
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
// However, it does consistently try each branch, which these games seem to expect.
bool delaySlotIsBranch = MIPSCodeUtils::IsVFPUBranch(delaySlotOp);
bool delaySlotIsNice = !delaySlotIsBranch && IsDelaySlotNiceVFPU(op, delaySlotOp);
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
CONDITIONAL_NICE_DELAYSLOT;
if (!likely && delaySlotIsNice)
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
if (!likely && branchInfo.delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_NICE);
if (delaySlotIsBranch && (signed short)(delaySlotOp & 0xFFFF) != (signed short)(op & 0xFFFF) - 1)
ERROR_LOG_REPORT(JIT, "VFPU branch in VFPU delay slot at %08x with different target", GetCompilerPC());
int imm3 = (op >> 18) & 7;
gpr.MapReg(MIPS_REG_VFPUCC);
Arm64Gen::FixupBranch ptr;
if (likely || delaySlotIsNice || delaySlotIsBranch) {
if (likely || branchInfo.delaySlotIsNice || branchInfo.delaySlotIsBranch) {
// FlushAll() won't actually change the reg.
ARM64Reg ar = gpr.R(MIPS_REG_VFPUCC);
FlushAll();
@@ -439,21 +473,31 @@ void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) {
}
} else {
TSTI2R(gpr.R(MIPS_REG_VFPUCC), 1ULL << imm3, SCRATCH1);
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
ptr = B(cc);
}
if (likely && !delaySlotIsBranch) {
if (likely && !branchInfo.delaySlotIsBranch) {
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
WriteExit(targetAddr, js.nextExit++);
SetJumpTarget(ptr);
// Not taken
u32 notTakenTarget = GetCompilerPC() + (delaySlotIsBranch ? 4 : 8);
WriteExit(notTakenTarget, js.nextExit++);
WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);
js.compiling = false;
}
+63 -30
View File
@@ -64,15 +64,16 @@ void IRFrontend::BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely) {
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
// Often, div/divu are followed by a likely "break" if the divisor was zero.
// Stalling is not really useful for us, so we optimize this out.
if (likely && offset == 4 && MIPS_IS_BREAK(delaySlotOp)) {
if (likely && offset == 4 && MIPS_IS_BREAK(branchInfo.delaySlotOp)) {
// Okay, let's not actually branch at all. We're done here.
EatInstruction(delaySlotOp);
EatInstruction(branchInfo.delaySlotOp);
// Let's not double-count the downcount, though.
js.downcountAmount--;
return;
@@ -80,7 +81,7 @@ void IRFrontend::BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely) {
MIPSGPReg lhs = rs;
MIPSGPReg rhs = rt;
if (!delaySlotIsNice && !likely) { // if likely, we don't need this
if (!branchInfo.delaySlotIsNice && !likely) { // if likely, we don't need this
if (rs != 0) {
ir.Write(IROp::Mov, IRTEMP_LHS, rs);
lhs = (MIPSGPReg)IRTEMP_LHS;
@@ -91,7 +92,7 @@ void IRFrontend::BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely) {
}
}
if (!likely)
if (!likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
int dcAmount = js.downcountAmount;
@@ -99,10 +100,18 @@ void IRFrontend::BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely) {
js.downcountAmount = 0;
FlushAll();
ir.Write(ComparisonToExit(cc), ir.AddConstant(GetCompilerPC() + 8), lhs, rhs);
ir.Write(ComparisonToExit(cc), ir.AddConstant(ResolveNotTakenTarget(branchInfo)), lhs, rhs);
// This makes the block "impure" :(
if (likely)
if (likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
ir.WriteSetConstant(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
ir.WriteSetConstant(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
}
FlushAll();
ir.Write(IROp::ExitToConst, ir.AddConstant(targetAddr));
@@ -121,19 +130,20 @@ void IRFrontend::BranchRSZeroComp(MIPSOpcode op, IRComparison cc, bool andLink,
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
MIPSGPReg lhs = rs;
if (!delaySlotIsNice) { // if likely, we don't need this
if (!branchInfo.delaySlotIsNice) { // if likely, we don't need this
ir.Write(IROp::Mov, IRTEMP_LHS, rs);
lhs = (MIPSGPReg)IRTEMP_LHS;
}
if (andLink)
ir.WriteSetConstant(MIPS_REG_RA, GetCompilerPC() + 8);
if (!likely)
if (!likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
int dcAmount = js.downcountAmount;
@@ -141,9 +151,18 @@ void IRFrontend::BranchRSZeroComp(MIPSOpcode op, IRComparison cc, bool andLink,
js.downcountAmount = 0;
FlushAll();
ir.Write(ComparisonToExit(cc), ir.AddConstant(GetCompilerPC() + 8), lhs);
if (likely)
ir.Write(ComparisonToExit(cc), ir.AddConstant(ResolveNotTakenTarget(branchInfo)), lhs);
if (likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
ir.WriteSetConstant(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
ir.WriteSetConstant(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
}
// Taken
FlushAll();
ir.Write(IROp::ExitToConst, ir.AddConstant(targetAddr));
@@ -199,8 +218,10 @@ void IRFrontend::BranchFPFlag(MIPSOpcode op, IRComparison cc, bool likely) {
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
ir.Write(IROp::FpCondToReg, IRTEMP_LHS);
if (!likely)
if (!likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
int dcAmount = js.downcountAmount;
@@ -209,10 +230,19 @@ void IRFrontend::BranchFPFlag(MIPSOpcode op, IRComparison cc, bool likely) {
FlushAll();
// Not taken
ir.Write(ComparisonToExit(cc), ir.AddConstant(GetCompilerPC() + 8), IRTEMP_LHS, 0);
ir.Write(ComparisonToExit(cc), ir.AddConstant(ResolveNotTakenTarget(branchInfo)), IRTEMP_LHS, 0);
// Taken
if (likely)
if (likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
ir.WriteSetConstant(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
ir.WriteSetConstant(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
}
FlushAll();
ir.Write(IROp::ExitToConst, ir.AddConstant(targetAddr));
@@ -242,34 +272,37 @@ void IRFrontend::BranchVFPUFlag(MIPSOpcode op, IRComparison cc, bool likely) {
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
ir.Write(IROp::VfpuCtrlToReg, IRTEMP_LHS, VFPU_CTRL_CC);
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
// However, it does consistently try each branch, which these games seem to expect.
bool delaySlotIsBranch = MIPSCodeUtils::IsVFPUBranch(delaySlotOp);
if (!likely)
if (!likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
int dcAmount = js.downcountAmount;
ir.Write(IROp::Downcount, 0, ir.AddConstant(dcAmount));
js.downcountAmount = 0;
if (delaySlotIsBranch && (signed short)(delaySlotOp & 0xFFFF) != (signed short)(op & 0xFFFF) - 1)
ERROR_LOG_REPORT(JIT, "VFPU branch in VFPU delay slot at %08x with different target", GetCompilerPC());
int imm3 = (op >> 18) & 7;
u32 notTakenTarget = GetCompilerPC() + (delaySlotIsBranch ? 4 : 8);
ir.Write(IROp::AndConst, IRTEMP_LHS, IRTEMP_LHS, ir.AddConstant(1 << imm3));
FlushAll();
ir.Write(ComparisonToExit(cc), ir.AddConstant(notTakenTarget), IRTEMP_LHS, 0);
ir.Write(ComparisonToExit(cc), ir.AddConstant(ResolveNotTakenTarget(branchInfo)), IRTEMP_LHS, 0);
if (likely)
if (likely && !branchInfo.delaySlotIsBranch)
CompileDelaySlot();
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
ir.WriteSetConstant(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
ir.WriteSetConstant(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
}
// Taken
FlushAll();
+30 -1
View File
@@ -22,6 +22,7 @@
#include "ext/disarm.h"
#include "ext/udis86/udis86.h"
#include "Common/LogReporting.h"
#include "Common/StringUtils.h"
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
@@ -29,9 +30,11 @@
#include "Core/Util/DisArm64.h"
#include "Core/Config.h"
#include "Core/MIPS/IR/IRJit.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/JitCommon/JitState.h"
#include "Core/MIPS/IR/IRJit.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSTables.h"
#if PPSSPP_ARCH(ARM)
#include "../ARM/ArmJit.h"
@@ -67,6 +70,32 @@ namespace MIPSComp {
}
}
BranchInfo::BranchInfo(u32 pc, MIPSOpcode o, MIPSOpcode delayO, bool al, bool l)
: compilerPC(pc), op(o), delaySlotOp(delayO), likely(l), andLink(al) {
delaySlotInfo = MIPSGetInfo(delaySlotOp).value;
delaySlotIsBranch = (delaySlotInfo & (IS_JUMP | IS_CONDBRANCH)) != 0;
}
u32 ResolveNotTakenTarget(const BranchInfo &branchInfo) {
u32 notTakenTarget = branchInfo.compilerPC + 8;
if ((branchInfo.delaySlotInfo & (IS_JUMP | IS_CONDBRANCH)) != 0) {
// If a branch has a j/jr/jal/jalr as a delay slot, that is run if the branch is not taken.
// TODO: Technically, in the likely case, we should somehow suppress andLink on this exit.
bool isJump = (branchInfo.delaySlotInfo & IS_JUMP) != 0;
// If the delay slot is a branch, likely skips it.
if (isJump || !branchInfo.likely)
notTakenTarget -= 4;
// For a branch (not a jump), it actually should try the delay slot and take its target potentially.
// This is similar to the VFPU case and has not been seen, so just report it.
if (!isJump && SignExtend16ToU32(branchInfo.delaySlotOp) != SignExtend16ToU32(branchInfo.op) - 1)
ERROR_LOG_REPORT(JIT, "Branch in branch delay slot at %08x with different target", branchInfo.compilerPC);
if (isJump && branchInfo.likely && (branchInfo.delaySlotInfo & (OUT_RA | OUT_RD)) != 0)
ERROR_LOG_REPORT(JIT, "Jump in likely branch delay slot with link at %08x", branchInfo.compilerPC);
}
return notTakenTarget;
}
JitInterface *CreateNativeJit(MIPSState *mipsState) {
#if PPSSPP_ARCH(ARM)
return new MIPSComp::ArmJit(mipsState);
+17
View File
@@ -153,6 +153,23 @@ namespace MIPSComp {
typedef void (MIPSFrontendInterface::*MIPSCompileFunc)(MIPSOpcode opcode);
typedef int (MIPSFrontendInterface::*MIPSReplaceFunc)();
struct BranchInfo {
BranchInfo(u32 pc, MIPSOpcode op, MIPSOpcode delaySlotOp, bool andLink, bool likely);
u32 compilerPC;
MIPSOpcode op;
MIPSOpcode delaySlotOp;
u64 delaySlotInfo;
bool likely;
bool andLink;
// Update manually if it's not always nice (rs/rt, rs/zero, etc.)
bool delaySlotIsNice = true;
bool delaySlotIsBranch;
};
// This seems to be the same for all branch types.
u32 ResolveNotTakenTarget(const BranchInfo &branchInfo);
extern JitInterface *jit;
extern std::recursive_mutex jitLock;
+74 -47
View File
@@ -63,7 +63,7 @@ using namespace MIPSAnalyst;
#define DO_CONDITIONAL_LOG 0
// We can also disable nice delay slots.
// #define CONDITIONAL_NICE_DELAYSLOT delaySlotIsNice = false;
// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;
#define CONDITIONAL_NICE_DELAYSLOT ;
#if DO_CONDITIONAL_LOG
@@ -197,22 +197,22 @@ bool Jit::PredictTakeBranch(u32 targetAddr, bool likely) {
return targetAddr > GetCompilerPC();
}
void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink) {
if (andLink)
void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, const BranchInfo &branchInfo) {
if (branchInfo.andLink)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
// We may want to try to continue along this branch a little while, to reduce reg flushing.
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
if (CanContinueBranch(predictTakeBranch ? targetAddr : notTakenAddr))
bool predictTakeBranch = PredictTakeBranch(targetAddr, branchInfo.likely);
if (!branchInfo.delaySlotIsBranch && CanContinueBranch(predictTakeBranch ? targetAddr : notTakenAddr))
{
if (predictTakeBranch)
cc = FlipCCFlag(cc);
Gen::FixupBranch ptr;
RegCacheState state;
if (!likely)
if (!branchInfo.likely)
{
if (!delaySlotIsNice)
if (!branchInfo.delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_SAFE);
ptr = J_CC(cc, true);
GetStateAndFlushAll(state);
@@ -243,7 +243,7 @@ void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool del
CONDITIONAL_LOG_EXIT(targetAddr);
// Don't forget to run the delay slot if likely.
if (likely)
if (branchInfo.likely)
CompileDelaySlot(DELAYSLOT_NICE);
AddContinuedBlock(targetAddr);
@@ -272,9 +272,9 @@ void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool del
else
{
Gen::FixupBranch ptr;
if (!likely)
if (!branchInfo.likely)
{
if (!delaySlotIsNice)
if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
@@ -284,7 +284,19 @@ void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool del
{
FlushAll();
ptr = J_CC(cc, true);
CompileDelaySlot(DELAYSLOT_FLUSH);
if (!branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_FLUSH);
}
// Handle the linkage of a delay slot, even when we're taking the branch.
if (branchInfo.delaySlotIsBranch) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
FlushAll();
}
// Take the branch
@@ -299,14 +311,25 @@ void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool del
}
}
void Jit::CompBranchExit(bool taken, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink) {
void Jit::CompBranchExit(bool taken, u32 targetAddr, u32 notTakenAddr, const BranchInfo &branchInfo) {
// Continuing is handled in the imm branch case... TODO: move it here?
if (andLink)
if (branchInfo.andLink)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
if (taken || !likely)
CompileDelaySlot(DELAYSLOT_FLUSH);
else
if (branchInfo.delaySlotIsBranch) {
if (taken) {
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
if ((branchInfo.delaySlotInfo & OUT_RA) != 0)
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);
if ((branchInfo.delaySlotInfo & OUT_RD) != 0)
gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);
}
FlushAll();
} else if (taken || !branchInfo.likely) {
CompileDelaySlot(DELAYSLOT_FLUSH);
} else {
FlushAll();
}
const u32 destAddr = taken ? targetAddr : notTakenAddr;
CONDITIONAL_LOG_EXIT(destAddr);
@@ -326,9 +349,13 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);
CONDITIONAL_NICE_DELAYSLOT;
bool immBranch = false;
bool immBranchTaken = false;
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {
// The cc flags are opposites: when NOT to take the branch.
bool immBranchNotTaken;
s32 rsImm = (s32)gpr.GetImm(rs);
@@ -364,16 +391,14 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
return;
}
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rt, rs);
CONDITIONAL_NICE_DELAYSLOT;
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
if (immBranch)
CompBranchExit(immBranchTaken, targetAddr, GetCompilerPC() + 8, delaySlotIsNice, likely, false);
CompBranchExit(immBranchTaken, targetAddr, notTakenTarget, branchInfo);
else
{
if (!likely && delaySlotIsNice)
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0)
@@ -387,7 +412,7 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
CMP(32, gpr.R(rs), gpr.R(rt));
}
CompBranchExits(cc, targetAddr, GetCompilerPC() + 8, delaySlotIsNice, likely, false);
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
}
}
@@ -402,9 +427,14 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li
MIPSGPReg rs = _RS;
u32 targetAddr = GetCompilerPC() + offset + 4;
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
CONDITIONAL_NICE_DELAYSLOT;
bool immBranch = false;
bool immBranchTaken = false;
if (gpr.IsImm(rs)) {
if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {
// The cc flags are opposites: when NOT to take the branch.
bool immBranchNotTaken;
s32 imm = (s32)gpr.GetImm(rs);
@@ -446,22 +476,20 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li
return;
}
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);
CONDITIONAL_NICE_DELAYSLOT;
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
if (immBranch)
CompBranchExit(immBranchTaken, targetAddr, GetCompilerPC() + 8, delaySlotIsNice, likely, andLink);
CompBranchExit(immBranchTaken, targetAddr, notTakenTarget, branchInfo);
else
{
if (!likely && delaySlotIsNice)
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
gpr.MapReg(rs, true, false);
CMP(32, gpr.R(rs), Imm32(0));
CompBranchExits(cc, targetAddr, GetCompilerPC() + 8, delaySlotIsNice, likely, andLink);
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
}
}
@@ -518,17 +546,19 @@ void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
bool delaySlotIsNice = IsDelaySlotNiceFPU(op, delaySlotOp);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);
CONDITIONAL_NICE_DELAYSLOT;
if (!likely && delaySlotIsNice)
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
gpr.KillImmediate(MIPS_REG_FPCOND, true, false);
TEST(32, gpr.R(MIPS_REG_FPCOND), Imm32(1));
CompBranchExits(cc, targetAddr, GetCompilerPC() + 8, delaySlotIsNice, likely, false);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
}
@@ -559,19 +589,16 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
int offset = TARGET16;
u32 targetAddr = GetCompilerPC() + offset + 4;
MIPSOpcode delaySlotOp = GetOffsetInstruction(1);
js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);
BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
// However, it does consistently try each branch, which these games seem to expect.
bool delaySlotIsBranch = MIPSCodeUtils::IsVFPUBranch(delaySlotOp);
bool delaySlotIsNice = !delaySlotIsBranch && IsDelaySlotNiceVFPU(op, delaySlotOp);
branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);
CONDITIONAL_NICE_DELAYSLOT;
if (!likely && delaySlotIsNice)
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_NICE);
if (delaySlotIsBranch && (signed short)(delaySlotOp & 0xFFFF) != (signed short)(op & 0xFFFF) - 1)
ERROR_LOG_REPORT(JIT, "VFPU branch in VFPU delay slot at %08x with different target %d / %d", GetCompilerPC(), (signed short)(delaySlotOp & 0xFFFF), (signed short)(op & 0xFFFF) - 1);
// THE CONDITION
int imm3 = (op >> 18) & 7;
@@ -579,8 +606,8 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
gpr.KillImmediate(MIPS_REG_VFPUCC, true, false);
TEST(32, gpr.R(MIPS_REG_VFPUCC), Imm32(1 << imm3));
u32 notTakenTarget = GetCompilerPC() + (delaySlotIsBranch ? 4 : 8);
CompBranchExits(cc, targetAddr, notTakenTarget, delaySlotIsNice, likely, false);
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
CompBranchExits(cc, targetAddr, notTakenTarget, branchInfo);
}
+2 -2
View File
@@ -230,8 +230,8 @@ private:
}
void CompITypeMemUnpairedLR(MIPSOpcode op, bool isStore);
void CompITypeMemUnpairedLRInner(MIPSOpcode op, Gen::X64Reg shiftReg);
void CompBranchExits(Gen::CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink);
void CompBranchExit(bool taken, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink);
void CompBranchExits(Gen::CCFlags cc, u32 targetAddr, u32 notTakenAddr, const BranchInfo &branchInfo);
void CompBranchExit(bool taken, u32 targetAddr, u32 notTakenAddr, const BranchInfo &branchInfo);
static Gen::CCFlags FlipCCFlag(Gen::CCFlags flag);
static Gen::CCFlags SwapCCFlag(Gen::CCFlags flag);
+1
View File
@@ -295,6 +295,7 @@ bool CPU_Init(std::string *errorString) {
HLEPlugins::Init();
if (!Memory::Init()) {
// We're screwed.
*errorString = "Memory init failed";
return false;
}
mipsr4k.Reset();
+1
View File
@@ -234,6 +234,7 @@ bool DrawEngineCommon::TestBoundingBox(const void* control_points, int vertexCou
float worldviewproj[16];
ConvertMatrix4x3To4x4(world, gstate.worldMatrix);
ConvertMatrix4x3To4x4(view, gstate.viewMatrix);
// TODO: Create a Matrix4x3ByMatrix4x3, and Matrix4x4ByMatrix4x3?
Matrix4ByMatrix4(worldview, world, view);
Matrix4ByMatrix4(worldviewproj, worldview, gstate.projMatrix);
PlanesFromMatrix(worldviewproj, planes);
+40 -15
View File
@@ -108,11 +108,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
blueToAlpha = true;
}
GEBlendSrcFactor replaceBlendFuncA = (GEBlendSrcFactor)id.Bits(FS_BIT_BLENDFUNC_A, 4);
GEBlendDstFactor replaceBlendFuncB = (GEBlendDstFactor)id.Bits(FS_BIT_BLENDFUNC_B, 4);
GEBlendMode replaceBlendEq = (GEBlendMode)id.Bits(FS_BIT_BLENDEQ, 3);
StencilValueType replaceAlphaWithStencilType = (StencilValueType)id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4);
bool isModeClear = id.Bit(FS_BIT_CLEARMODE);
const char *shading = "";
@@ -121,7 +116,16 @@ 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_READ_FRAMEBUFFER || colorWriteMask;
GEBlendSrcFactor replaceBlendFuncA = (GEBlendSrcFactor)id.Bits(FS_BIT_BLENDFUNC_A, 4);
GEBlendDstFactor replaceBlendFuncB = (GEBlendDstFactor)id.Bits(FS_BIT_BLENDFUNC_B, 4);
GEBlendMode replaceBlendEq = (GEBlendMode)id.Bits(FS_BIT_BLENDEQ, 3);
StencilValueType replaceAlphaWithStencilType = (StencilValueType)id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4);
// Distinct from the logic op simulation support.
GELogicOp replaceLogicOpType = isModeClear ? GE_LOGIC_COPY : (GELogicOp)id.Bits(FS_BIT_REPLACE_LOGIC_OP, 4);
bool replaceLogicOp = replaceLogicOpType != GE_LOGIC_COPY && compat.bitwiseOps;
bool readFramebuffer = replaceBlend == REPLACE_BLEND_READ_FRAMEBUFFER || colorWriteMask || replaceLogicOp;
bool readFramebufferTex = readFramebuffer && !gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH);
bool needFragCoord = readFramebuffer || gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT);
@@ -425,7 +429,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
}
// Provide implementations of packUnorm4x8 and unpackUnorm4x8 if not available.
if (colorWriteMask && !hasPackUnorm4x8) {
if ((colorWriteMask || replaceLogicOp) && !hasPackUnorm4x8) {
WRITE(p, "uint packUnorm4x8(%svec4 v) {\n", compat.shaderLanguage == GLSL_VULKAN ? "highp " : "");
WRITE(p, " highp vec4 f = clamp(v, 0.0, 1.0);\n");
WRITE(p, " uvec4 u = uvec4(255.0 * f);\n");
@@ -1078,16 +1082,37 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
return false;
}
// Final color computed - apply color write mask.
// TODO: Maybe optimize to only do math on the affected channels?
// Or .. meh. That would require more shader bits. Though we could
// of course optimize for the common mask 0xF00000, though again, blue-to-alpha
// does a better job with that.
if (colorWriteMask) {
// Final color computed - apply logic ops and bitwise color write mask, through shader blending, if specified.
if (colorWriteMask || replaceLogicOp) {
WRITE(p, " highp uint v32 = packUnorm4x8(%s);\n", compat.fragColor0);
WRITE(p, " highp uint d32 = packUnorm4x8(destColor);\n");
// Note that the mask has been flipped to the PC way - 1 means write.
WRITE(p, " v32 = (v32 & u_colorWriteMask) | (d32 & ~u_colorWriteMask);\n");
// v32 is both the "s" to the logical operation, and the value that we'll merge to the destination with masking later.
// d32 is the "d" to the logical operation.
// NOTE: Alpha of v32 needs to be preserved. Same equations as in the software renderer.
switch (replaceLogicOpType) {
case GE_LOGIC_CLEAR: p.C(" v32 &= 0xFF000000u;\n"); break;
case GE_LOGIC_AND: p.C(" v32 = v32 & (d32 | 0xFF000000u);\n"); break;
case GE_LOGIC_AND_REVERSE: p.C(" v32 = v32 & (~d32 | 0xFF000000u);\n"); break;
case GE_LOGIC_COPY: break; // source to dest, do nothing. Will be set to this, if not used.
case GE_LOGIC_AND_INVERTED: p.C(" v32 = (~v32 & (d32 & 0x00FFFFFFu)) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_NOOP: p.C(" v32 = (d32 & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_XOR: p.C(" v32 = v32 ^ (d32 & 0x00FFFFFFu);\n"); break;
case GE_LOGIC_OR: p.C(" v32 = v32 | (d32 & 0x00FFFFFFu);\n"); break;
case GE_LOGIC_NOR: p.C(" v32 = (~(v32 | d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_EQUIV: p.C(" v32 = (~(v32 ^ d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_INVERTED: p.C(" v32 = (~d32 & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_OR_REVERSE: p.C(" v32 = v32 | (~d32 & 0x00FFFFFFu);\n"); break;
case GE_LOGIC_COPY_INVERTED: p.C(" v32 = (~v32 & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_OR_INVERTED: p.C(" v32 = ((~v32 | d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_NAND: p.C(" v32 = (~(v32 & d32) & 0x00FFFFFFu) | (v32 & 0xFF000000u);\n"); break;
case GE_LOGIC_SET: p.C(" v32 |= 0x00FFFFFF;\n"); break;
}
// Note that the mask has already been flipped to the PC way - 1 means write.
if (colorWriteMask) {
WRITE(p, " v32 = (v32 & u_colorWriteMask) | (d32 & ~u_colorWriteMask);\n");
}
WRITE(p, " %s = unpackUnorm4x8(v32);\n", compat.fragColor0);
}
+12 -8
View File
@@ -84,14 +84,17 @@ void FramebufferManagerCommon::Init() {
bool FramebufferManagerCommon::UpdateSize() {
const bool newRender = renderWidth_ != (float)PSP_CoreParameter().renderWidth || renderHeight_ != (float)PSP_CoreParameter().renderHeight;
const bool newSettings = bloomHack_ != g_Config.iBloomHack || useBufferedRendering_ != (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE);
const int effectiveBloomHack = PSP_CoreParameter().compat.flags().ForceLowerResolutionForEffectsOn ? 3 : g_Config.iBloomHack;
const bool newSettings = bloomHack_ != effectiveBloomHack || useBufferedRendering_ != (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE);
renderWidth_ = (float)PSP_CoreParameter().renderWidth;
renderHeight_ = (float)PSP_CoreParameter().renderHeight;
renderScaleFactor_ = (float)PSP_CoreParameter().renderScaleFactor;
pixelWidth_ = PSP_CoreParameter().pixelWidth;
pixelHeight_ = PSP_CoreParameter().pixelHeight;
bloomHack_ = g_Config.iBloomHack;
bloomHack_ = effectiveBloomHack;
useBufferedRendering_ = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
presentation_->UpdateSize(pixelWidth_, pixelHeight_, renderWidth_, renderHeight_);
@@ -626,7 +629,7 @@ void FramebufferManagerCommon::CopyToDepthFromOverlappingFramebuffers(VirtualFra
BlitUsingRaster(
src->fbo, 0.0f, 0.0f, src->renderWidth, src->renderHeight,
dest->fbo, 0.0f, 0.0f, src->renderWidth, src->renderHeight,
false, Get2DPipeline(shader), "565_to_depth");
false, dest->renderScaleFactor, Get2DPipeline(shader), "565_to_depth");
}
}
@@ -802,7 +805,7 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
tookActions = true;
// OK we have the pipeline, now just do the blit.
BlitUsingRaster(src->fbo, 0.0f, 0.0f, srcWidth, srcHeight,
dst->fbo, dstX1, dstY1, dstX2, dstY2, false, pipeline, pass_name);
dst->fbo, dstX1, dstY1, dstX2, dstY2, false, dst->renderScaleFactor, pipeline, pass_name);
}
}
}
@@ -886,7 +889,7 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir
draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST, "BlitFramebufferDepth");
RebindFramebuffer("After BlitFramebufferDepth");
} else if (useRaster) {
BlitUsingRaster(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, false, Get2DPipeline(Draw2DShader::DRAW2D_COPY_DEPTH), "BlitDepthRaster");
BlitUsingRaster(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, false, dst->renderScaleFactor, Get2DPipeline(Draw2DShader::DRAW2D_COPY_DEPTH), "BlitDepthRaster");
}
draw_->InvalidateCachedState();
@@ -2784,10 +2787,10 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
Draw::Framebuffer *srcFBO = src->fbo;
if (src == dst) {
Draw::Framebuffer *tempFBO = GetTempFBO(TempFBO::BLIT, src->renderWidth, src->renderHeight);
BlitUsingRaster(src->fbo, srcX1, srcY1, srcX2, srcY2, tempFBO, dstX1, dstY1, dstX2, dstY2, false, pipeline, tag);
BlitUsingRaster(src->fbo, srcX1, srcY1, srcX2, srcY2, tempFBO, dstX1, dstY1, dstX2, dstY2, false, dst->renderScaleFactor, pipeline, tag);
srcFBO = tempFBO;
}
BlitUsingRaster(srcFBO, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, false, pipeline, tag);
BlitUsingRaster(srcFBO, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, false, dst->renderScaleFactor, pipeline, tag);
}
draw_->InvalidateCachedState();
@@ -2800,6 +2803,7 @@ void FramebufferManagerCommon::BlitUsingRaster(
Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2,
Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2,
bool linearFilter,
int scaleFactor,
Draw2DPipeline *pipeline, const char *tag) {
if (pipeline->info.writeChannel == RASTER_DEPTH) {
@@ -2825,7 +2829,7 @@ void FramebufferManagerCommon::BlitUsingRaster(
draw_->SetViewports(1, &vp);
draw_->SetScissorRect(0, 0, (int)dest->Width(), (int)dest->Height());
draw2D_.Blit(pipeline, srcX1, srcY1, srcX2, srcY2, destX1, destY1, destX2, destY2, (float)srcW, (float)srcH, (float)destW, (float)destH, linearFilter , renderScaleFactor_);
draw2D_.Blit(pipeline, srcX1, srcY1, srcX2, srcY2, destX1, destY1, destX2, destY2, (float)srcW, (float)srcH, (float)destW, (float)destH, linearFilter, scaleFactor);
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
}
+4 -1
View File
@@ -441,7 +441,10 @@ protected:
void BlitUsingRaster(
Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2,
Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, bool linearFilter, Draw2DPipeline *pipeline, const char *tag);
Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2,
bool linearFilter,
int scaleFactor, // usually unused, except for swizzle...
Draw2DPipeline *pipeline, const char *tag);
void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags);
+61 -21
View File
@@ -996,7 +996,7 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS
// we read from the framebuffer (or a copy of it).
// 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) {
static void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported) {
if (gstate_c.blueToAlpha) {
maskState.applyFramebufferRead = false;
maskState.uniformMask = 0xFF000000;
@@ -1011,7 +1011,7 @@ void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported) {
maskState.applyFramebufferRead = false;
maskState.channelMask = 0;
for (int i = 0; i < 4; i++) {
int channelMask = colorMask & 0xFF;
uint32_t channelMask = (colorMask >> (i * 8)) & 0xFF;
switch (channelMask) {
case 0x0:
break;
@@ -1030,7 +1030,6 @@ void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported) {
}
}
}
colorMask >>= 8;
}
// Let's not write to alpha if stencil isn't enabled.
@@ -1042,7 +1041,7 @@ void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported) {
}
// Called even if AlphaBlendEnable == false - it also deals with stencil-related blend state.
void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) {
static 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.
@@ -1087,6 +1086,7 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) {
case REPLACE_BLEND_READ_FRAMEBUFFER:
blendState.blendEnabled = true;
blendState.applyFramebufferRead = true;
blendState.simulateLogicOpType = LOGICOPTYPE_NORMAL;
break;
case REPLACE_BLEND_PRE_SRC:
@@ -1251,7 +1251,9 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) {
}
// Attempt to apply simulated logic ops, if any and if needed.
SimulateLogicOpIfNeeded(glBlendFuncA, glBlendFuncB, colorEq);
if (!forceReplaceBlend) {
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
@@ -1334,6 +1336,48 @@ void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend) {
blendState.setEquation(colorEq, alphaEq);
}
static void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported, bool forceApplyFramebuffer) {
// TODO: We can get more detailed with checks here. Some logic ops don't involve the destination at all.
// Several can be trivially supported even without any bitwise logic.
if (!gstate.isLogicOpEnabled() || gstate.getLogicOp() == GE_LOGIC_COPY) {
// No matter what, don't need to do anything.
logicOpState.logicOpEnabled = false;
logicOpState.logicOp = GE_LOGIC_COPY;
logicOpState.applyFramebufferRead = forceApplyFramebuffer;
return;
}
if (forceApplyFramebuffer && shaderBitOpsSupported) {
// We have to emulate logic ops in the shader.
logicOpState.logicOpEnabled = false; // Don't use any hardware logic op, supported or not.
logicOpState.applyFramebufferRead = true;
logicOpState.logicOp = gstate.getLogicOp();
} else if (logicSupported) {
// We can use hardware logic ops, if needed.
logicOpState.applyFramebufferRead = false;
if (gstate.isLogicOpEnabled()) {
logicOpState.logicOpEnabled = true;
logicOpState.logicOp = gstate.getLogicOp();
} else {
logicOpState.logicOpEnabled = false;
logicOpState.logicOp = GE_LOGIC_COPY;
}
} else if (shaderBitOpsSupported) {
// D3D11 and some OpenGL versions will end up here.
// Logic ops not support, bitops supported. Let's punt to the shader.
// We should possibly always do this and never use the hardware ops, since they'll mishandle the alpha channel..
logicOpState.logicOpEnabled = false; // Don't use any hardware logic op, supported or not.
logicOpState.applyFramebufferRead = true;
logicOpState.logicOp = gstate.getLogicOp();
} else {
// In this case, the SIMULATE fallback should kick in.
// Need to make sure this is checking for the same things though...
logicOpState.logicOpEnabled = false;
logicOpState.logicOp = GE_LOGIC_COPY;
logicOpState.applyFramebufferRead = false;
}
}
static void ConvertStencilFunc5551(GenericStencilFuncState &state) {
// Flaws:
// - INVERT should convert 1, 5, 0xFF to 0. Currently it won't always.
@@ -1466,19 +1510,6 @@ 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;
}
@@ -1528,7 +1559,7 @@ void ConvertStencilFuncState(GenericStencilFuncState &state) {
}
void GenericMaskState::Log() {
WARN_LOG(G3D, "Mask: %01X readfb=%d", uniformMask, channelMask, applyFramebufferRead);
WARN_LOG(G3D, "Mask: %08x %01X readfb=%d", uniformMask, channelMask, applyFramebufferRead);
}
void GenericBlendState::Log() {
@@ -1537,7 +1568,16 @@ void GenericBlendState::Log() {
}
void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) {
// Passing on the previous applyFramebufferRead as forceFrameBuffer read in the next one,
// thus propagating forward.
ConvertMaskState(maskState, shaderBitOpsSuppported);
ConvertLogicOpState(logicState, gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP), shaderBitOpsSuppported);
ConvertBlendState(blendState, maskState.applyFramebufferRead);
ConvertLogicOpState(logicState, gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP), shaderBitOpsSuppported, maskState.applyFramebufferRead);
ConvertBlendState(blendState, logicState.applyFramebufferRead);
// Note: If the blend state decided it had to use framebuffer reads,
// we need to make sure that both mask and logic also use it, otherwise things will go wrong.
if (blendState.applyFramebufferRead || logicState.applyFramebufferRead) {
maskState.ConvertToShaderBlend();
logicState.ConvertToShaderBlend();
}
}
+4 -5
View File
@@ -184,7 +184,6 @@ struct GenericBlendState {
void Log();
};
void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBlend);
void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState);
struct GenericMaskState {
@@ -204,8 +203,6 @@ struct GenericMaskState {
void Log();
};
void ConvertMaskState(GenericMaskState &maskState, bool shaderBitOpsSupported);
struct GenericStencilFuncState {
bool enabled;
GEComparison testFunc;
@@ -240,14 +237,16 @@ struct GenericLogicState {
void Log();
};
void ConvertLogicOpState(GenericLogicState &logicOpState, bool logicSupported, bool shaderBitOpsSupported);
struct ComputedPipelineState {
GenericBlendState blendState;
GenericMaskState maskState;
GenericLogicState logicState;
void Convert(bool shaderBitOpsSupported);
bool FramebufferRead() const {
return blendState.applyFramebufferRead;
}
};
// See issue #15898
+4
View File
@@ -268,6 +268,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip
ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend;
ReplaceAlphaType stencilToAlpha = pipelineState.blendState.replaceAlphaWithStencil;
SimulateLogicOpType simulateLogicOpType = pipelineState.blendState.simulateLogicOpType;
GELogicOp replaceLogicOpType = pipelineState.logicState.applyFramebufferRead ? pipelineState.logicState.logicOp : GE_LOGIC_COPY;
// 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.
@@ -325,6 +326,9 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip
// 2 bits.
id.SetBits(FS_BIT_SIMULATE_LOGIC_OP_TYPE, 2, simulateLogicOpType);
// 4 bits. Set to GE_LOGIC_COPY if not used, which does nothing in the shader generator.
id.SetBits(FS_BIT_REPLACE_LOGIC_OP, 4, (int)replaceLogicOpType);
// If replaceBlend == REPLACE_BLEND_STANDARD (or REPLACE_BLEND_NO) nothing is done, so we kill these bits.
if (replaceBlend == REPLACE_BLEND_BLUE_TO_ALPHA) {
id.SetBits(FS_BIT_REPLACE_BLEND, 3, replaceBlend);
+1
View File
@@ -95,6 +95,7 @@ enum FShaderBit : uint8_t {
FS_BIT_COLOR_WRITEMASK = 50,
FS_BIT_3D_TEXTURE = 51,
FS_BIT_SHADER_SMOOTHED_DEPAL = 52,
FS_BIT_REPLACE_LOGIC_OP = 53, // 4 bits. GE_LOGIC_COPY means no-op/off.
};
static inline FShaderBit operator +(FShaderBit bit, int i) {
+47 -30
View File
@@ -639,18 +639,7 @@ bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &en
return true;
}
if (Reporting::ShouldLogNTimes("multifbcandidate", 5)) {
std::string cands;
for (size_t i = 0; i < candidates.size(); i++) {
cands += candidates[i].ToString() + "\n";
}
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()
);
}
bool logging = Reporting::ShouldLogNTimes("multifbcandidate", 5);
// OK, multiple possible candidates. Will need to figure out which one is the most relevant.
int bestRelevancy = -1;
@@ -660,7 +649,7 @@ bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &en
// We simply use the sequence counter as relevancy nowadays.
for (size_t i = 0; i < candidates.size(); i++) {
const AttachCandidate &candidate = candidates[i];
AttachCandidate &candidate = candidates[i];
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.
@@ -678,13 +667,36 @@ bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &en
continue;
}
if (logging) {
candidate.relevancy = relevancy;
}
if (relevancy > bestRelevancy) {
bestRelevancy = relevancy;
bestIndex = i;
}
}
if (logging) {
std::string cands;
for (size_t i = 0; i < candidates.size(); i++) {
cands += candidates[i].ToString();
if (i != candidates.size() - 1)
cands += "\n";
}
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()
);
logging = true;
}
if (bestIndex != -1) {
if (logging) {
WARN_LOG(G3D, "Chose candidate %d:\n%s\n", (int)bestIndex, candidates[bestIndex].ToString().c_str());
}
*bestCandidate = candidates[bestIndex];
return true;
} else {
@@ -876,8 +888,12 @@ bool TextureCacheCommon::MatchFramebuffer(
case GE_TFMT_DXT3:
case GE_TFMT_DXT5:
return false;
default: break;
}
uint32_t fb_stride_in_bytes = fb_stride * BufferFormatBytesPerPixel(fb_format);
uint32_t tex_stride_in_bytes = entry.bufw * textureBitsPerPixel[entry.format] / 8; // Note, we're looking up bits here so need to divide by 8.
u32 addr = fb_address & 0x3FFFFFFF;
u32 texaddr = entry.addr + texaddrOffset;
@@ -897,7 +913,7 @@ bool TextureCacheCommon::MatchFramebuffer(
}
const bool noOffset = texaddr == addr;
const bool exactMatch = noOffset && entry.format < 4 && channel == RASTER_COLOR;
const bool exactMatch = noOffset && entry.format < 4 && channel == RASTER_COLOR && fb_stride_in_bytes == tex_stride_in_bytes;
const u32 w = 1 << ((entry.dim >> 0) & 0xf);
const u32 h = 1 << ((entry.dim >> 8) & 0xf);
@@ -906,10 +922,6 @@ bool TextureCacheCommon::MatchFramebuffer(
// If they match "exactly", it's non-CLUT and from the top left.
if (exactMatch) {
// TODO: Better checks for compatible strides here.
if (fb_stride != entry.bufw) {
WARN_LOG_ONCE(diffStrides1, G3D, "Found matching framebuffer at %08x with different strides %d != %d", fb_address, entry.bufw, (int)fb_stride);
}
// NOTE: This check is okay because the first texture formats are the same as the buffer formats.
if (IsTextureFormatBufferCompatible(entry.format)) {
if (TextureFormatMatchesBufferFormat(entry.format, fb_format) || (framebuffer->usageFlags & FB_USAGE_BLUE_TO_ALPHA)) {
@@ -957,15 +969,9 @@ bool TextureCacheCommon::MatchFramebuffer(
return false;
}
if (fb_stride != entry.bufw) {
if (noOffset) {
WARN_LOG_ONCE(diffStrides2, G3D, "Matching framebuffer(matching_clut = %s) different strides %d != %d", matchingClutFormat ? "yes" : "no", entry.bufw, fb_stride);
// Continue on with other checks.
// Not actually sure why we even try here. There's no way it'll go well if the strides are different.
} else {
// Assume any render-to-tex with different bufw + offset is a render from ram.
return false;
}
if (fb_stride_in_bytes != tex_stride_in_bytes) {
// Probably irrelevant.
return false;
}
// Check if it's in bufferWidth (which might be higher than width and may indicate the framebuffer includes the data.)
@@ -1860,9 +1866,10 @@ static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferForma
return true;
}
break;
default:
WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat));
return false;
}
WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat));
return false;
} else if (texFormat == GE_TFMT_5650 && bufferFormat == GE_FORMAT_DEPTH16) {
// We can also "depal" 565 format, this is used to read depth buffers as 565 on occasion (#15491).
return true;
@@ -1891,6 +1898,9 @@ static bool CanUseSmoothDepal(const GPUgstate &gstate, GEBufferFormat framebuffe
return gstate.getClutIndexMask() == 0x1F;
}
break;
default:
// No uses for the other formats yet, add if needed.
break;
}
}
return false;
@@ -2247,9 +2257,10 @@ 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]",
return StringFromFormat("[%s seq:%d rel:%d C:%08x/%d(%s) Z:%08x/%d X:%d Y:%d reint: %s]",
this->channel == RASTER_COLOR ? "COLOR" : "DEPTH",
this->channel == RASTER_COLOR ? this->fb->colorBindSeq : this->fb->depthBindSeq,
this->relevancy,
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");
@@ -2348,6 +2359,12 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
plan.scaleFactor = 1;
}
if (PSP_CoreParameter().compat.flags().ForceLowerResolutionForEffectsOn && gstate.FrameBufStride() < 0x1E0) {
// A bit of an esoteric workaround - force off upscaling for static textures that participate directly in small-resolution framebuffer effects.
// This fixes the water in Outrun/DiRT 2 with upscaling enabled.
plan.scaleFactor = 1;
}
if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) != 0 && plan.scaleFactor != 1 && plan.slowScaler) {
// Remember for later that we /wanted/ to scale this texture.
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
+1
View File
@@ -218,6 +218,7 @@ struct AttachCandidate {
VirtualFramebuffer *fb;
FramebufferMatchInfo match;
RasterChannel channel;
int relevancy;
std::string ToString() const;
};
+32 -13
View File
@@ -473,11 +473,14 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
WRITE(p, "uniform mat4 u_proj_through;\n");
*uniformMask |= DIRTY_PROJTHROUGHMATRIX;
} else if (useHWTransform) {
if (IsVRBuild() && IsMultiviewSupported()) {
WRITE(p, "layout(shared) uniform ProjectionMatrix { uniform mat4 u_proj[2]; };\n");
} else {
WRITE(p, "uniform mat4 u_proj;\n");
if (IsVRBuild()) {
if (IsMultiviewSupported()) {
WRITE(p, "layout(shared) uniform ProjectionMatrix { uniform mat4 u_proj_lens[2]; };\n");
} else {
WRITE(p, "uniform mat4 u_proj_lens;\n");
}
}
WRITE(p, "uniform mat4 u_proj;\n");
*uniformMask |= DIRTY_PROJMATRIX;
}
@@ -921,9 +924,19 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
// Final view and projection transforms.
if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj%s, viewPos));\n", matrixPostfix.c_str());
if (IsVRBuild()) {
WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj_lens%s, viewPos));\n", matrixPostfix.c_str());
WRITE(p, " vec4 orgPos = depthRoundZVP(mul(u_proj, viewPos));\n");
} else {
WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj, viewPos));\n");
}
} else {
WRITE(p, " vec4 outPos = mul(u_proj%s, viewPos);\n", matrixPostfix.c_str());
if (IsVRBuild()) {
WRITE(p, " vec4 outPos = mul(u_proj_lens%s, viewPos);\n", matrixPostfix.c_str());
WRITE(p, " vec4 orgPos = mul(u_proj, viewPos);\n");
} else {
WRITE(p, " vec4 outPos = mul(u_proj, viewPos);\n");
}
}
// TODO: Declare variables for dots for shade mapping if needed.
@@ -1161,7 +1174,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
WRITE(p, " %sv_fogdepth = (viewPos.z + u_fogcoef.x) * u_fogcoef.y;\n", compat.vsOutPrefix);
}
if (vertexRangeCulling) {
if (vertexRangeCulling && !IsVRBuild()) {
WRITE(p, " vec3 projPos = outPos.xyz / outPos.w;\n");
WRITE(p, " float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n");
// Vertex range culling doesn't happen when Z clips, note sign of w is important.
@@ -1200,19 +1213,25 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
// We've named the output gl_Position in HLSL as well.
WRITE(p, " %sgl_Position = outPos;\n", compat.vsOutPrefix);
if (needsZWHack) {
// See comment in thin3d_vulkan.cpp.
WRITE(p, " if (%sgl_Position.z == %sgl_Position.w) %sgl_Position.z *= 0.999999;\n",
compat.vsOutPrefix, compat.vsOutPrefix, compat.vsOutPrefix);
}
if (IsVRBuild()) {
// Z correction for the depth buffer
if (useHWTransform) {
WRITE(p, " %sgl_Position.z = orgPos.z / abs(orgPos.w) * abs(outPos.w);\n", compat.vsOutPrefix);
}
// HUD scaling
WRITE(p, " if ((u_scaleX < 0.99) || (u_scaleY < 0.99)) {\n");
WRITE(p, " %sgl_Position.x *= u_scaleX;\n", compat.vsOutPrefix);
WRITE(p, " %sgl_Position.y *= u_scaleY;\n", compat.vsOutPrefix);
WRITE(p, " }\n");
}
if (needsZWHack) {
// See comment in thin3d_vulkan.cpp.
WRITE(p, " if (%sgl_Position.z == %sgl_Position.w) %sgl_Position.z *= 0.999999;\n",
compat.vsOutPrefix, compat.vsOutPrefix, compat.vsOutPrefix);
}
if (compat.shaderLanguage == HLSL_D3D11 || compat.shaderLanguage == HLSL_D3D9) {
WRITE(p, " return Out;\n");
}
+1 -1
View File
@@ -152,7 +152,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
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) {
if (pipelineState_.FramebufferRead()) {
bool fboTexNeedsBind = false;
ApplyFramebufferRead(&fboTexNeedsBind);
// The shader takes over the responsibility for blending, so recompute.
+1 -1
View File
@@ -132,7 +132,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
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) {
if (pipelineState_.FramebufferRead()) {
bool fboTexNeedsBind = false;
ApplyFramebufferRead(&fboTexNeedsBind);
// The shader takes over the responsibility for blending, so recompute.
+11 -18
View File
@@ -102,6 +102,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
queries.push_back({ &u_fbotex, "fbotex" });
queries.push_back({ &u_proj, "u_proj" });
queries.push_back({ &u_proj_lens, "u_proj_lens" });
queries.push_back({ &u_proj_through, "u_proj_through" });
queries.push_back({ &u_texenv, "u_texenv" });
queries.push_back({ &u_fogcolor, "u_fogcolor" });
@@ -407,20 +408,16 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu
ScaleProjMatrix(leftEyeMatrix, useBufferedRendering);
ScaleProjMatrix(rightEyeMatrix, useBufferedRendering);
if (IsMultiviewSupported()) {
render_->SetUniformM4x4Stereo("u_proj", &u_proj, leftEyeMatrix.m, rightEyeMatrix.m);
} else {
render_->SetUniformM4x4(&u_proj, GetVRFBOIndex() == 0 ? leftEyeMatrix.m : rightEyeMatrix.m);
}
} else {
Matrix4x4 flippedMatrix;
memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float));
FlipProjMatrix(flippedMatrix, useBufferedRendering);
ScaleProjMatrix(flippedMatrix, useBufferedRendering);
render_->SetUniformM4x4(&u_proj, flippedMatrix.m);
render_->SetUniformM4x4Stereo("u_proj_lens", &u_proj_lens, leftEyeMatrix.m, rightEyeMatrix.m);
}
Matrix4x4 flippedMatrix;
memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float));
FlipProjMatrix(flippedMatrix, useBufferedRendering);
ScaleProjMatrix(flippedMatrix, useBufferedRendering);
render_->SetUniformM4x4(&u_proj, flippedMatrix.m);
render_->SetUniformF1(&u_rotation, useBufferedRendering ? 0 : (float)g_display_rotation);
}
if (dirty & DIRTY_PROJTHROUGHMATRIX)
@@ -534,11 +531,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu
if (!flatScreen && !is2D) {
UpdateVRView(gstate.projMatrix, leftEyeView, rightEyeView);
}
if (IsMultiviewSupported()) {
render_->SetUniformM4x4Stereo("u_view", &u_view, leftEyeView, rightEyeView);
} else {
render_->SetUniformM4x4(&u_view, GetVRFBOIndex() == 0 ? leftEyeView : rightEyeView);
}
render_->SetUniformM4x4Stereo("u_view", &u_view, leftEyeView, rightEyeView);
} else {
SetMatrix4x3(render_, &u_view, gstate.viewMatrix);
}
+1
View File
@@ -52,6 +52,7 @@ public:
int u_stencilReplaceValue;
int u_tex;
int u_proj;
int u_proj_lens;
int u_proj_through;
int u_texenv;
int u_view;
+1 -1
View File
@@ -148,7 +148,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
GenericBlendState &blendState = pipelineState_.blendState;
GenericLogicState &logicState = pipelineState_.logicState;
if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) {
if (pipelineState_.FramebufferRead()) {
bool fboTexNeedsBind = false;
ApplyFramebufferRead(&fboTexNeedsBind);
// The shader takes over the responsibility for blending, so recompute.
+2 -1
View File
@@ -19,6 +19,7 @@
#pragma once
#include <cstring>
#include <cstdint>
class GPUInterface;
class GPUDebugInterface;
@@ -27,7 +28,7 @@ class GraphicsContext;
// PSP rasterization has two outputs, color and depth. Stencil is packed
// into the alpha channel of color (if exists), so possibly RASTER_COLOR
// should be named RASTER_COLOR_STENCIL but it gets kinda hard to read.
enum RasterChannel {
enum RasterChannel : uint8_t {
RASTER_COLOR = 0,
RASTER_DEPTH = 1,
};
+6 -1
View File
@@ -1676,7 +1676,7 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) {
// See the documentation for gstate_c.blueToAlpha.
bool blueToAlpha = false;
if (PSP_CoreParameter().compat.flags().BlueToAlpha) {
if (gstate_c.framebufFormat == GEBufferFormat::GE_FORMAT_565 && gstate.getColorMask() == 0x0FFFFF) {
if (gstate_c.framebufFormat == GEBufferFormat::GE_FORMAT_565 && gstate.getColorMask() == 0x0FFFFF && !gstate.isLogicOpEnabled()) {
blueToAlpha = true;
gstate_c.framebufFormat = GEBufferFormat::GE_FORMAT_4444;
}
@@ -2143,6 +2143,7 @@ void GPUCommon::Execute_WorldMtxData(u32 op, u32 diff) {
}
num++;
gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0xF);
gstate.worldmtxdata = GE_CMD_WORLDMATRIXDATA << 24;
}
void GPUCommon::Execute_ViewMtxNum(u32 op, u32 diff) {
@@ -2190,6 +2191,7 @@ void GPUCommon::Execute_ViewMtxData(u32 op, u32 diff) {
}
num++;
gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0xF);
gstate.viewmtxdata = GE_CMD_VIEWMATRIXDATA << 24;
}
void GPUCommon::Execute_ProjMtxNum(u32 op, u32 diff) {
@@ -2238,6 +2240,7 @@ void GPUCommon::Execute_ProjMtxData(u32 op, u32 diff) {
num++;
if (num <= 16)
gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0xF);
gstate.projmtxdata = GE_CMD_PROJMATRIXDATA << 24;
}
void GPUCommon::Execute_TgenMtxNum(u32 op, u32 diff) {
@@ -2285,6 +2288,7 @@ void GPUCommon::Execute_TgenMtxData(u32 op, u32 diff) {
}
num++;
gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0xF);
gstate.texmtxdata = GE_CMD_TGENMATRIXDATA << 24;
}
void GPUCommon::Execute_BoneMtxNum(u32 op, u32 diff) {
@@ -2356,6 +2360,7 @@ void GPUCommon::Execute_BoneMtxData(u32 op, u32 diff) {
}
num++;
gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F);
gstate.boneMatrixData = GE_CMD_BONEMATRIXDATA << 24;
}
void GPUCommon::Execute_MorphWeight(u32 op, u32 diff) {
+91 -15
View File
@@ -94,6 +94,17 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
u32 cmd = op >> 24;
u32 data = op & 0xFFFFFF;
static constexpr char *primTypes[8] = {
"POINTS",
"LINES",
"LINE_STRIP",
"TRIANGLES",
"TRIANGLE_STRIP",
"TRIANGLE_FAN",
"RECTANGLES",
"CONTINUE_PREVIOUS",
};
// Handle control and drawing commands here directly. The others we delegate.
switch (cmd)
{
@@ -132,20 +143,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
{
u32 count = data & 0xFFFF;
u32 type = (data >> 16) & 7;
static const char* types[8] = {
"POINTS",
"LINES",
"LINE_STRIP",
"TRIANGLES",
"TRIANGLE_STRIP",
"TRIANGLE_FAN",
"RECTANGLES",
"CONTINUE_PREVIOUS",
};
if (gstate.vertType & GE_VTYPE_IDX_MASK)
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x, iaddr= %08x", type < 7 ? types[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr);
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x, iaddr= %08x", type < 7 ? primTypes[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr);
else
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x", type < 7 ? types[type] : "INVALID", count, gstate_c.vertexAddr);
snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x", type < 7 ? primTypes[type] : "INVALID", count, gstate_c.vertexAddr);
}
break;
@@ -347,11 +348,17 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
break;
case GE_CMD_OFFSETX:
snprintf(buffer, bufsize, "Offset X: %i", data);
if (data & ~0xFFFF)
snprintf(buffer, bufsize, "Offset X: %04x / %d with sub %d (extra %02x)", data & 0xFFFF, data >> 4, data & 0xF, data >> 16);
else
snprintf(buffer, bufsize, "Offset X: %04x / %d with sub %d", data & 0xFFFF, data >> 4, data & 0xF);
break;
case GE_CMD_OFFSETY:
snprintf(buffer, bufsize, "Offset Y: %i", data);
if (data & ~0xFFFF)
snprintf(buffer, bufsize, "Offset Y: %04x / %d with sub %d (extra %02x)", data & 0xFFFF, data >> 4, data & 0xF, data >> 16);
else
snprintf(buffer, bufsize, "Offset Y: %04x / %d with sub %d", data & 0xFFFF, data >> 4, data & 0xF);
break;
case GE_CMD_TEXSCALEU:
@@ -736,6 +743,20 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
}
break;
case GE_CMD_LKS0:
case GE_CMD_LKS1:
case GE_CMD_LKS2:
case GE_CMD_LKS3:
snprintf(buffer, bufsize, "Light %d spot exponent: %f", cmd - GE_CMD_LKS0, getFloat24(data));
break;
case GE_CMD_LKO0:
case GE_CMD_LKO1:
case GE_CMD_LKO2:
case GE_CMD_LKO3:
snprintf(buffer, bufsize, "Light %d spot cutoff: %f", cmd - GE_CMD_LKO0, getFloat24(data));
break;
case GE_CMD_LAC0:case GE_CMD_LAC1:case GE_CMD_LAC2:case GE_CMD_LAC3:
case GE_CMD_LDC0:case GE_CMD_LDC1:case GE_CMD_LDC2:case GE_CMD_LDC3:
case GE_CMD_LSC0:case GE_CMD_LSC1:case GE_CMD_LSC2:case GE_CMD_LSC3:
@@ -744,9 +765,15 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
float g = (float)((data>>8) & 0xff)/255.0f;
float b = (float)(data>>16)/255.0f;
static constexpr char *lightColorTypes[] = {
"ambient",
"diffuse",
"specular",
};
int l = (cmd - GE_CMD_LAC0) / 3;
int t = (cmd - GE_CMD_LAC0) % 3;
snprintf(buffer, bufsize, "Light %i color %i: %f %f %f", l, t, r, g, b);
snprintf(buffer, bufsize, "Light %d %s color: %f %f %f", l, lightColorTypes[t], r, g, b);
}
break;
@@ -1289,6 +1316,55 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
snprintf(buffer, bufsize, "BONE data # %f", getFloat24(data));
break;
case GE_CMD_VSCX:
if (data & ~0xFFFF)
snprintf(buffer, bufsize, "Vertex screen X: %04x / %d with sub %d (extra %02x)", data & 0xFFFF, data >> 4, data & 0xF, data >> 16);
else
snprintf(buffer, bufsize, "Vertex screen X: %04x / %d with sub %d", data & 0xFFFF, data >> 4, data & 0xF);
break;
case GE_CMD_VSCY:
if (data & ~0xFFFF)
snprintf(buffer, bufsize, "Vertex screen Y: %04x / %d with sub %d (extra %02x)", data & 0xFFFF, data >> 4, data & 0xF, data >> 16);
else
snprintf(buffer, bufsize, "Vertex screen Y: %04x / %d with sub %d", data & 0xFFFF, data >> 4, data & 0xF);
break;
case GE_CMD_VSCZ:
if (data & ~0xFFFF)
snprintf(buffer, bufsize, "Vertex Z: %04x (extra %02x)", data & 0xFFFF, data >> 16);
else
snprintf(buffer, bufsize, "Vertex Z: %04x", data & 0xFFFF);
break;
case GE_CMD_VTCS:
snprintf(buffer, bufsize, "Vertex tex S: %f", getFloat24(data));
break;
case GE_CMD_VTCT:
snprintf(buffer, bufsize, "Vertex tex T: %f", getFloat24(data));
break;
case GE_CMD_VTCQ:
snprintf(buffer, bufsize, "Vertex tex Q: %f", getFloat24(data));
break;
case GE_CMD_VCV:
snprintf(buffer, bufsize, "Vertex color: %06x", data);
break;
case GE_CMD_VAP:
snprintf(buffer, bufsize, "Vertex draw: alpha=%02x, prim=%s, other=%06x", data & 0xFF, primTypes[(data >> 8) & 7], data & ~0x0007FF);
break;
case GE_CMD_VFC:
snprintf(buffer, bufsize, "Vertex fog: %06x", data);
break;
case GE_CMD_VSCV:
snprintf(buffer, bufsize, "Vertex secondary color: %06x", data);
break;
default:
snprintf(buffer, bufsize, "Unknown: %08x", op);
break;
+1 -1
View File
@@ -286,7 +286,7 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b
// We already have a fast path for clear in ClearRectangle.
bool state_check = !state.pixelID.clearMode && !state.samplerID.hasAnyMips && NoClampOrWrap(state, v0.texturecoords) && NoClampOrWrap(state, v1.texturecoords);
// This doesn't work well with offset drawing, see #15876. Through never has a subpixel offset.
bool subpixel_check = ((v0.screenpos.x | v0.screenpos.y | v1.screenpos.x | v1.screenpos.y) & 0xF) != 0;
bool subpixel_check = ((v0.screenpos.x | v0.screenpos.y | v1.screenpos.x | v1.screenpos.y) & 0xF) == 0;
if ((coord_check || !state.enableTextures) && orient_check && state_check && subpixel_check) {
binner.AddSprite(v0, v1);
return true;
+34 -12
View File
@@ -1033,57 +1033,79 @@ void SoftGPU::Execute_VertexType(u32 op, u32 diff) {
void SoftGPU::Execute_WorldMtxData(u32 op, u32 diff) {
int num = gstate.worldmtxnum & 0xF;
u32 *target = num < 12 ? (u32 *)&gstate.worldMatrix[num] : (u32 *)&gstate.viewMatrix[num - 12];
u32 newVal = op << 8;
if (num < 12 && newVal != ((const u32 *)gstate.worldMatrix)[num]) {
((u32 *)gstate.worldMatrix)[num] = newVal;
if (newVal != *target) {
*target = newVal;
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
}
num++;
gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0xF);
gstate.worldmtxdata = GE_CMD_WORLDMATRIXDATA << 24;
}
void SoftGPU::Execute_ViewMtxData(u32 op, u32 diff) {
int num = gstate.viewmtxnum & 0xF;
u32 *target = num < 12 ? (u32 *)&gstate.viewMatrix[num] : (u32 *)&gstate.projMatrix[num - 12];
u32 newVal = op << 8;
if (num < 12 && newVal != ((const u32 *)gstate.viewMatrix)[num]) {
((u32 *)gstate.viewMatrix)[num] = newVal;
if (newVal != *target) {
*target = newVal;
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
}
num++;
gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0xF);
gstate.viewmtxdata = GE_CMD_VIEWMATRIXDATA << 24;
}
void SoftGPU::Execute_ProjMtxData(u32 op, u32 diff) {
// NOTE: Changed from 0xF to catch overflows.
int num = gstate.projmtxnum & 0x1F;
int num = gstate.projmtxnum & 0xF;
u32 *target = (u32 *)&gstate.projMatrix[num];
u32 newVal = op << 8;
if (num < 0x10 && newVal != ((const u32 *)gstate.projMatrix)[num]) {
((u32 *)gstate.projMatrix)[num] = newVal;
if (newVal != *target) {
*target = newVal;
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
}
num++;
if (num <= 16)
gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0xF);
gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0xF);
gstate.projmtxdata = GE_CMD_PROJMATRIXDATA << 24;
}
void SoftGPU::Execute_TgenMtxData(u32 op, u32 diff) {
int num = gstate.texmtxnum & 0xF;
u32 newVal = op << 8;
// Doesn't wrap to any other matrix.
if (num < 12 && newVal != ((const u32 *)gstate.tgenMatrix)[num]) {
((u32 *)gstate.tgenMatrix)[num] = newVal;
// No dirtying, read during vertex read.
}
num++;
gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0xF);
gstate.texmtxdata = GE_CMD_TGENMATRIXDATA << 24;
}
void SoftGPU::Execute_BoneMtxData(u32 op, u32 diff) {
int num = gstate.boneMatrixNumber & 0x7F;
u32 *target;
if (num < 96) {
target = (u32 *)&gstate.boneMatrix[num];
} else if (num < 96 + 12) {
target = (u32 *)&gstate.worldMatrix[num - 96];
} else if (num < 96 + 12 + 12) {
target = (u32 *)&gstate.viewMatrix[num - 96 - 12];
} else {
target = (u32 *)&gstate.projMatrix[num - 96 - 12 - 12];
}
u32 newVal = op << 8;
if (num < 96 && newVal != ((const u32 *)gstate.boneMatrix)[num]) {
((u32 *)gstate.boneMatrix)[num] = newVal;
if (newVal != *target) {
*target = newVal;
// Dirty if it overflowed. We read bone data during vertex read.
if (num >= 96)
dirtyFlags_ |= SoftDirty::TRANSFORM_MATRIX;
}
num++;
gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F);
gstate.boneMatrixData = GE_CMD_BONEMATRIXDATA << 24;
}
void SoftGPU::Execute_Call(u32 op, u32 diff) {
+1 -1
View File
@@ -370,7 +370,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM
// instantaneous.
#define CACHE_HEADER_MAGIC 0xff51f420
#define CACHE_VERSION 21
#define CACHE_VERSION 22
struct VulkanCacheHeader {
uint32_t magic;
uint32_t version;
+1 -1
View File
@@ -152,7 +152,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
GenericBlendState &blendState = pipelineState_.blendState;
GenericLogicState &logicState = pipelineState_.logicState;
if (blendState.applyFramebufferRead || maskState.applyFramebufferRead) {
if (pipelineState_.FramebufferRead()) {
ApplyFramebufferRead(&fboTexNeedsBind_);
// The shader takes over the responsibility for blending, so recompute.
// We might still end up using blend to write something to alpha.
+6 -4
View File
@@ -1115,13 +1115,15 @@ void GameSettingsScreen::CreateViews() {
LinearLayout *vrSettings = AddTab("GameSettingsVR", ms->T("VR"));
vrSettings->Add(new ItemHeader(vr->T("Virtual reality")));
vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Enable virtual reality")));
vrSettings->Add(new PopupSliderChoice(&g_Config.iCanvasDistance, 1, 10, vr->T("Distance to 2D menus and scenes", "Distance to 2D menus and scenes"), 1, screenManager(), ""));
CheckBox *vr6DoF = vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("Enable 6 degrees of freedom movement")));
vr6DoF->SetEnabledPtr(&g_Config.bEnableVR);
CheckBox *vrStereo = vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Enable stereoscopic vision")));
vrStereo->SetEnabledPtr(&g_Config.bEnableVR);
PopupSliderChoice *vrFieldOfView = vrSettings->Add(new PopupSliderChoice(&g_Config.iFieldOfViewPercentage, 100, 150, vr->T("Field of view scale", "Headset's field of view scale"), 10, screenManager(), vr->T("% of native FoV")));
PopupSliderChoice *vrFieldOfView = vrSettings->Add(new PopupSliderChoice(&g_Config.iFieldOfViewPercentage, 100, 200, vr->T("Field of view scale", "Headset's field of view scale"), 10, screenManager(), vr->T("% of native FoV")));
vrFieldOfView->SetEnabledPtr(&g_Config.bEnableVR);
vrSettings->Add(new PopupSliderChoice(&g_Config.iCanvasDistance, 1, 10, vr->T("Distance to 2D menus and scenes", "Distance to 2D menus and scenes"), 1, screenManager(), ""));
CheckBox *vrStereo = vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Enable stereoscopic vision (Experimental)")));
vrStereo->SetEnabledPtr(&g_Config.bEnableVR);
PopupSliderChoice *vrStereoSepararation = vrSettings->Add(new PopupSliderChoice(&g_Config.iStereoSeparation, 1, 50, vr->T("Stereo separation (differs per game)", "Stereo separation (differs per game)"), 1, screenManager(), "x"));
vrStereoSepararation->SetEnabledPtr(&g_Config.bEnableStereo);
}
}
+1
View File
@@ -968,6 +968,7 @@ void CreditsScreen::render() {
"ANR2ME",
"adenovan",
"iota97",
"Lubos",
"",
cr->T("specialthanks", "Special thanks to:"),
specialthanksMaxim.c_str(),
-2
View File
@@ -62,7 +62,6 @@
#include "Common/System/NativeApp.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Input/InputState.h"
#include "Common/Math/fast/fast_math.h"
#include "Common/Math/math_util.h"
#include "Common/Math/lin/matrix4x4.h"
#include "Common/Profiler/Profiler.h"
@@ -460,7 +459,6 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
ShaderTranslationInit();
InitFastMath();
g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
g_Discord.SetPresenceMenu();
-3
View File
@@ -432,7 +432,6 @@
<ClInclude Include="..\..\Common\Input\KeyCodes.h" />
<ClInclude Include="..\..\Common\Math\curves.h" />
<ClInclude Include="..\..\Common\Math\expression_parser.h" />
<ClInclude Include="..\..\Common\Math\fast\fast_math.h" />
<ClInclude Include="..\..\Common\Math\fast\fast_matrix.h" />
<ClInclude Include="..\..\Common\Math\geom2d.h" />
<ClInclude Include="..\..\Common\Math\lin\matrix4x4.h" />
@@ -555,9 +554,7 @@
<ClCompile Include="..\..\Common\Input\InputState.cpp" />
<ClCompile Include="..\..\Common\Math\curves.cpp" />
<ClCompile Include="..\..\Common\Math\expression_parser.cpp" />
<ClCompile Include="..\..\Common\Math\fast\fast_math.c" />
<ClCompile Include="..\..\Common\Math\fast\fast_matrix.c" />
<ClCompile Include="..\..\Common\Math\fast\fast_matrix_sse.c" />
<ClCompile Include="..\..\Common\Math\lin\matrix4x4.cpp" />
<ClCompile Include="..\..\Common\Math\lin\vec3.cpp" />
<ClCompile Include="..\..\Common\Math\math_util.cpp" />
-9
View File
@@ -225,15 +225,9 @@
<ClCompile Include="..\..\Common\Math\math_util.cpp">
<Filter>Math</Filter>
</ClCompile>
<ClCompile Include="..\..\Common\Math\fast\fast_math.c">
<Filter>Math\fast</Filter>
</ClCompile>
<ClCompile Include="..\..\Common\Math\fast\fast_matrix.c">
<Filter>Math\fast</Filter>
</ClCompile>
<ClCompile Include="..\..\Common\Math\fast\fast_matrix_sse.c">
<Filter>Math\fast</Filter>
</ClCompile>
<ClCompile Include="..\..\Common\Math\lin\matrix4x4.cpp">
<Filter>Math\lin</Filter>
</ClCompile>
@@ -529,9 +523,6 @@
<ClInclude Include="..\..\Common\Math\math_util.h">
<Filter>Math</Filter>
</ClInclude>
<ClInclude Include="..\..\Common\Math\fast\fast_math.h">
<Filter>Math\fast</Filter>
</ClInclude>
<ClInclude Include="..\..\Common\Math\fast\fast_matrix.h">
<Filter>Math\fast</Filter>
</ClInclude>
+132 -121
View File
@@ -1,12 +1,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#TARGET_PLATFORM := android-8
NATIVE := ../../ext/native
SRC := ../..
include $(CLEAR_VARS)
include $(LOCAL_PATH)/Locals.mk
# http://software.intel.com/en-us/articles/getting-started-on-optimizing-ndk-project-for-multiple-cpu-architectures
@@ -16,89 +11,25 @@ ARCH_FILES := \
$(SRC)/Common/ABI.cpp \
$(SRC)/Common/x64Emitter.cpp \
$(SRC)/Common/x64Analyzer.cpp \
$(SRC)/Common/Math/fast/fast_matrix_sse.c \
$(SRC)/Common/Thunk.cpp \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
$(SRC)/Core/MIPS/x86/CompBranch.cpp \
$(SRC)/Core/MIPS/x86/CompFPU.cpp \
$(SRC)/Core/MIPS/x86/CompLoadStore.cpp \
$(SRC)/Core/MIPS/x86/CompVFPU.cpp \
$(SRC)/Core/MIPS/x86/CompReplace.cpp \
$(SRC)/Core/MIPS/x86/Asm.cpp \
$(SRC)/Core/MIPS/x86/Jit.cpp \
$(SRC)/Core/MIPS/x86/JitSafeMem.cpp \
$(SRC)/Core/MIPS/x86/RegCache.cpp \
$(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \
$(SRC)/GPU/Common/VertexDecoderX86.cpp \
$(SRC)/GPU/Software/DrawPixelX86.cpp \
$(SRC)/GPU/Software/SamplerX86.cpp
endif
ifeq ($(TARGET_ARCH_ABI),x86_64)
$(SRC)/Common/Thunk.cpp
else ifeq ($(TARGET_ARCH_ABI),x86_64)
ARCH_FILES := \
$(SRC)/Common/ABI.cpp \
$(SRC)/Common/x64Emitter.cpp \
$(SRC)/Common/x64Analyzer.cpp \
$(SRC)/Common/Math/fast/fast_matrix_sse.c \
$(SRC)/Common/Thunk.cpp \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
$(SRC)/Core/MIPS/x86/CompBranch.cpp \
$(SRC)/Core/MIPS/x86/CompFPU.cpp \
$(SRC)/Core/MIPS/x86/CompLoadStore.cpp \
$(SRC)/Core/MIPS/x86/CompVFPU.cpp \
$(SRC)/Core/MIPS/x86/CompReplace.cpp \
$(SRC)/Core/MIPS/x86/Asm.cpp \
$(SRC)/Core/MIPS/x86/Jit.cpp \
$(SRC)/Core/MIPS/x86/JitSafeMem.cpp \
$(SRC)/Core/MIPS/x86/RegCache.cpp \
$(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \
$(SRC)/GPU/Common/VertexDecoderX86.cpp \
$(SRC)/GPU/Software/DrawPixelX86.cpp \
$(SRC)/GPU/Software/SamplerX86.cpp
endif
ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a)
$(SRC)/Common/Thunk.cpp
else ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a)
ARCH_FILES := \
$(SRC)/Common/ArmEmitter.cpp \
$(SRC)/Common/Math/fast/fast_matrix_neon.S.neon \
$(SRC)/Core/MIPS/ARM/ArmCompALU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompBranch.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPUNEON.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPUNEONUtil.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompReplace.cpp \
$(SRC)/Core/MIPS/ARM/ArmAsm.cpp \
$(SRC)/Core/MIPS/ARM/ArmJit.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCache.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCacheFPU.cpp \
$(SRC)/GPU/Common/VertexDecoderArm.cpp \
$(SRC)/ext/disarm.cpp \
$(SRC)/ext/libpng17/arm/arm_init.c \
$(SRC)/ext/libpng17/arm/filter_neon_intrinsics.c \
$(SRC)/ext/libpng17/arm/filter_neon.S.neon \
ArmEmitterTest.cpp
endif
ifeq ($(findstring arm64-v8a,$(TARGET_ARCH_ABI)),arm64-v8a)
$(SRC)/ext/libpng17/arm/filter_neon.S.neon
else ifeq ($(findstring arm64-v8a,$(TARGET_ARCH_ABI)),arm64-v8a)
ARCH_FILES := \
$(SRC)/Common/Arm64Emitter.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompALU.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompBranch.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompFPU.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompVFPU.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompReplace.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64Asm.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64Jit.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64RegCache.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp \
$(SRC)/Core/Util/DisArm64.cpp \
$(SRC)/GPU/Common/VertexDecoderArm64.cpp \
$(SRC)/ext/libpng17/arm/arm_init.c \
$(SRC)/ext/libpng17/arm/filter_neon_intrinsics.c \
Arm64EmitterTest.cpp
$(SRC)/ext/libpng17/arm/filter_neon_intrinsics.c
endif
NATIVE_FILES :=\
@@ -126,17 +57,7 @@ VULKAN_FILES := \
$(SRC)/Common/GPU/Vulkan/VulkanImage.cpp \
$(SRC)/Common/GPU/Vulkan/VulkanMemory.cpp \
$(SRC)/Common/GPU/Vulkan/VulkanProfiler.cpp \
$(SRC)/Common/GPU/Vulkan/VulkanBarrier.cpp \
$(SRC)/GPU/Vulkan/DrawEngineVulkan.cpp \
$(SRC)/GPU/Vulkan/FramebufferManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/GPU_Vulkan.cpp \
$(SRC)/GPU/Vulkan/PipelineManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/ShaderManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/StateMappingVulkan.cpp \
$(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \
$(SRC)/GPU/Vulkan/VulkanUtil.cpp \
$(SRC)/GPU/Vulkan/DebugVisVulkan.cpp
#endif
$(SRC)/Common/GPU/Vulkan/VulkanBarrier.cpp
VMA_FILES := \
$(SRC)/ext/vma/vk_mem_alloc.cpp
@@ -161,7 +82,6 @@ EXT_FILES := \
$(SRC)/ext/libpng17/pngrtran.c \
$(SRC)/ext/libpng17/pngrutil.c \
$(SRC)/ext/libpng17/pngset.c \
$(SRC)/ext/libpng17/pngtest.c \
$(SRC)/ext/libpng17/pngtrans.c \
$(SRC)/ext/libpng17/pngwio.c \
$(SRC)/ext/libpng17/pngwrite.c \
@@ -188,9 +108,7 @@ EXT_FILES := \
$(SRC)/ext/udis86/syn-intel.c \
$(SRC)/ext/udis86/syn.c \
$(SRC)/ext/udis86/udis86.c \
$(SRC)/ext/xbrz/xbrz.cpp \
$(SRC)/ext/xxhash.c \
$(SRC)/ext/xbrz/xbrz.cpp
EXEC_AND_LIB_FILES := \
$(ARCH_FILES) \
@@ -200,30 +118,6 @@ EXEC_AND_LIB_FILES := \
$(SPIRV_CROSS_FILES) \
$(EXT_FILES) \
$(NATIVE_FILES) \
TestRunner.cpp \
$(SRC)/Core/MIPS/MIPS.cpp.arm \
$(SRC)/Core/MIPS/MIPSAnalyst.cpp \
$(SRC)/Core/MIPS/MIPSDis.cpp \
$(SRC)/Core/MIPS/MIPSDisVFPU.cpp \
$(SRC)/Core/MIPS/MIPSAsm.cpp \
$(SRC)/Core/MIPS/MIPSInt.cpp.arm \
$(SRC)/Core/MIPS/MIPSIntVFPU.cpp.arm \
$(SRC)/Core/MIPS/MIPSStackWalk.cpp \
$(SRC)/Core/MIPS/MIPSTables.cpp \
$(SRC)/Core/MIPS/MIPSVFPUUtils.cpp.arm \
$(SRC)/Core/MIPS/MIPSCodeUtils.cpp.arm \
$(SRC)/Core/MIPS/MIPSDebugInterface.cpp \
$(SRC)/Core/MIPS/IR/IRFrontend.cpp \
$(SRC)/Core/MIPS/IR/IRJit.cpp \
$(SRC)/Core/MIPS/IR/IRCompALU.cpp \
$(SRC)/Core/MIPS/IR/IRCompBranch.cpp \
$(SRC)/Core/MIPS/IR/IRCompFPU.cpp \
$(SRC)/Core/MIPS/IR/IRCompLoadStore.cpp \
$(SRC)/Core/MIPS/IR/IRCompVFPU.cpp \
$(SRC)/Core/MIPS/IR/IRInst.cpp \
$(SRC)/Core/MIPS/IR/IRInterpreter.cpp \
$(SRC)/Core/MIPS/IR/IRPassSimplify.cpp \
$(SRC)/Core/MIPS/IR/IRRegCache.cpp \
$(SRC)/Common/Buffer.cpp \
$(SRC)/Common/Crypto/md5.cpp \
$(SRC)/Common/Crypto/sha1.cpp \
@@ -267,7 +161,6 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Common/Render/Text/draw_text_android.cpp \
$(SRC)/Common/Input/GestureDetector.cpp \
$(SRC)/Common/Input/InputState.cpp \
$(SRC)/Common/Math/fast/fast_math.c \
$(SRC)/Common/Math/fast/fast_matrix.c \
$(SRC)/Common/Math/math_util.cpp \
$(SRC)/Common/Math/curves.cpp \
@@ -311,7 +204,121 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Common/MipsCPUDetect.cpp \
$(SRC)/Common/StringUtils.cpp \
$(SRC)/Common/SysError.cpp \
$(SRC)/Common/TimeUtil.cpp \
$(SRC)/Common/TimeUtil.cpp
LOCAL_MODULE := ppsspp_common
LOCAL_SRC_FILES := $(EXEC_AND_LIB_FILES)
include $(BUILD_STATIC_LIBRARY)
# Next up, Core, GPU, and other core parts shared by headless.
include $(CLEAR_VARS)
include $(LOCAL_PATH)/Locals.mk
LOCAL_WHOLE_STATIC_LIBRARIES += ppsspp_common
ifeq ($(TARGET_ARCH_ABI),x86)
ARCH_FILES := \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
$(SRC)/Core/MIPS/x86/CompBranch.cpp \
$(SRC)/Core/MIPS/x86/CompFPU.cpp \
$(SRC)/Core/MIPS/x86/CompLoadStore.cpp \
$(SRC)/Core/MIPS/x86/CompVFPU.cpp \
$(SRC)/Core/MIPS/x86/CompReplace.cpp \
$(SRC)/Core/MIPS/x86/Asm.cpp \
$(SRC)/Core/MIPS/x86/Jit.cpp \
$(SRC)/Core/MIPS/x86/JitSafeMem.cpp \
$(SRC)/Core/MIPS/x86/RegCache.cpp \
$(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \
$(SRC)/GPU/Common/VertexDecoderX86.cpp \
$(SRC)/GPU/Software/DrawPixelX86.cpp \
$(SRC)/GPU/Software/SamplerX86.cpp
else ifeq ($(TARGET_ARCH_ABI),x86_64)
ARCH_FILES := \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
$(SRC)/Core/MIPS/x86/CompBranch.cpp \
$(SRC)/Core/MIPS/x86/CompFPU.cpp \
$(SRC)/Core/MIPS/x86/CompLoadStore.cpp \
$(SRC)/Core/MIPS/x86/CompVFPU.cpp \
$(SRC)/Core/MIPS/x86/CompReplace.cpp \
$(SRC)/Core/MIPS/x86/Asm.cpp \
$(SRC)/Core/MIPS/x86/Jit.cpp \
$(SRC)/Core/MIPS/x86/JitSafeMem.cpp \
$(SRC)/Core/MIPS/x86/RegCache.cpp \
$(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \
$(SRC)/GPU/Common/VertexDecoderX86.cpp \
$(SRC)/GPU/Software/DrawPixelX86.cpp \
$(SRC)/GPU/Software/SamplerX86.cpp
else ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a)
ARCH_FILES := \
$(SRC)/Core/MIPS/ARM/ArmCompALU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompBranch.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPUNEON.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPUNEONUtil.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompReplace.cpp \
$(SRC)/Core/MIPS/ARM/ArmAsm.cpp \
$(SRC)/Core/MIPS/ARM/ArmJit.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCache.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCacheFPU.cpp \
$(SRC)/GPU/Common/VertexDecoderArm.cpp \
ArmEmitterTest.cpp
else ifeq ($(findstring arm64-v8a,$(TARGET_ARCH_ABI)),arm64-v8a)
ARCH_FILES := \
$(SRC)/Core/MIPS/ARM64/Arm64CompALU.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompBranch.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompFPU.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompVFPU.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64CompReplace.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64Asm.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64Jit.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64RegCache.cpp \
$(SRC)/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp \
$(SRC)/Core/Util/DisArm64.cpp \
$(SRC)/GPU/Common/VertexDecoderArm64.cpp \
Arm64EmitterTest.cpp
endif
VULKAN_FILES := \
$(SRC)/GPU/Vulkan/DrawEngineVulkan.cpp \
$(SRC)/GPU/Vulkan/FramebufferManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/GPU_Vulkan.cpp \
$(SRC)/GPU/Vulkan/PipelineManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/ShaderManagerVulkan.cpp \
$(SRC)/GPU/Vulkan/StateMappingVulkan.cpp \
$(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \
$(SRC)/GPU/Vulkan/VulkanUtil.cpp \
$(SRC)/GPU/Vulkan/DebugVisVulkan.cpp
EXEC_AND_LIB_FILES := \
$(ARCH_FILES) \
$(VULKAN_FILES) \
$(SRC)/ext/xxhash.c \
TestRunner.cpp \
$(SRC)/Core/MIPS/MIPS.cpp.arm \
$(SRC)/Core/MIPS/MIPSAnalyst.cpp \
$(SRC)/Core/MIPS/MIPSDis.cpp \
$(SRC)/Core/MIPS/MIPSDisVFPU.cpp \
$(SRC)/Core/MIPS/MIPSAsm.cpp \
$(SRC)/Core/MIPS/MIPSInt.cpp.arm \
$(SRC)/Core/MIPS/MIPSIntVFPU.cpp.arm \
$(SRC)/Core/MIPS/MIPSStackWalk.cpp \
$(SRC)/Core/MIPS/MIPSTables.cpp \
$(SRC)/Core/MIPS/MIPSVFPUUtils.cpp.arm \
$(SRC)/Core/MIPS/MIPSCodeUtils.cpp.arm \
$(SRC)/Core/MIPS/MIPSDebugInterface.cpp \
$(SRC)/Core/MIPS/IR/IRFrontend.cpp \
$(SRC)/Core/MIPS/IR/IRJit.cpp \
$(SRC)/Core/MIPS/IR/IRCompALU.cpp \
$(SRC)/Core/MIPS/IR/IRCompBranch.cpp \
$(SRC)/Core/MIPS/IR/IRCompFPU.cpp \
$(SRC)/Core/MIPS/IR/IRCompLoadStore.cpp \
$(SRC)/Core/MIPS/IR/IRCompVFPU.cpp \
$(SRC)/Core/MIPS/IR/IRInst.cpp \
$(SRC)/Core/MIPS/IR/IRInterpreter.cpp \
$(SRC)/Core/MIPS/IR/IRPassSimplify.cpp \
$(SRC)/Core/MIPS/IR/IRRegCache.cpp \
$(SRC)/GPU/Math3D.cpp \
$(SRC)/GPU/GPU.cpp \
$(SRC)/GPU/GPUCommon.cpp \
@@ -631,13 +638,17 @@ LIBZSTD_FILES := \
$(SRC)/ext/zstd/lib/dictBuilder/fastcover.c \
$(SRC)/ext/zstd/lib/dictBuilder/zdict.c
ifeq ($(TARGET_ARCH_ABI),x86_64)
LIBZSTD_FILES += $(SRC)/ext/zstd/lib/decompress/huf_decompress_amd64.S
endif
LOCAL_MODULE := libzstd
LOCAL_SRC_FILES := $(LIBZSTD_FILES)
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/Locals.mk
LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips libzstd
LOCAL_STATIC_LIBRARIES += ppsspp_common ppsspp_core libarmips libzstd
# These are the files just for ppsspp_jni
LOCAL_MODULE := ppsspp_jni
@@ -689,7 +700,7 @@ endif
ifeq ($(HEADLESS),1)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/Locals.mk
LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips libzstd
LOCAL_STATIC_LIBRARIES += ppsspp_common ppsspp_core libarmips libzstd
# Android 5.0 requires PIE for executables. Only supported on 4.1+, but this is testing anyway.
LOCAL_CFLAGS += -fPIE
@@ -707,7 +718,7 @@ endif
ifeq ($(UNITTEST),1)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/Locals.mk
LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips libzstd
LOCAL_STATIC_LIBRARIES += ppsspp_common ppsspp_core libarmips libzstd
# Android 5.0 requires PIE for executables. Only supported on 4.1+, but this is testing anyway.
LOCAL_CFLAGS += -fPIE
-2
View File
@@ -13,11 +13,9 @@ LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../../ext/glslang \
$(LOCAL_PATH)/../../ext/miniupnp \
$(LOCAL_PATH)/../../ext/miniupnp-build \
$(LOCAL_PATH)/$(NATIVE)/base \
$(LOCAL_PATH)/../../ext/libpng17 \
$(LOCAL_PATH)/../../ext/libzip \
$(LOCAL_PATH)/../../ext/zstd/lib \
$(LOCAL_PATH)/$(NATIVE) \
$(LOCAL_PATH)
LOCAL_STATIC_LIBRARIES := libzip glslang-build miniupnp-build
+27 -6
View File
@@ -1119,15 +1119,13 @@ ULUS10513 = true
ULJM05812 = true
NPJH50371 = true
# Colin McRae's DiRT 2 - issue #13012 (car lighting)
# Previously used ReinterpretFramebuffers + ShaderColorBitmask
# Colin McRae's DiRT 2 - issue #13012 (car reflections)
ULUS10471 = true
ULJM05533 = true
NPJH50006 = true
ULES01301 = true
# Outrun 2006: Coast to Coast - issue #11358 (car reflections)
# Previously used ReinterpretFramebuffers + ShaderColorBitmask
ULES00262 = true
ULUS10064 = true
ULKS46087 = true
@@ -1138,7 +1136,16 @@ NPUZ00043 = true
NPEZ00198 = true
[ShaderColorBitmask]
# No users right now, but keeping it around as a more accurate option than BlueToAlpha, for debugging mainly Outrun.
# Colin McRae's DiRT 2 - issue #13012 (water)
ULUS10471 = true
ULJM05533 = true
NPJH50006 = true
ULES01301 = true
# Outrun 2006: Coast to Coast - issue #11358 (car reflections), #11928 (water)
ULES00262 = true
ULUS10064 = true
ULKS46087 = true
[DisableFirstFrameReadback]
# Wipeout Pure: Temporary workaround for lens flare flicker. See #13344
@@ -1274,8 +1281,22 @@ UCET00278 = true
UCUS98670 = true
UCUS98646 = true
[ForceLowerResolutionForEffectsOn]
# The water effect of DiRT 2 and Outrun doesn't work in higher resolutions.
# Colin McRae's DiRT 2 - issue #13012 (water)
ULUS10471 = true
ULJM05533 = true
NPJH50006 = true
ULES01301 = true
# Outrun 2006: Coast to Coast - issue #11358 (car reflections), #11928 (water)
ULES00262 = true
ULUS10064 = true
ULKS46087 = true
[VRClearFramebuffer]
#Grand Theft Auto: Liberty City Stories
# Grand Theft Auto: Liberty City Stories
ULUS10041 = true
ULES00151 = true
ULJM05255 = true
@@ -1283,7 +1304,7 @@ ULJM05359 = true
ULJM05885 = true
NPJH50825 = true
#Grand Theft Auto: Vice City Stories
# Grand Theft Auto: Vice City Stories
ULES00502 = true
ULUS10160 = true
ULJM05297 = true
+11621 -12600
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -33,9 +33,9 @@
*/
/*
* xxhash.c instantiates functions defined in xxhash.h
*/
/*
* xxhash.c instantiates functions defined in xxhash.h
*/
#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
#define XXH_IMPLEMENTATION /* access definitions */
+1527 -708
View File
File diff suppressed because it is too large. Load diff
Submodule ext/zstd updated: a488ba114e...155d6a58a2.
+5 -1
View File
@@ -95,7 +95,11 @@ void NativeResized() { }
std::string System_GetProperty(SystemProperty prop) { return ""; }
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
int System_GetPropertyInt(SystemProperty prop) { return -1; }
int System_GetPropertyInt(SystemProperty prop) {
if (prop == SYSPROP_SYSTEMVERSION)
return 31;
return -1;
}
float System_GetPropertyFloat(SystemProperty prop) { return -1.0f; }
bool System_GetPropertyBool(SystemProperty prop) {
switch (prop) {
+6 -15
View File
@@ -161,10 +161,6 @@ ifeq ($(PLATFORM_EXT), android)
$(EXTDIR)/libzip/zip_source_file_stdio_named.c \
$(EXTDIR)/libzip/zip_random_unix.c
endif
SOURCES_C +=\
$(COMMONDIR)/Math/fast/fast_math.c \
$(COMMONDIR)/Math/fast/fast_matrix.c
endif
SOURCES_C += $(LIBRETRODIR)/ext/glew/glew.c
@@ -312,7 +308,8 @@ SOURCES_CXX += \
$(COMMONDIR)/TimeUtil.cpp
SOURCES_C +=\
$(COMMONDIR)/GPU/OpenGL/gl3stub.c
$(COMMONDIR)/GPU/OpenGL/gl3stub.c \
$(COMMONDIR)/Math/fast/fast_matrix.c
SOURCES_CXX += \
$(GPUCOMMONDIR)/Draw2D.cpp \
@@ -633,8 +630,7 @@ ifeq ($(WITH_DYNAREC),1)
SOURCES_C += $(EXTDIR)/libpng17/arm/arm_init.c \
$(EXTDIR)/libpng17/arm/filter_neon_intrinsics.c
ASMFILES += $(COMMONDIR)/Math/fast/fast_matrix_neon.S \
$(EXTDIR)/libpng17/arm/filter_neon.S
ASMFILES += $(EXTDIR)/libpng17/arm/filter_neon.S
endif
else ifeq ($(TARGET_ARCH),arm64)
DYNAFLAGS += -D_ARCH_64
@@ -659,8 +655,7 @@ ifeq ($(WITH_DYNAREC),1)
SOURCES_C += $(EXTDIR)/libpng17/arm/arm_init.c \
$(EXTDIR)/libpng17/arm/filter_neon_intrinsics.c
ASMFILES += $(COMMONDIR)/math/fast/fast_matrix_neon.S \
$(EXTDIR)/libpng17/arm/filter_neon.S
ASMFILES += $(EXTDIR)/libpng17/arm/filter_neon.S
endif
else
ifneq (,$(findstring msvc,$(platform)))
@@ -696,7 +691,6 @@ ifeq ($(WITH_DYNAREC),1)
$(COREDIR)/MIPS/x86/RegCache.cpp \
$(COREDIR)/MIPS/x86/RegCacheFPU.cpp \
$(GPUDIR)/Common/VertexDecoderX86.cpp
SOURCES_C += $(COMMONDIR)/Math/fast/fast_matrix_sse.c
endif
endif
#UDIS86
@@ -781,11 +775,8 @@ SOURCES_C += $(ZSTDDIR)/common/debug.c \
$(ZSTDDIR)/common/error_private.c \
$(ZSTDDIR)/common/fse_decompress.c \
$(ZSTDDIR)/common/pool.c \
$(ZSTDDIR)/common/threading.c
#SOURCES_C += $(ZSTDDIR)/common/xxhash.c
SOURCES_C += \
$(ZSTDDIR)/common/threading.c \
$(ZSTDDIR)/common/xxhash.c \
$(ZSTDDIR)/common/zstd_common.c \
$(ZSTDDIR)/compress/fse_compress.c \
$(ZSTDDIR)/compress/hist.c \