Compare commits
55 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddf5577799 | ||
|
|
f706b3bd24 | ||
|
|
d574bb4610 | ||
|
|
b0ba1a0b65 | ||
|
|
0ba03d1b3a | ||
|
|
fcebd36cde | ||
|
|
ae6dd1143c | ||
|
|
1d38109714 | ||
|
|
6a9bbb0128 | ||
|
|
d2170075e6 | ||
|
|
553be194f6 | ||
|
|
048d3e2404 | ||
|
|
3c925a7282 | ||
|
|
e37d00332c | ||
|
|
d3114c620d | ||
|
|
26b76d2eaf | ||
|
|
e2164f3417 | ||
|
|
c0fb5e876d | ||
|
|
a9ace6856d | ||
|
|
64c2ccb0cb | ||
|
|
dbacb31f61 | ||
|
|
0b9f2c2f14 | ||
|
|
e158167139 | ||
|
|
3da4280e81 | ||
|
|
77177a7e33 | ||
|
|
61a8696510 | ||
|
|
9b34afa588 | ||
|
|
6bcd676b61 | ||
|
|
133a68ee9b | ||
|
|
b1cd6cec19 | ||
|
|
d9336860d7 | ||
|
|
4496030ea9 | ||
|
|
eb74ef474b | ||
|
|
682c50715c | ||
|
|
c3cae9d992 | ||
|
|
224a19758e | ||
|
|
8c9e238a7b | ||
|
|
55e6d0dae0 | ||
|
|
9632434243 | ||
|
|
ec9550ced5 | ||
|
|
47a2efee73 | ||
|
|
5b7c0f13d3 | ||
|
|
ddf64e56af | ||
|
|
155213484b | ||
|
|
b7ad83383f | ||
|
|
6f101e0f02 | ||
|
|
972b93bf00 | ||
|
|
1e35ade1ec | ||
|
|
b8777b6653 | ||
|
|
752659aef3 | ||
|
|
9574429c5f | ||
|
|
20cf09471a | ||
|
|
7969d4d5de | ||
|
|
1dba5fab62 | ||
|
|
09a87966e0 |
@@ -6,9 +6,8 @@
|
||||
# Setup RC file for tx
|
||||
cat << EOF > ~/.transifexrc
|
||||
[https://www.transifex.com]
|
||||
hostname = https://www.transifex.com
|
||||
username = api
|
||||
password = $TRANSIFEX_API_TOKEN
|
||||
rest_hostname = https://rest.api.transifex.com
|
||||
token = $TRANSIFEX_API_TOKEN
|
||||
EOF
|
||||
|
||||
|
||||
@@ -19,9 +18,6 @@ cmake --version
|
||||
gcc -v
|
||||
tx --version
|
||||
|
||||
# vcpkg needs these: curl zip unzip tar, have tar
|
||||
apt-get install -y curl zip unzip
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF -DYUZU_TESTS=OFF -DYUZU_USE_BUNDLED_VCPKG=ON
|
||||
make translation
|
||||
|
||||
@@ -10,13 +10,9 @@ set -e
|
||||
ccache -sv
|
||||
|
||||
mkdir -p build && cd build
|
||||
export LDFLAGS="-fuse-ld=lld"
|
||||
# -femulated-tls required due to an incompatibility between GCC and Clang
|
||||
# TODO(lat9nq): If this is widespread, we probably need to add this to CMakeLists where appropriate
|
||||
export CXXFLAGS="-femulated-tls"
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWClangCross.cmake" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWCross.cmake" \
|
||||
-DDISPLAY_VERSION="$1" \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
|
||||
@@ -252,7 +252,7 @@ if(ENABLE_QT)
|
||||
endif()
|
||||
|
||||
# Check for headers
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
|
||||
if (NOT QT_DEP_GLU_FOUND)
|
||||
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
|
||||
@@ -386,7 +386,7 @@ endif()
|
||||
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
|
||||
include(FindPkgConfig)
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
|
||||
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
|
||||
else()
|
||||
@@ -410,7 +410,7 @@ set(FFmpeg_COMPONENTS
|
||||
swscale)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
|
||||
2
dist/languages/.tx/config
vendored
2
dist/languages/.tx/config
vendored
@@ -1,7 +1,7 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[yuzu.emulator]
|
||||
[o:yuzu-emulator:p:yuzu:r:emulator]
|
||||
file_filter = <lang>.ts
|
||||
source_file = en.ts
|
||||
source_lang = en
|
||||
|
||||
4
dist/languages/README.md
vendored
4
dist/languages/README.md
vendored
@@ -1 +1,3 @@
|
||||
This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. Do not directly open PRs on github to modify the translation.
|
||||
This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -t -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically.
|
||||
|
||||
Do not directly open PRs on github to modify the translation.
|
||||
|
||||
2
externals/ffmpeg/CMakeLists.txt
vendored
2
externals/ffmpeg/CMakeLists.txt
vendored
@@ -43,7 +43,7 @@ if (NOT WIN32)
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(CUDA cuda)
|
||||
pkg_check_modules(FFNVCODEC ffnvcodec)
|
||||
|
||||
2
externals/libusb/CMakeLists.txt
vendored
2
externals/libusb/CMakeLists.txt
vendored
@@ -108,7 +108,7 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
Include(FindPkgConfig)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(LIBUDEV REQUIRED libudev)
|
||||
|
||||
if (LIBUDEV_FOUND)
|
||||
|
||||
@@ -23,7 +23,7 @@ System::~System() {
|
||||
void System::Finalize() {
|
||||
Stop();
|
||||
session->Finalize();
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
|
||||
void System::StartSession() {
|
||||
@@ -142,7 +142,7 @@ void System::ReleaseBuffers() {
|
||||
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ bool System::FlushAudioInBuffers() {
|
||||
buffers.FlushBuffers(buffers_released);
|
||||
|
||||
if (buffers_released > 0) {
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ System::~System() {
|
||||
void System::Finalize() {
|
||||
Stop();
|
||||
session->Finalize();
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
|
||||
std::string_view System::GetDefaultOutputDeviceName() const {
|
||||
@@ -141,7 +141,7 @@ void System::ReleaseBuffers() {
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ bool System::FlushAudioOutBuffers() {
|
||||
buffers.FlushBuffers(buffers_released);
|
||||
|
||||
if (buffers_released > 0) {
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
|
||||
return result;
|
||||
}
|
||||
|
||||
adsp_rendered_event->GetWritableEvent().Clear();
|
||||
adsp_rendered_event->Clear();
|
||||
num_times_updated++;
|
||||
|
||||
const auto end_time{core.CoreTiming().GetClockTicks()};
|
||||
@@ -625,7 +625,7 @@ void System::SendCommandToDsp() {
|
||||
reset_command_buffers = false;
|
||||
command_buffer_size = command_size;
|
||||
if (remaining_command_count == 0) {
|
||||
adsp_rendered_event->GetWritableEvent().Signal();
|
||||
adsp_rendered_event->Signal();
|
||||
}
|
||||
} else {
|
||||
adsp.ClearRemainCount(session_id);
|
||||
|
||||
@@ -66,10 +66,10 @@ public:
|
||||
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency);
|
||||
if (latency_error != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||
minimum_latency = 256U;
|
||||
minimum_latency = TargetSampleCount * 2;
|
||||
}
|
||||
|
||||
minimum_latency = std::max(minimum_latency, 256u);
|
||||
minimum_latency = std::max(minimum_latency, TargetSampleCount * 2);
|
||||
|
||||
LOG_INFO(Service_Audio,
|
||||
"Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) "
|
||||
@@ -326,4 +326,31 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetCubebLatency() {
|
||||
cubeb* ctx;
|
||||
|
||||
if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
// Return a large latency so we choose SDL instead.
|
||||
return 10000u;
|
||||
}
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.rate = TargetSampleRate;
|
||||
params.channels = 2;
|
||||
params.format = CUBEB_SAMPLE_S16LE;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
|
||||
u32 latency{0};
|
||||
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency);
|
||||
if (latency_error != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||
latency = TargetSampleCount * 2;
|
||||
}
|
||||
latency = std::max(latency, TargetSampleCount * 2);
|
||||
cubeb_destroy(ctx);
|
||||
return latency;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -96,4 +96,11 @@ private:
|
||||
*/
|
||||
std::vector<std::string> ListCubebSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
*/
|
||||
u32 GetCubebLatency();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -47,11 +47,7 @@ public:
|
||||
spec.freq = TargetSampleRate;
|
||||
spec.channels = static_cast<u8>(device_channels);
|
||||
spec.format = AUDIO_S16SYS;
|
||||
if (type == StreamType::Render) {
|
||||
spec.samples = TargetSampleCount;
|
||||
} else {
|
||||
spec.samples = 1024;
|
||||
}
|
||||
spec.samples = TargetSampleCount * 2;
|
||||
spec.callback = &SDLSinkStream::DataCallback;
|
||||
spec.userdata = this;
|
||||
|
||||
@@ -240,4 +236,8 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetSDLLatency() {
|
||||
return TargetSampleCount * 2;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -87,4 +87,11 @@ private:
|
||||
*/
|
||||
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
*/
|
||||
u32 GetSDLLatency();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -21,58 +21,80 @@ namespace {
|
||||
struct SinkDetails {
|
||||
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
|
||||
using ListDevicesFn = std::vector<std::string> (*)(bool);
|
||||
using LatencyFn = u32 (*)();
|
||||
|
||||
/// Name for this sink.
|
||||
const char* id;
|
||||
std::string_view id;
|
||||
/// A method to call to construct an instance of this type of sink.
|
||||
FactoryFn factory;
|
||||
/// A method to call to list available devices.
|
||||
ListDevicesFn list_devices;
|
||||
/// Method to get the latency of this backend.
|
||||
LatencyFn latency;
|
||||
};
|
||||
|
||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||
constexpr SinkDetails sink_details[] = {
|
||||
#ifdef HAVE_CUBEB
|
||||
SinkDetails{"cubeb",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<CubebSink>(device_id);
|
||||
},
|
||||
&ListCubebSinkDevices},
|
||||
SinkDetails{
|
||||
"cubeb",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<CubebSink>(device_id);
|
||||
},
|
||||
&ListCubebSinkDevices,
|
||||
&GetCubebLatency,
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
SinkDetails{"sdl2",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
&ListSDLSinkDevices},
|
||||
SinkDetails{
|
||||
"sdl2",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
&ListSDLSinkDevices,
|
||||
&GetSDLLatency,
|
||||
},
|
||||
#endif
|
||||
SinkDetails{"null",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<NullSink>(device_id);
|
||||
},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; }},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
|
||||
};
|
||||
|
||||
const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) {
|
||||
auto iter =
|
||||
std::find_if(std::begin(sink_details), std::end(sink_details),
|
||||
[sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
|
||||
const auto find_backend{[](std::string_view id) {
|
||||
return std::find_if(std::begin(sink_details), std::end(sink_details),
|
||||
[&id](const auto& sink_detail) { return sink_detail.id == id; });
|
||||
}};
|
||||
|
||||
if (sink_id == "auto" || iter == std::end(sink_details)) {
|
||||
if (sink_id != "auto") {
|
||||
LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
|
||||
auto iter = find_backend(sink_id);
|
||||
|
||||
if (sink_id == "auto") {
|
||||
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||
// causes audio issues, in that case go with SDL.
|
||||
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
|
||||
iter = find_backend("cubeb");
|
||||
if (iter->latency() > TargetSampleCount * 3) {
|
||||
iter = find_backend("sdl2");
|
||||
}
|
||||
// Auto-select.
|
||||
// sink_details is ordered in terms of desirability, with the best choice at the front.
|
||||
#else
|
||||
iter = std::begin(sink_details);
|
||||
#endif
|
||||
LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id);
|
||||
}
|
||||
|
||||
if (iter == std::end(sink_details)) {
|
||||
LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
|
||||
iter = find_backend("null");
|
||||
}
|
||||
|
||||
return *iter;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::vector<const char*> GetSinkIDs() {
|
||||
std::vector<const char*> sink_ids(std::size(sink_details));
|
||||
std::vector<std::string_view> GetSinkIDs() {
|
||||
std::vector<std::string_view> sink_ids(std::size(sink_details));
|
||||
|
||||
std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
|
||||
[](const auto& sink) { return sink.id; });
|
||||
|
||||
@@ -19,7 +19,7 @@ class Sink;
|
||||
*
|
||||
* @return Vector of available sink names.
|
||||
*/
|
||||
std::vector<const char*> GetSinkIDs();
|
||||
std::vector<std::string_view> GetSinkIDs();
|
||||
|
||||
/**
|
||||
* Gets the list of devices for a particular sink identified by the given ID.
|
||||
|
||||
@@ -277,8 +277,9 @@ struct CallbackStatus {
|
||||
BodyColorStatus color_status{};
|
||||
BatteryStatus battery_status{};
|
||||
VibrationStatus vibration_status{};
|
||||
CameraStatus camera_status{};
|
||||
NfcStatus nfc_status{};
|
||||
CameraFormat camera_status{CameraFormat::None};
|
||||
NfcState nfc_status{NfcState::Unknown};
|
||||
std::vector<u8> raw_data{};
|
||||
};
|
||||
|
||||
// Triggered once every input change
|
||||
|
||||
@@ -431,7 +431,7 @@ struct Values {
|
||||
FullscreenMode::Exclusive,
|
||||
#endif
|
||||
FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
|
||||
SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"};
|
||||
SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"};
|
||||
SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
|
||||
SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
|
||||
SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
|
||||
|
||||
@@ -261,8 +261,6 @@ add_library(core STATIC
|
||||
hle/kernel/k_worker_task.h
|
||||
hle/kernel/k_worker_task_manager.cpp
|
||||
hle/kernel/k_worker_task_manager.h
|
||||
hle/kernel/k_writable_event.cpp
|
||||
hle/kernel/k_writable_event.h
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory_types.h
|
||||
|
||||
@@ -111,6 +111,7 @@ public:
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, memory.Read32(pc));
|
||||
ReturnException(pc, ARM_Interface::no_execute);
|
||||
}
|
||||
|
||||
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
|
||||
|
||||
@@ -270,6 +270,7 @@ void CoreTiming::ThreadLoop() {
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
const auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
// Assume a timer resolution of 1ms.
|
||||
static constexpr s64 TimerResolutionNS = 1000000;
|
||||
|
||||
@@ -287,6 +288,9 @@ void CoreTiming::ThreadLoop() {
|
||||
if (event.IsSet()) {
|
||||
event.Reset();
|
||||
}
|
||||
#else
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Queue is empty, wait until another event is scheduled and signals us to continue.
|
||||
|
||||
@@ -67,6 +67,8 @@ float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) {
|
||||
return 3.0f / 4.0f;
|
||||
case AspectRatio::R21_9:
|
||||
return 9.0f / 21.0f;
|
||||
case AspectRatio::R16_10:
|
||||
return 10.0f / 16.0f;
|
||||
case AspectRatio::StretchToWindow:
|
||||
return window_aspect_ratio;
|
||||
default:
|
||||
|
||||
@@ -27,6 +27,7 @@ enum class AspectRatio {
|
||||
Default,
|
||||
R4_3,
|
||||
R21_9,
|
||||
R16_10,
|
||||
StretchToWindow,
|
||||
};
|
||||
|
||||
|
||||
@@ -277,7 +277,10 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu
|
||||
Common::Input::CameraStatus camera{};
|
||||
switch (callback.type) {
|
||||
case Common::Input::InputType::IrSensor:
|
||||
camera = callback.camera_status;
|
||||
camera = {
|
||||
.format = callback.camera_status,
|
||||
.data = callback.raw_data,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
|
||||
@@ -291,7 +294,10 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal
|
||||
Common::Input::NfcStatus nfc{};
|
||||
switch (callback.type) {
|
||||
case Common::Input::InputType::Nfc:
|
||||
return callback.nfc_status;
|
||||
nfc = {
|
||||
.state = callback.nfc_status,
|
||||
.data = callback.raw_data,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
|
||||
|
||||
@@ -14,7 +14,7 @@ enum class CameraAmbientNoiseLevel : u32 {
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Unkown3, // This level can't be reached
|
||||
Unknown3, // This level can't be reached
|
||||
};
|
||||
|
||||
// This is nn::irsensor::CameraLightTarget
|
||||
@@ -75,9 +75,9 @@ enum class IrCameraStatus : u32 {
|
||||
enum class IrCameraInternalStatus : u32 {
|
||||
Stopped,
|
||||
FirmwareUpdateNeeded,
|
||||
Unkown2,
|
||||
Unkown3,
|
||||
Unkown4,
|
||||
Unknown2,
|
||||
Unknown3,
|
||||
Unknown4,
|
||||
FirmwareVersionRequested,
|
||||
FirmwareVersionIsInvalid,
|
||||
Ready,
|
||||
@@ -121,20 +121,20 @@ enum class IrSensorFunctionLevel : u8 {
|
||||
|
||||
// This is nn::irsensor::MomentProcessorPreprocess
|
||||
enum class MomentProcessorPreprocess : u32 {
|
||||
Unkown0,
|
||||
Unkown1,
|
||||
Unknown0,
|
||||
Unknown1,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::PackedMomentProcessorPreprocess
|
||||
enum class PackedMomentProcessorPreprocess : u8 {
|
||||
Unkown0,
|
||||
Unkown1,
|
||||
Unknown0,
|
||||
Unknown1,
|
||||
};
|
||||
|
||||
// This is nn::irsensor::PointingStatus
|
||||
enum class PointingStatus : u32 {
|
||||
Unkown0,
|
||||
Unkown1,
|
||||
Unknown0,
|
||||
Unknown1,
|
||||
};
|
||||
|
||||
struct IrsRect {
|
||||
|
||||
@@ -152,7 +152,8 @@ public:
|
||||
Kernel::LimitableResource::Sessions, 1);
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(nullptr, iface->GetServiceName());
|
||||
session->Initialize(nullptr, iface->GetServiceName(),
|
||||
std::make_shared<Kernel::SessionRequestManager>(kernel));
|
||||
|
||||
context->AddMoveObject(&session->GetClientSession());
|
||||
iface->ClientConnected(&session->GetServerSession());
|
||||
|
||||
@@ -43,13 +43,13 @@ class Domain;
|
||||
class HLERequestContext;
|
||||
class KAutoObject;
|
||||
class KernelCore;
|
||||
class KEvent;
|
||||
class KHandleTable;
|
||||
class KProcess;
|
||||
class KServerSession;
|
||||
class KThread;
|
||||
class KReadableEvent;
|
||||
class KSession;
|
||||
class KWritableEvent;
|
||||
class ServiceThread;
|
||||
|
||||
enum class ThreadWakeupReason;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -42,13 +41,12 @@ static_assert(ClassToken<KPort> == 0b10000101'00000000);
|
||||
static_assert(ClassToken<KSession> == 0b00011001'00000000);
|
||||
static_assert(ClassToken<KSharedMemory> == 0b00101001'00000000);
|
||||
static_assert(ClassToken<KEvent> == 0b01001001'00000000);
|
||||
static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
|
||||
// static_assert(ClassToken<KLightClientSession> == 0b00110001'00000000);
|
||||
// static_assert(ClassToken<KLightServerSession> == 0b01010001'00000000);
|
||||
static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
|
||||
static_assert(ClassToken<KTransferMemory> == 0b01010001'00000000);
|
||||
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
|
||||
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
|
||||
static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
|
||||
static_assert(ClassToken<KCodeMemory> == 0b10100001'00000000);
|
||||
|
||||
// Ensure that the token hierarchy is correct.
|
||||
|
||||
@@ -73,13 +71,12 @@ static_assert(ClassToken<KPort> == ((0b10000101 << 8) | ClassToken<KAutoObject>)
|
||||
static_assert(ClassToken<KSession> == ((0b00011001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KSharedMemory> == ((0b00101001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KEvent> == ((0b01001001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAutoObject>));
|
||||
// static_assert(ClassToken<KLightClientSession> == ((0b00110001 << 8) | ClassToken<KAutoObject>));
|
||||
// static_assert(ClassToken<KLightServerSession> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KTransferMemory> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
|
||||
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
|
||||
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KCodeMemory> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
|
||||
|
||||
// Ensure that the token hierarchy reflects the class hierarchy.
|
||||
|
||||
@@ -110,7 +107,6 @@ static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
|
||||
static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
|
||||
static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
|
||||
static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
|
||||
static_assert(std::is_final_v<KWritableEvent> && std::is_base_of_v<KAutoObject, KWritableEvent>);
|
||||
// static_assert(std::is_final_v<KLightClientSession> &&
|
||||
// std::is_base_of_v<KAutoObject, KLightClientSession>);
|
||||
// static_assert(std::is_final_v<KLightServerSession> &&
|
||||
|
||||
@@ -101,7 +101,6 @@ public:
|
||||
KSession,
|
||||
KSharedMemory,
|
||||
KEvent,
|
||||
KWritableEvent,
|
||||
KLightClientSession,
|
||||
KLightServerSession,
|
||||
KTransferMemory,
|
||||
|
||||
@@ -21,10 +21,9 @@ void KClientSession::Destroy() {
|
||||
|
||||
void KClientSession::OnServerClosed() {}
|
||||
|
||||
Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing) {
|
||||
Result KClientSession::SendSyncRequest() {
|
||||
// Signal the server session that new data is available
|
||||
return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing);
|
||||
return parent->GetServerSession().OnRequest();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -46,8 +46,7 @@ public:
|
||||
return parent;
|
||||
}
|
||||
|
||||
Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing);
|
||||
Result SendSyncRequest();
|
||||
|
||||
void OnServerClosed();
|
||||
|
||||
|
||||
@@ -8,39 +8,45 @@
|
||||
namespace Kernel {
|
||||
|
||||
KEvent::KEvent(KernelCore& kernel_)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel_}, readable_event{kernel_}, writable_event{
|
||||
kernel_} {}
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_readable_event{kernel_} {}
|
||||
|
||||
KEvent::~KEvent() = default;
|
||||
|
||||
void KEvent::Initialize(std::string&& name_, KProcess* owner_) {
|
||||
// Increment reference count.
|
||||
// Because reference count is one on creation, this will result
|
||||
// in a reference count of two. Thus, when both readable and
|
||||
// writable events are closed this object will be destroyed.
|
||||
Open();
|
||||
void KEvent::Initialize(KProcess* owner) {
|
||||
// Create our readable event.
|
||||
KAutoObject::Create(std::addressof(m_readable_event));
|
||||
|
||||
// Create our sub events.
|
||||
KAutoObject::Create(std::addressof(readable_event));
|
||||
KAutoObject::Create(std::addressof(writable_event));
|
||||
|
||||
// Initialize our sub sessions.
|
||||
readable_event.Initialize(this, name_ + ":Readable");
|
||||
writable_event.Initialize(this, name_ + ":Writable");
|
||||
// Initialize our readable event.
|
||||
m_readable_event.Initialize(this);
|
||||
|
||||
// Set our owner process.
|
||||
owner = owner_;
|
||||
owner->Open();
|
||||
m_owner = owner;
|
||||
m_owner->Open();
|
||||
|
||||
// Mark initialized.
|
||||
name = std::move(name_);
|
||||
initialized = true;
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void KEvent::Finalize() {
|
||||
KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize();
|
||||
}
|
||||
|
||||
Result KEvent::Signal() {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
R_SUCCEED_IF(m_readable_event_destroyed);
|
||||
|
||||
return m_readable_event.Signal();
|
||||
}
|
||||
|
||||
Result KEvent::Clear() {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
R_SUCCEED_IF(m_readable_event_destroyed);
|
||||
|
||||
return m_readable_event.Clear();
|
||||
}
|
||||
|
||||
void KEvent::PostDestroy(uintptr_t arg) {
|
||||
// Release the event count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class KReadableEvent;
|
||||
class KWritableEvent;
|
||||
class KProcess;
|
||||
|
||||
class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> {
|
||||
@@ -21,37 +19,40 @@ public:
|
||||
explicit KEvent(KernelCore& kernel_);
|
||||
~KEvent() override;
|
||||
|
||||
void Initialize(std::string&& name, KProcess* owner_);
|
||||
void Initialize(KProcess* owner);
|
||||
|
||||
void Finalize() override;
|
||||
|
||||
bool IsInitialized() const override {
|
||||
return initialized;
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
uintptr_t GetPostDestroyArgument() const override {
|
||||
return reinterpret_cast<uintptr_t>(owner);
|
||||
return reinterpret_cast<uintptr_t>(m_owner);
|
||||
}
|
||||
|
||||
KProcess* GetOwner() const override {
|
||||
return owner;
|
||||
return m_owner;
|
||||
}
|
||||
|
||||
KReadableEvent& GetReadableEvent() {
|
||||
return readable_event;
|
||||
}
|
||||
|
||||
KWritableEvent& GetWritableEvent() {
|
||||
return writable_event;
|
||||
return m_readable_event;
|
||||
}
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
|
||||
void OnReadableEventDestroyed() {
|
||||
m_readable_event_destroyed = true;
|
||||
}
|
||||
|
||||
private:
|
||||
KReadableEvent readable_event;
|
||||
KWritableEvent writable_event;
|
||||
KProcess* owner{};
|
||||
bool initialized{};
|
||||
KReadableEvent m_readable_event;
|
||||
KProcess* m_owner{};
|
||||
bool m_initialized{};
|
||||
bool m_readable_event_destroyed{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -15,31 +15,44 @@ KReadableEvent::KReadableEvent(KernelCore& kernel_) : KSynchronizationObject{ker
|
||||
|
||||
KReadableEvent::~KReadableEvent() = default;
|
||||
|
||||
bool KReadableEvent::IsSignaled() const {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
void KReadableEvent::Initialize(KEvent* parent) {
|
||||
m_is_signaled = false;
|
||||
m_parent = parent;
|
||||
|
||||
return is_signaled;
|
||||
if (m_parent != nullptr) {
|
||||
m_parent->Open();
|
||||
}
|
||||
}
|
||||
|
||||
bool KReadableEvent::IsSignaled() const {
|
||||
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
|
||||
|
||||
return m_is_signaled;
|
||||
}
|
||||
|
||||
void KReadableEvent::Destroy() {
|
||||
if (parent) {
|
||||
parent->Close();
|
||||
if (m_parent) {
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
m_parent->OnReadableEventDestroyed();
|
||||
}
|
||||
m_parent->Close();
|
||||
}
|
||||
}
|
||||
|
||||
Result KReadableEvent::Signal() {
|
||||
KScopedSchedulerLock lk{kernel};
|
||||
|
||||
if (!is_signaled) {
|
||||
is_signaled = true;
|
||||
NotifyAvailable();
|
||||
if (!m_is_signaled) {
|
||||
m_is_signaled = true;
|
||||
this->NotifyAvailable();
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result KReadableEvent::Clear() {
|
||||
Reset();
|
||||
this->Reset();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -47,11 +60,11 @@ Result KReadableEvent::Clear() {
|
||||
Result KReadableEvent::Reset() {
|
||||
KScopedSchedulerLock lk{kernel};
|
||||
|
||||
if (!is_signaled) {
|
||||
if (!m_is_signaled) {
|
||||
return ResultInvalidState;
|
||||
}
|
||||
|
||||
is_signaled = false;
|
||||
m_is_signaled = false;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,26 +20,23 @@ public:
|
||||
explicit KReadableEvent(KernelCore& kernel_);
|
||||
~KReadableEvent() override;
|
||||
|
||||
void Initialize(KEvent* parent_event_, std::string&& name_) {
|
||||
is_signaled = false;
|
||||
parent = parent_event_;
|
||||
name = std::move(name_);
|
||||
}
|
||||
void Initialize(KEvent* parent);
|
||||
|
||||
KEvent* GetParent() const {
|
||||
return parent;
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
|
||||
bool IsSignaled() const override;
|
||||
void Destroy() override;
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
Result Reset();
|
||||
|
||||
private:
|
||||
bool is_signaled{};
|
||||
KEvent* parent{};
|
||||
bool m_is_signaled{};
|
||||
KEvent* m_parent{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
@@ -18,13 +20,19 @@
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
||||
using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
|
||||
|
||||
static constexpr u32 MessageBufferSize = 0x100;
|
||||
|
||||
KServerSession::KServerSession(KernelCore& kernel_)
|
||||
: KSynchronizationObject{kernel_}, m_lock{kernel_} {}
|
||||
|
||||
KServerSession::~KServerSession() = default;
|
||||
|
||||
@@ -33,17 +41,14 @@ void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
|
||||
// Set member variables.
|
||||
parent = parent_session_;
|
||||
name = std::move(name_);
|
||||
|
||||
if (manager_) {
|
||||
manager = manager_;
|
||||
} else {
|
||||
manager = std::make_shared<SessionRequestManager>(kernel);
|
||||
}
|
||||
manager = manager_;
|
||||
}
|
||||
|
||||
void KServerSession::Destroy() {
|
||||
parent->OnServerClosed();
|
||||
|
||||
this->CleanupRequests();
|
||||
|
||||
parent->Close();
|
||||
|
||||
// Release host emulation members.
|
||||
@@ -54,13 +59,13 @@ void KServerSession::Destroy() {
|
||||
}
|
||||
|
||||
void KServerSession::OnClientClosed() {
|
||||
if (manager->HasSessionHandler()) {
|
||||
if (manager && manager->HasSessionHandler()) {
|
||||
manager->SessionHandler().ClientDisconnected(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool KServerSession::IsSignaled() const {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
|
||||
|
||||
// If the client is closed, we're always signaled.
|
||||
if (parent->IsClientClosed()) {
|
||||
@@ -68,7 +73,7 @@ bool KServerSession::IsSignaled() const {
|
||||
}
|
||||
|
||||
// Otherwise, we're signaled if we have a request and aren't handling one.
|
||||
return false;
|
||||
return !m_thread_request_list.empty() && m_current_thread_request == nullptr;
|
||||
}
|
||||
|
||||
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
|
||||
@@ -173,9 +178,221 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing) {
|
||||
return QueueSyncRequest(thread, memory);
|
||||
Result KServerSession::OnRequest() {
|
||||
// Create the wait queue.
|
||||
ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
|
||||
|
||||
{
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Ensure that we can handle new requests.
|
||||
R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
|
||||
|
||||
// Check that we're not terminating.
|
||||
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
|
||||
|
||||
if (manager) {
|
||||
// HLE request.
|
||||
auto& memory{kernel.System().Memory()};
|
||||
this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
|
||||
} else {
|
||||
// Non-HLE request.
|
||||
auto* thread{GetCurrentThreadPointer(kernel)};
|
||||
|
||||
// Get whether we're empty.
|
||||
const bool was_empty = m_thread_request_list.empty();
|
||||
|
||||
// Add the thread to the list.
|
||||
thread->Open();
|
||||
m_thread_request_list.push_back(thread);
|
||||
|
||||
// If we were empty, signal.
|
||||
if (was_empty) {
|
||||
this->NotifyAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
// This is a synchronous request, so we should wait for our request to complete.
|
||||
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
GetCurrentThread(kernel).BeginWait(&wait_queue);
|
||||
}
|
||||
|
||||
return GetCurrentThread(kernel).GetWaitResult();
|
||||
}
|
||||
|
||||
Result KServerSession::SendReply() {
|
||||
// Lock the session.
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Get the request.
|
||||
KThread* client_thread;
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Get the current request.
|
||||
client_thread = m_current_thread_request;
|
||||
R_UNLESS(client_thread != nullptr, ResultInvalidState);
|
||||
|
||||
// Clear the current request, since we're processing it.
|
||||
m_current_thread_request = nullptr;
|
||||
if (!m_thread_request_list.empty()) {
|
||||
this->NotifyAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
// Close reference to the request once we're done processing it.
|
||||
SCOPE_EXIT({ client_thread->Close(); });
|
||||
|
||||
// Extract relevant information from the request.
|
||||
// const uintptr_t client_message = request->GetAddress();
|
||||
// const size_t client_buffer_size = request->GetSize();
|
||||
// KThread *client_thread = request->GetThread();
|
||||
// KEvent *event = request->GetEvent();
|
||||
|
||||
// Check whether we're closed.
|
||||
const bool closed = (client_thread == nullptr || parent->IsClientClosed());
|
||||
|
||||
Result result = ResultSuccess;
|
||||
if (!closed) {
|
||||
// If we're not closed, send the reply.
|
||||
Core::Memory::Memory& memory{kernel.System().Memory()};
|
||||
KThread* server_thread{GetCurrentThreadPointer(kernel)};
|
||||
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||
|
||||
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
||||
auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
|
||||
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
||||
} else {
|
||||
result = ResultSessionClosed;
|
||||
}
|
||||
|
||||
// Select a result for the client.
|
||||
Result client_result = result;
|
||||
if (closed && R_SUCCEEDED(result)) {
|
||||
result = ResultSessionClosed;
|
||||
client_result = ResultSessionClosed;
|
||||
} else {
|
||||
result = ResultSuccess;
|
||||
}
|
||||
|
||||
// If there's a client thread, update it.
|
||||
if (client_thread != nullptr) {
|
||||
// End the client thread's wait.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (!client_thread->IsTerminationRequested()) {
|
||||
client_thread->EndWait(client_result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Result KServerSession::ReceiveRequest() {
|
||||
// Lock the session.
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Get the request and client thread.
|
||||
// KSessionRequest *request;
|
||||
KThread* client_thread;
|
||||
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Ensure that we can service the request.
|
||||
R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
|
||||
|
||||
// Ensure we aren't already servicing a request.
|
||||
R_UNLESS(m_current_thread_request == nullptr, ResultNotFound);
|
||||
|
||||
// Ensure we have a request to service.
|
||||
R_UNLESS(!m_thread_request_list.empty(), ResultNotFound);
|
||||
|
||||
// Pop the first request from the list.
|
||||
client_thread = m_thread_request_list.front();
|
||||
m_thread_request_list.pop_front();
|
||||
|
||||
// Get the thread for the request.
|
||||
R_UNLESS(client_thread != nullptr, ResultSessionClosed);
|
||||
|
||||
// Open the client thread.
|
||||
client_thread->Open();
|
||||
}
|
||||
|
||||
// SCOPE_EXIT({ client_thread->Close(); });
|
||||
|
||||
// Set the request as our current.
|
||||
m_current_thread_request = client_thread;
|
||||
|
||||
// Receive the message.
|
||||
Core::Memory::Memory& memory{kernel.System().Memory()};
|
||||
KThread* server_thread{GetCurrentThreadPointer(kernel)};
|
||||
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||
|
||||
auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
|
||||
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
||||
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
||||
|
||||
// We succeeded.
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KServerSession::CleanupRequests() {
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Clean up any pending requests.
|
||||
while (true) {
|
||||
// Get the next request.
|
||||
// KSessionRequest *request = nullptr;
|
||||
KThread* client_thread = nullptr;
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (m_current_thread_request) {
|
||||
// Choose the current request if we have one.
|
||||
client_thread = m_current_thread_request;
|
||||
m_current_thread_request = nullptr;
|
||||
} else if (!m_thread_request_list.empty()) {
|
||||
// Pop the request from the front of the list.
|
||||
client_thread = m_thread_request_list.front();
|
||||
m_thread_request_list.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no request, we're done.
|
||||
if (client_thread == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Close a reference to the request once it's cleaned up.
|
||||
SCOPE_EXIT({ client_thread->Close(); });
|
||||
|
||||
// Extract relevant information from the request.
|
||||
// const uintptr_t client_message = request->GetAddress();
|
||||
// const size_t client_buffer_size = request->GetSize();
|
||||
// KThread *client_thread = request->GetThread();
|
||||
// KEvent *event = request->GetEvent();
|
||||
|
||||
// KProcess *server_process = request->GetServerProcess();
|
||||
// KProcess *client_process = (client_thread != nullptr) ?
|
||||
// client_thread->GetOwnerProcess() : nullptr;
|
||||
// KProcessPageTable *client_page_table = (client_process != nullptr) ?
|
||||
// &client_process->GetPageTable() : nullptr;
|
||||
|
||||
// Cleanup the mappings.
|
||||
// Result result = CleanupMap(request, server_process, client_page_table);
|
||||
|
||||
// If there's a client thread, update it.
|
||||
if (client_thread != nullptr) {
|
||||
// End the client thread's wait.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (!client_thread->IsTerminationRequested()) {
|
||||
client_thread->EndWait(ResultSessionClosed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -59,25 +61,15 @@ public:
|
||||
void OnClientClosed();
|
||||
|
||||
void ClientConnected(SessionRequestHandlerPtr handler) {
|
||||
manager->SetSessionHandler(std::move(handler));
|
||||
if (manager) {
|
||||
manager->SetSessionHandler(std::move(handler));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDisconnected() {
|
||||
manager = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a sync request from the emulated application.
|
||||
*
|
||||
* @param thread Thread that initiated the request.
|
||||
* @param memory Memory context to handle the sync request under.
|
||||
* @param core_timing Core timing context to schedule the request event under.
|
||||
*
|
||||
* @returns Result from the operation.
|
||||
*/
|
||||
Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing);
|
||||
|
||||
/// Adds a new domain request handler to the collection of request handlers within
|
||||
/// this ServerSession instance.
|
||||
void AppendDomainHandler(SessionRequestHandlerPtr handler);
|
||||
@@ -88,7 +80,7 @@ public:
|
||||
|
||||
/// Returns true if the session has been converted to a domain, otherwise False
|
||||
bool IsDomain() const {
|
||||
return manager->IsDomain();
|
||||
return manager && manager->IsDomain();
|
||||
}
|
||||
|
||||
/// Converts the session to a domain at the end of the current command
|
||||
@@ -101,7 +93,15 @@ public:
|
||||
return manager;
|
||||
}
|
||||
|
||||
/// TODO: flesh these out to match the real kernel
|
||||
Result OnRequest();
|
||||
Result SendReply();
|
||||
Result ReceiveRequest();
|
||||
|
||||
private:
|
||||
/// Frees up waiting client sessions when this server session is about to die
|
||||
void CleanupRequests();
|
||||
|
||||
/// Queues a sync request from the emulated application.
|
||||
Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
|
||||
|
||||
@@ -112,7 +112,7 @@ private:
|
||||
/// object handle.
|
||||
Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
|
||||
|
||||
/// This session's HLE request handlers
|
||||
/// This session's HLE request handlers; if nullptr, this is not an HLE server
|
||||
std::shared_ptr<SessionRequestManager> manager;
|
||||
|
||||
/// When set to True, converts the session to a domain at the end of the command
|
||||
@@ -120,6 +120,13 @@ private:
|
||||
|
||||
/// KSession that owns this KServerSession
|
||||
KSession* parent{};
|
||||
|
||||
/// List of threads which are pending a reply.
|
||||
/// FIXME: KSessionRequest
|
||||
std::list<KThread*> m_thread_request_list;
|
||||
KThread* m_current_thread_request{};
|
||||
|
||||
KLightLock m_lock;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KWritableEvent::KWritableEvent(KernelCore& kernel_)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
||||
|
||||
KWritableEvent::~KWritableEvent() = default;
|
||||
|
||||
void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) {
|
||||
parent = parent_event_;
|
||||
name = std::move(name_);
|
||||
parent->GetReadableEvent().Open();
|
||||
}
|
||||
|
||||
Result KWritableEvent::Signal() {
|
||||
return parent->GetReadableEvent().Signal();
|
||||
}
|
||||
|
||||
Result KWritableEvent::Clear() {
|
||||
return parent->GetReadableEvent().Clear();
|
||||
}
|
||||
|
||||
void KWritableEvent::Destroy() {
|
||||
// Close our references.
|
||||
parent->GetReadableEvent().Close();
|
||||
parent->Close();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -1,39 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class KEvent;
|
||||
|
||||
class KWritableEvent final
|
||||
: public KAutoObjectWithSlabHeapAndContainer<KWritableEvent, KAutoObjectWithList> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KWritableEvent, KAutoObject);
|
||||
|
||||
public:
|
||||
explicit KWritableEvent(KernelCore& kernel_);
|
||||
~KWritableEvent() override;
|
||||
|
||||
void Destroy() override;
|
||||
|
||||
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
|
||||
|
||||
void Initialize(KEvent* parent_, std::string&& name_);
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
|
||||
KEvent* GetParent() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
private:
|
||||
KEvent* parent{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -52,7 +52,6 @@ class KThread;
|
||||
class KThreadLocalPage;
|
||||
class KTransferMemory;
|
||||
class KWorkerTaskManager;
|
||||
class KWritableEvent;
|
||||
class KCodeMemory;
|
||||
class PhysicalCore;
|
||||
class ServiceThread;
|
||||
@@ -345,8 +344,6 @@ public:
|
||||
return slab_heap_container->thread;
|
||||
} else if constexpr (std::is_same_v<T, KTransferMemory>) {
|
||||
return slab_heap_container->transfer_memory;
|
||||
} else if constexpr (std::is_same_v<T, KWritableEvent>) {
|
||||
return slab_heap_container->writeable_event;
|
||||
} else if constexpr (std::is_same_v<T, KCodeMemory>) {
|
||||
return slab_heap_container->code_memory;
|
||||
} else if constexpr (std::is_same_v<T, KPageBuffer>) {
|
||||
@@ -412,7 +409,6 @@ private:
|
||||
KSlabHeap<KSharedMemoryInfo> shared_memory_info;
|
||||
KSlabHeap<KThread> thread;
|
||||
KSlabHeap<KTransferMemory> transfer_memory;
|
||||
KSlabHeap<KWritableEvent> writeable_event;
|
||||
KSlabHeap<KCodeMemory> code_memory;
|
||||
KSlabHeap<KPageBuffer> page_buffer;
|
||||
KSlabHeap<KThreadLocalPage> thread_local_page;
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@@ -256,6 +256,93 @@ static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u3
|
||||
return UnmapMemory(system, dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
|
||||
auto& process = *system.CurrentProcess();
|
||||
auto& handle_table = process.GetHandleTable();
|
||||
|
||||
// Declare the session we're going to allocate.
|
||||
T* session;
|
||||
|
||||
// Reserve a new session from the process resource limit.
|
||||
// FIXME: LimitableResource_SessionCountMax
|
||||
KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions);
|
||||
if (session_reservation.Succeeded()) {
|
||||
session = T::Create(system.Kernel());
|
||||
} else {
|
||||
return ResultLimitReached;
|
||||
|
||||
// // We couldn't reserve a session. Check that we support dynamically expanding the
|
||||
// // resource limit.
|
||||
// R_UNLESS(process.GetResourceLimit() ==
|
||||
// &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
|
||||
// R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
|
||||
|
||||
// // Try to allocate a session from unused slab memory.
|
||||
// session = T::CreateFromUnusedSlabMemory();
|
||||
// R_UNLESS(session != nullptr, ResultLimitReached);
|
||||
// ON_RESULT_FAILURE { session->Close(); };
|
||||
|
||||
// // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
|
||||
// // prevent request exhaustion.
|
||||
// // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
|
||||
// // no reason to not do this statically.
|
||||
// if constexpr (std::same_as<T, KSession>) {
|
||||
// for (size_t i = 0; i < 2; i++) {
|
||||
// KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
|
||||
// R_UNLESS(request != nullptr, ResultLimitReached);
|
||||
// request->Close();
|
||||
// }
|
||||
// }
|
||||
|
||||
// We successfully allocated a session, so add the object we allocated to the resource
|
||||
// limit.
|
||||
// system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1);
|
||||
}
|
||||
|
||||
// Check that we successfully created a session.
|
||||
R_UNLESS(session != nullptr, ResultOutOfResource);
|
||||
|
||||
// Initialize the session.
|
||||
session->Initialize(nullptr, fmt::format("{}", name));
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
|
||||
// Ensure that we clean up the session (and its only references are handle table) on function
|
||||
// end.
|
||||
SCOPE_EXIT({
|
||||
session->GetClientSession().Close();
|
||||
session->GetServerSession().Close();
|
||||
});
|
||||
|
||||
// Register the session.
|
||||
T::Register(system.Kernel(), session);
|
||||
|
||||
// Add the server session to the handle table.
|
||||
R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
|
||||
|
||||
// Add the client session to the handle table.
|
||||
const auto result = handle_table.Add(out_client, &session->GetClientSession());
|
||||
|
||||
if (!R_SUCCEEDED(result)) {
|
||||
// Ensure that we maintaing a clean handle state on exit.
|
||||
handle_table.Remove(*out_server);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client,
|
||||
u32 is_light, u64 name) {
|
||||
if (is_light) {
|
||||
// return CreateSession<KLightSession>(system, out_server, out_client, name);
|
||||
return ResultUnknown;
|
||||
} else {
|
||||
return CreateSession<KSession>(system, out_server, out_client, name);
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
|
||||
auto& memory = system.Memory();
|
||||
@@ -295,7 +382,8 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
|
||||
|
||||
// Create a session.
|
||||
KClientSession* session{};
|
||||
R_TRY(port->CreateSession(std::addressof(session)));
|
||||
R_TRY(port->CreateSession(std::addressof(session),
|
||||
std::make_shared<SessionRequestManager>(kernel)));
|
||||
port->Close();
|
||||
|
||||
// Register the session in the table, close the extra reference.
|
||||
@@ -313,7 +401,7 @@ static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
|
||||
return ConnectToNamedPort(system, out_handle, port_name_address);
|
||||
}
|
||||
|
||||
/// Makes a blocking IPC call to an OS service.
|
||||
/// Makes a blocking IPC call to a service.
|
||||
static Result SendSyncRequest(Core::System& system, Handle handle) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
@@ -327,22 +415,75 @@ static Result SendSyncRequest(Core::System& system, Handle handle) {
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
||||
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
|
||||
// This is a synchronous request, so we should wait for our request to complete.
|
||||
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
|
||||
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
|
||||
}
|
||||
|
||||
return GetCurrentThread(kernel).GetWaitResult();
|
||||
return session->SendSyncRequest();
|
||||
}
|
||||
|
||||
static Result SendSyncRequest32(Core::System& system, Handle handle) {
|
||||
return SendSyncRequest(system, handle);
|
||||
}
|
||||
|
||||
static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles,
|
||||
s32 num_handles, Handle reply_target, s64 timeout_ns) {
|
||||
auto& kernel = system.Kernel();
|
||||
auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
|
||||
|
||||
// Convert handle list to object table.
|
||||
std::vector<KSynchronizationObject*> objs(num_handles);
|
||||
R_UNLESS(
|
||||
handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
|
||||
ResultInvalidHandle);
|
||||
|
||||
// Ensure handles are closed when we're done.
|
||||
SCOPE_EXIT({
|
||||
for (auto i = 0; i < num_handles; ++i) {
|
||||
objs[i]->Close();
|
||||
}
|
||||
});
|
||||
|
||||
// Reply to the target, if one is specified.
|
||||
if (reply_target != InvalidHandle) {
|
||||
KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// If we fail to reply, we want to set the output index to -1.
|
||||
// ON_RESULT_FAILURE { *out_index = -1; };
|
||||
|
||||
// Send the reply.
|
||||
// R_TRY(session->SendReply());
|
||||
|
||||
Result rc = session->SendReply();
|
||||
if (!R_SUCCEEDED(rc)) {
|
||||
*out_index = -1;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a message.
|
||||
while (true) {
|
||||
// Wait for an object.
|
||||
s32 index;
|
||||
Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
|
||||
static_cast<s32>(objs.size()), timeout_ns);
|
||||
if (result == ResultTimedOut) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Receive the request.
|
||||
if (R_SUCCEEDED(result)) {
|
||||
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
|
||||
if (session != nullptr) {
|
||||
result = session->ReceiveRequest();
|
||||
if (result == ResultNotFound) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_index = index;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the ID for the specified thread.
|
||||
static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
|
||||
// Get the thread from its handle.
|
||||
@@ -2303,11 +2444,11 @@ static Result SignalEvent(Core::System& system, Handle event_handle) {
|
||||
// Get the current handle table.
|
||||
const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
|
||||
// Get the writable event.
|
||||
KScopedAutoObject writable_event = handle_table.GetObject<KWritableEvent>(event_handle);
|
||||
R_UNLESS(writable_event.IsNotNull(), ResultInvalidHandle);
|
||||
// Get the event.
|
||||
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
|
||||
R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
return writable_event->Signal();
|
||||
return event->Signal();
|
||||
}
|
||||
|
||||
static Result SignalEvent32(Core::System& system, Handle event_handle) {
|
||||
@@ -2322,9 +2463,9 @@ static Result ClearEvent(Core::System& system, Handle event_handle) {
|
||||
|
||||
// Try to clear the writable event.
|
||||
{
|
||||
KScopedAutoObject writable_event = handle_table.GetObject<KWritableEvent>(event_handle);
|
||||
if (writable_event.IsNotNull()) {
|
||||
return writable_event->Clear();
|
||||
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
|
||||
if (event.IsNotNull()) {
|
||||
return event->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2362,24 +2503,24 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r
|
||||
R_UNLESS(event != nullptr, ResultOutOfResource);
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize("CreateEvent", kernel.CurrentProcess());
|
||||
event->Initialize(kernel.CurrentProcess());
|
||||
|
||||
// Commit the thread reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
// Ensure that we clean up the event (and its only references are handle table) on function end.
|
||||
SCOPE_EXIT({
|
||||
event->GetWritableEvent().Close();
|
||||
event->GetReadableEvent().Close();
|
||||
event->Close();
|
||||
});
|
||||
|
||||
// Register the event.
|
||||
KEvent::Register(kernel, event);
|
||||
|
||||
// Add the writable event to the handle table.
|
||||
R_TRY(handle_table.Add(out_write, std::addressof(event->GetWritableEvent())));
|
||||
// Add the event to the handle table.
|
||||
R_TRY(handle_table.Add(out_write, event));
|
||||
|
||||
// Add the writable event to the handle table.
|
||||
// Ensure that we maintaing a clean handle state on exit.
|
||||
auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
|
||||
|
||||
// Add the readable event to the handle table.
|
||||
@@ -2860,10 +3001,10 @@ static const FunctionDef SVC_Table_64[] = {
|
||||
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
|
||||
{0x3E, nullptr, "Unknown3e"},
|
||||
{0x3F, nullptr, "Unknown3f"},
|
||||
{0x40, nullptr, "CreateSession"},
|
||||
{0x40, SvcWrap64<CreateSession>, "CreateSession"},
|
||||
{0x41, nullptr, "AcceptSession"},
|
||||
{0x42, nullptr, "ReplyAndReceiveLight"},
|
||||
{0x43, nullptr, "ReplyAndReceive"},
|
||||
{0x43, SvcWrap64<ReplyAndReceive>, "ReplyAndReceive"},
|
||||
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
|
||||
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
|
||||
{0x46, nullptr, "MapIoRegion"},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -346,6 +347,37 @@ void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by CreateSession
|
||||
template <Result func(Core::System&, Handle*, Handle*, u32, u64)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
Handle param_1 = 0;
|
||||
Handle param_2 = 0;
|
||||
const u32 retval = func(system, ¶m_1, ¶m_2, static_cast<u32>(Param(system, 2)),
|
||||
static_cast<u32>(Param(system, 3)))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
system.CurrentArmInterface().SetReg(2, param_2);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by ReplyAndReceive
|
||||
template <Result func(Core::System&, s32*, Handle*, s32, Handle, s64)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
s32 param_1 = 0;
|
||||
s32 num_handles = static_cast<s32>(Param(system, 2));
|
||||
|
||||
std::vector<Handle> handles(num_handles);
|
||||
system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle));
|
||||
|
||||
const u32 retval = func(system, ¶m_1, handles.data(), num_handles,
|
||||
static_cast<s32>(Param(system, 3)), static_cast<s64>(Param(system, 4)))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by WaitForAddress
|
||||
template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/expected.h"
|
||||
|
||||
@@ -130,6 +131,10 @@ union Result {
|
||||
[[nodiscard]] constexpr bool IsError() const {
|
||||
return !IsSuccess();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsFailure() const {
|
||||
return !IsSuccess();
|
||||
}
|
||||
};
|
||||
static_assert(std::is_trivial_v<Result>);
|
||||
|
||||
@@ -349,10 +354,109 @@ private:
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define R_SUCCEEDED(res) (res.IsSuccess())
|
||||
#define R_SUCCEEDED(res) (static_cast<Result>(res).IsSuccess())
|
||||
#define R_FAILED(res) (static_cast<Result>(res).IsFailure())
|
||||
|
||||
/// Evaluates a boolean expression, and succeeds if that expression is true.
|
||||
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)
|
||||
namespace ResultImpl {
|
||||
template <auto EvaluateResult, class F>
|
||||
class ScopedResultGuard {
|
||||
YUZU_NON_COPYABLE(ScopedResultGuard);
|
||||
YUZU_NON_MOVEABLE(ScopedResultGuard);
|
||||
|
||||
private:
|
||||
Result& m_ref;
|
||||
F m_f;
|
||||
|
||||
public:
|
||||
constexpr ScopedResultGuard(Result& ref, F f) : m_ref(ref), m_f(std::move(f)) {}
|
||||
constexpr ~ScopedResultGuard() {
|
||||
if (EvaluateResult(m_ref)) {
|
||||
m_f();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <auto EvaluateResult>
|
||||
class ResultReferenceForScopedResultGuard {
|
||||
private:
|
||||
Result& m_ref;
|
||||
|
||||
public:
|
||||
constexpr ResultReferenceForScopedResultGuard(Result& r) : m_ref(r) {}
|
||||
constexpr operator Result&() const {
|
||||
return m_ref;
|
||||
}
|
||||
};
|
||||
|
||||
template <auto EvaluateResult, typename F>
|
||||
constexpr ScopedResultGuard<EvaluateResult, F> operator+(
|
||||
ResultReferenceForScopedResultGuard<EvaluateResult> ref, F&& f) {
|
||||
return ScopedResultGuard<EvaluateResult, F>(static_cast<Result&>(ref), std::forward<F>(f));
|
||||
}
|
||||
|
||||
constexpr bool EvaluateResultSuccess(const Result& r) {
|
||||
return R_SUCCEEDED(r);
|
||||
}
|
||||
constexpr bool EvaluateResultFailure(const Result& r) {
|
||||
return R_FAILED(r);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete;
|
||||
// Intentionally not defined
|
||||
|
||||
template <>
|
||||
constexpr void UpdateCurrentResultReference<Result&>(Result& result_reference, Result result) {
|
||||
result_reference = result;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr void UpdateCurrentResultReference<const Result>(Result result_reference, Result result) {}
|
||||
} // namespace ResultImpl
|
||||
|
||||
#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \
|
||||
[[maybe_unused]] constexpr bool HasPrevRef_##COUNTER_VALUE = \
|
||||
std::same_as<decltype(__TmpCurrentResultReference), Result&>; \
|
||||
[[maybe_unused]] auto& PrevRef_##COUNTER_VALUE = __TmpCurrentResultReference; \
|
||||
[[maybe_unused]] Result __tmp_result_##COUNTER_VALUE = ResultSuccess; \
|
||||
Result& __TmpCurrentResultReference = \
|
||||
HasPrevRef_##COUNTER_VALUE ? PrevRef_##COUNTER_VALUE : __tmp_result_##COUNTER_VALUE
|
||||
|
||||
#define ON_RESULT_RETURN_IMPL(...) \
|
||||
static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>); \
|
||||
auto RESULT_GUARD_STATE_##__COUNTER__ = \
|
||||
ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \
|
||||
__TmpCurrentResultReference) + \
|
||||
[&]()
|
||||
|
||||
#define ON_RESULT_FAILURE_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultFailure)
|
||||
|
||||
#define ON_RESULT_FAILURE \
|
||||
DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
|
||||
ON_RESULT_FAILURE_2
|
||||
|
||||
#define ON_RESULT_SUCCESS_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultSuccess)
|
||||
|
||||
#define ON_RESULT_SUCCESS \
|
||||
DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
|
||||
ON_RESULT_SUCCESS_2
|
||||
|
||||
constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
|
||||
|
||||
/// Returns a result.
|
||||
#define R_RETURN(res_expr) \
|
||||
{ \
|
||||
const Result _tmp_r_throw_rc = (res_expr); \
|
||||
ResultImpl::UpdateCurrentResultReference<decltype(__TmpCurrentResultReference)>( \
|
||||
__TmpCurrentResultReference, _tmp_r_throw_rc); \
|
||||
return _tmp_r_throw_rc; \
|
||||
}
|
||||
|
||||
/// Returns ResultSuccess()
|
||||
#define R_SUCCEED() R_RETURN(ResultSuccess)
|
||||
|
||||
/// Throws a result.
|
||||
#define R_THROW(res_expr) R_RETURN(res_expr)
|
||||
|
||||
/// Evaluates a boolean expression, and returns a result unless that expression is true.
|
||||
#define R_UNLESS(expr, res) \
|
||||
@@ -361,7 +465,7 @@ private:
|
||||
if (res.IsError()) { \
|
||||
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
|
||||
} \
|
||||
return res; \
|
||||
R_THROW(res); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -369,7 +473,10 @@ private:
|
||||
#define R_TRY(res_expr) \
|
||||
{ \
|
||||
const auto _tmp_r_try_rc = (res_expr); \
|
||||
if (_tmp_r_try_rc.IsError()) { \
|
||||
return _tmp_r_try_rc; \
|
||||
if (R_FAILED(_tmp_r_try_rc)) { \
|
||||
R_THROW(_tmp_r_try_rc); \
|
||||
} \
|
||||
}
|
||||
|
||||
/// Evaluates a boolean expression, and succeeds if that expression is true.
|
||||
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)
|
||||
|
||||
@@ -64,7 +64,7 @@ void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void IAsyncContext::MarkComplete() {
|
||||
is_complete.store(true);
|
||||
completion_event->GetWritableEvent().Signal();
|
||||
completion_event->Signal();
|
||||
}
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -316,7 +316,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
|
||||
|
||||
accumulated_suspended_tick_changed_event =
|
||||
service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent");
|
||||
accumulated_suspended_tick_changed_event->GetWritableEvent().Signal();
|
||||
accumulated_suspended_tick_changed_event->Signal();
|
||||
}
|
||||
|
||||
ISelfController::~ISelfController() {
|
||||
@@ -378,7 +378,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
|
||||
void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
launchable_event->GetWritableEvent().Signal();
|
||||
launchable_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -618,18 +618,18 @@ Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
|
||||
|
||||
void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
messages.push(msg);
|
||||
on_new_message->GetWritableEvent().Signal();
|
||||
on_new_message->Signal();
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message->GetWritableEvent().Clear();
|
||||
on_new_message->Clear();
|
||||
return AppletMessage::None;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
if (messages.empty()) {
|
||||
on_new_message->GetWritableEvent().Clear();
|
||||
on_new_message->Clear();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
@@ -653,7 +653,7 @@ void AppletMessageQueue::FocusStateChanged() {
|
||||
void AppletMessageQueue::OperationModeChanged() {
|
||||
PushMessage(AppletMessage::OperationModeChanged);
|
||||
PushMessage(AppletMessage::PerformanceModeChanged);
|
||||
on_operation_mode_changed->GetWritableEvent().Signal();
|
||||
on_operation_mode_changed->Signal();
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
||||
|
||||
@@ -65,7 +65,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
|
||||
|
||||
auto out = std::move(out_channel.front());
|
||||
out_channel.pop_front();
|
||||
pop_out_data_event->GetWritableEvent().Clear();
|
||||
pop_out_data_event->Clear();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
|
||||
|
||||
auto out = std::move(out_interactive_channel.front());
|
||||
out_interactive_channel.pop_front();
|
||||
pop_interactive_out_data_event->GetWritableEvent().Clear();
|
||||
pop_interactive_out_data_event->Clear();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storag
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
|
||||
out_channel.emplace_back(std::move(storage));
|
||||
pop_out_data_event->GetWritableEvent().Signal();
|
||||
pop_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
|
||||
@@ -112,11 +112,11 @@ void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& s
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
|
||||
out_interactive_channel.emplace_back(std::move(storage));
|
||||
pop_interactive_out_data_event->GetWritableEvent().Signal();
|
||||
pop_interactive_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::SignalStateChanged() {
|
||||
state_changed_event->GetWritableEvent().Signal();
|
||||
state_changed_event->Signal();
|
||||
|
||||
switch (applet_mode) {
|
||||
case LibraryAppletMode::AllForeground:
|
||||
|
||||
@@ -239,7 +239,7 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
event->GetWritableEvent().Signal();
|
||||
event->Signal();
|
||||
}
|
||||
|
||||
~IAudioDevice() override {
|
||||
@@ -325,7 +325,7 @@ private:
|
||||
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called");
|
||||
|
||||
event->GetWritableEvent().Signal();
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@@ -82,7 +82,7 @@ void ProgressServiceBackend::FinishDownload(Result result) {
|
||||
}
|
||||
|
||||
void ProgressServiceBackend::SignalUpdate() {
|
||||
update_event->GetWritableEvent().Signal();
|
||||
update_event->Signal();
|
||||
}
|
||||
|
||||
Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
|
||||
|
||||
@@ -707,7 +707,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
||||
{31, nullptr, "OpenGameCardFileSystem"},
|
||||
{32, nullptr, "ExtendSaveDataFileSystem"},
|
||||
{33, nullptr, "DeleteCacheStorage"},
|
||||
{34, nullptr, "GetCacheStorageSize"},
|
||||
{34, &FSP_SRV::GetCacheStorageSize, "GetCacheStorageSize"},
|
||||
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
|
||||
{36, nullptr, "OpenHostFileSystemWithOption"},
|
||||
{51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
|
||||
@@ -1107,6 +1107,18 @@ void FSP_SRV::GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(access_log_program_index);
|
||||
}
|
||||
|
||||
void FSP_SRV::GetCacheStorageSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto index{rp.Pop<s32>()};
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(s64{0});
|
||||
rb.Push(s64{0});
|
||||
}
|
||||
|
||||
class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
|
||||
public:
|
||||
explicit IMultiCommitManager(Core::System& system_)
|
||||
|
||||
@@ -54,6 +54,7 @@ private:
|
||||
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
|
||||
void GetProgramIndexForAccessLog(Kernel::HLERequestContext& ctx);
|
||||
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
|
||||
void GetCacheStorageSize(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSystemController& fsc;
|
||||
const FileSys::ContentProvider& content_provider;
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{10101, &IFriendService::GetFriendList, "GetFriendList"},
|
||||
{10102, nullptr, "UpdateFriendInfo"},
|
||||
{10110, nullptr, "GetFriendProfileImage"},
|
||||
{10120, nullptr, "IsFriendListCacheAvailable"},
|
||||
{10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"},
|
||||
{10121, nullptr, "EnsureFriendListAvailable"},
|
||||
{10200, nullptr, "SendFriendRequestForApplication"},
|
||||
{10211, nullptr, "AddFacedFriendRequestForApplication"},
|
||||
@@ -194,6 +194,17 @@ private:
|
||||
// TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
|
||||
}
|
||||
|
||||
void CheckFriendListAvailability(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* completion_event;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
@@ -167,7 +166,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
const auto& battery_level = controller.device->GetBattery();
|
||||
auto* shared_memory = controller.shared_memory;
|
||||
if (controller_type == Core::HID::NpadStyleIndex::None) {
|
||||
controller.styleset_changed_event->GetWritableEvent().Signal();
|
||||
controller.styleset_changed_event->Signal();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1033,7 +1032,7 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad
|
||||
|
||||
void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
|
||||
const auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
controller.styleset_changed_event->GetWritableEvent().Signal();
|
||||
controller.styleset_changed_event->Signal();
|
||||
}
|
||||
|
||||
void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
|
||||
|
||||
@@ -73,7 +73,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
|
||||
operation.operation = PalmaOperationType::PlayActivity;
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data = {};
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
|
||||
operation.operation = PalmaOperationType::ReadStep;
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data = {};
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle
|
||||
operation.operation = PalmaOperationType::ReadUniqueCode;
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data = {};
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle&
|
||||
operation.operation = PalmaOperationType::SetUniqueCodeInvalid;
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data = {};
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl
|
||||
operation.operation = PalmaOperationType::WriteRgbLedPatternEntry;
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data = {};
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle
|
||||
operation.operation = PalmaOperationType::WriteWaveEntry;
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data = {};
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec
|
||||
operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data[0] = {};
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
|
||||
operation.result = PalmaResultSuccess;
|
||||
operation.data = {};
|
||||
operation.data[0] = static_cast<u8>(database_id_version);
|
||||
operation_complete_event->GetWritableEvent().Signal();
|
||||
operation_complete_event->Signal();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -2118,7 +2118,7 @@ void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, connection_handle={}, wave_set={}, unkown={}, "
|
||||
"(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
|
||||
"t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
|
||||
connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
|
||||
|
||||
|
||||
@@ -131,12 +131,12 @@ bool RingController::SetCommand(const std::vector<u8>& data) {
|
||||
case RingConCommands::ReadRepCount:
|
||||
case RingConCommands::ReadTotalPushCount:
|
||||
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
|
||||
send_command_async_event->GetWritableEvent().Signal();
|
||||
send_command_async_event->Signal();
|
||||
return true;
|
||||
case RingConCommands::ResetRepCount:
|
||||
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
|
||||
total_rep_count = 0;
|
||||
send_command_async_event->GetWritableEvent().Signal();
|
||||
send_command_async_event->Signal();
|
||||
return true;
|
||||
case RingConCommands::SaveCalData: {
|
||||
ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
|
||||
@@ -144,14 +144,14 @@ bool RingController::SetCommand(const std::vector<u8>& data) {
|
||||
SaveCalData save_info{};
|
||||
std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
|
||||
user_calibration = save_info.calibration;
|
||||
send_command_async_event->GetWritableEvent().Signal();
|
||||
send_command_async_event->Signal();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Command not implemented {}", command);
|
||||
command = RingConCommands::Error;
|
||||
// Signal a reply to avoid softlocking the game
|
||||
send_command_async_event->GetWritableEvent().Signal();
|
||||
send_command_async_event->Signal();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ private:
|
||||
u8 pointing_status;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u32 unknown;
|
||||
float unkown_float1;
|
||||
float unknown_float1;
|
||||
float position_x;
|
||||
float position_y;
|
||||
float unkown_float2;
|
||||
float unknown_float2;
|
||||
Core::IrSensor::IrsRect window_of_interest;
|
||||
};
|
||||
static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
@@ -46,7 +45,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
|
||||
}
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize(std::move(name), process);
|
||||
event->Initialize(process);
|
||||
|
||||
// Commit the thread reservation.
|
||||
event_reservation.Commit();
|
||||
@@ -59,7 +58,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
|
||||
|
||||
void ServiceContext::CloseEvent(Kernel::KEvent* event) {
|
||||
event->GetReadableEvent().Close();
|
||||
event->GetWritableEvent().Close();
|
||||
event->Close();
|
||||
}
|
||||
|
||||
} // namespace Service::KernelHelpers
|
||||
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
}
|
||||
|
||||
void OnEventFired() {
|
||||
state_change_event->GetWritableEvent().Signal();
|
||||
state_change_event->Signal();
|
||||
}
|
||||
|
||||
void GetState(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -58,7 +58,7 @@ NfpDevice::~NfpDevice() {
|
||||
void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Connected ||
|
||||
type == Core::HID::ControllerTriggerType::Disconnected) {
|
||||
availability_change_event->GetWritableEvent().Signal();
|
||||
availability_change_event->Signal();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
|
||||
|
||||
device_state = DeviceState::TagFound;
|
||||
deactivate_event->GetReadableEvent().Clear();
|
||||
activate_event->GetWritableEvent().Signal();
|
||||
activate_event->Signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ void NfpDevice::CloseAmiibo() {
|
||||
encrypted_tag_data = {};
|
||||
tag_data = {};
|
||||
activate_event->GetReadableEvent().Clear();
|
||||
deactivate_event->GetWritableEvent().Signal();
|
||||
deactivate_event->Signal();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const {
|
||||
|
||||
@@ -167,7 +167,7 @@ struct AmiiboDate {
|
||||
|
||||
bool IsValidDate() const {
|
||||
const bool is_day_valid = GetDay() > 0 && GetDay() < 32;
|
||||
const bool is_month_valid = GetMonth() >= 0 && GetMonth() < 13;
|
||||
const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13;
|
||||
const bool is_year_valid = GetYear() >= 2000;
|
||||
return is_year_valid && is_month_valid && is_day_valid;
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ private:
|
||||
void StartTask(Kernel::HLERequestContext& ctx) {
|
||||
// No need to connect to the internet, just finish the task straight away.
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
finished_event->GetWritableEvent().Signal();
|
||||
finished_event->Signal();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
@@ -350,7 +350,7 @@ private:
|
||||
|
||||
void Cancel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
finished_event->GetWritableEvent().Clear();
|
||||
finished_event->Clear();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/glue/glue_manager.h"
|
||||
#include "core/hle/service/ns/errors.h"
|
||||
#include "core/hle/service/ns/iplatform_service_manager.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
@@ -581,7 +582,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetApplicationControlData"},
|
||||
{0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||
{1, nullptr, "GetApplicationDesiredLanguage"},
|
||||
{2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
@@ -594,6 +595,33 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||
|
||||
IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
|
||||
|
||||
void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
enum class ApplicationControlSource : u8 {
|
||||
CacheOnly,
|
||||
Storage,
|
||||
StorageOnly,
|
||||
};
|
||||
|
||||
struct RequestParameters {
|
||||
ApplicationControlSource source;
|
||||
u64 application_id;
|
||||
};
|
||||
static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters{rp.PopRaw<RequestParameters>()};
|
||||
const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)};
|
||||
const auto result = nacp_data ? ResultSuccess : ResultUnknown;
|
||||
|
||||
if (nacp_data) {
|
||||
ctx.WriteBuffer(nacp_data->data(), nacp_data->size());
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
|
||||
@@ -78,6 +78,9 @@ class IReadOnlyApplicationControlDataInterface final
|
||||
public:
|
||||
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
|
||||
~IReadOnlyApplicationControlDataInterface() override;
|
||||
|
||||
private:
|
||||
void GetApplicationControlData(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class NS final : public ServiceFramework<NS> {
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
|
||||
@@ -206,7 +205,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
|
||||
auto& event_ = events[slot];
|
||||
if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) ==
|
||||
EventState::Waiting) {
|
||||
event_.kevent->GetWritableEvent().Signal();
|
||||
event_.kevent->Signal();
|
||||
}
|
||||
event_.status.store(EventState::Signalled, std::memory_order_release);
|
||||
});
|
||||
@@ -306,7 +305,7 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::v
|
||||
}
|
||||
event.fails++;
|
||||
event.status.store(EventState::Cancelled, std::memory_order_release);
|
||||
event.kevent->GetWritableEvent().Clear();
|
||||
event.kevent->Clear();
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KWritableEvent;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
class NVDRV final : public ServiceFramework<NVDRV> {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
@@ -110,7 +109,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
|
||||
|
||||
core->override_max_buffer_count = buffer_count;
|
||||
core->SignalDequeueCondition();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event->Signal();
|
||||
listener = core->consumer_listener;
|
||||
}
|
||||
|
||||
@@ -623,7 +622,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
|
||||
slots[slot].fence = fence;
|
||||
|
||||
core->SignalDequeueCondition();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
|
||||
@@ -753,7 +752,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
|
||||
core->connected_producer_listener = nullptr;
|
||||
core->connected_api = NativeWindowApi::NoConnectedApi;
|
||||
core->SignalDequeueCondition();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event->Signal();
|
||||
listener = core->consumer_listener;
|
||||
} else {
|
||||
LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
|
||||
@@ -802,7 +801,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
|
||||
}
|
||||
|
||||
core->SignalDequeueCondition();
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event->Signal();
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Kernel {
|
||||
class KernelCore;
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
class KWritableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
|
||||
@@ -25,7 +25,6 @@ struct EventType;
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
class KWritableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
@@ -37,19 +37,19 @@ public:
|
||||
|
||||
void SignalChargerTypeChanged() {
|
||||
if (should_signal && should_signal_charger_type) {
|
||||
state_change_event->GetWritableEvent().Signal();
|
||||
state_change_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void SignalPowerSupplyChanged() {
|
||||
if (should_signal && should_signal_power_supply) {
|
||||
state_change_event->GetWritableEvent().Signal();
|
||||
state_change_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void SignalBatteryVoltageStateChanged() {
|
||||
if (should_signal && should_signal_battery_voltage) {
|
||||
state_change_event->GetWritableEvent().Signal();
|
||||
state_change_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
|
||||
{0, nullptr, "GetTemperatureRange"},
|
||||
{1, &TS::GetTemperature, "GetTemperature"},
|
||||
{2, nullptr, "SetMeasurementMode"},
|
||||
{3, nullptr, "GetTemperatureMilliC"},
|
||||
{3, &TS::GetTemperatureMilliC, "GetTemperatureMilliC"},
|
||||
{4, nullptr, "OpenSession"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -29,8 +29,6 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto location{rp.PopEnum<Location>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
|
||||
|
||||
const s32 temperature = location == Location::Internal ? 35 : 20;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -38,4 +36,15 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(temperature);
|
||||
}
|
||||
|
||||
void TS::GetTemperatureMilliC(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto location{rp.PopEnum<Location>()};
|
||||
|
||||
const s32 temperature = location == Location::Internal ? 35000 : 20000;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(temperature);
|
||||
}
|
||||
|
||||
} // namespace Service::PTM
|
||||
|
||||
@@ -20,6 +20,7 @@ private:
|
||||
};
|
||||
|
||||
void GetTemperature(Kernel::HLERequestContext& ctx);
|
||||
void GetTemperatureMilliC(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::PTM
|
||||
|
||||
@@ -101,6 +101,81 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
// FIXME: implement support for the real system_settings.ini
|
||||
|
||||
template <typename T>
|
||||
static std::vector<u8> ToBytes(const T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
|
||||
const auto* begin = reinterpret_cast<const u8*>(&value);
|
||||
const auto* end = begin + sizeof(T);
|
||||
|
||||
return std::vector<u8>(begin, end);
|
||||
}
|
||||
|
||||
using Settings =
|
||||
std::map<std::string, std::map<std::string, std::vector<u8>, std::less<>>, std::less<>>;
|
||||
|
||||
static Settings GetSettings() {
|
||||
Settings ret;
|
||||
|
||||
ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0});
|
||||
ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SET_SYS::GetSettingsItemValueSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
// The category of the setting. This corresponds to the top-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_category_buf{ctx.ReadBuffer(0)};
|
||||
const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
|
||||
|
||||
// The name of the setting. This corresponds to the second-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_name_buf{ctx.ReadBuffer(1)};
|
||||
const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
|
||||
|
||||
auto settings{GetSettings()};
|
||||
u64 response_size{0};
|
||||
|
||||
if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
|
||||
response_size = settings[setting_category][setting_name].size();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess);
|
||||
rb.Push(response_size);
|
||||
}
|
||||
|
||||
void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
// The category of the setting. This corresponds to the top-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_category_buf{ctx.ReadBuffer(0)};
|
||||
const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
|
||||
|
||||
// The name of the setting. This corresponds to the second-level keys of
|
||||
// system_settings.ini.
|
||||
const auto setting_name_buf{ctx.ReadBuffer(1)};
|
||||
const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
|
||||
|
||||
auto settings{GetSettings()};
|
||||
Result response{ResultUnknown};
|
||||
|
||||
if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
|
||||
auto setting_value = settings[setting_category][setting_name];
|
||||
ctx.WriteBuffer(setting_value.data(), setting_value.size());
|
||||
response = ResultSuccess;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(response);
|
||||
}
|
||||
|
||||
SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -138,8 +213,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
|
||||
{32, nullptr, "SetAccountNotificationSettings"},
|
||||
{35, nullptr, "GetVibrationMasterVolume"},
|
||||
{36, nullptr, "SetVibrationMasterVolume"},
|
||||
{37, nullptr, "GetSettingsItemValueSize"},
|
||||
{38, nullptr, "GetSettingsItemValue"},
|
||||
{37, &SET_SYS::GetSettingsItemValueSize, "GetSettingsItemValueSize"},
|
||||
{38, &SET_SYS::GetSettingsItemValue, "GetSettingsItemValue"},
|
||||
{39, nullptr, "GetTvSettings"},
|
||||
{40, nullptr, "SetTvSettings"},
|
||||
{41, nullptr, "GetEdid"},
|
||||
|
||||
@@ -23,6 +23,8 @@ private:
|
||||
BasicBlack = 1,
|
||||
};
|
||||
|
||||
void GetSettingsItemValueSize(Kernel::HLERequestContext& ctx);
|
||||
void GetSettingsItemValue(Kernel::HLERequestContext& ctx);
|
||||
void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
|
||||
void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
|
||||
void GetColorSetId(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -156,7 +156,8 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
|
||||
|
||||
// Create a new session.
|
||||
Kernel::KClientSession* session{};
|
||||
if (const auto result = port->GetClientPort().CreateSession(std::addressof(session));
|
||||
if (const auto result = port->GetClientPort().CreateSession(
|
||||
std::addressof(session), std::make_shared<Kernel::SessionRequestManager>(kernel));
|
||||
result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
|
||||
return result;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/time/errors.h"
|
||||
#include "core/hle/service/time/system_clock_context_update_callback.h"
|
||||
|
||||
@@ -20,13 +20,13 @@ bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& valu
|
||||
}
|
||||
|
||||
void SystemClockContextUpdateCallback::RegisterOperationEvent(
|
||||
std::shared_ptr<Kernel::KWritableEvent>&& writable_event) {
|
||||
operation_event_list.emplace_back(std::move(writable_event));
|
||||
std::shared_ptr<Kernel::KEvent>&& event) {
|
||||
operation_event_list.emplace_back(std::move(event));
|
||||
}
|
||||
|
||||
void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
|
||||
for (const auto& writable_event : operation_event_list) {
|
||||
writable_event->Signal();
|
||||
for (const auto& event : operation_event_list) {
|
||||
event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KWritableEvent;
|
||||
class KEvent;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
|
||||
bool NeedUpdate(const SystemClockContext& value) const;
|
||||
|
||||
void RegisterOperationEvent(std::shared_ptr<Kernel::KWritableEvent>&& writable_event);
|
||||
void RegisterOperationEvent(std::shared_ptr<Kernel::KEvent>&& event);
|
||||
|
||||
void BroadcastOperationEvent();
|
||||
|
||||
@@ -37,7 +37,7 @@ protected:
|
||||
|
||||
private:
|
||||
bool has_context{};
|
||||
std::vector<std::shared_ptr<Kernel::KWritableEvent>> operation_event_list;
|
||||
std::vector<std::shared_ptr<Kernel::KEvent>> operation_event_list;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item_consumer.h"
|
||||
@@ -74,7 +73,7 @@ Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() {
|
||||
}
|
||||
|
||||
void Display::SignalVSyncEvent() {
|
||||
vsync_event->GetWritableEvent().Signal();
|
||||
vsync_event->Signal();
|
||||
}
|
||||
|
||||
void Display::CreateLayer(u64 layer_id, u32 binder_id,
|
||||
|
||||
@@ -691,9 +691,12 @@ public:
|
||||
}
|
||||
|
||||
void OnChange() {
|
||||
const auto camera_status = GetStatus();
|
||||
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::IrSensor,
|
||||
.camera_status = GetStatus(),
|
||||
.camera_status = camera_status.format,
|
||||
.raw_data = camera_status.data,
|
||||
};
|
||||
|
||||
TriggerOnChange(status);
|
||||
@@ -732,9 +735,12 @@ public:
|
||||
}
|
||||
|
||||
void OnChange() {
|
||||
const auto nfc_status = GetStatus();
|
||||
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Nfc,
|
||||
.nfc_status = GetStatus(),
|
||||
.nfc_status = nfc_status.state,
|
||||
.raw_data = nfc_status.data,
|
||||
};
|
||||
|
||||
TriggerOnChange(status);
|
||||
|
||||
@@ -994,13 +994,13 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
const u32 size = index_buffer.size;
|
||||
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
|
||||
if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
|
||||
const u32 new_offset = offset + maxwell3d->regs.index_array.first *
|
||||
maxwell3d->regs.index_array.FormatSizeInBytes();
|
||||
const u32 new_offset = offset + maxwell3d->regs.index_buffer.first *
|
||||
maxwell3d->regs.index_buffer.FormatSizeInBytes();
|
||||
runtime.BindIndexBuffer(buffer, new_offset, size);
|
||||
} else {
|
||||
runtime.BindIndexBuffer(maxwell3d->regs.draw.topology, maxwell3d->regs.index_array.format,
|
||||
maxwell3d->regs.index_array.first,
|
||||
maxwell3d->regs.index_array.count, buffer, offset, size);
|
||||
runtime.BindIndexBuffer(maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
|
||||
maxwell3d->regs.index_buffer.first,
|
||||
maxwell3d->regs.index_buffer.count, buffer, offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,7 +1017,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
|
||||
}
|
||||
flags[Dirty::VertexBuffer0 + index] = false;
|
||||
|
||||
const u32 stride = maxwell3d->regs.vertex_array[index].stride;
|
||||
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
|
||||
const u32 offset = buffer.Offset(binding.cpu_addr);
|
||||
runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride);
|
||||
}
|
||||
@@ -1157,7 +1157,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostTransformFeedbackBuffers() {
|
||||
if (maxwell3d->regs.tfb_enabled == 0) {
|
||||
if (maxwell3d->regs.transform_feedback_enabled == 0) {
|
||||
return;
|
||||
}
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
@@ -1268,7 +1268,7 @@ template <class P>
|
||||
void BufferCache<P>::UpdateIndexBuffer() {
|
||||
// We have to check for the dirty flags and index count
|
||||
// The index count is currently changed without updating the dirty flags
|
||||
const auto& index_array = maxwell3d->regs.index_array;
|
||||
const auto& index_array = maxwell3d->regs.index_buffer;
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) {
|
||||
return;
|
||||
@@ -1311,10 +1311,10 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
|
||||
if (!maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) {
|
||||
return;
|
||||
}
|
||||
const auto& array = maxwell3d->regs.vertex_array[index];
|
||||
const auto& limit = maxwell3d->regs.vertex_array_limit[index];
|
||||
const GPUVAddr gpu_addr_begin = array.StartAddress();
|
||||
const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1;
|
||||
const auto& array = maxwell3d->regs.vertex_streams[index];
|
||||
const auto& limit = maxwell3d->regs.vertex_stream_limits[index];
|
||||
const GPUVAddr gpu_addr_begin = array.Address();
|
||||
const GPUVAddr gpu_addr_end = limit.Address() + 1;
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||
u32 address_size = static_cast<u32>(
|
||||
std::min(gpu_addr_end - gpu_addr_begin, static_cast<u64>(std::numeric_limits<u32>::max())));
|
||||
@@ -1380,7 +1380,7 @@ void BufferCache<P>::UpdateTextureBuffers(size_t stage) {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateTransformFeedbackBuffers() {
|
||||
if (maxwell3d->regs.tfb_enabled == 0) {
|
||||
if (maxwell3d->regs.transform_feedback_enabled == 0) {
|
||||
return;
|
||||
}
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
@@ -1390,11 +1390,11 @@ void BufferCache<P>::UpdateTransformFeedbackBuffers() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) {
|
||||
const auto& binding = maxwell3d->regs.tfb_bindings[index];
|
||||
const GPUVAddr gpu_addr = binding.Address() + binding.buffer_offset;
|
||||
const u32 size = binding.buffer_size;
|
||||
const auto& binding = maxwell3d->regs.transform_feedback.buffers[index];
|
||||
const GPUVAddr gpu_addr = binding.Address() + binding.start_offset;
|
||||
const u32 size = binding.size;
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
|
||||
if (binding.buffer_enable == 0 || size == 0 || !cpu_addr) {
|
||||
if (binding.enable == 0 || size == 0 || !cpu_addr) {
|
||||
transform_feedback_buffers[index] = NULL_BINDING;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,21 +17,23 @@ using Tegra::Engines::Maxwell3D;
|
||||
void SetupDirtyVertexBuffers(Maxwell3D::DirtyState::Tables& tables) {
|
||||
static constexpr std::size_t num_array = 3;
|
||||
for (std::size_t i = 0; i < Maxwell3D::Regs::NumVertexArrays; ++i) {
|
||||
const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]);
|
||||
const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]);
|
||||
const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]);
|
||||
const std::size_t limit_offset =
|
||||
OFF(vertex_stream_limits) + i * NUM(vertex_stream_limits[0]);
|
||||
|
||||
FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers);
|
||||
FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers);
|
||||
FillBlock(tables, limit_offset, NUM(vertex_stream_limits), VertexBuffer0 + i,
|
||||
VertexBuffers);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupIndexBuffer(Maxwell3D::DirtyState::Tables& tables) {
|
||||
FillBlock(tables[0], OFF(index_array), NUM(index_array), IndexBuffer);
|
||||
FillBlock(tables[0], OFF(index_buffer), NUM(index_buffer), IndexBuffer);
|
||||
}
|
||||
|
||||
void SetupDirtyDescriptors(Maxwell3D::DirtyState::Tables& tables) {
|
||||
FillBlock(tables[0], OFF(tic), NUM(tic), Descriptors);
|
||||
FillBlock(tables[0], OFF(tsc), NUM(tsc), Descriptors);
|
||||
FillBlock(tables[0], OFF(tex_header), NUM(tex_header), Descriptors);
|
||||
FillBlock(tables[0], OFF(tex_sampler), NUM(tex_sampler), Descriptors);
|
||||
}
|
||||
|
||||
void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
|
||||
@@ -42,7 +44,7 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
|
||||
FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
|
||||
}
|
||||
FillBlock(tables[1], begin, num, RenderTargets);
|
||||
FillBlock(tables[0], OFF(render_area), NUM(render_area), RenderTargets);
|
||||
FillBlock(tables[0], OFF(surface_clip), NUM(surface_clip), RenderTargets);
|
||||
|
||||
tables[0][OFF(rt_control)] = RenderTargets;
|
||||
tables[1][OFF(rt_control)] = RenderTargetControl;
|
||||
@@ -52,15 +54,15 @@ void SetupDirtyRenderTargets(Maxwell3D::DirtyState::Tables& tables) {
|
||||
const u8 flag = zeta_flags[i];
|
||||
auto& table = tables[i];
|
||||
table[OFF(zeta_enable)] = flag;
|
||||
table[OFF(zeta_width)] = flag;
|
||||
table[OFF(zeta_height)] = flag;
|
||||
table[OFF(zeta_size.width)] = flag;
|
||||
table[OFF(zeta_size.height)] = flag;
|
||||
FillBlock(table, OFF(zeta), NUM(zeta), flag);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupDirtyShaders(Maxwell3D::DirtyState::Tables& tables) {
|
||||
FillBlock(tables[0], OFF(shader_config[0]),
|
||||
NUM(shader_config[0]) * Maxwell3D::Regs::MaxShaderProgram, Shaders);
|
||||
FillBlock(tables[0], OFF(pipelines), NUM(pipelines[0]) * Maxwell3D::Regs::MaxShaderProgram,
|
||||
Shaders);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
|
||||
@@ -56,37 +56,37 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
|
||||
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
|
||||
// so initialize blend registers with sane values
|
||||
regs.blend.equation_rgb = Regs::Blend::Equation::Add;
|
||||
regs.blend.factor_source_rgb = Regs::Blend::Factor::One;
|
||||
regs.blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
|
||||
regs.blend.equation_a = Regs::Blend::Equation::Add;
|
||||
regs.blend.factor_source_a = Regs::Blend::Factor::One;
|
||||
regs.blend.factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
for (auto& blend : regs.independent_blend) {
|
||||
blend.equation_rgb = Regs::Blend::Equation::Add;
|
||||
blend.factor_source_rgb = Regs::Blend::Factor::One;
|
||||
blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
|
||||
blend.equation_a = Regs::Blend::Equation::Add;
|
||||
blend.factor_source_a = Regs::Blend::Factor::One;
|
||||
blend.factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
regs.blend.color_op = Regs::Blend::Equation::Add_D3D;
|
||||
regs.blend.color_source = Regs::Blend::Factor::One_D3D;
|
||||
regs.blend.color_dest = Regs::Blend::Factor::Zero_D3D;
|
||||
regs.blend.alpha_op = Regs::Blend::Equation::Add_D3D;
|
||||
regs.blend.alpha_source = Regs::Blend::Factor::One_D3D;
|
||||
regs.blend.alpha_dest = Regs::Blend::Factor::Zero_D3D;
|
||||
for (auto& blend : regs.blend_per_target) {
|
||||
blend.color_op = Regs::Blend::Equation::Add_D3D;
|
||||
blend.color_source = Regs::Blend::Factor::One_D3D;
|
||||
blend.color_dest = Regs::Blend::Factor::Zero_D3D;
|
||||
blend.alpha_op = Regs::Blend::Equation::Add_D3D;
|
||||
blend.alpha_source = Regs::Blend::Factor::One_D3D;
|
||||
blend.alpha_dest = Regs::Blend::Factor::Zero_D3D;
|
||||
}
|
||||
regs.stencil_front_op_fail = Regs::StencilOp::Keep;
|
||||
regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
|
||||
regs.stencil_front_op_zpass = Regs::StencilOp::Keep;
|
||||
regs.stencil_front_func_func = Regs::ComparisonOp::Always;
|
||||
regs.stencil_front_op.fail = Regs::StencilOp::Op::Keep_D3D;
|
||||
regs.stencil_front_op.zfail = Regs::StencilOp::Op::Keep_D3D;
|
||||
regs.stencil_front_op.zpass = Regs::StencilOp::Op::Keep_D3D;
|
||||
regs.stencil_front_op.func = Regs::ComparisonOp::Always_GL;
|
||||
regs.stencil_front_func_mask = 0xFFFFFFFF;
|
||||
regs.stencil_front_mask = 0xFFFFFFFF;
|
||||
regs.stencil_two_side_enable = 1;
|
||||
regs.stencil_back_op_fail = Regs::StencilOp::Keep;
|
||||
regs.stencil_back_op_zfail = Regs::StencilOp::Keep;
|
||||
regs.stencil_back_op_zpass = Regs::StencilOp::Keep;
|
||||
regs.stencil_back_func_func = Regs::ComparisonOp::Always;
|
||||
regs.stencil_back_op.fail = Regs::StencilOp::Op::Keep_D3D;
|
||||
regs.stencil_back_op.zfail = Regs::StencilOp::Op::Keep_D3D;
|
||||
regs.stencil_back_op.zpass = Regs::StencilOp::Op::Keep_D3D;
|
||||
regs.stencil_back_op.func = Regs::ComparisonOp::Always_GL;
|
||||
regs.stencil_back_func_mask = 0xFFFFFFFF;
|
||||
regs.stencil_back_mask = 0xFFFFFFFF;
|
||||
|
||||
regs.depth_test_func = Regs::ComparisonOp::Always;
|
||||
regs.front_face = Regs::FrontFace::CounterClockWise;
|
||||
regs.cull_face = Regs::CullFace::Back;
|
||||
regs.depth_test_func = Regs::ComparisonOp::Always_GL;
|
||||
regs.gl_front_face = Regs::FrontFace::CounterClockWise;
|
||||
regs.gl_cull_face = Regs::CullFace::Back;
|
||||
|
||||
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
|
||||
// register carrying a default value. Assume it's OpenGL's default (1).
|
||||
@@ -107,20 +107,20 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
|
||||
// NVN games expect these values to be enabled at boot
|
||||
regs.rasterize_enable = 1;
|
||||
regs.rt_separate_frag_data = 1;
|
||||
regs.color_target_mrt_enable = 1;
|
||||
regs.framebuffer_srgb = 1;
|
||||
regs.line_width_aliased = 1.0f;
|
||||
regs.line_width_smooth = 1.0f;
|
||||
regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
|
||||
regs.gl_front_face = Maxwell3D::Regs::FrontFace::ClockWise;
|
||||
regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill;
|
||||
regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill;
|
||||
|
||||
shadow_state = regs;
|
||||
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.end)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.begin)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
|
||||
@@ -173,51 +173,56 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
||||
case MAXWELL3D_REG_INDEX(shadow_ram_control):
|
||||
shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
|
||||
return;
|
||||
case MAXWELL3D_REG_INDEX(macros.upload_address):
|
||||
return macro_engine->ClearCode(regs.macros.upload_address);
|
||||
case MAXWELL3D_REG_INDEX(macros.data):
|
||||
return macro_engine->AddCode(regs.macros.upload_address, argument);
|
||||
case MAXWELL3D_REG_INDEX(macros.bind):
|
||||
case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
|
||||
return macro_engine->ClearCode(regs.load_mme.instruction_ptr);
|
||||
case MAXWELL3D_REG_INDEX(load_mme.instruction):
|
||||
return macro_engine->AddCode(regs.load_mme.instruction_ptr, argument);
|
||||
case MAXWELL3D_REG_INDEX(load_mme.start_address):
|
||||
return ProcessMacroBind(argument);
|
||||
case MAXWELL3D_REG_INDEX(firmware[4]):
|
||||
case MAXWELL3D_REG_INDEX(falcon[4]):
|
||||
return ProcessFirmwareCall4();
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
|
||||
return ProcessCBData(argument);
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[0]):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config):
|
||||
return ProcessCBBind(0);
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[1]):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config):
|
||||
return ProcessCBBind(1);
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[2]):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config):
|
||||
return ProcessCBBind(2);
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[3]):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config):
|
||||
return ProcessCBBind(3);
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[4]):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
|
||||
return ProcessCBBind(4);
|
||||
case MAXWELL3D_REG_INDEX(draw.vertex_end_gl):
|
||||
case MAXWELL3D_REG_INDEX(draw.end):
|
||||
return DrawArrays();
|
||||
case MAXWELL3D_REG_INDEX(small_index):
|
||||
regs.index_array.count = regs.small_index.count;
|
||||
regs.index_array.first = regs.small_index.first;
|
||||
case MAXWELL3D_REG_INDEX(index_buffer32_first):
|
||||
regs.index_buffer.count = regs.index_buffer32_first.count;
|
||||
regs.index_buffer.first = regs.index_buffer32_first.first;
|
||||
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
return DrawArrays();
|
||||
case MAXWELL3D_REG_INDEX(small_index_2):
|
||||
regs.index_array.count = regs.small_index_2.count;
|
||||
regs.index_array.first = regs.small_index_2.first;
|
||||
case MAXWELL3D_REG_INDEX(index_buffer16_first):
|
||||
regs.index_buffer.count = regs.index_buffer16_first.count;
|
||||
regs.index_buffer.first = regs.index_buffer16_first.first;
|
||||
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
return DrawArrays();
|
||||
case MAXWELL3D_REG_INDEX(index_buffer8_first):
|
||||
regs.index_buffer.count = regs.index_buffer8_first.count;
|
||||
regs.index_buffer.first = regs.index_buffer8_first.first;
|
||||
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
// a macro calls this one over and over, should it increase instancing?
|
||||
// Used by Hades and likely other Vulkan games.
|
||||
@@ -225,28 +230,24 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
||||
case MAXWELL3D_REG_INDEX(topology_override):
|
||||
use_topology_override = true;
|
||||
return;
|
||||
case MAXWELL3D_REG_INDEX(clear_buffers):
|
||||
case MAXWELL3D_REG_INDEX(clear_surface):
|
||||
return ProcessClearBuffers();
|
||||
case MAXWELL3D_REG_INDEX(query.query_get):
|
||||
case MAXWELL3D_REG_INDEX(report_semaphore.query):
|
||||
return ProcessQueryGet();
|
||||
case MAXWELL3D_REG_INDEX(condition.mode):
|
||||
case MAXWELL3D_REG_INDEX(render_enable.mode):
|
||||
return ProcessQueryCondition();
|
||||
case MAXWELL3D_REG_INDEX(counter_reset):
|
||||
case MAXWELL3D_REG_INDEX(clear_report_value):
|
||||
return ProcessCounterReset();
|
||||
case MAXWELL3D_REG_INDEX(sync_info):
|
||||
return ProcessSyncPoint();
|
||||
case MAXWELL3D_REG_INDEX(exec_upload):
|
||||
return upload_state.ProcessExec(regs.exec_upload.linear != 0);
|
||||
case MAXWELL3D_REG_INDEX(data_upload):
|
||||
case MAXWELL3D_REG_INDEX(launch_dma):
|
||||
return upload_state.ProcessExec(regs.launch_dma.memory_layout.Value() ==
|
||||
Regs::LaunchDMA::Layout::Pitch);
|
||||
case MAXWELL3D_REG_INDEX(inline_data):
|
||||
upload_state.ProcessData(argument, is_last_call);
|
||||
return;
|
||||
case MAXWELL3D_REG_INDEX(fragment_barrier):
|
||||
return rasterizer->FragmentBarrier();
|
||||
case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache):
|
||||
rasterizer->InvalidateGPUCache();
|
||||
return rasterizer->WaitForIdle();
|
||||
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
|
||||
return rasterizer->TiledCacheBarrier();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,25 +297,25 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 1:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 2:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 3:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 4:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 5:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 6:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 7:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 8:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 9:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 10:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 11:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 12:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
|
||||
ProcessCBMultiData(base_start, amount);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(data_upload):
|
||||
case MAXWELL3D_REG_INDEX(inline_data):
|
||||
upload_state.ProcessData(base_start, static_cast<size_t>(amount));
|
||||
return;
|
||||
default:
|
||||
@@ -353,14 +354,15 @@ void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) {
|
||||
if (mme_inline[method]) {
|
||||
regs.reg_array[method] = method_argument;
|
||||
if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
|
||||
method == MAXWELL3D_REG_INDEX(index_array.count)) {
|
||||
method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
|
||||
const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
|
||||
? MMEDrawMode::Array
|
||||
: MMEDrawMode::Indexed;
|
||||
StepInstance(expected_mode, method_argument);
|
||||
} else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) {
|
||||
} else if (method == MAXWELL3D_REG_INDEX(draw.begin)) {
|
||||
mme_draw.instance_mode =
|
||||
(regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0);
|
||||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
|
||||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged);
|
||||
mme_draw.gl_begin_consume = true;
|
||||
} else {
|
||||
mme_draw.gl_end_count++;
|
||||
@@ -405,11 +407,12 @@ void Maxwell3D::ProcessTopologyOverride() {
|
||||
void Maxwell3D::FlushMMEInlineDraw() {
|
||||
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
|
||||
regs.vertex_buffer.count);
|
||||
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
|
||||
ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
|
||||
ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
|
||||
|
||||
// Both instance configuration registers can not be set at the same time.
|
||||
ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
|
||||
ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
|
||||
regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
|
||||
"Illegal combination of instancing parameters");
|
||||
|
||||
ProcessTopologyOverride();
|
||||
@@ -424,7 +427,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
|
||||
// it's possible that it is incorrect and that there is some other register used to specify the
|
||||
// drawing mode.
|
||||
if (is_indexed) {
|
||||
regs.index_array.count = 0;
|
||||
regs.index_buffer.count = 0;
|
||||
} else {
|
||||
regs.vertex_buffer.count = 0;
|
||||
}
|
||||
@@ -437,11 +440,11 @@ void Maxwell3D::FlushMMEInlineDraw() {
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacroUpload(u32 data) {
|
||||
macro_engine->AddCode(regs.macros.upload_address++, data);
|
||||
macro_engine->AddCode(regs.load_mme.instruction_ptr++, data);
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacroBind(u32 data) {
|
||||
macro_positions[regs.macros.entry++] = data;
|
||||
macro_positions[regs.load_mme.start_address_ptr++] = data;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessFirmwareCall4() {
|
||||
@@ -449,11 +452,11 @@ void Maxwell3D::ProcessFirmwareCall4() {
|
||||
|
||||
// Firmware call 4 is a blob that changes some registers depending on its parameters.
|
||||
// These registers don't affect emulation and so are stubbed by setting 0xd00 to 1.
|
||||
regs.reg_array[0xd00] = 1;
|
||||
regs.shadow_scratch[0] = 1;
|
||||
}
|
||||
|
||||
void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
|
||||
const GPUVAddr sequence_address{regs.query.QueryAddress()};
|
||||
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
|
||||
if (long_query) {
|
||||
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
|
||||
memory_manager.Write<u64>(sequence_address, payload);
|
||||
@@ -464,15 +467,17 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
|
||||
|
||||
void Maxwell3D::ProcessQueryGet() {
|
||||
// TODO(Subv): Support the other query units.
|
||||
if (regs.query.query_get.unit != Regs::QueryUnit::Crop) {
|
||||
LOG_DEBUG(HW_GPU, "Units other than CROP are unimplemented");
|
||||
if (regs.report_semaphore.query.location != Regs::ReportSemaphore::Location::All) {
|
||||
LOG_DEBUG(HW_GPU, "Locations other than ALL are unimplemented");
|
||||
}
|
||||
|
||||
switch (regs.query.query_get.operation) {
|
||||
case Regs::QueryOperation::Release:
|
||||
if (regs.query.query_get.fence == 1 || regs.query.query_get.short_query != 0) {
|
||||
const GPUVAddr sequence_address{regs.query.QueryAddress()};
|
||||
const u32 payload = regs.query.query_sequence;
|
||||
switch (regs.report_semaphore.query.operation) {
|
||||
case Regs::ReportSemaphore::Operation::Release:
|
||||
if (regs.report_semaphore.query.release ==
|
||||
Regs::ReportSemaphore::Release::AfterAllPreceedingWrites ||
|
||||
regs.report_semaphore.query.short_query != 0) {
|
||||
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
|
||||
const u32 payload = regs.report_semaphore.payload;
|
||||
std::function<void()> operation([this, sequence_address, payload] {
|
||||
memory_manager.Write<u32>(sequence_address, payload);
|
||||
});
|
||||
@@ -482,8 +487,8 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
u64_le value;
|
||||
u64_le timestamp;
|
||||
};
|
||||
const GPUVAddr sequence_address{regs.query.QueryAddress()};
|
||||
const u32 payload = regs.query.query_sequence;
|
||||
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
|
||||
const u32 payload = regs.report_semaphore.payload;
|
||||
std::function<void()> operation([this, sequence_address, payload] {
|
||||
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
|
||||
memory_manager.Write<u64>(sequence_address, payload);
|
||||
@@ -491,19 +496,19 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
rasterizer->SyncOperation(std::move(operation));
|
||||
}
|
||||
break;
|
||||
case Regs::QueryOperation::Acquire:
|
||||
case Regs::ReportSemaphore::Operation::Acquire:
|
||||
// TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
|
||||
// matches the current payload.
|
||||
UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE");
|
||||
break;
|
||||
case Regs::QueryOperation::Counter:
|
||||
case Regs::ReportSemaphore::Operation::ReportOnly:
|
||||
if (const std::optional<u64> result = GetQueryResult()) {
|
||||
// If the query returns an empty optional it means it's cached and deferred.
|
||||
// In this case we have a non-empty result, so we stamp it immediately.
|
||||
StampQueryResult(*result, regs.query.query_get.short_query == 0);
|
||||
StampQueryResult(*result, regs.report_semaphore.query.short_query == 0);
|
||||
}
|
||||
break;
|
||||
case Regs::QueryOperation::Trap:
|
||||
case Regs::ReportSemaphore::Operation::Trap:
|
||||
UNIMPLEMENTED_MSG("Unimplemented query operation TRAP");
|
||||
break;
|
||||
default:
|
||||
@@ -513,31 +518,31 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessQueryCondition() {
|
||||
const GPUVAddr condition_address{regs.condition.Address()};
|
||||
switch (regs.condition.mode) {
|
||||
case Regs::ConditionMode::Always: {
|
||||
const GPUVAddr condition_address{regs.render_enable.Address()};
|
||||
switch (regs.render_enable.mode) {
|
||||
case Regs::RenderEnable::Mode::True: {
|
||||
execute_on = true;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::Never: {
|
||||
case Regs::RenderEnable::Mode::False: {
|
||||
execute_on = false;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::ResNonZero: {
|
||||
Regs::QueryCompare cmp;
|
||||
case Regs::RenderEnable::Mode::Conditional: {
|
||||
Regs::ReportSemaphore::Compare cmp;
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::Equal: {
|
||||
Regs::QueryCompare cmp;
|
||||
case Regs::RenderEnable::Mode::IfEqual: {
|
||||
Regs::ReportSemaphore::Compare cmp;
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on =
|
||||
cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::NotEqual: {
|
||||
Regs::QueryCompare cmp;
|
||||
case Regs::RenderEnable::Mode::IfNotEqual: {
|
||||
Regs::ReportSemaphore::Compare cmp;
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on =
|
||||
cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode;
|
||||
@@ -552,21 +557,21 @@ void Maxwell3D::ProcessQueryCondition() {
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCounterReset() {
|
||||
switch (regs.counter_reset) {
|
||||
case Regs::CounterReset::SampleCnt:
|
||||
switch (regs.clear_report_value) {
|
||||
case Regs::ClearReport::ZPassPixelCount:
|
||||
rasterizer->ResetCounter(QueryType::SamplesPassed);
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.counter_reset);
|
||||
LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessSyncPoint() {
|
||||
const u32 sync_point = regs.sync_info.sync_point.Value();
|
||||
const u32 increment = regs.sync_info.increment.Value();
|
||||
[[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value();
|
||||
if (increment) {
|
||||
const auto condition = regs.sync_info.condition.Value();
|
||||
[[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value();
|
||||
if (condition == Regs::SyncInfo::Condition::RopWritesDone) {
|
||||
rasterizer->SignalSyncPoint(sync_point);
|
||||
}
|
||||
}
|
||||
@@ -574,23 +579,24 @@ void Maxwell3D::ProcessSyncPoint() {
|
||||
void Maxwell3D::DrawArrays() {
|
||||
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
|
||||
regs.vertex_buffer.count);
|
||||
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
|
||||
ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
|
||||
|
||||
// Both instance configuration registers can not be set at the same time.
|
||||
ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
|
||||
ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
|
||||
regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
|
||||
"Illegal combination of instancing parameters");
|
||||
|
||||
ProcessTopologyOverride();
|
||||
|
||||
if (regs.draw.instance_next) {
|
||||
if (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) {
|
||||
// Increment the current instance *before* drawing.
|
||||
state.current_instance += 1;
|
||||
} else if (!regs.draw.instance_cont) {
|
||||
state.current_instance++;
|
||||
} else if (regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged) {
|
||||
// Reset the current instance to 0.
|
||||
state.current_instance = 0;
|
||||
}
|
||||
|
||||
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
|
||||
const bool is_indexed{regs.index_buffer.count && !regs.vertex_buffer.count};
|
||||
if (ShouldExecute()) {
|
||||
rasterizer->Draw(is_indexed, false);
|
||||
}
|
||||
@@ -600,60 +606,60 @@ void Maxwell3D::DrawArrays() {
|
||||
// it's possible that it is incorrect and that there is some other register used to specify the
|
||||
// drawing mode.
|
||||
if (is_indexed) {
|
||||
regs.index_array.count = 0;
|
||||
regs.index_buffer.count = 0;
|
||||
} else {
|
||||
regs.vertex_buffer.count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u64> Maxwell3D::GetQueryResult() {
|
||||
switch (regs.query.query_get.select) {
|
||||
case Regs::QuerySelect::Payload:
|
||||
return regs.query.query_sequence;
|
||||
case Regs::QuerySelect::SamplesPassed:
|
||||
switch (regs.report_semaphore.query.report) {
|
||||
case Regs::ReportSemaphore::Report::Payload:
|
||||
return regs.report_semaphore.payload;
|
||||
case Regs::ReportSemaphore::Report::ZPassPixelCount64:
|
||||
// Deferred.
|
||||
rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed,
|
||||
rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed,
|
||||
system.GPU().GetTicks());
|
||||
return std::nullopt;
|
||||
default:
|
||||
LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
|
||||
regs.query.query_get.select.Value());
|
||||
LOG_DEBUG(HW_GPU, "Unimplemented query report type {}",
|
||||
regs.report_semaphore.query.report.Value());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(size_t stage_index) {
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
|
||||
const auto& bind_data = regs.cb_bind[stage_index];
|
||||
auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.index];
|
||||
const auto& bind_data = regs.bind_groups[stage_index];
|
||||
auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot];
|
||||
buffer.enabled = bind_data.valid.Value() != 0;
|
||||
buffer.address = regs.const_buffer.BufferAddress();
|
||||
buffer.size = regs.const_buffer.cb_size;
|
||||
buffer.address = regs.const_buffer.Address();
|
||||
buffer.size = regs.const_buffer.size;
|
||||
|
||||
const bool is_enabled = bind_data.valid.Value() != 0;
|
||||
if (!is_enabled) {
|
||||
rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.index);
|
||||
rasterizer->DisableGraphicsUniformBuffer(stage_index, bind_data.shader_slot);
|
||||
return;
|
||||
}
|
||||
const GPUVAddr gpu_addr = regs.const_buffer.BufferAddress();
|
||||
const u32 size = regs.const_buffer.cb_size;
|
||||
rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size);
|
||||
const GPUVAddr gpu_addr = regs.const_buffer.Address();
|
||||
const u32 size = regs.const_buffer.size;
|
||||
rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.shader_slot, gpu_addr, size);
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
|
||||
// Write the input value to the current const buffer at the current position.
|
||||
const GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
|
||||
const GPUVAddr buffer_address = regs.const_buffer.Address();
|
||||
ASSERT(buffer_address != 0);
|
||||
|
||||
// Don't allow writing past the end of the buffer.
|
||||
ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size);
|
||||
ASSERT(regs.const_buffer.offset <= regs.const_buffer.size);
|
||||
|
||||
const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
|
||||
const GPUVAddr address{buffer_address + regs.const_buffer.offset};
|
||||
const size_t copy_size = amount * sizeof(u32);
|
||||
memory_manager.WriteBlock(address, start_base, copy_size);
|
||||
|
||||
// Increment the current buffer position.
|
||||
regs.const_buffer.cb_pos += static_cast<u32>(copy_size);
|
||||
regs.const_buffer.offset += static_cast<u32>(copy_size);
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBData(u32 value) {
|
||||
@@ -661,7 +667,8 @@ void Maxwell3D::ProcessCBData(u32 value) {
|
||||
}
|
||||
|
||||
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
const GPUVAddr tic_address_gpu{regs.tic.Address() + tic_index * sizeof(Texture::TICEntry)};
|
||||
const GPUVAddr tic_address_gpu{regs.tex_header.Address() +
|
||||
tic_index * sizeof(Texture::TICEntry)};
|
||||
|
||||
Texture::TICEntry tic_entry;
|
||||
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
@@ -670,7 +677,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
}
|
||||
|
||||
Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
|
||||
const GPUVAddr tsc_address_gpu{regs.tsc.Address() + tsc_index * sizeof(Texture::TSCEntry)};
|
||||
const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() +
|
||||
tsc_index * sizeof(Texture::TSCEntry)};
|
||||
|
||||
Texture::TSCEntry tsc_entry;
|
||||
memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,11 +24,15 @@ namespace Tegra {
|
||||
class DmaPusher;
|
||||
struct CommandList;
|
||||
|
||||
// TODO: Implement the commented ones
|
||||
enum class RenderTargetFormat : u32 {
|
||||
NONE = 0x0,
|
||||
R32B32G32A32_FLOAT = 0xC0,
|
||||
R32G32B32A32_SINT = 0xC1,
|
||||
R32G32B32A32_UINT = 0xC2,
|
||||
// R32G32B32X32_FLOAT = 0xC3,
|
||||
// R32G32B32X32_SINT = 0xC4,
|
||||
// R32G32B32X32_UINT = 0xC5,
|
||||
R16G16B16A16_UNORM = 0xC6,
|
||||
R16G16B16A16_SNORM = 0xC7,
|
||||
R16G16B16A16_SINT = 0xC8,
|
||||
@@ -38,8 +42,8 @@ enum class RenderTargetFormat : u32 {
|
||||
R32G32_SINT = 0xCC,
|
||||
R32G32_UINT = 0xCD,
|
||||
R16G16B16X16_FLOAT = 0xCE,
|
||||
B8G8R8A8_UNORM = 0xCF,
|
||||
B8G8R8A8_SRGB = 0xD0,
|
||||
A8R8G8B8_UNORM = 0xCF,
|
||||
A8R8G8B8_SRGB = 0xD0,
|
||||
A2B10G10R10_UNORM = 0xD1,
|
||||
A2B10G10R10_UINT = 0xD2,
|
||||
A8B8G8R8_UNORM = 0xD5,
|
||||
@@ -52,10 +56,13 @@ enum class RenderTargetFormat : u32 {
|
||||
R16G16_SINT = 0xDC,
|
||||
R16G16_UINT = 0xDD,
|
||||
R16G16_FLOAT = 0xDE,
|
||||
// A2R10G10B10_UNORM = 0xDF,
|
||||
B10G11R11_FLOAT = 0xE0,
|
||||
R32_SINT = 0xE3,
|
||||
R32_UINT = 0xE4,
|
||||
R32_FLOAT = 0xE5,
|
||||
// X8R8G8B8_UNORM = 0xE6,
|
||||
// X8R8G8B8_SRGB = 0xE7,
|
||||
R5G6B5_UNORM = 0xE8,
|
||||
A1R5G5B5_UNORM = 0xE9,
|
||||
R8G8_UNORM = 0xEA,
|
||||
@@ -71,17 +78,42 @@ enum class RenderTargetFormat : u32 {
|
||||
R8_SNORM = 0xF4,
|
||||
R8_SINT = 0xF5,
|
||||
R8_UINT = 0xF6,
|
||||
|
||||
/*
|
||||
A8_UNORM = 0xF7,
|
||||
X1R5G5B5_UNORM = 0xF8,
|
||||
X8B8G8R8_UNORM = 0xF9,
|
||||
X8B8G8R8_SRGB = 0xFA,
|
||||
Z1R5G5B5_UNORM = 0xFB,
|
||||
O1R5G5B5_UNORM = 0xFC,
|
||||
Z8R8G8B8_UNORM = 0xFD,
|
||||
O8R8G8B8_UNORM = 0xFE,
|
||||
R32_UNORM = 0xFF,
|
||||
A16_UNORM = 0x40,
|
||||
A16_FLOAT = 0x41,
|
||||
A32_FLOAT = 0x42,
|
||||
A8R8_UNORM = 0x43,
|
||||
R16A16_UNORM = 0x44,
|
||||
R16A16_FLOAT = 0x45,
|
||||
R32A32_FLOAT = 0x46,
|
||||
B8G8R8A8_UNORM = 0x47,
|
||||
*/
|
||||
};
|
||||
|
||||
enum class DepthFormat : u32 {
|
||||
D32_FLOAT = 0xA,
|
||||
D16_UNORM = 0x13,
|
||||
S8_UINT_Z24_UNORM = 0x14,
|
||||
D24X8_UNORM = 0x15,
|
||||
D24S8_UNORM = 0x16,
|
||||
Z32_FLOAT = 0xA,
|
||||
Z16_UNORM = 0x13,
|
||||
Z24_UNORM_S8_UINT = 0x14,
|
||||
X8Z24_UNORM = 0x15,
|
||||
S8Z24_UNORM = 0x16,
|
||||
S8_UINT = 0x17,
|
||||
D24C8_UNORM = 0x18,
|
||||
D32_FLOAT_S8X24_UINT = 0x19,
|
||||
V8Z24_UNORM = 0x18,
|
||||
Z32_FLOAT_X24S8_UINT = 0x19,
|
||||
/*
|
||||
X8Z24_UNORM_X16V8S8_UINT = 0x1D,
|
||||
Z32_FLOAT_X16V8X8_UINT = 0x1E,
|
||||
Z32_FLOAT_X16V8S8_UINT = 0x1F,
|
||||
*/
|
||||
};
|
||||
|
||||
namespace Engines {
|
||||
|
||||
@@ -36,7 +36,17 @@ SyncpointManager::ActionHandle SyncpointManager::RegisterAction(
|
||||
void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_storage,
|
||||
ActionHandle& handle) {
|
||||
std::unique_lock lk(guard);
|
||||
action_storage.erase(handle);
|
||||
|
||||
// We want to ensure the iterator still exists prior to erasing it
|
||||
// Otherwise, if an invalid iterator was passed in then it could lead to UB
|
||||
// It is important to avoid UB in that case since the deregister isn't called from a locked
|
||||
// context
|
||||
for (auto it = action_storage.begin(); it != action_storage.end(); it++) {
|
||||
if (it == handle) {
|
||||
action_storage.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle) {
|
||||
|
||||
@@ -21,16 +21,16 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
|
||||
|
||||
maxwell3d.regs.draw.topology.Assign(
|
||||
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
|
||||
maxwell3d.regs.vb_base_instance = parameters[5];
|
||||
maxwell3d.regs.global_base_instance_index = parameters[5];
|
||||
maxwell3d.mme_draw.instance_count = instance_count;
|
||||
maxwell3d.regs.vb_element_base = parameters[3];
|
||||
maxwell3d.regs.index_array.count = parameters[1];
|
||||
maxwell3d.regs.index_array.first = parameters[4];
|
||||
maxwell3d.regs.global_base_vertex_index = parameters[3];
|
||||
maxwell3d.regs.index_buffer.count = parameters[1];
|
||||
maxwell3d.regs.index_buffer.first = parameters[4];
|
||||
|
||||
if (maxwell3d.ShouldExecute()) {
|
||||
maxwell3d.Rasterizer().Draw(true, true);
|
||||
}
|
||||
maxwell3d.regs.index_array.count = 0;
|
||||
maxwell3d.regs.index_buffer.count = 0;
|
||||
maxwell3d.mme_draw.instance_count = 0;
|
||||
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
|
||||
}
|
||||
@@ -40,7 +40,7 @@ void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
|
||||
|
||||
maxwell3d.regs.vertex_buffer.first = parameters[3];
|
||||
maxwell3d.regs.vertex_buffer.count = parameters[1];
|
||||
maxwell3d.regs.vb_base_instance = parameters[4];
|
||||
maxwell3d.regs.global_base_instance_index = parameters[4];
|
||||
maxwell3d.regs.draw.topology.Assign(
|
||||
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
|
||||
maxwell3d.mme_draw.instance_count = count;
|
||||
@@ -57,12 +57,12 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
|
||||
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||
const u32 element_base = parameters[4];
|
||||
const u32 base_instance = parameters[5];
|
||||
maxwell3d.regs.index_array.first = parameters[3];
|
||||
maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base?
|
||||
maxwell3d.regs.index_array.count = parameters[1];
|
||||
maxwell3d.regs.index_buffer.first = parameters[3];
|
||||
maxwell3d.regs.vertex_id_base = element_base;
|
||||
maxwell3d.regs.index_buffer.count = parameters[1];
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
|
||||
maxwell3d.regs.vb_element_base = element_base;
|
||||
maxwell3d.regs.vb_base_instance = base_instance;
|
||||
maxwell3d.regs.global_base_vertex_index = element_base;
|
||||
maxwell3d.regs.global_base_instance_index = base_instance;
|
||||
maxwell3d.mme_draw.instance_count = instance_count;
|
||||
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
|
||||
maxwell3d.CallMethodFromMME(0x8e4, element_base);
|
||||
@@ -72,10 +72,10 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
|
||||
if (maxwell3d.ShouldExecute()) {
|
||||
maxwell3d.Rasterizer().Draw(true, true);
|
||||
}
|
||||
maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
|
||||
maxwell3d.regs.index_array.count = 0;
|
||||
maxwell3d.regs.vb_element_base = 0x0;
|
||||
maxwell3d.regs.vb_base_instance = 0x0;
|
||||
maxwell3d.regs.vertex_id_base = 0x0;
|
||||
maxwell3d.regs.index_buffer.count = 0;
|
||||
maxwell3d.regs.global_base_vertex_index = 0x0;
|
||||
maxwell3d.regs.global_base_instance_index = 0x0;
|
||||
maxwell3d.mme_draw.instance_count = 0;
|
||||
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
|
||||
maxwell3d.CallMethodFromMME(0x8e4, 0x0);
|
||||
@@ -87,10 +87,10 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
|
||||
void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
|
||||
SCOPE_EXIT({
|
||||
// Clean everything.
|
||||
maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
|
||||
maxwell3d.regs.index_array.count = 0;
|
||||
maxwell3d.regs.vb_element_base = 0x0;
|
||||
maxwell3d.regs.vb_base_instance = 0x0;
|
||||
maxwell3d.regs.vertex_id_base = 0x0;
|
||||
maxwell3d.regs.index_buffer.count = 0;
|
||||
maxwell3d.regs.global_base_vertex_index = 0x0;
|
||||
maxwell3d.regs.global_base_instance_index = 0x0;
|
||||
maxwell3d.mme_draw.instance_count = 0;
|
||||
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
|
||||
maxwell3d.CallMethodFromMME(0x8e4, 0x0);
|
||||
@@ -122,11 +122,11 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
|
||||
const u32 first_index = parameters[base + 2];
|
||||
const u32 base_vertex = parameters[base + 3];
|
||||
const u32 base_instance = parameters[base + 4];
|
||||
maxwell3d.regs.index_array.first = first_index;
|
||||
maxwell3d.regs.reg_array[0x446] = base_vertex;
|
||||
maxwell3d.regs.index_array.count = num_vertices;
|
||||
maxwell3d.regs.vb_element_base = base_vertex;
|
||||
maxwell3d.regs.vb_base_instance = base_instance;
|
||||
maxwell3d.regs.index_buffer.first = first_index;
|
||||
maxwell3d.regs.vertex_id_base = base_vertex;
|
||||
maxwell3d.regs.index_buffer.count = num_vertices;
|
||||
maxwell3d.regs.global_base_vertex_index = base_vertex;
|
||||
maxwell3d.regs.global_base_instance_index = base_instance;
|
||||
maxwell3d.mme_draw.instance_count = instance_count;
|
||||
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
|
||||
maxwell3d.CallMethodFromMME(0x8e4, base_vertex);
|
||||
|
||||
@@ -137,7 +137,7 @@ public:
|
||||
std::unique_lock lock{mutex};
|
||||
if (maxwell3d) {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable);
|
||||
Stream(VideoCore::QueryType::SamplesPassed).Update(regs.zpass_pixel_count_enable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ GLenum AssemblyStage(size_t stage_index) {
|
||||
/// @param location Hardware location
|
||||
/// @return Pair of ARB_transform_feedback3 token stream first and third arguments
|
||||
/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt
|
||||
std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) {
|
||||
const u8 index = location / 4;
|
||||
std::pair<GLint, GLint> TransformFeedbackEnum(u32 location) {
|
||||
const auto index = location / 4;
|
||||
if (index >= 8 && index <= 39) {
|
||||
return {GL_GENERIC_ATTRIB_NV, index - 8};
|
||||
}
|
||||
@@ -286,7 +286,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
buffer_cache.runtime.SetEnableStorageBuffers(use_storage_buffers);
|
||||
|
||||
const auto& regs{maxwell3d->regs};
|
||||
const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex};
|
||||
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
|
||||
const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
|
||||
const Shader::Info& info{stage_infos[stage]};
|
||||
buffer_cache.UnbindGraphicsStorageBuffers(stage);
|
||||
@@ -557,10 +557,25 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
|
||||
++current_stream;
|
||||
|
||||
const auto& locations = key.xfb_state.varyings[feedback];
|
||||
std::optional<u8> current_index;
|
||||
std::optional<u32> current_index;
|
||||
for (u32 offset = 0; offset < layout.varying_count; ++offset) {
|
||||
const u8 location = locations[offset];
|
||||
const u8 index = location / 4;
|
||||
const auto get_attribute = [&locations](u32 index) -> u32 {
|
||||
switch (index % 4) {
|
||||
case 0:
|
||||
return locations[index / 4].attribute0.Value();
|
||||
case 1:
|
||||
return locations[index / 4].attribute1.Value();
|
||||
case 2:
|
||||
return locations[index / 4].attribute2.Value();
|
||||
case 3:
|
||||
return locations[index / 4].attribute3.Value();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
};
|
||||
|
||||
const auto attribute{get_attribute(offset)};
|
||||
const auto index = attribute / 4U;
|
||||
|
||||
if (current_index == index) {
|
||||
// Increase number of components of the previous attachment
|
||||
@@ -569,7 +584,7 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
|
||||
}
|
||||
current_index = index;
|
||||
|
||||
std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location);
|
||||
std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(attribute);
|
||||
cursor[1] = 1;
|
||||
cursor += XFB_ENTRY_STRIDE;
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ struct GraphicsPipelineKey {
|
||||
BitField<0, 1, u32> xfb_enabled;
|
||||
BitField<1, 1, u32> early_z;
|
||||
BitField<2, 4, Maxwell::PrimitiveTopology> gs_input_topology;
|
||||
BitField<6, 2, Maxwell::TessellationPrimitive> tessellation_primitive;
|
||||
BitField<8, 2, Maxwell::TessellationSpacing> tessellation_spacing;
|
||||
BitField<6, 2, Maxwell::Tessellation::DomainType> tessellation_primitive;
|
||||
BitField<8, 2, Maxwell::Tessellation::Spacing> tessellation_spacing;
|
||||
BitField<10, 1, u32> tessellation_clockwise;
|
||||
};
|
||||
std::array<u32, 3> padding;
|
||||
|
||||
@@ -87,7 +87,7 @@ void RasterizerOpenGL::SyncVertexFormats() {
|
||||
}
|
||||
flags[Dirty::VertexFormat0 + index] = false;
|
||||
|
||||
const auto attrib = maxwell3d->regs.vertex_attrib_format[index];
|
||||
const auto& attrib = maxwell3d->regs.vertex_attrib_format[index];
|
||||
const auto gl_index = static_cast<GLuint>(index);
|
||||
|
||||
// Disable constant attributes.
|
||||
@@ -97,8 +97,8 @@ void RasterizerOpenGL::SyncVertexFormats() {
|
||||
}
|
||||
glEnableVertexAttribArray(gl_index);
|
||||
|
||||
if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
|
||||
attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
|
||||
if (attrib.type == Maxwell::VertexAttribute::Type::SInt ||
|
||||
attrib.type == Maxwell::VertexAttribute::Type::UInt) {
|
||||
glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexFormat(attrib), attrib.offset);
|
||||
} else {
|
||||
@@ -125,8 +125,8 @@ void RasterizerOpenGL::SyncVertexInstances() {
|
||||
flags[Dirty::VertexInstance0 + index] = false;
|
||||
|
||||
const auto gl_index = static_cast<GLuint>(index);
|
||||
const bool instancing_enabled = regs.instanced_arrays.IsInstancingEnabled(gl_index);
|
||||
const GLuint divisor = instancing_enabled ? regs.vertex_array[index].divisor : 0;
|
||||
const bool instancing_enabled = regs.vertex_stream_instances.IsInstancingEnabled(gl_index);
|
||||
const GLuint divisor = instancing_enabled ? regs.vertex_streams[index].frequency : 0;
|
||||
glVertexBindingDivisor(gl_index, divisor);
|
||||
}
|
||||
}
|
||||
@@ -147,27 +147,27 @@ void RasterizerOpenGL::Clear() {
|
||||
bool use_depth{};
|
||||
bool use_stencil{};
|
||||
|
||||
if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
|
||||
regs.clear_buffers.A) {
|
||||
if (regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B ||
|
||||
regs.clear_surface.A) {
|
||||
use_color = true;
|
||||
|
||||
const GLuint index = regs.clear_buffers.RT;
|
||||
const GLuint index = regs.clear_surface.RT;
|
||||
state_tracker.NotifyColorMask(index);
|
||||
glColorMaski(index, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0,
|
||||
regs.clear_buffers.B != 0, regs.clear_buffers.A != 0);
|
||||
glColorMaski(index, regs.clear_surface.R != 0, regs.clear_surface.G != 0,
|
||||
regs.clear_surface.B != 0, regs.clear_surface.A != 0);
|
||||
|
||||
// TODO(Rodrigo): Determine if clamping is used on clears
|
||||
SyncFragmentColorClampState();
|
||||
SyncFramebufferSRGB();
|
||||
}
|
||||
if (regs.clear_buffers.Z) {
|
||||
if (regs.clear_surface.Z) {
|
||||
ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
|
||||
use_depth = true;
|
||||
|
||||
state_tracker.NotifyDepthMask();
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
if (regs.clear_buffers.S) {
|
||||
if (regs.clear_surface.S) {
|
||||
ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!");
|
||||
use_stencil = true;
|
||||
}
|
||||
@@ -184,16 +184,16 @@ void RasterizerOpenGL::Clear() {
|
||||
texture_cache.UpdateRenderTargets(true);
|
||||
state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
|
||||
SyncViewport();
|
||||
if (regs.clear_flags.scissor) {
|
||||
if (regs.clear_control.use_scissor) {
|
||||
SyncScissorTest();
|
||||
} else {
|
||||
state_tracker.NotifyScissor0();
|
||||
glDisablei(GL_SCISSOR_TEST, 0);
|
||||
}
|
||||
UNIMPLEMENTED_IF(regs.clear_flags.viewport);
|
||||
UNIMPLEMENTED_IF(regs.clear_control.use_viewport_clip0);
|
||||
|
||||
if (use_color) {
|
||||
glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color);
|
||||
glClearBufferfv(GL_COLOR, regs.clear_surface.RT, regs.clear_color.data());
|
||||
}
|
||||
if (use_depth && use_stencil) {
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil);
|
||||
@@ -227,14 +227,14 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
|
||||
BeginTransformFeedback(pipeline, primitive_mode);
|
||||
|
||||
const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.vb_base_instance);
|
||||
const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index);
|
||||
const GLsizei num_instances =
|
||||
static_cast<GLsizei>(is_instanced ? maxwell3d->mme_draw.instance_count : 1);
|
||||
if (is_indexed) {
|
||||
const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.vb_element_base);
|
||||
const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_array.count);
|
||||
const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index);
|
||||
const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count);
|
||||
const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
|
||||
const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_array.format);
|
||||
const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
|
||||
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
|
||||
glDrawElements(primitive_mode, num_vertices, format, offset);
|
||||
} else if (num_instances == 1 && base_instance == 0) {
|
||||
@@ -555,9 +555,9 @@ void RasterizerOpenGL::SyncViewport() {
|
||||
if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) {
|
||||
flags[Dirty::FrontFace] = false;
|
||||
|
||||
GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
|
||||
GLenum mode = MaxwellToGL::FrontFace(regs.gl_front_face);
|
||||
bool flip_faces = true;
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
if (regs.window_origin.flip_y != 0) {
|
||||
flip_faces = !flip_faces;
|
||||
}
|
||||
if (regs.viewport_transform[0].scale_y < 0.0f) {
|
||||
@@ -582,14 +582,15 @@ void RasterizerOpenGL::SyncViewport() {
|
||||
if (regs.viewport_transform[0].scale_y < 0.0f) {
|
||||
flip_y = !flip_y;
|
||||
}
|
||||
if (regs.screen_y_control.y_negate != 0) {
|
||||
const bool lower_left{regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft};
|
||||
if (lower_left) {
|
||||
flip_y = !flip_y;
|
||||
}
|
||||
const bool is_zero_to_one = regs.depth_mode == Maxwell::DepthMode::ZeroToOne;
|
||||
const GLenum origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
|
||||
const GLenum depth = is_zero_to_one ? GL_ZERO_TO_ONE : GL_NEGATIVE_ONE_TO_ONE;
|
||||
state_tracker.ClipControl(origin, depth);
|
||||
state_tracker.SetYNegate(regs.screen_y_control.y_negate != 0);
|
||||
state_tracker.SetYNegate(lower_left);
|
||||
}
|
||||
const bool is_rescaling{texture_cache.IsRescaling()};
|
||||
const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
|
||||
@@ -657,7 +658,13 @@ void RasterizerOpenGL::SyncDepthClamp() {
|
||||
}
|
||||
flags[Dirty::DepthClampEnabled] = false;
|
||||
|
||||
oglEnable(GL_DEPTH_CLAMP, maxwell3d->regs.view_volume_clip_control.depth_clamp_disabled == 0);
|
||||
bool depth_clamp_disabled{maxwell3d->regs.viewport_clip_control.geometry_clip ==
|
||||
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
|
||||
maxwell3d->regs.viewport_clip_control.geometry_clip ==
|
||||
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
|
||||
maxwell3d->regs.viewport_clip_control.geometry_clip ==
|
||||
Maxwell::ViewportClipControl::GeometryClip::FrustumZ};
|
||||
oglEnable(GL_DEPTH_CLAMP, !depth_clamp_disabled);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
|
||||
@@ -667,7 +674,7 @@ void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
|
||||
}
|
||||
flags[Dirty::ClipDistances] = false;
|
||||
|
||||
clip_mask &= maxwell3d->regs.clip_distance_enabled;
|
||||
clip_mask &= maxwell3d->regs.user_clip_enable.raw;
|
||||
if (clip_mask == last_clip_distance_mask) {
|
||||
return;
|
||||
}
|
||||
@@ -689,9 +696,9 @@ void RasterizerOpenGL::SyncCullMode() {
|
||||
if (flags[Dirty::CullTest]) {
|
||||
flags[Dirty::CullTest] = false;
|
||||
|
||||
if (regs.cull_test_enabled) {
|
||||
if (regs.gl_cull_test_enabled) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(MaxwellToGL::CullFace(regs.cull_face));
|
||||
glCullFace(MaxwellToGL::CullFace(regs.gl_cull_face));
|
||||
} else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
@@ -743,19 +750,19 @@ void RasterizerOpenGL::SyncStencilTestState() {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
|
||||
|
||||
glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
|
||||
regs.stencil_front_func_ref, regs.stencil_front_func_mask);
|
||||
glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_front_op_zfail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_front_op_zpass));
|
||||
glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_op.func),
|
||||
regs.stencil_front_ref, regs.stencil_front_func_mask);
|
||||
glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op.fail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_front_op.zfail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_front_op.zpass));
|
||||
glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask);
|
||||
|
||||
if (regs.stencil_two_side_enable) {
|
||||
glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_func_func),
|
||||
regs.stencil_back_func_ref, regs.stencil_back_func_mask);
|
||||
glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op_fail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_back_op_zfail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_back_op_zpass));
|
||||
glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func),
|
||||
regs.stencil_back_ref, regs.stencil_back_mask);
|
||||
glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_back_op.zfail),
|
||||
MaxwellToGL::StencilOp(regs.stencil_back_op.zpass));
|
||||
glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask);
|
||||
} else {
|
||||
glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF);
|
||||
@@ -782,7 +789,7 @@ void RasterizerOpenGL::SyncPolygonModes() {
|
||||
flags[Dirty::PolygonModes] = false;
|
||||
|
||||
const auto& regs = maxwell3d->regs;
|
||||
if (regs.fill_rectangle) {
|
||||
if (regs.fill_via_triangle_mode != Maxwell::FillViaTriangleMode::Disabled) {
|
||||
if (!GLAD_GL_NV_fill_rectangle) {
|
||||
LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported");
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
@@ -855,8 +862,8 @@ void RasterizerOpenGL::SyncMultiSampleState() {
|
||||
flags[Dirty::MultisampleControl] = false;
|
||||
|
||||
const auto& regs = maxwell3d->regs;
|
||||
oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage);
|
||||
oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one);
|
||||
oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.anti_alias_alpha_control.alpha_to_coverage);
|
||||
oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.anti_alias_alpha_control.alpha_to_one);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncFragmentColorClampState() {
|
||||
@@ -866,7 +873,8 @@ void RasterizerOpenGL::SyncFragmentColorClampState() {
|
||||
}
|
||||
flags[Dirty::FragmentClampColor] = false;
|
||||
|
||||
glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d->regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
|
||||
glClampColor(GL_CLAMP_FRAGMENT_COLOR,
|
||||
maxwell3d->regs.frag_color_clamp.AnyEnabled() ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendState() {
|
||||
@@ -886,18 +894,18 @@ void RasterizerOpenGL::SyncBlendState() {
|
||||
}
|
||||
flags[Dirty::BlendStates] = false;
|
||||
|
||||
if (!regs.independent_blend_enable) {
|
||||
if (!regs.blend_per_target_enabled) {
|
||||
if (!regs.blend.enable[0]) {
|
||||
glDisable(GL_BLEND);
|
||||
return;
|
||||
}
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb),
|
||||
MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb),
|
||||
MaxwellToGL::BlendFunc(regs.blend.factor_source_a),
|
||||
MaxwellToGL::BlendFunc(regs.blend.factor_dest_a));
|
||||
glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.equation_rgb),
|
||||
MaxwellToGL::BlendEquation(regs.blend.equation_a));
|
||||
glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.color_source),
|
||||
MaxwellToGL::BlendFunc(regs.blend.color_dest),
|
||||
MaxwellToGL::BlendFunc(regs.blend.alpha_source),
|
||||
MaxwellToGL::BlendFunc(regs.blend.alpha_dest));
|
||||
glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.color_op),
|
||||
MaxwellToGL::BlendEquation(regs.blend.alpha_op));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -916,14 +924,13 @@ void RasterizerOpenGL::SyncBlendState() {
|
||||
}
|
||||
glEnablei(GL_BLEND, static_cast<GLuint>(i));
|
||||
|
||||
const auto& src = regs.independent_blend[i];
|
||||
glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.factor_source_rgb),
|
||||
MaxwellToGL::BlendFunc(src.factor_dest_rgb),
|
||||
MaxwellToGL::BlendFunc(src.factor_source_a),
|
||||
MaxwellToGL::BlendFunc(src.factor_dest_a));
|
||||
glBlendEquationSeparatei(static_cast<GLuint>(i),
|
||||
MaxwellToGL::BlendEquation(src.equation_rgb),
|
||||
MaxwellToGL::BlendEquation(src.equation_a));
|
||||
const auto& src = regs.blend_per_target[i];
|
||||
glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.color_source),
|
||||
MaxwellToGL::BlendFunc(src.color_dest),
|
||||
MaxwellToGL::BlendFunc(src.alpha_source),
|
||||
MaxwellToGL::BlendFunc(src.alpha_dest));
|
||||
glBlendEquationSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendEquation(src.color_op),
|
||||
MaxwellToGL::BlendEquation(src.alpha_op));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,7 +944,7 @@ void RasterizerOpenGL::SyncLogicOpState() {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
if (regs.logic_op.enable) {
|
||||
glEnable(GL_COLOR_LOGIC_OP);
|
||||
glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
|
||||
glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.op));
|
||||
} else {
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
}
|
||||
@@ -996,7 +1003,7 @@ void RasterizerOpenGL::SyncPointState() {
|
||||
flags[Dirty::PointSize] = false;
|
||||
|
||||
oglEnable(GL_POINT_SPRITE, maxwell3d->regs.point_sprite_enable);
|
||||
oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.vp_point_size.enable);
|
||||
oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.point_size_attribute.enabled);
|
||||
const bool is_rescaling{texture_cache.IsRescaling()};
|
||||
const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
|
||||
glPointSize(std::max(1.0f, maxwell3d->regs.point_size * scale));
|
||||
@@ -1010,8 +1017,8 @@ void RasterizerOpenGL::SyncLineState() {
|
||||
flags[Dirty::LineWidth] = false;
|
||||
|
||||
const auto& regs = maxwell3d->regs;
|
||||
oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable);
|
||||
glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased);
|
||||
oglEnable(GL_LINE_SMOOTH, regs.line_anti_alias_enable);
|
||||
glLineWidth(regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncPolygonOffset() {
|
||||
@@ -1029,8 +1036,8 @@ void RasterizerOpenGL::SyncPolygonOffset() {
|
||||
if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable ||
|
||||
regs.polygon_offset_point_enable) {
|
||||
// Hardware divides polygon offset units by two
|
||||
glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f,
|
||||
regs.polygon_offset_clamp);
|
||||
glPolygonOffsetClamp(regs.slope_scale_depth_bias, regs.depth_bias / 2.0f,
|
||||
regs.depth_bias_clamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1062,14 +1069,14 @@ void RasterizerOpenGL::SyncFramebufferSRGB() {
|
||||
|
||||
void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
if (regs.tfb_enabled == 0) {
|
||||
if (regs.transform_feedback_enabled == 0) {
|
||||
return;
|
||||
}
|
||||
program->ConfigureTransformFeedback();
|
||||
|
||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
|
||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
|
||||
UNIMPLEMENTED_IF(primitive_mode != GL_POINTS);
|
||||
|
||||
// We may have to call BeginTransformFeedbackNV here since they seem to call different
|
||||
@@ -1080,7 +1087,7 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::EndTransformFeedback() {
|
||||
if (maxwell3d->regs.tfb_enabled != 0) {
|
||||
if (maxwell3d->regs.transform_feedback_enabled != 0) {
|
||||
glEndTransformFeedback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +78,11 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
|
||||
info.tess_clockwise = key.tessellation_clockwise != 0;
|
||||
info.tess_primitive = [&key] {
|
||||
switch (key.tessellation_primitive) {
|
||||
case Maxwell::TessellationPrimitive::Isolines:
|
||||
case Maxwell::Tessellation::DomainType::Isolines:
|
||||
return Shader::TessPrimitive::Isolines;
|
||||
case Maxwell::TessellationPrimitive::Triangles:
|
||||
case Maxwell::Tessellation::DomainType::Triangles:
|
||||
return Shader::TessPrimitive::Triangles;
|
||||
case Maxwell::TessellationPrimitive::Quads:
|
||||
case Maxwell::Tessellation::DomainType::Quads:
|
||||
return Shader::TessPrimitive::Quads;
|
||||
}
|
||||
ASSERT(false);
|
||||
@@ -90,11 +90,11 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
|
||||
}();
|
||||
info.tess_spacing = [&] {
|
||||
switch (key.tessellation_spacing) {
|
||||
case Maxwell::TessellationSpacing::Equal:
|
||||
case Maxwell::Tessellation::Spacing::Integer:
|
||||
return Shader::TessSpacing::Equal;
|
||||
case Maxwell::TessellationSpacing::FractionalOdd:
|
||||
case Maxwell::Tessellation::Spacing::FractionalOdd:
|
||||
return Shader::TessSpacing::FractionalOdd;
|
||||
case Maxwell::TessellationSpacing::FractionalEven:
|
||||
case Maxwell::Tessellation::Spacing::FractionalEven:
|
||||
return Shader::TessSpacing::FractionalEven;
|
||||
}
|
||||
ASSERT(false);
|
||||
@@ -139,14 +139,15 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
|
||||
}
|
||||
|
||||
void SetXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
|
||||
std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) {
|
||||
return VideoCommon::TransformFeedbackState::Layout{
|
||||
.stream = layout.stream,
|
||||
.varying_count = layout.varying_count,
|
||||
.stride = layout.stride,
|
||||
};
|
||||
});
|
||||
state.varyings = regs.tfb_varying_locs;
|
||||
std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(),
|
||||
[](const auto& layout) {
|
||||
return VideoCommon::TransformFeedbackState::Layout{
|
||||
.stream = layout.stream,
|
||||
.varying_count = layout.varying_count,
|
||||
.stride = layout.stride,
|
||||
};
|
||||
});
|
||||
state.varyings = regs.stream_out_layout;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -309,14 +310,16 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
|
||||
}
|
||||
const auto& regs{maxwell3d->regs};
|
||||
graphics_key.raw = 0;
|
||||
graphics_key.early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0);
|
||||
graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
|
||||
graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0
|
||||
? regs.draw.topology.Value()
|
||||
: Maxwell::PrimitiveTopology{});
|
||||
graphics_key.tessellation_primitive.Assign(regs.tess_mode.prim.Value());
|
||||
graphics_key.tessellation_spacing.Assign(regs.tess_mode.spacing.Value());
|
||||
graphics_key.tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
|
||||
graphics_key.xfb_enabled.Assign(regs.tfb_enabled != 0 ? 1 : 0);
|
||||
graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value());
|
||||
graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value());
|
||||
graphics_key.tessellation_clockwise.Assign(
|
||||
regs.tessellation.params.output_primitives.Value() ==
|
||||
Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
|
||||
graphics_key.xfb_enabled.Assign(regs.transform_feedback_enabled != 0 ? 1 : 0);
|
||||
if (graphics_key.xfb_enabled) {
|
||||
SetXfbState(graphics_key.xfb_state, regs);
|
||||
}
|
||||
@@ -354,7 +357,7 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n
|
||||
// If games are using a small index count, we can assume these are full screen quads.
|
||||
// Usually these shaders are only used once for building textures so we can assume they
|
||||
// can't be built async
|
||||
if (maxwell3d->regs.index_array.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
|
||||
if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
|
||||
return pipeline;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -38,12 +38,12 @@ void SetupDirtyColorMasks(Tables& tables) {
|
||||
void SetupDirtyVertexInstances(Tables& tables) {
|
||||
static constexpr std::size_t instance_base_offset = 3;
|
||||
for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
|
||||
const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]);
|
||||
const std::size_t array_offset = OFF(vertex_streams) + i * NUM(vertex_streams[0]);
|
||||
const std::size_t instance_array_offset = array_offset + instance_base_offset;
|
||||
tables[0][instance_array_offset] = static_cast<u8>(VertexInstance0 + i);
|
||||
tables[1][instance_array_offset] = VertexInstances;
|
||||
|
||||
const std::size_t instance_offset = OFF(instanced_arrays) + i;
|
||||
const std::size_t instance_offset = OFF(vertex_stream_instances) + i;
|
||||
tables[0][instance_offset] = static_cast<u8>(VertexInstance0 + i);
|
||||
tables[1][instance_offset] = VertexInstances;
|
||||
}
|
||||
@@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) {
|
||||
FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
|
||||
FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
|
||||
|
||||
tables[0][OFF(viewport_transform_enabled)] = ViewportTransform;
|
||||
tables[1][OFF(viewport_transform_enabled)] = Viewports;
|
||||
tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform;
|
||||
tables[1][OFF(viewport_scale_offset_enbled)] = Viewports;
|
||||
}
|
||||
|
||||
void SetupDirtyScissors(Tables& tables) {
|
||||
@@ -88,7 +88,7 @@ void SetupDirtyPolygonModes(Tables& tables) {
|
||||
|
||||
tables[1][OFF(polygon_mode_front)] = PolygonModes;
|
||||
tables[1][OFF(polygon_mode_back)] = PolygonModes;
|
||||
tables[0][OFF(fill_rectangle)] = PolygonModes;
|
||||
tables[0][OFF(fill_via_triangle_mode)] = PolygonModes;
|
||||
}
|
||||
|
||||
void SetupDirtyDepthTest(Tables& tables) {
|
||||
@@ -100,11 +100,11 @@ void SetupDirtyDepthTest(Tables& tables) {
|
||||
|
||||
void SetupDirtyStencilTest(Tables& tables) {
|
||||
static constexpr std::array offsets = {
|
||||
OFF(stencil_enable), OFF(stencil_front_func_func), OFF(stencil_front_func_ref),
|
||||
OFF(stencil_front_func_mask), OFF(stencil_front_op_fail), OFF(stencil_front_op_zfail),
|
||||
OFF(stencil_front_op_zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable),
|
||||
OFF(stencil_back_func_func), OFF(stencil_back_func_ref), OFF(stencil_back_func_mask),
|
||||
OFF(stencil_back_op_fail), OFF(stencil_back_op_zfail), OFF(stencil_back_op_zpass),
|
||||
OFF(stencil_enable), OFF(stencil_front_op.func), OFF(stencil_front_ref),
|
||||
OFF(stencil_front_func_mask), OFF(stencil_front_op.fail), OFF(stencil_front_op.zfail),
|
||||
OFF(stencil_front_op.zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable),
|
||||
OFF(stencil_back_op.func), OFF(stencil_back_ref), OFF(stencil_back_func_mask),
|
||||
OFF(stencil_back_op.fail), OFF(stencil_back_op.zfail), OFF(stencil_back_op.zpass),
|
||||
OFF(stencil_back_mask)};
|
||||
for (const auto offset : offsets) {
|
||||
tables[0][offset] = StencilTest;
|
||||
@@ -121,15 +121,15 @@ void SetupDirtyAlphaTest(Tables& tables) {
|
||||
void SetupDirtyBlend(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
|
||||
|
||||
tables[0][OFF(independent_blend_enable)] = BlendIndependentEnabled;
|
||||
tables[0][OFF(blend_per_target_enabled)] = BlendIndependentEnabled;
|
||||
|
||||
for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
|
||||
const std::size_t offset = OFF(independent_blend) + i * NUM(independent_blend[0]);
|
||||
FillBlock(tables[0], offset, NUM(independent_blend[0]), BlendState0 + i);
|
||||
const std::size_t offset = OFF(blend_per_target) + i * NUM(blend_per_target[0]);
|
||||
FillBlock(tables[0], offset, NUM(blend_per_target[0]), BlendState0 + i);
|
||||
|
||||
tables[0][OFF(blend.enable) + i] = static_cast<u8>(BlendState0 + i);
|
||||
}
|
||||
FillBlock(tables[1], OFF(independent_blend), NUM(independent_blend), BlendStates);
|
||||
FillBlock(tables[1], OFF(blend_per_target), NUM(blend_per_target), BlendStates);
|
||||
FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates);
|
||||
}
|
||||
|
||||
@@ -142,13 +142,14 @@ void SetupDirtyPolygonOffset(Tables& tables) {
|
||||
table[OFF(polygon_offset_fill_enable)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_line_enable)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_point_enable)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_factor)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_units)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_clamp)] = PolygonOffset;
|
||||
table[OFF(slope_scale_depth_bias)] = PolygonOffset;
|
||||
table[OFF(depth_bias)] = PolygonOffset;
|
||||
table[OFF(depth_bias_clamp)] = PolygonOffset;
|
||||
}
|
||||
|
||||
void SetupDirtyMultisampleControl(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl);
|
||||
FillBlock(tables[0], OFF(anti_alias_alpha_control), NUM(anti_alias_alpha_control),
|
||||
MultisampleControl);
|
||||
}
|
||||
|
||||
void SetupDirtyRasterizeEnable(Tables& tables) {
|
||||
@@ -168,7 +169,7 @@ void SetupDirtyFragmentClampColor(Tables& tables) {
|
||||
}
|
||||
|
||||
void SetupDirtyPointSize(Tables& tables) {
|
||||
tables[0][OFF(vp_point_size)] = PointSize;
|
||||
tables[0][OFF(point_size_attribute)] = PointSize;
|
||||
tables[0][OFF(point_size)] = PointSize;
|
||||
tables[0][OFF(point_sprite_enable)] = PointSize;
|
||||
}
|
||||
@@ -176,28 +177,28 @@ void SetupDirtyPointSize(Tables& tables) {
|
||||
void SetupDirtyLineWidth(Tables& tables) {
|
||||
tables[0][OFF(line_width_smooth)] = LineWidth;
|
||||
tables[0][OFF(line_width_aliased)] = LineWidth;
|
||||
tables[0][OFF(line_smooth_enable)] = LineWidth;
|
||||
tables[0][OFF(line_anti_alias_enable)] = LineWidth;
|
||||
}
|
||||
|
||||
void SetupDirtyClipControl(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(screen_y_control)] = ClipControl;
|
||||
table[OFF(window_origin)] = ClipControl;
|
||||
table[OFF(depth_mode)] = ClipControl;
|
||||
}
|
||||
|
||||
void SetupDirtyDepthClampEnabled(Tables& tables) {
|
||||
tables[0][OFF(view_volume_clip_control)] = DepthClampEnabled;
|
||||
tables[0][OFF(viewport_clip_control)] = DepthClampEnabled;
|
||||
}
|
||||
|
||||
void SetupDirtyMisc(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
|
||||
table[OFF(clip_distance_enabled)] = ClipDistances;
|
||||
table[OFF(user_clip_enable)] = ClipDistances;
|
||||
|
||||
table[OFF(front_face)] = FrontFace;
|
||||
table[OFF(gl_front_face)] = FrontFace;
|
||||
|
||||
table[OFF(cull_test_enabled)] = CullTest;
|
||||
table[OFF(cull_face)] = CullTest;
|
||||
table[OFF(gl_cull_test_enabled)] = CullTest;
|
||||
table[OFF(gl_cull_face)] = CullTest;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -126,51 +126,60 @@ inline const FormatTuple& GetFormatTuple(VideoCore::Surface::PixelFormat pixel_f
|
||||
|
||||
inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
|
||||
switch (attrib.type) {
|
||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||
case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
|
||||
ASSERT_MSG(false, "Invalid vertex attribute type!");
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UNorm:
|
||||
case Maxwell::VertexAttribute::Type::UScaled:
|
||||
case Maxwell::VertexAttribute::Type::UInt:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
|
||||
return GL_UNSIGNED_INT;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
case Maxwell::VertexAttribute::Type::SNorm:
|
||||
case Maxwell::VertexAttribute::Type::SScaled:
|
||||
case Maxwell::VertexAttribute::Type::SInt:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return GL_BYTE;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return GL_SHORT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
|
||||
return GL_INT;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return GL_INT_2_10_10_10_REV;
|
||||
default:
|
||||
break;
|
||||
@@ -178,17 +187,17 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return GL_HALF_FLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
|
||||
return GL_FLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_11_11_10:
|
||||
case Maxwell::VertexAttribute::Size::Size_B10_G11_R11:
|
||||
return GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||
default:
|
||||
break;
|
||||
@@ -335,20 +344,20 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
|
||||
|
||||
inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
switch (equation) {
|
||||
case Maxwell::Blend::Equation::Add:
|
||||
case Maxwell::Blend::Equation::AddGL:
|
||||
case Maxwell::Blend::Equation::Add_D3D:
|
||||
case Maxwell::Blend::Equation::Add_GL:
|
||||
return GL_FUNC_ADD;
|
||||
case Maxwell::Blend::Equation::Subtract:
|
||||
case Maxwell::Blend::Equation::SubtractGL:
|
||||
case Maxwell::Blend::Equation::Subtract_D3D:
|
||||
case Maxwell::Blend::Equation::Subtract_GL:
|
||||
return GL_FUNC_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::ReverseSubtract:
|
||||
case Maxwell::Blend::Equation::ReverseSubtractGL:
|
||||
case Maxwell::Blend::Equation::ReverseSubtract_D3D:
|
||||
case Maxwell::Blend::Equation::ReverseSubtract_GL:
|
||||
return GL_FUNC_REVERSE_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::Min:
|
||||
case Maxwell::Blend::Equation::MinGL:
|
||||
case Maxwell::Blend::Equation::Min_D3D:
|
||||
case Maxwell::Blend::Equation::Min_GL:
|
||||
return GL_MIN;
|
||||
case Maxwell::Blend::Equation::Max:
|
||||
case Maxwell::Blend::Equation::MaxGL:
|
||||
case Maxwell::Blend::Equation::Max_D3D:
|
||||
case Maxwell::Blend::Equation::Max_GL:
|
||||
return GL_MAX;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation);
|
||||
@@ -357,62 +366,62 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
|
||||
inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
|
||||
switch (factor) {
|
||||
case Maxwell::Blend::Factor::Zero:
|
||||
case Maxwell::Blend::Factor::ZeroGL:
|
||||
case Maxwell::Blend::Factor::Zero_D3D:
|
||||
case Maxwell::Blend::Factor::Zero_GL:
|
||||
return GL_ZERO;
|
||||
case Maxwell::Blend::Factor::One:
|
||||
case Maxwell::Blend::Factor::OneGL:
|
||||
case Maxwell::Blend::Factor::One_D3D:
|
||||
case Maxwell::Blend::Factor::One_GL:
|
||||
return GL_ONE;
|
||||
case Maxwell::Blend::Factor::SourceColor:
|
||||
case Maxwell::Blend::Factor::SourceColorGL:
|
||||
case Maxwell::Blend::Factor::SourceColor_D3D:
|
||||
case Maxwell::Blend::Factor::SourceColor_GL:
|
||||
return GL_SRC_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor_GL:
|
||||
return GL_ONE_MINUS_SRC_COLOR;
|
||||
case Maxwell::Blend::Factor::SourceAlpha:
|
||||
case Maxwell::Blend::Factor::SourceAlphaGL:
|
||||
case Maxwell::Blend::Factor::SourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::SourceAlpha_GL:
|
||||
return GL_SRC_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL:
|
||||
return GL_ONE_MINUS_SRC_ALPHA;
|
||||
case Maxwell::Blend::Factor::DestAlpha:
|
||||
case Maxwell::Blend::Factor::DestAlphaGL:
|
||||
case Maxwell::Blend::Factor::DestAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::DestAlpha_GL:
|
||||
return GL_DST_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha_GL:
|
||||
return GL_ONE_MINUS_DST_ALPHA;
|
||||
case Maxwell::Blend::Factor::DestColor:
|
||||
case Maxwell::Blend::Factor::DestColorGL:
|
||||
case Maxwell::Blend::Factor::DestColor_D3D:
|
||||
case Maxwell::Blend::Factor::DestColor_GL:
|
||||
return GL_DST_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor_GL:
|
||||
return GL_ONE_MINUS_DST_COLOR;
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturateGL:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate_GL:
|
||||
return GL_SRC_ALPHA_SATURATE;
|
||||
case Maxwell::Blend::Factor::Source1Color:
|
||||
case Maxwell::Blend::Factor::Source1ColorGL:
|
||||
case Maxwell::Blend::Factor::Source1Color_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Color_GL:
|
||||
return GL_SRC1_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1ColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
|
||||
return GL_ONE_MINUS_SRC1_COLOR;
|
||||
case Maxwell::Blend::Factor::Source1Alpha:
|
||||
case Maxwell::Blend::Factor::Source1AlphaGL:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_GL:
|
||||
return GL_SRC1_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1AlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
|
||||
return GL_ONE_MINUS_SRC1_ALPHA;
|
||||
case Maxwell::Blend::Factor::ConstantColor:
|
||||
case Maxwell::Blend::Factor::ConstantColorGL:
|
||||
case Maxwell::Blend::Factor::BlendFactor_D3D:
|
||||
case Maxwell::Blend::Factor::ConstantColor_GL:
|
||||
return GL_CONSTANT_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColor:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColor_GL:
|
||||
return GL_ONE_MINUS_CONSTANT_COLOR;
|
||||
case Maxwell::Blend::Factor::ConstantAlpha:
|
||||
case Maxwell::Blend::Factor::ConstantAlphaGL:
|
||||
case Maxwell::Blend::Factor::BothSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::ConstantAlpha_GL:
|
||||
return GL_CONSTANT_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL:
|
||||
return GL_ONE_MINUS_CONSTANT_ALPHA;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor);
|
||||
@@ -421,60 +430,60 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
|
||||
|
||||
inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
|
||||
switch (comparison) {
|
||||
case Maxwell::ComparisonOp::Never:
|
||||
case Maxwell::ComparisonOp::NeverOld:
|
||||
case Maxwell::ComparisonOp::Never_D3D:
|
||||
case Maxwell::ComparisonOp::Never_GL:
|
||||
return GL_NEVER;
|
||||
case Maxwell::ComparisonOp::Less:
|
||||
case Maxwell::ComparisonOp::LessOld:
|
||||
case Maxwell::ComparisonOp::Less_D3D:
|
||||
case Maxwell::ComparisonOp::Less_GL:
|
||||
return GL_LESS;
|
||||
case Maxwell::ComparisonOp::Equal:
|
||||
case Maxwell::ComparisonOp::EqualOld:
|
||||
case Maxwell::ComparisonOp::Equal_D3D:
|
||||
case Maxwell::ComparisonOp::Equal_GL:
|
||||
return GL_EQUAL;
|
||||
case Maxwell::ComparisonOp::LessEqual:
|
||||
case Maxwell::ComparisonOp::LessEqualOld:
|
||||
case Maxwell::ComparisonOp::LessEqual_D3D:
|
||||
case Maxwell::ComparisonOp::LessEqual_GL:
|
||||
return GL_LEQUAL;
|
||||
case Maxwell::ComparisonOp::Greater:
|
||||
case Maxwell::ComparisonOp::GreaterOld:
|
||||
case Maxwell::ComparisonOp::Greater_D3D:
|
||||
case Maxwell::ComparisonOp::Greater_GL:
|
||||
return GL_GREATER;
|
||||
case Maxwell::ComparisonOp::NotEqual:
|
||||
case Maxwell::ComparisonOp::NotEqualOld:
|
||||
case Maxwell::ComparisonOp::NotEqual_D3D:
|
||||
case Maxwell::ComparisonOp::NotEqual_GL:
|
||||
return GL_NOTEQUAL;
|
||||
case Maxwell::ComparisonOp::GreaterEqual:
|
||||
case Maxwell::ComparisonOp::GreaterEqualOld:
|
||||
case Maxwell::ComparisonOp::GreaterEqual_D3D:
|
||||
case Maxwell::ComparisonOp::GreaterEqual_GL:
|
||||
return GL_GEQUAL;
|
||||
case Maxwell::ComparisonOp::Always:
|
||||
case Maxwell::ComparisonOp::AlwaysOld:
|
||||
case Maxwell::ComparisonOp::Always_D3D:
|
||||
case Maxwell::ComparisonOp::Always_GL:
|
||||
return GL_ALWAYS;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
|
||||
return GL_ALWAYS;
|
||||
}
|
||||
|
||||
inline GLenum StencilOp(Maxwell::StencilOp stencil) {
|
||||
inline GLenum StencilOp(Maxwell::StencilOp::Op stencil) {
|
||||
switch (stencil) {
|
||||
case Maxwell::StencilOp::Keep:
|
||||
case Maxwell::StencilOp::KeepOGL:
|
||||
case Maxwell::StencilOp::Op::Keep_D3D:
|
||||
case Maxwell::StencilOp::Op::Keep_GL:
|
||||
return GL_KEEP;
|
||||
case Maxwell::StencilOp::Zero:
|
||||
case Maxwell::StencilOp::ZeroOGL:
|
||||
case Maxwell::StencilOp::Op::Zero_D3D:
|
||||
case Maxwell::StencilOp::Op::Zero_GL:
|
||||
return GL_ZERO;
|
||||
case Maxwell::StencilOp::Replace:
|
||||
case Maxwell::StencilOp::ReplaceOGL:
|
||||
case Maxwell::StencilOp::Op::Replace_D3D:
|
||||
case Maxwell::StencilOp::Op::Replace_GL:
|
||||
return GL_REPLACE;
|
||||
case Maxwell::StencilOp::Incr:
|
||||
case Maxwell::StencilOp::IncrOGL:
|
||||
case Maxwell::StencilOp::Op::IncrSaturate_D3D:
|
||||
case Maxwell::StencilOp::Op::IncrSaturate_GL:
|
||||
return GL_INCR;
|
||||
case Maxwell::StencilOp::Decr:
|
||||
case Maxwell::StencilOp::DecrOGL:
|
||||
case Maxwell::StencilOp::Op::DecrSaturate_D3D:
|
||||
case Maxwell::StencilOp::Op::DecrSaturate_GL:
|
||||
return GL_DECR;
|
||||
case Maxwell::StencilOp::Invert:
|
||||
case Maxwell::StencilOp::InvertOGL:
|
||||
case Maxwell::StencilOp::Op::Invert_D3D:
|
||||
case Maxwell::StencilOp::Op::Invert_GL:
|
||||
return GL_INVERT;
|
||||
case Maxwell::StencilOp::IncrWrap:
|
||||
case Maxwell::StencilOp::IncrWrapOGL:
|
||||
case Maxwell::StencilOp::Op::Incr_D3D:
|
||||
case Maxwell::StencilOp::Op::Incr_GL:
|
||||
return GL_INCR_WRAP;
|
||||
case Maxwell::StencilOp::DecrWrap:
|
||||
case Maxwell::StencilOp::DecrWrapOGL:
|
||||
case Maxwell::StencilOp::Op::Decr_D3D:
|
||||
case Maxwell::StencilOp::Op::Decr_GL:
|
||||
return GL_DECR_WRAP;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil);
|
||||
@@ -505,39 +514,39 @@ inline GLenum CullFace(Maxwell::CullFace cull_face) {
|
||||
return GL_BACK;
|
||||
}
|
||||
|
||||
inline GLenum LogicOp(Maxwell::LogicOperation operation) {
|
||||
inline GLenum LogicOp(Maxwell::LogicOp::Op operation) {
|
||||
switch (operation) {
|
||||
case Maxwell::LogicOperation::Clear:
|
||||
case Maxwell::LogicOp::Op::Clear:
|
||||
return GL_CLEAR;
|
||||
case Maxwell::LogicOperation::And:
|
||||
case Maxwell::LogicOp::Op::And:
|
||||
return GL_AND;
|
||||
case Maxwell::LogicOperation::AndReverse:
|
||||
case Maxwell::LogicOp::Op::AndReverse:
|
||||
return GL_AND_REVERSE;
|
||||
case Maxwell::LogicOperation::Copy:
|
||||
case Maxwell::LogicOp::Op::Copy:
|
||||
return GL_COPY;
|
||||
case Maxwell::LogicOperation::AndInverted:
|
||||
case Maxwell::LogicOp::Op::AndInverted:
|
||||
return GL_AND_INVERTED;
|
||||
case Maxwell::LogicOperation::NoOp:
|
||||
case Maxwell::LogicOp::Op::NoOp:
|
||||
return GL_NOOP;
|
||||
case Maxwell::LogicOperation::Xor:
|
||||
case Maxwell::LogicOp::Op::Xor:
|
||||
return GL_XOR;
|
||||
case Maxwell::LogicOperation::Or:
|
||||
case Maxwell::LogicOp::Op::Or:
|
||||
return GL_OR;
|
||||
case Maxwell::LogicOperation::Nor:
|
||||
case Maxwell::LogicOp::Op::Nor:
|
||||
return GL_NOR;
|
||||
case Maxwell::LogicOperation::Equiv:
|
||||
case Maxwell::LogicOp::Op::Equiv:
|
||||
return GL_EQUIV;
|
||||
case Maxwell::LogicOperation::Invert:
|
||||
case Maxwell::LogicOp::Op::Invert:
|
||||
return GL_INVERT;
|
||||
case Maxwell::LogicOperation::OrReverse:
|
||||
case Maxwell::LogicOp::Op::OrReverse:
|
||||
return GL_OR_REVERSE;
|
||||
case Maxwell::LogicOperation::CopyInverted:
|
||||
case Maxwell::LogicOp::Op::CopyInverted:
|
||||
return GL_COPY_INVERTED;
|
||||
case Maxwell::LogicOperation::OrInverted:
|
||||
case Maxwell::LogicOp::Op::OrInverted:
|
||||
return GL_OR_INVERTED;
|
||||
case Maxwell::LogicOperation::Nand:
|
||||
case Maxwell::LogicOp::Op::Nand:
|
||||
return GL_NAND;
|
||||
case Maxwell::LogicOperation::Set:
|
||||
case Maxwell::LogicOp::Op::Set:
|
||||
return GL_SET;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented logic operation={}", operation);
|
||||
|
||||
@@ -34,14 +34,15 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
|
||||
};
|
||||
|
||||
void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
|
||||
std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) {
|
||||
return VideoCommon::TransformFeedbackState::Layout{
|
||||
.stream = layout.stream,
|
||||
.varying_count = layout.varying_count,
|
||||
.stride = layout.stride,
|
||||
};
|
||||
});
|
||||
state.varyings = regs.tfb_varying_locs;
|
||||
std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(),
|
||||
[](const auto& layout) {
|
||||
return VideoCommon::TransformFeedbackState::Layout{
|
||||
.stream = layout.stream,
|
||||
.varying_count = layout.varying_count,
|
||||
.stride = layout.stride,
|
||||
};
|
||||
});
|
||||
state.varyings = regs.stream_out_layout;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -58,33 +59,38 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
|
||||
raw1 = 0;
|
||||
extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0);
|
||||
dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0);
|
||||
xfb_enabled.Assign(regs.tfb_enabled != 0);
|
||||
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
|
||||
primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
|
||||
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
|
||||
depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
|
||||
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
|
||||
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
|
||||
regs.viewport_clip_control.geometry_clip ==
|
||||
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
|
||||
regs.viewport_clip_control.geometry_clip ==
|
||||
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
|
||||
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
|
||||
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
|
||||
patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
|
||||
tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
|
||||
tessellation_spacing.Assign(static_cast<u32>(regs.tess_mode.spacing.Value()));
|
||||
tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
|
||||
tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
|
||||
tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
|
||||
tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
|
||||
Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
|
||||
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
|
||||
logic_op.Assign(PackLogicOp(regs.logic_op.operation));
|
||||
logic_op.Assign(PackLogicOp(regs.logic_op.op));
|
||||
topology.Assign(regs.draw.topology);
|
||||
msaa_mode.Assign(regs.multisample_mode);
|
||||
msaa_mode.Assign(regs.anti_alias_samples_mode);
|
||||
|
||||
raw2 = 0;
|
||||
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
|
||||
const auto test_func =
|
||||
regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always;
|
||||
regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL;
|
||||
alpha_test_func.Assign(PackComparisonOp(test_func));
|
||||
early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0);
|
||||
early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
|
||||
depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0);
|
||||
depth_format.Assign(static_cast<u32>(regs.zeta.format));
|
||||
y_negate.Assign(regs.screen_y_control.y_negate != 0 ? 1 : 0);
|
||||
provoking_vertex_last.Assign(regs.provoking_vertex_last != 0 ? 1 : 0);
|
||||
conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0);
|
||||
smooth_lines.Assign(regs.line_smooth_enable != 0 ? 1 : 0);
|
||||
y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0);
|
||||
provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0);
|
||||
smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
|
||||
|
||||
for (size_t i = 0; i < regs.rt.size(); ++i) {
|
||||
color_formats[i] = static_cast<u8>(regs.rt[i].format);
|
||||
@@ -116,8 +122,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
|
||||
maxwell3d.dirty.flags[Dirty::VertexInput] = false;
|
||||
enabled_divisors = 0;
|
||||
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index);
|
||||
binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0;
|
||||
const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index);
|
||||
binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0;
|
||||
enabled_divisors |= (is_enabled ? u64{1} : 0) << index;
|
||||
}
|
||||
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||
@@ -164,17 +170,17 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
|
||||
|
||||
// TODO: C++20 Use templated lambda to deduplicate code
|
||||
|
||||
if (!regs.independent_blend_enable) {
|
||||
const auto& src = regs.blend;
|
||||
if (!src.enable[index]) {
|
||||
if (!regs.blend_per_target_enabled) {
|
||||
if (!regs.blend.enable[index]) {
|
||||
return;
|
||||
}
|
||||
equation_rgb.Assign(PackBlendEquation(src.equation_rgb));
|
||||
equation_a.Assign(PackBlendEquation(src.equation_a));
|
||||
factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb));
|
||||
factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb));
|
||||
factor_source_a.Assign(PackBlendFactor(src.factor_source_a));
|
||||
factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a));
|
||||
const auto& src = regs.blend;
|
||||
equation_rgb.Assign(PackBlendEquation(src.color_op));
|
||||
equation_a.Assign(PackBlendEquation(src.alpha_op));
|
||||
factor_source_rgb.Assign(PackBlendFactor(src.color_source));
|
||||
factor_dest_rgb.Assign(PackBlendFactor(src.color_dest));
|
||||
factor_source_a.Assign(PackBlendFactor(src.alpha_source));
|
||||
factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
|
||||
enable.Assign(1);
|
||||
return;
|
||||
}
|
||||
@@ -182,34 +188,34 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
|
||||
if (!regs.blend.enable[index]) {
|
||||
return;
|
||||
}
|
||||
const auto& src = regs.independent_blend[index];
|
||||
equation_rgb.Assign(PackBlendEquation(src.equation_rgb));
|
||||
equation_a.Assign(PackBlendEquation(src.equation_a));
|
||||
factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb));
|
||||
factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb));
|
||||
factor_source_a.Assign(PackBlendFactor(src.factor_source_a));
|
||||
factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a));
|
||||
const auto& src = regs.blend_per_target[index];
|
||||
equation_rgb.Assign(PackBlendEquation(src.color_op));
|
||||
equation_a.Assign(PackBlendEquation(src.alpha_op));
|
||||
factor_source_rgb.Assign(PackBlendFactor(src.color_source));
|
||||
factor_dest_rgb.Assign(PackBlendFactor(src.color_dest));
|
||||
factor_source_a.Assign(PackBlendFactor(src.alpha_source));
|
||||
factor_dest_a.Assign(PackBlendFactor(src.alpha_dest));
|
||||
enable.Assign(1);
|
||||
}
|
||||
|
||||
void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
|
||||
u32 packed_front_face = PackFrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
u32 packed_front_face = PackFrontFace(regs.gl_front_face);
|
||||
if (regs.window_origin.flip_y != 0) {
|
||||
// Flip front face
|
||||
packed_front_face = 1 - packed_front_face;
|
||||
}
|
||||
|
||||
raw1 = 0;
|
||||
raw2 = 0;
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_op.func));
|
||||
if (regs.stencil_two_side_enable) {
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op.fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op.zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op.zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_op.func));
|
||||
} else {
|
||||
back.action_stencil_fail.Assign(front.action_stencil_fail);
|
||||
back.action_depth_fail.Assign(front.action_depth_fail);
|
||||
@@ -222,9 +228,9 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
|
||||
depth_test_enable.Assign(regs.depth_test_enable);
|
||||
front_face.Assign(packed_front_face);
|
||||
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
|
||||
cull_face.Assign(PackCullFace(regs.cull_face));
|
||||
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
|
||||
std::ranges::transform(regs.vertex_array, vertex_strides.begin(), [](const auto& array) {
|
||||
cull_face.Assign(PackCullFace(regs.gl_cull_face));
|
||||
cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0);
|
||||
std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) {
|
||||
return static_cast<u16>(array.stride.Value());
|
||||
});
|
||||
}
|
||||
@@ -251,41 +257,42 @@ Maxwell::ComparisonOp FixedPipelineState::UnpackComparisonOp(u32 packed) noexcep
|
||||
return static_cast<Maxwell::ComparisonOp>(packed + 1);
|
||||
}
|
||||
|
||||
u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp op) noexcept {
|
||||
u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp::Op op) noexcept {
|
||||
switch (op) {
|
||||
case Maxwell::StencilOp::Keep:
|
||||
case Maxwell::StencilOp::KeepOGL:
|
||||
case Maxwell::StencilOp::Op::Keep_D3D:
|
||||
case Maxwell::StencilOp::Op::Keep_GL:
|
||||
return 0;
|
||||
case Maxwell::StencilOp::Zero:
|
||||
case Maxwell::StencilOp::ZeroOGL:
|
||||
case Maxwell::StencilOp::Op::Zero_D3D:
|
||||
case Maxwell::StencilOp::Op::Zero_GL:
|
||||
return 1;
|
||||
case Maxwell::StencilOp::Replace:
|
||||
case Maxwell::StencilOp::ReplaceOGL:
|
||||
case Maxwell::StencilOp::Op::Replace_D3D:
|
||||
case Maxwell::StencilOp::Op::Replace_GL:
|
||||
return 2;
|
||||
case Maxwell::StencilOp::Incr:
|
||||
case Maxwell::StencilOp::IncrOGL:
|
||||
case Maxwell::StencilOp::Op::IncrSaturate_D3D:
|
||||
case Maxwell::StencilOp::Op::IncrSaturate_GL:
|
||||
return 3;
|
||||
case Maxwell::StencilOp::Decr:
|
||||
case Maxwell::StencilOp::DecrOGL:
|
||||
case Maxwell::StencilOp::Op::DecrSaturate_D3D:
|
||||
case Maxwell::StencilOp::Op::DecrSaturate_GL:
|
||||
return 4;
|
||||
case Maxwell::StencilOp::Invert:
|
||||
case Maxwell::StencilOp::InvertOGL:
|
||||
case Maxwell::StencilOp::Op::Invert_D3D:
|
||||
case Maxwell::StencilOp::Op::Invert_GL:
|
||||
return 5;
|
||||
case Maxwell::StencilOp::IncrWrap:
|
||||
case Maxwell::StencilOp::IncrWrapOGL:
|
||||
case Maxwell::StencilOp::Op::Incr_D3D:
|
||||
case Maxwell::StencilOp::Op::Incr_GL:
|
||||
return 6;
|
||||
case Maxwell::StencilOp::DecrWrap:
|
||||
case Maxwell::StencilOp::DecrWrapOGL:
|
||||
case Maxwell::StencilOp::Op::Decr_D3D:
|
||||
case Maxwell::StencilOp::Op::Decr_GL:
|
||||
return 7;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Maxwell::StencilOp FixedPipelineState::UnpackStencilOp(u32 packed) noexcept {
|
||||
static constexpr std::array LUT = {Maxwell::StencilOp::Keep, Maxwell::StencilOp::Zero,
|
||||
Maxwell::StencilOp::Replace, Maxwell::StencilOp::Incr,
|
||||
Maxwell::StencilOp::Decr, Maxwell::StencilOp::Invert,
|
||||
Maxwell::StencilOp::IncrWrap, Maxwell::StencilOp::DecrWrap};
|
||||
Maxwell::StencilOp::Op FixedPipelineState::UnpackStencilOp(u32 packed) noexcept {
|
||||
static constexpr std::array LUT = {
|
||||
Maxwell::StencilOp::Op::Keep_D3D, Maxwell::StencilOp::Op::Zero_D3D,
|
||||
Maxwell::StencilOp::Op::Replace_D3D, Maxwell::StencilOp::Op::IncrSaturate_D3D,
|
||||
Maxwell::StencilOp::Op::DecrSaturate_D3D, Maxwell::StencilOp::Op::Invert_D3D,
|
||||
Maxwell::StencilOp::Op::Incr_D3D, Maxwell::StencilOp::Op::Decr_D3D};
|
||||
return LUT[packed];
|
||||
}
|
||||
|
||||
@@ -318,30 +325,30 @@ Maxwell::PolygonMode FixedPipelineState::UnpackPolygonMode(u32 packed) noexcept
|
||||
return static_cast<Maxwell::PolygonMode>(packed + 0x1B00);
|
||||
}
|
||||
|
||||
u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOperation op) noexcept {
|
||||
u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOp::Op op) noexcept {
|
||||
return static_cast<u32>(op) - 0x1500;
|
||||
}
|
||||
|
||||
Maxwell::LogicOperation FixedPipelineState::UnpackLogicOp(u32 packed) noexcept {
|
||||
return static_cast<Maxwell::LogicOperation>(packed + 0x1500);
|
||||
Maxwell::LogicOp::Op FixedPipelineState::UnpackLogicOp(u32 packed) noexcept {
|
||||
return static_cast<Maxwell::LogicOp::Op>(packed + 0x1500);
|
||||
}
|
||||
|
||||
u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noexcept {
|
||||
switch (equation) {
|
||||
case Maxwell::Blend::Equation::Add:
|
||||
case Maxwell::Blend::Equation::AddGL:
|
||||
case Maxwell::Blend::Equation::Add_D3D:
|
||||
case Maxwell::Blend::Equation::Add_GL:
|
||||
return 0;
|
||||
case Maxwell::Blend::Equation::Subtract:
|
||||
case Maxwell::Blend::Equation::SubtractGL:
|
||||
case Maxwell::Blend::Equation::Subtract_D3D:
|
||||
case Maxwell::Blend::Equation::Subtract_GL:
|
||||
return 1;
|
||||
case Maxwell::Blend::Equation::ReverseSubtract:
|
||||
case Maxwell::Blend::Equation::ReverseSubtractGL:
|
||||
case Maxwell::Blend::Equation::ReverseSubtract_D3D:
|
||||
case Maxwell::Blend::Equation::ReverseSubtract_GL:
|
||||
return 2;
|
||||
case Maxwell::Blend::Equation::Min:
|
||||
case Maxwell::Blend::Equation::MinGL:
|
||||
case Maxwell::Blend::Equation::Min_D3D:
|
||||
case Maxwell::Blend::Equation::Min_GL:
|
||||
return 3;
|
||||
case Maxwell::Blend::Equation::Max:
|
||||
case Maxwell::Blend::Equation::MaxGL:
|
||||
case Maxwell::Blend::Equation::Max_D3D:
|
||||
case Maxwell::Blend::Equation::Max_GL:
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
@@ -349,97 +356,99 @@ u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noe
|
||||
|
||||
Maxwell::Blend::Equation FixedPipelineState::UnpackBlendEquation(u32 packed) noexcept {
|
||||
static constexpr std::array LUT = {
|
||||
Maxwell::Blend::Equation::Add, Maxwell::Blend::Equation::Subtract,
|
||||
Maxwell::Blend::Equation::ReverseSubtract, Maxwell::Blend::Equation::Min,
|
||||
Maxwell::Blend::Equation::Max};
|
||||
Maxwell::Blend::Equation::Add_D3D, Maxwell::Blend::Equation::Subtract_D3D,
|
||||
Maxwell::Blend::Equation::ReverseSubtract_D3D, Maxwell::Blend::Equation::Min_D3D,
|
||||
Maxwell::Blend::Equation::Max_D3D};
|
||||
return LUT[packed];
|
||||
}
|
||||
|
||||
u32 FixedPipelineState::PackBlendFactor(Maxwell::Blend::Factor factor) noexcept {
|
||||
switch (factor) {
|
||||
case Maxwell::Blend::Factor::Zero:
|
||||
case Maxwell::Blend::Factor::ZeroGL:
|
||||
case Maxwell::Blend::Factor::Zero_D3D:
|
||||
case Maxwell::Blend::Factor::Zero_GL:
|
||||
return 0;
|
||||
case Maxwell::Blend::Factor::One:
|
||||
case Maxwell::Blend::Factor::OneGL:
|
||||
case Maxwell::Blend::Factor::One_D3D:
|
||||
case Maxwell::Blend::Factor::One_GL:
|
||||
return 1;
|
||||
case Maxwell::Blend::Factor::SourceColor:
|
||||
case Maxwell::Blend::Factor::SourceColorGL:
|
||||
case Maxwell::Blend::Factor::SourceColor_D3D:
|
||||
case Maxwell::Blend::Factor::SourceColor_GL:
|
||||
return 2;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor_GL:
|
||||
return 3;
|
||||
case Maxwell::Blend::Factor::SourceAlpha:
|
||||
case Maxwell::Blend::Factor::SourceAlphaGL:
|
||||
case Maxwell::Blend::Factor::SourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::SourceAlpha_GL:
|
||||
return 4;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL:
|
||||
return 5;
|
||||
case Maxwell::Blend::Factor::DestAlpha:
|
||||
case Maxwell::Blend::Factor::DestAlphaGL:
|
||||
case Maxwell::Blend::Factor::DestAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::DestAlpha_GL:
|
||||
return 6;
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha_GL:
|
||||
return 7;
|
||||
case Maxwell::Blend::Factor::DestColor:
|
||||
case Maxwell::Blend::Factor::DestColorGL:
|
||||
case Maxwell::Blend::Factor::DestColor_D3D:
|
||||
case Maxwell::Blend::Factor::DestColor_GL:
|
||||
return 8;
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor_GL:
|
||||
return 9;
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturateGL:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate_GL:
|
||||
return 10;
|
||||
case Maxwell::Blend::Factor::Source1Color:
|
||||
case Maxwell::Blend::Factor::Source1ColorGL:
|
||||
case Maxwell::Blend::Factor::Source1Color_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Color_GL:
|
||||
return 11;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1ColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
|
||||
return 12;
|
||||
case Maxwell::Blend::Factor::Source1Alpha:
|
||||
case Maxwell::Blend::Factor::Source1AlphaGL:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_GL:
|
||||
return 13;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1AlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
|
||||
return 14;
|
||||
case Maxwell::Blend::Factor::ConstantColor:
|
||||
case Maxwell::Blend::Factor::ConstantColorGL:
|
||||
case Maxwell::Blend::Factor::BlendFactor_D3D:
|
||||
case Maxwell::Blend::Factor::ConstantColor_GL:
|
||||
return 15;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColor:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColor_GL:
|
||||
return 16;
|
||||
case Maxwell::Blend::Factor::ConstantAlpha:
|
||||
case Maxwell::Blend::Factor::ConstantAlphaGL:
|
||||
case Maxwell::Blend::Factor::BothSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::ConstantAlpha_GL:
|
||||
return 17;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL:
|
||||
return 18;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unknown blend factor {}", static_cast<u32>(factor));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Maxwell::Blend::Factor FixedPipelineState::UnpackBlendFactor(u32 packed) noexcept {
|
||||
static constexpr std::array LUT = {
|
||||
Maxwell::Blend::Factor::Zero,
|
||||
Maxwell::Blend::Factor::One,
|
||||
Maxwell::Blend::Factor::SourceColor,
|
||||
Maxwell::Blend::Factor::OneMinusSourceColor,
|
||||
Maxwell::Blend::Factor::SourceAlpha,
|
||||
Maxwell::Blend::Factor::OneMinusSourceAlpha,
|
||||
Maxwell::Blend::Factor::DestAlpha,
|
||||
Maxwell::Blend::Factor::OneMinusDestAlpha,
|
||||
Maxwell::Blend::Factor::DestColor,
|
||||
Maxwell::Blend::Factor::OneMinusDestColor,
|
||||
Maxwell::Blend::Factor::SourceAlphaSaturate,
|
||||
Maxwell::Blend::Factor::Source1Color,
|
||||
Maxwell::Blend::Factor::OneMinusSource1Color,
|
||||
Maxwell::Blend::Factor::Source1Alpha,
|
||||
Maxwell::Blend::Factor::OneMinusSource1Alpha,
|
||||
Maxwell::Blend::Factor::ConstantColor,
|
||||
Maxwell::Blend::Factor::OneMinusConstantColor,
|
||||
Maxwell::Blend::Factor::ConstantAlpha,
|
||||
Maxwell::Blend::Factor::OneMinusConstantAlpha,
|
||||
Maxwell::Blend::Factor::Zero_D3D,
|
||||
Maxwell::Blend::Factor::One_D3D,
|
||||
Maxwell::Blend::Factor::SourceColor_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusSourceColor_D3D,
|
||||
Maxwell::Blend::Factor::SourceAlpha_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D,
|
||||
Maxwell::Blend::Factor::DestAlpha_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusDestAlpha_D3D,
|
||||
Maxwell::Blend::Factor::DestColor_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusDestColor_D3D,
|
||||
Maxwell::Blend::Factor::SourceAlphaSaturate_D3D,
|
||||
Maxwell::Blend::Factor::Source1Color_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusSource1Color_D3D,
|
||||
Maxwell::Blend::Factor::Source1Alpha_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D,
|
||||
Maxwell::Blend::Factor::BlendFactor_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusBlendFactor_D3D,
|
||||
Maxwell::Blend::Factor::BothSourceAlpha_D3D,
|
||||
Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D,
|
||||
};
|
||||
ASSERT(packed < LUT.size());
|
||||
return LUT[packed];
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ struct FixedPipelineState {
|
||||
static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
|
||||
static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept;
|
||||
|
||||
static u32 PackStencilOp(Maxwell::StencilOp op) noexcept;
|
||||
static Maxwell::StencilOp UnpackStencilOp(u32 packed) noexcept;
|
||||
static u32 PackStencilOp(Maxwell::StencilOp::Op op) noexcept;
|
||||
static Maxwell::StencilOp::Op UnpackStencilOp(u32 packed) noexcept;
|
||||
|
||||
static u32 PackCullFace(Maxwell::CullFace cull) noexcept;
|
||||
static Maxwell::CullFace UnpackCullFace(u32 packed) noexcept;
|
||||
@@ -33,8 +33,8 @@ struct FixedPipelineState {
|
||||
static u32 PackPolygonMode(Maxwell::PolygonMode mode) noexcept;
|
||||
static Maxwell::PolygonMode UnpackPolygonMode(u32 packed) noexcept;
|
||||
|
||||
static u32 PackLogicOp(Maxwell::LogicOperation op) noexcept;
|
||||
static Maxwell::LogicOperation UnpackLogicOp(u32 packed) noexcept;
|
||||
static u32 PackLogicOp(Maxwell::LogicOp::Op op) noexcept;
|
||||
static Maxwell::LogicOp::Op UnpackLogicOp(u32 packed) noexcept;
|
||||
|
||||
static u32 PackBlendEquation(Maxwell::Blend::Equation equation) noexcept;
|
||||
static Maxwell::Blend::Equation UnpackBlendEquation(u32 packed) noexcept;
|
||||
@@ -113,15 +113,15 @@ struct FixedPipelineState {
|
||||
BitField<Position + 6, 3, u32> action_depth_pass;
|
||||
BitField<Position + 9, 3, u32> test_func;
|
||||
|
||||
Maxwell::StencilOp ActionStencilFail() const noexcept {
|
||||
Maxwell::StencilOp::Op ActionStencilFail() const noexcept {
|
||||
return UnpackStencilOp(action_stencil_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthFail() const noexcept {
|
||||
Maxwell::StencilOp::Op ActionDepthFail() const noexcept {
|
||||
return UnpackStencilOp(action_depth_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthPass() const noexcept {
|
||||
Maxwell::StencilOp::Op ActionDepthPass() const noexcept {
|
||||
return UnpackStencilOp(action_depth_pass);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,6 @@ struct FixedPipelineState {
|
||||
BitField<6, 5, u32> depth_format;
|
||||
BitField<11, 1, u32> y_negate;
|
||||
BitField<12, 1, u32> provoking_vertex_last;
|
||||
BitField<13, 1, u32> conservative_raster_enable;
|
||||
BitField<14, 1, u32> smooth_lines;
|
||||
};
|
||||
std::array<u8, Maxwell::NumRenderTargets> color_formats;
|
||||
|
||||
@@ -323,161 +323,182 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
|
||||
Maxwell::VertexAttribute::Size size) {
|
||||
const VkFormat format{([&]() {
|
||||
switch (type) {
|
||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
|
||||
ASSERT_MSG(false, "Invalid vertex attribute type!");
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UNorm:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
return VK_FORMAT_R8G8_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
return VK_FORMAT_R8G8B8_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
return VK_FORMAT_R16_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
return VK_FORMAT_R16G16_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
return VK_FORMAT_R16G16B16_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return VK_FORMAT_R16G16B16A16_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::SNorm:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
return VK_FORMAT_R8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
return VK_FORMAT_R8G8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
return VK_FORMAT_R8G8B8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
return VK_FORMAT_R16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
return VK_FORMAT_R16G16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
return VK_FORMAT_R16G16B16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return VK_FORMAT_R16G16B16A16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::UScaled:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
return VK_FORMAT_R8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
return VK_FORMAT_R8G8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
return VK_FORMAT_R8G8B8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return VK_FORMAT_R8G8B8A8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
return VK_FORMAT_R16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
return VK_FORMAT_R16G16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
return VK_FORMAT_R16G16B16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return VK_FORMAT_R16G16B16A16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::SScaled:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
return VK_FORMAT_R8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
return VK_FORMAT_R8G8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
return VK_FORMAT_R8G8B8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return VK_FORMAT_R8G8B8A8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
return VK_FORMAT_R16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
return VK_FORMAT_R16G16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
return VK_FORMAT_R16G16B16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return VK_FORMAT_R16G16B16A16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||
case Maxwell::VertexAttribute::Type::UInt:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
return VK_FORMAT_R8_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
return VK_FORMAT_R8G8_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
return VK_FORMAT_R8G8B8_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return VK_FORMAT_R8G8B8A8_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
return VK_FORMAT_R16_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
return VK_FORMAT_R16G16_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
return VK_FORMAT_R16G16B16_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return VK_FORMAT_R16G16B16A16_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32:
|
||||
return VK_FORMAT_R32_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32:
|
||||
return VK_FORMAT_R32G32_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
|
||||
return VK_FORMAT_R32G32B32_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return VK_FORMAT_A2B10G10R10_UINT_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
case Maxwell::VertexAttribute::Type::SInt:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8:
|
||||
case Maxwell::VertexAttribute::Size::Size_A8:
|
||||
return VK_FORMAT_R8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8:
|
||||
case Maxwell::VertexAttribute::Size::Size_G8_R8:
|
||||
return VK_FORMAT_R8G8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
|
||||
return VK_FORMAT_R8G8B8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
|
||||
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
|
||||
return VK_FORMAT_R8G8B8A8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
return VK_FORMAT_R16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
return VK_FORMAT_R16G16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
return VK_FORMAT_R16G16B16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return VK_FORMAT_R16G16B16A16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
|
||||
return VK_FORMAT_R32G32B32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
|
||||
return VK_FORMAT_A2B10G10R10_SINT_PACK32;
|
||||
default:
|
||||
break;
|
||||
@@ -485,23 +506,23 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16:
|
||||
return VK_FORMAT_R16_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16:
|
||||
return VK_FORMAT_R16G16_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
|
||||
return VK_FORMAT_R16G16B16_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
|
||||
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32:
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32:
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_11_11_10:
|
||||
case Maxwell::VertexAttribute::Size::Size_B10_G11_R11:
|
||||
return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
|
||||
default:
|
||||
break;
|
||||
@@ -521,29 +542,29 @@ VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
|
||||
|
||||
VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) {
|
||||
switch (comparison) {
|
||||
case Maxwell::ComparisonOp::Never:
|
||||
case Maxwell::ComparisonOp::NeverOld:
|
||||
case Maxwell::ComparisonOp::Never_D3D:
|
||||
case Maxwell::ComparisonOp::Never_GL:
|
||||
return VK_COMPARE_OP_NEVER;
|
||||
case Maxwell::ComparisonOp::Less:
|
||||
case Maxwell::ComparisonOp::LessOld:
|
||||
case Maxwell::ComparisonOp::Less_D3D:
|
||||
case Maxwell::ComparisonOp::Less_GL:
|
||||
return VK_COMPARE_OP_LESS;
|
||||
case Maxwell::ComparisonOp::Equal:
|
||||
case Maxwell::ComparisonOp::EqualOld:
|
||||
case Maxwell::ComparisonOp::Equal_D3D:
|
||||
case Maxwell::ComparisonOp::Equal_GL:
|
||||
return VK_COMPARE_OP_EQUAL;
|
||||
case Maxwell::ComparisonOp::LessEqual:
|
||||
case Maxwell::ComparisonOp::LessEqualOld:
|
||||
case Maxwell::ComparisonOp::LessEqual_D3D:
|
||||
case Maxwell::ComparisonOp::LessEqual_GL:
|
||||
return VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
case Maxwell::ComparisonOp::Greater:
|
||||
case Maxwell::ComparisonOp::GreaterOld:
|
||||
case Maxwell::ComparisonOp::Greater_D3D:
|
||||
case Maxwell::ComparisonOp::Greater_GL:
|
||||
return VK_COMPARE_OP_GREATER;
|
||||
case Maxwell::ComparisonOp::NotEqual:
|
||||
case Maxwell::ComparisonOp::NotEqualOld:
|
||||
case Maxwell::ComparisonOp::NotEqual_D3D:
|
||||
case Maxwell::ComparisonOp::NotEqual_GL:
|
||||
return VK_COMPARE_OP_NOT_EQUAL;
|
||||
case Maxwell::ComparisonOp::GreaterEqual:
|
||||
case Maxwell::ComparisonOp::GreaterEqualOld:
|
||||
case Maxwell::ComparisonOp::GreaterEqual_D3D:
|
||||
case Maxwell::ComparisonOp::GreaterEqual_GL:
|
||||
return VK_COMPARE_OP_GREATER_OR_EQUAL;
|
||||
case Maxwell::ComparisonOp::Always:
|
||||
case Maxwell::ComparisonOp::AlwaysOld:
|
||||
case Maxwell::ComparisonOp::Always_D3D:
|
||||
case Maxwell::ComparisonOp::Always_GL:
|
||||
return VK_COMPARE_OP_ALWAYS;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
|
||||
@@ -563,31 +584,31 @@ VkIndexType IndexFormat(Maxwell::IndexFormat index_format) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) {
|
||||
VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op) {
|
||||
switch (stencil_op) {
|
||||
case Maxwell::StencilOp::Keep:
|
||||
case Maxwell::StencilOp::KeepOGL:
|
||||
case Maxwell::StencilOp::Op::Keep_D3D:
|
||||
case Maxwell::StencilOp::Op::Keep_GL:
|
||||
return VK_STENCIL_OP_KEEP;
|
||||
case Maxwell::StencilOp::Zero:
|
||||
case Maxwell::StencilOp::ZeroOGL:
|
||||
case Maxwell::StencilOp::Op::Zero_D3D:
|
||||
case Maxwell::StencilOp::Op::Zero_GL:
|
||||
return VK_STENCIL_OP_ZERO;
|
||||
case Maxwell::StencilOp::Replace:
|
||||
case Maxwell::StencilOp::ReplaceOGL:
|
||||
case Maxwell::StencilOp::Op::Replace_D3D:
|
||||
case Maxwell::StencilOp::Op::Replace_GL:
|
||||
return VK_STENCIL_OP_REPLACE;
|
||||
case Maxwell::StencilOp::Incr:
|
||||
case Maxwell::StencilOp::IncrOGL:
|
||||
case Maxwell::StencilOp::Op::IncrSaturate_D3D:
|
||||
case Maxwell::StencilOp::Op::IncrSaturate_GL:
|
||||
return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
|
||||
case Maxwell::StencilOp::Decr:
|
||||
case Maxwell::StencilOp::DecrOGL:
|
||||
case Maxwell::StencilOp::Op::DecrSaturate_D3D:
|
||||
case Maxwell::StencilOp::Op::DecrSaturate_GL:
|
||||
return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
|
||||
case Maxwell::StencilOp::Invert:
|
||||
case Maxwell::StencilOp::InvertOGL:
|
||||
case Maxwell::StencilOp::Op::Invert_D3D:
|
||||
case Maxwell::StencilOp::Op::Invert_GL:
|
||||
return VK_STENCIL_OP_INVERT;
|
||||
case Maxwell::StencilOp::IncrWrap:
|
||||
case Maxwell::StencilOp::IncrWrapOGL:
|
||||
case Maxwell::StencilOp::Op::Incr_D3D:
|
||||
case Maxwell::StencilOp::Op::Incr_GL:
|
||||
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
||||
case Maxwell::StencilOp::DecrWrap:
|
||||
case Maxwell::StencilOp::DecrWrapOGL:
|
||||
case Maxwell::StencilOp::Op::Decr_D3D:
|
||||
case Maxwell::StencilOp::Op::Decr_GL:
|
||||
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented stencil op={}", stencil_op);
|
||||
@@ -596,20 +617,20 @@ VkStencilOp StencilOp(Maxwell::StencilOp stencil_op) {
|
||||
|
||||
VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
switch (equation) {
|
||||
case Maxwell::Blend::Equation::Add:
|
||||
case Maxwell::Blend::Equation::AddGL:
|
||||
case Maxwell::Blend::Equation::Add_D3D:
|
||||
case Maxwell::Blend::Equation::Add_GL:
|
||||
return VK_BLEND_OP_ADD;
|
||||
case Maxwell::Blend::Equation::Subtract:
|
||||
case Maxwell::Blend::Equation::SubtractGL:
|
||||
case Maxwell::Blend::Equation::Subtract_D3D:
|
||||
case Maxwell::Blend::Equation::Subtract_GL:
|
||||
return VK_BLEND_OP_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::ReverseSubtract:
|
||||
case Maxwell::Blend::Equation::ReverseSubtractGL:
|
||||
case Maxwell::Blend::Equation::ReverseSubtract_D3D:
|
||||
case Maxwell::Blend::Equation::ReverseSubtract_GL:
|
||||
return VK_BLEND_OP_REVERSE_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::Min:
|
||||
case Maxwell::Blend::Equation::MinGL:
|
||||
case Maxwell::Blend::Equation::Min_D3D:
|
||||
case Maxwell::Blend::Equation::Min_GL:
|
||||
return VK_BLEND_OP_MIN;
|
||||
case Maxwell::Blend::Equation::Max:
|
||||
case Maxwell::Blend::Equation::MaxGL:
|
||||
case Maxwell::Blend::Equation::Max_D3D:
|
||||
case Maxwell::Blend::Equation::Max_GL:
|
||||
return VK_BLEND_OP_MAX;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented blend equation={}", equation);
|
||||
@@ -618,62 +639,62 @@ VkBlendOp BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
|
||||
VkBlendFactor BlendFactor(Maxwell::Blend::Factor factor) {
|
||||
switch (factor) {
|
||||
case Maxwell::Blend::Factor::Zero:
|
||||
case Maxwell::Blend::Factor::ZeroGL:
|
||||
case Maxwell::Blend::Factor::Zero_D3D:
|
||||
case Maxwell::Blend::Factor::Zero_GL:
|
||||
return VK_BLEND_FACTOR_ZERO;
|
||||
case Maxwell::Blend::Factor::One:
|
||||
case Maxwell::Blend::Factor::OneGL:
|
||||
case Maxwell::Blend::Factor::One_D3D:
|
||||
case Maxwell::Blend::Factor::One_GL:
|
||||
return VK_BLEND_FACTOR_ONE;
|
||||
case Maxwell::Blend::Factor::SourceColor:
|
||||
case Maxwell::Blend::Factor::SourceColorGL:
|
||||
case Maxwell::Blend::Factor::SourceColor_D3D:
|
||||
case Maxwell::Blend::Factor::SourceColor_GL:
|
||||
return VK_BLEND_FACTOR_SRC_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
|
||||
case Maxwell::Blend::Factor::SourceAlpha:
|
||||
case Maxwell::Blend::Factor::SourceAlphaGL:
|
||||
case Maxwell::Blend::Factor::SourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::SourceAlpha_GL:
|
||||
return VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
case Maxwell::Blend::Factor::DestAlpha:
|
||||
case Maxwell::Blend::Factor::DestAlphaGL:
|
||||
case Maxwell::Blend::Factor::DestAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::DestAlpha_GL:
|
||||
return VK_BLEND_FACTOR_DST_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
case Maxwell::Blend::Factor::DestColor:
|
||||
case Maxwell::Blend::Factor::DestColorGL:
|
||||
case Maxwell::Blend::Factor::DestColor_D3D:
|
||||
case Maxwell::Blend::Factor::DestColor_GL:
|
||||
return VK_BLEND_FACTOR_DST_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturateGL:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate_D3D:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate_GL:
|
||||
return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
|
||||
case Maxwell::Blend::Factor::Source1Color:
|
||||
case Maxwell::Blend::Factor::Source1ColorGL:
|
||||
case Maxwell::Blend::Factor::Source1Color_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Color_GL:
|
||||
return VK_BLEND_FACTOR_SRC1_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1ColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
|
||||
case Maxwell::Blend::Factor::Source1Alpha:
|
||||
case Maxwell::Blend::Factor::Source1AlphaGL:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_GL:
|
||||
return VK_BLEND_FACTOR_SRC1_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1AlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
|
||||
case Maxwell::Blend::Factor::ConstantColor:
|
||||
case Maxwell::Blend::Factor::ConstantColorGL:
|
||||
case Maxwell::Blend::Factor::BlendFactor_D3D:
|
||||
case Maxwell::Blend::Factor::ConstantColor_GL:
|
||||
return VK_BLEND_FACTOR_CONSTANT_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColor:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColorGL:
|
||||
case Maxwell::Blend::Factor::OneMinusBlendFactor_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColor_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
|
||||
case Maxwell::Blend::Factor::ConstantAlpha:
|
||||
case Maxwell::Blend::Factor::ConstantAlphaGL:
|
||||
case Maxwell::Blend::Factor::BothSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::ConstantAlpha_GL:
|
||||
return VK_BLEND_FACTOR_CONSTANT_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
|
||||
case Maxwell::Blend::Factor::OneMinusBothSourceAlpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlpha_GL:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented blend factor={}", factor);
|
||||
|
||||
@@ -55,7 +55,7 @@ VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison);
|
||||
|
||||
VkIndexType IndexFormat(Maxwell::IndexFormat index_format);
|
||||
|
||||
VkStencilOp StencilOp(Maxwell::StencilOp stencil_op);
|
||||
VkStencilOp StencilOp(Maxwell::StencilOp::Op stencil_op);
|
||||
|
||||
VkBlendOp BlendEquation(Maxwell::Blend::Equation equation);
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes);
|
||||
|
||||
const auto& regs{maxwell3d->regs};
|
||||
const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex};
|
||||
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
|
||||
const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
|
||||
const Shader::Info& info{stage_infos[stage]};
|
||||
buffer_cache.UnbindGraphicsStorageBuffers(stage);
|
||||
@@ -664,15 +664,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.lineStippleFactor = 0,
|
||||
.lineStipplePattern = 0,
|
||||
};
|
||||
VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.conservativeRasterizationMode = key.state.conservative_raster_enable != 0
|
||||
? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
|
||||
: VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT,
|
||||
.extraPrimitiveOverestimationSize = 0.0f,
|
||||
};
|
||||
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
@@ -683,9 +674,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) {
|
||||
line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state);
|
||||
}
|
||||
if (device.IsExtConservativeRasterizationSupported()) {
|
||||
conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster);
|
||||
}
|
||||
if (device.IsExtProvokingVertexSupported()) {
|
||||
provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex);
|
||||
}
|
||||
|
||||
@@ -62,29 +62,29 @@ auto MakeSpan(Container& container) {
|
||||
|
||||
Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) {
|
||||
switch (comparison) {
|
||||
case Maxwell::ComparisonOp::Never:
|
||||
case Maxwell::ComparisonOp::NeverOld:
|
||||
case Maxwell::ComparisonOp::Never_D3D:
|
||||
case Maxwell::ComparisonOp::Never_GL:
|
||||
return Shader::CompareFunction::Never;
|
||||
case Maxwell::ComparisonOp::Less:
|
||||
case Maxwell::ComparisonOp::LessOld:
|
||||
case Maxwell::ComparisonOp::Less_D3D:
|
||||
case Maxwell::ComparisonOp::Less_GL:
|
||||
return Shader::CompareFunction::Less;
|
||||
case Maxwell::ComparisonOp::Equal:
|
||||
case Maxwell::ComparisonOp::EqualOld:
|
||||
case Maxwell::ComparisonOp::Equal_D3D:
|
||||
case Maxwell::ComparisonOp::Equal_GL:
|
||||
return Shader::CompareFunction::Equal;
|
||||
case Maxwell::ComparisonOp::LessEqual:
|
||||
case Maxwell::ComparisonOp::LessEqualOld:
|
||||
case Maxwell::ComparisonOp::LessEqual_D3D:
|
||||
case Maxwell::ComparisonOp::LessEqual_GL:
|
||||
return Shader::CompareFunction::LessThanEqual;
|
||||
case Maxwell::ComparisonOp::Greater:
|
||||
case Maxwell::ComparisonOp::GreaterOld:
|
||||
case Maxwell::ComparisonOp::Greater_D3D:
|
||||
case Maxwell::ComparisonOp::Greater_GL:
|
||||
return Shader::CompareFunction::Greater;
|
||||
case Maxwell::ComparisonOp::NotEqual:
|
||||
case Maxwell::ComparisonOp::NotEqualOld:
|
||||
case Maxwell::ComparisonOp::NotEqual_D3D:
|
||||
case Maxwell::ComparisonOp::NotEqual_GL:
|
||||
return Shader::CompareFunction::NotEqual;
|
||||
case Maxwell::ComparisonOp::GreaterEqual:
|
||||
case Maxwell::ComparisonOp::GreaterEqualOld:
|
||||
case Maxwell::ComparisonOp::GreaterEqual_D3D:
|
||||
case Maxwell::ComparisonOp::GreaterEqual_GL:
|
||||
return Shader::CompareFunction::GreaterThanEqual;
|
||||
case Maxwell::ComparisonOp::Always:
|
||||
case Maxwell::ComparisonOp::AlwaysOld:
|
||||
case Maxwell::ComparisonOp::Always_D3D:
|
||||
case Maxwell::ComparisonOp::Always_GL:
|
||||
return Shader::CompareFunction::Always;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
|
||||
@@ -96,15 +96,18 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut
|
||||
return Shader::AttributeType::Disabled;
|
||||
}
|
||||
switch (attr.Type()) {
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
|
||||
ASSERT_MSG(false, "Invalid vertex attribute type!");
|
||||
return Shader::AttributeType::Disabled;
|
||||
case Maxwell::VertexAttribute::Type::SNorm:
|
||||
case Maxwell::VertexAttribute::Type::UNorm:
|
||||
case Maxwell::VertexAttribute::Type::UScaled:
|
||||
case Maxwell::VertexAttribute::Type::SScaled:
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
return Shader::AttributeType::Float;
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
case Maxwell::VertexAttribute::Type::SInt:
|
||||
return Shader::AttributeType::SignedInt;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||
case Maxwell::VertexAttribute::Type::UInt:
|
||||
return Shader::AttributeType::UnsignedInt;
|
||||
}
|
||||
return Shader::AttributeType::Float;
|
||||
@@ -162,16 +165,14 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
}
|
||||
break;
|
||||
case Shader::Stage::TessellationEval:
|
||||
// We have to flip tessellation clockwise for some reason...
|
||||
info.tess_clockwise = key.state.tessellation_clockwise == 0;
|
||||
info.tess_primitive = [&key] {
|
||||
const u32 raw{key.state.tessellation_primitive.Value()};
|
||||
switch (static_cast<Maxwell::TessellationPrimitive>(raw)) {
|
||||
case Maxwell::TessellationPrimitive::Isolines:
|
||||
switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) {
|
||||
case Maxwell::Tessellation::DomainType::Isolines:
|
||||
return Shader::TessPrimitive::Isolines;
|
||||
case Maxwell::TessellationPrimitive::Triangles:
|
||||
case Maxwell::Tessellation::DomainType::Triangles:
|
||||
return Shader::TessPrimitive::Triangles;
|
||||
case Maxwell::TessellationPrimitive::Quads:
|
||||
case Maxwell::Tessellation::DomainType::Quads:
|
||||
return Shader::TessPrimitive::Quads;
|
||||
}
|
||||
ASSERT(false);
|
||||
@@ -179,12 +180,12 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
}();
|
||||
info.tess_spacing = [&] {
|
||||
const u32 raw{key.state.tessellation_spacing};
|
||||
switch (static_cast<Maxwell::TessellationSpacing>(raw)) {
|
||||
case Maxwell::TessellationSpacing::Equal:
|
||||
switch (static_cast<Maxwell::Tessellation::Spacing>(raw)) {
|
||||
case Maxwell::Tessellation::Spacing::Integer:
|
||||
return Shader::TessSpacing::Equal;
|
||||
case Maxwell::TessellationSpacing::FractionalOdd:
|
||||
case Maxwell::Tessellation::Spacing::FractionalOdd:
|
||||
return Shader::TessSpacing::FractionalOdd;
|
||||
case Maxwell::TessellationSpacing::FractionalEven:
|
||||
case Maxwell::Tessellation::Spacing::FractionalEven:
|
||||
return Shader::TessSpacing::FractionalEven;
|
||||
}
|
||||
ASSERT(false);
|
||||
@@ -490,7 +491,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
|
||||
// If games are using a small index count, we can assume these are full screen quads.
|
||||
// Usually these shaders are only used once for building textures so we can assume they
|
||||
// can't be built async
|
||||
if (maxwell3d->regs.index_array.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
|
||||
if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
|
||||
return pipeline;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -70,7 +70,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
|
||||
const float width = conv(src.scale_x * 2.0f);
|
||||
float y = conv(src.translate_y - src.scale_y);
|
||||
float height = conv(src.scale_y * 2.0f);
|
||||
bool y_negate = regs.screen_y_control.y_negate;
|
||||
bool y_negate = regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft;
|
||||
|
||||
if (!device.IsNvViewportSwizzleSupported()) {
|
||||
y_negate = y_negate != (src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY);
|
||||
@@ -130,11 +130,11 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
|
||||
DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced,
|
||||
bool is_indexed) {
|
||||
DrawParams params{
|
||||
.base_instance = regs.vb_base_instance,
|
||||
.base_instance = regs.global_base_instance_index,
|
||||
.num_instances = is_instanced ? num_instances : 1,
|
||||
.base_vertex = is_indexed ? regs.vb_element_base : regs.vertex_buffer.first,
|
||||
.num_vertices = is_indexed ? regs.index_array.count : regs.vertex_buffer.count,
|
||||
.first_index = is_indexed ? regs.index_array.first : 0,
|
||||
.base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first,
|
||||
.num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count,
|
||||
.first_index = is_indexed ? regs.index_buffer.first : 0,
|
||||
.is_indexed = is_indexed,
|
||||
};
|
||||
if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) {
|
||||
@@ -225,10 +225,10 @@ void RasterizerVulkan::Clear() {
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
auto& regs = maxwell3d->regs;
|
||||
const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
|
||||
regs.clear_buffers.A;
|
||||
const bool use_depth = regs.clear_buffers.Z;
|
||||
const bool use_stencil = regs.clear_buffers.S;
|
||||
const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B ||
|
||||
regs.clear_surface.A;
|
||||
const bool use_depth = regs.clear_surface.Z;
|
||||
const bool use_stencil = regs.clear_surface.S;
|
||||
if (!use_color && !use_depth && !use_stencil) {
|
||||
return;
|
||||
}
|
||||
@@ -254,9 +254,9 @@ void RasterizerVulkan::Clear() {
|
||||
default_scissor.extent.height = std::numeric_limits<s32>::max();
|
||||
|
||||
VkClearRect clear_rect{
|
||||
.rect = regs.clear_flags.scissor ? GetScissorState(regs, 0, up_scale, down_shift)
|
||||
: default_scissor,
|
||||
.baseArrayLayer = regs.clear_buffers.layer,
|
||||
.rect = regs.clear_control.use_scissor ? GetScissorState(regs, 0, up_scale, down_shift)
|
||||
: default_scissor,
|
||||
.baseArrayLayer = regs.clear_surface.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) {
|
||||
@@ -267,7 +267,7 @@ void RasterizerVulkan::Clear() {
|
||||
.height = std::min(clear_rect.rect.extent.height, render_area.height),
|
||||
};
|
||||
|
||||
const u32 color_attachment = regs.clear_buffers.RT;
|
||||
const u32 color_attachment = regs.clear_surface.RT;
|
||||
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
|
||||
VkClearValue clear_value;
|
||||
bool is_integer = false;
|
||||
@@ -289,7 +289,8 @@ void RasterizerVulkan::Clear() {
|
||||
break;
|
||||
}
|
||||
if (!is_integer) {
|
||||
std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color));
|
||||
std::memcpy(clear_value.color.float32, regs.clear_color.data(),
|
||||
regs.clear_color.size() * sizeof(f32));
|
||||
} else if (!is_signed) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
clear_value.color.uint32[i] = static_cast<u32>(
|
||||
@@ -648,23 +649,23 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
|
||||
void RasterizerVulkan::BeginTransformFeedback() {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
if (regs.tfb_enabled == 0) {
|
||||
if (regs.transform_feedback_enabled == 0) {
|
||||
return;
|
||||
}
|
||||
if (!device.IsExtTransformFeedbackSupported()) {
|
||||
LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported");
|
||||
return;
|
||||
}
|
||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
|
||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
|
||||
scheduler.Record(
|
||||
[](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); });
|
||||
}
|
||||
|
||||
void RasterizerVulkan::EndTransformFeedback() {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
if (regs.tfb_enabled == 0) {
|
||||
if (regs.transform_feedback_enabled == 0) {
|
||||
return;
|
||||
}
|
||||
if (!device.IsExtTransformFeedbackSupported()) {
|
||||
@@ -728,11 +729,11 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBias()) {
|
||||
return;
|
||||
}
|
||||
float units = regs.polygon_offset_units / 2.0f;
|
||||
const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::S8_UINT_Z24_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::D24X8_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::D24S8_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::D24C8_UNORM;
|
||||
float units = regs.depth_bias / 2.0f;
|
||||
const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::Z24_UNORM_S8_UINT ||
|
||||
regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
|
||||
if (is_d24 && !device.SupportsD24DepthBuffer()) {
|
||||
// the base formulas can be obtained from here:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
|
||||
@@ -740,8 +741,8 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
|
||||
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
|
||||
}
|
||||
scheduler.Record([constant = units, clamp = regs.polygon_offset_clamp,
|
||||
factor = regs.polygon_offset_factor](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
|
||||
factor = regs.slope_scale_depth_bias](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthBias(constant, clamp, factor);
|
||||
});
|
||||
}
|
||||
@@ -771,8 +772,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
if (regs.stencil_two_side_enable) {
|
||||
// Separate values per face
|
||||
scheduler.Record(
|
||||
[front_ref = regs.stencil_front_func_ref, front_write_mask = regs.stencil_front_mask,
|
||||
front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_func_ref,
|
||||
[front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask,
|
||||
front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref,
|
||||
back_write_mask = regs.stencil_back_mask,
|
||||
back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) {
|
||||
// Front face
|
||||
@@ -787,7 +788,7 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
});
|
||||
} else {
|
||||
// Front face defines both faces
|
||||
scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask,
|
||||
scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask,
|
||||
test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
|
||||
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
|
||||
@@ -800,7 +801,8 @@ void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchLineWidth()) {
|
||||
return;
|
||||
}
|
||||
const float width = regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased;
|
||||
const float width =
|
||||
regs.line_anti_alias_enable ? regs.line_width_smooth : regs.line_width_aliased;
|
||||
scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); });
|
||||
}
|
||||
|
||||
@@ -808,10 +810,10 @@ void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchCullMode()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record(
|
||||
[enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
|
||||
});
|
||||
scheduler.Record([enabled = regs.gl_cull_test_enabled,
|
||||
cull_face = regs.gl_cull_face](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
@@ -860,8 +862,8 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
return;
|
||||
}
|
||||
|
||||
VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
VkFrontFace front_face = MaxwellToVK::FrontFace(regs.gl_front_face);
|
||||
if (regs.window_origin.flip_y != 0) {
|
||||
front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
|
||||
: VK_FRONT_FACE_CLOCKWISE;
|
||||
}
|
||||
@@ -873,16 +875,16 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchStencilOp()) {
|
||||
return;
|
||||
}
|
||||
const Maxwell::StencilOp fail = regs.stencil_front_op_fail;
|
||||
const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail;
|
||||
const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
|
||||
const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
|
||||
const Maxwell::StencilOp::Op fail = regs.stencil_front_op.fail;
|
||||
const Maxwell::StencilOp::Op zfail = regs.stencil_front_op.zfail;
|
||||
const Maxwell::StencilOp::Op zpass = regs.stencil_front_op.zpass;
|
||||
const Maxwell::ComparisonOp compare = regs.stencil_front_op.func;
|
||||
if (regs.stencil_two_side_enable) {
|
||||
// Separate stencil op per face
|
||||
const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
|
||||
const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
|
||||
const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
|
||||
const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func;
|
||||
const Maxwell::StencilOp::Op back_fail = regs.stencil_back_op.fail;
|
||||
const Maxwell::StencilOp::Op back_zfail = regs.stencil_back_op.zfail;
|
||||
const Maxwell::StencilOp::Op back_zpass = regs.stencil_back_op.zpass;
|
||||
const Maxwell::ComparisonOp back_compare = regs.stencil_back_op.func;
|
||||
scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
|
||||
back_compare](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
|
||||
@@ -954,15 +956,15 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
dirty[Dirty::VertexBinding0 + index] = false;
|
||||
|
||||
const u32 binding{static_cast<u32>(index)};
|
||||
const auto& input_binding{regs.vertex_array[binding]};
|
||||
const bool is_instanced{regs.instanced_arrays.IsInstancingEnabled(binding)};
|
||||
const auto& input_binding{regs.vertex_streams[binding]};
|
||||
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
|
||||
bindings.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
|
||||
.pNext = nullptr,
|
||||
.binding = binding,
|
||||
.stride = input_binding.stride,
|
||||
.inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
.divisor = is_instanced ? input_binding.divisor : 1,
|
||||
.divisor = is_instanced ? input_binding.frequency : 1,
|
||||
});
|
||||
}
|
||||
scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user