diff options
| -rw-r--r-- | CMakeLists.txt | 9 | ||||
| -rw-r--r-- | externals/CMakeLists.txt | 4 | ||||
| m--------- | externals/dynarmic | 0 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 7 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 3 | ||||
| -rw-r--r-- | src/tests/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/tests/core/hle/kernel/hle_ipc.cpp | 193 | ||||
| -rw-r--r-- | src/video_core/regs_lighting.h | 4 | ||||
| -rw-r--r-- | src/video_core/regs_texturing.h | 8 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 69 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/pica_to_gl.h | 13 | ||||
| -rw-r--r-- | src/video_core/shader/shader_jit_x64_compiler.cpp | 26 | ||||
| -rw-r--r-- | src/video_core/swrasterizer/rasterizer.cpp | 20 | ||||
| -rw-r--r-- | src/video_core/swrasterizer/texturing.cpp | 19 |
16 files changed, 335 insertions, 52 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f0af2d41..a61dee6e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -92,10 +92,13 @@ else() | |||
| 92 | # /W3 - Level 3 warnings | 92 | # /W3 - Level 3 warnings |
| 93 | # /MP - Multi-threaded compilation | 93 | # /MP - Multi-threaded compilation |
| 94 | # /Zi - Output debugging information | 94 | # /Zi - Output debugging information |
| 95 | # /Zo - enahnced debug info for optimized builds | 95 | # /Zo - enhanced debug info for optimized builds |
| 96 | set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo" CACHE STRING "" FORCE) | 96 | # /permissive- - enables stricter C++ standards conformance checks |
| 97 | set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo /permissive-" CACHE STRING "" FORCE) | ||
| 97 | # /EHsc - C++-only exception handling semantics | 98 | # /EHsc - C++-only exception handling semantics |
| 98 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc" CACHE STRING "" FORCE) | 99 | # /Zc:throwingNew - let codegen assume `operator new` will never return null |
| 100 | # /Zc:inline - let codegen omit inline functions in object files | ||
| 101 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc /Zc:throwingNew,inline" CACHE STRING "" FORCE) | ||
| 99 | 102 | ||
| 100 | # /MDd - Multi-threaded Debug Runtime DLL | 103 | # /MDd - Multi-threaded Debug Runtime DLL |
| 101 | set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE) | 104 | set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE) |
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 1e04931ee..02e02350c 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -46,7 +46,5 @@ if (ARCHITECTURE_x86_64) | |||
| 46 | # Defined before "dynarmic" above | 46 | # Defined before "dynarmic" above |
| 47 | # add_library(xbyak INTERFACE) | 47 | # add_library(xbyak INTERFACE) |
| 48 | target_include_directories(xbyak INTERFACE ./xbyak/xbyak) | 48 | target_include_directories(xbyak INTERFACE ./xbyak/xbyak) |
| 49 | if (NOT MSVC) | 49 | target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) |
| 50 | target_compile_options(xbyak INTERFACE -fno-operator-names) | ||
| 51 | endif() | ||
| 52 | endif() | 50 | endif() |
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject 7707ff13e981b0aecf87f3156ee0b641469f7bb | Subproject 8f15e3f70cb96e56705e5de6ba97b5d09423a56 | ||
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 6cf1886cf..1cac1d0c9 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -23,6 +23,11 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s | |||
| 23 | boost::range::remove_erase(connected_sessions, server_session); | 23 | boost::range::remove_erase(connected_sessions, server_session); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | HLERequestContext::HLERequestContext(SharedPtr<ServerSession> session) | ||
| 27 | : session(std::move(session)) { | ||
| 28 | cmd_buf[0] = 0; | ||
| 29 | } | ||
| 30 | |||
| 26 | HLERequestContext::~HLERequestContext() = default; | 31 | HLERequestContext::~HLERequestContext() = default; |
| 27 | 32 | ||
| 28 | SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { | 33 | SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index cbb109d8f..35795fc1d 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -84,6 +84,7 @@ protected: | |||
| 84 | */ | 84 | */ |
| 85 | class HLERequestContext { | 85 | class HLERequestContext { |
| 86 | public: | 86 | public: |
| 87 | HLERequestContext(SharedPtr<ServerSession> session); | ||
| 87 | ~HLERequestContext(); | 88 | ~HLERequestContext(); |
| 88 | 89 | ||
| 89 | /// Returns a pointer to the IPC command buffer for this request. | 90 | /// Returns a pointer to the IPC command buffer for this request. |
| @@ -118,14 +119,14 @@ public: | |||
| 118 | */ | 119 | */ |
| 119 | void ClearIncomingObjects(); | 120 | void ClearIncomingObjects(); |
| 120 | 121 | ||
| 121 | private: | 122 | /// Populates this context with data from the requesting process/thread. |
| 122 | friend class Service::ServiceFrameworkBase; | ||
| 123 | |||
| 124 | ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, | 123 | ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, |
| 125 | HandleTable& src_table); | 124 | HandleTable& src_table); |
| 125 | /// Writes data from this context back to the requesting process/thread. | ||
| 126 | ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | 126 | ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, |
| 127 | HandleTable& dst_table) const; | 127 | HandleTable& dst_table) const; |
| 128 | 128 | ||
| 129 | private: | ||
| 129 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | 130 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; |
| 130 | SharedPtr<ServerSession> session; | 131 | SharedPtr<ServerSession> session; |
| 131 | // TODO(yuriks): Check common usage of this and optimize size accordingly | 132 | // TODO(yuriks): Check common usage of this and optimize size accordingly |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 791a65c19..6754cfeea 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -173,8 +173,7 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses | |||
| 173 | 173 | ||
| 174 | // TODO(yuriks): The kernel should be the one handling this as part of translation after | 174 | // TODO(yuriks): The kernel should be the one handling this as part of translation after |
| 175 | // everything else is migrated | 175 | // everything else is migrated |
| 176 | Kernel::HLERequestContext context; | 176 | Kernel::HLERequestContext context(std::move(server_session)); |
| 177 | context.session = std::move(server_session); | ||
| 178 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, | 177 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, |
| 179 | Kernel::g_handle_table); | 178 | Kernel::g_handle_table); |
| 180 | 179 | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 00d7c636a..a14df325a 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | set(SRCS | 1 | set(SRCS |
| 2 | glad.cpp | ||
| 3 | tests.cpp | ||
| 4 | common/param_package.cpp | 2 | common/param_package.cpp |
| 5 | core/file_sys/path_parser.cpp | 3 | core/file_sys/path_parser.cpp |
| 4 | core/hle/kernel/hle_ipc.cpp | ||
| 5 | glad.cpp | ||
| 6 | tests.cpp | ||
| 6 | ) | 7 | ) |
| 7 | 8 | ||
| 8 | set(HEADERS | 9 | set(HEADERS |
diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp new file mode 100644 index 000000000..e07a28c5b --- /dev/null +++ b/src/tests/core/hle/kernel/hle_ipc.cpp | |||
| @@ -0,0 +1,193 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch.hpp> | ||
| 6 | #include "core/hle/ipc.h" | ||
| 7 | #include "core/hle/kernel/client_port.h" | ||
| 8 | #include "core/hle/kernel/client_session.h" | ||
| 9 | #include "core/hle/kernel/event.h" | ||
| 10 | #include "core/hle/kernel/handle_table.h" | ||
| 11 | #include "core/hle/kernel/hle_ipc.h" | ||
| 12 | #include "core/hle/kernel/process.h" | ||
| 13 | #include "core/hle/kernel/server_session.h" | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | static SharedPtr<Object> MakeObject() { | ||
| 18 | return Event::Create(ResetType::OneShot); | ||
| 19 | } | ||
| 20 | |||
| 21 | TEST_CASE("HLERequestContext::PopoulateFromIncomingCommandBuffer", "[core][kernel]") { | ||
| 22 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | ||
| 23 | HLERequestContext context(std::move(session)); | ||
| 24 | |||
| 25 | auto process = Process::Create(CodeSet::Create("", 0)); | ||
| 26 | HandleTable handle_table; | ||
| 27 | |||
| 28 | SECTION("works with empty cmdbuf") { | ||
| 29 | const u32_le input[]{ | ||
| 30 | IPC::MakeHeader(0x1234, 0, 0), | ||
| 31 | }; | ||
| 32 | |||
| 33 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 34 | |||
| 35 | REQUIRE(context.CommandBuffer()[0] == 0x12340000); | ||
| 36 | } | ||
| 37 | |||
| 38 | SECTION("translates regular params") { | ||
| 39 | const u32_le input[]{ | ||
| 40 | IPC::MakeHeader(0, 3, 0), 0x12345678, 0x21122112, 0xAABBCCDD, | ||
| 41 | }; | ||
| 42 | |||
| 43 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 44 | |||
| 45 | auto* output = context.CommandBuffer(); | ||
| 46 | REQUIRE(output[1] == 0x12345678); | ||
| 47 | REQUIRE(output[2] == 0x21122112); | ||
| 48 | REQUIRE(output[3] == 0xAABBCCDD); | ||
| 49 | } | ||
| 50 | |||
| 51 | SECTION("translates move handles") { | ||
| 52 | auto a = MakeObject(); | ||
| 53 | Handle a_handle = handle_table.Create(a).Unwrap(); | ||
| 54 | const u32_le input[]{ | ||
| 55 | IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), a_handle, | ||
| 56 | }; | ||
| 57 | |||
| 58 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 59 | |||
| 60 | auto* output = context.CommandBuffer(); | ||
| 61 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 62 | REQUIRE(handle_table.GetGeneric(a_handle) == nullptr); | ||
| 63 | } | ||
| 64 | |||
| 65 | SECTION("translates copy handles") { | ||
| 66 | auto a = MakeObject(); | ||
| 67 | Handle a_handle = handle_table.Create(a).Unwrap(); | ||
| 68 | const u32_le input[]{ | ||
| 69 | IPC::MakeHeader(0, 0, 2), IPC::CopyHandleDesc(1), a_handle, | ||
| 70 | }; | ||
| 71 | |||
| 72 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 73 | |||
| 74 | auto* output = context.CommandBuffer(); | ||
| 75 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 76 | REQUIRE(handle_table.GetGeneric(a_handle) == a); | ||
| 77 | } | ||
| 78 | |||
| 79 | SECTION("translates multi-handle descriptors") { | ||
| 80 | auto a = MakeObject(); | ||
| 81 | auto b = MakeObject(); | ||
| 82 | auto c = MakeObject(); | ||
| 83 | const u32_le input[]{ | ||
| 84 | IPC::MakeHeader(0, 0, 5), IPC::MoveHandleDesc(2), | ||
| 85 | handle_table.Create(a).Unwrap(), handle_table.Create(b).Unwrap(), | ||
| 86 | IPC::MoveHandleDesc(1), handle_table.Create(c).Unwrap(), | ||
| 87 | }; | ||
| 88 | |||
| 89 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 90 | |||
| 91 | auto* output = context.CommandBuffer(); | ||
| 92 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 93 | REQUIRE(context.GetIncomingHandle(output[3]) == b); | ||
| 94 | REQUIRE(context.GetIncomingHandle(output[5]) == c); | ||
| 95 | } | ||
| 96 | |||
| 97 | SECTION("translates CallingPid descriptors") { | ||
| 98 | const u32_le input[]{ | ||
| 99 | IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898, | ||
| 100 | }; | ||
| 101 | |||
| 102 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 103 | |||
| 104 | REQUIRE(context.CommandBuffer()[2] == process->process_id); | ||
| 105 | } | ||
| 106 | |||
| 107 | SECTION("translates mixed params") { | ||
| 108 | auto a = MakeObject(); | ||
| 109 | const u32_le input[]{ | ||
| 110 | IPC::MakeHeader(0, 2, 4), | ||
| 111 | 0x12345678, | ||
| 112 | 0xABCDEF00, | ||
| 113 | IPC::MoveHandleDesc(1), | ||
| 114 | handle_table.Create(a).Unwrap(), | ||
| 115 | IPC::CallingPidDesc(), | ||
| 116 | 0, | ||
| 117 | }; | ||
| 118 | |||
| 119 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 120 | |||
| 121 | auto* output = context.CommandBuffer(); | ||
| 122 | REQUIRE(output[1] == 0x12345678); | ||
| 123 | REQUIRE(output[2] == 0xABCDEF00); | ||
| 124 | REQUIRE(context.GetIncomingHandle(output[4]) == a); | ||
| 125 | REQUIRE(output[6] == process->process_id); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { | ||
| 130 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | ||
| 131 | HLERequestContext context(std::move(session)); | ||
| 132 | |||
| 133 | auto process = Process::Create(CodeSet::Create("", 0)); | ||
| 134 | HandleTable handle_table; | ||
| 135 | auto* input = context.CommandBuffer(); | ||
| 136 | u32_le output[IPC::COMMAND_BUFFER_LENGTH]; | ||
| 137 | |||
| 138 | SECTION("works with empty cmdbuf") { | ||
| 139 | input[0] = IPC::MakeHeader(0x1234, 0, 0); | ||
| 140 | |||
| 141 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 142 | |||
| 143 | REQUIRE(output[0] == 0x12340000); | ||
| 144 | } | ||
| 145 | |||
| 146 | SECTION("translates regular params") { | ||
| 147 | input[0] = IPC::MakeHeader(0, 3, 0); | ||
| 148 | input[1] = 0x12345678; | ||
| 149 | input[2] = 0x21122112; | ||
| 150 | input[3] = 0xAABBCCDD; | ||
| 151 | |||
| 152 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 153 | |||
| 154 | REQUIRE(output[1] == 0x12345678); | ||
| 155 | REQUIRE(output[2] == 0x21122112); | ||
| 156 | REQUIRE(output[3] == 0xAABBCCDD); | ||
| 157 | } | ||
| 158 | |||
| 159 | SECTION("translates move/copy handles") { | ||
| 160 | auto a = MakeObject(); | ||
| 161 | auto b = MakeObject(); | ||
| 162 | input[0] = IPC::MakeHeader(0, 0, 4); | ||
| 163 | input[1] = IPC::MoveHandleDesc(1); | ||
| 164 | input[2] = context.AddOutgoingHandle(a); | ||
| 165 | input[3] = IPC::CopyHandleDesc(1); | ||
| 166 | input[4] = context.AddOutgoingHandle(b); | ||
| 167 | |||
| 168 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 169 | |||
| 170 | REQUIRE(handle_table.GetGeneric(output[2]) == a); | ||
| 171 | REQUIRE(handle_table.GetGeneric(output[4]) == b); | ||
| 172 | } | ||
| 173 | |||
| 174 | SECTION("translates multi-handle descriptors") { | ||
| 175 | auto a = MakeObject(); | ||
| 176 | auto b = MakeObject(); | ||
| 177 | auto c = MakeObject(); | ||
| 178 | input[0] = IPC::MakeHeader(0, 0, 5); | ||
| 179 | input[1] = IPC::MoveHandleDesc(2); | ||
| 180 | input[2] = context.AddOutgoingHandle(a); | ||
| 181 | input[3] = context.AddOutgoingHandle(b); | ||
| 182 | input[4] = IPC::CopyHandleDesc(1); | ||
| 183 | input[5] = context.AddOutgoingHandle(c); | ||
| 184 | |||
| 185 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 186 | |||
| 187 | REQUIRE(handle_table.GetGeneric(output[2]) == a); | ||
| 188 | REQUIRE(handle_table.GetGeneric(output[3]) == b); | ||
| 189 | REQUIRE(handle_table.GetGeneric(output[5]) == c); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | } // namespace Kernel | ||
diff --git a/src/video_core/regs_lighting.h b/src/video_core/regs_lighting.h index fbfebc0a7..7221d1688 100644 --- a/src/video_core/regs_lighting.h +++ b/src/video_core/regs_lighting.h | |||
| @@ -84,7 +84,7 @@ struct LightingRegs { | |||
| 84 | NV = 2, // Cosine of the angle between the normal and the view vector | 84 | NV = 2, // Cosine of the angle between the normal and the view vector |
| 85 | LN = 3, // Cosine of the angle between the light and the normal vectors | 85 | LN = 3, // Cosine of the angle between the light and the normal vectors |
| 86 | SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors | 86 | SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors |
| 87 | CP = 5, // TODO: document and implement | 87 | CP = 5, // Cosine of the angle between the tangent and projection of half-angle vectors |
| 88 | }; | 88 | }; |
| 89 | 89 | ||
| 90 | enum class LightingBumpMode : u32 { | 90 | enum class LightingBumpMode : u32 { |
| @@ -168,6 +168,8 @@ struct LightingRegs { | |||
| 168 | union { | 168 | union { |
| 169 | BitField<0, 1, u32> directional; | 169 | BitField<0, 1, u32> directional; |
| 170 | BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 | 170 | BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 |
| 171 | BitField<2, 1, u32> geometric_factor_0; | ||
| 172 | BitField<3, 1, u32> geometric_factor_1; | ||
| 171 | } config; | 173 | } config; |
| 172 | 174 | ||
| 173 | BitField<0, 20, u32> dist_atten_bias; | 175 | BitField<0, 20, u32> dist_atten_bias; |
diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h index 3f5355fa9..0b09f2299 100644 --- a/src/video_core/regs_texturing.h +++ b/src/video_core/regs_texturing.h | |||
| @@ -30,10 +30,10 @@ struct TexturingRegs { | |||
| 30 | Repeat = 2, | 30 | Repeat = 2, |
| 31 | MirroredRepeat = 3, | 31 | MirroredRepeat = 3, |
| 32 | // Mode 4-7 produces some weird result and may be just invalid: | 32 | // Mode 4-7 produces some weird result and may be just invalid: |
| 33 | // 4: Positive coord: clamp to edge; negative coord: repeat | 33 | ClampToEdge2 = 4, // Positive coord: clamp to edge; negative coord: repeat |
| 34 | // 5: Positive coord: clamp to border; negative coord: repeat | 34 | ClampToBorder2 = 5, // Positive coord: clamp to border; negative coord: repeat |
| 35 | // 6: Repeat | 35 | Repeat2 = 6, // Same as Repeat |
| 36 | // 7: Repeat | 36 | Repeat3 = 7, // Same as Repeat |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | enum TextureFilter : u32 { | 39 | enum TextureFilter : u32 { |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index db53710aa..540cbb9d0 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -73,6 +73,8 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { | |||
| 73 | state.lighting.light[light_index].num = num; | 73 | state.lighting.light[light_index].num = num; |
| 74 | state.lighting.light[light_index].directional = light.config.directional != 0; | 74 | state.lighting.light[light_index].directional = light.config.directional != 0; |
| 75 | state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; | 75 | state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; |
| 76 | state.lighting.light[light_index].geometric_factor_0 = light.config.geometric_factor_0 != 0; | ||
| 77 | state.lighting.light[light_index].geometric_factor_1 = light.config.geometric_factor_1 != 0; | ||
| 76 | state.lighting.light[light_index].dist_atten_enable = | 78 | state.lighting.light[light_index].dist_atten_enable = |
| 77 | !regs.lighting.IsDistAttenDisabled(num); | 79 | !regs.lighting.IsDistAttenDisabled(num); |
| 78 | state.lighting.light[light_index].spot_atten_enable = | 80 | state.lighting.light[light_index].spot_atten_enable = |
| @@ -518,14 +520,16 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 518 | "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" | 520 | "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| 519 | "vec3 light_vector = vec3(0.0);\n" | 521 | "vec3 light_vector = vec3(0.0);\n" |
| 520 | "vec3 refl_value = vec3(0.0);\n" | 522 | "vec3 refl_value = vec3(0.0);\n" |
| 521 | "vec3 spot_dir = vec3(0.0);\n;"; | 523 | "vec3 spot_dir = vec3(0.0);\n" |
| 524 | "vec3 half_vector = vec3(0.0);\n" | ||
| 525 | "float geo_factor = 1.0;\n"; | ||
| 522 | 526 | ||
| 523 | // Compute fragment normals | 527 | // Compute fragment normals and tangents |
| 528 | const std::string pertubation = | ||
| 529 | "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; | ||
| 524 | if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { | 530 | if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { |
| 525 | // Bump mapping is enabled using a normal map, read perturbation vector from the selected | 531 | // Bump mapping is enabled using a normal map |
| 526 | // texture | 532 | out += "vec3 surface_normal = " + pertubation + ";\n"; |
| 527 | out += "vec3 surface_normal = 2.0 * (" + SampleTexture(config, lighting.bump_selector) + | ||
| 528 | ").rgb - 1.0;\n"; | ||
| 529 | 533 | ||
| 530 | // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher | 534 | // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher |
| 531 | // precision result | 535 | // precision result |
| @@ -534,31 +538,41 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 534 | "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; | 538 | "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; |
| 535 | out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; | 539 | out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; |
| 536 | } | 540 | } |
| 541 | |||
| 542 | // The tangent vector is not perturbed by the normal map and is just a unit vector. | ||
| 543 | out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; | ||
| 537 | } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { | 544 | } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { |
| 538 | // Bump mapping is enabled using a tangent map | 545 | // Bump mapping is enabled using a tangent map |
| 539 | LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)"); | 546 | out += "vec3 surface_tangent = " + pertubation + ";\n"; |
| 540 | UNIMPLEMENTED(); | 547 | // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant |
| 548 | // computation below, which is also confirmed on 3DS. So we don't bother recomputing here | ||
| 549 | // even if 'renorm' is enabled. | ||
| 550 | |||
| 551 | // The normal vector is not perturbed by the tangent map and is just a unit vector. | ||
| 552 | out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; | ||
| 541 | } else { | 553 | } else { |
| 542 | // No bump mapping - surface local normal is just a unit normal | 554 | // No bump mapping - surface local normal and tangent are just unit vectors |
| 543 | out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; | 555 | out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; |
| 556 | out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; | ||
| 544 | } | 557 | } |
| 545 | 558 | ||
| 546 | // Rotate the surface-local normal by the interpolated normal quaternion to convert it to | 559 | // Rotate the surface-local normal by the interpolated normal quaternion to convert it to |
| 547 | // eyespace. | 560 | // eyespace. |
| 548 | out += "vec3 normal = quaternion_rotate(normalize(normquat), surface_normal);\n"; | 561 | out += "vec4 normalized_normquat = normalize(normquat);\n"; |
| 562 | out += "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n"; | ||
| 563 | out += "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n"; | ||
| 549 | 564 | ||
| 550 | // Gets the index into the specified lookup table for specular lighting | 565 | // Gets the index into the specified lookup table for specular lighting |
| 551 | auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, | 566 | auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, |
| 552 | bool abs) { | 567 | bool abs) { |
| 553 | const std::string half_angle = "normalize(normalize(view) + light_vector)"; | ||
| 554 | std::string index; | 568 | std::string index; |
| 555 | switch (input) { | 569 | switch (input) { |
| 556 | case LightingRegs::LightingLutInput::NH: | 570 | case LightingRegs::LightingLutInput::NH: |
| 557 | index = "dot(normal, " + half_angle + ")"; | 571 | index = "dot(normal, normalize(half_vector))"; |
| 558 | break; | 572 | break; |
| 559 | 573 | ||
| 560 | case LightingRegs::LightingLutInput::VH: | 574 | case LightingRegs::LightingLutInput::VH: |
| 561 | index = std::string("dot(normalize(view), " + half_angle + ")"); | 575 | index = std::string("dot(normalize(view), normalize(half_vector))"); |
| 562 | break; | 576 | break; |
| 563 | 577 | ||
| 564 | case LightingRegs::LightingLutInput::NV: | 578 | case LightingRegs::LightingLutInput::NV: |
| @@ -573,6 +587,22 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 573 | index = std::string("dot(light_vector, spot_dir)"); | 587 | index = std::string("dot(light_vector, spot_dir)"); |
| 574 | break; | 588 | break; |
| 575 | 589 | ||
| 590 | case LightingRegs::LightingLutInput::CP: | ||
| 591 | // CP input is only available with configuration 7 | ||
| 592 | if (lighting.config == LightingRegs::LightingConfig::Config7) { | ||
| 593 | // Note: even if the normal vector is modified by normal map, which is not the | ||
| 594 | // normal of the tangent plane anymore, the half angle vector is still projected | ||
| 595 | // using the modified normal vector. | ||
| 596 | std::string half_angle_proj = "normalize(half_vector) - normal / dot(normal, " | ||
| 597 | "normal) * dot(normal, normalize(half_vector))"; | ||
| 598 | // Note: the half angle vector projection is confirmed not normalized before the dot | ||
| 599 | // product. The result is in fact not cos(phi) as the name suggested. | ||
| 600 | index = "dot(" + half_angle_proj + ", tangent)"; | ||
| 601 | } else { | ||
| 602 | index = "0.0"; | ||
| 603 | } | ||
| 604 | break; | ||
| 605 | |||
| 576 | default: | 606 | default: |
| 577 | LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); | 607 | LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); |
| 578 | UNIMPLEMENTED(); | 608 | UNIMPLEMENTED(); |
| @@ -610,6 +640,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 610 | out += "light_vector = normalize(" + light_src + ".position + view);\n"; | 640 | out += "light_vector = normalize(" + light_src + ".position + view);\n"; |
| 611 | 641 | ||
| 612 | out += "spot_dir = " + light_src + ".spot_direction;\n"; | 642 | out += "spot_dir = " + light_src + ".spot_direction;\n"; |
| 643 | out += "half_vector = normalize(view) + light_vector;\n"; | ||
| 613 | 644 | ||
| 614 | // Compute dot product of light_vector and normal, adjust if lighting is one-sided or | 645 | // Compute dot product of light_vector and normal, adjust if lighting is one-sided or |
| 615 | // two-sided | 646 | // two-sided |
| @@ -643,6 +674,12 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 643 | std::string clamp_highlights = | 674 | std::string clamp_highlights = |
| 644 | lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; | 675 | lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; |
| 645 | 676 | ||
| 677 | if (light_config.geometric_factor_0 || light_config.geometric_factor_1) { | ||
| 678 | out += "geo_factor = dot(half_vector, half_vector);\n" | ||
| 679 | "geo_factor = geo_factor == 0.0 ? 0.0 : min(" + | ||
| 680 | dot_product + " / geo_factor, 1.0);\n"; | ||
| 681 | } | ||
| 682 | |||
| 646 | // Specular 0 component | 683 | // Specular 0 component |
| 647 | std::string d0_lut_value = "1.0"; | 684 | std::string d0_lut_value = "1.0"; |
| 648 | if (lighting.lut_d0.enable && | 685 | if (lighting.lut_d0.enable && |
| @@ -655,6 +692,9 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 655 | GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")"; | 692 | GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")"; |
| 656 | } | 693 | } |
| 657 | std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; | 694 | std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; |
| 695 | if (light_config.geometric_factor_0) { | ||
| 696 | specular_0 = "(" + specular_0 + " * geo_factor)"; | ||
| 697 | } | ||
| 658 | 698 | ||
| 659 | // If enabled, lookup ReflectRed value, otherwise, 1.0 is used | 699 | // If enabled, lookup ReflectRed value, otherwise, 1.0 is used |
| 660 | if (lighting.lut_rr.enable && | 700 | if (lighting.lut_rr.enable && |
| @@ -710,6 +750,9 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 710 | } | 750 | } |
| 711 | std::string specular_1 = | 751 | std::string specular_1 = |
| 712 | "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; | 752 | "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; |
| 753 | if (light_config.geometric_factor_1) { | ||
| 754 | specular_1 = "(" + specular_1 + " * geo_factor)"; | ||
| 755 | } | ||
| 713 | 756 | ||
| 714 | // Fresnel | 757 | // Fresnel |
| 715 | if (lighting.lut_fr.enable && | 758 | if (lighting.lut_fr.enable && |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 9c90eadf9..2302ae453 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -94,6 +94,8 @@ union PicaShaderConfig { | |||
| 94 | bool two_sided_diffuse; | 94 | bool two_sided_diffuse; |
| 95 | bool dist_atten_enable; | 95 | bool dist_atten_enable; |
| 96 | bool spot_atten_enable; | 96 | bool spot_atten_enable; |
| 97 | bool geometric_factor_0; | ||
| 98 | bool geometric_factor_1; | ||
| 97 | } light[8]; | 99 | } light[8]; |
| 98 | 100 | ||
| 99 | bool enable; | 101 | bool enable; |
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 93d7b0b71..70298e211 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h | |||
| @@ -55,6 +55,12 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { | |||
| 55 | GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder | 55 | GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder |
| 56 | GL_REPEAT, // WrapMode::Repeat | 56 | GL_REPEAT, // WrapMode::Repeat |
| 57 | GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat | 57 | GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat |
| 58 | // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the | ||
| 59 | // comments in enum WrapMode. | ||
| 60 | GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge2 | ||
| 61 | GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2 | ||
| 62 | GL_REPEAT, // WrapMode::Repeat2 | ||
| 63 | GL_REPEAT, // WrapMode::Repeat3 | ||
| 58 | }; | 64 | }; |
| 59 | 65 | ||
| 60 | // Range check table for input | 66 | // Range check table for input |
| @@ -65,6 +71,13 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { | |||
| 65 | return GL_CLAMP_TO_EDGE; | 71 | return GL_CLAMP_TO_EDGE; |
| 66 | } | 72 | } |
| 67 | 73 | ||
| 74 | if (static_cast<u32>(mode) > 3) { | ||
| 75 | // It is still unclear whether mode 4-7 are valid, so log it if a game uses them. | ||
| 76 | // TODO(wwylele): telemetry should be added here so we can collect more info about which | ||
| 77 | // game uses this. | ||
| 78 | LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode)); | ||
| 79 | } | ||
| 80 | |||
| 68 | GLenum gl_mode = wrap_mode_table[mode]; | 81 | GLenum gl_mode = wrap_mode_table[mode]; |
| 69 | 82 | ||
| 70 | // Check for dummy values indicating an unknown mode | 83 | // Check for dummy values indicating an unknown mode |
diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp index 5d9b6448c..42a57aab1 100644 --- a/src/video_core/shader/shader_jit_x64_compiler.cpp +++ b/src/video_core/shader/shader_jit_x64_compiler.cpp | |||
| @@ -321,27 +321,27 @@ void JitShader::Compile_EvaluateCondition(Instruction instr) { | |||
| 321 | case Instruction::FlowControlType::Or: | 321 | case Instruction::FlowControlType::Or: |
| 322 | mov(eax, COND0); | 322 | mov(eax, COND0); |
| 323 | mov(ebx, COND1); | 323 | mov(ebx, COND1); |
| 324 | xor(eax, (instr.flow_control.refx.Value() ^ 1)); | 324 | xor_(eax, (instr.flow_control.refx.Value() ^ 1)); |
| 325 | xor(ebx, (instr.flow_control.refy.Value() ^ 1)); | 325 | xor_(ebx, (instr.flow_control.refy.Value() ^ 1)); |
| 326 | or (eax, ebx); | 326 | or_(eax, ebx); |
| 327 | break; | 327 | break; |
| 328 | 328 | ||
| 329 | case Instruction::FlowControlType::And: | 329 | case Instruction::FlowControlType::And: |
| 330 | mov(eax, COND0); | 330 | mov(eax, COND0); |
| 331 | mov(ebx, COND1); | 331 | mov(ebx, COND1); |
| 332 | xor(eax, (instr.flow_control.refx.Value() ^ 1)); | 332 | xor_(eax, (instr.flow_control.refx.Value() ^ 1)); |
| 333 | xor(ebx, (instr.flow_control.refy.Value() ^ 1)); | 333 | xor_(ebx, (instr.flow_control.refy.Value() ^ 1)); |
| 334 | and(eax, ebx); | 334 | and_(eax, ebx); |
| 335 | break; | 335 | break; |
| 336 | 336 | ||
| 337 | case Instruction::FlowControlType::JustX: | 337 | case Instruction::FlowControlType::JustX: |
| 338 | mov(eax, COND0); | 338 | mov(eax, COND0); |
| 339 | xor(eax, (instr.flow_control.refx.Value() ^ 1)); | 339 | xor_(eax, (instr.flow_control.refx.Value() ^ 1)); |
| 340 | break; | 340 | break; |
| 341 | 341 | ||
| 342 | case Instruction::FlowControlType::JustY: | 342 | case Instruction::FlowControlType::JustY: |
| 343 | mov(eax, COND1); | 343 | mov(eax, COND1); |
| 344 | xor(eax, (instr.flow_control.refy.Value() ^ 1)); | 344 | xor_(eax, (instr.flow_control.refy.Value() ^ 1)); |
| 345 | break; | 345 | break; |
| 346 | } | 346 | } |
| 347 | } | 347 | } |
| @@ -734,10 +734,10 @@ void JitShader::Compile_LOOP(Instruction instr) { | |||
| 734 | mov(LOOPCOUNT, dword[SETUP + offset]); | 734 | mov(LOOPCOUNT, dword[SETUP + offset]); |
| 735 | mov(LOOPCOUNT_REG, LOOPCOUNT); | 735 | mov(LOOPCOUNT_REG, LOOPCOUNT); |
| 736 | shr(LOOPCOUNT_REG, 4); | 736 | shr(LOOPCOUNT_REG, 4); |
| 737 | and(LOOPCOUNT_REG, 0xFF0); // Y-component is the start | 737 | and_(LOOPCOUNT_REG, 0xFF0); // Y-component is the start |
| 738 | mov(LOOPINC, LOOPCOUNT); | 738 | mov(LOOPINC, LOOPCOUNT); |
| 739 | shr(LOOPINC, 12); | 739 | shr(LOOPINC, 12); |
| 740 | and(LOOPINC, 0xFF0); // Z-component is the incrementer | 740 | and_(LOOPINC, 0xFF0); // Z-component is the incrementer |
| 741 | movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count | 741 | movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count |
| 742 | add(LOOPCOUNT, 1); // Iteration count is X-component + 1 | 742 | add(LOOPCOUNT, 1); // Iteration count is X-component + 1 |
| 743 | 743 | ||
| @@ -858,9 +858,9 @@ void JitShader::Compile(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>* program_ | |||
| 858 | mov(STATE, ABI_PARAM2); | 858 | mov(STATE, ABI_PARAM2); |
| 859 | 859 | ||
| 860 | // Zero address/loop registers | 860 | // Zero address/loop registers |
| 861 | xor(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); | 861 | xor_(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); |
| 862 | xor(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); | 862 | xor_(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); |
| 863 | xor(LOOPCOUNT_REG, LOOPCOUNT_REG); | 863 | xor_(LOOPCOUNT_REG, LOOPCOUNT_REG); |
| 864 | 864 | ||
| 865 | // Used to set a register to one | 865 | // Used to set a register to one |
| 866 | static const __m128 one = {1.f, 1.f, 1.f, 1.f}; | 866 | static const __m128 one = {1.f, 1.f, 1.f, 1.f}; |
diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp index 8b7b1defb..cd7b6c39d 100644 --- a/src/video_core/swrasterizer/rasterizer.cpp +++ b/src/video_core/swrasterizer/rasterizer.cpp | |||
| @@ -357,10 +357,22 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | |||
| 357 | int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height))) | 357 | int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height))) |
| 358 | .ToFloat32(); | 358 | .ToFloat32(); |
| 359 | 359 | ||
| 360 | if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder && | 360 | bool use_border_s = false; |
| 361 | (s < 0 || static_cast<u32>(s) >= texture.config.width)) || | 361 | bool use_border_t = false; |
| 362 | (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder && | 362 | |
| 363 | (t < 0 || static_cast<u32>(t) >= texture.config.height))) { | 363 | if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder) { |
| 364 | use_border_s = s < 0 || s >= static_cast<int>(texture.config.width); | ||
| 365 | } else if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder2) { | ||
| 366 | use_border_s = s >= static_cast<int>(texture.config.width); | ||
| 367 | } | ||
| 368 | |||
| 369 | if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder) { | ||
| 370 | use_border_t = t < 0 || t >= static_cast<int>(texture.config.height); | ||
| 371 | } else if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder2) { | ||
| 372 | use_border_t = t >= static_cast<int>(texture.config.height); | ||
| 373 | } | ||
| 374 | |||
| 375 | if (use_border_s || use_border_t) { | ||
| 364 | auto border_color = texture.config.border_color; | 376 | auto border_color = texture.config.border_color; |
| 365 | texture_color[i] = {border_color.r, border_color.g, border_color.b, | 377 | texture_color[i] = {border_color.r, border_color.g, border_color.b, |
| 366 | border_color.a}; | 378 | border_color.a}; |
diff --git a/src/video_core/swrasterizer/texturing.cpp b/src/video_core/swrasterizer/texturing.cpp index aeb6aeb8c..4f02b93f2 100644 --- a/src/video_core/swrasterizer/texturing.cpp +++ b/src/video_core/swrasterizer/texturing.cpp | |||
| @@ -18,22 +18,33 @@ using TevStageConfig = TexturingRegs::TevStageConfig; | |||
| 18 | 18 | ||
| 19 | int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) { | 19 | int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) { |
| 20 | switch (mode) { | 20 | switch (mode) { |
| 21 | case TexturingRegs::TextureConfig::ClampToEdge2: | ||
| 22 | // For negative coordinate, ClampToEdge2 behaves the same as Repeat | ||
| 23 | if (val < 0) { | ||
| 24 | return static_cast<int>(static_cast<unsigned>(val) % size); | ||
| 25 | } | ||
| 26 | // [[fallthrough]] | ||
| 21 | case TexturingRegs::TextureConfig::ClampToEdge: | 27 | case TexturingRegs::TextureConfig::ClampToEdge: |
| 22 | val = std::max(val, 0); | 28 | val = std::max(val, 0); |
| 23 | val = std::min(val, (int)size - 1); | 29 | val = std::min(val, static_cast<int>(size) - 1); |
| 24 | return val; | 30 | return val; |
| 25 | 31 | ||
| 26 | case TexturingRegs::TextureConfig::ClampToBorder: | 32 | case TexturingRegs::TextureConfig::ClampToBorder: |
| 27 | return val; | 33 | return val; |
| 28 | 34 | ||
| 35 | case TexturingRegs::TextureConfig::ClampToBorder2: | ||
| 36 | // For ClampToBorder2, the case of positive coordinate beyond the texture size is already | ||
| 37 | // handled outside. Here we only handle the negative coordinate in the same way as Repeat. | ||
| 38 | case TexturingRegs::TextureConfig::Repeat2: | ||
| 39 | case TexturingRegs::TextureConfig::Repeat3: | ||
| 29 | case TexturingRegs::TextureConfig::Repeat: | 40 | case TexturingRegs::TextureConfig::Repeat: |
| 30 | return (int)((unsigned)val % size); | 41 | return static_cast<int>(static_cast<unsigned>(val) % size); |
| 31 | 42 | ||
| 32 | case TexturingRegs::TextureConfig::MirroredRepeat: { | 43 | case TexturingRegs::TextureConfig::MirroredRepeat: { |
| 33 | unsigned int coord = ((unsigned)val % (2 * size)); | 44 | unsigned int coord = (static_cast<unsigned>(val) % (2 * size)); |
| 34 | if (coord >= size) | 45 | if (coord >= size) |
| 35 | coord = 2 * size - 1 - coord; | 46 | coord = 2 * size - 1 - coord; |
| 36 | return (int)coord; | 47 | return static_cast<int>(coord); |
| 37 | } | 48 | } |
| 38 | 49 | ||
| 39 | default: | 50 | default: |