diff options
24 files changed, 246 insertions, 200 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8c8e3884..906a4bc7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
| @@ -10,16 +10,16 @@ Citra is a brand new project, so we have a great opportunity to keep things clea | |||
| 10 | 10 | ||
| 11 | ### Naming Rules | 11 | ### Naming Rules |
| 12 | * Functions | 12 | * Functions |
| 13 | * CamelCase, "_" may also be used for clarity (e.g. ARM_InitCore) | 13 | * PascalCase, "_" may also be used for clarity (e.g. ARM_InitCore) |
| 14 | * Variables | 14 | * Variables |
| 15 | * lower_case_underscored | 15 | * lower_case_underscored |
| 16 | * Prefix "g_" if global | 16 | * Prefix "g_" if global |
| 17 | * Classes | 17 | * Classes |
| 18 | * CamelCase, "_" may also be used for clarity (e.g. OGL_VideoInterface) | 18 | * PascalCase, "_" may also be used for clarity (e.g. OGL_VideoInterface) |
| 19 | * Files/Folders | 19 | * Files/Folders |
| 20 | * lower_case_underscored | 20 | * lower_case_underscored |
| 21 | * Namespaces | 21 | * Namespaces |
| 22 | * CamelCase, "_" may also be used for clarity (e.g. ARM_InitCore) | 22 | * PascalCase, "_" may also be used for clarity (e.g. ARM_InitCore) |
| 23 | 23 | ||
| 24 | ### Indentation/Whitespace Style | 24 | ### Indentation/Whitespace Style |
| 25 | Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead. | 25 | Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spaces instead. |
diff --git a/appveyor.yml b/appveyor.yml index 46362d1b4..8b420f20c 100644 --- a/appveyor.yml +++ b/appveyor.yml | |||
| @@ -33,12 +33,15 @@ after_build: | |||
| 33 | $GITREV = $(git show -s --format='%h') | 33 | $GITREV = $(git show -s --format='%h') |
| 34 | # Where are these spaces coming from? Regardless, let's remove them | 34 | # Where are these spaces coming from? Regardless, let's remove them |
| 35 | $BUILD_NAME = "citra-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ","" | 35 | $BUILD_NAME = "citra-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ","" |
| 36 | $BUILD_NAME_NOQT = "citra-noqt-${GITDATE}-${GITREV}-windows-amd64.7z" -replace " ","" | ||
| 36 | # Zip up the build folder | 37 | # Zip up the build folder |
| 37 | 7z a $BUILD_NAME .\build\bin\release\* | 38 | 7z a $BUILD_NAME .\build\bin\release\* |
| 39 | # Do a second archive with only the binaries | ||
| 40 | 7z a $BUILD_NAME_NOQT .\build\bin\release\*.exe | ||
| 38 | 41 | ||
| 39 | # Download winscp | 42 | # Download winscp |
| 40 | Invoke-WebRequest "http://hivelocity.dl.sourceforge.net/project/winscp/WinSCP/5.7/winscp570.zip" -OutFile "winscp570.zip" | 43 | Invoke-WebRequest "https://cdn.winscp.net/files/winscp573.zip?secure=yE1jHlRoBZTA0htWnu1OVQ==,1434405422" -OutFile "winscp573.zip" |
| 41 | 7z e -y winscp570.zip | 44 | 7z e -y winscp573.zip |
| 42 | 45 | ||
| 43 | # Upload to server | 46 | # Upload to server |
| 44 | .\WinSCP.com /command ` | 47 | .\WinSCP.com /command ` |
| @@ -46,5 +49,6 @@ after_build: | |||
| 46 | "option confirm off" ` | 49 | "option confirm off" ` |
| 47 | "open sftp://citra-builds:${env:BUILD_PASSWORD}@builds.citra-emu.org -hostkey=*" ` | 50 | "open sftp://citra-builds:${env:BUILD_PASSWORD}@builds.citra-emu.org -hostkey=*" ` |
| 48 | "put $BUILD_NAME /citra/nightly/windows-amd64/" ` | 51 | "put $BUILD_NAME /citra/nightly/windows-amd64/" ` |
| 52 | "put $BUILD_NAME_NOQT /citra/nightly/windows-noqt-amd64/" ` | ||
| 49 | "exit" | 53 | "exit" |
| 50 | } | 54 | } |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index c05779380..c2d1ad240 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -91,10 +91,10 @@ if (Qt5_FOUND AND MSVC) | |||
| 91 | set(PLATFORMS ${DLL_DEST}platforms/) | 91 | set(PLATFORMS ${DLL_DEST}platforms/) |
| 92 | 92 | ||
| 93 | # windows commandline expects the / to be \ so switch them | 93 | # windows commandline expects the / to be \ so switch them |
| 94 | string(REPLACE "/" "\\" Qt5_DLL_DIR ${Qt5_DLL_DIR}) | 94 | string(REPLACE "/" "\\\\" Qt5_DLL_DIR ${Qt5_DLL_DIR}) |
| 95 | string(REPLACE "/" "\\" Qt5_PLATFORMS_DIR ${Qt5_PLATFORMS_DIR}) | 95 | string(REPLACE "/" "\\\\" Qt5_PLATFORMS_DIR ${Qt5_PLATFORMS_DIR}) |
| 96 | string(REPLACE "/" "\\" DLL_DEST ${DLL_DEST}) | 96 | string(REPLACE "/" "\\\\" DLL_DEST ${DLL_DEST}) |
| 97 | string(REPLACE "/" "\\" PLATFORMS ${PLATFORMS}) | 97 | string(REPLACE "/" "\\\\" PLATFORMS ${PLATFORMS}) |
| 98 | 98 | ||
| 99 | # /NJH /NJS /NDL /NFL /NC /NS /NP - Silence any output | 99 | # /NJH /NJS /NDL /NFL /NC /NS /NP - Silence any output |
| 100 | # cmake adds an extra check for command success which doesn't work too well with robocopy | 100 | # cmake adds an extra check for command success which doesn't work too well with robocopy |
diff --git a/src/citra_qt/debugger/callstack.ui b/src/citra_qt/debugger/callstack.ui index b0e31120f..248ea3dd7 100644 --- a/src/citra_qt/debugger/callstack.ui +++ b/src/citra_qt/debugger/callstack.ui | |||
| @@ -17,6 +17,9 @@ | |||
| 17 | <layout class="QVBoxLayout" name="verticalLayout"> | 17 | <layout class="QVBoxLayout" name="verticalLayout"> |
| 18 | <item> | 18 | <item> |
| 19 | <widget class="QTreeView" name="treeView"> | 19 | <widget class="QTreeView" name="treeView"> |
| 20 | <property name="editTriggers"> | ||
| 21 | <set>QAbstractItemView::NoEditTriggers</set> | ||
| 22 | </property> | ||
| 20 | <property name="alternatingRowColors"> | 23 | <property name="alternatingRowColors"> |
| 21 | <bool>true</bool> | 24 | <bool>true</bool> |
| 22 | </property> | 25 | </property> |
diff --git a/src/common/math_util.h b/src/common/math_util.h index 4b0910741..d44b06e74 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -12,7 +12,7 @@ namespace MathUtil | |||
| 12 | { | 12 | { |
| 13 | 13 | ||
| 14 | inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, unsigned length1) { | 14 | inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, unsigned length1) { |
| 15 | return (std::max(start0, start1) <= std::min(start0 + length0, start1 + length1)); | 15 | return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1)); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | template<typename T> | 18 | template<typename T> |
diff --git a/src/core/arm/skyeye_common/arm_regformat.h b/src/core/arm/skyeye_common/arm_regformat.h index 6c89774eb..a92effbb4 100644 --- a/src/core/arm/skyeye_common/arm_regformat.h +++ b/src/core/arm/skyeye_common/arm_regformat.h | |||
| @@ -59,6 +59,8 @@ enum { | |||
| 59 | VFP_FPSID, | 59 | VFP_FPSID, |
| 60 | VFP_FPSCR, | 60 | VFP_FPSCR, |
| 61 | VFP_FPEXC, | 61 | VFP_FPEXC, |
| 62 | VFP_MVFR0, | ||
| 63 | VFP_MVFR1, | ||
| 62 | 64 | ||
| 63 | // Not an actual register. | 65 | // Not an actual register. |
| 64 | // All VFP system registers should be defined above this. | 66 | // All VFP system registers should be defined above this. |
diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp index b88d47750..571d6c2f2 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.cpp +++ b/src/core/arm/skyeye_common/vfp/vfp.cpp | |||
| @@ -33,6 +33,10 @@ unsigned VFPInit(ARMul_State* state) | |||
| 33 | state->VFP[VFP_FPEXC] = 0; | 33 | state->VFP[VFP_FPEXC] = 0; |
| 34 | state->VFP[VFP_FPSCR] = 0; | 34 | state->VFP[VFP_FPSCR] = 0; |
| 35 | 35 | ||
| 36 | // ARM11 MPCore feature register values. | ||
| 37 | state->VFP[VFP_MVFR0] = 0x11111111; | ||
| 38 | state->VFP[VFP_MVFR1] = 0; | ||
| 39 | |||
| 36 | return 0; | 40 | return 0; |
| 37 | } | 41 | } |
| 38 | 42 | ||
diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h index 727350f14..acefae9bb 100644 --- a/src/core/arm/skyeye_common/vfp/vfp.h +++ b/src/core/arm/skyeye_common/vfp/vfp.h | |||
| @@ -22,7 +22,6 @@ | |||
| 22 | 22 | ||
| 23 | #include "core/arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */ | 23 | #include "core/arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */ |
| 24 | 24 | ||
| 25 | #define VFP_DEBUG_UNIMPLEMENTED(x) LOG_ERROR(Core_ARM11, "in func %s, " #x " unimplemented\n", __FUNCTION__); exit(-1); | ||
| 26 | #define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested\n", __FUNCTION__); | 25 | #define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested\n", __FUNCTION__); |
| 27 | #define CHECK_VFP_ENABLED | 26 | #define CHECK_VFP_ENABLED |
| 28 | #define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); | 27 | #define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); |
diff --git a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp index 3ed918a93..67fe63aa4 100644 --- a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp | |||
| @@ -1068,10 +1068,12 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbrc)(unsigned int inst, int index) | |||
| 1068 | #ifdef VFP_INTERPRETER_IMPL | 1068 | #ifdef VFP_INTERPRETER_IMPL |
| 1069 | VMOVBRC_INST: | 1069 | VMOVBRC_INST: |
| 1070 | { | 1070 | { |
| 1071 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { | 1071 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 1072 | CHECK_VFP_ENABLED; | 1072 | CHECK_VFP_ENABLED; |
| 1073 | 1073 | ||
| 1074 | VFP_DEBUG_UNIMPLEMENTED(VMOVBRC); | 1074 | vmovbrc_inst* const inst_cream = (vmovbrc_inst*)inst_base->component; |
| 1075 | |||
| 1076 | cpu->ExtReg[(2 * inst_cream->d) + inst_cream->index] = cpu->Reg[inst_cream->t]; | ||
| 1075 | } | 1077 | } |
| 1076 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 1078 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 1077 | INC_PC(sizeof(vmovbrc_inst)); | 1079 | INC_PC(sizeof(vmovbrc_inst)); |
| @@ -1139,12 +1141,10 @@ VMRS_INST: | |||
| 1139 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSID]; | 1141 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPSID]; |
| 1140 | break; | 1142 | break; |
| 1141 | case 6: | 1143 | case 6: |
| 1142 | /* MVFR1, VFPv3 only ? */ | 1144 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR1]; |
| 1143 | LOG_TRACE(Core_ARM11, "\tr%d <= MVFR1 unimplemented\n", inst_cream->Rt); | ||
| 1144 | break; | 1145 | break; |
| 1145 | case 7: | 1146 | case 7: |
| 1146 | /* MVFR0, VFPv3 only? */ | 1147 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_MVFR0]; |
| 1147 | LOG_TRACE(Core_ARM11, "\tr%d <= MVFR0 unimplemented\n", inst_cream->Rt); | ||
| 1148 | break; | 1148 | break; |
| 1149 | case 8: | 1149 | case 8: |
| 1150 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPEXC]; | 1150 | cpu->Reg[inst_cream->Rt] = cpu->VFP[VFP_FPEXC]; |
| @@ -1195,10 +1195,12 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vmovbcr)(unsigned int inst, int index) | |||
| 1195 | #ifdef VFP_INTERPRETER_IMPL | 1195 | #ifdef VFP_INTERPRETER_IMPL |
| 1196 | VMOVBCR_INST: | 1196 | VMOVBCR_INST: |
| 1197 | { | 1197 | { |
| 1198 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { | 1198 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 1199 | CHECK_VFP_ENABLED; | 1199 | CHECK_VFP_ENABLED; |
| 1200 | 1200 | ||
| 1201 | VFP_DEBUG_UNIMPLEMENTED(VMOVBCR); | 1201 | vmovbcr_inst* const inst_cream = (vmovbcr_inst*) inst_base->component; |
| 1202 | |||
| 1203 | cpu->Reg[inst_cream->t] = cpu->ExtReg[(2 * inst_cream->d) + inst_cream->index]; | ||
| 1202 | } | 1204 | } |
| 1203 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 1205 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 1204 | INC_PC(sizeof(vmovbcr_inst)); | 1206 | INC_PC(sizeof(vmovbcr_inst)); |
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 23c86a72d..5949cb470 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -9,11 +9,15 @@ | |||
| 9 | #include "core/arm/arm_interface.h" | 9 | #include "core/arm/arm_interface.h" |
| 10 | #include "core/memory.h" | 10 | #include "core/memory.h" |
| 11 | #include "core/hle/hle.h" | 11 | #include "core/hle/hle.h" |
| 12 | #include "core/hle/result.h" | ||
| 12 | 13 | ||
| 13 | namespace HLE { | 14 | namespace HLE { |
| 14 | 15 | ||
| 15 | #define PARAM(n) Core::g_app_core->GetReg(n) | 16 | #define PARAM(n) Core::g_app_core->GetReg(n) |
| 16 | 17 | ||
| 18 | /// An invalid result code that is meant to be overwritten when a thread resumes from waiting | ||
| 19 | static const ResultCode RESULT_INVALID(0xDEADC0DE); | ||
| 20 | |||
| 17 | /** | 21 | /** |
| 18 | * HLE a function return from the current ARM11 userland process | 22 | * HLE a function return from the current ARM11 userland process |
| 19 | * @param res Result to return | 23 | * @param res Result to return |
| @@ -57,8 +61,11 @@ template<ResultCode func(s32*, u32*, s32, bool, s64)> void Wrap() { | |||
| 57 | s32 param_1 = 0; | 61 | s32 param_1 = 0; |
| 58 | s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), | 62 | s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), |
| 59 | (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))).raw; | 63 | (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))).raw; |
| 60 | Core::g_app_core->SetReg(1, (u32)param_1); | 64 | |
| 61 | FuncReturn(retval); | 65 | if (retval != RESULT_INVALID.raw) { |
| 66 | Core::g_app_core->SetReg(1, (u32)param_1); | ||
| 67 | FuncReturn(retval); | ||
| 68 | } | ||
| 62 | } | 69 | } |
| 63 | 70 | ||
| 64 | template<ResultCode func(u32, u32, u32, u32, s64)> void Wrap() { | 71 | template<ResultCode func(u32, u32, u32, u32, s64)> void Wrap() { |
| @@ -73,7 +80,11 @@ template<ResultCode func(u32*)> void Wrap(){ | |||
| 73 | } | 80 | } |
| 74 | 81 | ||
| 75 | template<ResultCode func(u32, s64)> void Wrap() { | 82 | template<ResultCode func(u32, s64)> void Wrap() { |
| 76 | FuncReturn(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw); | 83 | s32 retval = func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw; |
| 84 | |||
| 85 | if (retval != RESULT_INVALID.raw) { | ||
| 86 | FuncReturn(retval); | ||
| 87 | } | ||
| 77 | } | 88 | } |
| 78 | 89 | ||
| 79 | template<ResultCode func(void*, void*, u32)> void Wrap(){ | 90 | template<ResultCode func(void*, void*, u32)> void Wrap(){ |
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index e45deb1c6..f338f3266 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -41,10 +41,7 @@ void Event::Acquire() { | |||
| 41 | 41 | ||
| 42 | void Event::Signal() { | 42 | void Event::Signal() { |
| 43 | signaled = true; | 43 | signaled = true; |
| 44 | |||
| 45 | WakeupAllWaitingThreads(); | 44 | WakeupAllWaitingThreads(); |
| 46 | |||
| 47 | HLE::Reschedule(__func__); | ||
| 48 | } | 45 | } |
| 49 | 46 | ||
| 50 | void Event::Clear() { | 47 | void Event::Clear() { |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 726e4d2ff..20e11da16 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -32,27 +32,13 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { | |||
| 32 | waiting_threads.erase(itr); | 32 | waiting_threads.erase(itr); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | SharedPtr<Thread> WaitObject::WakeupNextThread() { | ||
| 36 | if (waiting_threads.empty()) | ||
| 37 | return nullptr; | ||
| 38 | |||
| 39 | auto next_thread = std::move(waiting_threads.front()); | ||
| 40 | waiting_threads.erase(waiting_threads.begin()); | ||
| 41 | |||
| 42 | next_thread->ReleaseWaitObject(this); | ||
| 43 | |||
| 44 | return next_thread; | ||
| 45 | } | ||
| 46 | |||
| 47 | void WaitObject::WakeupAllWaitingThreads() { | 35 | void WaitObject::WakeupAllWaitingThreads() { |
| 48 | auto waiting_threads_copy = waiting_threads; | 36 | for (auto thread : waiting_threads) |
| 37 | thread->ResumeFromWait(); | ||
| 49 | 38 | ||
| 50 | // We use a copy because ReleaseWaitObject will remove the thread from this object's | 39 | waiting_threads.clear(); |
| 51 | // waiting_threads list | ||
| 52 | for (auto thread : waiting_threads_copy) | ||
| 53 | thread->ReleaseWaitObject(this); | ||
| 54 | 40 | ||
| 55 | ASSERT_MSG(waiting_threads.empty(), "failed to awaken all waiting threads!"); | 41 | HLE::Reschedule(__func__); |
| 56 | } | 42 | } |
| 57 | 43 | ||
| 58 | HandleTable::HandleTable() { | 44 | HandleTable::HandleTable() { |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index a5a0f4800..64595f758 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -140,12 +140,6 @@ public: | |||
| 140 | */ | 140 | */ |
| 141 | void RemoveWaitingThread(Thread* thread); | 141 | void RemoveWaitingThread(Thread* thread); |
| 142 | 142 | ||
| 143 | /** | ||
| 144 | * Wake up the next thread waiting on this object | ||
| 145 | * @return Pointer to the thread that was resumed, nullptr if no threads are waiting | ||
| 146 | */ | ||
| 147 | SharedPtr<Thread> WakeupNextThread(); | ||
| 148 | |||
| 149 | /// Wake up all threads waiting on this object | 143 | /// Wake up all threads waiting on this object |
| 150 | void WakeupAllWaitingThreads(); | 144 | void WakeupAllWaitingThreads(); |
| 151 | 145 | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 6aa73df86..edb97d324 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -23,12 +23,7 @@ static void ResumeWaitingThread(Mutex* mutex) { | |||
| 23 | // Reset mutex lock thread handle, nothing is waiting | 23 | // Reset mutex lock thread handle, nothing is waiting |
| 24 | mutex->lock_count = 0; | 24 | mutex->lock_count = 0; |
| 25 | mutex->holding_thread = nullptr; | 25 | mutex->holding_thread = nullptr; |
| 26 | 26 | mutex->WakeupAllWaitingThreads(); | |
| 27 | // Find the next waiting thread for the mutex... | ||
| 28 | auto next_thread = mutex->WakeupNextThread(); | ||
| 29 | if (next_thread != nullptr) { | ||
| 30 | mutex->Acquire(next_thread); | ||
| 31 | } | ||
| 32 | } | 27 | } |
| 33 | 28 | ||
| 34 | void ReleaseThreadMutexes(Thread* thread) { | 29 | void ReleaseThreadMutexes(Thread* thread) { |
| @@ -94,8 +89,6 @@ void Mutex::Release() { | |||
| 94 | ResumeWaitingThread(this); | 89 | ResumeWaitingThread(this); |
| 95 | } | 90 | } |
| 96 | } | 91 | } |
| 97 | |||
| 98 | HLE::Reschedule(__func__); | ||
| 99 | } | 92 | } |
| 100 | 93 | ||
| 101 | } // namespace | 94 | } // namespace |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 96d61ed3a..4b359ed07 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -48,13 +48,7 @@ ResultVal<s32> Semaphore::Release(s32 release_count) { | |||
| 48 | s32 previous_count = available_count; | 48 | s32 previous_count = available_count; |
| 49 | available_count += release_count; | 49 | available_count += release_count; |
| 50 | 50 | ||
| 51 | // Notify some of the threads that the semaphore has been released | 51 | WakeupAllWaitingThreads(); |
| 52 | // stop once the semaphore is full again or there are no more waiting threads | ||
| 53 | while (!ShouldWait() && WakeupNextThread() != nullptr) { | ||
| 54 | Acquire(); | ||
| 55 | } | ||
| 56 | |||
| 57 | HLE::Reschedule(__func__); | ||
| 58 | 52 | ||
| 59 | return MakeResult<s32>(previous_count); | 53 | return MakeResult<s32>(previous_count); |
| 60 | } | 54 | } |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 22c795ad4..4729a7fe0 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "common/thread_queue_list.h" | 13 | #include "common/thread_queue_list.h" |
| 14 | 14 | ||
| 15 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/arm/skyeye_common/armdefs.h" | ||
| 16 | #include "core/core.h" | 17 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 18 | #include "core/hle/hle.h" | 19 | #include "core/hle/hle.h" |
| @@ -193,8 +194,22 @@ static void SwitchContext(Thread* new_thread) { | |||
| 193 | if (new_thread) { | 194 | if (new_thread) { |
| 194 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); | 195 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); |
| 195 | 196 | ||
| 197 | // Cancel any outstanding wakeup events for this thread | ||
| 198 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | ||
| 199 | |||
| 196 | current_thread = new_thread; | 200 | current_thread = new_thread; |
| 197 | 201 | ||
| 202 | // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun | ||
| 203 | // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire | ||
| 204 | // the requested wait object(s) before continuing. | ||
| 205 | if (new_thread->waitsynch_waited) { | ||
| 206 | // CPSR flag indicates CPU mode | ||
| 207 | bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0; | ||
| 208 | |||
| 209 | // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM | ||
| 210 | new_thread->context.pc -= thumb_mode ? 2 : 4; | ||
| 211 | } | ||
| 212 | |||
| 198 | ready_queue.remove(new_thread->current_priority, new_thread); | 213 | ready_queue.remove(new_thread->current_priority, new_thread); |
| 199 | new_thread->status = THREADSTATUS_RUNNING; | 214 | new_thread->status = THREADSTATUS_RUNNING; |
| 200 | 215 | ||
| @@ -243,6 +258,7 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa | |||
| 243 | thread->wait_set_output = wait_set_output; | 258 | thread->wait_set_output = wait_set_output; |
| 244 | thread->wait_all = wait_all; | 259 | thread->wait_all = wait_all; |
| 245 | thread->wait_objects = std::move(wait_objects); | 260 | thread->wait_objects = std::move(wait_objects); |
| 261 | thread->waitsynch_waited = true; | ||
| 246 | thread->status = THREADSTATUS_WAIT_SYNCH; | 262 | thread->status = THREADSTATUS_WAIT_SYNCH; |
| 247 | } | 263 | } |
| 248 | 264 | ||
| @@ -268,6 +284,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 268 | return; | 284 | return; |
| 269 | } | 285 | } |
| 270 | 286 | ||
| 287 | thread->waitsynch_waited = false; | ||
| 288 | |||
| 271 | if (thread->status == THREADSTATUS_WAIT_SYNCH) { | 289 | if (thread->status == THREADSTATUS_WAIT_SYNCH) { |
| 272 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 290 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, |
| 273 | ErrorSummary::StatusChanged, ErrorLevel::Info)); | 291 | ErrorSummary::StatusChanged, ErrorLevel::Info)); |
| @@ -288,63 +306,20 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 288 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); | 306 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); |
| 289 | } | 307 | } |
| 290 | 308 | ||
| 291 | void Thread::ReleaseWaitObject(WaitObject* wait_object) { | ||
| 292 | if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) { | ||
| 293 | LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); | ||
| 294 | return; | ||
| 295 | } | ||
| 296 | |||
| 297 | // Remove this thread from the waiting object's thread list | ||
| 298 | wait_object->RemoveWaitingThread(this); | ||
| 299 | |||
| 300 | unsigned index = 0; | ||
| 301 | bool wait_all_failed = false; // Will be set to true if any object is unavailable | ||
| 302 | |||
| 303 | // Iterate through all waiting objects to check availability... | ||
| 304 | for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { | ||
| 305 | if ((*itr)->ShouldWait()) | ||
| 306 | wait_all_failed = true; | ||
| 307 | |||
| 308 | // The output should be the last index of wait_object | ||
| 309 | if (*itr == wait_object) | ||
| 310 | index = itr - wait_objects.begin(); | ||
| 311 | } | ||
| 312 | |||
| 313 | // If we are waiting on all objects... | ||
| 314 | if (wait_all) { | ||
| 315 | // Resume the thread only if all are available... | ||
| 316 | if (!wait_all_failed) { | ||
| 317 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 318 | SetWaitSynchronizationOutput(-1); | ||
| 319 | |||
| 320 | ResumeFromWait(); | ||
| 321 | } | ||
| 322 | } else { | ||
| 323 | // Otherwise, resume | ||
| 324 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 325 | |||
| 326 | if (wait_set_output) | ||
| 327 | SetWaitSynchronizationOutput(index); | ||
| 328 | |||
| 329 | ResumeFromWait(); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | void Thread::ResumeFromWait() { | 309 | void Thread::ResumeFromWait() { |
| 334 | // Cancel any outstanding wakeup events for this thread | ||
| 335 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | ||
| 336 | |||
| 337 | switch (status) { | 310 | switch (status) { |
| 338 | case THREADSTATUS_WAIT_SYNCH: | 311 | case THREADSTATUS_WAIT_SYNCH: |
| 339 | // Remove this thread from all other WaitObjects | ||
| 340 | for (auto wait_object : wait_objects) | ||
| 341 | wait_object->RemoveWaitingThread(this); | ||
| 342 | break; | ||
| 343 | case THREADSTATUS_WAIT_ARB: | 312 | case THREADSTATUS_WAIT_ARB: |
| 344 | case THREADSTATUS_WAIT_SLEEP: | 313 | case THREADSTATUS_WAIT_SLEEP: |
| 345 | break; | 314 | break; |
| 346 | case THREADSTATUS_RUNNING: | 315 | |
| 347 | case THREADSTATUS_READY: | 316 | case THREADSTATUS_READY: |
| 317 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | ||
| 318 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | ||
| 319 | // already been set to THREADSTATUS_READY. | ||
| 320 | return; | ||
| 321 | |||
| 322 | case THREADSTATUS_RUNNING: | ||
| 348 | DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); | 323 | DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); |
| 349 | return; | 324 | return; |
| 350 | case THREADSTATUS_DEAD: | 325 | case THREADSTATUS_DEAD: |
| @@ -415,6 +390,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 415 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); | 390 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); |
| 416 | thread->owner_process = g_current_process; | 391 | thread->owner_process = g_current_process; |
| 417 | thread->tls_index = -1; | 392 | thread->tls_index = -1; |
| 393 | thread->waitsynch_waited = false; | ||
| 418 | 394 | ||
| 419 | // Find the next available TLS index, and mark it as used | 395 | // Find the next available TLS index, and mark it as used |
| 420 | auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; | 396 | auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 2c65419c3..b8160bb2c 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -96,12 +96,6 @@ public: | |||
| 96 | u32 GetThreadId() const { return thread_id; } | 96 | u32 GetThreadId() const { return thread_id; } |
| 97 | 97 | ||
| 98 | /** | 98 | /** |
| 99 | * Release an acquired wait object | ||
| 100 | * @param wait_object WaitObject to release | ||
| 101 | */ | ||
| 102 | void ReleaseWaitObject(WaitObject* wait_object); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Resumes a thread from waiting | 99 | * Resumes a thread from waiting |
| 106 | */ | 100 | */ |
| 107 | void ResumeFromWait(); | 101 | void ResumeFromWait(); |
| @@ -152,6 +146,8 @@ public: | |||
| 152 | 146 | ||
| 153 | s32 tls_index; ///< Index of the Thread Local Storage of the thread | 147 | s32 tls_index; ///< Index of the Thread Local Storage of the thread |
| 154 | 148 | ||
| 149 | bool waitsynch_waited; ///< Set to true if the last svcWaitSynch call caused the thread to wait | ||
| 150 | |||
| 155 | /// Mutexes currently held by this thread, which will be released when it exits. | 151 | /// Mutexes currently held by this thread, which will be released when it exits. |
| 156 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; | 152 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; |
| 157 | 153 | ||
| @@ -163,12 +159,12 @@ public: | |||
| 163 | 159 | ||
| 164 | std::string name; | 160 | std::string name; |
| 165 | 161 | ||
| 162 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | ||
| 163 | Handle callback_handle; | ||
| 164 | |||
| 166 | private: | 165 | private: |
| 167 | Thread(); | 166 | Thread(); |
| 168 | ~Thread() override; | 167 | ~Thread() override; |
| 169 | |||
| 170 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | ||
| 171 | Handle callback_handle; | ||
| 172 | }; | 168 | }; |
| 173 | 169 | ||
| 174 | /** | 170 | /** |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index d1555c753..6cde4fc87 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -40,9 +40,6 @@ const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel, | |||
| 40 | const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, | 40 | const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, |
| 41 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E | 41 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E |
| 42 | 42 | ||
| 43 | /// An invalid result code that is meant to be overwritten when a thread resumes from waiting | ||
| 44 | const ResultCode RESULT_INVALID(0xDEADC0DE); | ||
| 45 | |||
| 46 | enum ControlMemoryOperation { | 43 | enum ControlMemoryOperation { |
| 47 | MEMORY_OPERATION_HEAP = 0x00000003, | 44 | MEMORY_OPERATION_HEAP = 0x00000003, |
| 48 | MEMORY_OPERATION_GSP_HEAP = 0x00010003, | 45 | MEMORY_OPERATION_GSP_HEAP = 0x00010003, |
| @@ -143,6 +140,10 @@ static ResultCode CloseHandle(Handle handle) { | |||
| 143 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds | 140 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds |
| 144 | static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | 141 | static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { |
| 145 | auto object = Kernel::g_handle_table.GetWaitObject(handle); | 142 | auto object = Kernel::g_handle_table.GetWaitObject(handle); |
| 143 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||
| 144 | |||
| 145 | thread->waitsynch_waited = false; | ||
| 146 | |||
| 146 | if (object == nullptr) | 147 | if (object == nullptr) |
| 147 | return ERR_INVALID_HANDLE; | 148 | return ERR_INVALID_HANDLE; |
| 148 | 149 | ||
| @@ -154,14 +155,14 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 154 | // Check for next thread to schedule | 155 | // Check for next thread to schedule |
| 155 | if (object->ShouldWait()) { | 156 | if (object->ShouldWait()) { |
| 156 | 157 | ||
| 157 | object->AddWaitingThread(Kernel::GetCurrentThread()); | 158 | object->AddWaitingThread(thread); |
| 158 | Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false); | 159 | Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false); |
| 159 | 160 | ||
| 160 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 161 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 161 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); | 162 | thread->WakeAfterDelay(nano_seconds); |
| 162 | 163 | ||
| 163 | // NOTE: output of this SVC will be set later depending on how the thread resumes | 164 | // NOTE: output of this SVC will be set later depending on how the thread resumes |
| 164 | return RESULT_INVALID; | 165 | return HLE::RESULT_INVALID; |
| 165 | } | 166 | } |
| 166 | 167 | ||
| 167 | object->Acquire(); | 168 | object->Acquire(); |
| @@ -173,6 +174,9 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 173 | static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { | 174 | static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { |
| 174 | bool wait_thread = !wait_all; | 175 | bool wait_thread = !wait_all; |
| 175 | int handle_index = 0; | 176 | int handle_index = 0; |
| 177 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||
| 178 | bool was_waiting = thread->waitsynch_waited; | ||
| 179 | thread->waitsynch_waited = false; | ||
| 176 | 180 | ||
| 177 | // Check if 'handles' is invalid | 181 | // Check if 'handles' is invalid |
| 178 | if (handles == nullptr) | 182 | if (handles == nullptr) |
| @@ -190,6 +194,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 190 | // necessary | 194 | // necessary |
| 191 | if (handle_count != 0) { | 195 | if (handle_count != 0) { |
| 192 | bool selected = false; // True once an object has been selected | 196 | bool selected = false; // True once an object has been selected |
| 197 | |||
| 198 | Kernel::SharedPtr<Kernel::WaitObject> wait_object; | ||
| 199 | |||
| 193 | for (int i = 0; i < handle_count; ++i) { | 200 | for (int i = 0; i < handle_count; ++i) { |
| 194 | auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | 201 | auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); |
| 195 | if (object == nullptr) | 202 | if (object == nullptr) |
| @@ -204,10 +211,11 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 204 | wait_thread = true; | 211 | wait_thread = true; |
| 205 | } else { | 212 | } else { |
| 206 | // Do not wait on this object, check if this object should be selected... | 213 | // Do not wait on this object, check if this object should be selected... |
| 207 | if (!wait_all && !selected) { | 214 | if (!wait_all && (!selected || (wait_object == object && was_waiting))) { |
| 208 | // Do not wait the thread | 215 | // Do not wait the thread |
| 209 | wait_thread = false; | 216 | wait_thread = false; |
| 210 | handle_index = i; | 217 | handle_index = i; |
| 218 | wait_object = object; | ||
| 211 | selected = true; | 219 | selected = true; |
| 212 | } | 220 | } |
| 213 | } | 221 | } |
| @@ -241,7 +249,7 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 241 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); | 249 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); |
| 242 | 250 | ||
| 243 | // NOTE: output of this SVC will be set later depending on how the thread resumes | 251 | // NOTE: output of this SVC will be set later depending on how the thread resumes |
| 244 | return RESULT_INVALID; | 252 | return HLE::RESULT_INVALID; |
| 245 | } | 253 | } |
| 246 | 254 | ||
| 247 | // Acquire objects if we did not wait... | 255 | // Acquire objects if we did not wait... |
| @@ -261,7 +269,7 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 261 | 269 | ||
| 262 | // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does | 270 | // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does |
| 263 | // not seem to set it to any meaningful value. | 271 | // not seem to set it to any meaningful value. |
| 264 | *out = wait_all ? 0 : handle_index; | 272 | *out = handle_count != 0 ? (wait_all ? -1 : handle_index) : 0; |
| 265 | 273 | ||
| 266 | return RESULT_SUCCESS; | 274 | return RESULT_SUCCESS; |
| 267 | } | 275 | } |
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index f4906cc7e..c7006a498 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp | |||
| @@ -18,7 +18,7 @@ inline void Read(T &var, const u32 addr) { | |||
| 18 | GPU::Read(var, addr); | 18 | GPU::Read(var, addr); |
| 19 | break; | 19 | break; |
| 20 | case VADDR_LCD: | 20 | case VADDR_LCD: |
| 21 | LCD::Write(var, addr); | 21 | LCD::Read(var, addr); |
| 22 | break; | 22 | break; |
| 23 | default: | 23 | default: |
| 24 | LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); | 24 | LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 684ec9818..9628a7589 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -372,9 +372,9 @@ struct Regs { | |||
| 372 | INSERT_PADDING_WORDS(0x2); | 372 | INSERT_PADDING_WORDS(0x2); |
| 373 | 373 | ||
| 374 | const std::array<Regs::TevStageConfig,6> GetTevStages() const { | 374 | const std::array<Regs::TevStageConfig,6> GetTevStages() const { |
| 375 | return { tev_stage0, tev_stage1, | 375 | return {{ tev_stage0, tev_stage1, |
| 376 | tev_stage2, tev_stage3, | 376 | tev_stage2, tev_stage3, |
| 377 | tev_stage4, tev_stage5 }; | 377 | tev_stage4, tev_stage5 }}; |
| 378 | }; | 378 | }; |
| 379 | 379 | ||
| 380 | enum class BlendEquation : u32 { | 380 | enum class BlendEquation : u32 { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index d31c46cca..518f79331 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -94,14 +94,27 @@ void RasterizerOpenGL::InitObjects() { | |||
| 94 | // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation | 94 | // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation |
| 95 | fb_color_texture.texture.Create(); | 95 | fb_color_texture.texture.Create(); |
| 96 | ReconfigureColorTexture(fb_color_texture, Pica::Regs::ColorFormat::RGBA8, 1, 1); | 96 | ReconfigureColorTexture(fb_color_texture, Pica::Regs::ColorFormat::RGBA8, 1, 1); |
| 97 | |||
| 98 | state.texture_units[0].enabled_2d = true; | ||
| 99 | state.texture_units[0].texture_2d = fb_color_texture.texture.handle; | ||
| 100 | state.Apply(); | ||
| 101 | |||
| 97 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | 102 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); |
| 98 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 103 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 99 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 104 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 100 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 105 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 101 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 106 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 102 | 107 | ||
| 108 | state.texture_units[0].texture_2d = 0; | ||
| 109 | state.Apply(); | ||
| 110 | |||
| 103 | fb_depth_texture.texture.Create(); | 111 | fb_depth_texture.texture.Create(); |
| 104 | ReconfigureDepthTexture(fb_depth_texture, Pica::Regs::DepthFormat::D16, 1, 1); | 112 | ReconfigureDepthTexture(fb_depth_texture, Pica::Regs::DepthFormat::D16, 1, 1); |
| 113 | |||
| 114 | state.texture_units[0].enabled_2d = true; | ||
| 115 | state.texture_units[0].texture_2d = fb_depth_texture.texture.handle; | ||
| 116 | state.Apply(); | ||
| 117 | |||
| 105 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | 118 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); |
| 106 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 119 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 107 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 120 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| @@ -110,14 +123,13 @@ void RasterizerOpenGL::InitObjects() { | |||
| 110 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); | 123 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); |
| 111 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); | 124 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); |
| 112 | 125 | ||
| 126 | state.texture_units[0].texture_2d = 0; | ||
| 127 | state.Apply(); | ||
| 128 | |||
| 113 | // Configure OpenGL framebuffer | 129 | // Configure OpenGL framebuffer |
| 114 | framebuffer.Create(); | 130 | framebuffer.Create(); |
| 115 | 131 | ||
| 116 | state.draw.framebuffer = framebuffer.handle; | 132 | state.draw.framebuffer = framebuffer.handle; |
| 117 | |||
| 118 | // Unbind texture to allow binding to framebuffer | ||
| 119 | state.texture_units[0].enabled_2d = true; | ||
| 120 | state.texture_units[0].texture_2d = 0; | ||
| 121 | state.Apply(); | 133 | state.Apply(); |
| 122 | 134 | ||
| 123 | glActiveTexture(GL_TEXTURE0); | 135 | glActiveTexture(GL_TEXTURE0); |
| @@ -205,7 +217,19 @@ void RasterizerOpenGL::DrawTriangles() { | |||
| 205 | 217 | ||
| 206 | vertex_batch.clear(); | 218 | vertex_batch.clear(); |
| 207 | 219 | ||
| 208 | // TODO: Flush the resource cache at the current depth and color framebuffer addresses for render-to-texture | 220 | // Flush the resource cache at the current depth and color framebuffer addresses for render-to-texture |
| 221 | const auto& regs = Pica::g_state.regs; | ||
| 222 | |||
| 223 | PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress(); | ||
| 224 | u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format) | ||
| 225 | * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); | ||
| 226 | |||
| 227 | PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); | ||
| 228 | u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format) | ||
| 229 | * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); | ||
| 230 | |||
| 231 | res_cache.NotifyFlush(cur_fb_color_addr, cur_fb_color_size); | ||
| 232 | res_cache.NotifyFlush(cur_fb_depth_addr, cur_fb_depth_size); | ||
| 209 | } | 233 | } |
| 210 | 234 | ||
| 211 | void RasterizerOpenGL::CommitFramebuffer() { | 235 | void RasterizerOpenGL::CommitFramebuffer() { |
| @@ -472,6 +496,9 @@ void RasterizerOpenGL::ReconfigureColorTexture(TextureInfo& texture, Pica::Regs: | |||
| 472 | glActiveTexture(GL_TEXTURE0); | 496 | glActiveTexture(GL_TEXTURE0); |
| 473 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | 497 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, |
| 474 | texture.gl_format, texture.gl_type, nullptr); | 498 | texture.gl_format, texture.gl_type, nullptr); |
| 499 | |||
| 500 | state.texture_units[0].texture_2d = 0; | ||
| 501 | state.Apply(); | ||
| 475 | } | 502 | } |
| 476 | 503 | ||
| 477 | void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height) { | 504 | void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height) { |
| @@ -491,7 +518,7 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: | |||
| 491 | case Pica::Regs::DepthFormat::D24: | 518 | case Pica::Regs::DepthFormat::D24: |
| 492 | internal_format = GL_DEPTH_COMPONENT24; | 519 | internal_format = GL_DEPTH_COMPONENT24; |
| 493 | texture.gl_format = GL_DEPTH_COMPONENT; | 520 | texture.gl_format = GL_DEPTH_COMPONENT; |
| 494 | texture.gl_type = GL_UNSIGNED_INT_24_8; | 521 | texture.gl_type = GL_UNSIGNED_INT; |
| 495 | break; | 522 | break; |
| 496 | 523 | ||
| 497 | case Pica::Regs::DepthFormat::D24S8: | 524 | case Pica::Regs::DepthFormat::D24S8: |
| @@ -513,6 +540,9 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: | |||
| 513 | glActiveTexture(GL_TEXTURE0); | 540 | glActiveTexture(GL_TEXTURE0); |
| 514 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | 541 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, |
| 515 | texture.gl_format, texture.gl_type, nullptr); | 542 | texture.gl_format, texture.gl_type, nullptr); |
| 543 | |||
| 544 | state.texture_units[0].texture_2d = 0; | ||
| 545 | state.Apply(); | ||
| 516 | } | 546 | } |
| 517 | 547 | ||
| 518 | void RasterizerOpenGL::SyncFramebuffer() { | 548 | void RasterizerOpenGL::SyncFramebuffer() { |
| @@ -652,6 +682,10 @@ void RasterizerOpenGL::SyncDepthTest() { | |||
| 652 | const auto& regs = Pica::g_state.regs; | 682 | const auto& regs = Pica::g_state.regs; |
| 653 | state.depth.test_enabled = (regs.output_merger.depth_test_enable == 1); | 683 | state.depth.test_enabled = (regs.output_merger.depth_test_enable == 1); |
| 654 | state.depth.test_func = PicaToGL::CompareFunc(regs.output_merger.depth_test_func); | 684 | state.depth.test_func = PicaToGL::CompareFunc(regs.output_merger.depth_test_func); |
| 685 | state.color_mask.red_enabled = regs.output_merger.red_enable; | ||
| 686 | state.color_mask.green_enabled = regs.output_merger.green_enable; | ||
| 687 | state.color_mask.blue_enabled = regs.output_merger.blue_enable; | ||
| 688 | state.color_mask.alpha_enabled = regs.output_merger.alpha_enable; | ||
| 655 | state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE; | 689 | state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE; |
| 656 | } | 690 | } |
| 657 | 691 | ||
| @@ -759,10 +793,10 @@ void RasterizerOpenGL::ReloadColorBuffer() { | |||
| 759 | for (int x = 0; x < fb_color_texture.width; ++x) { | 793 | for (int x = 0; x < fb_color_texture.width; ++x) { |
| 760 | const u32 coarse_y = y & ~7; | 794 | const u32 coarse_y = y & ~7; |
| 761 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; | 795 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; |
| 762 | u32 gl_px_idx = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel; | 796 | u32 gl_pixel_index = (x + y * fb_color_texture.width) * bytes_per_pixel; |
| 763 | 797 | ||
| 764 | u8* pixel = color_buffer + dst_offset; | 798 | u8* pixel = color_buffer + dst_offset; |
| 765 | memcpy(&temp_fb_color_buffer[gl_px_idx], pixel, bytes_per_pixel); | 799 | memcpy(&temp_fb_color_buffer[gl_pixel_index], pixel, bytes_per_pixel); |
| 766 | } | 800 | } |
| 767 | } | 801 | } |
| 768 | 802 | ||
| @@ -773,6 +807,9 @@ void RasterizerOpenGL::ReloadColorBuffer() { | |||
| 773 | glActiveTexture(GL_TEXTURE0); | 807 | glActiveTexture(GL_TEXTURE0); |
| 774 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_color_texture.width, fb_color_texture.height, | 808 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_color_texture.width, fb_color_texture.height, |
| 775 | fb_color_texture.gl_format, fb_color_texture.gl_type, temp_fb_color_buffer.get()); | 809 | fb_color_texture.gl_format, fb_color_texture.gl_type, temp_fb_color_buffer.get()); |
| 810 | |||
| 811 | state.texture_units[0].texture_2d = 0; | ||
| 812 | state.Apply(); | ||
| 776 | } | 813 | } |
| 777 | 814 | ||
| 778 | void RasterizerOpenGL::ReloadDepthBuffer() { | 815 | void RasterizerOpenGL::ReloadDepthBuffer() { |
| @@ -790,29 +827,29 @@ void RasterizerOpenGL::ReloadDepthBuffer() { | |||
| 790 | 827 | ||
| 791 | std::unique_ptr<u8[]> temp_fb_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]); | 828 | std::unique_ptr<u8[]> temp_fb_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]); |
| 792 | 829 | ||
| 793 | for (int y = 0; y < fb_depth_texture.height; ++y) { | 830 | u8* temp_fb_depth_data = bytes_per_pixel == 3 ? (temp_fb_depth_buffer.get() + 1) : temp_fb_depth_buffer.get(); |
| 794 | for (int x = 0; x < fb_depth_texture.width; ++x) { | 831 | |
| 795 | const u32 coarse_y = y & ~7; | 832 | if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) { |
| 796 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; | 833 | for (int y = 0; y < fb_depth_texture.height; ++y) { |
| 797 | u32 gl_px_idx = x + y * fb_depth_texture.width; | 834 | for (int x = 0; x < fb_depth_texture.width; ++x) { |
| 798 | 835 | const u32 coarse_y = y & ~7; | |
| 799 | switch (fb_depth_texture.format) { | 836 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; |
| 800 | case Pica::Regs::DepthFormat::D16: | 837 | u32 gl_pixel_index = (x + y * fb_depth_texture.width); |
| 801 | ((u16*)temp_fb_depth_buffer.get())[gl_px_idx] = Color::DecodeD16(depth_buffer + dst_offset); | 838 | |
| 802 | break; | 839 | u8* pixel = depth_buffer + dst_offset; |
| 803 | case Pica::Regs::DepthFormat::D24: | 840 | u32 depth_stencil = *(u32*)pixel; |
| 804 | ((u32*)temp_fb_depth_buffer.get())[gl_px_idx] = Color::DecodeD24(depth_buffer + dst_offset); | 841 | ((u32*)temp_fb_depth_data)[gl_pixel_index] = (depth_stencil << 8) | (depth_stencil >> 24); |
| 805 | break; | ||
| 806 | case Pica::Regs::DepthFormat::D24S8: | ||
| 807 | { | ||
| 808 | Math::Vec2<u32> depth_stencil = Color::DecodeD24S8(depth_buffer + dst_offset); | ||
| 809 | ((u32*)temp_fb_depth_buffer.get())[gl_px_idx] = (depth_stencil.x << 8) | depth_stencil.y; | ||
| 810 | break; | ||
| 811 | } | 842 | } |
| 812 | default: | 843 | } |
| 813 | LOG_CRITICAL(Render_OpenGL, "Unknown memory framebuffer depth format %x", fb_depth_texture.format); | 844 | } else { |
| 814 | UNIMPLEMENTED(); | 845 | for (int y = 0; y < fb_depth_texture.height; ++y) { |
| 815 | break; | 846 | for (int x = 0; x < fb_depth_texture.width; ++x) { |
| 847 | const u32 coarse_y = y & ~7; | ||
| 848 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; | ||
| 849 | u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp; | ||
| 850 | |||
| 851 | u8* pixel = depth_buffer + dst_offset; | ||
| 852 | memcpy(&temp_fb_depth_data[gl_pixel_index], pixel, bytes_per_pixel); | ||
| 816 | } | 853 | } |
| 817 | } | 854 | } |
| 818 | } | 855 | } |
| @@ -824,6 +861,9 @@ void RasterizerOpenGL::ReloadDepthBuffer() { | |||
| 824 | glActiveTexture(GL_TEXTURE0); | 861 | glActiveTexture(GL_TEXTURE0); |
| 825 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height, | 862 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height, |
| 826 | fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_fb_depth_buffer.get()); | 863 | fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_fb_depth_buffer.get()); |
| 864 | |||
| 865 | state.texture_units[0].texture_2d = 0; | ||
| 866 | state.Apply(); | ||
| 827 | } | 867 | } |
| 828 | 868 | ||
| 829 | void RasterizerOpenGL::CommitColorBuffer() { | 869 | void RasterizerOpenGL::CommitColorBuffer() { |
| @@ -842,15 +882,18 @@ void RasterizerOpenGL::CommitColorBuffer() { | |||
| 842 | glActiveTexture(GL_TEXTURE0); | 882 | glActiveTexture(GL_TEXTURE0); |
| 843 | glGetTexImage(GL_TEXTURE_2D, 0, fb_color_texture.gl_format, fb_color_texture.gl_type, temp_gl_color_buffer.get()); | 883 | glGetTexImage(GL_TEXTURE_2D, 0, fb_color_texture.gl_format, fb_color_texture.gl_type, temp_gl_color_buffer.get()); |
| 844 | 884 | ||
| 885 | state.texture_units[0].texture_2d = 0; | ||
| 886 | state.Apply(); | ||
| 887 | |||
| 845 | // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary. | 888 | // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary. |
| 846 | for (int y = 0; y < fb_color_texture.height; ++y) { | 889 | for (int y = 0; y < fb_color_texture.height; ++y) { |
| 847 | for (int x = 0; x < fb_color_texture.width; ++x) { | 890 | for (int x = 0; x < fb_color_texture.width; ++x) { |
| 848 | const u32 coarse_y = y & ~7; | 891 | const u32 coarse_y = y & ~7; |
| 849 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; | 892 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; |
| 850 | u32 gl_px_idx = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel; | 893 | u32 gl_pixel_index = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel; |
| 851 | 894 | ||
| 852 | u8* pixel = color_buffer + dst_offset; | 895 | u8* pixel = color_buffer + dst_offset; |
| 853 | memcpy(pixel, &temp_gl_color_buffer[gl_px_idx], bytes_per_pixel); | 896 | memcpy(pixel, &temp_gl_color_buffer[gl_pixel_index], bytes_per_pixel); |
| 854 | } | 897 | } |
| 855 | } | 898 | } |
| 856 | } | 899 | } |
| @@ -877,29 +920,32 @@ void RasterizerOpenGL::CommitDepthBuffer() { | |||
| 877 | glActiveTexture(GL_TEXTURE0); | 920 | glActiveTexture(GL_TEXTURE0); |
| 878 | glGetTexImage(GL_TEXTURE_2D, 0, fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_gl_depth_buffer.get()); | 921 | glGetTexImage(GL_TEXTURE_2D, 0, fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_gl_depth_buffer.get()); |
| 879 | 922 | ||
| 880 | for (int y = 0; y < fb_depth_texture.height; ++y) { | 923 | state.texture_units[0].texture_2d = 0; |
| 881 | for (int x = 0; x < fb_depth_texture.width; ++x) { | 924 | state.Apply(); |
| 882 | const u32 coarse_y = y & ~7; | 925 | |
| 883 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; | 926 | u8* temp_gl_depth_data = bytes_per_pixel == 3 ? (temp_gl_depth_buffer.get() + 1) : temp_gl_depth_buffer.get(); |
| 884 | u32 gl_px_idx = x + y * fb_depth_texture.width; | 927 | |
| 885 | 928 | if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) { | |
| 886 | switch (fb_depth_texture.format) { | 929 | for (int y = 0; y < fb_depth_texture.height; ++y) { |
| 887 | case Pica::Regs::DepthFormat::D16: | 930 | for (int x = 0; x < fb_depth_texture.width; ++x) { |
| 888 | Color::EncodeD16(((u16*)temp_gl_depth_buffer.get())[gl_px_idx], depth_buffer + dst_offset); | 931 | const u32 coarse_y = y & ~7; |
| 889 | break; | 932 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; |
| 890 | case Pica::Regs::DepthFormat::D24: | 933 | u32 gl_pixel_index = (x + y * fb_depth_texture.width); |
| 891 | Color::EncodeD24(((u32*)temp_gl_depth_buffer.get())[gl_px_idx], depth_buffer + dst_offset); | 934 | |
| 892 | break; | 935 | u8* pixel = depth_buffer + dst_offset; |
| 893 | case Pica::Regs::DepthFormat::D24S8: | 936 | u32 depth_stencil = ((u32*)temp_gl_depth_data)[gl_pixel_index]; |
| 894 | { | 937 | *(u32*)pixel = (depth_stencil >> 8) | (depth_stencil << 24); |
| 895 | u32 depth_stencil = ((u32*)temp_gl_depth_buffer.get())[gl_px_idx]; | ||
| 896 | Color::EncodeD24S8((depth_stencil >> 8), depth_stencil & 0xFF, depth_buffer + dst_offset); | ||
| 897 | break; | ||
| 898 | } | 938 | } |
| 899 | default: | 939 | } |
| 900 | LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer depth format %x", fb_depth_texture.format); | 940 | } else { |
| 901 | UNIMPLEMENTED(); | 941 | for (int y = 0; y < fb_depth_texture.height; ++y) { |
| 902 | break; | 942 | for (int x = 0; x < fb_depth_texture.width; ++x) { |
| 943 | const u32 coarse_y = y & ~7; | ||
| 944 | u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; | ||
| 945 | u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp; | ||
| 946 | |||
| 947 | u8* pixel = depth_buffer + dst_offset; | ||
| 948 | memcpy(pixel, &temp_gl_depth_data[gl_pixel_index], bytes_per_pixel); | ||
| 903 | } | 949 | } |
| 904 | } | 950 | } |
| 905 | } | 951 | } |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 9c5f38f94..3526e16d5 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -16,6 +16,11 @@ OpenGLState::OpenGLState() { | |||
| 16 | depth.test_func = GL_LESS; | 16 | depth.test_func = GL_LESS; |
| 17 | depth.write_mask = GL_TRUE; | 17 | depth.write_mask = GL_TRUE; |
| 18 | 18 | ||
| 19 | color_mask.red_enabled = GL_TRUE; | ||
| 20 | color_mask.green_enabled = GL_TRUE; | ||
| 21 | color_mask.blue_enabled = GL_TRUE; | ||
| 22 | color_mask.alpha_enabled = GL_TRUE; | ||
| 23 | |||
| 19 | stencil.test_enabled = false; | 24 | stencil.test_enabled = false; |
| 20 | stencil.test_func = GL_ALWAYS; | 25 | stencil.test_func = GL_ALWAYS; |
| 21 | stencil.test_ref = 0; | 26 | stencil.test_ref = 0; |
| @@ -77,6 +82,15 @@ void OpenGLState::Apply() { | |||
| 77 | glDepthMask(depth.write_mask); | 82 | glDepthMask(depth.write_mask); |
| 78 | } | 83 | } |
| 79 | 84 | ||
| 85 | // Color mask | ||
| 86 | if (color_mask.red_enabled != cur_state.color_mask.red_enabled || | ||
| 87 | color_mask.green_enabled != cur_state.color_mask.green_enabled || | ||
| 88 | color_mask.blue_enabled != cur_state.color_mask.blue_enabled || | ||
| 89 | color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) { | ||
| 90 | glColorMask(color_mask.red_enabled, color_mask.green_enabled, | ||
| 91 | color_mask.blue_enabled, color_mask.alpha_enabled); | ||
| 92 | } | ||
| 93 | |||
| 80 | // Stencil test | 94 | // Stencil test |
| 81 | if (stencil.test_enabled != cur_state.stencil.test_enabled) { | 95 | if (stencil.test_enabled != cur_state.stencil.test_enabled) { |
| 82 | if (stencil.test_enabled) { | 96 | if (stencil.test_enabled) { |
| @@ -87,8 +101,8 @@ void OpenGLState::Apply() { | |||
| 87 | } | 101 | } |
| 88 | 102 | ||
| 89 | if (stencil.test_func != cur_state.stencil.test_func || | 103 | if (stencil.test_func != cur_state.stencil.test_func || |
| 90 | stencil.test_ref != cur_state.stencil.test_ref || | 104 | stencil.test_ref != cur_state.stencil.test_ref || |
| 91 | stencil.test_mask != cur_state.stencil.test_mask) { | 105 | stencil.test_mask != cur_state.stencil.test_mask) { |
| 92 | glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask); | 106 | glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask); |
| 93 | } | 107 | } |
| 94 | 108 | ||
| @@ -112,17 +126,19 @@ void OpenGLState::Apply() { | |||
| 112 | } | 126 | } |
| 113 | 127 | ||
| 114 | if (blend.color.red != cur_state.blend.color.red || | 128 | if (blend.color.red != cur_state.blend.color.red || |
| 115 | blend.color.green != cur_state.blend.color.green || | 129 | blend.color.green != cur_state.blend.color.green || |
| 116 | blend.color.blue != cur_state.blend.color.blue || | 130 | blend.color.blue != cur_state.blend.color.blue || |
| 117 | blend.color.alpha != cur_state.blend.color.alpha) { | 131 | blend.color.alpha != cur_state.blend.color.alpha) { |
| 118 | glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); | 132 | glBlendColor(blend.color.red, blend.color.green, |
| 133 | blend.color.blue, blend.color.alpha); | ||
| 119 | } | 134 | } |
| 120 | 135 | ||
| 121 | if (blend.src_rgb_func != cur_state.blend.src_rgb_func || | 136 | if (blend.src_rgb_func != cur_state.blend.src_rgb_func || |
| 122 | blend.dst_rgb_func != cur_state.blend.dst_rgb_func || | 137 | blend.dst_rgb_func != cur_state.blend.dst_rgb_func || |
| 123 | blend.src_a_func != cur_state.blend.src_a_func || | 138 | blend.src_a_func != cur_state.blend.src_a_func || |
| 124 | blend.dst_a_func != cur_state.blend.dst_a_func) { | 139 | blend.dst_a_func != cur_state.blend.dst_a_func) { |
| 125 | glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, blend.dst_a_func); | 140 | glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, |
| 141 | blend.src_a_func, blend.dst_a_func); | ||
| 126 | } | 142 | } |
| 127 | 143 | ||
| 128 | if (logic_op != cur_state.logic_op) { | 144 | if (logic_op != cur_state.logic_op) { |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 6b97721d6..26b916360 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -20,6 +20,13 @@ public: | |||
| 20 | } depth; | 20 | } depth; |
| 21 | 21 | ||
| 22 | struct { | 22 | struct { |
| 23 | GLboolean red_enabled; | ||
| 24 | GLboolean green_enabled; | ||
| 25 | GLboolean blue_enabled; | ||
| 26 | GLboolean alpha_enabled; | ||
| 27 | } color_mask; // GL_COLOR_WRITEMASK | ||
| 28 | |||
| 29 | struct { | ||
| 23 | bool test_enabled; // GL_STENCIL_TEST | 30 | bool test_enabled; // GL_STENCIL_TEST |
| 24 | GLenum test_func; // GL_STENCIL_FUNC | 31 | GLenum test_func; // GL_STENCIL_FUNC |
| 25 | GLint test_ref; // GL_STENCIL_REF | 32 | GLint test_ref; // GL_STENCIL_REF |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 382aeaa05..3399ca123 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -170,6 +170,9 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||
| 170 | texture.gl_format, texture.gl_type, framebuffer_data); | 170 | texture.gl_format, texture.gl_type, framebuffer_data); |
| 171 | 171 | ||
| 172 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | 172 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
| 173 | |||
| 174 | state.texture_units[0].texture_2d = 0; | ||
| 175 | state.Apply(); | ||
| 173 | } | 176 | } |
| 174 | 177 | ||
| 175 | /** | 178 | /** |
| @@ -239,6 +242,9 @@ void RendererOpenGL::InitOpenGLObjects() { | |||
| 239 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 242 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 240 | } | 243 | } |
| 241 | 244 | ||
| 245 | state.texture_units[0].texture_2d = 0; | ||
| 246 | state.Apply(); | ||
| 247 | |||
| 242 | hw_rasterizer->InitObjects(); | 248 | hw_rasterizer->InitObjects(); |
| 243 | } | 249 | } |
| 244 | 250 | ||
| @@ -370,6 +376,8 @@ void RendererOpenGL::Init() { | |||
| 370 | } | 376 | } |
| 371 | 377 | ||
| 372 | LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION)); | 378 | LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION)); |
| 379 | LOG_INFO(Render_OpenGL, "GL_VENDOR: %s", glGetString(GL_VENDOR)); | ||
| 380 | LOG_INFO(Render_OpenGL, "GL_RENDERER: %s", glGetString(GL_RENDERER)); | ||
| 373 | InitOpenGLObjects(); | 381 | InitOpenGLObjects(); |
| 374 | } | 382 | } |
| 375 | 383 | ||