Compare commits
6 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ebc27234d | ||
|
|
bbb396d7f1 | ||
|
|
9ff72ca9f2 | ||
|
|
5f2d9f282a | ||
|
|
f2a2f818b6 | ||
|
|
c6a0ab9792 |
21
.github/ISSUE_TEMPLATE.md
vendored
21
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,27 +1,16 @@
|
||||
<!--
|
||||
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
|
||||
|
||||
Please read the FAQ:
|
||||
https://yuzu-emu.org/wiki/faq/
|
||||
Please read the FAQ: https://yuzu-emu.org/wiki/faq/
|
||||
|
||||
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
||||
https://community.citra-emu.org/
|
||||
When submitting an issue, please do the following:
|
||||
|
||||
If the FAQ does not answer your question, please go to:
|
||||
https://community.citra-emu.org/
|
||||
|
||||
When submitting an issue, please check the following:
|
||||
|
||||
- You have read the above.
|
||||
- You have provided the version (commit hash) of yuzu you are using.
|
||||
- You have provided sufficient detail for the issue to be reproduced.
|
||||
- You have provided system specs (if relevant).
|
||||
- Please also provide:
|
||||
- For any issues, a log file
|
||||
- Provide the version (commit hash) of yuzu you are using.
|
||||
- Provide sufficient detail for the issue to be reproduced.
|
||||
- Provide:
|
||||
- For crashes, a backtrace.
|
||||
- For graphical issues, comparison screenshots with real hardware.
|
||||
- For emulation inaccuracies, a test-case (if able).
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ TRAVIS_BRANCH
|
||||
TRAVIS_BUILD_ID
|
||||
TRAVIS_BUILD_NUMBER
|
||||
TRAVIS_COMMIT
|
||||
TRAVIS_COMMIT_RANGE
|
||||
TRAVIS_EVENT_TYPE
|
||||
TRAVIS_JOB_ID
|
||||
TRAVIS_JOB_NUMBER
|
||||
TRAVIS_REPO_SLUG
|
||||
|
||||
@@ -419,6 +419,19 @@ function(create_target_directory_groups target_name)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Gets a UTC timstamp and sets the provided variable to it
|
||||
function(get_timestamp _var)
|
||||
string(TIMESTAMP timestamp UTC)
|
||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# generate git/build information
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(externals)
|
||||
add_subdirectory(src)
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
# Gets a UTC timstamp and sets the provided variable to it
|
||||
function(get_timestamp _var)
|
||||
string(TIMESTAMP timestamp UTC)
|
||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules")
|
||||
# generate git/build information
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
# Generate cpp with Git revision from template
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
|
||||
set(REPO_NAME "")
|
||||
set(BUILD_VERSION "0")
|
||||
if (BUILD_REPOSITORY)
|
||||
# regex capture the string nightly or canary into CMAKE_MATCH_1
|
||||
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
# capitalize the first letter of each word in the repo name.
|
||||
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
|
||||
foreach(WORD ${REPO_NAME_LIST})
|
||||
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
|
||||
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
|
||||
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
|
||||
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
|
||||
endforeach()
|
||||
if (BUILD_TAG)
|
||||
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
set(BUILD_VERSION ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
if (BUILD_VERSION)
|
||||
# This leaves a trailing space on the last word, but we actually want that
|
||||
# because of how it's styled in the title bar.
|
||||
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
|
||||
else()
|
||||
set(BUILD_FULLNAME "")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
|
||||
set(VIDEO_CORE "${SRC_DIR}/src/video_core")
|
||||
set(HASH_FILES
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfe.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfi.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/conversion.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/ffma.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/other.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/shift.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
)
|
||||
set(COMBINED "")
|
||||
foreach (F IN LISTS HASH_FILES)
|
||||
file(READ ${F} TMP)
|
||||
set(COMBINED "${COMBINED}${TMP}")
|
||||
endforeach()
|
||||
string(MD5 SHADER_CACHE_VERSION "${COMBINED}")
|
||||
configure_file("${SRC_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY)
|
||||
@@ -37,7 +37,7 @@ Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callbac
|
||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
||||
sink_stream{sink_stream}, name{std::move(name_)} {
|
||||
|
||||
release_event = Core::Timing::RegisterEvent(
|
||||
release_event = CoreTiming::RegisterEvent(
|
||||
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ Stream::State Stream::GetState() const {
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
@@ -68,7 +68,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
}
|
||||
|
||||
// Implementation of a volume slider with a dynamic range of 60 dB
|
||||
const float volume_scale_factor = volume == 0 ? 0 : std::exp(6.90775f * volume) * 0.001f;
|
||||
const float volume_scale_factor{std::exp(6.90775f * volume) * 0.001f};
|
||||
for (auto& sample : samples) {
|
||||
sample = static_cast<s16>(sample * volume_scale_factor);
|
||||
}
|
||||
@@ -99,8 +99,7 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
Core::Timing::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event,
|
||||
{});
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "audio_core/buffer.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -91,16 +91,16 @@ private:
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseCycles(const Buffer& buffer) const;
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream
|
||||
BufferPtr active_buffer; ///< Actively playing buffer in the stream
|
||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||
SinkStream& sink_stream; ///< Output sink for the stream
|
||||
std::string name; ///< Name of the stream, must be unique
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
|
||||
BufferPtr active_buffer; ///< Actively playing buffer in the stream
|
||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||
SinkStream& sink_stream; ///< Output sink for the stream
|
||||
std::string name; ///< Name of the stream, must be unique
|
||||
};
|
||||
|
||||
using StreamPtr = std::shared_ptr<Stream>;
|
||||
|
||||
@@ -1,69 +1,42 @@
|
||||
# Add a custom command to generate a new shader_cache_version hash when any of the following files change
|
||||
# NOTE: This is an approximation of what files affect shader generation, its possible something else
|
||||
# could affect the result, but much more unlikely than the following files. Keeping a list of files
|
||||
# like this allows for much better caching since it doesn't force the user to recompile binary shaders every update
|
||||
set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core")
|
||||
if (DEFINED ENV{CI})
|
||||
if (DEFINED ENV{TRAVIS})
|
||||
# Generate cpp with Git revision from template
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
|
||||
set(REPO_NAME "")
|
||||
set(BUILD_VERSION "0")
|
||||
if ($ENV{CI})
|
||||
if ($ENV{TRAVIS})
|
||||
set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
|
||||
set(BUILD_TAG $ENV{TRAVIS_TAG})
|
||||
elseif(DEFINED ENV{APPVEYOR})
|
||||
elseif($ENV{APPVEYOR})
|
||||
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
|
||||
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
|
||||
endif()
|
||||
# regex capture the string nightly or canary into CMAKE_MATCH_1
|
||||
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
# capitalize the first letter of each word in the repo name.
|
||||
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
|
||||
foreach(WORD ${REPO_NAME_LIST})
|
||||
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
|
||||
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
|
||||
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
|
||||
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
|
||||
endforeach()
|
||||
if (BUILD_TAG)
|
||||
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
set(BUILD_VERSION ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
if (BUILD_VERSION)
|
||||
# This leaves a trailing space on the last word, but we actually want that
|
||||
# because of how it's styled in the title bar.
|
||||
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
|
||||
else()
|
||||
set(BUILD_FULLNAME "")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
add_custom_command(OUTPUT scm_rev.cpp
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DSRC_DIR="${CMAKE_SOURCE_DIR}"
|
||||
-DBUILD_REPOSITORY="${BUILD_REPOSITORY}"
|
||||
-DBUILD_TAG="${BUILD_TAG}"
|
||||
-P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
|
||||
DEPENDS
|
||||
# WARNING! It was too much work to try and make a common location for this list,
|
||||
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfe.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfi.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/conversion.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/ffma.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/other.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/shift.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
# and also check that the scm_rev files haven't changed
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
|
||||
# technically we should regenerate if the git version changed, but its not worth the effort imo
|
||||
"${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
|
||||
)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
|
||||
|
||||
add_library(common STATIC
|
||||
alignment.h
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#define KEYS_DIR "keys"
|
||||
#define LOAD_DIR "load"
|
||||
#define DUMP_DIR "dump"
|
||||
#define SHADER_DIR "shader"
|
||||
#define LOG_DIR "log"
|
||||
|
||||
// Filenames
|
||||
|
||||
@@ -710,7 +710,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
|
||||
// TODO: Put the logs in a better location for each OS
|
||||
|
||||
@@ -31,7 +31,6 @@ enum class UserPath {
|
||||
SDMCDir,
|
||||
LoadDir,
|
||||
DumpDir,
|
||||
ShaderDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define BUILD_DATE "@BUILD_DATE@"
|
||||
#define BUILD_FULLNAME "@BUILD_FULLNAME@"
|
||||
#define BUILD_VERSION "@BUILD_VERSION@"
|
||||
#define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -22,7 +21,6 @@ const char g_build_name[] = BUILD_NAME;
|
||||
const char g_build_date[] = BUILD_DATE;
|
||||
const char g_build_fullname[] = BUILD_FULLNAME;
|
||||
const char g_build_version[] = BUILD_VERSION;
|
||||
const char g_shader_cache_version[] = SHADER_CACHE_VERSION;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -13,6 +13,5 @@ extern const char g_build_name[];
|
||||
extern const char g_build_date[];
|
||||
extern const char g_build_fullname[];
|
||||
extern const char g_build_version[];
|
||||
extern const char g_shader_cache_version[];
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -95,8 +95,6 @@ add_library(core STATIC
|
||||
frontend/framebuffer_layout.cpp
|
||||
frontend/framebuffer_layout.h
|
||||
frontend/input.h
|
||||
frontend/scope_acquire_window_context.cpp
|
||||
frontend/scope_acquire_window_context.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hle/ipc.h
|
||||
@@ -140,6 +138,8 @@ add_library(core STATIC
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/thread.cpp
|
||||
hle/kernel/thread.h
|
||||
hle/kernel/timer.cpp
|
||||
hle/kernel/timer.h
|
||||
hle/kernel/vm_manager.cpp
|
||||
hle/kernel/vm_manager.h
|
||||
hle/kernel/wait_object.cpp
|
||||
|
||||
@@ -112,14 +112,14 @@ public:
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
Timing::AddTicks(amortized_ticks);
|
||||
CoreTiming::AddTicks(amortized_ticks);
|
||||
num_interpreted_instructions = 0;
|
||||
}
|
||||
u64 GetTicksRemaining() override {
|
||||
return std::max(Timing::GetDowncount(), 0);
|
||||
return std::max(CoreTiming::GetDowncount(), 0);
|
||||
}
|
||||
u64 GetCNTPCT() override {
|
||||
return Timing::GetTicks();
|
||||
return CoreTiming::GetTicks();
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
|
||||
@@ -177,7 +177,7 @@ void ARM_Unicorn::Run() {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000, 0));
|
||||
} else {
|
||||
ExecuteInstructions(std::max(Timing::GetDowncount(), 0));
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
Timing::AddTicks(num_instructions);
|
||||
CoreTiming::AddTicks(num_instructions);
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
if (last_bkpt_hit) {
|
||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||
|
||||
@@ -94,7 +94,7 @@ struct System::Impl {
|
||||
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
Timing::Init();
|
||||
CoreTiming::Init();
|
||||
kernel.Initialize();
|
||||
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
@@ -123,7 +123,7 @@ struct System::Impl {
|
||||
Service::Init(service_manager, *virtual_filesystem);
|
||||
GDBStub::Init();
|
||||
|
||||
renderer = VideoCore::CreateRenderer(emu_window, system);
|
||||
renderer = VideoCore::CreateRenderer(emu_window);
|
||||
if (!renderer->Init()) {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
@@ -175,7 +175,6 @@ struct System::Impl {
|
||||
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
|
||||
static_cast<u32>(load_result));
|
||||
}
|
||||
|
||||
status = ResultStatus::Success;
|
||||
return status;
|
||||
}
|
||||
@@ -205,7 +204,7 @@ struct System::Impl {
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
kernel.Shutdown();
|
||||
Timing::Shutdown();
|
||||
CoreTiming::Shutdown();
|
||||
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
@@ -232,7 +231,7 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
PerfStatsResults GetAndResetPerfStats() {
|
||||
return perf_stats.GetAndResetStats(Timing::GetGlobalTimeUs());
|
||||
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
Kernel::KernelCore kernel;
|
||||
|
||||
@@ -93,14 +93,14 @@ void Cpu::RunLoop(bool tight_loop) {
|
||||
|
||||
if (IsMainCore()) {
|
||||
// TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
|
||||
Timing::Idle();
|
||||
Timing::Advance();
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
if (IsMainCore()) {
|
||||
Timing::Advance();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
|
||||
if (tight_loop) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/core_timing_util.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
|
||||
static s64 global_timer;
|
||||
static int slice_length;
|
||||
@@ -242,4 +242,4 @@ int GetDowncount() {
|
||||
return downcount;
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
} // namespace CoreTiming
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
|
||||
struct EventType;
|
||||
|
||||
@@ -92,4 +92,4 @@ std::chrono::microseconds GetGlobalTimeUs();
|
||||
|
||||
int GetDowncount();
|
||||
|
||||
} // namespace Core::Timing
|
||||
} // namespace CoreTiming
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <limits>
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
|
||||
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
|
||||
|
||||
@@ -60,4 +60,4 @@ s64 nsToCycles(u64 ns) {
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
} // namespace CoreTiming
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
|
||||
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
|
||||
// The exact value used is of course unverified.
|
||||
@@ -61,4 +61,4 @@ inline u64 cyclesToMs(s64 cycles) {
|
||||
return cycles * 1000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
} // namespace CoreTiming
|
||||
|
||||
@@ -12,6 +12,23 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
/**
|
||||
* Represents a graphics context that can be used for background computation or drawing. If the
|
||||
* graphics backend doesn't require the context, then the implementation of these methods can be
|
||||
* stubs
|
||||
*/
|
||||
class GraphicsContext {
|
||||
public:
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
virtual void SwapBuffers() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||
@@ -30,7 +47,7 @@ namespace Core::Frontend {
|
||||
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
||||
* re-read the upper points again and think about it if you don't see this.
|
||||
*/
|
||||
class EmuWindow {
|
||||
class EmuWindow : public GraphicsContext {
|
||||
public:
|
||||
/// Data structure to store emuwindow configuration
|
||||
struct WindowConfig {
|
||||
@@ -40,17 +57,21 @@ public:
|
||||
std::pair<unsigned, unsigned> min_client_area_size;
|
||||
};
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
virtual void SwapBuffers() = 0;
|
||||
|
||||
/// Polls window events
|
||||
virtual void PollEvents() = 0;
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() = 0;
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
/**
|
||||
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
|
||||
* context can be used from other threads for background graphics computation. If the frontend
|
||||
* is using a graphics backend that doesn't need anything specific to run on a different thread,
|
||||
* then it can use a stubbed implemenation for GraphicsContext.
|
||||
*
|
||||
* If the return value is null, then the core should assume that the frontend cannot provide a
|
||||
* Shared Context
|
||||
*/
|
||||
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/scope_acquire_window_context.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_)
|
||||
: emu_window{emu_window_} {
|
||||
emu_window.MakeCurrent();
|
||||
}
|
||||
ScopeAcquireWindowContext::~ScopeAcquireWindowContext() {
|
||||
emu_window.DoneCurrent();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class EmuWindow;
|
||||
|
||||
/// Helper class to acquire/release window context within a given scope
|
||||
class ScopeAcquireWindowContext : NonCopyable {
|
||||
public:
|
||||
explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window);
|
||||
~ScopeAcquireWindowContext();
|
||||
|
||||
private:
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -507,11 +507,8 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
|
||||
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
|
||||
bp->second.len, bp->second.addr, static_cast<int>(type));
|
||||
|
||||
if (type == BreakpointType::Execute) {
|
||||
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
}
|
||||
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
p.erase(addr);
|
||||
}
|
||||
|
||||
@@ -1060,12 +1057,9 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
|
||||
breakpoint.addr = addr;
|
||||
breakpoint.len = len;
|
||||
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
|
||||
|
||||
static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
|
||||
if (type == BreakpointType::Execute) {
|
||||
Memory::WriteBlock(addr, btrap.data(), btrap.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
}
|
||||
Memory::WriteBlock(addr, btrap.data(), btrap.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
p.insert({addr, breakpoint});
|
||||
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
|
||||
|
||||
@@ -216,11 +216,6 @@ private:
|
||||
|
||||
/// Push ///
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s32 value) {
|
||||
cmdbuf[index++] = static_cast<u32>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(u32 value) {
|
||||
cmdbuf[index++] = value;
|
||||
@@ -239,22 +234,6 @@ inline void ResponseBuilder::Push(ResultCode value) {
|
||||
Push<u32>(0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s8 value) {
|
||||
PushRaw(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s16 value) {
|
||||
PushRaw(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(s64 value) {
|
||||
Push(static_cast<u32>(value));
|
||||
Push(static_cast<u32>(value >> 32));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(u8 value) {
|
||||
PushRaw(value);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -85,12 +86,27 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
|
||||
}
|
||||
}
|
||||
|
||||
/// The timer callback event, called when a timer is fired
|
||||
static void TimerCallback(u64 timer_handle, int cycles_late) {
|
||||
const auto proper_handle = static_cast<Handle>(timer_handle);
|
||||
const auto& system = Core::System::GetInstance();
|
||||
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
|
||||
|
||||
if (timer == nullptr) {
|
||||
LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
timer->Signal(cycles_late);
|
||||
}
|
||||
|
||||
struct KernelCore::Impl {
|
||||
void Initialize(KernelCore& kernel) {
|
||||
Shutdown();
|
||||
|
||||
InitializeSystemResourceLimit(kernel);
|
||||
InitializeThreads();
|
||||
InitializeTimers();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
@@ -106,6 +122,9 @@ struct KernelCore::Impl {
|
||||
thread_wakeup_callback_handle_table.Clear();
|
||||
thread_wakeup_event_type = nullptr;
|
||||
|
||||
timer_callback_handle_table.Clear();
|
||||
timer_callback_event_type = nullptr;
|
||||
|
||||
named_ports.clear();
|
||||
}
|
||||
|
||||
@@ -124,7 +143,12 @@ struct KernelCore::Impl {
|
||||
|
||||
void InitializeThreads() {
|
||||
thread_wakeup_event_type =
|
||||
Core::Timing::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
}
|
||||
|
||||
void InitializeTimers() {
|
||||
timer_callback_handle_table.Clear();
|
||||
timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
@@ -137,7 +161,13 @@ struct KernelCore::Impl {
|
||||
|
||||
SharedPtr<ResourceLimit> system_resource_limit;
|
||||
|
||||
Core::Timing::EventType* thread_wakeup_event_type = nullptr;
|
||||
/// The event type of the generic timer callback event
|
||||
CoreTiming::EventType* timer_callback_event_type = nullptr;
|
||||
// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future,
|
||||
// allowing us to simply use a pool index or similar.
|
||||
Kernel::HandleTable timer_callback_handle_table;
|
||||
|
||||
CoreTiming::EventType* thread_wakeup_event_type = nullptr;
|
||||
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
|
||||
// allowing us to simply use a pool index or similar.
|
||||
Kernel::HandleTable thread_wakeup_callback_handle_table;
|
||||
@@ -168,6 +198,10 @@ SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle
|
||||
return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
|
||||
}
|
||||
|
||||
SharedPtr<Timer> KernelCore::RetrieveTimerFromCallbackHandleTable(Handle handle) const {
|
||||
return impl->timer_callback_handle_table.Get<Timer>(handle);
|
||||
}
|
||||
|
||||
void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
|
||||
impl->process_list.push_back(std::move(process));
|
||||
}
|
||||
@@ -213,10 +247,18 @@ u64 KernelCore::CreateNewProcessID() {
|
||||
return impl->next_process_id++;
|
||||
}
|
||||
|
||||
Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
|
||||
ResultVal<Handle> KernelCore::CreateTimerCallbackHandle(const SharedPtr<Timer>& timer) {
|
||||
return impl->timer_callback_handle_table.Create(timer);
|
||||
}
|
||||
|
||||
CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
|
||||
return impl->thread_wakeup_event_type;
|
||||
}
|
||||
|
||||
CoreTiming::EventType* KernelCore::TimerCallbackEventType() const {
|
||||
return impl->timer_callback_event_type;
|
||||
}
|
||||
|
||||
Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() {
|
||||
return impl->thread_wakeup_callback_handle_table;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
template <typename T>
|
||||
class ResultVal;
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class HandleTable;
|
||||
class Process;
|
||||
class ResourceLimit;
|
||||
class Thread;
|
||||
class Timer;
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
@@ -50,6 +51,9 @@ public:
|
||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
||||
|
||||
/// Retrieves a shared pointer to a Timer instance within the timer callback handle table.
|
||||
SharedPtr<Timer> RetrieveTimerFromCallbackHandleTable(Handle handle) const;
|
||||
|
||||
/// Adds the given shared pointer to an internal list of active processes.
|
||||
void AppendNewProcess(SharedPtr<Process> process);
|
||||
|
||||
@@ -78,6 +82,7 @@ private:
|
||||
friend class Object;
|
||||
friend class Process;
|
||||
friend class Thread;
|
||||
friend class Timer;
|
||||
|
||||
/// Creates a new object ID, incrementing the internal object ID counter.
|
||||
u32 CreateNewObjectID();
|
||||
@@ -88,8 +93,14 @@ private:
|
||||
/// Creates a new thread ID, incrementing the internal thread ID counter.
|
||||
u64 CreateNewThreadID();
|
||||
|
||||
/// Creates a timer callback handle for the given timer.
|
||||
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
|
||||
|
||||
/// Retrieves the event type used for thread wakeup callbacks.
|
||||
Core::Timing::EventType* ThreadWakeupCallbackEventType() const;
|
||||
CoreTiming::EventType* ThreadWakeupCallbackEventType() const;
|
||||
|
||||
/// Retrieves the event type used for timer callbacks.
|
||||
CoreTiming::EventType* TimerCallbackEventType() const;
|
||||
|
||||
/// Provides a reference to the thread wakeup callback handle table.
|
||||
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
|
||||
|
||||
@@ -16,6 +16,7 @@ bool Object::IsWaitable() const {
|
||||
case HandleType::ReadableEvent:
|
||||
case HandleType::Thread:
|
||||
case HandleType::Process:
|
||||
case HandleType::Timer:
|
||||
case HandleType::ServerPort:
|
||||
case HandleType::ServerSession:
|
||||
return true;
|
||||
|
||||
@@ -25,6 +25,7 @@ enum class HandleType : u32 {
|
||||
Thread,
|
||||
Process,
|
||||
AddressArbiter,
|
||||
Timer,
|
||||
ResourceLimit,
|
||||
ClientPort,
|
||||
ServerPort,
|
||||
|
||||
@@ -44,4 +44,8 @@ ResultCode ReadableEvent::Reset() {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void ReadableEvent::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
void WakeupAllWaitingThreads() override;
|
||||
|
||||
/// Unconditionally clears the readable event's state.
|
||||
void Clear();
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
|
||||
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
const u64 prev_switch_ticks = last_context_switch_time;
|
||||
const u64 most_recent_switch_ticks = Core::Timing::GetTicks();
|
||||
const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
|
||||
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
||||
|
||||
if (thread != nullptr) {
|
||||
|
||||
@@ -597,7 +597,6 @@ enum class BreakType : u32 {
|
||||
PostNROLoad = 4,
|
||||
PreNROUnload = 5,
|
||||
PostNROUnload = 6,
|
||||
CppException = 7,
|
||||
};
|
||||
|
||||
struct BreakReason {
|
||||
@@ -670,9 +669,6 @@ static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
break;
|
||||
case BreakType::CppException:
|
||||
LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(
|
||||
Debug_Emulated,
|
||||
@@ -927,9 +923,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
|
||||
|
||||
out_ticks = thread_ticks + (Core::Timing::GetTicks() - prev_ctx_ticks);
|
||||
out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
out_ticks = Core::Timing::GetTicks() - prev_ctx_ticks;
|
||||
out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
|
||||
}
|
||||
|
||||
*result = out_ticks;
|
||||
@@ -1546,10 +1542,10 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
|
||||
static u64 GetSystemTick() {
|
||||
LOG_TRACE(Kernel_SVC, "called");
|
||||
|
||||
const u64 result{Core::Timing::GetTicks()};
|
||||
const u64 result{CoreTiming::GetTicks()};
|
||||
|
||||
// Advance time to defeat dumb games that busy-wait for the frame to end.
|
||||
Core::Timing::AddTicks(400);
|
||||
CoreTiming::AddTicks(400);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ Thread::~Thread() = default;
|
||||
|
||||
void Thread::Stop() {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
Core::Timing::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
||||
callback_handle = 0;
|
||||
|
||||
@@ -85,13 +85,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
|
||||
// This function might be called from any thread so we have to be cautious and use the
|
||||
// thread-safe version of ScheduleEvent.
|
||||
Core::Timing::ScheduleEventThreadsafe(Core::Timing::nsToCycles(nanoseconds),
|
||||
kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds),
|
||||
kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
void Thread::CancelWakeupTimer() {
|
||||
Core::Timing::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(),
|
||||
callback_handle);
|
||||
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
static std::optional<s32> GetNextProcessorId(u64 mask) {
|
||||
@@ -198,7 +197,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||
thread->stack_top = stack_top;
|
||||
thread->tpidr_el0 = 0;
|
||||
thread->nominal_priority = thread->current_priority = priority;
|
||||
thread->last_running_ticks = Core::Timing::GetTicks();
|
||||
thread->last_running_ticks = CoreTiming::GetTicks();
|
||||
thread->processor_id = processor_id;
|
||||
thread->ideal_core = processor_id;
|
||||
thread->affinity_mask = 1ULL << processor_id;
|
||||
@@ -258,7 +257,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
|
||||
}
|
||||
|
||||
if (status == ThreadStatus::Running) {
|
||||
last_running_ticks = Core::Timing::GetTicks();
|
||||
last_running_ticks = CoreTiming::GetTicks();
|
||||
}
|
||||
|
||||
status = new_status;
|
||||
|
||||
88
src/core/hle/kernel/timer.cpp
Normal file
88
src/core/hle/kernel/timer.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
Timer::Timer(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
Timer::~Timer() = default;
|
||||
|
||||
SharedPtr<Timer> Timer::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
|
||||
SharedPtr<Timer> timer(new Timer(kernel));
|
||||
|
||||
timer->reset_type = reset_type;
|
||||
timer->signaled = false;
|
||||
timer->name = std::move(name);
|
||||
timer->initial_delay = 0;
|
||||
timer->interval_delay = 0;
|
||||
timer->callback_handle = kernel.CreateTimerCallbackHandle(timer).Unwrap();
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
bool Timer::ShouldWait(Thread* thread) const {
|
||||
return !signaled;
|
||||
}
|
||||
|
||||
void Timer::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||
|
||||
if (reset_type == ResetType::OneShot)
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
void Timer::Set(s64 initial, s64 interval) {
|
||||
// Ensure we get rid of any previous scheduled event
|
||||
Cancel();
|
||||
|
||||
initial_delay = initial;
|
||||
interval_delay = interval;
|
||||
|
||||
if (initial == 0) {
|
||||
// Immediately invoke the callback
|
||||
Signal(0);
|
||||
} else {
|
||||
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), kernel.TimerCallbackEventType(),
|
||||
callback_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::Cancel() {
|
||||
CoreTiming::UnscheduleEvent(kernel.TimerCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
void Timer::Clear() {
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
void Timer::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
}
|
||||
|
||||
void Timer::Signal(int cycles_late) {
|
||||
LOG_TRACE(Kernel, "Timer {} fired", GetObjectId());
|
||||
|
||||
signaled = true;
|
||||
|
||||
// Resume all waiting threads
|
||||
WakeupAllWaitingThreads();
|
||||
|
||||
if (interval_delay != 0) {
|
||||
// Reschedule the timer with the interval delay
|
||||
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(interval_delay) - cycles_late,
|
||||
kernel.TimerCallbackEventType(), callback_handle);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
90
src/core/hle/kernel/timer.h
Normal file
90
src/core/hle/kernel/timer.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
class Timer final : public WaitObject {
|
||||
public:
|
||||
/**
|
||||
* Creates a timer
|
||||
* @param kernel The kernel instance to create the timer callback handle for.
|
||||
* @param reset_type ResetType describing how to create the timer
|
||||
* @param name Optional name of timer
|
||||
* @return The created Timer
|
||||
*/
|
||||
static SharedPtr<Timer> Create(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Timer";
|
||||
}
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::Timer;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
ResetType GetResetType() const {
|
||||
return reset_type;
|
||||
}
|
||||
|
||||
u64 GetInitialDelay() const {
|
||||
return initial_delay;
|
||||
}
|
||||
|
||||
u64 GetIntervalDelay() const {
|
||||
return interval_delay;
|
||||
}
|
||||
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
void WakeupAllWaitingThreads() override;
|
||||
|
||||
/**
|
||||
* Starts the timer, with the specified initial delay and interval.
|
||||
* @param initial Delay until the timer is first fired
|
||||
* @param interval Delay until the timer is fired after the first time
|
||||
*/
|
||||
void Set(s64 initial, s64 interval);
|
||||
|
||||
void Cancel();
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* Signals the timer, waking up any waiting threads and rescheduling it
|
||||
* for the next interval.
|
||||
* This method should not be called from outside the timer callback handler,
|
||||
* lest multiple callback events get scheduled.
|
||||
*/
|
||||
void Signal(int cycles_late);
|
||||
|
||||
private:
|
||||
explicit Timer(KernelCore& kernel);
|
||||
~Timer() override;
|
||||
|
||||
ResetType reset_type; ///< The ResetType of this timer
|
||||
|
||||
u64 initial_delay; ///< The delay until the timer fires for the first time
|
||||
u64 interval_delay; ///< The delay until the timer fires after the first time
|
||||
|
||||
bool signaled; ///< Whether the timer has been signaled or not
|
||||
std::string name; ///< Name of timer (optional)
|
||||
|
||||
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
|
||||
Handle callback_handle;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -33,19 +33,19 @@ public:
|
||||
* Add a thread to wait on this object
|
||||
* @param thread Pointer to thread to add
|
||||
*/
|
||||
void AddWaitingThread(SharedPtr<Thread> thread);
|
||||
virtual void AddWaitingThread(SharedPtr<Thread> thread);
|
||||
|
||||
/**
|
||||
* Removes a thread from waiting on this object (e.g. if it was resumed already)
|
||||
* @param thread Pointer to thread to remove
|
||||
*/
|
||||
void RemoveWaitingThread(Thread* thread);
|
||||
virtual void RemoveWaitingThread(Thread* thread);
|
||||
|
||||
/**
|
||||
* Wake up all threads waiting on this object that can be awoken, in priority order,
|
||||
* and set the synchronization result and output of the thread.
|
||||
*/
|
||||
void WakeupAllWaitingThreads();
|
||||
virtual void WakeupAllWaitingThreads();
|
||||
|
||||
/**
|
||||
* Wakes up a single thread waiting on this object.
|
||||
|
||||
@@ -322,15 +322,14 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
|
||||
|
||||
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
const auto display_id = nvflinger->OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger->CreateLayer(*display_id);
|
||||
u64 display_id = nvflinger->OpenDisplay("Default");
|
||||
u64 layer_id = nvflinger->CreateLayer(display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*layer_id);
|
||||
rb.Push(layer_id);
|
||||
}
|
||||
|
||||
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -249,8 +249,7 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
{300, nullptr, "OpenOverlayAppletProxy"},
|
||||
{350, nullptr, "OpenSystemApplicationProxy"},
|
||||
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
{410, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
{1000, nullptr, "GetDebugFunctions"},
|
||||
{401, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace Service::Audio {
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
IAudioIn() : ServiceFramework("IAudioIn") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetAudioInState"},
|
||||
{1, nullptr, "StartAudioIn"},
|
||||
@@ -29,24 +28,16 @@ public:
|
||||
{12, nullptr, "SetAudioInDeviceGain"},
|
||||
{13, nullptr, "GetAudioInDeviceGain"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IAudioIn() = default;
|
||||
};
|
||||
|
||||
AudInU::AudInU() : ServiceFramework("audin:u") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListAudioIns"},
|
||||
{1, nullptr, "OpenAudioIn"},
|
||||
{2, nullptr, "Unknown"},
|
||||
{3, nullptr, "OpenAudioInAuto"},
|
||||
{4, nullptr, "ListAudioInsAuto"},
|
||||
{0, nullptr, "ListAudioIns"}, {1, nullptr, "OpenAudioIn"}, {2, nullptr, "Unknown"},
|
||||
{3, nullptr, "OpenAudioInAuto"}, {4, nullptr, "ListAudioInsAuto"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace Service::Audio {
|
||||
class IFinalOutputRecorder final : public ServiceFramework<IFinalOutputRecorder> {
|
||||
public:
|
||||
IFinalOutputRecorder() : ServiceFramework("IFinalOutputRecorder") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetFinalOutputRecorderState"},
|
||||
{1, nullptr, "StartFinalOutputRecorder"},
|
||||
@@ -21,13 +20,10 @@ public:
|
||||
{4, nullptr, "RegisterBufferEvent"},
|
||||
{5, nullptr, "GetReleasedFinalOutputRecorderBuffer"},
|
||||
{6, nullptr, "ContainsFinalOutputRecorderBuffer"},
|
||||
{7, nullptr, "GetFinalOutputRecorderBufferEndTime"},
|
||||
{7, nullptr, "Unknown"},
|
||||
{8, nullptr, "AppendFinalOutputRecorderBufferAuto"},
|
||||
{9, nullptr, "GetReleasedFinalOutputRecorderBufferAuto"},
|
||||
{10, nullptr, "FlushFinalOutputRecorderBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IFinalOutputRecorder() = default;
|
||||
|
||||
@@ -229,16 +229,14 @@ private:
|
||||
}; // namespace Audio
|
||||
|
||||
AudRenU::AudRenU() : ServiceFramework("audren:u") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
|
||||
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
|
||||
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
|
||||
{2, &AudRenU::GetAudioDevice, "GetAudioDevice"},
|
||||
{3, nullptr, "OpenAudioRendererAuto"},
|
||||
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
|
||||
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo,
|
||||
"GetAudioDeviceServiceWithRevisionInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -315,7 +313,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
|
||||
void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
private:
|
||||
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDevice(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <opus.h>
|
||||
@@ -29,66 +30,48 @@ public:
|
||||
u32 channel_count)
|
||||
: ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
|
||||
sample_rate(sample_rate), channel_count(channel_count) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
|
||||
{0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{1, nullptr, "SetContext"},
|
||||
{2, nullptr, "DecodeInterleavedForMultiStreamOld"},
|
||||
{2, nullptr, "DecodeInterleavedForMultiStream"},
|
||||
{3, nullptr, "SetContextForMultiStream"},
|
||||
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
|
||||
{5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
|
||||
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{7, nullptr, "DecodeInterleavedForMultiStream"},
|
||||
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
|
||||
"DecodeInterleavedWithPerformance"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Describes extra behavior that may be asked of the decoding context.
|
||||
enum class ExtraBehavior {
|
||||
/// No extra behavior.
|
||||
None,
|
||||
|
||||
/// Resets the decoder context back to a freshly initialized state.
|
||||
ResetContext,
|
||||
};
|
||||
|
||||
void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None);
|
||||
}
|
||||
|
||||
void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
u64 performance = 0;
|
||||
DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None);
|
||||
}
|
||||
|
||||
void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto extra_behavior =
|
||||
rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None;
|
||||
|
||||
u64 performance = 0;
|
||||
DecodeInterleavedHelper(ctx, &performance, extra_behavior);
|
||||
}
|
||||
|
||||
void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance,
|
||||
ExtraBehavior extra_behavior) {
|
||||
u32 consumed = 0;
|
||||
u32 sample_count = 0;
|
||||
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
|
||||
|
||||
if (extra_behavior == ExtraBehavior::ResetContext) {
|
||||
ResetDecoderContext();
|
||||
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) {
|
||||
LOG_ERROR(Audio, "Failed to decode opus data");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(ogniK): Use correct error code
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(consumed);
|
||||
rb.Push<u32>(sample_count);
|
||||
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
|
||||
}
|
||||
|
||||
void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
u32 consumed = 0;
|
||||
u32 sample_count = 0;
|
||||
u64 performance = 0;
|
||||
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
|
||||
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
|
||||
performance)) {
|
||||
LOG_ERROR(Audio, "Failed to decode opus data");
|
||||
@@ -97,28 +80,25 @@ private:
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 param_size = performance != nullptr ? 6 : 4;
|
||||
IPC::ResponseBuilder rb{ctx, param_size};
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(consumed);
|
||||
rb.Push<u32>(sample_count);
|
||||
if (performance) {
|
||||
rb.Push<u64>(*performance);
|
||||
}
|
||||
rb.Push<u64>(performance);
|
||||
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
|
||||
}
|
||||
|
||||
bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
|
||||
std::vector<opus_int16>& output, u64* out_performance_time) {
|
||||
bool Decoder_DecodeInterleaved(
|
||||
u32& consumed, u32& sample_count, const std::vector<u8>& input,
|
||||
std::vector<opus_int16>& output,
|
||||
std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
|
||||
const auto start_time = std::chrono::high_resolution_clock::now();
|
||||
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
|
||||
std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
|
||||
if (sizeof(OpusHeader) > input.size()) {
|
||||
LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
|
||||
sizeof(OpusHeader), input.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
OpusHeader hdr{};
|
||||
std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
|
||||
if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
|
||||
@@ -126,9 +106,8 @@ private:
|
||||
sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto frame = input.data() + sizeof(OpusHeader);
|
||||
const auto decoded_sample_count = opus_packet_get_nb_samples(
|
||||
auto frame = input.data() + sizeof(OpusHeader);
|
||||
auto decoded_sample_count = opus_packet_get_nb_samples(
|
||||
frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
|
||||
static_cast<opus_int32>(sample_rate));
|
||||
if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
|
||||
@@ -138,9 +117,8 @@ private:
|
||||
decoded_sample_count * channel_count * sizeof(u16), raw_output_sz);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
|
||||
const auto out_sample_count =
|
||||
auto out_sample_count =
|
||||
opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0);
|
||||
if (out_sample_count < 0) {
|
||||
LOG_ERROR(Audio,
|
||||
@@ -149,24 +127,16 @@ private:
|
||||
out_sample_count, frame_size, static_cast<u32>(hdr.sz));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
|
||||
sample_count = out_sample_count;
|
||||
consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
|
||||
if (out_performance_time != nullptr) {
|
||||
*out_performance_time =
|
||||
if (performance_time.has_value()) {
|
||||
performance_time->get() =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResetDecoderContext() {
|
||||
ASSERT(decoder != nullptr);
|
||||
|
||||
opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
|
||||
}
|
||||
|
||||
struct OpusHeader {
|
||||
u32_be sz; // Needs to be BE for some odd reason
|
||||
INSERT_PADDING_WORDS(1);
|
||||
@@ -187,7 +157,6 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto sample_rate = rp.Pop<u32>();
|
||||
const auto channel_count = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count);
|
||||
|
||||
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
|
||||
@@ -205,10 +174,9 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto sample_rate = rp.Pop<u32>();
|
||||
const auto channel_count = rp.Pop<u32>();
|
||||
const auto buffer_sz = rp.Pop<u32>();
|
||||
|
||||
auto sample_rate = rp.Pop<u32>();
|
||||
auto channel_count = rp.Pop<u32>();
|
||||
auto buffer_sz = rp.Pop<u32>();
|
||||
LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
|
||||
channel_count, buffer_sz);
|
||||
|
||||
@@ -217,9 +185,8 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
|
||||
"Invalid sample rate");
|
||||
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
|
||||
|
||||
const std::size_t worker_sz = WorkerBufferSize(channel_count);
|
||||
std::size_t worker_sz = WorkerBufferSize(channel_count);
|
||||
ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
|
||||
|
||||
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
|
||||
static_cast<OpusDecoder*>(operator new(worker_sz))};
|
||||
if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
|
||||
|
||||
@@ -19,16 +19,16 @@ public:
|
||||
explicit Bt() : ServiceFramework{"bt"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "LeClientReadCharacteristic"},
|
||||
{1, nullptr, "LeClientReadDescriptor"},
|
||||
{2, nullptr, "LeClientWriteCharacteristic"},
|
||||
{3, nullptr, "LeClientWriteDescriptor"},
|
||||
{4, nullptr, "LeClientRegisterNotification"},
|
||||
{5, nullptr, "LeClientDeregisterNotification"},
|
||||
{6, nullptr, "SetLeResponse"},
|
||||
{7, nullptr, "LeSendIndication"},
|
||||
{8, nullptr, "GetLeEventInfo"},
|
||||
{9, &Bt::RegisterBleEvent, "RegisterBleEvent"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, &Bt::RegisterEvent, "RegisterEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterBleEvent(Kernel::HLERequestContext& ctx) {
|
||||
void RegisterEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -55,11 +55,11 @@ public:
|
||||
explicit BtDrv() : ServiceFramework{"btdrv"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "InitializeBluetoothDriver"},
|
||||
{1, nullptr, "InitializeBluetooth"},
|
||||
{2, nullptr, "EnableBluetooth"},
|
||||
{3, nullptr, "DisableBluetooth"},
|
||||
{4, nullptr, "CleanupBluetooth"},
|
||||
{0, nullptr, "Unknown"},
|
||||
{1, nullptr, "Init"},
|
||||
{2, nullptr, "Enable"},
|
||||
{3, nullptr, "Disable"},
|
||||
{4, nullptr, "CleanupAndShutdown"},
|
||||
{5, nullptr, "GetAdapterProperties"},
|
||||
{6, nullptr, "GetAdapterProperty"},
|
||||
{7, nullptr, "SetAdapterProperty"},
|
||||
@@ -70,91 +70,36 @@ public:
|
||||
{12, nullptr, "CancelBond"},
|
||||
{13, nullptr, "PinReply"},
|
||||
{14, nullptr, "SspReply"},
|
||||
{15, nullptr, "GetEventInfo"},
|
||||
{16, nullptr, "InitializeHid"},
|
||||
{17, nullptr, "HidConnect"},
|
||||
{18, nullptr, "HidDisconnect"},
|
||||
{19, nullptr, "HidSendData"},
|
||||
{20, nullptr, "HidSendData2"},
|
||||
{21, nullptr, "HidSetReport"},
|
||||
{22, nullptr, "HidGetReport"},
|
||||
{23, nullptr, "HidWakeController"},
|
||||
{24, nullptr, "HidAddPairedDevice"},
|
||||
{25, nullptr, "HidGetPairedDevice"},
|
||||
{26, nullptr, "CleanupHid"},
|
||||
{27, nullptr, "HidGetEventInfo"},
|
||||
{28, nullptr, "ExtSetTsi"},
|
||||
{29, nullptr, "ExtSetBurstMode"},
|
||||
{30, nullptr, "ExtSetZeroRetran"},
|
||||
{31, nullptr, "ExtSetMcMode"},
|
||||
{32, nullptr, "ExtStartLlrMode"},
|
||||
{33, nullptr, "ExtExitLlrMode"},
|
||||
{34, nullptr, "ExtSetRadio"},
|
||||
{35, nullptr, "ExtSetVisibility"},
|
||||
{36, nullptr, "ExtSetTbfcScan"},
|
||||
{37, nullptr, "RegisterHidReportEvent"},
|
||||
{38, nullptr, "HidGetReportEventInfo"},
|
||||
{39, nullptr, "GetLatestPlr"},
|
||||
{40, nullptr, "ExtGetPendingConnections"},
|
||||
{41, nullptr, "GetChannelMap"},
|
||||
{42, nullptr, "EnableBluetoothBoostSetting"},
|
||||
{43, nullptr, "IsBluetoothBoostSettingEnabled"},
|
||||
{44, nullptr, "EnableBluetoothAfhSetting"},
|
||||
{45, nullptr, "IsBluetoothAfhSettingEnabled"},
|
||||
{46, nullptr, "InitializeBluetoothLe"},
|
||||
{47, nullptr, "EnableBluetoothLe"},
|
||||
{48, nullptr, "DisableBluetoothLe"},
|
||||
{49, nullptr, "CleanupBluetoothLe"},
|
||||
{50, nullptr, "SetLeVisibility"},
|
||||
{51, nullptr, "SetLeConnectionParameter"},
|
||||
{52, nullptr, "SetLeDefaultConnectionParameter"},
|
||||
{53, nullptr, "SetLeAdvertiseData"},
|
||||
{54, nullptr, "SetLeAdvertiseParameter"},
|
||||
{55, nullptr, "StartLeScan"},
|
||||
{56, nullptr, "StopLeScan"},
|
||||
{57, nullptr, "AddLeScanFilterCondition"},
|
||||
{58, nullptr, "DeleteLeScanFilterCondition"},
|
||||
{59, nullptr, "DeleteLeScanFilter"},
|
||||
{60, nullptr, "ClearLeScanFilters"},
|
||||
{61, nullptr, "EnableLeScanFilter"},
|
||||
{62, nullptr, "RegisterLeClient"},
|
||||
{63, nullptr, "UnregisterLeClient"},
|
||||
{64, nullptr, "UnregisterLeClientAll"},
|
||||
{65, nullptr, "LeClientConnect"},
|
||||
{66, nullptr, "LeClientCancelConnection"},
|
||||
{67, nullptr, "LeClientDisconnect"},
|
||||
{68, nullptr, "LeClientGetAttributes"},
|
||||
{69, nullptr, "LeClientDiscoverService"},
|
||||
{70, nullptr, "LeClientConfigureMtu"},
|
||||
{71, nullptr, "RegisterLeServer"},
|
||||
{72, nullptr, "UnregisterLeServer"},
|
||||
{73, nullptr, "LeServerConnect"},
|
||||
{74, nullptr, "LeServerDisconnect"},
|
||||
{75, nullptr, "CreateLeService"},
|
||||
{76, nullptr, "StartLeService"},
|
||||
{77, nullptr, "AddLeCharacteristic"},
|
||||
{78, nullptr, "AddLeDescriptor"},
|
||||
{79, nullptr, "GetLeCoreEventInfo"},
|
||||
{80, nullptr, "LeGetFirstCharacteristic"},
|
||||
{81, nullptr, "LeGetNextCharacteristic"},
|
||||
{82, nullptr, "LeGetFirstDescriptor"},
|
||||
{83, nullptr, "LeGetNextDescriptor"},
|
||||
{84, nullptr, "RegisterLeCoreDataPath"},
|
||||
{85, nullptr, "UnregisterLeCoreDataPath"},
|
||||
{86, nullptr, "RegisterLeHidDataPath"},
|
||||
{87, nullptr, "UnregisterLeHidDataPath"},
|
||||
{88, nullptr, "RegisterLeDataPath"},
|
||||
{89, nullptr, "UnregisterLeDataPath"},
|
||||
{90, nullptr, "LeClientReadCharacteristic"},
|
||||
{91, nullptr, "LeClientReadDescriptor"},
|
||||
{92, nullptr, "LeClientWriteCharacteristic"},
|
||||
{93, nullptr, "LeClientWriteDescriptor"},
|
||||
{94, nullptr, "LeClientRegisterNotification"},
|
||||
{95, nullptr, "LeClientDeregisterNotification"},
|
||||
{96, nullptr, "GetLeHidEventInfo"},
|
||||
{97, nullptr, "RegisterBleHidEvent"},
|
||||
{98, nullptr, "SetLeScanParameter"},
|
||||
{256, nullptr, "GetIsManufacturingMode"}
|
||||
{15, nullptr, "Unknown2"},
|
||||
{16, nullptr, "InitInterfaces"},
|
||||
{17, nullptr, "HidHostInterface_Connect"},
|
||||
{18, nullptr, "HidHostInterface_Disconnect"},
|
||||
{19, nullptr, "HidHostInterface_SendData"},
|
||||
{20, nullptr, "HidHostInterface_SendData2"},
|
||||
{21, nullptr, "HidHostInterface_SetReport"},
|
||||
{22, nullptr, "HidHostInterface_GetReport"},
|
||||
{23, nullptr, "HidHostInterface_WakeController"},
|
||||
{24, nullptr, "HidHostInterface_AddPairedDevice"},
|
||||
{25, nullptr, "HidHostInterface_GetPairedDevice"},
|
||||
{26, nullptr, "HidHostInterface_CleanupAndShutdown"},
|
||||
{27, nullptr, "Unknown3"},
|
||||
{28, nullptr, "ExtInterface_SetTSI"},
|
||||
{29, nullptr, "ExtInterface_SetBurstMode"},
|
||||
{30, nullptr, "ExtInterface_SetZeroRetran"},
|
||||
{31, nullptr, "ExtInterface_SetMcMode"},
|
||||
{32, nullptr, "ExtInterface_StartLlrMode"},
|
||||
{33, nullptr, "ExtInterface_ExitLlrMode"},
|
||||
{34, nullptr, "ExtInterface_SetRadio"},
|
||||
{35, nullptr, "ExtInterface_SetVisibility"},
|
||||
{36, nullptr, "Unknown4"},
|
||||
{37, nullptr, "Unknown5"},
|
||||
{38, nullptr, "HidHostInterface_GetLatestPlr"},
|
||||
{39, nullptr, "ExtInterface_GetPendingConnections"},
|
||||
{40, nullptr, "HidHostInterface_GetChannelMap"},
|
||||
{41, nullptr, "SetIsBluetoothBoostEnabled"},
|
||||
{42, nullptr, "GetIsBluetoothBoostEnabled"},
|
||||
{43, nullptr, "SetIsBluetoothAfhEnabled"},
|
||||
{44, nullptr, "GetIsBluetoothAfhEnabled"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -20,38 +20,38 @@ public:
|
||||
explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
|
||||
{1, nullptr, "GetBleScanFilterParameter"},
|
||||
{2, nullptr, "GetBleScanFilterParameter2"},
|
||||
{3, nullptr, "StartBleScanForGeneral"},
|
||||
{4, nullptr, "StopBleScanForGeneral"},
|
||||
{5, nullptr, "GetBleScanResultsForGeneral"},
|
||||
{6, nullptr, "StartBleScanForPaired"},
|
||||
{7, nullptr, "StopBleScanForPaired"},
|
||||
{8, nullptr, "StartBleScanForSmartDevice"},
|
||||
{9, nullptr, "StopBleScanForSmartDevice"},
|
||||
{10, nullptr, "GetBleScanResultsForSmartDevice"},
|
||||
{17, &IBtmUserCore::AcquireBleConnectionEvent, "AcquireBleConnectionEvent"},
|
||||
{18, nullptr, "BleConnect"},
|
||||
{19, nullptr, "BleDisconnect"},
|
||||
{20, nullptr, "BleGetConnectionState"},
|
||||
{21, nullptr, "AcquireBlePairingEvent"},
|
||||
{22, nullptr, "BlePairDevice"},
|
||||
{23, nullptr, "BleUnPairDevice"},
|
||||
{24, nullptr, "BleUnPairDevice2"},
|
||||
{25, nullptr, "BleGetPairedDevices"},
|
||||
{26, &IBtmUserCore::AcquireBleServiceDiscoveryEvent, "AcquireBleServiceDiscoveryEvent"},
|
||||
{27, nullptr, "GetGattServices"},
|
||||
{28, nullptr, "GetGattService"},
|
||||
{29, nullptr, "GetGattIncludedServices"},
|
||||
{30, nullptr, "GetBelongingGattService"},
|
||||
{31, nullptr, "GetGattCharacteristics"},
|
||||
{32, nullptr, "GetGattDescriptors"},
|
||||
{33, &IBtmUserCore::AcquireBleMtuConfigEvent, "AcquireBleMtuConfigEvent"},
|
||||
{34, nullptr, "ConfigureBleMtu"},
|
||||
{35, nullptr, "GetBleMtu"},
|
||||
{36, nullptr, "RegisterBleGattDataPath"},
|
||||
{37, nullptr, "UnregisterBleGattDataPath"},
|
||||
{0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "Unknown28"},
|
||||
{29, nullptr, "Unknown29"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "Unknown32"},
|
||||
{33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
|
||||
{34, nullptr, "Unknown34"},
|
||||
{35, nullptr, "Unknown35"},
|
||||
{36, nullptr, "Unknown36"},
|
||||
{37, nullptr, "Unknown37"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void AcquireBleScanEvent(Kernel::HLERequestContext& ctx) {
|
||||
void GetScanEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
rb.PushCopyObjects(scan_event.readable);
|
||||
}
|
||||
|
||||
void AcquireBleConnectionEvent(Kernel::HLERequestContext& ctx) {
|
||||
void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
rb.PushCopyObjects(connection_event.readable);
|
||||
}
|
||||
|
||||
void AcquireBleServiceDiscoveryEvent(Kernel::HLERequestContext& ctx) {
|
||||
void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -92,7 +92,7 @@ private:
|
||||
rb.PushCopyObjects(service_discovery.readable);
|
||||
}
|
||||
|
||||
void AcquireBleMtuConfigEvent(Kernel::HLERequestContext& ctx) {
|
||||
void GetConfigEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -111,14 +111,14 @@ public:
|
||||
explicit BTM_USR() : ServiceFramework{"btm:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BTM_USR::GetCore, "GetCore"},
|
||||
{0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCore(Kernel::HLERequestContext& ctx) {
|
||||
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
@@ -134,64 +134,26 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"},
|
||||
{2, nullptr, "RegisterSystemEventForConnectedDeviceConditionImpl"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"},
|
||||
{8, nullptr, "RegisterSystemEventForRegisteredDeviceInfoImpl"},
|
||||
{9, nullptr, "Unknown8"},
|
||||
{10, nullptr, "Unknown9"},
|
||||
{11, nullptr, "Unknown10"},
|
||||
{12, nullptr, "Unknown11"},
|
||||
{13, nullptr, "Unknown12"},
|
||||
{14, nullptr, "EnableRadio"},
|
||||
{15, nullptr, "DisableRadio"},
|
||||
{14, nullptr, "EnableRadioImpl"},
|
||||
{15, nullptr, "DisableRadioImpl"},
|
||||
{16, nullptr, "Unknown13"},
|
||||
{17, nullptr, "Unknown14"},
|
||||
{18, nullptr, "Unknown15"},
|
||||
{19, nullptr, "Unknown16"},
|
||||
{20, nullptr, "Unknown17"},
|
||||
{21, nullptr, "Unknown18"},
|
||||
{22, nullptr, "Unknown19"},
|
||||
{23, nullptr, "Unknown20"},
|
||||
{24, nullptr, "Unknown21"},
|
||||
{25, nullptr, "Unknown22"},
|
||||
{26, nullptr, "Unknown23"},
|
||||
{27, nullptr, "Unknown24"},
|
||||
{28, nullptr, "Unknown25"},
|
||||
{29, nullptr, "Unknown26"},
|
||||
{30, nullptr, "Unknown27"},
|
||||
{31, nullptr, "Unknown28"},
|
||||
{32, nullptr, "Unknown29"},
|
||||
{33, nullptr, "Unknown30"},
|
||||
{34, nullptr, "Unknown31"},
|
||||
{35, nullptr, "Unknown32"},
|
||||
{36, nullptr, "Unknown33"},
|
||||
{37, nullptr, "Unknown34"},
|
||||
{38, nullptr, "Unknown35"},
|
||||
{39, nullptr, "Unknown36"},
|
||||
{40, nullptr, "Unknown37"},
|
||||
{41, nullptr, "Unknown38"},
|
||||
{42, nullptr, "Unknown39"},
|
||||
{43, nullptr, "Unknown40"},
|
||||
{44, nullptr, "Unknown41"},
|
||||
{45, nullptr, "Unknown42"},
|
||||
{46, nullptr, "Unknown43"},
|
||||
{47, nullptr, "Unknown44"},
|
||||
{48, nullptr, "Unknown45"},
|
||||
{49, nullptr, "Unknown46"},
|
||||
{50, nullptr, "Unknown47"},
|
||||
{51, nullptr, "Unknown48"},
|
||||
{52, nullptr, "Unknown49"},
|
||||
{53, nullptr, "Unknown50"},
|
||||
{54, nullptr, "Unknown51"},
|
||||
{55, nullptr, "Unknown52"},
|
||||
{56, nullptr, "Unknown53"},
|
||||
{57, nullptr, "Unknown54"},
|
||||
{58, nullptr, "Unknown55"},
|
||||
{59, nullptr, "Unknown56"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -204,7 +166,7 @@ public:
|
||||
explicit BTM_DBG() : ServiceFramework{"btm:dbg"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RegisterSystemEventForDiscovery"},
|
||||
{0, nullptr, "RegisterSystemEventForDiscoveryImpl"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
@@ -213,10 +175,6 @@ public:
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
{12, nullptr, "Unknown11"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -229,16 +187,16 @@ public:
|
||||
explicit IBtmSystemCore() : ServiceFramework{"IBtmSystemCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "StartGamepadPairing"},
|
||||
{1, nullptr, "CancelGamepadPairing"},
|
||||
{2, nullptr, "ClearGamepadPairingDatabase"},
|
||||
{3, nullptr, "GetPairedGamepadCount"},
|
||||
{4, nullptr, "EnableRadio"},
|
||||
{5, nullptr, "DisableRadio"},
|
||||
{6, nullptr, "GetRadioOnOff"},
|
||||
{7, nullptr, "AcquireRadioEvent"},
|
||||
{8, nullptr, "AcquireGamepadPairingEvent"},
|
||||
{9, nullptr, "IsGamepadPairingStarted"},
|
||||
{0, nullptr, "StartGamepadPairingImpl"},
|
||||
{1, nullptr, "CancelGamepadPairingImpl"},
|
||||
{2, nullptr, "ClearGamepadPairingDatabaseImpl"},
|
||||
{3, nullptr, "GetPairedGamepadCountImpl"},
|
||||
{4, nullptr, "EnableRadioImpl"},
|
||||
{5, nullptr, "DisableRadioImpl"},
|
||||
{6, nullptr, "GetRadioOnOffImpl"},
|
||||
{7, nullptr, "AcquireRadioEventImpl"},
|
||||
{8, nullptr, "AcquireGamepadPairingEventImpl"},
|
||||
{9, nullptr, "IsGamepadPairingStartedImpl"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -251,7 +209,7 @@ public:
|
||||
explicit BTM_SYS() : ServiceFramework{"btm:sys"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BTM_SYS::GetCore, "GetCore"},
|
||||
{0, &BTM_SYS::GetCoreImpl, "GetCoreImpl"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -259,7 +217,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCore(Kernel::HLERequestContext& ctx) {
|
||||
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -627,8 +627,8 @@ private:
|
||||
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "OpenFileSystem"},
|
||||
{1, &FSP_SRV::SetCurrentProcess, "SetCurrentProcess"},
|
||||
{0, nullptr, "MountContent"},
|
||||
{1, &FSP_SRV::Initialize, "Initialize"},
|
||||
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
|
||||
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
|
||||
{8, nullptr, "OpenFileSystemWithId"},
|
||||
@@ -637,10 +637,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{12, nullptr, "OpenBisStorage"},
|
||||
{13, nullptr, "InvalidateBisCache"},
|
||||
{17, nullptr, "OpenHostFileSystem"},
|
||||
{18, &FSP_SRV::OpenSdCardFileSystem, "OpenSdCardFileSystem"},
|
||||
{18, &FSP_SRV::MountSdCard, "MountSdCard"},
|
||||
{19, nullptr, "FormatSdCardFileSystem"},
|
||||
{21, nullptr, "DeleteSaveDataFileSystem"},
|
||||
{22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"},
|
||||
{22, &FSP_SRV::CreateSaveData, "CreateSaveData"},
|
||||
{23, nullptr, "CreateSaveDataFileSystemBySystemSaveDataId"},
|
||||
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
|
||||
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
|
||||
@@ -652,8 +652,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{32, nullptr, "ExtendSaveDataFileSystem"},
|
||||
{33, nullptr, "DeleteCacheStorage"},
|
||||
{34, nullptr, "GetCacheStorageSize"},
|
||||
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
|
||||
{51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
|
||||
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
|
||||
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
|
||||
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
|
||||
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
|
||||
@@ -665,26 +664,21 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
|
||||
{65, nullptr, "UpdateSaveDataMacForDebug"},
|
||||
{66, nullptr, "WriteSaveDataFileSystemExtraData2"},
|
||||
{67, nullptr, "FindSaveDataWithFilter"},
|
||||
{68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
|
||||
{80, nullptr, "OpenSaveDataMetaFile"},
|
||||
{81, nullptr, "OpenSaveDataTransferManager"},
|
||||
{82, nullptr, "OpenSaveDataTransferManagerVersion2"},
|
||||
{83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
|
||||
{84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
|
||||
{100, nullptr, "OpenImageDirectoryFileSystem"},
|
||||
{110, nullptr, "OpenContentStorageFileSystem"},
|
||||
{120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
|
||||
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
|
||||
{201, nullptr, "OpenDataStorageByProgramId"},
|
||||
{202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
|
||||
{203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
|
||||
{203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"},
|
||||
{400, nullptr, "OpenDeviceOperator"},
|
||||
{500, nullptr, "OpenSdCardDetectionEventNotifier"},
|
||||
{501, nullptr, "OpenGameCardDetectionEventNotifier"},
|
||||
{510, nullptr, "OpenSystemDataUpdateEventNotifier"},
|
||||
{511, nullptr, "NotifySystemDataUpdateEvent"},
|
||||
{520, nullptr, "SimulateGameCardDetectionEvent"},
|
||||
{600, nullptr, "SetCurrentPosixTime"},
|
||||
{601, nullptr, "QuerySaveDataTotalSize"},
|
||||
{602, nullptr, "VerifySaveDataFileSystem"},
|
||||
@@ -723,8 +717,6 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{1008, nullptr, "OpenRegisteredUpdatePartition"},
|
||||
{1009, nullptr, "GetAndClearMemoryReportInfo"},
|
||||
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
|
||||
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
|
||||
{1200, nullptr, "OpenMultiCommitManager"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -732,7 +724,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
|
||||
FSP_SRV::~FSP_SRV() = default;
|
||||
|
||||
void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -751,7 +743,7 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultCode(-1));
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IFileSystem filesystem(OpenSDMC().Unwrap());
|
||||
@@ -761,7 +753,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
|
||||
}
|
||||
|
||||
void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
|
||||
@@ -775,7 +767,7 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
|
||||
@@ -801,7 +793,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
|
||||
OpenSaveDataFileSystem(ctx);
|
||||
MountSaveData(ctx);
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
|
||||
@@ -889,7 +881,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage));
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto storage_id = rp.PopRaw<FileSys::StorageId>();
|
||||
|
||||
@@ -19,17 +19,17 @@ public:
|
||||
~FSP_SRV() override;
|
||||
|
||||
private:
|
||||
void SetCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
|
||||
void OpenSdCardFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void MountSdCard(Kernel::HLERequestContext& ctx);
|
||||
void CreateSaveData(Kernel::HLERequestContext& ctx);
|
||||
void MountSaveData(Kernel::HLERequestContext& ctx);
|
||||
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
|
||||
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
|
||||
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenRomStorage(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSys::VirtualFile romfs;
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ void Controller_DebugPad::OnInit() {}
|
||||
void Controller_DebugPad::OnRelease() {}
|
||||
|
||||
void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -18,7 +18,7 @@ void Controller_Gesture::OnInit() {}
|
||||
void Controller_Gesture::OnRelease() {}
|
||||
|
||||
void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -20,7 +20,7 @@ void Controller_Keyboard::OnInit() {}
|
||||
void Controller_Keyboard::OnRelease() {}
|
||||
|
||||
void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -18,7 +18,7 @@ void Controller_Mouse::OnInit() {}
|
||||
void Controller_Mouse::OnRelease() {}
|
||||
|
||||
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -308,7 +308,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
const auto& last_entry =
|
||||
main_controller->npad[main_controller->common.last_entry_index];
|
||||
|
||||
main_controller->common.timestamp = Core::Timing::GetTicks();
|
||||
main_controller->common.timestamp = CoreTiming::GetTicks();
|
||||
main_controller->common.last_entry_index =
|
||||
(main_controller->common.last_entry_index + 1) % 17;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
|
||||
}
|
||||
|
||||
CommonHeader header{};
|
||||
header.timestamp = Core::Timing::GetTicks();
|
||||
header.timestamp = CoreTiming::GetTicks();
|
||||
header.total_entry_count = 17;
|
||||
header.entry_count = 0;
|
||||
header.last_entry_index = 0;
|
||||
|
||||
@@ -21,7 +21,7 @@ void Controller_Touchscreen::OnInit() {}
|
||||
void Controller_Touchscreen::OnRelease() {}
|
||||
|
||||
void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||
shared_memory.header.timestamp = Core::Timing::GetTicks();
|
||||
shared_memory.header.timestamp = CoreTiming::GetTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
@@ -48,7 +48,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||
const u64 tick = Core::Timing::GetTicks();
|
||||
const u64 tick = CoreTiming::GetTicks();
|
||||
touch_entry.delta_time = tick - last_touch;
|
||||
last_touch = tick;
|
||||
touch_entry.finger = Settings::values.touchscreen.finger;
|
||||
|
||||
@@ -19,7 +19,7 @@ void Controller_XPad::OnRelease() {}
|
||||
|
||||
void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
|
||||
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
|
||||
xpad_entry.header.timestamp = Core::Timing::GetTicks();
|
||||
xpad_entry.header.timestamp = CoreTiming::GetTicks();
|
||||
xpad_entry.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// TODO(ogniK): Find actual polling rate of hid
|
||||
constexpr u64 pad_update_ticks = Core::Timing::BASE_CLOCK_RATE / 66;
|
||||
constexpr u64 accelerometer_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 gyroscope_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
|
||||
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
|
||||
|
||||
IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
|
||||
@@ -73,13 +73,14 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
|
||||
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
|
||||
|
||||
// Register update callbacks
|
||||
pad_update_event = Core::Timing::RegisterEvent(
|
||||
"HID::UpdatePadCallback",
|
||||
[this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
|
||||
pad_update_event =
|
||||
CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
|
||||
UpdateControllers(userdata, cycles_late);
|
||||
});
|
||||
|
||||
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
|
||||
|
||||
Core::Timing::ScheduleEvent(pad_update_ticks, pad_update_event);
|
||||
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
|
||||
|
||||
ReloadInputDevices();
|
||||
}
|
||||
@@ -93,7 +94,7 @@ void IAppletResource::DeactivateController(HidController controller) {
|
||||
}
|
||||
|
||||
IAppletResource ::~IAppletResource() {
|
||||
Core::Timing::UnscheduleEvent(pad_update_event, 0);
|
||||
CoreTiming::UnscheduleEvent(pad_update_event, 0);
|
||||
}
|
||||
|
||||
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||
@@ -113,7 +114,7 @@ void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
|
||||
controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
|
||||
}
|
||||
|
||||
Core::Timing::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
|
||||
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
|
||||
}
|
||||
|
||||
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "controllers/controller_base.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ private:
|
||||
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
|
||||
|
||||
Core::Timing::EventType* pad_update_event;
|
||||
CoreTiming::EventType* pad_update_event;
|
||||
|
||||
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
|
||||
controllers{};
|
||||
|
||||
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u64>(Core::Timing::GetTicks());
|
||||
rb.PushRaw<u64>(CoreTiming::GetTicks());
|
||||
rb.PushRaw<u32>(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@ public:
|
||||
{6, nullptr, "CloseContentStorageForcibly"},
|
||||
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
|
||||
{8, nullptr, "CleanupContentMetaDatabase"},
|
||||
{9, nullptr, "ActivateContentStorage"},
|
||||
{10, nullptr, "InactivateContentStorage"},
|
||||
{11, nullptr, "ActivateContentMetaDatabase"},
|
||||
{12, nullptr, "InactivateContentMetaDatabase"},
|
||||
{9, nullptr, "OpenContentStorage2"},
|
||||
{10, nullptr, "CloseContentStorage"},
|
||||
{11, nullptr, "OpenContentMetaDatabase2"},
|
||||
{12, nullptr, "CloseContentMetaDatabase"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{19, nullptr, "LaunchApplication"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{22, nullptr, "TerminateApplication"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
@@ -96,10 +96,10 @@ public:
|
||||
{86, nullptr, "EnableApplicationCrashReport"},
|
||||
{87, nullptr, "IsApplicationCrashReportEnabled"},
|
||||
{90, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{91, nullptr, "DeprecatedLaunchApplication"},
|
||||
{92, nullptr, "GetRunningApplicationProgramId"},
|
||||
{91, nullptr, "Unknown1"},
|
||||
{92, nullptr, "Unknown2"},
|
||||
{93, nullptr, "GetMainApplicationProgramIndex"},
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{94, nullptr, "LaunchApplication2"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
@@ -163,7 +163,7 @@ public:
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
|
||||
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
|
||||
{910, nullptr, "HasApplicationRecord"},
|
||||
{910, nullptr, "Unknown3"},
|
||||
{911, nullptr, "SetPreInstalledApplication"},
|
||||
{912, nullptr, "ClearPreInstalledApplicationFlag"},
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
@@ -219,10 +219,10 @@ public:
|
||||
{2015, nullptr, "CompareSystemDeliveryInfo"},
|
||||
{2016, nullptr, "ListNotCommittedContentMeta"},
|
||||
{2017, nullptr, "CreateDownloadTask"},
|
||||
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
|
||||
{2050, nullptr, "GetApplicationRightsOnClient"},
|
||||
{2100, nullptr, "GetApplicationTerminateResult"},
|
||||
{2101, nullptr, "GetRawApplicationTerminateResult"},
|
||||
{2018, nullptr, "Unknown4"},
|
||||
{2050, nullptr, "Unknown5"},
|
||||
{2100, nullptr, "Unknown6"},
|
||||
{2101, nullptr, "Unknown7"},
|
||||
{2150, nullptr, "CreateRightsEnvironment"},
|
||||
{2151, nullptr, "DestroyRightsEnvironment"},
|
||||
{2152, nullptr, "ActivateRightsEnvironment"},
|
||||
@@ -237,10 +237,10 @@ public:
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
{2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
|
||||
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
|
||||
{2250, nullptr, "RequestReportActiveELicence"},
|
||||
{2300, nullptr, "ListEventLog"},
|
||||
{2200, nullptr, "Unknown8"},
|
||||
{2201, nullptr, "Unknown9"},
|
||||
{2250, nullptr, "Unknown10"},
|
||||
{2300, nullptr, "Unknown11"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -355,7 +355,6 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{93, nullptr, "GetRunningApplicationProgramId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -390,11 +389,6 @@ public:
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestLinkDevice"},
|
||||
{1, nullptr, "RequestCleanupAllPreInstalledApplications"},
|
||||
{2, nullptr, "RequestCleanupPreInstalledApplication"},
|
||||
{3, nullptr, "RequestSyncRights"},
|
||||
{4, nullptr, "RequestUnlinkDevice"},
|
||||
{5, nullptr, "RequestRevokeAllELicense"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -409,7 +403,7 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment "},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect) {
|
||||
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
LOG_TRACE(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
addr, offset, width, height, stride, format);
|
||||
LOG_WARNING(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
addr, offset, width, height, stride, format);
|
||||
|
||||
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
|
||||
const Tegra::FramebufferConfig framebuffer{
|
||||
|
||||
@@ -184,7 +184,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
|
||||
|
||||
IoctlGetGpuTime params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
params.gpu_time = Core::Timing::cyclesToNs(Core::Timing::GetTicks());
|
||||
params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
struct IGBPBuffer {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
@@ -21,145 +22,98 @@
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
|
||||
constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
|
||||
NVFlinger::NVFlinger() {
|
||||
// Add the different displays to the list of displays.
|
||||
displays.emplace_back(0, "Default");
|
||||
displays.emplace_back(1, "External");
|
||||
displays.emplace_back(2, "Edid");
|
||||
displays.emplace_back(3, "Internal");
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
Core::Timing::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
||||
CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
|
||||
Compose();
|
||||
Core::Timing::ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||
});
|
||||
|
||||
Core::Timing::ScheduleEvent(frame_ticks, composition_event);
|
||||
CoreTiming::ScheduleEvent(frame_ticks, composition_event);
|
||||
}
|
||||
|
||||
NVFlinger::~NVFlinger() {
|
||||
Core::Timing::UnscheduleEvent(composition_event, 0);
|
||||
CoreTiming::UnscheduleEvent(composition_event, 0);
|
||||
}
|
||||
|
||||
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||
nvdrv = std::move(instance);
|
||||
}
|
||||
|
||||
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
||||
LOG_DEBUG(Service, "Opening \"{}\" display", name);
|
||||
u64 NVFlinger::OpenDisplay(std::string_view name) {
|
||||
LOG_WARNING(Service, "Opening display {}", name);
|
||||
|
||||
// TODO(Subv): Currently we only support the Default display.
|
||||
ASSERT(name == "Default");
|
||||
|
||||
const auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.name == name; });
|
||||
if (itr == displays.end()) {
|
||||
return {};
|
||||
}
|
||||
auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.name == name; });
|
||||
|
||||
ASSERT(itr != displays.end());
|
||||
|
||||
return itr->id;
|
||||
}
|
||||
|
||||
std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||
auto* const display = FindDisplay(display_id);
|
||||
u64 NVFlinger::CreateLayer(u64 display_id) {
|
||||
auto& display = GetDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return {};
|
||||
}
|
||||
ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
|
||||
|
||||
ASSERT_MSG(display->layers.empty(), "Only one layer is supported per display at the moment");
|
||||
|
||||
const u64 layer_id = next_layer_id++;
|
||||
const u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
u64 layer_id = next_layer_id++;
|
||||
u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
|
||||
display->layers.emplace_back(layer_id, buffer_queue);
|
||||
display.layers.emplace_back(layer_id, buffer_queue);
|
||||
buffer_queues.emplace_back(std::move(buffer_queue));
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const {
|
||||
const auto* const layer = FindLayer(display_id, layer_id);
|
||||
|
||||
if (layer == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return layer->buffer_queue->GetId();
|
||||
u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
|
||||
const auto& layer = GetLayer(display_id, layer_id);
|
||||
return layer.buffer_queue->GetId();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return display->vsync_event.readable;
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) {
|
||||
return GetDisplay(display_id).vsync_event.readable;
|
||||
}
|
||||
|
||||
std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const {
|
||||
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
||||
[&](const auto& queue) { return queue->GetId() == id; });
|
||||
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
|
||||
auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
||||
[&](const auto& queue) { return queue->GetId() == id; });
|
||||
|
||||
ASSERT(itr != buffer_queues.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
Display* NVFlinger::FindDisplay(u64 display_id) {
|
||||
const auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.id == display_id; });
|
||||
Display& NVFlinger::GetDisplay(u64 display_id) {
|
||||
auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.id == display_id; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*itr;
|
||||
ASSERT(itr != displays.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
const Display* NVFlinger::FindDisplay(u64 display_id) const {
|
||||
const auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.id == display_id; });
|
||||
Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
|
||||
auto& display = GetDisplay(display_id);
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto itr = std::find_if(display.layers.begin(), display.layers.end(),
|
||||
[&](const Layer& layer) { return layer.id == layer_id; });
|
||||
|
||||
return &*itr;
|
||||
}
|
||||
|
||||
Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) {
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto itr = std::find_if(display->layers.begin(), display->layers.end(),
|
||||
[&](const Layer& layer) { return layer.id == layer_id; });
|
||||
|
||||
if (itr == display->layers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*itr;
|
||||
}
|
||||
|
||||
const Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const {
|
||||
const auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto itr = std::find_if(display->layers.begin(), display->layers.end(),
|
||||
[&](const Layer& layer) { return layer.id == layer_id; });
|
||||
|
||||
if (itr == display->layers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*itr;
|
||||
ASSERT(itr != display.layers.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
void NVFlinger::Compose() {
|
||||
@@ -191,7 +145,7 @@ void NVFlinger::Compose() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& igbp_buffer = buffer->get().igbp_buffer;
|
||||
auto& igbp_buffer = buffer->get().igbp_buffer;
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -14,7 +12,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -58,55 +56,35 @@ public:
|
||||
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
||||
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
|
||||
|
||||
/// Opens the specified display and returns the ID.
|
||||
///
|
||||
/// If an invalid display name is provided, then an empty optional is returned.
|
||||
std::optional<u64> OpenDisplay(std::string_view name);
|
||||
/// Opens the specified display and returns the id.
|
||||
u64 OpenDisplay(std::string_view name);
|
||||
|
||||
/// Creates a layer on the specified display and returns the layer ID.
|
||||
///
|
||||
/// If an invalid display ID is specified, then an empty optional is returned.
|
||||
std::optional<u64> CreateLayer(u64 display_id);
|
||||
/// Creates a layer on the specified display and returns the layer id.
|
||||
u64 CreateLayer(u64 display_id);
|
||||
|
||||
/// Finds the buffer queue ID of the specified layer in the specified display.
|
||||
///
|
||||
/// If an invalid display ID or layer ID is provided, then an empty optional is returned.
|
||||
std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const;
|
||||
/// Gets the buffer queue id of the specified layer in the specified display.
|
||||
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Gets the vsync event for the specified display.
|
||||
///
|
||||
/// If an invalid display ID is provided, then nullptr is returned.
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id);
|
||||
|
||||
/// Obtains a buffer queue identified by the ID.
|
||||
std::shared_ptr<BufferQueue> FindBufferQueue(u32 id) const;
|
||||
/// Obtains a buffer queue identified by the id.
|
||||
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
|
||||
|
||||
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
|
||||
/// finished.
|
||||
void Compose();
|
||||
|
||||
private:
|
||||
/// Finds the display identified by the specified ID.
|
||||
Display* FindDisplay(u64 display_id);
|
||||
/// Returns the display identified by the specified id.
|
||||
Display& GetDisplay(u64 display_id);
|
||||
|
||||
/// Finds the display identified by the specified ID.
|
||||
const Display* FindDisplay(u64 display_id) const;
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
Layer* FindLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
const Layer* FindLayer(u64 display_id, u64 layer_id) const;
|
||||
/// Returns the layer identified by the specified id in the desired display.
|
||||
Layer& GetLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||
|
||||
std::array<Display, 5> displays{{
|
||||
{0, "Default"},
|
||||
{1, "External"},
|
||||
{2, "Edid"},
|
||||
{3, "Internal"},
|
||||
{4, "Null"},
|
||||
}};
|
||||
std::vector<Display> displays;
|
||||
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
|
||||
|
||||
/// Id to use for the next layer that is created, this counter is shared among all displays.
|
||||
@@ -115,8 +93,8 @@ private:
|
||||
/// layers.
|
||||
u32 next_buffer_queue_id = 1;
|
||||
|
||||
/// Event that handles screen composition.
|
||||
Core::Timing::EventType* composition_event;
|
||||
/// CoreTiming event that handles screen composition.
|
||||
CoreTiming::EventType* composition_event;
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
explicit BootMode() : ServiceFramework{"pm:bm"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BootMode::GetBootMode, "GetBootMode"},
|
||||
{1, &BootMode::SetMaintenanceBoot, "SetMaintenanceBoot"},
|
||||
{1, nullptr, "SetMaintenanceBoot"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
@@ -24,19 +24,8 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(boot_mode);
|
||||
rb.Push<u32>(static_cast<u32>(SystemBootMode::Normal)); // Normal boot mode
|
||||
}
|
||||
|
||||
void SetMaintenanceBoot(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
|
||||
boot_mode = SystemBootMode::Maintenance;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
SystemBootMode boot_mode = SystemBootMode::Normal;
|
||||
};
|
||||
|
||||
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
|
||||
|
||||
@@ -9,12 +9,7 @@ class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::PM {
|
||||
|
||||
enum class SystemBootMode {
|
||||
Normal,
|
||||
Maintenance,
|
||||
};
|
||||
|
||||
enum class SystemBootMode : u32 { Normal = 0, Maintenance = 1 };
|
||||
/// Registers all PM services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ public:
|
||||
explicit PSC_C() : ServiceFramework{"psc:c"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Initialize"},
|
||||
{1, nullptr, "DispatchRequest"},
|
||||
{2, nullptr, "GetResult"},
|
||||
{3, nullptr, "GetState"},
|
||||
{4, nullptr, "Cancel"},
|
||||
{5, nullptr, "PrintModuleInformation"},
|
||||
{6, nullptr, "GetModuleInformation"},
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{6, nullptr, "Unknown7"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -39,8 +39,7 @@ public:
|
||||
{0, nullptr, "Initialize"},
|
||||
{1, nullptr, "GetRequest"},
|
||||
{2, nullptr, "Acknowledge"},
|
||||
{3, nullptr, "Finalize"},
|
||||
{4, nullptr, "AcknowledgeEx"},
|
||||
{3, nullptr, "Unknown1"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -106,8 +106,8 @@ private:
|
||||
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000};
|
||||
SteadyClockTimePoint steady_clock_time_point{
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000};
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(steady_clock_time_point);
|
||||
@@ -282,7 +282,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000, {}};
|
||||
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace Service::VI {
|
||||
|
||||
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
|
||||
constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
|
||||
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
|
||||
|
||||
struct DisplayInfo {
|
||||
/// The name of this particular display.
|
||||
@@ -525,7 +524,7 @@ private:
|
||||
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
|
||||
static_cast<u32>(transaction), flags);
|
||||
|
||||
auto buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
if (transaction == TransactionId::Connect) {
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
@@ -559,7 +558,7 @@ private:
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
auto buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
|
||||
|
||||
@@ -629,7 +628,7 @@ private:
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
||||
|
||||
const auto buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
const auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
// TODO(Subv): Find out what this actually is.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -705,14 +704,13 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
// This function currently does nothing but return a success error code in
|
||||
// the vi library itself, so do the same thing, but log out the passed in values.
|
||||
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 layer_id = rp.Pop<u64>();
|
||||
const bool visibility = rp.Pop<bool>();
|
||||
|
||||
LOG_DEBUG(Service_VI, "called, layer_id=0x{:08X}, visibility={}", layer_id, visibility);
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
|
||||
visibility);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -839,16 +837,11 @@ private:
|
||||
"(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
|
||||
unknown, display, aruid);
|
||||
|
||||
const auto layer_id = nv_flinger->CreateLayer(display);
|
||||
if (!layer_id) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
const u64 layer_id = nv_flinger->CreateLayer(display);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*layer_id);
|
||||
rb.Push(layer_id);
|
||||
}
|
||||
|
||||
void AddToLayerStack(Kernel::HLERequestContext& ctx) {
|
||||
@@ -956,16 +949,9 @@ private:
|
||||
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
|
||||
const auto display_id = nv_flinger->OpenDisplay(name);
|
||||
if (!display_id) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(*display_id);
|
||||
rb.Push<u64>(nv_flinger->OpenDisplay(name));
|
||||
}
|
||||
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1056,21 +1042,10 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
|
||||
|
||||
const auto display_id = nv_flinger->OpenDisplay(display_name);
|
||||
if (!display_id) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
const u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id);
|
||||
if (!buffer_queue_id) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
NativeWindow native_window{*buffer_queue_id};
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
@@ -1086,24 +1061,13 @@ private:
|
||||
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
|
||||
const auto layer_id = nv_flinger->CreateLayer(display_id);
|
||||
if (!layer_id) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
const u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id);
|
||||
if (!buffer_queue_id) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
NativeWindow native_window{*buffer_queue_id};
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*layer_id);
|
||||
rb.Push(layer_id);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
|
||||
@@ -1123,12 +1087,7 @@ private:
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
|
||||
|
||||
const auto vsync_event = nv_flinger->FindVsyncEvent(display_id);
|
||||
if (!vsync_event) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
const auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -74,33 +74,4 @@ void Apply() {
|
||||
Service::HID::ReloadInputDevices();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LogSetting(const std::string& name, const T& value) {
|
||||
LOG_INFO(Config, "{}: {}", name, value);
|
||||
}
|
||||
|
||||
void LogSettings() {
|
||||
LOG_INFO(Config, "yuzu Configuration:");
|
||||
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
LogSetting("System_EnableNfc", Settings::values.enable_nfc);
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
|
||||
LogSetting("System_CurrentUser", Settings::values.current_user);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index);
|
||||
LogSetting("Core_UseCpuJit", Settings::values.use_cpu_jit);
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
|
||||
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
||||
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
|
||||
LogSetting("DataStorage_NandDir", Settings::values.nand_dir);
|
||||
LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir);
|
||||
LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
|
||||
LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
|
||||
LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -391,7 +391,6 @@ struct Values {
|
||||
float resolution_factor;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_disk_shader_cache;
|
||||
bool use_accurate_gpu_emulation;
|
||||
|
||||
float bg_red;
|
||||
@@ -426,5 +425,4 @@ struct Values {
|
||||
} extern values;
|
||||
|
||||
void Apply();
|
||||
void LogSettings();
|
||||
} // namespace Settings
|
||||
|
||||
@@ -158,8 +158,6 @@ TelemetrySession::TelemetrySession() {
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
|
||||
Settings::values.use_frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseDiskShaderCache",
|
||||
Settings::values.use_disk_shader_cache);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
|
||||
Settings::values.use_accurate_gpu_emulation);
|
||||
AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
|
||||
|
||||
@@ -31,10 +31,10 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
|
||||
class ScopeInit final {
|
||||
public:
|
||||
ScopeInit() {
|
||||
Core::Timing::Init();
|
||||
CoreTiming::Init();
|
||||
}
|
||||
~ScopeInit() {
|
||||
Core::Timing::Shutdown();
|
||||
CoreTiming::Shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,37 +44,37 @@ static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0,
|
||||
expected_callback = CB_IDS[idx];
|
||||
lateness = expected_lateness;
|
||||
|
||||
// Pretend we executed X cycles of instructions.
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount() - cpu_downcount);
|
||||
Core::Timing::Advance();
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount() -
|
||||
cpu_downcount); // Pretend we executed X cycles of instructions.
|
||||
CoreTiming::Advance();
|
||||
|
||||
REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
|
||||
REQUIRE(downcount == Core::Timing::GetDowncount());
|
||||
REQUIRE(downcount == CoreTiming::GetDowncount());
|
||||
}
|
||||
|
||||
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||
ScopeInit guard;
|
||||
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
|
||||
// Enter slice 0
|
||||
Core::Timing::Advance();
|
||||
CoreTiming::Advance();
|
||||
|
||||
// D -> B -> C -> A -> E
|
||||
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(500, cb_b, CB_IDS[1]);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(800, cb_c, CB_IDS[2]);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(100, cb_d, CB_IDS[3]);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
REQUIRE(1000 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(500, cb_b, CB_IDS[1]);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(800, cb_c, CB_IDS[2]);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(100, cb_d, CB_IDS[3]);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
|
||||
AdvanceAndCheck(3, 400);
|
||||
AdvanceAndCheck(1, 300);
|
||||
@@ -86,36 +86,36 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||
TEST_CASE("CoreTiming[Threadsave]", "[core]") {
|
||||
ScopeInit guard;
|
||||
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||
|
||||
// Enter slice 0
|
||||
Core::Timing::Advance();
|
||||
CoreTiming::Advance();
|
||||
|
||||
// D -> B -> C -> A -> E
|
||||
Core::Timing::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
|
||||
CoreTiming::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
Core::Timing::ForceExceptionCheck(1000);
|
||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
|
||||
CoreTiming::ForceExceptionCheck(1000);
|
||||
REQUIRE(1000 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
Core::Timing::ForceExceptionCheck(500);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
|
||||
CoreTiming::ForceExceptionCheck(500);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
Core::Timing::ForceExceptionCheck(800);
|
||||
REQUIRE(500 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
|
||||
CoreTiming::ForceExceptionCheck(800);
|
||||
REQUIRE(500 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
Core::Timing::ForceExceptionCheck(100);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
Core::Timing::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
|
||||
CoreTiming::ForceExceptionCheck(100);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
CoreTiming::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
|
||||
// Manually force since ScheduleEventThreadsafe doesn't call it
|
||||
Core::Timing::ForceExceptionCheck(1200);
|
||||
REQUIRE(100 == Core::Timing::GetDowncount());
|
||||
CoreTiming::ForceExceptionCheck(1200);
|
||||
REQUIRE(100 == CoreTiming::GetDowncount());
|
||||
|
||||
AdvanceAndCheck(3, 400);
|
||||
AdvanceAndCheck(1, 300);
|
||||
@@ -143,42 +143,42 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
|
||||
|
||||
ScopeInit guard;
|
||||
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", FifoCallback<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", FifoCallback<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", FifoCallback<2>);
|
||||
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", FifoCallback<3>);
|
||||
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", FifoCallback<4>);
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", FifoCallback<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", FifoCallback<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", FifoCallback<2>);
|
||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", FifoCallback<3>);
|
||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", FifoCallback<4>);
|
||||
|
||||
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
||||
|
||||
// Enter slice 0
|
||||
Core::Timing::Advance();
|
||||
REQUIRE(1000 == Core::Timing::GetDowncount());
|
||||
CoreTiming::Advance();
|
||||
REQUIRE(1000 == CoreTiming::GetDowncount());
|
||||
|
||||
callbacks_ran_flags = 0;
|
||||
counter = 0;
|
||||
lateness = 0;
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
||||
Core::Timing::Advance();
|
||||
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount());
|
||||
CoreTiming::Advance();
|
||||
REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount());
|
||||
REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
|
||||
}
|
||||
|
||||
TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
|
||||
TEST_CASE("CoreTiming[PredictableLateness]", "[core]") {
|
||||
ScopeInit guard;
|
||||
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
|
||||
// Enter slice 0
|
||||
Core::Timing::Advance();
|
||||
CoreTiming::Advance();
|
||||
|
||||
Core::Timing::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||
Core::Timing::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||
CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||
CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||
|
||||
AdvanceAndCheck(0, 90, 10, -10); // (100 - 10)
|
||||
AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50);
|
||||
@@ -192,10 +192,9 @@ static void RescheduleCallback(u64 userdata, s64 cycles_late) {
|
||||
REQUIRE(reschedules >= 0);
|
||||
REQUIRE(lateness == cycles_late);
|
||||
|
||||
if (reschedules > 0) {
|
||||
Core::Timing::ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
|
||||
userdata);
|
||||
}
|
||||
if (reschedules > 0)
|
||||
CoreTiming::ScheduleEvent(1000, reinterpret_cast<CoreTiming::EventType*>(userdata),
|
||||
userdata);
|
||||
}
|
||||
} // namespace ChainSchedulingTest
|
||||
|
||||
@@ -204,35 +203,35 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
|
||||
|
||||
ScopeInit guard;
|
||||
|
||||
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
Core::Timing::EventType* cb_rs =
|
||||
Core::Timing::RegisterEvent("callbackReschedule", RescheduleCallback);
|
||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||
CoreTiming::EventType* cb_rs =
|
||||
CoreTiming::RegisterEvent("callbackReschedule", RescheduleCallback);
|
||||
|
||||
// Enter slice 0
|
||||
Core::Timing::Advance();
|
||||
CoreTiming::Advance();
|
||||
|
||||
Core::Timing::ScheduleEvent(800, cb_a, CB_IDS[0]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
Core::Timing::ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
||||
Core::Timing::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
||||
REQUIRE(800 == Core::Timing::GetDowncount());
|
||||
CoreTiming::ScheduleEvent(800, cb_a, CB_IDS[0]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||
CoreTiming::ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
||||
CoreTiming::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
||||
REQUIRE(800 == CoreTiming::GetDowncount());
|
||||
|
||||
reschedules = 3;
|
||||
AdvanceAndCheck(0, 200); // cb_a
|
||||
AdvanceAndCheck(1, 1000); // cb_b, cb_rs
|
||||
REQUIRE(2 == reschedules);
|
||||
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
||||
Core::Timing::Advance(); // cb_rs
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount());
|
||||
CoreTiming::Advance(); // cb_rs
|
||||
REQUIRE(1 == reschedules);
|
||||
REQUIRE(200 == Core::Timing::GetDowncount());
|
||||
REQUIRE(200 == CoreTiming::GetDowncount());
|
||||
|
||||
AdvanceAndCheck(2, 800); // cb_c
|
||||
|
||||
Core::Timing::AddTicks(Core::Timing::GetDowncount());
|
||||
Core::Timing::Advance(); // cb_rs
|
||||
CoreTiming::AddTicks(CoreTiming::GetDowncount());
|
||||
CoreTiming::Advance(); // cb_rs
|
||||
REQUIRE(0 == reschedules);
|
||||
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
|
||||
REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount());
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ add_library(video_core STATIC
|
||||
debug_utils/debug_utils.h
|
||||
engines/fermi_2d.cpp
|
||||
engines/fermi_2d.h
|
||||
engines/kepler_compute.cpp
|
||||
engines/kepler_compute.h
|
||||
engines/kepler_memory.cpp
|
||||
engines/kepler_memory.h
|
||||
engines/maxwell_3d.cpp
|
||||
engines/maxwell_3d.h
|
||||
engines/maxwell_compute.cpp
|
||||
engines/maxwell_compute.h
|
||||
engines/maxwell_dma.cpp
|
||||
engines/maxwell_dma.h
|
||||
engines/shader_bytecode.h
|
||||
@@ -44,8 +44,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_shader_cache.h
|
||||
renderer_opengl/gl_shader_decompiler.cpp
|
||||
renderer_opengl/gl_shader_decompiler.h
|
||||
renderer_opengl/gl_shader_disk_cache.cpp
|
||||
renderer_opengl/gl_shader_disk_cache.h
|
||||
renderer_opengl/gl_shader_gen.cpp
|
||||
renderer_opengl/gl_shader_gen.h
|
||||
renderer_opengl/gl_shader_manager.cpp
|
||||
@@ -61,35 +59,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/renderer_opengl.h
|
||||
renderer_opengl/utils.cpp
|
||||
renderer_opengl/utils.h
|
||||
shader/decode/arithmetic.cpp
|
||||
shader/decode/arithmetic_immediate.cpp
|
||||
shader/decode/bfe.cpp
|
||||
shader/decode/bfi.cpp
|
||||
shader/decode/shift.cpp
|
||||
shader/decode/arithmetic_integer.cpp
|
||||
shader/decode/arithmetic_integer_immediate.cpp
|
||||
shader/decode/arithmetic_half.cpp
|
||||
shader/decode/arithmetic_half_immediate.cpp
|
||||
shader/decode/ffma.cpp
|
||||
shader/decode/hfma2.cpp
|
||||
shader/decode/conversion.cpp
|
||||
shader/decode/memory.cpp
|
||||
shader/decode/float_set_predicate.cpp
|
||||
shader/decode/integer_set_predicate.cpp
|
||||
shader/decode/half_set_predicate.cpp
|
||||
shader/decode/predicate_set_register.cpp
|
||||
shader/decode/predicate_set_predicate.cpp
|
||||
shader/decode/register_set_predicate.cpp
|
||||
shader/decode/float_set.cpp
|
||||
shader/decode/integer_set.cpp
|
||||
shader/decode/half_set.cpp
|
||||
shader/decode/video.cpp
|
||||
shader/decode/xmad.cpp
|
||||
shader/decode/other.cpp
|
||||
shader/decode.cpp
|
||||
shader/shader_ir.cpp
|
||||
shader/shader_ir.h
|
||||
shader/track.cpp
|
||||
surface.cpp
|
||||
surface.h
|
||||
textures/astc.cpp
|
||||
@@ -104,4 +73,4 @@ add_library(video_core STATIC
|
||||
create_target_directory_groups(video_core)
|
||||
|
||||
target_link_libraries(video_core PUBLIC common core)
|
||||
target_link_libraries(video_core PRIVATE glad lz4_static)
|
||||
target_link_libraries(video_core PRIVATE glad)
|
||||
|
||||
@@ -35,10 +35,8 @@ void DmaPusher::DispatchCalls() {
|
||||
bool DmaPusher::Step() {
|
||||
if (dma_get != dma_put) {
|
||||
// Push buffer non-empty, read a word
|
||||
const auto address = gpu.MemoryManager().GpuToCpuAddress(dma_get);
|
||||
ASSERT_MSG(address, "Invalid GPU address");
|
||||
|
||||
const CommandHeader command_header{Memory::Read32(*address)};
|
||||
const CommandHeader command_header{
|
||||
Memory::Read32(*gpu.MemoryManager().GpuToCpuAddress(dma_get))};
|
||||
|
||||
dma_get += sizeof(u32);
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ private:
|
||||
u32 subchannel; ///< Current subchannel
|
||||
u32 method_count; ///< Current method count
|
||||
u32 length_pending; ///< Large NI command length pending
|
||||
bool non_incrementing; ///< Current command's NI flag
|
||||
bool non_incrementing; ///< Current command’s NI flag
|
||||
};
|
||||
|
||||
DmaState dma_state{};
|
||||
|
||||
@@ -21,9 +21,7 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
|
||||
switch (method_call.method) {
|
||||
// Trigger the surface copy on the last register write. This is blit_src_y, but this is 64-bit,
|
||||
// so trigger on the second 32-bit write.
|
||||
case FERMI2D_REG_INDEX(blit_src_y) + 1: {
|
||||
case FERMI2D_REG_INDEX(trigger): {
|
||||
HandleSurfaceCopy();
|
||||
break;
|
||||
}
|
||||
@@ -34,23 +32,55 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}",
|
||||
static_cast<u32>(regs.operation));
|
||||
|
||||
const GPUVAddr source = regs.src.Address();
|
||||
const GPUVAddr dest = regs.dst.Address();
|
||||
|
||||
// TODO(Subv): Only same-format and same-size copies are allowed for now.
|
||||
ASSERT(regs.src.format == regs.dst.format);
|
||||
ASSERT(regs.src.width * regs.src.height == regs.dst.width * regs.dst.height);
|
||||
|
||||
// TODO(Subv): Only raw copies are implemented.
|
||||
ASSERT(regs.operation == Regs::Operation::SrcCopy);
|
||||
|
||||
const u32 src_blit_x1{static_cast<u32>(regs.blit_src_x >> 32)};
|
||||
const u32 src_blit_y1{static_cast<u32>(regs.blit_src_y >> 32)};
|
||||
const u32 src_blit_x2{
|
||||
static_cast<u32>((regs.blit_src_x + (regs.blit_dst_width * regs.blit_du_dx)) >> 32)};
|
||||
const u32 src_blit_y2{
|
||||
static_cast<u32>((regs.blit_src_y + (regs.blit_dst_height * regs.blit_dv_dy)) >> 32)};
|
||||
const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source);
|
||||
const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest);
|
||||
|
||||
const MathUtil::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2};
|
||||
const MathUtil::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y,
|
||||
regs.blit_dst_x + regs.blit_dst_width,
|
||||
regs.blit_dst_y + regs.blit_dst_height};
|
||||
u32 src_bytes_per_pixel = RenderTargetBytesPerPixel(regs.src.format);
|
||||
u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
|
||||
|
||||
if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst, src_rect, dst_rect)) {
|
||||
UNIMPLEMENTED();
|
||||
if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
|
||||
// All copies here update the main memory, so mark all rasterizer states as invalid.
|
||||
Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
|
||||
|
||||
rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
|
||||
// We have to invalidate the destination region to evict any outdated surfaces from the
|
||||
// cache. We do this before actually writing the new data because the destination address
|
||||
// might contain a dirty surface that will have to be written back to memory.
|
||||
rasterizer.InvalidateRegion(dest_cpu,
|
||||
dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
|
||||
|
||||
if (regs.src.linear == regs.dst.linear) {
|
||||
// If the input layout and the output layout are the same, just perform a raw copy.
|
||||
ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
|
||||
Memory::CopyBlock(dest_cpu, source_cpu,
|
||||
src_bytes_per_pixel * regs.dst.width * regs.dst.height);
|
||||
return;
|
||||
}
|
||||
u8* src_buffer = Memory::GetPointer(source_cpu);
|
||||
u8* dst_buffer = Memory::GetPointer(dest_cpu);
|
||||
if (!regs.src.linear && regs.dst.linear) {
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
|
||||
src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
|
||||
dst_buffer, true, regs.src.BlockHeight(),
|
||||
regs.src.BlockDepth(), 0);
|
||||
} else {
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
|
||||
src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
|
||||
src_buffer, false, regs.dst.BlockHeight(),
|
||||
regs.dst.BlockDepth(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,22 +94,12 @@ public:
|
||||
|
||||
Operation operation;
|
||||
|
||||
INSERT_PADDING_WORDS(0x177);
|
||||
INSERT_PADDING_WORDS(0x9);
|
||||
|
||||
u32 blit_control;
|
||||
// TODO(Subv): This is only a guess.
|
||||
u32 trigger;
|
||||
|
||||
INSERT_PADDING_WORDS(0x8);
|
||||
|
||||
u32 blit_dst_x;
|
||||
u32 blit_dst_y;
|
||||
u32 blit_dst_width;
|
||||
u32 blit_dst_height;
|
||||
u64 blit_du_dx;
|
||||
u64 blit_dv_dy;
|
||||
u64 blit_src_x;
|
||||
u64 blit_src_y;
|
||||
|
||||
INSERT_PADDING_WORDS(0x21);
|
||||
INSERT_PADDING_WORDS(0x1A3);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
@@ -132,16 +122,7 @@ private:
|
||||
ASSERT_REG_POSITION(dst, 0x80);
|
||||
ASSERT_REG_POSITION(src, 0x8C);
|
||||
ASSERT_REG_POSITION(operation, 0xAB);
|
||||
ASSERT_REG_POSITION(blit_control, 0x223);
|
||||
ASSERT_REG_POSITION(blit_dst_x, 0x22c);
|
||||
ASSERT_REG_POSITION(blit_dst_y, 0x22d);
|
||||
ASSERT_REG_POSITION(blit_dst_width, 0x22e);
|
||||
ASSERT_REG_POSITION(blit_dst_height, 0x22f);
|
||||
ASSERT_REG_POSITION(blit_du_dx, 0x230);
|
||||
ASSERT_REG_POSITION(blit_dv_dy, 0x232);
|
||||
ASSERT_REG_POSITION(blit_src_x, 0x234);
|
||||
ASSERT_REG_POSITION(blit_src_y, 0x236);
|
||||
|
||||
ASSERT_REG_POSITION(trigger, 0xB5);
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerCompute::KeplerCompute(MemoryManager& memory_manager) : memory_manager{memory_manager} {}
|
||||
|
||||
KeplerCompute::~KeplerCompute() = default;
|
||||
|
||||
void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ASSERT_MSG(method_call.method < Regs::NUM_REGS,
|
||||
"Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
|
||||
switch (method_call.method) {
|
||||
case KEPLER_COMPUTE_REG_INDEX(launch):
|
||||
// Abort execution since compute shaders can be used to alter game memory (e.g. CUDA
|
||||
// kernels)
|
||||
UNREACHABLE_MSG("Compute shaders are not implemented");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
@@ -39,17 +39,16 @@ void KeplerMemory::ProcessData(u32 data) {
|
||||
ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported");
|
||||
ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0);
|
||||
|
||||
const GPUVAddr address = regs.dest.Address();
|
||||
const auto dest_address =
|
||||
memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
|
||||
ASSERT_MSG(dest_address, "Invalid GPU address");
|
||||
GPUVAddr address = regs.dest.Address();
|
||||
VAddr dest_address =
|
||||
*memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
|
||||
|
||||
// We have to invalidate the destination region to evict any outdated surfaces from the cache.
|
||||
// We do this before actually writing the new data because the destination address might contain
|
||||
// a dirty surface that will have to be written back to memory.
|
||||
rasterizer.InvalidateRegion(*dest_address, sizeof(u32));
|
||||
rasterizer.InvalidateRegion(dest_address, sizeof(u32));
|
||||
|
||||
Memory::Write32(*dest_address, data);
|
||||
Memory::Write32(dest_address, data);
|
||||
Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
|
||||
|
||||
state.write_offset++;
|
||||
|
||||
@@ -37,7 +37,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.viewports[viewport].depth_range_near = 0.0f;
|
||||
regs.viewports[viewport].depth_range_far = 1.0f;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -67,7 +66,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.stencil_back_func_func = Regs::ComparisonOp::Always;
|
||||
regs.stencil_back_func_mask = 0xFFFFFFFF;
|
||||
regs.stencil_back_mask = 0xFFFFFFFF;
|
||||
|
||||
// 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).
|
||||
regs.point_size = 1.0f;
|
||||
@@ -80,9 +78,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.color_mask[color_mask].B.Assign(1);
|
||||
regs.color_mask[color_mask].A.Assign(1);
|
||||
}
|
||||
|
||||
// Commercial games seem to assume this value is enabled and nouveau sets this value manually.
|
||||
regs.rt_separate_frag_data = 1;
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
@@ -273,8 +268,7 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
GPUVAddr sequence_address = regs.query.QueryAddress();
|
||||
// Since the sequence address is given as a GPU VAddr, we have to convert it to an application
|
||||
// VAddr before writing.
|
||||
const auto address = memory_manager.GpuToCpuAddress(sequence_address);
|
||||
ASSERT_MSG(address, "Invalid GPU address");
|
||||
std::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address);
|
||||
|
||||
// TODO(Subv): Support the other query units.
|
||||
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
|
||||
@@ -317,7 +311,7 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
LongQueryResult query_result{};
|
||||
query_result.value = result;
|
||||
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
|
||||
query_result.timestamp = Core::Timing::GetTicks();
|
||||
query_result.timestamp = CoreTiming::GetTicks();
|
||||
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
|
||||
}
|
||||
dirty_flags.OnMemoryWrite();
|
||||
@@ -387,14 +381,14 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
|
||||
void Maxwell3D::ProcessCBData(u32 value) {
|
||||
// Write the input value to the current const buffer at the current position.
|
||||
const GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
|
||||
GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
|
||||
ASSERT(buffer_address != 0);
|
||||
|
||||
// Don't allow writing past the end of the buffer.
|
||||
ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
|
||||
|
||||
const auto address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
|
||||
ASSERT_MSG(address, "Invalid GPU address");
|
||||
std::optional<VAddr> address =
|
||||
memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
|
||||
|
||||
Memory::Write32(*address, value);
|
||||
dirty_flags.OnMemoryWrite();
|
||||
@@ -404,11 +398,10 @@ void Maxwell3D::ProcessCBData(u32 value) {
|
||||
}
|
||||
|
||||
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
const GPUVAddr tic_base_address = regs.tic.TICAddress();
|
||||
GPUVAddr tic_base_address = regs.tic.TICAddress();
|
||||
|
||||
const GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
|
||||
const auto tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
|
||||
ASSERT_MSG(tic_address_cpu, "Invalid GPU address");
|
||||
GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
|
||||
std::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
|
||||
|
||||
Texture::TICEntry tic_entry;
|
||||
Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
@@ -417,10 +410,10 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
|
||||
"TIC versions other than BlockLinear or Pitch are unimplemented");
|
||||
|
||||
const auto r_type = tic_entry.r_type.Value();
|
||||
const auto g_type = tic_entry.g_type.Value();
|
||||
const auto b_type = tic_entry.b_type.Value();
|
||||
const auto a_type = tic_entry.a_type.Value();
|
||||
auto r_type = tic_entry.r_type.Value();
|
||||
auto g_type = tic_entry.g_type.Value();
|
||||
auto b_type = tic_entry.b_type.Value();
|
||||
auto a_type = tic_entry.a_type.Value();
|
||||
|
||||
// TODO(Subv): Different data types for separate components are not supported
|
||||
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||
@@ -429,11 +422,10 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
}
|
||||
|
||||
Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
|
||||
const GPUVAddr tsc_base_address = regs.tsc.TSCAddress();
|
||||
GPUVAddr tsc_base_address = regs.tsc.TSCAddress();
|
||||
|
||||
const GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
|
||||
const auto tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
|
||||
ASSERT_MSG(tsc_address_cpu, "Invalid GPU address");
|
||||
GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
|
||||
std::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
|
||||
|
||||
Texture::TSCEntry tsc_entry;
|
||||
Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry));
|
||||
@@ -455,10 +447,8 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
|
||||
for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
|
||||
current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
|
||||
|
||||
const auto address = memory_manager.GpuToCpuAddress(current_texture);
|
||||
ASSERT_MSG(address, "Invalid GPU address");
|
||||
|
||||
const Texture::TextureHandle tex_handle{Memory::Read32(*address)};
|
||||
Texture::TextureHandle tex_handle{
|
||||
Memory::Read32(*memory_manager.GpuToCpuAddress(current_texture))};
|
||||
|
||||
Texture::FullTextureInfo tex_info{};
|
||||
// TODO(Subv): Use the shader to determine which textures are actually accessed.
|
||||
@@ -467,16 +457,23 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
|
||||
sizeof(Texture::TextureHandle);
|
||||
|
||||
// Load the TIC data.
|
||||
auto tic_entry = GetTICEntry(tex_handle.tic_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry));
|
||||
if (tex_handle.tic_id != 0) {
|
||||
tex_info.enabled = true;
|
||||
|
||||
auto tic_entry = GetTICEntry(tex_handle.tic_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry));
|
||||
}
|
||||
|
||||
// Load the TSC data
|
||||
auto tsc_entry = GetTSCEntry(tex_handle.tsc_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry));
|
||||
if (tex_handle.tsc_id != 0) {
|
||||
auto tsc_entry = GetTSCEntry(tex_handle.tsc_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry));
|
||||
}
|
||||
|
||||
textures.push_back(tex_info);
|
||||
if (tex_info.enabled)
|
||||
textures.push_back(tex_info);
|
||||
}
|
||||
|
||||
return textures;
|
||||
@@ -488,28 +485,31 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
|
||||
auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index];
|
||||
ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
|
||||
|
||||
const GPUVAddr tex_info_address =
|
||||
tex_info_buffer.address + offset * sizeof(Texture::TextureHandle);
|
||||
GPUVAddr tex_info_address = tex_info_buffer.address + offset * sizeof(Texture::TextureHandle);
|
||||
|
||||
ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
|
||||
|
||||
const auto tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
|
||||
ASSERT_MSG(tex_address_cpu, "Invalid GPU address");
|
||||
|
||||
const Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
|
||||
std::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
|
||||
Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
|
||||
|
||||
Texture::FullTextureInfo tex_info{};
|
||||
tex_info.index = static_cast<u32>(offset);
|
||||
|
||||
// Load the TIC data.
|
||||
auto tic_entry = GetTICEntry(tex_handle.tic_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry));
|
||||
if (tex_handle.tic_id != 0) {
|
||||
tex_info.enabled = true;
|
||||
|
||||
auto tic_entry = GetTICEntry(tex_handle.tic_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry));
|
||||
}
|
||||
|
||||
// Load the TSC data
|
||||
auto tsc_entry = GetTSCEntry(tex_handle.tsc_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry));
|
||||
if (tex_handle.tsc_id != 0) {
|
||||
auto tsc_entry = GetTSCEntry(tex_handle.tsc_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry));
|
||||
}
|
||||
|
||||
return tex_info;
|
||||
}
|
||||
|
||||
28
src/video_core/engines/maxwell_compute.cpp
Normal file
28
src/video_core/engines/maxwell_compute.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
void MaxwellCompute::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ASSERT_MSG(method_call.method < Regs::NUM_REGS,
|
||||
"Invalid MaxwellCompute register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
|
||||
switch (method_call.method) {
|
||||
case MAXWELL_COMPUTE_REG_INDEX(compute): {
|
||||
LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented");
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
@@ -10,48 +10,47 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
#define KEPLER_COMPUTE_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32))
|
||||
#define MAXWELL_COMPUTE_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32))
|
||||
|
||||
class KeplerCompute final {
|
||||
class MaxwellCompute final {
|
||||
public:
|
||||
explicit KeplerCompute(MemoryManager& memory_manager);
|
||||
~KeplerCompute();
|
||||
|
||||
static constexpr std::size_t NumConstBuffers = 8;
|
||||
MaxwellCompute() = default;
|
||||
~MaxwellCompute() = default;
|
||||
|
||||
struct Regs {
|
||||
static constexpr std::size_t NUM_REGS = 0xCF8;
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0xAF);
|
||||
INSERT_PADDING_WORDS(0x281);
|
||||
|
||||
u32 launch;
|
||||
union {
|
||||
u32 compute_end;
|
||||
BitField<0, 1, u32> unknown;
|
||||
} compute;
|
||||
|
||||
INSERT_PADDING_WORDS(0xC48);
|
||||
INSERT_PADDING_WORDS(0xA76);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
|
||||
"KeplerCompute Regs has wrong size");
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
|
||||
"MaxwellCompute Regs has wrong size");
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(const GPU::MethodCall& method_call);
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \
|
||||
static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(launch, 0xAF);
|
||||
ASSERT_REG_POSITION(compute, 0x281);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
@@ -39,10 +39,8 @@ void MaxwellDMA::HandleCopy() {
|
||||
const GPUVAddr source = regs.src_address.Address();
|
||||
const GPUVAddr dest = regs.dst_address.Address();
|
||||
|
||||
const auto source_cpu = memory_manager.GpuToCpuAddress(source);
|
||||
const auto dest_cpu = memory_manager.GpuToCpuAddress(dest);
|
||||
ASSERT_MSG(source_cpu, "Invalid source GPU address");
|
||||
ASSERT_MSG(dest_cpu, "Invalid destination GPU address");
|
||||
const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source);
|
||||
const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest);
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
ASSERT(regs.exec.enable_swizzle == 0);
|
||||
@@ -66,7 +64,7 @@ void MaxwellDMA::HandleCopy() {
|
||||
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
|
||||
// y_count).
|
||||
if (!regs.exec.enable_2d) {
|
||||
Memory::CopyBlock(*dest_cpu, *source_cpu, regs.x_count);
|
||||
Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,8 +73,8 @@ void MaxwellDMA::HandleCopy() {
|
||||
// rectangle. There is no need to manually flush/invalidate the regions because
|
||||
// CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.y_count; ++line) {
|
||||
const VAddr source_line = *source_cpu + line * regs.src_pitch;
|
||||
const VAddr dest_line = *dest_cpu + line * regs.dst_pitch;
|
||||
const VAddr source_line = source_cpu + line * regs.src_pitch;
|
||||
const VAddr dest_line = dest_cpu + line * regs.dst_pitch;
|
||||
Memory::CopyBlock(dest_line, source_line, regs.x_count);
|
||||
}
|
||||
return;
|
||||
@@ -89,12 +87,12 @@ void MaxwellDMA::HandleCopy() {
|
||||
const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
|
||||
// TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
|
||||
// copying.
|
||||
rasterizer.FlushRegion(*source_cpu, src_size);
|
||||
rasterizer.FlushRegion(source_cpu, src_size);
|
||||
|
||||
// We have to invalidate the destination region to evict any outdated surfaces from the
|
||||
// cache. We do this before actually writing the new data because the destination address
|
||||
// might contain a dirty surface that will have to be written back to memory.
|
||||
rasterizer.InvalidateRegion(*dest_cpu, dst_size);
|
||||
rasterizer.InvalidateRegion(dest_cpu, dst_size);
|
||||
};
|
||||
|
||||
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
@@ -107,8 +105,8 @@ void MaxwellDMA::HandleCopy() {
|
||||
copy_size * src_bytes_per_pixel);
|
||||
|
||||
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
|
||||
regs.src_params.size_x, src_bytes_per_pixel, *source_cpu,
|
||||
*dest_cpu, regs.src_params.BlockHeight(), regs.src_params.pos_x,
|
||||
regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu,
|
||||
regs.src_params.BlockHeight(), regs.src_params.pos_x,
|
||||
regs.src_params.pos_y);
|
||||
} else {
|
||||
ASSERT(regs.dst_params.size_z == 1);
|
||||
@@ -121,7 +119,7 @@ void MaxwellDMA::HandleCopy() {
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
|
||||
src_bpp, *dest_cpu, *source_cpu, regs.dst_params.BlockHeight());
|
||||
src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ enum class SubOp : u64 {
|
||||
};
|
||||
|
||||
enum class F2iRoundingOp : u64 {
|
||||
RoundEven = 0,
|
||||
None = 0,
|
||||
Floor = 1,
|
||||
Ceil = 2,
|
||||
Trunc = 3,
|
||||
@@ -208,8 +208,6 @@ enum class UniformType : u64 {
|
||||
SignedShort = 3,
|
||||
Single = 4,
|
||||
Double = 5,
|
||||
Quad = 6,
|
||||
UnsignedQuad = 7,
|
||||
};
|
||||
|
||||
enum class StoreType : u64 {
|
||||
@@ -217,9 +215,9 @@ enum class StoreType : u64 {
|
||||
Signed8 = 1,
|
||||
Unsigned16 = 2,
|
||||
Signed16 = 3,
|
||||
Bits32 = 4,
|
||||
Bits64 = 5,
|
||||
Bits128 = 6,
|
||||
Bytes32 = 4,
|
||||
Bytes64 = 5,
|
||||
Bytes128 = 6,
|
||||
};
|
||||
|
||||
enum class IMinMaxExchange : u64 {
|
||||
@@ -399,10 +397,6 @@ struct IpaMode {
|
||||
bool operator!=(const IpaMode& a) const {
|
||||
return !operator==(a);
|
||||
}
|
||||
bool operator<(const IpaMode& a) const {
|
||||
return std::tie(interpolation_mode, sampling_mode) <
|
||||
std::tie(a.interpolation_mode, a.sampling_mode);
|
||||
}
|
||||
};
|
||||
|
||||
enum class SystemVariable : u64 {
|
||||
@@ -650,7 +644,6 @@ union Instruction {
|
||||
BitField<37, 2, HalfPrecision> precision;
|
||||
BitField<32, 1, u64> saturate;
|
||||
|
||||
BitField<31, 1, u64> negate_b;
|
||||
BitField<30, 1, u64> negate_c;
|
||||
BitField<35, 2, HalfType> type_c;
|
||||
} rr;
|
||||
@@ -786,12 +779,6 @@ union Instruction {
|
||||
BitField<44, 2, u64> unknown;
|
||||
} st_l;
|
||||
|
||||
union {
|
||||
BitField<48, 3, UniformType> type;
|
||||
BitField<46, 2, u64> cache_mode;
|
||||
BitField<20, 24, s64> immediate_offset;
|
||||
} ldg;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
@@ -981,10 +968,6 @@ union Instruction {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsComponentEnabled(std::size_t component) const {
|
||||
return ((1ULL << component) & component_mask) != 0;
|
||||
}
|
||||
} txq;
|
||||
|
||||
union {
|
||||
@@ -1252,19 +1235,11 @@ union Instruction {
|
||||
union {
|
||||
BitField<20, 14, u64> offset;
|
||||
BitField<34, 5, u64> index;
|
||||
|
||||
u64 GetOffset() const {
|
||||
return offset * 4;
|
||||
}
|
||||
} cbuf34;
|
||||
|
||||
union {
|
||||
BitField<20, 16, s64> offset;
|
||||
BitField<36, 5, u64> index;
|
||||
|
||||
s64 GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
} cbuf36;
|
||||
|
||||
// Unsure about the size of this one.
|
||||
@@ -1456,7 +1431,6 @@ public:
|
||||
PredicateSetRegister,
|
||||
RegisterSetPredicate,
|
||||
Conversion,
|
||||
Video,
|
||||
Xmad,
|
||||
Unknown,
|
||||
};
|
||||
@@ -1588,8 +1562,8 @@ private:
|
||||
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
|
||||
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
|
||||
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
|
||||
INST("01011111--------", Id::VMAD, Type::Video, "VMAD"),
|
||||
INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"),
|
||||
INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
|
||||
INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
|
||||
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
|
||||
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
|
||||
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
|
||||
|
||||
@@ -106,7 +106,7 @@ struct Header {
|
||||
} ps;
|
||||
};
|
||||
|
||||
u64 GetLocalMemorySize() const {
|
||||
u64 GetLocalMemorySize() {
|
||||
return (common1.shader_local_memory_low_size |
|
||||
(common2.shader_local_memory_high_size << 24));
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/engines/kepler_memory.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
@@ -18,8 +16,6 @@ namespace Tegra {
|
||||
u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
case PixelFormat::RGB565:
|
||||
case PixelFormat::BGRA8:
|
||||
return 4;
|
||||
default:
|
||||
return 4;
|
||||
@@ -33,7 +29,7 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
|
||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
|
||||
kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager);
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
|
||||
kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
|
||||
}
|
||||
@@ -128,36 +124,9 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
|
||||
// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
|
||||
// So the values you see in docs might be multiplied by 4.
|
||||
enum class BufferMethods {
|
||||
BindObject = 0x0,
|
||||
Nop = 0x2,
|
||||
SemaphoreAddressHigh = 0x4,
|
||||
SemaphoreAddressLow = 0x5,
|
||||
SemaphoreSequence = 0x6,
|
||||
SemaphoreTrigger = 0x7,
|
||||
NotifyIntr = 0x8,
|
||||
WrcacheFlush = 0x9,
|
||||
Unk28 = 0xA,
|
||||
Unk2c = 0xB,
|
||||
RefCnt = 0x14,
|
||||
SemaphoreAcquire = 0x1A,
|
||||
SemaphoreRelease = 0x1B,
|
||||
Unk70 = 0x1C,
|
||||
Unk74 = 0x1D,
|
||||
Unk78 = 0x1E,
|
||||
Unk7c = 0x1F,
|
||||
Yield = 0x20,
|
||||
NonPullerMethods = 0x40,
|
||||
};
|
||||
|
||||
enum class GpuSemaphoreOperation {
|
||||
AcquireEqual = 0x1,
|
||||
WriteLong = 0x2,
|
||||
AcquireGequal = 0x4,
|
||||
AcquireMask = 0x8,
|
||||
BindObject = 0,
|
||||
CountBufferMethods = 0x40,
|
||||
};
|
||||
|
||||
void GPU::CallMethod(const MethodCall& method_call) {
|
||||
@@ -166,78 +135,20 @@ void GPU::CallMethod(const MethodCall& method_call) {
|
||||
|
||||
ASSERT(method_call.subchannel < bound_engines.size());
|
||||
|
||||
if (ExecuteMethodOnEngine(method_call)) {
|
||||
CallEngineMethod(method_call);
|
||||
} else {
|
||||
CallPullerMethod(method_call);
|
||||
if (method_call.method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
|
||||
method_call.argument);
|
||||
bound_engines[method_call.subchannel] = static_cast<EngineID>(method_call.argument);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool GPU::ExecuteMethodOnEngine(const MethodCall& method_call) {
|
||||
const auto method = static_cast<BufferMethods>(method_call.method);
|
||||
return method >= BufferMethods::NonPullerMethods;
|
||||
}
|
||||
if (method_call.method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
|
||||
// TODO(Subv): Research and implement these methods.
|
||||
LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
void GPU::CallPullerMethod(const MethodCall& method_call) {
|
||||
regs.reg_array[method_call.method] = method_call.argument;
|
||||
const auto method = static_cast<BufferMethods>(method_call.method);
|
||||
|
||||
switch (method) {
|
||||
case BufferMethods::BindObject: {
|
||||
ProcessBindMethod(method_call);
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Nop:
|
||||
case BufferMethods::SemaphoreAddressHigh:
|
||||
case BufferMethods::SemaphoreAddressLow:
|
||||
case BufferMethods::SemaphoreSequence:
|
||||
case BufferMethods::RefCnt:
|
||||
break;
|
||||
case BufferMethods::SemaphoreTrigger: {
|
||||
ProcessSemaphoreTriggerMethod();
|
||||
break;
|
||||
}
|
||||
case BufferMethods::NotifyIntr: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::WrcacheFlush: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method WrcacheFlush not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Unk28: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Unk2c: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method Unk2c not implemented");
|
||||
break;
|
||||
}
|
||||
case BufferMethods::SemaphoreAcquire: {
|
||||
ProcessSemaphoreAcquire();
|
||||
break;
|
||||
}
|
||||
case BufferMethods::SemaphoreRelease: {
|
||||
ProcessSemaphoreRelease();
|
||||
break;
|
||||
}
|
||||
case BufferMethods::Yield: {
|
||||
// TODO(Kmather73): Research and implement this method.
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented",
|
||||
static_cast<u32>(method));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::CallEngineMethod(const MethodCall& method_call) {
|
||||
const EngineID engine = bound_engines[method_call.subchannel];
|
||||
|
||||
switch (engine) {
|
||||
@@ -247,8 +158,8 @@ void GPU::CallEngineMethod(const MethodCall& method_call) {
|
||||
case EngineID::MAXWELL_B:
|
||||
maxwell_3d->CallMethod(method_call);
|
||||
break;
|
||||
case EngineID::KEPLER_COMPUTE_B:
|
||||
kepler_compute->CallMethod(method_call);
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->CallMethod(method_call);
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
maxwell_dma->CallMethod(method_call);
|
||||
@@ -261,76 +172,4 @@ void GPU::CallEngineMethod(const MethodCall& method_call) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::ProcessBindMethod(const MethodCall& method_call) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
|
||||
method_call.argument);
|
||||
bound_engines[method_call.subchannel] = static_cast<EngineID>(method_call.argument);
|
||||
}
|
||||
|
||||
void GPU::ProcessSemaphoreTriggerMethod() {
|
||||
const auto semaphoreOperationMask = 0xF;
|
||||
const auto op =
|
||||
static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
|
||||
if (op == GpuSemaphoreOperation::WriteLong) {
|
||||
auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
struct Block {
|
||||
u32 sequence;
|
||||
u32 zeros = 0;
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
Block block{};
|
||||
block.sequence = regs.semaphore_sequence;
|
||||
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
|
||||
// CoreTiming
|
||||
block.timestamp = Core::Timing::GetTicks();
|
||||
Memory::WriteBlock(*address, &block, sizeof(block));
|
||||
} else {
|
||||
const auto address =
|
||||
memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
const u32 word = Memory::Read32(*address);
|
||||
if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
|
||||
(op == GpuSemaphoreOperation::AcquireGequal &&
|
||||
static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
|
||||
(op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) {
|
||||
// Nothing to do in this case
|
||||
} else {
|
||||
regs.acquire_source = true;
|
||||
regs.acquire_value = regs.semaphore_sequence;
|
||||
if (op == GpuSemaphoreOperation::AcquireEqual) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_mode = false;
|
||||
} else if (op == GpuSemaphoreOperation::AcquireGequal) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_mode = true;
|
||||
} else if (op == GpuSemaphoreOperation::AcquireMask) {
|
||||
// TODO(kemathe) The acquire mask operation waits for a value that, ANDed with
|
||||
// semaphore_sequence, gives a non-0 result
|
||||
LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented");
|
||||
} else {
|
||||
LOG_ERROR(HW_GPU, "Invalid semaphore operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::ProcessSemaphoreRelease() {
|
||||
const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
Memory::Write32(*address, regs.semaphore_release);
|
||||
}
|
||||
|
||||
void GPU::ProcessSemaphoreAcquire() {
|
||||
const auto address = memory_manager->GpuToCpuAddress(regs.smaphore_address.SmaphoreAddress());
|
||||
const u32 word = Memory::Read32(*address);
|
||||
const auto value = regs.semaphore_acquire;
|
||||
if (word != value) {
|
||||
regs.acquire_active = true;
|
||||
regs.acquire_value = value;
|
||||
// TODO(kemathe73) figure out how to do the acquire_timeout
|
||||
regs.acquire_mode = false;
|
||||
regs.acquire_source = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -80,8 +80,6 @@ class DebugContext;
|
||||
struct FramebufferConfig {
|
||||
enum class PixelFormat : u32 {
|
||||
ABGR8 = 1,
|
||||
RGB565 = 4,
|
||||
BGRA8 = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -104,15 +102,15 @@ struct FramebufferConfig {
|
||||
namespace Engines {
|
||||
class Fermi2D;
|
||||
class Maxwell3D;
|
||||
class MaxwellCompute;
|
||||
class MaxwellDMA;
|
||||
class KeplerCompute;
|
||||
class KeplerMemory;
|
||||
} // namespace Engines
|
||||
|
||||
enum class EngineID {
|
||||
FERMI_TWOD_A = 0x902D, // 2D Engine
|
||||
MAXWELL_B = 0xB197, // 3D Engine
|
||||
KEPLER_COMPUTE_B = 0xB1C0,
|
||||
MAXWELL_COMPUTE_B = 0xB1C0,
|
||||
KEPLER_INLINE_TO_MEMORY_B = 0xA140,
|
||||
MAXWELL_DMA_COPY_A = 0xB0B5,
|
||||
};
|
||||
@@ -158,46 +156,6 @@ public:
|
||||
/// Returns a const reference to the GPU DMA pusher.
|
||||
const Tegra::DmaPusher& DmaPusher() const;
|
||||
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0x100;
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0x4);
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr SmaphoreAddress() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} smaphore_address;
|
||||
|
||||
u32 semaphore_sequence;
|
||||
u32 semaphore_trigger;
|
||||
INSERT_PADDING_WORDS(0xC);
|
||||
|
||||
// The puser and the puller share the reference counter, the pusher only has read
|
||||
// access
|
||||
u32 reference_count;
|
||||
INSERT_PADDING_WORDS(0x5);
|
||||
|
||||
u32 semaphore_acquire;
|
||||
u32 semaphore_release;
|
||||
INSERT_PADDING_WORDS(0xE4);
|
||||
|
||||
// Puller state
|
||||
u32 acquire_mode;
|
||||
u32 acquire_source;
|
||||
u32 acquire_active;
|
||||
u32 acquire_timeout;
|
||||
u32 acquire_value;
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
private:
|
||||
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
|
||||
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
||||
@@ -210,42 +168,11 @@ private:
|
||||
/// 2D engine
|
||||
std::unique_ptr<Engines::Fermi2D> fermi_2d;
|
||||
/// Compute engine
|
||||
std::unique_ptr<Engines::KeplerCompute> kepler_compute;
|
||||
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
|
||||
/// DMA engine
|
||||
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
|
||||
/// Inline memory engine
|
||||
std::unique_ptr<Engines::KeplerMemory> kepler_memory;
|
||||
|
||||
void ProcessBindMethod(const MethodCall& method_call);
|
||||
void ProcessSemaphoreTriggerMethod();
|
||||
void ProcessSemaphoreRelease();
|
||||
void ProcessSemaphoreAcquire();
|
||||
|
||||
// Calls a GPU puller method.
|
||||
void CallPullerMethod(const MethodCall& method_call);
|
||||
// Calls a GPU engine method.
|
||||
void CallEngineMethod(const MethodCall& method_call);
|
||||
// Determines where the method should be executed.
|
||||
bool ExecuteMethodOnEngine(const MethodCall& method_call);
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(GPU::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(smaphore_address, 0x4);
|
||||
ASSERT_REG_POSITION(semaphore_sequence, 0x6);
|
||||
ASSERT_REG_POSITION(semaphore_trigger, 0x7);
|
||||
ASSERT_REG_POSITION(reference_count, 0x14);
|
||||
ASSERT_REG_POSITION(semaphore_acquire, 0x1A);
|
||||
ASSERT_REG_POSITION(semaphore_release, 0x1B);
|
||||
|
||||
ASSERT_REG_POSITION(acquire_mode, 0x100);
|
||||
ASSERT_REG_POSITION(acquire_source, 0x101);
|
||||
ASSERT_REG_POSITION(acquire_active, 0x102);
|
||||
ASSERT_REG_POSITION(acquire_timeout, 0x103);
|
||||
ASSERT_REG_POSITION(acquire_value, 0x104);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -154,8 +154,7 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
|
||||
const VAddr base_addr{PageSlot(gpu_addr)};
|
||||
|
||||
if (base_addr == static_cast<u64>(PageStatus::Allocated) ||
|
||||
base_addr == static_cast<u64>(PageStatus::Unmapped) ||
|
||||
base_addr == static_cast<u64>(PageStatus::Reserved)) {
|
||||
base_addr == static_cast<u64>(PageStatus::Unmapped)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
@@ -46,9 +45,12 @@ public:
|
||||
|
||||
/// Attempt to use a faster method to perform a surface copy
|
||||
virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst,
|
||||
const MathUtil::Rectangle<u32>& src_rect,
|
||||
const MathUtil::Rectangle<u32>& dst_rect) {
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to fill a region
|
||||
virtual bool AccelerateFill(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -64,9 +66,5 @@ public:
|
||||
|
||||
/// Increase/decrease the number of object in pages touching the specified region
|
||||
virtual void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {}
|
||||
|
||||
/// Initialize disk cached resources for the game being emulated
|
||||
virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
|
||||
const DiskResourceLoadCallback& callback = {}) {}
|
||||
};
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -19,8 +19,7 @@ OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
|
||||
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
|
||||
std::size_t alignment, bool cache) {
|
||||
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
|
||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
||||
ASSERT_MSG(cpu_addr, "Invalid GPU address");
|
||||
const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
||||
|
||||
// Cache management is a big overhead, so only cache entries with a given size.
|
||||
// TODO: Figure out which size is the best for given games.
|
||||
|
||||
@@ -4,13 +4,8 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/renderer_opengl/gl_global_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@@ -23,72 +18,7 @@ CachedGlobalRegion::CachedGlobalRegion(VAddr addr, u32 size) : addr{addr}, size{
|
||||
LabelGLObject(GL_BUFFER, buffer.handle, addr, "GlobalMemory");
|
||||
}
|
||||
|
||||
void CachedGlobalRegion::Reload(u32 size_) {
|
||||
constexpr auto max_size = static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize);
|
||||
|
||||
size = size_;
|
||||
if (size > max_size) {
|
||||
size = max_size;
|
||||
LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the expected size {}!", size_,
|
||||
max_size);
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, size, Memory::GetPointer(addr), GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(VAddr addr, u32 size) const {
|
||||
const auto search{reserve.find(addr)};
|
||||
if (search == reserve.end()) {
|
||||
return {};
|
||||
}
|
||||
return search->second;
|
||||
}
|
||||
|
||||
GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(VAddr addr, u32 size) {
|
||||
GlobalRegion region{TryGetReservedGlobalRegion(addr, size)};
|
||||
if (!region) {
|
||||
// No reserved surface available, create a new one and reserve it
|
||||
region = std::make_shared<CachedGlobalRegion>(addr, size);
|
||||
ReserveGlobalRegion(region);
|
||||
}
|
||||
region->Reload(size);
|
||||
return region;
|
||||
}
|
||||
|
||||
void GlobalRegionCacheOpenGL::ReserveGlobalRegion(const GlobalRegion& region) {
|
||||
reserve[region->GetAddr()] = region;
|
||||
}
|
||||
|
||||
GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
|
||||
: RasterizerCache{rasterizer} {}
|
||||
|
||||
GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
|
||||
const GLShader::GlobalMemoryEntry& global_region,
|
||||
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
|
||||
|
||||
auto& gpu{Core::System::GetInstance().GPU()};
|
||||
const auto cbufs = gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)];
|
||||
const auto cbuf_addr = gpu.MemoryManager().GpuToCpuAddress(
|
||||
cbufs.const_buffers[global_region.GetCbufIndex()].address + global_region.GetCbufOffset());
|
||||
ASSERT(cbuf_addr);
|
||||
|
||||
const auto actual_addr_gpu = Memory::Read64(*cbuf_addr);
|
||||
const auto size = Memory::Read32(*cbuf_addr + 8);
|
||||
const auto actual_addr = gpu.MemoryManager().GpuToCpuAddress(actual_addr_gpu);
|
||||
ASSERT(actual_addr);
|
||||
|
||||
// Look up global region in the cache based on address
|
||||
GlobalRegion region = TryGet(*actual_addr);
|
||||
|
||||
if (!region) {
|
||||
// No global region found - create a new one
|
||||
region = GetUncachedGlobalRegion(*actual_addr, size);
|
||||
Register(region);
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
@@ -44,9 +40,6 @@ public:
|
||||
return buffer.handle;
|
||||
}
|
||||
|
||||
/// Reloads the global region from guest memory
|
||||
void Reload(u32 size_);
|
||||
|
||||
// TODO(Rodrigo): When global memory is written (STG), implement flushing
|
||||
void Flush() override {
|
||||
UNIMPLEMENTED();
|
||||
@@ -62,17 +55,6 @@ private:
|
||||
class GlobalRegionCacheOpenGL final : public RasterizerCache<GlobalRegion> {
|
||||
public:
|
||||
explicit GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer);
|
||||
|
||||
/// Gets the current specified shader stage program
|
||||
GlobalRegion GetGlobalRegion(const GLShader::GlobalMemoryEntry& descriptor,
|
||||
Tegra::Engines::Maxwell3D::Regs::ShaderStage stage);
|
||||
|
||||
private:
|
||||
GlobalRegion TryGetReservedGlobalRegion(VAddr addr, u32 size) const;
|
||||
GlobalRegion GetUncachedGlobalRegion(VAddr addr, u32 size);
|
||||
void ReserveGlobalRegion(const GlobalRegion& region);
|
||||
|
||||
std::unordered_map<VAddr, GlobalRegion> reserve;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -46,9 +46,7 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
|
||||
auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
|
||||
|
||||
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
|
||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
||||
ASSERT_MSG(cpu_addr, "Invalid GPU address");
|
||||
|
||||
const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
||||
const u8* source{Memory::GetPointer(*cpu_addr)};
|
||||
|
||||
for (u32 primitive = 0; primitive < count / 4; ++primitive) {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "core/settings.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
#include "video_core/renderer_opengl/maxwell_to_gl.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
@@ -100,9 +99,8 @@ struct FramebufferCacheKey {
|
||||
}
|
||||
};
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system,
|
||||
ScreenInfo& info)
|
||||
: res_cache{*this}, shader_cache{*this, system}, emu_window{window}, screen_info{info},
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
|
||||
: res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info},
|
||||
buffer_cache(*this, STREAM_BUFFER_SIZE), global_cache{*this} {
|
||||
// Create sampler objects
|
||||
for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
|
||||
@@ -299,7 +297,10 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Shader);
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
|
||||
BaseBindings base_bindings;
|
||||
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
|
||||
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
|
||||
u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage;
|
||||
u32 current_texture_bindpoint = 0;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
@@ -323,35 +324,43 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
const GLintptr offset = buffer_cache.UploadHostMemory(
|
||||
&ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
|
||||
|
||||
// Bind the emulation info buffer
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, base_bindings.cbuf, buffer_cache.GetHandle(), offset,
|
||||
static_cast<GLsizeiptr>(sizeof(ubo)));
|
||||
// Bind the buffer
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
|
||||
offset, static_cast<GLsizeiptr>(sizeof(ubo)));
|
||||
|
||||
Shader shader{shader_cache.GetStageProgram(program)};
|
||||
const auto [program_handle, next_bindings] =
|
||||
shader->GetProgramHandle(primitive_mode, base_bindings);
|
||||
|
||||
switch (program) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
case Maxwell::ShaderProgram::VertexB:
|
||||
shader_program_manager->UseProgrammableVertexShader(program_handle);
|
||||
case Maxwell::ShaderProgram::VertexB: {
|
||||
shader_program_manager->UseProgrammableVertexShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
break;
|
||||
case Maxwell::ShaderProgram::Geometry:
|
||||
shader_program_manager->UseProgrammableGeometryShader(program_handle);
|
||||
}
|
||||
case Maxwell::ShaderProgram::Geometry: {
|
||||
shader_program_manager->UseProgrammableGeometryShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
break;
|
||||
case Maxwell::ShaderProgram::Fragment:
|
||||
shader_program_manager->UseProgrammableFragmentShader(program_handle);
|
||||
}
|
||||
case Maxwell::ShaderProgram::Fragment: {
|
||||
shader_program_manager->UseProgrammableFragmentShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented shader index={}, enable={}, offset=0x{:08X}", index,
|
||||
shader_config.enable.Value(), shader_config.offset);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
|
||||
SetupConstBuffers(stage_enum, shader, program_handle, base_bindings);
|
||||
SetupGlobalRegions(stage_enum, shader, program_handle, base_bindings);
|
||||
SetupTextures(stage_enum, shader, program_handle, base_bindings);
|
||||
// Configure the const buffers for this shader stage.
|
||||
current_constbuffer_bindpoint =
|
||||
SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
|
||||
current_constbuffer_bindpoint);
|
||||
|
||||
// Configure the textures for this shader stage.
|
||||
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
|
||||
primitive_mode, current_texture_bindpoint);
|
||||
|
||||
// Workaround for Intel drivers.
|
||||
// When a clip distance is enabled but not set in the shader it crops parts of the screen
|
||||
@@ -366,8 +375,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
// VertexB was combined with VertexA, so we skip the VertexB iteration
|
||||
index++;
|
||||
}
|
||||
|
||||
base_bindings = next_bindings;
|
||||
}
|
||||
|
||||
SyncClipEnabled(clip_distances);
|
||||
@@ -449,7 +456,7 @@ static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
|
||||
return boost::make_iterator_range(map.equal_range(interval));
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {
|
||||
void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
|
||||
const u64 page_start{addr >> Memory::PAGE_BITS};
|
||||
const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS};
|
||||
|
||||
@@ -479,14 +486,9 @@ void RasterizerOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, in
|
||||
cached_pages.add({pages_interval, delta});
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
shader_cache.LoadDiskCache(stop_loading, callback);
|
||||
}
|
||||
|
||||
std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
|
||||
OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents,
|
||||
std::optional<std::size_t> single_color_target) {
|
||||
void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb,
|
||||
bool using_depth_fb, bool preserve_contents,
|
||||
std::optional<std::size_t> single_color_target) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
@@ -498,7 +500,7 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
|
||||
// Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
|
||||
// single color targets). This is done because the guest registers may not change but the
|
||||
// host framebuffer may contain different attachments
|
||||
return current_depth_stencil_usage;
|
||||
return;
|
||||
}
|
||||
current_framebuffer_config_state = fb_config_state;
|
||||
|
||||
@@ -507,7 +509,10 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
|
||||
depth_surface = res_cache.GetDepthBufferSurface(preserve_contents);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
|
||||
// TODO(bunnei): Figure out how the below register works. According to envytools, this should be
|
||||
// used to enable multiple render targets. However, it is left unset on all games that I have
|
||||
// tested.
|
||||
UNIMPLEMENTED_IF(regs.rt_separate_frag_data != 0);
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
|
||||
@@ -568,14 +573,12 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
|
||||
depth_surface->MarkAsModified(true, res_cache);
|
||||
|
||||
fbkey.zeta = depth_surface->Texture().handle;
|
||||
fbkey.stencil_enable = regs.stencil_enable &&
|
||||
depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
|
||||
fbkey.stencil_enable = regs.stencil_enable;
|
||||
}
|
||||
|
||||
SetupCachedFramebuffer(fbkey, current_state);
|
||||
SyncViewport(current_state);
|
||||
|
||||
return current_depth_stencil_usage = {static_cast<bool>(depth_surface), fbkey.stencil_enable};
|
||||
SyncViewport(current_state);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::Clear() {
|
||||
@@ -643,8 +646,10 @@ void RasterizerOpenGL::Clear() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [clear_depth, clear_stencil] = ConfigureFramebuffers(
|
||||
clear_state, use_color, use_depth || use_stencil, false, regs.clear_buffers.RT.Value());
|
||||
ScopeAcquireGLContext acquire_context{emu_window};
|
||||
|
||||
ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
|
||||
regs.clear_buffers.RT.Value());
|
||||
if (regs.clear_flags.scissor) {
|
||||
SyncScissorTest(clear_state);
|
||||
}
|
||||
@@ -659,11 +664,11 @@ void RasterizerOpenGL::Clear() {
|
||||
glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color);
|
||||
}
|
||||
|
||||
if (clear_depth && clear_stencil) {
|
||||
if (use_depth && use_stencil) {
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil);
|
||||
} else if (clear_depth) {
|
||||
} else if (use_depth) {
|
||||
glClearBufferfv(GL_DEPTH, 0, ®s.clear_depth);
|
||||
} else if (clear_stencil) {
|
||||
} else if (use_stencil) {
|
||||
glClearBufferiv(GL_STENCIL, 0, ®s.clear_stencil);
|
||||
}
|
||||
}
|
||||
@@ -676,6 +681,8 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
ScopeAcquireGLContext acquire_context{emu_window};
|
||||
|
||||
ConfigureFramebuffers(state);
|
||||
SyncColorMask();
|
||||
SyncFragmentColorClampState();
|
||||
@@ -778,11 +785,20 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst,
|
||||
const MathUtil::Rectangle<u32>& src_rect,
|
||||
const MathUtil::Rectangle<u32>& dst_rect) {
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Blits);
|
||||
res_cache.FermiCopySurface(src, dst, src_rect, dst_rect);
|
||||
|
||||
if (Settings::values.use_accurate_gpu_emulation) {
|
||||
// Skip the accelerated copy and perform a slow but more accurate copy
|
||||
return false;
|
||||
}
|
||||
|
||||
res_cache.FermiCopySurface(src, dst);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::AccelerateFill(const void* config) {
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -915,14 +931,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLuint program_handle,
|
||||
BaseBindings base_bindings) {
|
||||
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_bindpoint) {
|
||||
MICROPROFILE_SCOPE(OpenGL_UBO);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
|
||||
const auto& entries = shader->GetShaderEntries().const_buffers;
|
||||
const auto& entries = shader->GetShaderEntries().const_buffer_entries;
|
||||
|
||||
constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers;
|
||||
std::array<GLuint, max_binds> bind_buffers;
|
||||
@@ -957,7 +972,7 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
|
||||
}
|
||||
} else {
|
||||
// Buffer is accessed directly, upload just what we use
|
||||
size = used_buffer.GetSize();
|
||||
size = used_buffer.GetSize() * sizeof(float);
|
||||
}
|
||||
|
||||
// Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
|
||||
@@ -965,64 +980,75 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
|
||||
size = Common::AlignUp(size, sizeof(GLvec4));
|
||||
ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
|
||||
|
||||
const GLintptr const_buffer_offset = buffer_cache.UploadMemory(
|
||||
GLintptr const_buffer_offset = buffer_cache.UploadMemory(
|
||||
buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
|
||||
|
||||
// Now configure the bindpoint of the buffer inside the shader
|
||||
glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
|
||||
shader->GetProgramResourceIndex(used_buffer),
|
||||
current_bindpoint + bindpoint);
|
||||
|
||||
// Prepare values for multibind
|
||||
bind_buffers[bindpoint] = buffer_cache.GetHandle();
|
||||
bind_offsets[bindpoint] = const_buffer_offset;
|
||||
bind_sizes[bindpoint] = size;
|
||||
}
|
||||
|
||||
// The first binding is reserved for emulation values
|
||||
const GLuint ubo_base_binding = base_bindings.cbuf + 1;
|
||||
glBindBuffersRange(GL_UNIFORM_BUFFER, ubo_base_binding, static_cast<GLsizei>(entries.size()),
|
||||
glBindBuffersRange(GL_UNIFORM_BUFFER, current_bindpoint, static_cast<GLsizei>(entries.size()),
|
||||
bind_buffers.data(), bind_offsets.data(), bind_sizes.data());
|
||||
|
||||
return current_bindpoint + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLenum primitive_mode,
|
||||
BaseBindings base_bindings) {
|
||||
// TODO(Rodrigo): Use ARB_multi_bind here
|
||||
const auto& entries = shader->GetShaderEntries().global_memory_entries;
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < static_cast<u32>(entries.size()); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
const u32 current_bindpoint = base_bindings.gmem + bindpoint;
|
||||
const auto& region = global_cache.GetGlobalRegion(entry, stage);
|
||||
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, current_bindpoint, region->GetBufferHandle());
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings) {
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_unit) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
const auto& entries = shader->GetShaderEntries().samplers;
|
||||
const auto& entries = shader->GetShaderEntries().texture_samplers;
|
||||
|
||||
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.texture_units),
|
||||
ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
|
||||
"Exceeded the number of active textures.");
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
const auto texture = maxwell3d.GetStageTexture(stage, entry.GetOffset());
|
||||
const u32 current_bindpoint = base_bindings.sampler + bindpoint;
|
||||
const u32 current_bindpoint = current_unit + bindpoint;
|
||||
|
||||
// Bind the uniform to the sampler.
|
||||
|
||||
glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
|
||||
shader->GetUniformLocation(entry), current_bindpoint);
|
||||
|
||||
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
|
||||
|
||||
if (!texture.enabled) {
|
||||
state.texture_units[current_bindpoint].texture = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
|
||||
|
||||
Surface surface = res_cache.GetTextureSurface(texture, entry);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[current_bindpoint].texture =
|
||||
const GLuint handle =
|
||||
entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle;
|
||||
surface->UpdateSwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
|
||||
texture.tic.w_source);
|
||||
const GLenum target = entry.IsArray() ? surface->TargetLayer() : surface->Target();
|
||||
state.texture_units[current_bindpoint].texture = handle;
|
||||
state.texture_units[current_bindpoint].target = target;
|
||||
state.texture_units[current_bindpoint].swizzle.r =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.x_source);
|
||||
state.texture_units[current_bindpoint].swizzle.g =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.y_source);
|
||||
state.texture_units[current_bindpoint].swizzle.b =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.z_source);
|
||||
state.texture_units[current_bindpoint].swizzle.a =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.w_source);
|
||||
} else {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
state.texture_units[current_bindpoint].texture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return current_unit + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -34,10 +33,6 @@
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
@@ -50,8 +45,7 @@ struct FramebufferCacheKey;
|
||||
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
|
||||
public:
|
||||
explicit RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system,
|
||||
ScreenInfo& info);
|
||||
explicit RasterizerOpenGL(Core::Frontend::EmuWindow& renderer, ScreenInfo& info);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
void DrawArrays() override;
|
||||
@@ -61,15 +55,12 @@ public:
|
||||
void InvalidateRegion(VAddr addr, u64 size) override;
|
||||
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
|
||||
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst,
|
||||
const MathUtil::Rectangle<u32>& src_rect,
|
||||
const MathUtil::Rectangle<u32>& dst_rect) override;
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst) override;
|
||||
bool AccelerateFill(const void* config) override;
|
||||
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
|
||||
u32 pixel_stride) override;
|
||||
bool AccelerateDrawBatch(bool is_indexed) override;
|
||||
void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override;
|
||||
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) override;
|
||||
|
||||
/// Maximum supported size that a constbuffer can have in bytes.
|
||||
static constexpr std::size_t MaxConstbufferSize = 0x10000;
|
||||
@@ -131,25 +122,30 @@ private:
|
||||
* @param using_depth_fb If true, configure the depth/stencil framebuffer.
|
||||
* @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
|
||||
* @param single_color_target Specifies if a single color buffer target should be used.
|
||||
* @returns If depth (first) or stencil (second) are being stored in the bound zeta texture
|
||||
* (requires using_depth_fb to be true)
|
||||
*/
|
||||
std::pair<bool, bool> ConfigureFramebuffers(
|
||||
OpenGLState& current_state, bool use_color_fb = true, bool using_depth_fb = true,
|
||||
bool preserve_contents = true, std::optional<std::size_t> single_color_target = {});
|
||||
void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true,
|
||||
bool using_depth_fb = true, bool preserve_contents = true,
|
||||
std::optional<std::size_t> single_color_target = {});
|
||||
|
||||
/// Configures the current constbuffers to use for the draw command.
|
||||
void SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings);
|
||||
/**
|
||||
* Configures the current constbuffers to use for the draw command.
|
||||
* @param stage The shader stage to configure buffers for.
|
||||
* @param shader The shader object that contains the specified stage.
|
||||
* @param current_bindpoint The offset at which to start counting new buffer bindpoints.
|
||||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_bindpoint);
|
||||
|
||||
/// Configures the current global memory entries to use for the draw command.
|
||||
void SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLenum primitive_mode,
|
||||
BaseBindings base_bindings);
|
||||
|
||||
/// Configures the current textures to use for the draw command.
|
||||
void SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings);
|
||||
/**
|
||||
* Configures the current textures to use for the draw command.
|
||||
* @param stage The shader stage to configure textures for.
|
||||
* @param shader The shader object that contains the specified stage.
|
||||
* @param current_unit The offset at which to start counting unused texture units.
|
||||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_unit);
|
||||
|
||||
/// Syncs the viewport and depth range to match the guest state
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
@@ -225,7 +221,6 @@ private:
|
||||
|
||||
std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache;
|
||||
FramebufferConfigState current_framebuffer_config_state;
|
||||
std::pair<bool, bool> current_depth_stencil_usage{};
|
||||
|
||||
std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "video_core/morton.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/textures/astc.h"
|
||||
@@ -43,14 +44,14 @@ struct FormatTuple {
|
||||
bool compressed;
|
||||
};
|
||||
|
||||
static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) {
|
||||
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, max_mip_level - 1);
|
||||
static void ApplyTextureDefaults(GLenum target, u32 max_mip_level) {
|
||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, max_mip_level - 1);
|
||||
if (max_mip_level == 1) {
|
||||
glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0);
|
||||
glTexParameterf(target, GL_TEXTURE_LOD_BIAS, 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +126,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
|
||||
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
|
||||
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
|
||||
if (!params.is_tiled) {
|
||||
params.pitch = config.tic.Pitch();
|
||||
}
|
||||
params.unaligned_height = config.tic.Height();
|
||||
params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
|
||||
params.identity = SurfaceClass::Uploaded;
|
||||
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
@@ -170,7 +167,6 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
}
|
||||
|
||||
params.is_layered = SurfaceTargetIsLayered(params.target);
|
||||
params.is_array = SurfaceTargetIsArray(params.target);
|
||||
params.max_mip_level = config.tic.max_mip_level + 1;
|
||||
params.rt = {};
|
||||
|
||||
@@ -194,17 +190,10 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
|
||||
params.component_type = ComponentTypeFromRenderTarget(config.format);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
if (params.is_tiled) {
|
||||
params.width = config.width;
|
||||
} else {
|
||||
params.pitch = config.width;
|
||||
const u32 bpp = params.GetFormatBpp() / 8;
|
||||
params.width = params.pitch / bpp;
|
||||
}
|
||||
params.width = config.width;
|
||||
params.height = config.height;
|
||||
params.unaligned_height = config.height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.identity = SurfaceClass::RenderTarget;
|
||||
params.depth = 1;
|
||||
params.max_mip_level = 1;
|
||||
params.is_layered = false;
|
||||
@@ -240,7 +229,6 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.height = zeta_height;
|
||||
params.unaligned_height = zeta_height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.identity = SurfaceClass::DepthBuffer;
|
||||
params.depth = 1;
|
||||
params.max_mip_level = 1;
|
||||
params.is_layered = false;
|
||||
@@ -269,7 +257,6 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
|
||||
params.height = config.height;
|
||||
params.unaligned_height = config.height;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
params.identity = SurfaceClass::Copy;
|
||||
params.depth = 1;
|
||||
params.max_mip_level = 1;
|
||||
params.rt = {};
|
||||
@@ -437,8 +424,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerCacheOpenGL::FastCopySurface(const Surface& src_surface,
|
||||
const Surface& dst_surface) {
|
||||
static void FastCopySurface(const Surface& src_surface, const Surface& dst_surface) {
|
||||
const auto& src_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
|
||||
@@ -448,15 +434,12 @@ void RasterizerCacheOpenGL::FastCopySurface(const Surface& src_surface,
|
||||
glCopyImageSubData(src_surface->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0, 0,
|
||||
0, dst_surface->Texture().handle, SurfaceTargetToGL(dst_params.target), 0, 0,
|
||||
0, 0, width, height, 1);
|
||||
|
||||
dst_surface->MarkAsModified(true, *this);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
|
||||
void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
const GLuint copy_pbo_handle, const GLenum src_attachment,
|
||||
const GLenum dst_attachment,
|
||||
const std::size_t cubemap_face) {
|
||||
static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
const GLuint copy_pbo_handle, const GLenum src_attachment = 0,
|
||||
const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CopySurface);
|
||||
ASSERT_MSG(dst_attachment == 0, "Unimplemented");
|
||||
|
||||
@@ -536,50 +519,63 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
dst_surface->MarkAsModified(true, *this);
|
||||
}
|
||||
|
||||
CachedSurface::CachedSurface(const SurfaceParams& params)
|
||||
: params(params), gl_target(SurfaceTargetToGL(params.target)),
|
||||
cached_size_in_bytes(params.size_in_bytes) {
|
||||
texture.Create(gl_target);
|
||||
texture.Create();
|
||||
const auto& rect{params.GetRect()};
|
||||
|
||||
// TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0)
|
||||
// alternatives. This signals a bug on those functions.
|
||||
const auto width = static_cast<GLsizei>(params.MipWidth(0));
|
||||
const auto height = static_cast<GLsizei>(params.MipHeight(0));
|
||||
// Keep track of previous texture bindings
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
const auto& old_tex = cur_state.texture_units[0];
|
||||
SCOPE_EXIT({
|
||||
cur_state.texture_units[0] = old_tex;
|
||||
cur_state.Apply();
|
||||
});
|
||||
|
||||
cur_state.texture_units[0].texture = texture.handle;
|
||||
cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
|
||||
cur_state.Apply();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
|
||||
gl_internal_format = format_tuple.internal_format;
|
||||
gl_is_compressed = format_tuple.compressed;
|
||||
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
glTextureStorage1D(texture.handle, params.max_mip_level, format_tuple.internal_format,
|
||||
width);
|
||||
break;
|
||||
case SurfaceTarget::Texture2D:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
glTextureStorage2D(texture.handle, params.max_mip_level, format_tuple.internal_format,
|
||||
width, height);
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
glTextureStorage3D(texture.handle, params.max_mip_level, format_tuple.internal_format,
|
||||
width, height, params.depth);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
||||
static_cast<u32>(params.target));
|
||||
UNREACHABLE();
|
||||
glTextureStorage2D(texture.handle, params.max_mip_level, format_tuple.internal_format,
|
||||
width, height);
|
||||
if (!format_tuple.compressed) {
|
||||
// Only pre-create the texture for non-compressed textures.
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level,
|
||||
format_tuple.internal_format, rect.GetWidth());
|
||||
break;
|
||||
case SurfaceTarget::Texture2D:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level,
|
||||
format_tuple.internal_format, rect.GetWidth(), rect.GetHeight());
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
|
||||
format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
|
||||
params.depth);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
||||
static_cast<u32>(params.target));
|
||||
UNREACHABLE();
|
||||
glTexStorage2D(GL_TEXTURE_2D, params.max_mip_level, format_tuple.internal_format,
|
||||
rect.GetWidth(), rect.GetHeight());
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTextureDefaults(texture.handle, params.max_mip_level);
|
||||
ApplyTextureDefaults(SurfaceTargetToGL(params.target), params.max_mip_level);
|
||||
|
||||
OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString());
|
||||
LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
|
||||
SurfaceParams::SurfaceTargetName(params.target));
|
||||
|
||||
// Clamp size to mapped GPU memory region
|
||||
// TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
|
||||
@@ -703,20 +699,9 @@ void CachedSurface::LoadGLBuffer() {
|
||||
for (u32 i = 0; i < params.max_mip_level; i++)
|
||||
SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i);
|
||||
} else {
|
||||
const u32 bpp = params.GetFormatBpp() / 8;
|
||||
const u32 copy_size = params.width * bpp;
|
||||
if (params.pitch == copy_size) {
|
||||
std::memcpy(gl_buffer[0].data(), Memory::GetPointer(params.addr),
|
||||
params.size_in_bytes_gl);
|
||||
} else {
|
||||
const u8* start = Memory::GetPointer(params.addr);
|
||||
u8* write_to = gl_buffer[0].data();
|
||||
for (u32 h = params.height; h > 0; h--) {
|
||||
std::memcpy(write_to, start, copy_size);
|
||||
start += params.pitch;
|
||||
write_to += copy_size;
|
||||
}
|
||||
}
|
||||
const auto texture_src_data{Memory::GetPointer(params.addr)};
|
||||
const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
|
||||
gl_buffer[0].assign(texture_src_data, texture_src_data_end);
|
||||
}
|
||||
for (u32 i = 0; i < params.max_mip_level; i++) {
|
||||
ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
|
||||
@@ -745,6 +730,7 @@ void CachedSurface::FlushGLBuffer() {
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width,
|
||||
params.height);
|
||||
ASSERT(params.type != SurfaceType::Fill);
|
||||
const u8* const texture_src_data = Memory::GetPointer(params.addr);
|
||||
ASSERT(texture_src_data);
|
||||
if (params.is_tiled) {
|
||||
@@ -753,19 +739,7 @@ void CachedSurface::FlushGLBuffer() {
|
||||
|
||||
SwizzleFunc(MortonSwizzleMode::LinearToMorton, params, gl_buffer[0], 0);
|
||||
} else {
|
||||
const u32 bpp = params.GetFormatBpp() / 8;
|
||||
const u32 copy_size = params.width * bpp;
|
||||
if (params.pitch == copy_size) {
|
||||
std::memcpy(Memory::GetPointer(params.addr), gl_buffer[0].data(), GetSizeInBytes());
|
||||
} else {
|
||||
u8* start = Memory::GetPointer(params.addr);
|
||||
const u8* read_to = gl_buffer[0].data();
|
||||
for (u32 h = params.height; h > 0; h--) {
|
||||
std::memcpy(start, read_to, copy_size);
|
||||
start += params.pitch;
|
||||
read_to += copy_size;
|
||||
}
|
||||
}
|
||||
std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,50 +748,63 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
|
||||
const auto& rect{params.GetRect(mip_map)};
|
||||
|
||||
// Load data from memory to the surface
|
||||
const auto x0 = static_cast<GLint>(rect.left);
|
||||
const auto y0 = static_cast<GLint>(rect.bottom);
|
||||
auto buffer_offset =
|
||||
const GLint x0 = static_cast<GLint>(rect.left);
|
||||
const GLint y0 = static_cast<GLint>(rect.bottom);
|
||||
std::size_t buffer_offset =
|
||||
static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) +
|
||||
static_cast<std::size_t>(x0)) *
|
||||
GetBytesPerPixel(params.pixel_format);
|
||||
|
||||
const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
|
||||
const GLuint target_tex = texture.handle;
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
|
||||
const auto& old_tex = cur_state.texture_units[0];
|
||||
SCOPE_EXIT({
|
||||
cur_state.texture_units[0] = old_tex;
|
||||
cur_state.Apply();
|
||||
});
|
||||
cur_state.texture_units[0].texture = target_tex;
|
||||
cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
|
||||
cur_state.Apply();
|
||||
|
||||
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
|
||||
ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map)));
|
||||
|
||||
const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
|
||||
GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
if (tuple.compressed) {
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture2D:
|
||||
glCompressedTextureSubImage2D(
|
||||
texture.handle, mip_map, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)), tuple.internal_format, image_size,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
|
||||
static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
glCompressedTextureSubImage3D(
|
||||
texture.handle, mip_map, 0, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)),
|
||||
static_cast<GLsizei>(params.MipDepth(mip_map)), tuple.internal_format, image_size,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
|
||||
static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)),
|
||||
static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
break;
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
glCompressedTextureSubImage3D(
|
||||
texture.handle, mip_map, 0, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)), static_cast<GLsizei>(params.depth),
|
||||
tuple.internal_format, image_size, &gl_buffer[mip_map][buffer_offset]);
|
||||
glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
|
||||
static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)),
|
||||
static_cast<GLsizei>(params.depth), 0, image_size,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap: {
|
||||
const auto layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map));
|
||||
GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map));
|
||||
for (std::size_t face = 0; face < params.depth; ++face) {
|
||||
glCompressedTextureSubImage3D(
|
||||
texture.handle, mip_map, 0, 0, static_cast<GLint>(face),
|
||||
static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)), 1, tuple.internal_format,
|
||||
layer_size, &gl_buffer[mip_map][buffer_offset]);
|
||||
glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
|
||||
mip_map, tuple.internal_format,
|
||||
static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
|
||||
layer_size, &gl_buffer[mip_map][buffer_offset]);
|
||||
buffer_offset += layer_size;
|
||||
}
|
||||
break;
|
||||
@@ -826,43 +813,46 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
||||
static_cast<u32>(params.target));
|
||||
UNREACHABLE();
|
||||
glCompressedTextureSubImage2D(
|
||||
texture.handle, mip_map, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)), tuple.internal_format,
|
||||
static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[mip_map][buffer_offset]);
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, mip_map, tuple.internal_format,
|
||||
static_cast<GLsizei>(params.MipWidth(mip_map)),
|
||||
static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
|
||||
static_cast<GLsizei>(params.size_in_bytes_gl),
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
}
|
||||
} else {
|
||||
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
glTextureSubImage1D(texture.handle, mip_map, x0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
|
||||
glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0,
|
||||
static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
break;
|
||||
case SurfaceTarget::Texture2D:
|
||||
glTextureSubImage2D(texture.handle, mip_map, x0, y0,
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0,
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
glTextureSubImage3D(texture.handle, mip_map, x0, y0, 0,
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map),
|
||||
tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
|
||||
glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map),
|
||||
tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
|
||||
break;
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
glTextureSubImage3D(texture.handle, mip_map, x0, y0, 0,
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
|
||||
tuple.type, &gl_buffer[mip_map][buffer_offset]);
|
||||
glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
|
||||
tuple.type, &gl_buffer[mip_map][buffer_offset]);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap: {
|
||||
std::size_t start = buffer_offset;
|
||||
for (std::size_t face = 0; face < params.depth; ++face) {
|
||||
glTextureSubImage3D(texture.handle, mip_map, x0, y0, static_cast<GLint>(face),
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), 1, tuple.format,
|
||||
tuple.type, &gl_buffer[mip_map][buffer_offset]);
|
||||
glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map,
|
||||
x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
buffer_offset += params.LayerSizeGL(mip_map);
|
||||
}
|
||||
break;
|
||||
@@ -871,10 +861,9 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
||||
static_cast<u32>(params.target));
|
||||
UNREACHABLE();
|
||||
glTextureSubImage2D(texture.handle, mip_map, x0, y0,
|
||||
static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, mip_map, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[mip_map][buffer_offset]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -884,47 +873,39 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
|
||||
void CachedSurface::EnsureTextureView() {
|
||||
if (texture_view.handle != 0)
|
||||
return;
|
||||
// Compressed texture are not being created with immutable storage
|
||||
UNIMPLEMENTED_IF(gl_is_compressed);
|
||||
|
||||
const GLenum target{TargetLayer()};
|
||||
const GLuint num_layers{target == GL_TEXTURE_CUBE_MAP_ARRAY ? 6u : 1u};
|
||||
constexpr GLuint min_layer = 0;
|
||||
constexpr GLuint min_level = 0;
|
||||
|
||||
glGenTextures(1, &texture_view.handle);
|
||||
glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, min_level,
|
||||
params.max_mip_level, min_layer, num_layers);
|
||||
ApplyTextureDefaults(texture_view.handle, params.max_mip_level);
|
||||
glTextureParameteriv(texture_view.handle, GL_TEXTURE_SWIZZLE_RGBA,
|
||||
reinterpret_cast<const GLint*>(swizzle.data()));
|
||||
texture_view.Create();
|
||||
glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, 0,
|
||||
params.max_mip_level, 0, 1);
|
||||
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
const auto& old_tex = cur_state.texture_units[0];
|
||||
SCOPE_EXIT({
|
||||
cur_state.texture_units[0] = old_tex;
|
||||
cur_state.Apply();
|
||||
});
|
||||
cur_state.texture_units[0].texture = texture_view.handle;
|
||||
cur_state.texture_units[0].target = target;
|
||||
cur_state.Apply();
|
||||
|
||||
ApplyTextureDefaults(target, params.max_mip_level);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
|
||||
if (params.type == SurfaceType::Fill)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_TextureUL);
|
||||
|
||||
for (u32 i = 0; i < params.max_mip_level; i++)
|
||||
UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle);
|
||||
}
|
||||
|
||||
void CachedSurface::UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x,
|
||||
Tegra::Texture::SwizzleSource swizzle_y,
|
||||
Tegra::Texture::SwizzleSource swizzle_z,
|
||||
Tegra::Texture::SwizzleSource swizzle_w) {
|
||||
const GLenum new_x = MaxwellToGL::SwizzleSource(swizzle_x);
|
||||
const GLenum new_y = MaxwellToGL::SwizzleSource(swizzle_y);
|
||||
const GLenum new_z = MaxwellToGL::SwizzleSource(swizzle_z);
|
||||
const GLenum new_w = MaxwellToGL::SwizzleSource(swizzle_w);
|
||||
if (swizzle[0] == new_x && swizzle[1] == new_y && swizzle[2] == new_z && swizzle[3] == new_w) {
|
||||
return;
|
||||
}
|
||||
swizzle = {new_x, new_y, new_z, new_w};
|
||||
const auto swizzle_data = reinterpret_cast<const GLint*>(swizzle.data());
|
||||
glTextureParameteriv(texture.handle, GL_TEXTURE_SWIZZLE_RGBA, swizzle_data);
|
||||
if (texture_view.handle != 0) {
|
||||
glTextureParameteriv(texture_view.handle, GL_TEXTURE_SWIZZLE_RGBA, swizzle_data);
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerCacheOpenGL::RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer)
|
||||
: RasterizerCache{rasterizer} {
|
||||
read_framebuffer.Create();
|
||||
@@ -1057,161 +1038,26 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
|
||||
}
|
||||
address += layer_size;
|
||||
}
|
||||
|
||||
dst_surface->MarkAsModified(true, *this);
|
||||
}
|
||||
|
||||
static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
const MathUtil::Rectangle<u32>& src_rect,
|
||||
const MathUtil::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
|
||||
GLuint draw_fb_handle, GLenum src_attachment = 0, GLenum dst_attachment = 0,
|
||||
std::size_t cubemap_face = 0) {
|
||||
|
||||
const auto& src_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
|
||||
OpenGLState prev_state{OpenGLState::GetCurState()};
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.draw.read_framebuffer = read_fb_handle;
|
||||
state.draw.draw_framebuffer = draw_fb_handle;
|
||||
state.Apply();
|
||||
|
||||
u32 buffers{};
|
||||
|
||||
if (src_params.type == SurfaceType::ColorTexture) {
|
||||
switch (src_params.target) {
|
||||
case SurfaceTarget::Texture2D:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
glFramebufferTexture2D(
|
||||
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
|
||||
src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(
|
||||
GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
|
||||
break;
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
src_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
SurfaceTargetToGL(src_params.target),
|
||||
src_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
SurfaceTargetToGL(src_params.target), 0, 0, 0);
|
||||
break;
|
||||
default:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (dst_params.target) {
|
||||
case SurfaceTarget::Texture2D:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
glFramebufferTexture2D(
|
||||
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
|
||||
dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(
|
||||
GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
|
||||
break;
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
dst_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
|
||||
break;
|
||||
|
||||
case SurfaceTarget::Texture3D:
|
||||
glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
SurfaceTargetToGL(dst_params.target),
|
||||
dst_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
SurfaceTargetToGL(dst_params.target), 0, 0, 0);
|
||||
break;
|
||||
default:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
buffers = GL_COLOR_BUFFER_BIT;
|
||||
} else if (src_params.type == SurfaceType::Depth) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
buffers = GL_DEPTH_BUFFER_BIT;
|
||||
} else if (src_params.type == SurfaceType::DepthStencil) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
src_surface->Texture().handle, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
dst_surface->Texture().handle, 0);
|
||||
|
||||
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
}
|
||||
|
||||
glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
|
||||
dst_rect.top, dst_rect.right, dst_rect.bottom, buffers,
|
||||
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerCacheOpenGL::FermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config,
|
||||
const MathUtil::Rectangle<u32>& src_rect, const MathUtil::Rectangle<u32>& dst_rect) {
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
|
||||
|
||||
const auto& src_params = SurfaceParams::CreateForFermiCopySurface(src_config);
|
||||
const auto& dst_params = SurfaceParams::CreateForFermiCopySurface(dst_config);
|
||||
|
||||
ASSERT(src_params.width == dst_params.width);
|
||||
ASSERT(src_params.height == dst_params.height);
|
||||
ASSERT(src_params.pixel_format == dst_params.pixel_format);
|
||||
ASSERT(src_params.block_height == dst_params.block_height);
|
||||
ASSERT(src_params.is_tiled == dst_params.is_tiled);
|
||||
ASSERT(src_params.depth == dst_params.depth);
|
||||
ASSERT(src_params.depth == 1); // Currently, FastCopySurface only works with 2D surfaces
|
||||
ASSERT(src_params.target == dst_params.target);
|
||||
ASSERT(src_params.rt.index == dst_params.rt.index);
|
||||
|
||||
auto src_surface = GetSurface(src_params, true);
|
||||
auto dst_surface = GetSurface(dst_params, true);
|
||||
|
||||
BlitSurface(src_surface, dst_surface, src_rect, dst_rect, read_framebuffer.handle,
|
||||
draw_framebuffer.handle);
|
||||
|
||||
dst_surface->MarkAsModified(true, *this);
|
||||
FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
|
||||
}
|
||||
|
||||
void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user