mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-11-11 14:39:32 +01:00
Compare commits
6 commits
fe308f5644
...
fd87c977bc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd87c977bc | ||
|
|
a8ac300e9b | ||
|
|
811f506606 | ||
|
|
b16b7a93b1 | ||
|
|
2f288b7f0d | ||
|
|
19d420f183 |
29 changed files with 533 additions and 467 deletions
|
|
@ -25,3 +25,4 @@ Policies and information on development.
|
|||
- **[AI and LLM Usage](./policies/AI.md)**
|
||||
- **[Release Policy](./policies/Release.md)**
|
||||
- **[Coding guidelines](./policies/Coding.md)**
|
||||
- **[Coding Style guidelines](./policies/CodingStyle.md)**
|
||||
|
|
|
|||
|
|
@ -1,126 +1,70 @@
|
|||
# Coding guidelines
|
||||
|
||||
These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go.
|
||||
These are **not** stylistic guidelines, they're, for the most part, suggestions on how to architecture new systems or improve upon the existing codebase.
|
||||
|
||||
But for new developers you may find that following these guidelines will make everything x10 easier.
|
||||
# Foreword
|
||||
|
||||
## Naming conventions
|
||||
Don't try to micro-optimize out of the get go, while yes, most of the code is pretty, subpar, most of these are aftertoughts and details that can be glossed over **generally**.
|
||||
|
||||
Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`.
|
||||
Architectural issues are more important, for example an API returning a `std::string` is not as efficient as one that operates on `std::string_view` directly (cost of constructing an `std::string` w/o small-string optimization and all of that).
|
||||
|
||||
Except for Qt MOC where `functionName` is preferred.
|
||||
Regardless of the details, try to keep things simple. As a general rule of thumb.
|
||||
|
||||
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
|
||||
# C++ guidelines
|
||||
|
||||
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
|
||||
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
|
||||
- `X`: Defined by some utility headers, avoid.
|
||||
- `_`: Defined by gettext, avoid.
|
||||
- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants.
|
||||
- `TR`: Used by some weird `<ucontext.h>` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific).
|
||||
Everyone has their own way of viewing good/bad C++ practices, my general outline:
|
||||
|
||||
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
|
||||
- At your disposal you may use `boost::container::static_vector<>` (beware it has a ctor/initialization cost which goes up the more elements you add).
|
||||
- Or you may use `boost::container::small_vector<>` (which has an initialization cost as well, and will use extra book-keeping for heap, try to keep a balance).
|
||||
- Don't use `[[likely]]` or `[[unlikely]]`; PGO builds exist for that.
|
||||
- Don't use inline assembly to try to outsmart the compiler unless you're 100% sure the assembly you're writing is actually good.
|
||||
- And if so, try to restructure your C++ code so the compiler vectorizes it/makes it better, right?
|
||||
- Or if that fails, use intrinsics instead of raw `asm volatile`.
|
||||
- Use `std::optional<>` instead of `std::unique_ptr<>` if possible.
|
||||
- `std::unique_ptr<>` carries indirection cost due to it being memory allocated on the heap.
|
||||
- It isn't often that objects that contain `std::unique_ptr<>`, are allocated on the heap themselves, allocating even more things on the heap seems redundant.
|
||||
- Avoid `std::recursive_mutex` at all costs.
|
||||
- It's basically implemented as a linked list most of the time and has HEAVY performance penalties.
|
||||
- Exploit the fact `std::atomic<uint32_t>/std::atomic<int32_t>` is basically free on most arches that matter.
|
||||
- In x86_64, an atomic `uint32_t` is basically `mov [m32], r32`, which is essentially free/cheap.
|
||||
- Avoid template parameters unless you really need them.
|
||||
- For small inlineable functions this is fine, for more complex ones, please consider the generated assembly.
|
||||
- Dont make your own memcpy/memset/strcpy/strncpy/etc.
|
||||
- Seriously DON'T DO THIS. You will NOT beat the compiler.
|
||||
- Nor 30 years of writing optimized `mem*`.
|
||||
- If your code is slow, don't blame `mem*`, blame your code.
|
||||
- Try to avoid using `virtual` since vtable indirection has a cost
|
||||
- Avoid `dynamic_cast` and `typeid` at all costs.
|
||||
- The reason is because the project has `-fno-rtti` disabled by default, due to the costs of dynamic polymorphism.
|
||||
- Always copy-on-value for objects with `sizeof(void *) >= sizeof(T) * 2`, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual.
|
||||
- Try using move semantics instead of references, whenever possible.
|
||||
- Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function.
|
||||
- Don't save a reference in structures of a parent object, i.e:
|
||||
```c++
|
||||
struct Child {
|
||||
Parent& parent;
|
||||
void Mehod() {
|
||||
parent.Something();
|
||||
}
|
||||
};
|
||||
```
|
||||
- Instead you can do the following:
|
||||
```c++
|
||||
struct Child {
|
||||
void Mehod(Parent& parent) {
|
||||
parent.Something();
|
||||
}
|
||||
};
|
||||
```
|
||||
- This reduces the amount of pointers you have lying around, and also works better because of the aforementioned cheapness of parameter functions.
|
||||
|
||||
Try not using hungarian notation, if you're able.
|
||||
# Engineering guidelines
|
||||
|
||||
## Formatting
|
||||
Coding isn't also writing stuff but architecturing stuff, consider the following:
|
||||
|
||||
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
|
||||
|
||||
Do not put if/while/etc braces after lines:
|
||||
|
||||
```c++
|
||||
// no dont do this
|
||||
// this is more lines of code for no good reason (why braces need their separate lines?)
|
||||
// and those take space in someone's screen, cumulatively
|
||||
if (thing)
|
||||
{ //<--
|
||||
some(); // ...
|
||||
} //<-- 2 lines of code for basically "opening" and "closing" an statment
|
||||
|
||||
// do this
|
||||
if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code
|
||||
// that's being closed
|
||||
some(); // ...
|
||||
} //<-- only one line, and it's clearer since you know its closing something [...]
|
||||
|
||||
// or this, albeit the extra line isn't needed (at your discretion of course)
|
||||
if (thing)
|
||||
some(); // ...
|
||||
|
||||
// this is also ok, keeps things in one line and makes it extremely clear
|
||||
if (thing) some();
|
||||
|
||||
// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments
|
||||
// in a single line, doing this will definitely ruin someone's day - just do the thing below
|
||||
// vvv
|
||||
if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo());
|
||||
// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think
|
||||
// that it makes the code more readable (the situation may be rare however)
|
||||
|
||||
// Wow so much clearer! Now I can actually see what each statment is meant to do!
|
||||
if (thing) {
|
||||
some();
|
||||
thing();
|
||||
a2(a1(), y1(), j1());
|
||||
do_complex_shit(wa(), wo(), ploo());
|
||||
}
|
||||
```
|
||||
|
||||
Brace rules are lax, if you can get the point across, do it:
|
||||
|
||||
```c++
|
||||
// this is fine
|
||||
do {
|
||||
if (thing) {
|
||||
return 0;
|
||||
}
|
||||
} while (other);
|
||||
|
||||
// this is also ok --- albeit a bit more dense
|
||||
do if (thing) return 0; while (other);
|
||||
|
||||
// ok as well
|
||||
do {
|
||||
if (thing) return 0;
|
||||
} while (other);
|
||||
```
|
||||
|
||||
There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line).
|
||||
|
||||
```c++
|
||||
// someone is going to be mad due to this
|
||||
SDL_AudioSpec obtained;
|
||||
device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
||||
|
||||
// maybe consider this
|
||||
SDL_AudioSpec obtained;
|
||||
if (device_name.empty()) {
|
||||
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
|
||||
} else {
|
||||
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
||||
}
|
||||
|
||||
// or this is fine as well
|
||||
SDL_AudioSpec obtained;
|
||||
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
|
||||
```
|
||||
|
||||
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
|
||||
|
||||
```c++
|
||||
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
|
||||
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine
|
||||
return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment
|
||||
void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as *
|
||||
```
|
||||
|
||||
And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally:
|
||||
|
||||
```c++
|
||||
// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS.
|
||||
template<typename T, size_t N, size_t C, size_t P> inline void what() const noexcept;
|
||||
|
||||
// OK use parameters like a normal person, don't be afraid to use them :)
|
||||
template<typename T> inline void what(size_t n, size_t c, size_t p) const noexcept;
|
||||
```
|
||||
- Try to reduce dependency on... dependencies
|
||||
- While some dependencies are useful `boost::container` and `fmt` to name a few, remember each dependency added incurs a cost
|
||||
- It may also be subpar with a hand rolled implementation, biggest exemplar of this is `spirv-tools` providing subpar SPIRV optimizations in comparison to the in-house optimizer.
|
||||
- Try to rely less on indirection for architecturing systems
|
||||
- If the underlying HLE kernel emulation requires it, try making a solution that keeps things local
|
||||
- For example, there isn't a need for file descriptors to each be pointer, when they could be a fixed table size with elements that may be emplaced at will.
|
||||
|
|
|
|||
126
docs/policies/CodingStyle.md
Normal file
126
docs/policies/CodingStyle.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Coding Style guidelines
|
||||
|
||||
These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go.
|
||||
|
||||
But for new developers you may find that following these guidelines will make everything x10 easier.
|
||||
|
||||
## Naming conventions
|
||||
|
||||
Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`.
|
||||
|
||||
Except for Qt MOC where `functionName` is preferred.
|
||||
|
||||
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
|
||||
|
||||
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
|
||||
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
|
||||
- `X`: Defined by some utility headers, avoid.
|
||||
- `_`: Defined by gettext, avoid.
|
||||
- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants.
|
||||
- `TR`: Used by some weird `<ucontext.h>` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific).
|
||||
|
||||
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
|
||||
|
||||
Try not using hungarian notation, if you're able.
|
||||
|
||||
## Formatting
|
||||
|
||||
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
|
||||
|
||||
Do not put if/while/etc braces after lines:
|
||||
|
||||
```c++
|
||||
// no dont do this
|
||||
// this is more lines of code for no good reason (why braces need their separate lines?)
|
||||
// and those take space in someone's screen, cumulatively
|
||||
if (thing)
|
||||
{ //<--
|
||||
some(); // ...
|
||||
} //<-- 2 lines of code for basically "opening" and "closing" an statment
|
||||
|
||||
// do this
|
||||
if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code
|
||||
// that's being closed
|
||||
some(); // ...
|
||||
} //<-- only one line, and it's clearer since you know its closing something [...]
|
||||
|
||||
// or this, albeit the extra line isn't needed (at your discretion of course)
|
||||
if (thing)
|
||||
some(); // ...
|
||||
|
||||
// this is also ok, keeps things in one line and makes it extremely clear
|
||||
if (thing) some();
|
||||
|
||||
// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments
|
||||
// in a single line, doing this will definitely ruin someone's day - just do the thing below
|
||||
// vvv
|
||||
if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo());
|
||||
// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think
|
||||
// that it makes the code more readable (the situation may be rare however)
|
||||
|
||||
// Wow so much clearer! Now I can actually see what each statment is meant to do!
|
||||
if (thing) {
|
||||
some();
|
||||
thing();
|
||||
a2(a1(), y1(), j1());
|
||||
do_complex_shit(wa(), wo(), ploo());
|
||||
}
|
||||
```
|
||||
|
||||
Brace rules are lax, if you can get the point across, do it:
|
||||
|
||||
```c++
|
||||
// this is fine
|
||||
do {
|
||||
if (thing) {
|
||||
return 0;
|
||||
}
|
||||
} while (other);
|
||||
|
||||
// this is also ok --- albeit a bit more dense
|
||||
do if (thing) return 0; while (other);
|
||||
|
||||
// ok as well
|
||||
do {
|
||||
if (thing) return 0;
|
||||
} while (other);
|
||||
```
|
||||
|
||||
There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line).
|
||||
|
||||
```c++
|
||||
// someone is going to be mad due to this
|
||||
SDL_AudioSpec obtained;
|
||||
device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
||||
|
||||
// maybe consider this
|
||||
SDL_AudioSpec obtained;
|
||||
if (device_name.empty()) {
|
||||
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
|
||||
} else {
|
||||
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
||||
}
|
||||
|
||||
// or this is fine as well
|
||||
SDL_AudioSpec obtained;
|
||||
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
|
||||
```
|
||||
|
||||
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
|
||||
|
||||
```c++
|
||||
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
|
||||
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine
|
||||
return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment
|
||||
void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as *
|
||||
```
|
||||
|
||||
And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally:
|
||||
|
||||
```c++
|
||||
// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS.
|
||||
template<typename T, size_t N, size_t C, size_t P> inline void what() const noexcept;
|
||||
|
||||
// OK use parameters like a normal person, don't be afraid to use them :)
|
||||
template<typename T> inline void what(size_t n, size_t c, size_t p) const noexcept;
|
||||
```
|
||||
|
|
@ -27,7 +27,9 @@ void CpuManager::Initialize() {
|
|||
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
||||
gpu_barrier.emplace(num_cores + 1);
|
||||
for (std::size_t core = 0; core < num_cores; core++)
|
||||
core_data[core].host_thread = std::jthread(&CpuManager::RunThread, core);
|
||||
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) {
|
||||
RunThread(token, core);
|
||||
});
|
||||
}
|
||||
|
||||
void CpuManager::Shutdown() {
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@
|
|||
namespace Tegra::Control {
|
||||
|
||||
ChannelState::Payload::Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state)
|
||||
: maxwell_3d(system, memory_manager)
|
||||
: maxwell_3d(memory_manager)
|
||||
, fermi_2d(memory_manager)
|
||||
, kepler_compute(system, memory_manager)
|
||||
, maxwell_dma(system, memory_manager)
|
||||
, kepler_memory(system, memory_manager)
|
||||
, nv01_timer(system, memory_manager)
|
||||
, kepler_compute(memory_manager)
|
||||
, maxwell_dma(memory_manager)
|
||||
, kepler_memory(memory_manager)
|
||||
, nv01_timer(memory_manager)
|
||||
, dma_pusher(system, memory_manager, channel_state)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
|
@ -6,8 +9,11 @@
|
|||
namespace VideoCommon {
|
||||
|
||||
ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
|
||||
: maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute},
|
||||
gpu_memory{*channel_state.memory_manager}, program_id{channel_state.program_id} {}
|
||||
: maxwell3d{channel_state.payload->maxwell_3d}
|
||||
, kepler_compute{channel_state.payload->kepler_compute}
|
||||
, gpu_memory{*channel_state.memory_manager}
|
||||
, program_id{channel_state.program_id}
|
||||
{}
|
||||
|
||||
template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& c
|
|||
AddressSpaceRef new_gpu_mem_ref{
|
||||
.ref_count = 1,
|
||||
.storage_id = address_spaces.size(),
|
||||
.gpu_memory = channel.memory_manager.get(),
|
||||
.gpu_memory = channel.memory_manager,
|
||||
};
|
||||
address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
|
||||
OnGPUASRegister(channel.memory_manager->GetID());
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
|
|
@ -12,11 +12,8 @@
|
|||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra::Control {
|
||||
Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
|
||||
|
||||
Scheduler::~Scheduler() = default;
|
||||
|
||||
void Scheduler::Push(s32 channel, CommandList&& entries) {
|
||||
void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) {
|
||||
std::shared_ptr<ChannelState> channel_state;
|
||||
{
|
||||
std::unique_lock lk(scheduling_guard);
|
||||
|
|
@ -27,8 +24,8 @@ void Scheduler::Push(s32 channel, CommandList&& entries) {
|
|||
}
|
||||
// Process commands outside the lock to reduce contention.
|
||||
// Multiple channels can prepare their commands in parallel.
|
||||
channel_state->dma_pusher->Push(std::move(entries));
|
||||
channel_state->dma_pusher->DispatchCalls();
|
||||
channel_state->payload->dma_pusher.Push(std::move(entries));
|
||||
channel_state->payload->dma_pusher.DispatchCalls();
|
||||
}
|
||||
|
||||
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
|
||||
|
|
|
|||
|
|
@ -22,17 +22,13 @@ struct ChannelState;
|
|||
|
||||
class Scheduler {
|
||||
public:
|
||||
explicit Scheduler(GPU& gpu_);
|
||||
~Scheduler();
|
||||
|
||||
void Push(s32 channel, CommandList&& entries);
|
||||
void Push(GPU& gpu, s32 channel, CommandList&& entries);
|
||||
|
||||
void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
|
||||
|
||||
private:
|
||||
ankerl::unordered_dense::map<s32, std::shared_ptr<ChannelState>> channels;
|
||||
std::mutex scheduling_guard;
|
||||
GPU& gpu;
|
||||
};
|
||||
|
||||
} // namespace Control
|
||||
|
|
|
|||
|
|
@ -179,9 +179,9 @@ void DmaPusher::CallMethod(u32 argument) {
|
|||
if (!subchannel->execution_mask[dma_state.method]) {
|
||||
subchannel->method_sink.emplace_back(dma_state.method, argument);
|
||||
} else {
|
||||
subchannel->ConsumeSink();
|
||||
subchannel->ConsumeSink(system);
|
||||
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
||||
subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call);
|
||||
subchannel->CallMethod(system, dma_state.method, argument, dma_state.is_last_call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -191,9 +191,9 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) {
|
|||
puller.CallMultiMethod(*this, dma_state.method, dma_state.subchannel, base_start, num_methods, dma_state.method_count);
|
||||
} else {
|
||||
auto subchannel = subchannels[dma_state.subchannel];
|
||||
subchannel->ConsumeSink();
|
||||
subchannel->ConsumeSink(system);
|
||||
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
||||
subchannel->CallMultiMethod(dma_state.method, base_start, num_methods, dma_state.method_count);
|
||||
subchannel->CallMultiMethod(system, dma_state.method, base_start, num_methods, dma_state.method_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@
|
|||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
enum class EngineTypes : u32 {
|
||||
|
|
@ -28,28 +32,25 @@ public:
|
|||
virtual ~EngineInterface() = default;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
virtual void CallMethod(u32 method, u32 method_argument, bool is_last_call) = 0;
|
||||
virtual void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) = 0;
|
||||
|
||||
/// Write multiple values to the register identified by method.
|
||||
virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) = 0;
|
||||
virtual void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) = 0;
|
||||
|
||||
void ConsumeSink() {
|
||||
if (method_sink.empty()) {
|
||||
return;
|
||||
void ConsumeSink(Core::System& system) {
|
||||
if (!method_sink.empty()) {
|
||||
ConsumeSinkImpl(system);
|
||||
}
|
||||
ConsumeSinkImpl();
|
||||
}
|
||||
|
||||
std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{};
|
||||
std::vector<std::pair<u32, u32>> method_sink{};
|
||||
bool current_dirty{};
|
||||
GPUVAddr current_dma_segment;
|
||||
|
||||
bool current_dirty{};
|
||||
protected:
|
||||
virtual void ConsumeSinkImpl() {
|
||||
virtual void ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
CallMethod(method, value, true);
|
||||
CallMethod(system, method, value, true);
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,9 +36,8 @@ void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
|||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Fermi2D register, increase the size of the Regs structure");
|
||||
void Fermi2D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Fermi2D register, increase the size of the Regs structure");
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
||||
if (method == FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1) {
|
||||
|
|
@ -46,13 +45,13 @@ void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
|||
}
|
||||
}
|
||||
|
||||
void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
void Fermi2D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
for (u32 i = 0; i < amount; ++i) {
|
||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
||||
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Fermi2D::ConsumeSinkImpl() {
|
||||
void Fermi2D::ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
|
|
@ -60,8 +59,7 @@ void Fermi2D::ConsumeSinkImpl() {
|
|||
}
|
||||
|
||||
void Fermi2D::Blit() {
|
||||
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
|
||||
regs.src.Address(), regs.dst.Address());
|
||||
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", regs.src.Address(), regs.dst.Address());
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy");
|
||||
UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero");
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -44,11 +47,10 @@ public:
|
|||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
||||
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||
|
||||
/// Write multiple values to the register identified by method.
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||
|
||||
enum class Origin : u32 {
|
||||
Center = 0,
|
||||
|
|
@ -311,7 +313,7 @@ private:
|
|||
/// registers.
|
||||
void Blit();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
|
||||
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
|
||||
KeplerCompute::KeplerCompute(MemoryManager& memory_manager_)
|
||||
: memory_manager{memory_manager_}
|
||||
, upload_state{memory_manager, regs.upload}
|
||||
{
|
||||
execution_mask.reset();
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
|
||||
|
|
@ -31,16 +33,15 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
|
|||
upload_state.BindRasterizer(rasterizer);
|
||||
}
|
||||
|
||||
void KeplerCompute::ConsumeSinkImpl() {
|
||||
void KeplerCompute::ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||
void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
||||
|
|
@ -78,8 +79,7 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal
|
|||
}
|
||||
}
|
||||
|
||||
void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
switch (method) {
|
||||
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
||||
upload_address = current_dma_segment;
|
||||
|
|
@ -87,7 +87,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun
|
|||
return;
|
||||
default:
|
||||
for (u32 i = 0; i < amount; i++) {
|
||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
||||
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -42,7 +45,7 @@ namespace Tegra::Engines {
|
|||
|
||||
class KeplerCompute final : public EngineInterface {
|
||||
public:
|
||||
explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
|
||||
explicit KeplerCompute(MemoryManager& memory_manager);
|
||||
~KeplerCompute();
|
||||
|
||||
/// Binds a rasterizer to this engine.
|
||||
|
|
@ -199,11 +202,10 @@ public:
|
|||
"KeplerCompute LaunchParams has wrong size");
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
||||
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||
|
||||
/// Write multiple values to the register identified by method.
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||
|
||||
std::optional<GPUVAddr> GetIndirectComputeAddress() const {
|
||||
return indirect_compute;
|
||||
|
|
@ -212,7 +214,7 @@ public:
|
|||
private:
|
||||
void ProcessLaunch();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
|
||||
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
||||
Texture::TICEntry GetTICEntry(u32 tic_index) const;
|
||||
|
|
@ -220,7 +222,6 @@ private:
|
|||
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
||||
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
||||
|
||||
Core::System& system;
|
||||
MemoryManager& memory_manager;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
Upload::State upload_state;
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@
|
|||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerMemory::KeplerMemory(Core::System& system_, MemoryManager& memory_manager)
|
||||
: system{system_}, upload_state{memory_manager, regs.upload} {}
|
||||
KeplerMemory::KeplerMemory(MemoryManager& memory_manager)
|
||||
: upload_state{memory_manager, regs.upload}
|
||||
{}
|
||||
|
||||
KeplerMemory::~KeplerMemory() = default;
|
||||
|
||||
|
|
@ -27,16 +28,15 @@ void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
|||
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
|
||||
}
|
||||
|
||||
void KeplerMemory::ConsumeSinkImpl() {
|
||||
void KeplerMemory::ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid KeplerMemory register, increase the size of the Regs structure");
|
||||
void KeplerMemory::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerMemory register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
||||
|
|
@ -52,15 +52,14 @@ void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call
|
|||
}
|
||||
}
|
||||
|
||||
void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
void KeplerMemory::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
switch (method) {
|
||||
case KEPLERMEMORY_REG_INDEX(data):
|
||||
upload_state.ProcessData(base_start, amount);
|
||||
return;
|
||||
default:
|
||||
for (u32 i = 0; i < amount; i++) {
|
||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
||||
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -36,18 +39,17 @@ namespace Tegra::Engines {
|
|||
|
||||
class KeplerMemory final : public EngineInterface {
|
||||
public:
|
||||
explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager);
|
||||
explicit KeplerMemory(MemoryManager& memory_manager);
|
||||
~KeplerMemory() override;
|
||||
|
||||
/// Binds a rasterizer to this engine.
|
||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
||||
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||
|
||||
/// Write multiple values to the register identified by method.
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0x7F;
|
||||
|
|
@ -73,9 +75,7 @@ public:
|
|||
} regs{};
|
||||
|
||||
private:
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
Core::System& system;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
Upload::State upload_state;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -25,9 +25,8 @@ namespace Tegra::Engines {
|
|||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
|
||||
Maxwell3D::Maxwell3D(MemoryManager& memory_manager_)
|
||||
: draw_manager()
|
||||
, system{system_}
|
||||
, memory_manager{memory_manager_}
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
, macro_engine(bool(Settings::values.disable_macro_jit))
|
||||
|
|
@ -201,11 +200,10 @@ bool Maxwell3D::IsMethodExecutable(u32 method) {
|
|||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
|
||||
void Maxwell3D::ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call) {
|
||||
if (executing_macro == 0) {
|
||||
// A macro call must begin by writing the macro method's register, not its argument.
|
||||
ASSERT_MSG((method % 2) == 0,
|
||||
"Can't start macro execution by writing to the ARGS register");
|
||||
ASSERT((method % 2) == 0 && "Can't start macro execution by writing to the ARGS register");
|
||||
executing_macro = method;
|
||||
}
|
||||
|
||||
|
|
@ -219,8 +217,8 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool
|
|||
|
||||
// Call the macro when there are no more parameters in the command buffer
|
||||
if (is_last_call) {
|
||||
ConsumeSink();
|
||||
CallMacroMethod(executing_macro, macro_params);
|
||||
ConsumeSink(system);
|
||||
CallMacroMethod(system, executing_macro, macro_params);
|
||||
macro_params.clear();
|
||||
macro_addresses.clear();
|
||||
macro_segments.clear();
|
||||
|
|
@ -298,7 +296,7 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
|
|||
return argument;
|
||||
}
|
||||
|
||||
void Maxwell3D::ConsumeSinkImpl() {
|
||||
void Maxwell3D::ConsumeSinkImpl(Core::System& system) {
|
||||
const auto control = shadow_state.shadow_ram_control;
|
||||
if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
|
|
@ -391,7 +389,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
|||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
|
||||
void Maxwell3D::CallMacroMethod(Core::System& system, u32 method, const std::vector<u32>& parameters) {
|
||||
// Reset the current macro.
|
||||
executing_macro = 0;
|
||||
|
||||
|
|
@ -400,11 +398,11 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
|
|||
((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
|
||||
|
||||
// Execute the current macro.
|
||||
macro_engine.Execute(*this, macro_positions[entry], parameters);
|
||||
macro_engine.Execute(system, *this, macro_positions[entry], parameters);
|
||||
draw_manager.DrawDeferred(*this);
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
void Maxwell3D::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
// It is an error to write to a register other than the current macro's ARG register before
|
||||
// it has finished execution.
|
||||
if (executing_macro != 0) {
|
||||
|
|
@ -414,7 +412,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
|||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||
// uploaded to the GPU during initialization.
|
||||
if (method >= MacroRegistersStart) {
|
||||
ProcessMacro(method, &method_argument, 1, is_last_call);
|
||||
ProcessMacro(system, method, &method_argument, 1, is_last_call);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -424,12 +422,11 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
|||
ProcessMethodCall(method, argument, method_argument, is_last_call);
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||
// uploaded to the GPU during initialization.
|
||||
if (method >= MacroRegistersStart) {
|
||||
ProcessMacro(method, base_start, amount, amount == methods_pending);
|
||||
ProcessMacro(system, method, base_start, amount, amount == methods_pending);
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
|
|
@ -458,7 +455,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
|||
}
|
||||
default:
|
||||
for (u32 i = 0; i < amount; i++) {
|
||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
||||
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -480,7 +477,7 @@ void Maxwell3D::ProcessFirmwareCall4() {
|
|||
regs.shadow_scratch[0] = 1;
|
||||
}
|
||||
|
||||
void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
|
||||
void Maxwell3D::StampQueryResult(Core::System& system, u64 payload, bool long_query) {
|
||||
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
|
||||
if (long_query) {
|
||||
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Tegra::Engines {
|
|||
|
||||
class Maxwell3D final : public EngineInterface {
|
||||
public:
|
||||
explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager);
|
||||
explicit Maxwell3D(MemoryManager& memory_manager);
|
||||
~Maxwell3D();
|
||||
|
||||
/// Binds a rasterizer to this engine.
|
||||
|
|
@ -3129,11 +3129,10 @@ public:
|
|||
u32 GetRegisterValue(u32 method) const;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
||||
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||
|
||||
/// Write multiple values to the register identified by method.
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||
|
||||
bool ShouldExecute() const {
|
||||
return execute_on;
|
||||
|
|
@ -3190,13 +3189,13 @@ public:
|
|||
private:
|
||||
void InitializeRegisterDefaults();
|
||||
|
||||
void ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call);
|
||||
void ProcessMacro(Core::System& system, u32 method, const u32* base_start, u32 amount, bool is_last_call);
|
||||
|
||||
u32 ProcessShadowRam(u32 method, u32 argument);
|
||||
|
||||
void ProcessDirtyRegisters(u32 method, u32 argument);
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
|
||||
void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
|
||||
|
||||
|
|
@ -3212,7 +3211,7 @@ private:
|
|||
* @param method Method to call
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
|
||||
void CallMacroMethod(Core::System& system, u32 method, const std::vector<u32>& parameters);
|
||||
|
||||
/// Handles writes to the macro uploading register.
|
||||
void ProcessMacroUpload(u32 data);
|
||||
|
|
@ -3227,7 +3226,7 @@ private:
|
|||
void ProcessQueryGet();
|
||||
|
||||
/// Writes the query result accordingly.
|
||||
void StampQueryResult(u64 payload, bool long_query);
|
||||
void StampQueryResult(Core::System& system, u64 payload, bool long_query);
|
||||
|
||||
/// Handles conditional rendering.
|
||||
void ProcessQueryCondition();
|
||||
|
|
@ -3242,7 +3241,6 @@ private:
|
|||
|
||||
bool IsMethodExecutable(u32 method);
|
||||
|
||||
Core::System& system;
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ namespace Tegra::Engines {
|
|||
|
||||
using namespace Texture;
|
||||
|
||||
MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
|
||||
: system{system_}, memory_manager{memory_manager_} {
|
||||
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager_)
|
||||
: memory_manager{memory_manager_}
|
||||
{
|
||||
execution_mask.reset();
|
||||
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
|
||||
}
|
||||
|
|
@ -33,14 +34,14 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
|||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
void MaxwellDMA::ConsumeSinkImpl() {
|
||||
void MaxwellDMA::ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
void MaxwellDMA::CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
|
||||
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
|
@ -50,16 +51,14 @@ void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call)
|
|||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
void MaxwellDMA::CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) {
|
||||
for (u32 i = 0; i < amount; ++i) {
|
||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
||||
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::Launch() {
|
||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
|
||||
static_cast<GPUVAddr>(regs.offset_out));
|
||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in), GPUVAddr(regs.offset_out));
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
const LaunchDMA& launch = regs.launch_dma;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -238,15 +241,14 @@ public:
|
|||
|
||||
void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
|
||||
|
||||
explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_);
|
||||
explicit MaxwellDMA(MemoryManager& memory_manager_);
|
||||
~MaxwellDMA() override;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
|
||||
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override;
|
||||
|
||||
/// Write multiple values to the register identified by method.
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override;
|
||||
|
||||
private:
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
|
|
@ -261,9 +263,7 @@ private:
|
|||
|
||||
void ReleaseSemaphore();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
Core::System& system;
|
||||
void ConsumeSinkImpl(Core::System& system) override;
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
|
|
|||
|
|
@ -26,18 +26,16 @@ class MemoryManager;
|
|||
namespace Tegra::Engines {
|
||||
class Nv01Timer final : public EngineInterface {
|
||||
public:
|
||||
explicit Nv01Timer(Core::System& system_, MemoryManager& memory_manager)
|
||||
: system{system_}
|
||||
{}
|
||||
~Nv01Timer() override;
|
||||
explicit Nv01Timer(MemoryManager& memory_manager) noexcept {}
|
||||
~Nv01Timer() noexcept override {}
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(u32 method, u32 method_argument, bool is_last_call) override {
|
||||
void CallMethod(Core::System& system, u32 method, u32 method_argument, bool is_last_call) override {
|
||||
LOG_DEBUG(HW_GPU, "method={}, argument={}, is_last_call={}", method, method_argument, is_last_call);
|
||||
}
|
||||
|
||||
/// Write multiple values to the register identified by method.
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) override {
|
||||
void CallMultiMethod(Core::System& system, u32 method, const u32* base_start, u32 amount, u32 methods_pending) override {
|
||||
LOG_DEBUG(HW_GPU, "method={}, base_start={}, amount={}, pending={}", method, fmt::ptr(base_start), amount, methods_pending);
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +44,6 @@ public:
|
|||
INSERT_PADDING_BYTES_NOINIT(0x48);
|
||||
} regs{};
|
||||
private:
|
||||
void ConsumeSinkImpl() override {}
|
||||
Core::System& system;
|
||||
void ConsumeSinkImpl(Core::System& system) override {}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,22 +29,22 @@ void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_c
|
|||
bound_engines[method_call.subchannel] = engine_id;
|
||||
switch (engine_id) {
|
||||
case EngineID::FERMI_TWOD_A:
|
||||
dma_pusher.BindSubchannel(&*dma_pusher.channel_state.fermi_2d, method_call.subchannel, EngineTypes::Fermi2D);
|
||||
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->fermi_2d, method_call.subchannel, EngineTypes::Fermi2D);
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
dma_pusher.BindSubchannel(&*dma_pusher.channel_state.maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D);
|
||||
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_3d, method_call.subchannel, EngineTypes::Maxwell3D);
|
||||
break;
|
||||
case EngineID::KEPLER_COMPUTE_B:
|
||||
dma_pusher.BindSubchannel(&*dma_pusher.channel_state.kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute);
|
||||
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_compute, method_call.subchannel, EngineTypes::KeplerCompute);
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
dma_pusher.BindSubchannel(&*dma_pusher.channel_state.maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA);
|
||||
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->maxwell_dma, method_call.subchannel, EngineTypes::MaxwellDMA);
|
||||
break;
|
||||
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
||||
dma_pusher.BindSubchannel(&*dma_pusher.channel_state.kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory);
|
||||
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->kepler_memory, method_call.subchannel, EngineTypes::KeplerMemory);
|
||||
break;
|
||||
case EngineID::NV01_TIMER:
|
||||
dma_pusher.BindSubchannel(&*dma_pusher.channel_state.nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer);
|
||||
dma_pusher.BindSubchannel(&dma_pusher.channel_state.payload->nv01_timer, method_call.subchannel, EngineTypes::Nv01Timer);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
|
||||
|
|
@ -198,22 +198,22 @@ void Puller::CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_ca
|
|||
const EngineID engine = bound_engines[method_call.subchannel];
|
||||
switch (engine) {
|
||||
case EngineID::FERMI_TWOD_A:
|
||||
dma_pusher.channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
dma_pusher.channel_state.payload->fermi_2d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
dma_pusher.channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
dma_pusher.channel_state.payload->maxwell_3d.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
break;
|
||||
case EngineID::KEPLER_COMPUTE_B:
|
||||
dma_pusher.channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
dma_pusher.channel_state.payload->kepler_compute.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
dma_pusher.channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
dma_pusher.channel_state.payload->maxwell_dma.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
break;
|
||||
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
||||
dma_pusher.channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
dma_pusher.channel_state.payload->kepler_memory.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
break;
|
||||
case EngineID::NV01_TIMER:
|
||||
dma_pusher.channel_state.nv01_timer->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
dma_pusher.channel_state.payload->nv01_timer.CallMethod(dma_pusher.system, method_call.method, method_call.argument, method_call.IsLastCall());
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
|
|
@ -226,22 +226,22 @@ void Puller::CallEngineMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subcha
|
|||
const EngineID engine = bound_engines[subchannel];
|
||||
switch (engine) {
|
||||
case EngineID::FERMI_TWOD_A:
|
||||
dma_pusher.channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending);
|
||||
dma_pusher.channel_state.payload->fermi_2d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
dma_pusher.channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending);
|
||||
dma_pusher.channel_state.payload->maxwell_3d.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||
break;
|
||||
case EngineID::KEPLER_COMPUTE_B:
|
||||
dma_pusher.channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending);
|
||||
dma_pusher.channel_state.payload->kepler_compute.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
dma_pusher.channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending);
|
||||
dma_pusher.channel_state.payload->maxwell_dma.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||
break;
|
||||
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
||||
dma_pusher.channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending);
|
||||
dma_pusher.channel_state.payload->kepler_memory.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||
break;
|
||||
case EngineID::NV01_TIMER:
|
||||
dma_pusher.channel_state.nv01_timer->CallMultiMethod(method, base_start, amount, methods_pending);
|
||||
dma_pusher.channel_state.payload->nv01_timer.CallMultiMethod(dma_pusher.system, method, base_start, amount, methods_pending);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ struct GPU::Impl {
|
|||
, shader_notify()
|
||||
, is_async{is_async_}
|
||||
, gpu_thread{system_, is_async_}
|
||||
, scheduler(system_.GPU())
|
||||
{}
|
||||
|
||||
~Impl() = default;
|
||||
|
|
@ -387,27 +386,27 @@ const Host1x::Host1x& GPU::Host1x() const {
|
|||
}
|
||||
|
||||
Engines::Maxwell3D& GPU::Maxwell3D() {
|
||||
return *impl->current_channel->maxwell_3d;
|
||||
return impl->current_channel->payload->maxwell_3d;
|
||||
}
|
||||
|
||||
const Engines::Maxwell3D& GPU::Maxwell3D() const {
|
||||
return *impl->current_channel->maxwell_3d;
|
||||
return impl->current_channel->payload->maxwell_3d;
|
||||
}
|
||||
|
||||
Engines::KeplerCompute& GPU::KeplerCompute() {
|
||||
return *impl->current_channel->kepler_compute;
|
||||
return impl->current_channel->payload->kepler_compute;
|
||||
}
|
||||
|
||||
const Engines::KeplerCompute& GPU::KeplerCompute() const {
|
||||
return *impl->current_channel->kepler_compute;
|
||||
return impl->current_channel->payload->kepler_compute;
|
||||
}
|
||||
|
||||
Tegra::DmaPusher& GPU::DmaPusher() {
|
||||
return *impl->current_channel->dma_pusher;
|
||||
return impl->current_channel->payload->dma_pusher;
|
||||
}
|
||||
|
||||
const Tegra::DmaPusher& GPU::DmaPusher() const {
|
||||
return *impl->current_channel->dma_pusher;
|
||||
return impl->current_channel->payload->dma_pusher;
|
||||
}
|
||||
|
||||
VideoCore::RendererBase& GPU::Renderer() {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
|
|||
break;
|
||||
}
|
||||
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
|
||||
scheduler.Push(submit_list->channel, std::move(submit_list->entries));
|
||||
scheduler.Push(system.GPU(), submit_list->channel, std::move(submit_list->entries));
|
||||
} else if (std::holds_alternative<GPUTickCommand>(next.data)) {
|
||||
system.GPU().TickWork();
|
||||
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
|
||||
|
|
|
|||
|
|
@ -76,10 +76,10 @@ bool IsTopologySafe(Maxwell3D::Regs::PrimitiveTopology topology) {
|
|||
|
||||
} // Anonymous namespace
|
||||
|
||||
void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_DrawArraysIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
|
||||
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
Fallback(system, maxwell3d, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<co
|
|||
maxwell3d.replace_table.clear();
|
||||
}
|
||||
}
|
||||
void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
void HLE_DrawArraysIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
SCOPE_EXIT {
|
||||
if (extended) {
|
||||
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
||||
|
|
@ -135,10 +135,10 @@ void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<c
|
|||
}
|
||||
}
|
||||
|
||||
void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_DrawIndexedIndirect::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
|
||||
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
Fallback(system, maxwell3d, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +173,7 @@ void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<c
|
|||
maxwell3d.replace_table.clear();
|
||||
}
|
||||
}
|
||||
void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
void HLE_DrawIndexedIndirect::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
maxwell3d.RefreshParameters();
|
||||
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||
const u32 element_base = parameters[4];
|
||||
|
|
@ -196,7 +196,7 @@ void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<
|
|||
maxwell3d.replace_table.clear();
|
||||
}
|
||||
}
|
||||
void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_MultiLayerClear::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
maxwell3d.RefreshParameters();
|
||||
ASSERT(parameters.size() == 1);
|
||||
|
||||
|
|
@ -208,47 +208,44 @@ void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const
|
|||
maxwell3d.regs.clear_surface.raw = clear_params.raw;
|
||||
maxwell3d.draw_manager.Clear(maxwell3d, num_layers);
|
||||
}
|
||||
void HLE_MultiDrawIndexedIndirectCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_MultiDrawIndexedIndirectCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
|
||||
if (!IsTopologySafe(topology)) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
return;
|
||||
if (IsTopologySafe(topology)) {
|
||||
const u32 start_indirect = parameters[0];
|
||||
const u32 end_indirect = parameters[1];
|
||||
if (start_indirect >= end_indirect) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
const u32 padding = parameters[3]; // padding is in words
|
||||
// size of each indirect segment
|
||||
const u32 indirect_words = 5 + padding;
|
||||
const u32 stride = indirect_words * sizeof(u32);
|
||||
const std::size_t draw_count = end_indirect - start_indirect;
|
||||
const u32 estimate = u32(maxwell3d.EstimateIndexBufferSize());
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
auto& params = maxwell3d.draw_manager.indirect_state;
|
||||
params.is_byte_count = false;
|
||||
params.is_indexed = true;
|
||||
params.include_count = true;
|
||||
params.count_start_address = maxwell3d.GetMacroAddress(4);
|
||||
params.indirect_start_address = maxwell3d.GetMacroAddress(5);
|
||||
params.buffer_size = stride * draw_count;
|
||||
params.max_draw_counts = draw_count;
|
||||
params.stride = stride;
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID);
|
||||
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
|
||||
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
||||
maxwell3d.replace_table.clear();
|
||||
} else {
|
||||
Fallback(system, maxwell3d, parameters);
|
||||
}
|
||||
|
||||
const u32 start_indirect = parameters[0];
|
||||
const u32 end_indirect = parameters[1];
|
||||
if (start_indirect >= end_indirect) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 padding = parameters[3]; // padding is in words
|
||||
|
||||
// size of each indirect segment
|
||||
const u32 indirect_words = 5 + padding;
|
||||
const u32 stride = indirect_words * sizeof(u32);
|
||||
const std::size_t draw_count = end_indirect - start_indirect;
|
||||
const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
auto& params = maxwell3d.draw_manager.indirect_state;
|
||||
params.is_byte_count = false;
|
||||
params.is_indexed = true;
|
||||
params.include_count = true;
|
||||
params.count_start_address = maxwell3d.GetMacroAddress(4);
|
||||
params.indirect_start_address = maxwell3d.GetMacroAddress(5);
|
||||
params.buffer_size = stride * draw_count;
|
||||
params.max_draw_counts = draw_count;
|
||||
params.stride = stride;
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID);
|
||||
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
|
||||
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
|
||||
maxwell3d.replace_table.clear();
|
||||
}
|
||||
void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
void HLE_MultiDrawIndexedIndirectCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
SCOPE_EXIT {
|
||||
// Clean everything.
|
||||
maxwell3d.regs.vertex_id_base = 0x0;
|
||||
|
|
@ -262,7 +259,7 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
|
|||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
const auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
|
||||
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
|
||||
const u32 padding = parameters[3];
|
||||
const std::size_t max_draws = parameters[4];
|
||||
const u32 indirect_words = 5 + padding;
|
||||
|
|
@ -277,41 +274,41 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
|
|||
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
|
||||
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
|
||||
maxwell3d.CallMethod(0x8e3, 0x648, true);
|
||||
maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true);
|
||||
maxwell3d.CallMethod(system, 0x8e3, 0x648, true);
|
||||
maxwell3d.CallMethod(system, 0x8e4, u32(index), true);
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
maxwell3d.draw_manager.DrawIndex(maxwell3d, topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]);
|
||||
}
|
||||
}
|
||||
void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_DrawIndirectByteCount::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
|
||||
if (!force) {
|
||||
Fallback(maxwell3d, parameters);
|
||||
return;
|
||||
if (force) {
|
||||
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
|
||||
auto& params = maxwell3d.draw_manager.indirect_state;
|
||||
params.is_byte_count = true;
|
||||
params.is_indexed = false;
|
||||
params.include_count = false;
|
||||
params.count_start_address = 0;
|
||||
params.indirect_start_address = maxwell3d.GetMacroAddress(2);
|
||||
params.buffer_size = 4;
|
||||
params.max_draw_counts = 1;
|
||||
params.stride = parameters[1];
|
||||
maxwell3d.regs.draw.begin = parameters[0];
|
||||
maxwell3d.regs.draw_auto_stride = parameters[1];
|
||||
maxwell3d.regs.draw_auto_byte_count = parameters[2];
|
||||
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
|
||||
} else {
|
||||
Fallback(system, maxwell3d, parameters);
|
||||
}
|
||||
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
|
||||
auto& params = maxwell3d.draw_manager.indirect_state;
|
||||
params.is_byte_count = true;
|
||||
params.is_indexed = false;
|
||||
params.include_count = false;
|
||||
params.count_start_address = 0;
|
||||
params.indirect_start_address = maxwell3d.GetMacroAddress(2);
|
||||
params.buffer_size = 4;
|
||||
params.max_draw_counts = 1;
|
||||
params.stride = parameters[1];
|
||||
maxwell3d.regs.draw.begin = parameters[0];
|
||||
maxwell3d.regs.draw_auto_stride = parameters[1];
|
||||
maxwell3d.regs.draw_auto_byte_count = parameters[2];
|
||||
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
|
||||
}
|
||||
void HLE_DrawIndirectByteCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
void HLE_DrawIndirectByteCount::Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
|
||||
maxwell3d.RefreshParameters();
|
||||
maxwell3d.regs.draw.begin = parameters[0];
|
||||
maxwell3d.regs.draw_auto_stride = parameters[1];
|
||||
maxwell3d.regs.draw_auto_byte_count = parameters[2];
|
||||
maxwell3d.draw_manager.DrawArray(maxwell3d, maxwell3d.regs.draw.topology, 0, maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
|
||||
}
|
||||
void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_C713C83D8F63CCF3::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
maxwell3d.RefreshParameters();
|
||||
const u32 offset = (parameters[0] & 0x3FFFFFFF) << 2;
|
||||
const u32 address = maxwell3d.regs.shadow_scratch[24];
|
||||
|
|
@ -321,7 +318,7 @@ void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
|||
const_buffer.address_low = address << 8;
|
||||
const_buffer.offset = offset;
|
||||
}
|
||||
void HLE_D7333D26E0A93EDE::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_D7333D26E0A93EDE::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
maxwell3d.RefreshParameters();
|
||||
const size_t index = parameters[0];
|
||||
const u32 address = maxwell3d.regs.shadow_scratch[42 + index];
|
||||
|
|
@ -331,7 +328,7 @@ void HLE_D7333D26E0A93EDE::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
|||
const_buffer.address_high = (address >> 24) & 0xFF;
|
||||
const_buffer.address_low = address << 8;
|
||||
}
|
||||
void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_BindShader::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
maxwell3d.RefreshParameters();
|
||||
auto& regs = maxwell3d.regs;
|
||||
const u32 index = parameters[0];
|
||||
|
|
@ -355,7 +352,7 @@ void HLE_BindShader::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32>
|
|||
bind_group.raw_config = 0x11;
|
||||
maxwell3d.ProcessCBBind(bind_group_id);
|
||||
}
|
||||
void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_SetRasterBoundingBox::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
maxwell3d.RefreshParameters();
|
||||
const u32 raster_mode = parameters[0];
|
||||
auto& regs = maxwell3d.regs;
|
||||
|
|
@ -364,7 +361,7 @@ void HLE_SetRasterBoundingBox::Execute(Engines::Maxwell3D& maxwell3d, std::span<
|
|||
regs.raster_bounding_box.raw = raster_mode & 0xFFFFF00F;
|
||||
regs.raster_bounding_box.pad.Assign(scratch_data & raster_enabled);
|
||||
}
|
||||
void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_ClearConstBuffer::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
static constexpr std::array<u32, 0x7000> zeroes{}; //must be bigger than either 7000 or 5F00
|
||||
maxwell3d.RefreshParameters();
|
||||
auto& regs = maxwell3d.regs;
|
||||
|
|
@ -374,7 +371,7 @@ void HLE_ClearConstBuffer::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
|||
regs.const_buffer.offset = 0;
|
||||
maxwell3d.ProcessCBMultiData(zeroes.data(), parameters[2] * 4);
|
||||
}
|
||||
void HLE_ClearMemory::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_ClearMemory::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
maxwell3d.RefreshParameters();
|
||||
const u32 needed_memory = parameters[2] / sizeof(u32);
|
||||
if (needed_memory > zero_memory.size()) {
|
||||
|
|
@ -385,10 +382,10 @@ void HLE_ClearMemory::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32
|
|||
regs.upload.line_count = 1;
|
||||
regs.upload.dest.address_high = parameters[0];
|
||||
regs.upload.dest.address_low = parameters[1];
|
||||
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||
maxwell3d.CallMultiMethod(size_t(MAXWELL3D_REG_INDEX(inline_data)), zero_memory.data(), needed_memory, needed_memory);
|
||||
maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||
maxwell3d.CallMultiMethod(system, size_t(MAXWELL3D_REG_INDEX(inline_data)), zero_memory.data(), needed_memory, needed_memory);
|
||||
}
|
||||
void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
void HLE_TransformFeedbackSetup::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
|
||||
maxwell3d.RefreshParameters();
|
||||
auto& regs = maxwell3d.regs;
|
||||
regs.transform_feedback_enabled = 1;
|
||||
|
|
@ -400,8 +397,8 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa
|
|||
regs.upload.line_count = 1;
|
||||
regs.upload.dest.address_high = parameters[0];
|
||||
regs.upload.dest.address_low = parameters[1];
|
||||
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||
maxwell3d.CallMethod(size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true);
|
||||
maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
|
||||
maxwell3d.CallMethod(system, size_t(MAXWELL3D_REG_INDEX(inline_data)), regs.transform_feedback.controls[0].stride, true);
|
||||
maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
|
||||
}
|
||||
|
||||
|
|
@ -441,7 +438,7 @@ void HLE_TransformFeedbackSetup::Execute(Engines::Maxwell3D& maxwell3d, std::spa
|
|||
}
|
||||
}
|
||||
|
||||
void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method) {
|
||||
void MacroInterpreterImpl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method) {
|
||||
Reset();
|
||||
|
||||
registers[1] = params[0];
|
||||
|
|
@ -451,7 +448,7 @@ void MacroInterpreterImpl::Execute(Engines::Maxwell3D& maxwell3d, std::span<cons
|
|||
// Execute the code until we hit an exit condition.
|
||||
bool keep_executing = true;
|
||||
while (keep_executing) {
|
||||
keep_executing = Step(maxwell3d, false);
|
||||
keep_executing = Step(system, maxwell3d, false);
|
||||
}
|
||||
|
||||
// Assert the the macro used all the input parameters
|
||||
|
|
@ -474,7 +471,7 @@ void MacroInterpreterImpl::Reset() {
|
|||
/// @brief Executes a single macro instruction located at the current program counter. Returns whether
|
||||
/// the interpreter should keep running.
|
||||
/// @param is_delay_slot Whether the current step is being executed due to a delay slot in a previous instruction.
|
||||
bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slot) {
|
||||
bool MacroInterpreterImpl::Step(Core::System& system, Engines::Maxwell3D& maxwell3d, bool is_delay_slot) {
|
||||
u32 base_address = pc;
|
||||
|
||||
Macro::Opcode opcode = GetOpcode();
|
||||
|
|
@ -490,11 +487,11 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
switch (opcode.operation) {
|
||||
case Macro::Operation::ALU: {
|
||||
u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), GetRegister(opcode.src_b));
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
break;
|
||||
}
|
||||
case Macro::Operation::AddImmediate: {
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, GetRegister(opcode.src_a) + opcode.immediate);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, GetRegister(opcode.src_a) + opcode.immediate);
|
||||
break;
|
||||
}
|
||||
case Macro::Operation::ExtractInsert: {
|
||||
|
|
@ -504,7 +501,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
src = (src >> opcode.bf_src_bit) & opcode.GetBitfieldMask();
|
||||
dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
|
||||
dst |= src << opcode.bf_dst_bit;
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, dst);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, dst);
|
||||
break;
|
||||
}
|
||||
case Macro::Operation::ExtractShiftLeftImmediate: {
|
||||
|
|
@ -513,7 +510,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
|
||||
u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit;
|
||||
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
break;
|
||||
}
|
||||
case Macro::Operation::ExtractShiftLeftRegister: {
|
||||
|
|
@ -522,12 +519,12 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
|
||||
u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst;
|
||||
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
break;
|
||||
}
|
||||
case Macro::Operation::Read: {
|
||||
u32 result = Read(maxwell3d, GetRegister(opcode.src_a) + opcode.immediate);
|
||||
ProcessResult(maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
ProcessResult(system, maxwell3d, opcode.result_operation, opcode.dst, result);
|
||||
break;
|
||||
}
|
||||
case Macro::Operation::Branch: {
|
||||
|
|
@ -543,7 +540,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
|
||||
delayed_pc = base_address + opcode.GetBranchTarget();
|
||||
// Execute one more instruction due to the delay slot.
|
||||
return Step(maxwell3d, true);
|
||||
return Step(system, maxwell3d, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -556,7 +553,7 @@ bool MacroInterpreterImpl::Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slo
|
|||
// cause an exit if it's executed inside a delay slot.
|
||||
if (opcode.is_exit && !is_delay_slot) {
|
||||
// Exit has a delay slot, execute the next instruction
|
||||
Step(maxwell3d, true);
|
||||
Step(system, maxwell3d, true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -603,7 +600,7 @@ u32 MacroInterpreterImpl::GetALUResult(Macro::ALUOperation operation, u32 src_a,
|
|||
}
|
||||
|
||||
/// Performs the result operation on the input result and stores it in the specified register (if necessary).
|
||||
void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) {
|
||||
void MacroInterpreterImpl::ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result) {
|
||||
switch (operation) {
|
||||
case Macro::ResultOperation::IgnoreAndFetch:
|
||||
// Fetch parameter and ignore result.
|
||||
|
|
@ -621,12 +618,12 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R
|
|||
case Macro::ResultOperation::FetchAndSend:
|
||||
// Fetch parameter and send result.
|
||||
SetRegister(reg, FetchParameter());
|
||||
Send(maxwell3d, result);
|
||||
Send(system, maxwell3d, result);
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSend:
|
||||
// Move and send result.
|
||||
SetRegister(reg, result);
|
||||
Send(maxwell3d, result);
|
||||
Send(system, maxwell3d, result);
|
||||
break;
|
||||
case Macro::ResultOperation::FetchAndSetMethod:
|
||||
// Fetch parameter and use result as Method Address.
|
||||
|
|
@ -637,13 +634,13 @@ void MacroInterpreterImpl::ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::R
|
|||
// Move result and use as Method Address, then fetch and send parameter.
|
||||
SetRegister(reg, result);
|
||||
SetMethodAddress(result);
|
||||
Send(maxwell3d, FetchParameter());
|
||||
Send(system, maxwell3d, FetchParameter());
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSetMethodSend:
|
||||
// Move result and use as Method Address, then send bits 12:17 of result.
|
||||
SetRegister(reg, result);
|
||||
SetMethodAddress(result);
|
||||
Send(maxwell3d, (result >> 12) & 0b111111);
|
||||
Send(system, maxwell3d, (result >> 12) & 0b111111);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
|
||||
|
|
@ -684,8 +681,8 @@ void MacroInterpreterImpl::SetRegister(u32 register_id, u32 value) {
|
|||
}
|
||||
|
||||
/// Calls a GPU Engine method with the input parameter.
|
||||
void MacroInterpreterImpl::Send(Engines::Maxwell3D& maxwell3d, u32 value) {
|
||||
maxwell3d.CallMethod(method_address.address, value, true);
|
||||
void MacroInterpreterImpl::Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value) {
|
||||
maxwell3d.CallMethod(system, method_address.address, value, true);
|
||||
// Increment the method address by the method increment.
|
||||
method_address.address.Assign(method_address.address.Value() + method_address.increment.Value());
|
||||
}
|
||||
|
|
@ -736,34 +733,35 @@ static const auto default_cg_mode = nullptr; //Allow RWE
|
|||
#endif
|
||||
|
||||
struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCachedMacro {
|
||||
explicit MacroJITx64Impl(std::span<const u32> code_)
|
||||
explicit MacroJITx64Impl(Core::System& system, std::span<const u32> code_)
|
||||
: Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode)
|
||||
, code{code_}
|
||||
{
|
||||
Compile();
|
||||
Compile(system);
|
||||
}
|
||||
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) override;
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) override;
|
||||
|
||||
void Compile_ALU(Macro::Opcode opcode);
|
||||
void Compile_AddImmediate(Macro::Opcode opcode);
|
||||
void Compile_ExtractInsert(Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode);
|
||||
void Compile_Read(Macro::Opcode opcode);
|
||||
void Compile_ALU(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_AddImmediate(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_Read(Core::System& system, Macro::Opcode opcode);
|
||||
void Compile_Branch(Macro::Opcode opcode);
|
||||
|
||||
void Optimizer_ScanFlags();
|
||||
void Compile();
|
||||
bool Compile_NextInstruction();
|
||||
void Compile(Core::System& system);
|
||||
bool Compile_NextInstruction(Core::System& system);
|
||||
Xbyak::Reg32 Compile_FetchParameter();
|
||||
Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst);
|
||||
void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg);
|
||||
void Compile_Send(Xbyak::Reg32 value);
|
||||
void Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg);
|
||||
void Compile_Send(Core::System& system, Xbyak::Reg32 value);
|
||||
Macro::Opcode GetOpCode() const;
|
||||
|
||||
struct JITState {
|
||||
Engines::Maxwell3D* maxwell3d{};
|
||||
Engines::Maxwell3D* maxwell3d = nullptr;
|
||||
Core::System* system = nullptr;
|
||||
std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{};
|
||||
u32 carry_flag{};
|
||||
};
|
||||
|
|
@ -789,15 +787,16 @@ struct MacroJITx64Impl final : public Xbyak::CodeGenerator, public DynamicCached
|
|||
std::span<const u32> code;
|
||||
};
|
||||
|
||||
void MacroJITx64Impl::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) {
|
||||
void MacroJITx64Impl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) {
|
||||
ASSERT_OR_EXECUTE(program != nullptr, { return; });
|
||||
JITState state{};
|
||||
state.maxwell3d = &maxwell3d;
|
||||
state.system = &system;
|
||||
state.registers = {};
|
||||
program(&state, parameters.data(), parameters.data() + parameters.size());
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
|
||||
void MacroJITx64Impl::Compile_ALU(Core::System& system, Macro::Opcode opcode) {
|
||||
const bool is_a_zero = opcode.src_a == 0;
|
||||
const bool is_b_zero = opcode.src_b == 0;
|
||||
const bool valid_operation = !is_a_zero && !is_b_zero;
|
||||
|
|
@ -914,10 +913,10 @@ void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
|
|||
UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", opcode.alu_operation.Value());
|
||||
break;
|
||||
}
|
||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
||||
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
|
||||
void MacroJITx64Impl::Compile_AddImmediate(Core::System& system, Macro::Opcode opcode) {
|
||||
if (optimizer.skip_dummy_addimmediate) {
|
||||
// Games tend to use this as an exit instruction placeholder. It's to encode an instruction
|
||||
// without doing anything. In our case we can just not emit anything.
|
||||
|
|
@ -952,10 +951,10 @@ void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
|
|||
sub(result, opcode.immediate * -1);
|
||||
}
|
||||
}
|
||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
||||
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
|
||||
void MacroJITx64Impl::Compile_ExtractInsert(Core::System& system, Macro::Opcode opcode) {
|
||||
auto dst = Compile_GetRegister(opcode.src_a, RESULT);
|
||||
auto src = Compile_GetRegister(opcode.src_b, eax);
|
||||
|
||||
|
|
@ -966,10 +965,10 @@ void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
|
|||
shl(src, opcode.bf_dst_bit);
|
||||
or_(dst, src);
|
||||
|
||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
||||
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
|
||||
void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Core::System& system, Macro::Opcode opcode) {
|
||||
const auto dst = Compile_GetRegister(opcode.src_a, ecx);
|
||||
const auto src = Compile_GetRegister(opcode.src_b, RESULT);
|
||||
|
||||
|
|
@ -977,10 +976,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
|
|||
and_(src, opcode.GetBitfieldMask());
|
||||
shl(src, opcode.bf_dst_bit);
|
||||
|
||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
||||
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
|
||||
void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Core::System& system, Macro::Opcode opcode) {
|
||||
const auto dst = Compile_GetRegister(opcode.src_a, ecx);
|
||||
const auto src = Compile_GetRegister(opcode.src_b, RESULT);
|
||||
|
||||
|
|
@ -988,10 +987,10 @@ void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
|
|||
and_(src, opcode.GetBitfieldMask());
|
||||
shl(src, dst.cvt8());
|
||||
|
||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
||||
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
|
||||
void MacroJITx64Impl::Compile_Read(Core::System& system, Macro::Opcode opcode) {
|
||||
if (optimizer.zero_reg_skip && opcode.src_a == 0) {
|
||||
if (opcode.immediate == 0) {
|
||||
xor_(RESULT, RESULT);
|
||||
|
|
@ -1017,23 +1016,21 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
|
|||
int3();
|
||||
L(pass_range_check);
|
||||
}
|
||||
mov(rax, qword[STATE]);
|
||||
mov(RESULT,
|
||||
dword[rax + offsetof(Engines::Maxwell3D, regs) +
|
||||
offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
|
||||
|
||||
Compile_ProcessResult(opcode.result_operation, opcode.dst);
|
||||
mov(rax, qword[STATE + offsetof(JITState, maxwell3d)]);
|
||||
mov(RESULT, dword[rax + offsetof(Engines::Maxwell3D, regs) + offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
|
||||
Compile_ProcessResult(system, opcode.result_operation, opcode.dst);
|
||||
}
|
||||
|
||||
static void MacroJIT_SendThunk(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
|
||||
maxwell3d->CallMethod(method_address.address, value, true);
|
||||
static void MacroJIT_SendThunk(Core::System* system, Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
|
||||
maxwell3d->CallMethod(*system, method_address.address, value, true);
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
|
||||
void MacroJITx64Impl::Compile_Send(Core::System& system, Xbyak::Reg32 value) {
|
||||
Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
||||
mov(Common::X64::ABI_PARAM1, qword[STATE]);
|
||||
mov(Common::X64::ABI_PARAM2.cvt32(), METHOD_ADDRESS);
|
||||
mov(Common::X64::ABI_PARAM3.cvt32(), value);
|
||||
mov(Common::X64::ABI_PARAM1, qword[STATE + offsetof(JITState, system)]);
|
||||
mov(Common::X64::ABI_PARAM2, qword[STATE + offsetof(JITState, maxwell3d)]);
|
||||
mov(Common::X64::ABI_PARAM3.cvt32(), METHOD_ADDRESS);
|
||||
mov(Common::X64::ABI_PARAM4.cvt32(), value);
|
||||
Common::X64::CallFarFunction(*this, &MacroJIT_SendThunk);
|
||||
Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
||||
|
||||
|
|
@ -1057,9 +1054,8 @@ void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
|
|||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
|
||||
ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
|
||||
const s32 jump_address =
|
||||
static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32));
|
||||
ASSERT(!is_delay_slot && "Executing a branch in a delay slot is not valid");
|
||||
const s32 jump_address = s32(pc) + s32(opcode.GetBranchTarget() / sizeof(s32));
|
||||
|
||||
Xbyak::Label end;
|
||||
auto value = Compile_GetRegister(opcode.src_a, eax);
|
||||
|
|
@ -1128,7 +1124,7 @@ void MacroJITx64Impl::Optimizer_ScanFlags() {
|
|||
}
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile() {
|
||||
void MacroJITx64Impl::Compile(Core::System& system) {
|
||||
labels.fill(Xbyak::Label());
|
||||
|
||||
Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
|
||||
|
|
@ -1168,7 +1164,7 @@ void MacroJITx64Impl::Compile() {
|
|||
next_opcode = {};
|
||||
}
|
||||
pc = i;
|
||||
Compile_NextInstruction();
|
||||
Compile_NextInstruction(system);
|
||||
}
|
||||
|
||||
L(end_of_code);
|
||||
|
|
@ -1179,7 +1175,7 @@ void MacroJITx64Impl::Compile() {
|
|||
program = getCode<ProgramType>();
|
||||
}
|
||||
|
||||
bool MacroJITx64Impl::Compile_NextInstruction() {
|
||||
bool MacroJITx64Impl::Compile_NextInstruction(Core::System& system) {
|
||||
const auto opcode = GetOpCode();
|
||||
if (labels[pc].getAddress()) {
|
||||
return false;
|
||||
|
|
@ -1189,22 +1185,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() {
|
|||
|
||||
switch (opcode.operation) {
|
||||
case Macro::Operation::ALU:
|
||||
Compile_ALU(opcode);
|
||||
Compile_ALU(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::AddImmediate:
|
||||
Compile_AddImmediate(opcode);
|
||||
Compile_AddImmediate(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::ExtractInsert:
|
||||
Compile_ExtractInsert(opcode);
|
||||
Compile_ExtractInsert(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::ExtractShiftLeftImmediate:
|
||||
Compile_ExtractShiftLeftImmediate(opcode);
|
||||
Compile_ExtractShiftLeftImmediate(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::ExtractShiftLeftRegister:
|
||||
Compile_ExtractShiftLeftRegister(opcode);
|
||||
Compile_ExtractShiftLeftRegister(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::Read:
|
||||
Compile_Read(opcode);
|
||||
Compile_Read(system, opcode);
|
||||
break;
|
||||
case Macro::Operation::Branch:
|
||||
Compile_Branch(opcode);
|
||||
|
|
@ -1276,7 +1272,7 @@ Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) {
|
|||
return dst;
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) {
|
||||
void MacroJITx64Impl::Compile_ProcessResult(Core::System& system, Macro::ResultOperation operation, u32 reg) {
|
||||
const auto SetRegister = [this](u32 reg_index, const Xbyak::Reg32& result) {
|
||||
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
|
||||
// register.
|
||||
|
|
@ -1301,12 +1297,12 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
|||
case Macro::ResultOperation::FetchAndSend:
|
||||
// Fetch parameter and send result.
|
||||
SetRegister(reg, Compile_FetchParameter());
|
||||
Compile_Send(RESULT);
|
||||
Compile_Send(system, RESULT);
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSend:
|
||||
// Move and send result.
|
||||
SetRegister(reg, RESULT);
|
||||
Compile_Send(RESULT);
|
||||
Compile_Send(system, RESULT);
|
||||
break;
|
||||
case Macro::ResultOperation::FetchAndSetMethod:
|
||||
// Fetch parameter and use result as Method Address.
|
||||
|
|
@ -1317,7 +1313,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
|||
// Move result and use as Method Address, then fetch and send parameter.
|
||||
SetRegister(reg, RESULT);
|
||||
SetMethodAddress(RESULT);
|
||||
Compile_Send(Compile_FetchParameter());
|
||||
Compile_Send(system, Compile_FetchParameter());
|
||||
break;
|
||||
case Macro::ResultOperation::MoveAndSetMethodSend:
|
||||
// Move result and use as Method Address, then send bits 12:17 of result.
|
||||
|
|
@ -1325,7 +1321,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
|
|||
SetMethodAddress(RESULT);
|
||||
shr(RESULT, 12);
|
||||
and_(RESULT, 0b111111);
|
||||
Compile_Send(RESULT);
|
||||
Compile_Send(system, RESULT);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
|
||||
|
|
@ -1366,36 +1362,36 @@ static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
|
|||
macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
|
||||
}
|
||||
|
||||
void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
|
||||
auto const execute_variant = [&maxwell3d, ¶meters, method](AnyCachedMacro& acm) {
|
||||
void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
|
||||
auto const execute_variant = [&system, &maxwell3d, ¶meters, method](AnyCachedMacro& acm) {
|
||||
if (auto a = std::get_if<HLE_DrawArraysIndirect>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_DrawIndexedIndirect>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_MultiDrawIndexedIndirectCount>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_MultiLayerClear>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_C713C83D8F63CCF3>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_D7333D26E0A93EDE>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_BindShader>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_SetRasterBoundingBox>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_ClearConstBuffer>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_ClearMemory>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_TransformFeedbackSetup>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_DrawIndirectByteCount>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<MacroInterpreterImpl>(&acm))
|
||||
a->Execute(maxwell3d, parameters, method);
|
||||
a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<std::unique_ptr<DynamicCachedMacro>>(&acm))
|
||||
a->get()->Execute(maxwell3d, parameters, method);
|
||||
a->get()->Execute(system, maxwell3d, parameters, method);
|
||||
};
|
||||
if (auto const it = macro_cache.find(method); it != macro_cache.end()) {
|
||||
auto& ci = it->second;
|
||||
|
|
@ -1426,9 +1422,9 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<c
|
|||
code.resize(macro_cached.size() - rebased_method);
|
||||
std::memcpy(code.data(), macro_cached.data() + rebased_method, code.size() * sizeof(u32));
|
||||
ci.hash = Common::HashValue(code);
|
||||
ci.program = Compile(maxwell3d, code);
|
||||
ci.program = Compile(system, maxwell3d, code);
|
||||
} else {
|
||||
ci.program = Compile(maxwell3d, macro_code->second);
|
||||
ci.program = Compile(system, maxwell3d, macro_code->second);
|
||||
ci.hash = Common::HashValue(macro_code->second);
|
||||
}
|
||||
if (CanBeHLEProgram(ci.hash) && !Settings::values.disable_macro_hle) {
|
||||
|
|
@ -1443,10 +1439,10 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<c
|
|||
}
|
||||
}
|
||||
|
||||
AnyCachedMacro MacroEngine::Compile(Engines::Maxwell3D& maxwell3d, std::span<const u32> code) {
|
||||
AnyCachedMacro MacroEngine::Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> code) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
if (!is_interpreted)
|
||||
return std::make_unique<MacroJITx64Impl>(code);
|
||||
return std::make_unique<MacroJITx64Impl>(system, code);
|
||||
#endif
|
||||
return MacroInterpreterImpl(code);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
namespace Engines {
|
||||
|
|
@ -106,61 +110,61 @@ struct HLEMacro {
|
|||
/// also assigning the base vertex/instance.
|
||||
struct HLE_DrawArraysIndirect final {
|
||||
HLE_DrawArraysIndirect(bool extended_) noexcept : extended{extended_} {}
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
bool extended;
|
||||
};
|
||||
/// @note: these macros have two versions, a normal and extended version, with the extended version
|
||||
/// also assigning the base vertex/instance.
|
||||
struct HLE_DrawIndexedIndirect final {
|
||||
explicit HLE_DrawIndexedIndirect(bool extended_) noexcept : extended{extended_} {}
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
bool extended;
|
||||
};
|
||||
struct HLE_MultiLayerClear final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
};
|
||||
struct HLE_MultiDrawIndexedIndirectCount final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
};
|
||||
struct HLE_DrawIndirectByteCount final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Fallback(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters);
|
||||
};
|
||||
struct HLE_C713C83D8F63CCF3 final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
};
|
||||
struct HLE_D7333D26E0A93EDE final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
};
|
||||
struct HLE_BindShader final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
};
|
||||
struct HLE_SetRasterBoundingBox final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
};
|
||||
struct HLE_ClearConstBuffer final {
|
||||
HLE_ClearConstBuffer(size_t base_size_) noexcept : base_size{base_size_} {}
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
size_t base_size;
|
||||
};
|
||||
struct HLE_ClearMemory final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
std::vector<u32> zero_memory;
|
||||
};
|
||||
struct HLE_TransformFeedbackSetup final {
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method);
|
||||
};
|
||||
struct MacroInterpreterImpl final {
|
||||
MacroInterpreterImpl() {}
|
||||
MacroInterpreterImpl(std::span<const u32> code_) : code{code_} {}
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method);
|
||||
void Reset();
|
||||
bool Step(Engines::Maxwell3D& maxwell3d, bool is_delay_slot);
|
||||
bool Step(Core::System& system, Engines::Maxwell3D& maxwell3d, bool is_delay_slot);
|
||||
u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b);
|
||||
void ProcessResult(Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result);
|
||||
void ProcessResult(Core::System& system, Engines::Maxwell3D& maxwell3d, Macro::ResultOperation operation, u32 reg, u32 result);
|
||||
bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const;
|
||||
Macro::Opcode GetOpcode() const;
|
||||
u32 GetRegister(u32 register_id) const;
|
||||
|
|
@ -169,7 +173,7 @@ struct MacroInterpreterImpl final {
|
|||
[[nodiscard]] inline void SetMethodAddress(u32 address) noexcept {
|
||||
method_address.raw = address;
|
||||
}
|
||||
void Send(Engines::Maxwell3D& maxwell3d, u32 value);
|
||||
void Send(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 value);
|
||||
u32 Read(Engines::Maxwell3D& maxwell3d, u32 method) const;
|
||||
u32 FetchParameter();
|
||||
/// General purpose macro registers.
|
||||
|
|
@ -192,7 +196,7 @@ struct DynamicCachedMacro {
|
|||
/// Executes the macro code with the specified input parameters.
|
||||
/// @param parameters The parameters of the macro
|
||||
/// @param method The method to execute
|
||||
virtual void Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) = 0;
|
||||
virtual void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, u32 method) = 0;
|
||||
};
|
||||
|
||||
using AnyCachedMacro = std::variant<
|
||||
|
|
@ -227,8 +231,8 @@ struct MacroEngine {
|
|||
uploaded_macro_code.erase(method);
|
||||
}
|
||||
// Compiles the macro if its not in the cache, and executes the compiled macro
|
||||
void Execute(Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters);
|
||||
AnyCachedMacro Compile(Engines::Maxwell3D& maxwell3d, std::span<const u32> code);
|
||||
void Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters);
|
||||
AnyCachedMacro Compile(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> code);
|
||||
struct CacheInfo {
|
||||
AnyCachedMacro program;
|
||||
u64 hash{};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -204,7 +207,7 @@ void SetupDirtyMisc(Tables& tables) {
|
|||
} // Anonymous namespace
|
||||
|
||||
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
||||
auto& tables{channel_state.maxwell_3d->dirty.tables};
|
||||
auto& tables{channel_state.payload->maxwell_3d.dirty.tables};
|
||||
SetupDirtyFlags(tables);
|
||||
SetupDirtyColorMasks(tables);
|
||||
SetupDirtyViewports(tables);
|
||||
|
|
@ -231,7 +234,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
|||
}
|
||||
|
||||
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
|
||||
flags = &channel_state.maxwell_3d->dirty.flags;
|
||||
flags = &channel_state.payload->maxwell_3d.dirty.flags;
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateState() {
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ void SetupRasterModes(Tables &tables) {
|
|||
} // Anonymous namespace
|
||||
|
||||
void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
||||
auto& tables{channel_state.maxwell_3d->dirty.tables};
|
||||
auto& tables{channel_state.payload->maxwell_3d.dirty.tables};
|
||||
SetupDirtyFlags(tables);
|
||||
SetupDirtyViewports(tables);
|
||||
SetupDirtyScissors(tables);
|
||||
|
|
@ -261,7 +261,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
|
|||
}
|
||||
|
||||
void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
|
||||
flags = &channel_state.maxwell_3d->dirty.flags;
|
||||
flags = &channel_state.payload->maxwell_3d.dirty.flags;
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateState() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue