Compare commits

..

55 Commits

Author SHA1 Message Date
Morph
ddf5577799 video_core: Fix spelling of "synchronize" 2022-10-16 00:50:53 -04:00
Morph
f706b3bd24 general: Fix spelling of "unknown" 2022-10-16 00:46:22 -04:00
bunnei
d574bb4610 Merge pull request #9058 from Docteh/new_transifex_cli
New transifex client needs migrating to.
2022-10-15 21:39:03 -07:00
bunnei
b0ba1a0b65 Merge pull request #9076 from Docteh/unknown
fix a tiny spelling mistake
2022-10-15 21:37:47 -07:00
Kyle Kienapfel
0ba03d1b3a fix a tiny spelling mistake
Kreato pointed this out over on discord.
2022-10-15 14:58:44 -07:00
Kyle Kienapfel
fcebd36cde Translations: new transifex client
Currently we're using the python client which uses an API that they
state will sunset Nov 30, 2022.

`tx push -s` actually appears to work properly, some of the other
commands require tweaking, like instead of suggesting `tx pull -a` in
dist/languages we need to suggest `tx pull -t -a`
2022-10-14 23:12:40 -07:00
liamwhite
ae6dd1143c Merge pull request #9061 from liamwhite/writable-event
kernel: remove KWritableEvent
2022-10-14 17:30:38 -04:00
liamwhite
1d38109714 Merge pull request #9055 from liamwhite/hbl
Preliminary support for nx-hbloader
2022-10-14 17:30:11 -04:00
Morph
6a9bbb0128 Merge pull request #9069 from german77/sdl2
audio_core: Revert sink name to sdl2
2022-10-14 13:16:38 -04:00
Narr the Reg
d2170075e6 audio_core: Revert sink name to sdl2 2022-10-14 10:59:33 -05:00
liamwhite
553be194f6 Merge pull request #9067 from Morph1984/tess-cw
renderer_(opengl/vulkan): Fix tessellation clockwise parameter
2022-10-13 20:12:31 -04:00
liamwhite
048d3e2404 Merge pull request #9039 from Kelebek1/auto_backend
Auto select the SDL audio backend when Cubeb latency is too high
2022-10-13 20:12:22 -04:00
liamwhite
3c925a7282 Merge pull request #9032 from liamwhite/stub-friends
IFriendService: stub CheckFriendListAvailability
2022-10-13 20:12:08 -04:00
Mai
e37d00332c Merge pull request #9065 from liamwhite/result-mess
result: enforce reference check specialization
2022-10-13 19:54:10 +00:00
Morph
d3114c620d renderer_(opengl/vulkan): Fix tessellation clockwise parameter
This should be assigned CW only on Triangles_CW rather than not Triangles_CCW, making CCW the default winding order rather than CW.
2022-10-13 15:52:56 -04:00
Narr the Reg
26b76d2eaf Merge pull request #9066 from Morph1984/fix-stretch-to-window
settings: Update aspect_ratio range
2022-10-13 13:00:07 -05:00
Morph
e2164f3417 settings: Update aspect_ratio range
Since 16:10 was added, the maximum value is now 4.
2022-10-13 12:24:04 -04:00
Liam
c0fb5e876d result: enforce reference check specialization 2022-10-13 12:10:39 -04:00
Liam
a9ace6856d kernel: remove KWritableEvent 2022-10-12 20:29:29 -04:00
bunnei
64c2ccb0cb Merge pull request #9034 from liamwhite/result-macros
kernel: add expanded result macros
2022-10-12 17:11:07 -07:00
bunnei
dbacb31f61 Merge pull request #9027 from yuzu-emu/revert-8987-another-name-for-reinforcement-steel
Revert "vulkan: automatically use larger staging buffer sizes when possible"
2022-10-12 15:36:56 -07:00
bunnei
0b9f2c2f14 Merge pull request #9040 from liamwhite/woe-thirty-two
core_timing: use high-precision sleeps on non-Windows targets
2022-10-12 15:35:06 -07:00
bunnei
e158167139 Merge pull request #9024 from liamwhite/async-screenshot
video_core: don't block rendering on screenshots
2022-10-12 13:26:32 -07:00
bunnei
3da4280e81 Merge pull request #9047 from german77/steam-aspect
yuzu: Add 16:10 aspect ratio
2022-10-12 12:54:23 -07:00
bunnei
77177a7e33 Merge pull request #9049 from liamwhite/monkeyhawk
syncpoint_manager: ensure handle is removable before removing
2022-10-12 12:34:22 -07:00
Liam
61a8696510 k_server_session: preliminary support for userspace server sessions 2022-10-11 18:40:40 -04:00
Liam
9b34afa588 Add implementation of svcCreateSession 2022-10-11 18:15:45 -04:00
Liam
6bcd676b61 general: preliminary support for hbl 2022-10-11 18:15:30 -04:00
liamwhite
133a68ee9b Merge pull request #9048 from Kelebek1/regs
[video_core] Fix stencil mask registers
2022-10-11 17:22:40 -04:00
Liam
b1cd6cec19 syncpoint_manager: ensure handle is removable before removing 2022-10-10 19:22:26 -04:00
liamwhite
d9336860d7 Merge pull request #9044 from lat9nq/mingw-gcc-revert
ci/windows: Revert to using GCC for MinGW builds
2022-10-10 19:04:02 -04:00
Kelebek1
4496030ea9 Fix stencil func registers, make clip control equivalent to how it was before, but surely wrong. 2022-10-10 20:59:57 +01:00
Narr the Reg
eb74ef474b yuzu: Add 16:10 aspect ratio 2022-10-10 13:32:33 -05:00
lat9nq
682c50715c ci/windows: Revert to using GCC for MinGW builds
Using MinGW in the future may not be ideal as it does not work very well
with crash dumps (#8682).

Switch back to GCC on MinGW. This also gives CI a way to check GCC 12
(as of writing, or whatever version of mingw-gcc Arch happens to be
shipping on a given week).
2022-10-09 17:47:52 -04:00
liamwhite
c3cae9d992 Merge pull request #9043 from german77/vector_data
input_common: have an unique vector in callback status
2022-10-09 17:46:45 -04:00
german77
224a19758e input_common: have an unique vector in callback status 2022-10-09 12:49:18 -05:00
Kelebek1
8c9e238a7b Choose the SDL audio backend when Cubeb reports too high of a latency 2022-10-09 13:47:59 +01:00
Fernando S
55e6d0dae0 Merge pull request #8766 from Kelebek1/regs
[video_core] Update 3D registers
2022-10-09 07:04:03 +02:00
Liam
9632434243 core_timing: use high-precision sleeps on non-Windows targets 2022-10-08 18:27:40 -04:00
Mai
ec9550ced5 Merge pull request #9033 from liamwhite/stub-fsp
fsp_srv: stub GetCacheStorageSize
2022-10-08 16:33:13 -04:00
Liam
47a2efee73 kernel: add expanded result macros 2022-10-08 12:41:27 -04:00
Liam
5b7c0f13d3 fsp_srv: stub GetCacheStorageSize 2022-10-08 12:24:00 -04:00
Liam
ddf64e56af IFriendService: stub CheckFriendListAvailability 2022-10-07 22:19:41 -04:00
Mai
155213484b Merge pull request #9016 from liamwhite/drunken-schedule
vk_scheduler: wait for command processing to complete
2022-10-07 20:27:16 -04:00
Mai
b7ad83383f Merge pull request #8932 from abouvier/cmake-pkgconfig
cmake: Fix FindPkgConfig
2022-10-07 20:25:51 -04:00
Mai
6f101e0f02 Merge pull request #9030 from Morph1984/api-disable
configure_graphics: Fix graphics API selection when a game is running
2022-10-07 20:25:23 -04:00
liamwhite
972b93bf00 Merge pull request #8807 from Docteh/default_fonts
Qt: work around Qt5's font choice for Chinese (in Windows)
2022-10-07 17:39:39 -04:00
Morph
1e35ade1ec configure_graphics: Fix graphics API selection when a game is running
The graphics API setting should not be changed when a game is running.
2022-10-07 15:11:26 -04:00
Narr the Reg
b8777b6653 Merge pull request #9028 from liamwhite/wtype-limits
nfp_types: silence -Wtype-limits
2022-10-07 09:03:35 -05:00
Kelebek1
752659aef3 Update 3D regs 2022-10-07 14:13:45 +01:00
Liam
9574429c5f nfp_types: silence -Wtype-limits 2022-10-07 06:52:28 -04:00
liamwhite
20cf09471a Revert "vulkan: automatically use larger staging buffer sizes when possible" 2022-10-07 04:49:08 -04:00
Liam
7969d4d5de vk_scheduler: wait for command processing to complete 2022-10-03 20:03:25 -04:00
Kyle Kienapfel
1dba5fab62 Qt: work around Qt5's font choice for Chinese
On Windows there are currently two fonts used.

The first, does the Menu, QTreeView and Tooltips
Second is Everything else which is a default font.

From inspecting QApplication::font() at runtime
Windows 10 English: QFont(MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0)
Windows 11 Japanese:        MS UI Gothic,9   ,-1,5,50,0,0,0,0,0
Windows 11 Traditional Chinese: PMingLiU,9   ,-1,5,50,0,0,0,0,0
Windows 11 Simplified Chinese:    SimSun,9   ,-1,5,50,0,0,0,0,0
Windows 11 Korean:                 Gulim,9   ,-1,5,50,0,0,0,0,0

I initially investigated dynamically changing the font when
the UI language is English, but this was getting quite messy

Qt6 makes changes to default font in some situations, so this
PR is being narrowed in scope to only effect Chinese font choices.
This change only effects rendering of Latin/Cyrillic characters.
2022-10-01 15:27:23 -07:00
Alexandre Bouvier
09a87966e0 cmake: Fix FindPkgConfig 2022-09-20 22:21:52 +02:00
117 changed files with 5112 additions and 2529 deletions

View File

@@ -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

View File

@@ -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 \

View File

@@ -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)

View File

@@ -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

View File

@@ -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.

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -66,10 +66,10 @@ public:
const auto latency_error = cubeb_get_min_latency(ctx, &params, &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, &params, &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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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; });

View File

@@ -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.

View File

@@ -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

View File

@@ -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"};

View File

@@ -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

View File

@@ -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,

View File

@@ -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.

View File

@@ -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:

View File

@@ -27,6 +27,7 @@ enum class AspectRatio {
Default,
R4_3,
R21_9,
R16_10,
StretchToWindow,
};

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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());

View File

@@ -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;

View File

@@ -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> &&

View File

@@ -101,7 +101,6 @@ public:
KSession,
KSharedMemory,
KEvent,
KWritableEvent,
KLightClientSession,
KLightServerSession,
KTransferMemory,

View File

@@ -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

View File

@@ -46,8 +46,7 @@ public:
return parent;
}
Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing);
Result SendSyncRequest();
void OnServerClosed();

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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"},

View File

@@ -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, &param_1, &param_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, &param_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) {

View File

@@ -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)

View File

@@ -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

View File

@@ -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_,

View File

@@ -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:

View File

@@ -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);

View File

@@ -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)) {}

View File

@@ -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_)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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

View File

@@ -165,7 +165,7 @@ public:
}
void OnEventFired() {
state_change_event->GetWritableEvent().Signal();
state_change_event->Signal();
}
void GetState(Kernel::HLERequestContext& ctx) {

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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[] = {

View File

@@ -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> {

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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> {

View File

@@ -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;
}

View File

@@ -24,7 +24,6 @@ namespace Kernel {
class KernelCore;
class KEvent;
class KReadableEvent;
class KWritableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {

View File

@@ -25,7 +25,6 @@ struct EventType;
namespace Kernel {
class KReadableEvent;
class KWritableEvent;
} // namespace Kernel
namespace Service::Nvidia {

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -20,6 +20,7 @@ private:
};
void GetTemperature(Kernel::HLERequestContext& ctx);
void GetTemperatureMilliC(Kernel::HLERequestContext& ctx);
};
} // namespace Service::PTM

View File

@@ -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"},

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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];
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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