Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1989e1b9ac | |||
| d4da52bbd9 | |||
| f5cf67140b | |||
| 19bce3685a | |||
| 27d57e0c4a | |||
| 7dc488a375 | |||
| 4a6a1aeab4 | |||
| ef27b4b7b5 | |||
| 6b2719c0bb | |||
| dc7ebc2d01 | |||
| c0c4da27d9 | |||
| fe8e5d8ae4 | |||
| ecf275887b | |||
| fbbad95845 | |||
| 2342c0d50e | |||
| bf0c929d4c | |||
| d65fa7d65c | |||
| d31156931d | |||
| b2bc7682b4 | |||
| c8261a1a57 | |||
| fd4c5463e8 | |||
| 88cb05e6e7 | |||
| e61c7e9310 | |||
| 47106ab152 | |||
| 1b6308727c | |||
| a2c2c5768f | |||
| a8508f2bc0 | |||
| 556f3a6e9a | |||
| e545c2322c | |||
| 2ef8af93aa | |||
| ad4e5c15fb | |||
| f6f0762e81 | |||
| 7f6c686d55 | |||
| ec206f7f95 | |||
| eaf60ca5d8 | |||
| 93acfbd3a5 | |||
| 9295966d26 | |||
| 9fc42fffd9 | |||
| 493f0ad904 | |||
| ba84f0988f | |||
| 9e42025e5b | |||
| 76b55c3624 | |||
| 293d4d553a | |||
| 72d4c6fee0 | |||
| 7f380f4ffa | |||
| 13b02a1414 | |||
| 26006cbd2c | |||
| af29e9d98e | |||
| bfda5ff3f6 | |||
| 5257a83ebe | |||
| 6c0eb6026b | |||
| 7fadc9c180 | |||
| 619f64d7f4 | |||
| 0f70f68fb3 | |||
| 6910ade146 | |||
| 91aa58e410 | |||
| 6d3a046caa | |||
| 54a00ee4cf | |||
| cc0694559f | |||
| bcd348f238 | |||
| c31382ced5 | |||
| 73d2d3342d | |||
| aae8c180cb | |||
| ef9920e164 | |||
| fe1238be7a | |||
| 2b58652f08 | |||
| f552d553ba | |||
| c3d0a0d627 | |||
| 63a59b9935 | |||
| 4501bd8ca9 | |||
| 20dc2e3622 | |||
| 22f58cca5e | |||
| 27e19f87c6 | |||
| 74feed372c | |||
| 3217400dd1 | |||
| 3563af2364 | |||
| 336a4f8e99 | |||
| cbea8c74de | |||
| d6ed31b9fa | |||
| 8b9a56033a | |||
| 0d85b6bfe1 | |||
| be269e21a5 | |||
| 9f2c703137 | |||
| 8f8dda2d5b | |||
| 2506f7b3a1 | |||
| f00a54f508 | |||
| eefd97e80d | |||
| b80c348b09 | |||
| 926ea5a16d | |||
| 37f1cf8cbd | |||
| 68043dd233 | |||
| 8e9a4944db | |||
| 0cb3bcfbb7 | |||
| bf9a822b87 | |||
| 7da52673d0 | |||
| e3cad7d49e |
+12
-5
@@ -151,15 +151,22 @@ if (ENABLE_SDL2)
|
||||
set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers")
|
||||
set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library")
|
||||
set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll")
|
||||
else()
|
||||
find_package(SDL2 REQUIRED)
|
||||
endif()
|
||||
|
||||
if (SDL2_FOUND)
|
||||
# TODO(yuriks): Make FindSDL2.cmake export an IMPORTED library instead
|
||||
add_library(SDL2 INTERFACE)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}")
|
||||
target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}")
|
||||
else()
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
# Some installations don't set SDL2_LIBRARIES
|
||||
if("${SDL2_LIBRARIES}" STREQUAL "")
|
||||
message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2")
|
||||
set(SDL2_LIBRARIES "SDL2::SDL2")
|
||||
endif()
|
||||
|
||||
include_directories(${SDL2_INCLUDE_DIRS})
|
||||
add_library(SDL2 INTERFACE)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
|
||||
endif()
|
||||
else()
|
||||
set(SDL2_FOUND NO)
|
||||
|
||||
-239
@@ -1,239 +0,0 @@
|
||||
|
||||
# This module defines
|
||||
# SDL2_LIBRARY, the name of the library to link against
|
||||
# SDL2_FOUND, if false, do not try to link to SDL2
|
||||
# SDL2_INCLUDE_DIR, where to find SDL.h
|
||||
# SDL2_DLL_DIR, where to find SDL2.dll if it exists
|
||||
#
|
||||
# This module responds to the the flag:
|
||||
# SDL2_BUILDING_LIBRARY
|
||||
# If this is defined, then no SDL2main will be linked in because
|
||||
# only applications need main().
|
||||
# Otherwise, it is assumed you are building an application and this
|
||||
# module will attempt to locate and set the the proper link flags
|
||||
# as part of the returned SDL2_LIBRARY variable.
|
||||
#
|
||||
# Don't forget to include SDLmain.h and SDLmain.m your project for the
|
||||
# OS X framework based version. (Other versions link to -lSDL2main which
|
||||
# this module will try to find on your behalf.) Also for OS X, this
|
||||
# module will automatically add the -framework Cocoa on your behalf.
|
||||
#
|
||||
#
|
||||
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
|
||||
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
|
||||
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
|
||||
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
|
||||
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
|
||||
# as appropriate. These values are used to generate the final SDL2_LIBRARY
|
||||
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
|
||||
#
|
||||
#
|
||||
# $SDL2DIR is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$SDL2DIR
|
||||
# used in building SDL2.
|
||||
# l.e.galup 9-20-02
|
||||
#
|
||||
# Modified by Eric Wing.
|
||||
# Added code to assist with automated building by using environmental variables
|
||||
# and providing a more controlled/consistent search behavior.
|
||||
# Added new modifications to recognize OS X frameworks and
|
||||
# additional Unix paths (FreeBSD, etc).
|
||||
# Also corrected the header search path to follow "proper" SDL guidelines.
|
||||
# Added a search for SDL2main which is needed by some platforms.
|
||||
# Added a search for threads which is needed by some platforms.
|
||||
# Added needed compile switches for MinGW.
|
||||
#
|
||||
# On OSX, this will prefer the Framework version (if found) over others.
|
||||
# People will have to manually change the cache values of
|
||||
# SDL2_LIBRARY to override this selection or set the CMake environment
|
||||
# CMAKE_INCLUDE_PATH to modify the search paths.
|
||||
#
|
||||
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
|
||||
# This needed to change because "proper" SDL convention
|
||||
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
|
||||
# reasons because not all systems place things in SDL2/ (see FreeBSD).
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2003-2009 Kitware, Inc.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License").
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# CMake - Cross Platform Makefile Generator
|
||||
# Copyright 2000-2016 Kitware, Inc.
|
||||
# Copyright 2000-2011 Insight Software Consortium
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
|
||||
# nor the names of their contributors may be used to endorse or promote
|
||||
# products derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# ------------------------------------------------------------------------------
|
||||
#
|
||||
# The above copyright and license notice applies to distributions of
|
||||
# CMake in source and binary form. Some source files contain additional
|
||||
# notices of original copyright by their contributors; see each source
|
||||
# for details. Third-party software packages supplied with CMake under
|
||||
# compatible licenses provide their own copyright notices documented in
|
||||
# corresponding subdirectories.
|
||||
#
|
||||
# ------------------------------------------------------------------------------
|
||||
#
|
||||
# CMake was initially developed by Kitware with the following sponsorship:
|
||||
#
|
||||
# * National Library of Medicine at the National Institutes of Health
|
||||
# as part of the Insight Segmentation and Registration Toolkit (ITK).
|
||||
#
|
||||
# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
|
||||
# Visualization Initiative.
|
||||
#
|
||||
# * National Alliance for Medical Image Computing (NAMIC) is funded by the
|
||||
# National Institutes of Health through the NIH Roadmap for Medical Research,
|
||||
# Grant U54 EB005149.
|
||||
#
|
||||
# * Kitware, Inc.
|
||||
#
|
||||
|
||||
message("<FindSDL2.cmake>")
|
||||
|
||||
SET(SDL2_SEARCH_PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
${SDL2_PATH}
|
||||
)
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(VC_LIB_PATH_SUFFIX lib/x64)
|
||||
else()
|
||||
set(VC_LIB_PATH_SUFFIX lib/x86)
|
||||
endif()
|
||||
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP
|
||||
NAMES SDL2
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib ${VC_LIB_PATH_SUFFIX}
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
|
||||
IF(SDL2_LIBRARY_TEMP)
|
||||
if(MSVC)
|
||||
get_filename_component(SDL2_DLL_DIR_TEMP ${SDL2_LIBRARY_TEMP} DIRECTORY)
|
||||
if(EXISTS ${SDL2_DLL_DIR_TEMP}/SDL2.dll)
|
||||
set(SDL2_DLL_DIR ${SDL2_DLL_DIR_TEMP})
|
||||
unset(SDL2_DLL_DIR_TEMP)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES include/SDL2 include
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
# Non-OS X framework versions expect you to also dynamically link to
|
||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
||||
# seem to provide SDL2main for compatibility even though they don't
|
||||
# necessarily need it.
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# SDL2 may require threads on your system.
|
||||
# The Apple build may not need an explicit flag because one of the
|
||||
# frameworks may already provide it.
|
||||
# But for non-OSX systems, I will use the CMake Threads package.
|
||||
IF(NOT APPLE)
|
||||
FIND_PACKAGE(Threads)
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# MinGW needs an additional library, mwindows
|
||||
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
|
||||
# (Actually on second look, I think it only needs one of the m* libraries.)
|
||||
IF(MINGW)
|
||||
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
|
||||
ENDIF(MINGW)
|
||||
|
||||
# For SDL2main
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(SDL2MAIN_LIBRARY)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(SDL2MAIN_LIBRARY)
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
|
||||
# CMake doesn't display the -framework Cocoa string in the UI even
|
||||
# though it actually is there if I modify a pre-used variable.
|
||||
# I think it has something to do with the CACHE STRING.
|
||||
# So I use a temporary variable until the end so I can set the
|
||||
# "real" variable in one-shot.
|
||||
IF(APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
|
||||
ENDIF(APPLE)
|
||||
|
||||
# For threads, as mentioned Apple doesn't need this.
|
||||
# In fact, there seems to be a problem if I used the Threads package
|
||||
# and try using this line, so I'm just skipping it entirely for OS X.
|
||||
IF(NOT APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# For MinGW library
|
||||
IF(MINGW)
|
||||
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(MINGW)
|
||||
|
||||
# Set the final string here so the GUI reflects the final state.
|
||||
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
|
||||
|
||||
# Unset the temp variable to INTERNAL so it is not seen in the CMake GUI
|
||||
UNSET(SDL2_LIBRARY_TEMP)
|
||||
ENDIF(SDL2_LIBRARY_TEMP)
|
||||
|
||||
message("</FindSDL2.cmake>")
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
From https://github.com/yhirose/cpp-httplib/commit/d9479bc0b12e8a1e8bce2d34da4feeef488581f3
|
||||
From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53
|
||||
|
||||
MIT License
|
||||
|
||||
|
||||
Vendored
+1750
-685
File diff suppressed because it is too large
Load Diff
@@ -5,21 +5,141 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// The Lanczos kernel
|
||||
static double Lanczos(std::size_t a, double x) {
|
||||
if (x == 0.0)
|
||||
return 1.0;
|
||||
const double px = M_PI * x;
|
||||
return a * std::sin(px) * std::sin(px / a) / (px * px);
|
||||
}
|
||||
constexpr std::array<s16, 512> curve_lut0{
|
||||
6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239,
|
||||
19412, 7093, 22, 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377,
|
||||
7472, 41, 5773, 19361, 7600, 48, 5659, 19342, 7728, 55, 5546, 19321, 7857,
|
||||
62, 5434, 19298, 7987, 69, 5323, 19273, 8118, 77, 5213, 19245, 8249, 84,
|
||||
5104, 19215, 8381, 92, 4997, 19183, 8513, 101, 4890, 19148, 8646, 109, 4785,
|
||||
19112, 8780, 118, 4681, 19073, 8914, 127, 4579, 19031, 9048, 137, 4477, 18988,
|
||||
9183, 147, 4377, 18942, 9318, 157, 4277, 18895, 9454, 168, 4179, 18845, 9590,
|
||||
179, 4083, 18793, 9726, 190, 3987, 18738, 9863, 202, 3893, 18682, 10000, 215,
|
||||
3800, 18624, 10137, 228, 3709, 18563, 10274, 241, 3618, 18500, 10411, 255, 3529,
|
||||
18436, 10549, 270, 3441, 18369, 10687, 285, 3355, 18300, 10824, 300, 3269, 18230,
|
||||
10962, 317, 3186, 18157, 11100, 334, 3103, 18082, 11238, 351, 3022, 18006, 11375,
|
||||
369, 2942, 17927, 11513, 388, 2863, 17847, 11650, 408, 2785, 17765, 11788, 428,
|
||||
2709, 17681, 11925, 449, 2635, 17595, 12062, 471, 2561, 17507, 12198, 494, 2489,
|
||||
17418, 12334, 517, 2418, 17327, 12470, 541, 2348, 17234, 12606, 566, 2280, 17140,
|
||||
12741, 592, 2213, 17044, 12876, 619, 2147, 16946, 13010, 647, 2083, 16846, 13144,
|
||||
675, 2020, 16745, 13277, 704, 1958, 16643, 13409, 735, 1897, 16539, 13541, 766,
|
||||
1838, 16434, 13673, 798, 1780, 16327, 13803, 832, 1723, 16218, 13933, 866, 1667,
|
||||
16109, 14062, 901, 1613, 15998, 14191, 937, 1560, 15885, 14318, 975, 1508, 15772,
|
||||
14445, 1013, 1457, 15657, 14571, 1052, 1407, 15540, 14695, 1093, 1359, 15423, 14819,
|
||||
1134, 1312, 15304, 14942, 1177, 1266, 15185, 15064, 1221, 1221, 15064, 15185, 1266,
|
||||
1177, 14942, 15304, 1312, 1134, 14819, 15423, 1359, 1093, 14695, 15540, 1407, 1052,
|
||||
14571, 15657, 1457, 1013, 14445, 15772, 1508, 975, 14318, 15885, 1560, 937, 14191,
|
||||
15998, 1613, 901, 14062, 16109, 1667, 866, 13933, 16218, 1723, 832, 13803, 16327,
|
||||
1780, 798, 13673, 16434, 1838, 766, 13541, 16539, 1897, 735, 13409, 16643, 1958,
|
||||
704, 13277, 16745, 2020, 675, 13144, 16846, 2083, 647, 13010, 16946, 2147, 619,
|
||||
12876, 17044, 2213, 592, 12741, 17140, 2280, 566, 12606, 17234, 2348, 541, 12470,
|
||||
17327, 2418, 517, 12334, 17418, 2489, 494, 12198, 17507, 2561, 471, 12062, 17595,
|
||||
2635, 449, 11925, 17681, 2709, 428, 11788, 17765, 2785, 408, 11650, 17847, 2863,
|
||||
388, 11513, 17927, 2942, 369, 11375, 18006, 3022, 351, 11238, 18082, 3103, 334,
|
||||
11100, 18157, 3186, 317, 10962, 18230, 3269, 300, 10824, 18300, 3355, 285, 10687,
|
||||
18369, 3441, 270, 10549, 18436, 3529, 255, 10411, 18500, 3618, 241, 10274, 18563,
|
||||
3709, 228, 10137, 18624, 3800, 215, 10000, 18682, 3893, 202, 9863, 18738, 3987,
|
||||
190, 9726, 18793, 4083, 179, 9590, 18845, 4179, 168, 9454, 18895, 4277, 157,
|
||||
9318, 18942, 4377, 147, 9183, 18988, 4477, 137, 9048, 19031, 4579, 127, 8914,
|
||||
19073, 4681, 118, 8780, 19112, 4785, 109, 8646, 19148, 4890, 101, 8513, 19183,
|
||||
4997, 92, 8381, 19215, 5104, 84, 8249, 19245, 5213, 77, 8118, 19273, 5323,
|
||||
69, 7987, 19298, 5434, 62, 7857, 19321, 5546, 55, 7728, 19342, 5659, 48,
|
||||
7600, 19361, 5773, 41, 7472, 19377, 5888, 34, 7345, 19391, 6004, 28, 7219,
|
||||
19403, 6121, 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424,
|
||||
6479, 3, 6722, 19426, 6600};
|
||||
|
||||
constexpr std::array<s16, 512> curve_lut1{
|
||||
-68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450,
|
||||
32586, 512, -36, -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454,
|
||||
1000, -69, -891, 32393, 1174, -80, -990, 32323, 1352, -92, -1084, 32244, 1536,
|
||||
-103, -1173, 32157, 1724, -115, -1258, 32061, 1919, -128, -1338, 31956, 2118, -140,
|
||||
-1414, 31844, 2322, -153, -1486, 31723, 2532, -167, -1554, 31593, 2747, -180, -1617,
|
||||
31456, 2967, -194, -1676, 31310, 3192, -209, -1732, 31157, 3422, -224, -1783, 30995,
|
||||
3657, -240, -1830, 30826, 3897, -256, -1874, 30649, 4143, -272, -1914, 30464, 4393,
|
||||
-289, -1951, 30272, 4648, -307, -1984, 30072, 4908, -325, -2014, 29866, 5172, -343,
|
||||
-2040, 29652, 5442, -362, -2063, 29431, 5716, -382, -2083, 29203, 5994, -403, -2100,
|
||||
28968, 6277, -424, -2114, 28727, 6565, -445, -2125, 28480, 6857, -468, -2133, 28226,
|
||||
7153, -490, -2139, 27966, 7453, -514, -2142, 27700, 7758, -538, -2142, 27428, 8066,
|
||||
-563, -2141, 27151, 8378, -588, -2136, 26867, 8694, -614, -2130, 26579, 9013, -641,
|
||||
-2121, 26285, 9336, -668, -2111, 25987, 9663, -696, -2098, 25683, 9993, -724, -2084,
|
||||
25375, 10326, -753, -2067, 25063, 10662, -783, -2049, 24746, 11000, -813, -2030, 24425,
|
||||
11342, -844, -2009, 24100, 11686, -875, -1986, 23771, 12033, -907, -1962, 23438, 12382,
|
||||
-939, -1937, 23103, 12733, -972, -1911, 22764, 13086, -1005, -1883, 22422, 13441, -1039,
|
||||
-1855, 22077, 13798, -1072, -1825, 21729, 14156, -1107, -1795, 21380, 14516, -1141, -1764,
|
||||
21027, 14877, -1176, -1732, 20673, 15239, -1211, -1700, 20317, 15602, -1246, -1667, 19959,
|
||||
15965, -1282, -1633, 19600, 16329, -1317, -1599, 19239, 16694, -1353, -1564, 18878, 17058,
|
||||
-1388, -1530, 18515, 17423, -1424, -1495, 18151, 17787, -1459, -1459, 17787, 18151, -1495,
|
||||
-1424, 17423, 18515, -1530, -1388, 17058, 18878, -1564, -1353, 16694, 19239, -1599, -1317,
|
||||
16329, 19600, -1633, -1282, 15965, 19959, -1667, -1246, 15602, 20317, -1700, -1211, 15239,
|
||||
20673, -1732, -1176, 14877, 21027, -1764, -1141, 14516, 21380, -1795, -1107, 14156, 21729,
|
||||
-1825, -1072, 13798, 22077, -1855, -1039, 13441, 22422, -1883, -1005, 13086, 22764, -1911,
|
||||
-972, 12733, 23103, -1937, -939, 12382, 23438, -1962, -907, 12033, 23771, -1986, -875,
|
||||
11686, 24100, -2009, -844, 11342, 24425, -2030, -813, 11000, 24746, -2049, -783, 10662,
|
||||
25063, -2067, -753, 10326, 25375, -2084, -724, 9993, 25683, -2098, -696, 9663, 25987,
|
||||
-2111, -668, 9336, 26285, -2121, -641, 9013, 26579, -2130, -614, 8694, 26867, -2136,
|
||||
-588, 8378, 27151, -2141, -563, 8066, 27428, -2142, -538, 7758, 27700, -2142, -514,
|
||||
7453, 27966, -2139, -490, 7153, 28226, -2133, -468, 6857, 28480, -2125, -445, 6565,
|
||||
28727, -2114, -424, 6277, 28968, -2100, -403, 5994, 29203, -2083, -382, 5716, 29431,
|
||||
-2063, -362, 5442, 29652, -2040, -343, 5172, 29866, -2014, -325, 4908, 30072, -1984,
|
||||
-307, 4648, 30272, -1951, -289, 4393, 30464, -1914, -272, 4143, 30649, -1874, -256,
|
||||
3897, 30826, -1830, -240, 3657, 30995, -1783, -224, 3422, 31157, -1732, -209, 3192,
|
||||
31310, -1676, -194, 2967, 31456, -1617, -180, 2747, 31593, -1554, -167, 2532, 31723,
|
||||
-1486, -153, 2322, 31844, -1414, -140, 2118, 31956, -1338, -128, 1919, 32061, -1258,
|
||||
-115, 1724, 32157, -1173, -103, 1536, 32244, -1084, -92, 1352, 32323, -990, -80,
|
||||
1174, 32393, -891, -69, 1000, 32454, -788, -58, 832, 32507, -680, -47, 669,
|
||||
32551, -568, -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630,
|
||||
-200, -5, 69, 32639, -68};
|
||||
|
||||
constexpr std::array<s16, 512> curve_lut2{
|
||||
3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811,
|
||||
26253, 3751, -42, 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169,
|
||||
4199, -54, 2338, 26130, 4354, -58, 2227, 26085, 4512, -63, 2120, 26035, 4673,
|
||||
-67, 2015, 25980, 4837, -72, 1912, 25919, 5004, -76, 1813, 25852, 5174, -81,
|
||||
1716, 25780, 5347, -87, 1622, 25704, 5522, -92, 1531, 25621, 5701, -98, 1442,
|
||||
25533, 5882, -103, 1357, 25440, 6066, -109, 1274, 25342, 6253, -115, 1193, 25239,
|
||||
6442, -121, 1115, 25131, 6635, -127, 1040, 25018, 6830, -133, 967, 24899, 7027,
|
||||
-140, 897, 24776, 7227, -146, 829, 24648, 7430, -153, 764, 24516, 7635, -159,
|
||||
701, 24379, 7842, -166, 641, 24237, 8052, -174, 583, 24091, 8264, -181, 526,
|
||||
23940, 8478, -187, 472, 23785, 8695, -194, 420, 23626, 8914, -202, 371, 23462,
|
||||
9135, -209, 324, 23295, 9358, -215, 279, 23123, 9583, -222, 236, 22948, 9809,
|
||||
-230, 194, 22769, 10038, -237, 154, 22586, 10269, -243, 117, 22399, 10501, -250,
|
||||
81, 22208, 10735, -258, 47, 22015, 10970, -265, 15, 21818, 11206, -271, -16,
|
||||
21618, 11444, -277, -44, 21415, 11684, -283, -71, 21208, 11924, -290, -97, 20999,
|
||||
12166, -296, -121, 20786, 12409, -302, -143, 20571, 12653, -306, -163, 20354, 12898,
|
||||
-311, -183, 20134, 13143, -316, -201, 19911, 13389, -321, -218, 19686, 13635, -325,
|
||||
-234, 19459, 13882, -328, -248, 19230, 14130, -332, -261, 18998, 14377, -335, -273,
|
||||
18765, 14625, -337, -284, 18531, 14873, -339, -294, 18295, 15121, -341, -302, 18057,
|
||||
15369, -341, -310, 17817, 15617, -341, -317, 17577, 15864, -340, -323, 17335, 16111,
|
||||
-340, -328, 17092, 16357, -338, -332, 16848, 16603, -336, -336, 16603, 16848, -332,
|
||||
-338, 16357, 17092, -328, -340, 16111, 17335, -323, -340, 15864, 17577, -317, -341,
|
||||
15617, 17817, -310, -341, 15369, 18057, -302, -341, 15121, 18295, -294, -339, 14873,
|
||||
18531, -284, -337, 14625, 18765, -273, -335, 14377, 18998, -261, -332, 14130, 19230,
|
||||
-248, -328, 13882, 19459, -234, -325, 13635, 19686, -218, -321, 13389, 19911, -201,
|
||||
-316, 13143, 20134, -183, -311, 12898, 20354, -163, -306, 12653, 20571, -143, -302,
|
||||
12409, 20786, -121, -296, 12166, 20999, -97, -290, 11924, 21208, -71, -283, 11684,
|
||||
21415, -44, -277, 11444, 21618, -16, -271, 11206, 21818, 15, -265, 10970, 22015,
|
||||
47, -258, 10735, 22208, 81, -250, 10501, 22399, 117, -243, 10269, 22586, 154,
|
||||
-237, 10038, 22769, 194, -230, 9809, 22948, 236, -222, 9583, 23123, 279, -215,
|
||||
9358, 23295, 324, -209, 9135, 23462, 371, -202, 8914, 23626, 420, -194, 8695,
|
||||
23785, 472, -187, 8478, 23940, 526, -181, 8264, 24091, 583, -174, 8052, 24237,
|
||||
641, -166, 7842, 24379, 701, -159, 7635, 24516, 764, -153, 7430, 24648, 829,
|
||||
-146, 7227, 24776, 897, -140, 7027, 24899, 967, -133, 6830, 25018, 1040, -127,
|
||||
6635, 25131, 1115, -121, 6442, 25239, 1193, -115, 6253, 25342, 1274, -109, 6066,
|
||||
25440, 1357, -103, 5882, 25533, 1442, -98, 5701, 25621, 1531, -92, 5522, 25704,
|
||||
1622, -87, 5347, 25780, 1716, -81, 5174, 25852, 1813, -76, 5004, 25919, 1912,
|
||||
-72, 4837, 25980, 2015, -67, 4673, 26035, 2120, -63, 4512, 26085, 2227, -58,
|
||||
4354, 26130, 2338, -54, 4199, 26169, 2451, -50, 4046, 26202, 2568, -46, 3897,
|
||||
26230, 2688, -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281,
|
||||
3064, -32, 3329, 26287, 3195};
|
||||
|
||||
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) {
|
||||
if (input.size() < 2)
|
||||
@@ -27,43 +147,51 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
|
||||
|
||||
if (ratio <= 0) {
|
||||
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
|
||||
ratio = 1.0;
|
||||
return input;
|
||||
}
|
||||
|
||||
if (ratio != state.current_ratio) {
|
||||
const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio);
|
||||
state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3);
|
||||
state.current_ratio = ratio;
|
||||
}
|
||||
state.nyquist.Process(input);
|
||||
const s32 step{static_cast<s32>(ratio * 0x8000)};
|
||||
const std::array<s16, 512>& lut = [step] {
|
||||
if (step > 0xaaaa) {
|
||||
return curve_lut0;
|
||||
}
|
||||
if (step <= 0x8000) {
|
||||
return curve_lut1;
|
||||
}
|
||||
return curve_lut2;
|
||||
}();
|
||||
|
||||
constexpr std::size_t taps = InterpolationState::lanczos_taps;
|
||||
const std::size_t num_frames = input.size() / 2;
|
||||
const std::size_t num_frames{input.size() / 2};
|
||||
|
||||
std::vector<s16> output;
|
||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + 4));
|
||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + InterpolationState::taps));
|
||||
|
||||
double& pos = state.position;
|
||||
auto& h = state.history;
|
||||
for (std::size_t i = 0; i < num_frames; ++i) {
|
||||
std::rotate(h.begin(), h.end() - 1, h.end());
|
||||
h[0][0] = input[i * 2 + 0];
|
||||
h[0][1] = input[i * 2 + 1];
|
||||
for (std::size_t frame{}; frame < num_frames; ++frame) {
|
||||
const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
|
||||
|
||||
while (pos <= 1.0) {
|
||||
double l = 0.0;
|
||||
double r = 0.0;
|
||||
for (std::size_t j = 0; j < h.size(); j++) {
|
||||
const double lanczos_calc = Lanczos(taps, pos + j - taps + 1);
|
||||
l += lanczos_calc * h[j][0];
|
||||
r += lanczos_calc * h[j][1];
|
||||
}
|
||||
output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0)));
|
||||
output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0)));
|
||||
std::rotate(state.history.begin(), state.history.end() - 1, state.history.end());
|
||||
state.history[0][0] = input[frame * 2 + 0];
|
||||
state.history[0][1] = input[frame * 2 + 1];
|
||||
|
||||
pos += ratio;
|
||||
while (state.position <= 1.0) {
|
||||
const s32 left{state.history[0][0] * lut[lut_index + 0] +
|
||||
state.history[1][0] * lut[lut_index + 1] +
|
||||
state.history[2][0] * lut[lut_index + 2] +
|
||||
state.history[3][0] * lut[lut_index + 3]};
|
||||
const s32 right{state.history[0][1] * lut[lut_index + 0] +
|
||||
state.history[1][1] * lut[lut_index + 1] +
|
||||
state.history[2][1] * lut[lut_index + 2] +
|
||||
state.history[3][1] * lut[lut_index + 3]};
|
||||
const s32 new_offset{state.fraction + step};
|
||||
|
||||
state.fraction = new_offset & 0x7fff;
|
||||
|
||||
output.emplace_back(static_cast<s16>(std::clamp(left >> 15, SHRT_MIN, SHRT_MAX)));
|
||||
output.emplace_back(static_cast<s16>(std::clamp(right >> 15, SHRT_MIN, SHRT_MAX)));
|
||||
|
||||
state.position += ratio;
|
||||
}
|
||||
pos -= 1.0;
|
||||
state.position -= 1.0;
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -6,19 +6,17 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/filter.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct InterpolationState {
|
||||
static constexpr std::size_t lanczos_taps = 4;
|
||||
static constexpr std::size_t history_size = lanczos_taps * 2 - 1;
|
||||
|
||||
double current_ratio = 0.0;
|
||||
CascadingFilter nyquist;
|
||||
std::array<std::array<s16, 2>, history_size> history = {};
|
||||
double position = 0;
|
||||
static constexpr std::size_t taps{4};
|
||||
static constexpr std::size_t history_size{taps * 2 - 1};
|
||||
std::array<std::array<s16, 2>, history_size> history{};
|
||||
double position{};
|
||||
s32 fraction{};
|
||||
};
|
||||
|
||||
/// Interpolates input signal to produce output signal.
|
||||
|
||||
@@ -27,9 +27,9 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
// so just calculate them both even if the other isn't showing.
|
||||
FramebufferLayout res{width, height};
|
||||
|
||||
const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) /
|
||||
ScreenUndocked::Width};
|
||||
const auto window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const float emulation_aspect_ratio = EmulationAspectRatio(
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio);
|
||||
|
||||
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
@@ -58,4 +58,19 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
|
||||
return DefaultFrameLayout(width, height);
|
||||
}
|
||||
|
||||
float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) {
|
||||
switch (aspect) {
|
||||
case AspectRatio::Default:
|
||||
return static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width;
|
||||
case AspectRatio::R4_3:
|
||||
return 3.0f / 4.0f;
|
||||
case AspectRatio::R21_9:
|
||||
return 9.0f / 21.0f;
|
||||
case AspectRatio::StretchToWindow:
|
||||
return window_aspect_ratio;
|
||||
default:
|
||||
return static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Layout
|
||||
|
||||
@@ -18,6 +18,13 @@ enum ScreenDocked : u32 {
|
||||
HeightDocked = 1080,
|
||||
};
|
||||
|
||||
enum class AspectRatio {
|
||||
Default,
|
||||
R4_3,
|
||||
R21_9,
|
||||
StretchToWindow,
|
||||
};
|
||||
|
||||
/// Describes the layout of the window framebuffer
|
||||
struct FramebufferLayout {
|
||||
u32 width{ScreenUndocked::Width};
|
||||
@@ -48,4 +55,12 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height);
|
||||
*/
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale);
|
||||
|
||||
/**
|
||||
* Convenience method to determine emulation aspect ratio
|
||||
* @param aspect Represents the index of aspect ratio stored in Settings::values.aspect_ratio
|
||||
* @param window_aspect_ratio Current window aspect ratio
|
||||
* @return Emulation render window aspect ratio
|
||||
*/
|
||||
float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio);
|
||||
|
||||
} // namespace Layout
|
||||
|
||||
@@ -201,42 +201,39 @@ void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) {
|
||||
void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
|
||||
const VAddr arb_addr = thread->GetArbiterWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread>& current_thread = *it;
|
||||
if (current_thread->GetPriority() >= thread->GetPriority()) {
|
||||
thread_list.insert(it, thread);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
|
||||
const auto iter =
|
||||
std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) {
|
||||
return entry->GetPriority() >= thread->GetPriority();
|
||||
});
|
||||
|
||||
if (iter == thread_list.cend()) {
|
||||
thread_list.push_back(std::move(thread));
|
||||
} else {
|
||||
thread_list.insert(iter, std::move(thread));
|
||||
}
|
||||
thread_list.push_back(std::move(thread));
|
||||
}
|
||||
|
||||
void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
const VAddr arb_addr = thread->GetArbiterWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread>& current_thread = *it;
|
||||
if (current_thread.get() == thread.get()) {
|
||||
thread_list.erase(it);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
||||
const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(),
|
||||
[&thread](const auto& entry) { return thread == entry; });
|
||||
|
||||
ASSERT(iter != thread_list.cend());
|
||||
|
||||
thread_list.erase(iter);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) {
|
||||
std::vector<std::shared_ptr<Thread>> result;
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[address];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
std::shared_ptr<Thread> current_thread = *it;
|
||||
result.push_back(std::move(current_thread));
|
||||
++it;
|
||||
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
|
||||
VAddr address) const {
|
||||
const auto iter = arb_threads.find(address);
|
||||
if (iter == arb_threads.cend()) {
|
||||
return {};
|
||||
}
|
||||
return result;
|
||||
|
||||
const std::list<std::shared_ptr<Thread>>& thread_list = iter->second;
|
||||
return {thread_list.cbegin(), thread_list.cend()};
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
void RemoveThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
// Gets the threads waiting on an address.
|
||||
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address);
|
||||
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
|
||||
|
||||
/// List of threads waiting for a address arbiter
|
||||
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
|
||||
|
||||
@@ -481,7 +481,7 @@ void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
|
||||
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
|
||||
return;
|
||||
}
|
||||
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
if (processor_id >= 0) {
|
||||
scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this);
|
||||
}
|
||||
@@ -513,7 +513,7 @@ void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
|
||||
}
|
||||
|
||||
void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
|
||||
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
|
||||
current_priority >= THREADPRIO_COUNT) {
|
||||
return;
|
||||
|
||||
@@ -170,8 +170,10 @@ public:
|
||||
{3, nullptr, "SetContextForMultiStream"},
|
||||
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
|
||||
{5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
|
||||
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{7, nullptr, "DecodeInterleavedForMultiStream"},
|
||||
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
|
||||
{7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
|
||||
{8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{9, nullptr, "DecodeInterleavedForMultiStream"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -117,13 +117,13 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
|
||||
}
|
||||
|
||||
bool NullBackend::Clear(u64 title_id) {
|
||||
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}");
|
||||
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
|
||||
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id,
|
||||
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
|
||||
Common::HexToString(passphrase));
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,8 @@ private:
|
||||
DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
|
||||
const std::string& content_type_name) {
|
||||
if (client == nullptr) {
|
||||
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds);
|
||||
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT);
|
||||
client->set_timeout_sec(timeout_seconds);
|
||||
}
|
||||
|
||||
httplib::Headers headers{
|
||||
@@ -448,8 +449,8 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
|
||||
|
||||
Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
|
||||
std::map<std::string, EventStatus>& games) {
|
||||
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT),
|
||||
static_cast<int>(TIMEOUT_SECONDS)};
|
||||
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)};
|
||||
client.set_timeout_sec(static_cast<int>(TIMEOUT_SECONDS));
|
||||
|
||||
httplib::Headers headers{
|
||||
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
|
||||
|
||||
@@ -129,12 +129,20 @@ public:
|
||||
{304, nullptr, "Disconnect"},
|
||||
{400, nullptr, "Initialize"},
|
||||
{401, nullptr, "Finalize"},
|
||||
{402, nullptr, "SetOperationMode"},
|
||||
{402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void Initialize2(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
||||
// Result success seem make this services start network and continue.
|
||||
// If we just pass result error then it will stop and maybe try again and again.
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_UNKNOWN);
|
||||
}
|
||||
};
|
||||
|
||||
class LDNS final : public ServiceFramework<LDNS> {
|
||||
|
||||
@@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
|
||||
return GetWaitbase(input, output);
|
||||
case IoctlCommand::IocChannelSetTimeoutCommand:
|
||||
return ChannelSetTimeout(input, output);
|
||||
case IoctlCommand::IocChannelSetTimeslice:
|
||||
return ChannelSetTimeslice(input, output);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -228,4 +230,14 @@ u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>&
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSetTimeslice params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSetTimeslice));
|
||||
LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
|
||||
|
||||
channel_timeslice = params.timeslice;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -48,6 +48,7 @@ private:
|
||||
IocAllocObjCtxCommand = 0xC0104809,
|
||||
IocChannelGetWaitbaseCommand = 0xC0080003,
|
||||
IocChannelSetTimeoutCommand = 0x40044803,
|
||||
IocChannelSetTimeslice = 0xC004481D,
|
||||
};
|
||||
|
||||
enum class CtxObjects : u32_le {
|
||||
@@ -101,6 +102,11 @@ private:
|
||||
static_assert(sizeof(IoctlChannelSetPriority) == 4,
|
||||
"IoctlChannelSetPriority is incorrect size");
|
||||
|
||||
struct IoctlSetTimeslice {
|
||||
u32_le timeslice;
|
||||
};
|
||||
static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size");
|
||||
|
||||
struct IoctlEventIdControl {
|
||||
u32_le cmd; // 0=disable, 1=enable, 2=clear
|
||||
u32_le id;
|
||||
@@ -174,6 +180,7 @@ private:
|
||||
u64_le user_data{};
|
||||
IoctlZCullBind zcull_params{};
|
||||
u32_le channel_priority{};
|
||||
u32_le channel_timeslice{};
|
||||
|
||||
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
@@ -188,6 +195,7 @@ private:
|
||||
const std::vector<u8>& input2, IoctlVersion version);
|
||||
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
u32 assigned_syncpoints{};
|
||||
|
||||
@@ -429,6 +429,7 @@ struct Values {
|
||||
int vulkan_device;
|
||||
|
||||
float resolution_factor;
|
||||
int aspect_ratio;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_disk_shader_cache;
|
||||
|
||||
@@ -37,6 +37,7 @@ add_library(video_core STATIC
|
||||
memory_manager.h
|
||||
morton.cpp
|
||||
morton.h
|
||||
query_cache.h
|
||||
rasterizer_accelerated.cpp
|
||||
rasterizer_accelerated.h
|
||||
rasterizer_cache.cpp
|
||||
@@ -74,6 +75,8 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_stream_buffer.h
|
||||
renderer_opengl/gl_texture_cache.cpp
|
||||
renderer_opengl/gl_texture_cache.h
|
||||
renderer_opengl/gl_query_cache.cpp
|
||||
renderer_opengl/gl_query_cache.h
|
||||
renderer_opengl/maxwell_to_gl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
@@ -177,6 +180,8 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/vk_memory_manager.h
|
||||
renderer_vulkan/vk_pipeline_cache.cpp
|
||||
renderer_vulkan/vk_pipeline_cache.h
|
||||
renderer_vulkan/vk_query_cache.cpp
|
||||
renderer_vulkan/vk_query_cache.h
|
||||
renderer_vulkan/vk_rasterizer.cpp
|
||||
renderer_vulkan/vk_rasterizer.h
|
||||
renderer_vulkan/vk_renderpass_cache.cpp
|
||||
|
||||
@@ -4,17 +4,21 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_type.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
using VideoCore::QueryType;
|
||||
|
||||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
@@ -399,6 +403,10 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ProcessQueryCondition();
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(counter_reset): {
|
||||
ProcessCounterReset();
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(sync_info): {
|
||||
ProcessSyncPoint();
|
||||
break;
|
||||
@@ -481,7 +489,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
|
||||
|
||||
const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
|
||||
if (ShouldExecute()) {
|
||||
rasterizer.DrawMultiBatch(is_indexed);
|
||||
rasterizer.Draw(is_indexed, true);
|
||||
}
|
||||
|
||||
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
|
||||
@@ -519,61 +527,51 @@ void Maxwell3D::ProcessFirmwareCall4() {
|
||||
regs.reg_array[0xd00] = 1;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessQueryGet() {
|
||||
const 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.
|
||||
|
||||
// TODO(Subv): Support the other query units.
|
||||
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
|
||||
"Units other than CROP are unimplemented");
|
||||
|
||||
u64 result = 0;
|
||||
|
||||
// TODO(Subv): Support the other query variables
|
||||
switch (regs.query.query_get.select) {
|
||||
case Regs::QuerySelect::Zero:
|
||||
// This seems to actually write the query sequence to the query address.
|
||||
result = regs.query.query_sequence;
|
||||
break;
|
||||
default:
|
||||
result = 1;
|
||||
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
|
||||
static_cast<u32>(regs.query.query_get.select.Value()));
|
||||
}
|
||||
|
||||
// TODO(Subv): Research and implement how query sync conditions work.
|
||||
|
||||
void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
|
||||
struct LongQueryResult {
|
||||
u64_le value;
|
||||
u64_le timestamp;
|
||||
};
|
||||
static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size");
|
||||
const GPUVAddr sequence_address{regs.query.QueryAddress()};
|
||||
if (long_query) {
|
||||
// Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
|
||||
// GPU, this command may actually take a while to complete in real hardware due to GPU
|
||||
// wait queues.
|
||||
LongQueryResult query_result{payload, system.GPU().GetTicks()};
|
||||
memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result));
|
||||
} else {
|
||||
memory_manager.Write<u32>(sequence_address, static_cast<u32>(payload));
|
||||
}
|
||||
}
|
||||
|
||||
switch (regs.query.query_get.mode) {
|
||||
case Regs::QueryMode::Write:
|
||||
case Regs::QueryMode::Write2: {
|
||||
u32 sequence = regs.query.query_sequence;
|
||||
if (regs.query.query_get.short_query) {
|
||||
// Write the current query sequence to the sequence address.
|
||||
// TODO(Subv): Find out what happens if you use a long query type but mark it as a short
|
||||
// query.
|
||||
memory_manager.Write<u32>(sequence_address, sequence);
|
||||
} else {
|
||||
// Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
|
||||
// GPU, this command may actually take a while to complete in real hardware due to GPU
|
||||
// wait queues.
|
||||
LongQueryResult query_result{};
|
||||
query_result.value = result;
|
||||
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
|
||||
query_result.timestamp = system.CoreTiming().GetTicks();
|
||||
memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result));
|
||||
void Maxwell3D::ProcessQueryGet() {
|
||||
// TODO(Subv): Support the other query units.
|
||||
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
|
||||
"Units other than CROP are unimplemented");
|
||||
|
||||
switch (regs.query.query_get.operation) {
|
||||
case Regs::QueryOperation::Release:
|
||||
StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
|
||||
break;
|
||||
case Regs::QueryOperation::Acquire:
|
||||
// TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
|
||||
// matches the current payload.
|
||||
UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE");
|
||||
break;
|
||||
case Regs::QueryOperation::Counter:
|
||||
if (const std::optional<u64> result = GetQueryResult()) {
|
||||
// If the query returns an empty optional it means it's cached and deferred.
|
||||
// In this case we have a non-empty result, so we stamp it immediately.
|
||||
StampQueryResult(*result, regs.query.query_get.short_query == 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Regs::QueryOperation::Trap:
|
||||
UNIMPLEMENTED_MSG("Unimplemented query operation TRAP");
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Query mode {} not implemented",
|
||||
static_cast<u32>(regs.query.query_get.mode.Value()));
|
||||
UNIMPLEMENTED_MSG("Unknown query operation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,20 +588,20 @@ void Maxwell3D::ProcessQueryCondition() {
|
||||
}
|
||||
case Regs::ConditionMode::ResNonZero: {
|
||||
Regs::QueryCompare cmp;
|
||||
memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp));
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::Equal: {
|
||||
Regs::QueryCompare cmp;
|
||||
memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp));
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on =
|
||||
cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::NotEqual: {
|
||||
Regs::QueryCompare cmp;
|
||||
memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp));
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on =
|
||||
cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode;
|
||||
break;
|
||||
@@ -616,6 +614,18 @@ void Maxwell3D::ProcessQueryCondition() {
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCounterReset() {
|
||||
switch (regs.counter_reset) {
|
||||
case Regs::CounterReset::SampleCnt:
|
||||
rasterizer.ResetCounter(QueryType::SamplesPassed);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Render_OpenGL, "Unimplemented counter reset={}",
|
||||
static_cast<int>(regs.counter_reset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessSyncPoint() {
|
||||
const u32 sync_point = regs.sync_info.sync_point.Value();
|
||||
const u32 increment = regs.sync_info.increment.Value();
|
||||
@@ -644,7 +654,7 @@ void Maxwell3D::DrawArrays() {
|
||||
|
||||
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
|
||||
if (ShouldExecute()) {
|
||||
rasterizer.DrawBatch(is_indexed);
|
||||
rasterizer.Draw(is_indexed, false);
|
||||
}
|
||||
|
||||
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
|
||||
@@ -658,6 +668,22 @@ void Maxwell3D::DrawArrays() {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u64> Maxwell3D::GetQueryResult() {
|
||||
switch (regs.query.query_get.select) {
|
||||
case Regs::QuerySelect::Zero:
|
||||
return 0;
|
||||
case Regs::QuerySelect::SamplesPassed:
|
||||
// Deferred.
|
||||
rasterizer.Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
|
||||
system.GPU().GetTicks());
|
||||
return {};
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
|
||||
static_cast<u32>(regs.query.query_get.select.Value()));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(std::size_t stage_index) {
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
|
||||
auto& shader = state.shader_stages[stage_index];
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -71,12 +72,11 @@ public:
|
||||
static constexpr std::size_t MaxConstBuffers = 18;
|
||||
static constexpr std::size_t MaxConstBufferSize = 0x10000;
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
Write = 0,
|
||||
Sync = 1,
|
||||
// TODO(Subv): It is currently unknown what the difference between method 2 and method 0
|
||||
// is.
|
||||
Write2 = 2,
|
||||
enum class QueryOperation : u32 {
|
||||
Release = 0,
|
||||
Acquire = 1,
|
||||
Counter = 2,
|
||||
Trap = 3,
|
||||
};
|
||||
|
||||
enum class QueryUnit : u32 {
|
||||
@@ -410,6 +410,27 @@ public:
|
||||
Linear = 1,
|
||||
};
|
||||
|
||||
enum class CounterReset : u32 {
|
||||
SampleCnt = 0x01,
|
||||
Unk02 = 0x02,
|
||||
Unk03 = 0x03,
|
||||
Unk04 = 0x04,
|
||||
EmittedPrimitives = 0x10, // Not tested
|
||||
Unk11 = 0x11,
|
||||
Unk12 = 0x12,
|
||||
Unk13 = 0x13,
|
||||
Unk15 = 0x15,
|
||||
Unk16 = 0x16,
|
||||
Unk17 = 0x17,
|
||||
Unk18 = 0x18,
|
||||
Unk1A = 0x1A,
|
||||
Unk1B = 0x1B,
|
||||
Unk1C = 0x1C,
|
||||
Unk1D = 0x1D,
|
||||
Unk1E = 0x1E,
|
||||
GeneratedPrimitives = 0x1F,
|
||||
};
|
||||
|
||||
struct Cull {
|
||||
enum class FrontFace : u32 {
|
||||
ClockWise = 0x0900,
|
||||
@@ -858,11 +879,19 @@ public:
|
||||
BitField<7, 1, u32> c7;
|
||||
} clip_distance_enabled;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x1);
|
||||
u32 samplecnt_enable;
|
||||
|
||||
float point_size;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x7);
|
||||
INSERT_UNION_PADDING_WORDS(0x1);
|
||||
|
||||
u32 point_sprite_enable;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x3);
|
||||
|
||||
CounterReset counter_reset;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x1);
|
||||
|
||||
u32 zeta_enable;
|
||||
|
||||
@@ -1077,7 +1106,7 @@ public:
|
||||
u32 query_sequence;
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 2, QueryMode> mode;
|
||||
BitField<0, 2, QueryOperation> operation;
|
||||
BitField<4, 1, u32> fence;
|
||||
BitField<12, 4, QueryUnit> unit;
|
||||
BitField<16, 1, QuerySyncCondition> sync_cond;
|
||||
@@ -1409,9 +1438,15 @@ private:
|
||||
/// Handles a write to the QUERY_GET register.
|
||||
void ProcessQueryGet();
|
||||
|
||||
// Handles Conditional Rendering
|
||||
/// Writes the query result accordingly.
|
||||
void StampQueryResult(u64 payload, bool long_query);
|
||||
|
||||
/// Handles conditional rendering.
|
||||
void ProcessQueryCondition();
|
||||
|
||||
/// Handles counter resets.
|
||||
void ProcessCounterReset();
|
||||
|
||||
/// Handles writes to syncing register.
|
||||
void ProcessSyncPoint();
|
||||
|
||||
@@ -1428,6 +1463,9 @@ private:
|
||||
|
||||
// Handles a instance drawcall from MME
|
||||
void StepInstance(MMEDrawMode expected_mode, u32 count);
|
||||
|
||||
/// Returns a query's value or an empty object if the value will be deferred through a cache.
|
||||
std::optional<u64> GetQueryResult();
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
@@ -1493,7 +1531,10 @@ ASSERT_REG_POSITION(screen_y_control, 0x4EB);
|
||||
ASSERT_REG_POSITION(vb_element_base, 0x50D);
|
||||
ASSERT_REG_POSITION(vb_base_instance, 0x50E);
|
||||
ASSERT_REG_POSITION(clip_distance_enabled, 0x544);
|
||||
ASSERT_REG_POSITION(samplecnt_enable, 0x545);
|
||||
ASSERT_REG_POSITION(point_size, 0x546);
|
||||
ASSERT_REG_POSITION(point_sprite_enable, 0x548);
|
||||
ASSERT_REG_POSITION(counter_reset, 0x54C);
|
||||
ASSERT_REG_POSITION(zeta_enable, 0x54E);
|
||||
ASSERT_REG_POSITION(multisample_control, 0x54F);
|
||||
ASSERT_REG_POSITION(condition, 0x554);
|
||||
|
||||
@@ -1677,11 +1677,11 @@ union Instruction {
|
||||
} xmad;
|
||||
|
||||
union {
|
||||
BitField<20, 14, u64> offset;
|
||||
BitField<20, 14, u64> shifted_offset;
|
||||
BitField<34, 5, u64> index;
|
||||
|
||||
u64 GetOffset() const {
|
||||
return offset * 4;
|
||||
return shifted_offset * 4;
|
||||
}
|
||||
} cbuf34;
|
||||
|
||||
|
||||
+16
-2
@@ -6,6 +6,7 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
@@ -23,7 +24,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
|
||||
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
|
||||
: system{system}, renderer{renderer}, is_async{is_async} {
|
||||
auto& rasterizer{renderer.Rasterizer()};
|
||||
memory_manager = std::make_unique<Tegra::MemoryManager>(system);
|
||||
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
|
||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer);
|
||||
@@ -122,6 +123,19 @@ bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 GPU::GetTicks() const {
|
||||
// This values were reversed engineered by fincs from NVN
|
||||
// The gpu clock is reported in units of 385/625 nanoseconds
|
||||
constexpr u64 gpu_ticks_num = 384;
|
||||
constexpr u64 gpu_ticks_den = 625;
|
||||
|
||||
const u64 cpu_ticks = system.CoreTiming().GetTicks();
|
||||
const u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count();
|
||||
const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;
|
||||
const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den;
|
||||
return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den;
|
||||
}
|
||||
|
||||
void GPU::FlushCommands() {
|
||||
renderer.Rasterizer().FlushCommands();
|
||||
}
|
||||
@@ -340,7 +354,7 @@ void GPU::ProcessSemaphoreTriggerMethod() {
|
||||
block.sequence = regs.semaphore_sequence;
|
||||
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
|
||||
// CoreTiming
|
||||
block.timestamp = system.CoreTiming().GetTicks();
|
||||
block.timestamp = GetTicks();
|
||||
memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block,
|
||||
sizeof(block));
|
||||
} else {
|
||||
|
||||
@@ -192,6 +192,8 @@ public:
|
||||
|
||||
bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
|
||||
|
||||
u64 GetTicks() const;
|
||||
|
||||
std::unique_lock<std::mutex> LockSync() {
|
||||
return std::unique_lock{sync_mutex};
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
#include "core/memory.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
MemoryManager::MemoryManager(Core::System& system) : system{system} {
|
||||
MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||
: rasterizer{rasterizer}, system{system} {
|
||||
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
|
||||
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
|
||||
Common::PageType::Unmapped);
|
||||
@@ -83,6 +85,7 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
|
||||
const auto cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
|
||||
// Flush and invalidate through the GPU interface, to be asynchronous if possible.
|
||||
system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size);
|
||||
|
||||
UnmapRange(gpu_addr, aligned_size);
|
||||
@@ -242,7 +245,9 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
|
||||
system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
// Flush must happen on the rasterizer interface, such that memory is always synchronous
|
||||
// when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
|
||||
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
@@ -292,7 +297,9 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
u8* dest_ptr{page_table.pointers[page_index] + page_offset};
|
||||
system.GPU().InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
|
||||
// Invalidate must happen on the rasterizer interface, such that memory is always
|
||||
// synchronous when it is written (even when in asynchronous GPU mode).
|
||||
rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
@@ -339,8 +346,10 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
// Flush must happen on the rasterizer interface, such that memory is always synchronous
|
||||
// when it is copied (even when in asynchronous GPU mode).
|
||||
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
|
||||
system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
WriteBlock(dest_addr, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/page_table.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RasterizerInterface;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
@@ -47,7 +51,7 @@ struct VirtualMemoryArea {
|
||||
|
||||
class MemoryManager final {
|
||||
public:
|
||||
explicit MemoryManager(Core::System& system);
|
||||
explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
|
||||
~MemoryManager();
|
||||
|
||||
GPUVAddr AllocateSpace(u64 size, u64 align);
|
||||
@@ -172,6 +176,7 @@ private:
|
||||
|
||||
Common::PageTable page_table{page_bits};
|
||||
VMAMap vma_map;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
template <class QueryCache, class HostCounter>
|
||||
class CounterStreamBase {
|
||||
public:
|
||||
explicit CounterStreamBase(QueryCache& cache, VideoCore::QueryType type)
|
||||
: cache{cache}, type{type} {}
|
||||
|
||||
/// Updates the state of the stream, enabling or disabling as needed.
|
||||
void Update(bool enabled) {
|
||||
if (enabled) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the stream to zero. It doesn't disable the query after resetting.
|
||||
void Reset() {
|
||||
if (current) {
|
||||
current->EndQuery();
|
||||
|
||||
// Immediately start a new query to avoid disabling its state.
|
||||
current = cache.Counter(nullptr, type);
|
||||
}
|
||||
last = nullptr;
|
||||
}
|
||||
|
||||
/// Returns the current counter slicing as needed.
|
||||
std::shared_ptr<HostCounter> Current() {
|
||||
if (!current) {
|
||||
return nullptr;
|
||||
}
|
||||
current->EndQuery();
|
||||
last = std::move(current);
|
||||
current = cache.Counter(last, type);
|
||||
return last;
|
||||
}
|
||||
|
||||
/// Returns true when the counter stream is enabled.
|
||||
bool IsEnabled() const {
|
||||
return current != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Enables the stream.
|
||||
void Enable() {
|
||||
if (current) {
|
||||
return;
|
||||
}
|
||||
current = cache.Counter(last, type);
|
||||
}
|
||||
|
||||
// Disables the stream.
|
||||
void Disable() {
|
||||
if (current) {
|
||||
current->EndQuery();
|
||||
}
|
||||
last = std::exchange(current, nullptr);
|
||||
}
|
||||
|
||||
QueryCache& cache;
|
||||
const VideoCore::QueryType type;
|
||||
|
||||
std::shared_ptr<HostCounter> current;
|
||||
std::shared_ptr<HostCounter> last;
|
||||
};
|
||||
|
||||
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter,
|
||||
class QueryPool>
|
||||
class QueryCacheBase {
|
||||
public:
|
||||
explicit QueryCacheBase(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||
: system{system}, rasterizer{rasterizer}, streams{{CounterStream{
|
||||
static_cast<QueryCache&>(*this),
|
||||
VideoCore::QueryType::SamplesPassed}}} {}
|
||||
|
||||
void InvalidateRegion(CacheAddr addr, std::size_t size) {
|
||||
std::unique_lock lock{mutex};
|
||||
FlushAndRemoveRegion(addr, size);
|
||||
}
|
||||
|
||||
void FlushRegion(CacheAddr addr, std::size_t size) {
|
||||
std::unique_lock lock{mutex};
|
||||
FlushAndRemoveRegion(addr, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a query in GPU mapped memory, potentially marked with a timestamp.
|
||||
* @param gpu_addr GPU address to flush to when the mapped memory is read.
|
||||
* @param type Query type, e.g. SamplesPassed.
|
||||
* @param timestamp Timestamp, when empty the flushed query is assumed to be short.
|
||||
*/
|
||||
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) {
|
||||
std::unique_lock lock{mutex};
|
||||
auto& memory_manager = system.GPU().MemoryManager();
|
||||
const auto host_ptr = memory_manager.GetPointer(gpu_addr);
|
||||
|
||||
CachedQuery* query = TryGet(ToCacheAddr(host_ptr));
|
||||
if (!query) {
|
||||
const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr);
|
||||
ASSERT_OR_EXECUTE(cpu_addr, return;);
|
||||
|
||||
query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
|
||||
}
|
||||
|
||||
query->BindCounter(Stream(type).Current(), timestamp);
|
||||
}
|
||||
|
||||
/// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
|
||||
void UpdateCounters() {
|
||||
std::unique_lock lock{mutex};
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable);
|
||||
}
|
||||
|
||||
/// Resets a counter to zero. It doesn't disable the query after resetting.
|
||||
void ResetCounter(VideoCore::QueryType type) {
|
||||
std::unique_lock lock{mutex};
|
||||
Stream(type).Reset();
|
||||
}
|
||||
|
||||
/// Disable all active streams. Expected to be called at the end of a command buffer.
|
||||
void DisableStreams() {
|
||||
std::unique_lock lock{mutex};
|
||||
for (auto& stream : streams) {
|
||||
stream.Update(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new host counter.
|
||||
std::shared_ptr<HostCounter> Counter(std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type) {
|
||||
return std::make_shared<HostCounter>(static_cast<QueryCache&>(*this), std::move(dependency),
|
||||
type);
|
||||
}
|
||||
|
||||
/// Returns the counter stream of the specified type.
|
||||
CounterStream& Stream(VideoCore::QueryType type) {
|
||||
return streams[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
/// Returns the counter stream of the specified type.
|
||||
const CounterStream& Stream(VideoCore::QueryType type) const {
|
||||
return streams[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
protected:
|
||||
std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
|
||||
|
||||
private:
|
||||
/// Flushes a memory range to guest memory and removes it from the cache.
|
||||
void FlushAndRemoveRegion(CacheAddr addr, std::size_t size) {
|
||||
const u64 addr_begin = static_cast<u64>(addr);
|
||||
const u64 addr_end = addr_begin + static_cast<u64>(size);
|
||||
const auto in_range = [addr_begin, addr_end](CachedQuery& query) {
|
||||
const u64 cache_begin = query.GetCacheAddr();
|
||||
const u64 cache_end = cache_begin + query.SizeInBytes();
|
||||
return cache_begin < addr_end && addr_begin < cache_end;
|
||||
};
|
||||
|
||||
const u64 page_end = addr_end >> PAGE_SHIFT;
|
||||
for (u64 page = addr_begin >> PAGE_SHIFT; page <= page_end; ++page) {
|
||||
const auto& it = cached_queries.find(page);
|
||||
if (it == std::end(cached_queries)) {
|
||||
continue;
|
||||
}
|
||||
auto& contents = it->second;
|
||||
for (auto& query : contents) {
|
||||
if (!in_range(query)) {
|
||||
continue;
|
||||
}
|
||||
rasterizer.UpdatePagesCachedCount(query.CpuAddr(), query.SizeInBytes(), -1);
|
||||
query.Flush();
|
||||
}
|
||||
contents.erase(std::remove_if(std::begin(contents), std::end(contents), in_range),
|
||||
std::end(contents));
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers the passed parameters as cached and returns a pointer to the stored cached query.
|
||||
CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) {
|
||||
rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1);
|
||||
const u64 page = static_cast<u64>(ToCacheAddr(host_ptr)) >> PAGE_SHIFT;
|
||||
return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr,
|
||||
host_ptr);
|
||||
}
|
||||
|
||||
/// Tries to a get a cached query. Returns nullptr on failure.
|
||||
CachedQuery* TryGet(CacheAddr addr) {
|
||||
const u64 page = static_cast<u64>(addr) >> PAGE_SHIFT;
|
||||
const auto it = cached_queries.find(page);
|
||||
if (it == std::end(cached_queries)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& contents = it->second;
|
||||
const auto found =
|
||||
std::find_if(std::begin(contents), std::end(contents),
|
||||
[addr](auto& query) { return query.GetCacheAddr() == addr; });
|
||||
return found != std::end(contents) ? &*found : nullptr;
|
||||
}
|
||||
|
||||
static constexpr std::uintptr_t PAGE_SIZE = 4096;
|
||||
static constexpr unsigned PAGE_SHIFT = 12;
|
||||
|
||||
Core::System& system;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
std::unordered_map<u64, std::vector<CachedQuery>> cached_queries;
|
||||
|
||||
std::array<CounterStream, VideoCore::NumQueryTypes> streams;
|
||||
};
|
||||
|
||||
template <class QueryCache, class HostCounter>
|
||||
class HostCounterBase {
|
||||
public:
|
||||
explicit HostCounterBase(std::shared_ptr<HostCounter> dependency_)
|
||||
: dependency{std::move(dependency_)}, depth{dependency ? (dependency->Depth() + 1) : 0} {
|
||||
// Avoid nesting too many dependencies to avoid a stack overflow when these are deleted.
|
||||
constexpr u64 depth_threshold = 96;
|
||||
if (depth > depth_threshold) {
|
||||
depth = 0;
|
||||
base_result = dependency->Query();
|
||||
dependency = nullptr;
|
||||
}
|
||||
}
|
||||
virtual ~HostCounterBase() = default;
|
||||
|
||||
/// Returns the current value of the query.
|
||||
u64 Query() {
|
||||
if (result) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
u64 value = BlockingQuery() + base_result;
|
||||
if (dependency) {
|
||||
value += dependency->Query();
|
||||
dependency = nullptr;
|
||||
}
|
||||
|
||||
result = value;
|
||||
return *result;
|
||||
}
|
||||
|
||||
/// Returns true when flushing this query will potentially wait.
|
||||
bool WaitPending() const noexcept {
|
||||
return result.has_value();
|
||||
}
|
||||
|
||||
u64 Depth() const noexcept {
|
||||
return depth;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Returns the value of query from the backend API blocking as needed.
|
||||
virtual u64 BlockingQuery() const = 0;
|
||||
|
||||
private:
|
||||
std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value.
|
||||
std::optional<u64> result; ///< Filled with the already returned value.
|
||||
u64 depth; ///< Number of nested dependencies.
|
||||
u64 base_result = 0; ///< Equivalent to nested dependencies value.
|
||||
};
|
||||
|
||||
template <class HostCounter>
|
||||
class CachedQueryBase {
|
||||
public:
|
||||
explicit CachedQueryBase(VAddr cpu_addr, u8* host_ptr)
|
||||
: cpu_addr{cpu_addr}, host_ptr{host_ptr} {}
|
||||
virtual ~CachedQueryBase() = default;
|
||||
|
||||
CachedQueryBase(CachedQueryBase&&) noexcept = default;
|
||||
CachedQueryBase(const CachedQueryBase&) = delete;
|
||||
|
||||
CachedQueryBase& operator=(CachedQueryBase&&) noexcept = default;
|
||||
CachedQueryBase& operator=(const CachedQueryBase&) = delete;
|
||||
|
||||
/// Flushes the query to guest memory.
|
||||
virtual void Flush() {
|
||||
// When counter is nullptr it means that it's just been reseted. We are supposed to write a
|
||||
// zero in these cases.
|
||||
const u64 value = counter ? counter->Query() : 0;
|
||||
std::memcpy(host_ptr, &value, sizeof(u64));
|
||||
|
||||
if (timestamp) {
|
||||
std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64));
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a counter to this query.
|
||||
void BindCounter(std::shared_ptr<HostCounter> counter_, std::optional<u64> timestamp_) {
|
||||
if (counter) {
|
||||
// If there's an old counter set it means the query is being rewritten by the game.
|
||||
// To avoid losing the data forever, flush here.
|
||||
Flush();
|
||||
}
|
||||
counter = std::move(counter_);
|
||||
timestamp = timestamp_;
|
||||
}
|
||||
|
||||
VAddr CpuAddr() const noexcept {
|
||||
return cpu_addr;
|
||||
}
|
||||
|
||||
CacheAddr GetCacheAddr() const noexcept {
|
||||
return ToCacheAddr(host_ptr);
|
||||
}
|
||||
|
||||
u64 SizeInBytes() const noexcept {
|
||||
return SizeInBytes(timestamp.has_value());
|
||||
}
|
||||
|
||||
static constexpr u64 SizeInBytes(bool with_timestamp) noexcept {
|
||||
return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Returns true when querying the counter may potentially block.
|
||||
bool WaitPending() const noexcept {
|
||||
return counter && counter->WaitPending();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::size_t SMALL_QUERY_SIZE = 8; // Query size without timestamp.
|
||||
static constexpr std::size_t LARGE_QUERY_SIZE = 16; // Query size with timestamp.
|
||||
static constexpr std::intptr_t TIMESTAMP_OFFSET = 8; // Timestamp offset in a large query.
|
||||
|
||||
VAddr cpu_addr; ///< Guest CPU address.
|
||||
u8* host_ptr; ///< Writable host pointer.
|
||||
std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree.
|
||||
std::optional<u64> timestamp; ///< Timestamp to flush to guest memory.
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/gpu.h"
|
||||
@@ -17,6 +18,11 @@ class MemoryManager;
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
enum class QueryType {
|
||||
SamplesPassed,
|
||||
};
|
||||
constexpr std::size_t NumQueryTypes = 1;
|
||||
|
||||
enum class LoadCallbackStage {
|
||||
Prepare,
|
||||
Decompile,
|
||||
@@ -29,11 +35,8 @@ class RasterizerInterface {
|
||||
public:
|
||||
virtual ~RasterizerInterface() {}
|
||||
|
||||
/// Draw the current batch of vertex arrays
|
||||
virtual bool DrawBatch(bool is_indexed) = 0;
|
||||
|
||||
/// Draw the current batch of multiple instances of vertex arrays
|
||||
virtual bool DrawMultiBatch(bool is_indexed) = 0;
|
||||
/// Dispatches a draw invocation
|
||||
virtual void Draw(bool is_indexed, bool is_instanced) = 0;
|
||||
|
||||
/// Clear the current framebuffer
|
||||
virtual void Clear() = 0;
|
||||
@@ -41,6 +44,12 @@ public:
|
||||
/// Dispatches a compute shader invocation
|
||||
virtual void DispatchCompute(GPUVAddr code_addr) = 0;
|
||||
|
||||
/// Resets the counter of a query
|
||||
virtual void ResetCounter(QueryType type) = 0;
|
||||
|
||||
/// Records a GPU query and caches it
|
||||
virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0;
|
||||
|
||||
/// Notify rasterizer that all caches should be flushed to Switch memory
|
||||
virtual void FlushAll() = 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_query_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<GLenum, VideoCore::NumQueryTypes> QueryTargets = {GL_SAMPLES_PASSED};
|
||||
|
||||
constexpr GLenum GetTarget(VideoCore::QueryType type) {
|
||||
return QueryTargets[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
QueryCache::QueryCache(Core::System& system, RasterizerOpenGL& gl_rasterizer)
|
||||
: VideoCommon::QueryCacheBase<
|
||||
QueryCache, CachedQuery, CounterStream, HostCounter,
|
||||
std::vector<OGLQuery>>{system,
|
||||
static_cast<VideoCore::RasterizerInterface&>(gl_rasterizer)},
|
||||
gl_rasterizer{gl_rasterizer} {}
|
||||
|
||||
QueryCache::~QueryCache() = default;
|
||||
|
||||
OGLQuery QueryCache::AllocateQuery(VideoCore::QueryType type) {
|
||||
auto& reserve = query_pools[static_cast<std::size_t>(type)];
|
||||
OGLQuery query;
|
||||
if (reserve.empty()) {
|
||||
query.Create(GetTarget(type));
|
||||
return query;
|
||||
}
|
||||
|
||||
query = std::move(reserve.back());
|
||||
reserve.pop_back();
|
||||
return query;
|
||||
}
|
||||
|
||||
void QueryCache::Reserve(VideoCore::QueryType type, OGLQuery&& query) {
|
||||
query_pools[static_cast<std::size_t>(type)].push_back(std::move(query));
|
||||
}
|
||||
|
||||
bool QueryCache::AnyCommandQueued() const noexcept {
|
||||
return gl_rasterizer.AnyCommandQueued();
|
||||
}
|
||||
|
||||
HostCounter::HostCounter(QueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type)
|
||||
: VideoCommon::HostCounterBase<QueryCache, HostCounter>{std::move(dependency)}, cache{cache},
|
||||
type{type}, query{cache.AllocateQuery(type)} {
|
||||
glBeginQuery(GetTarget(type), query.handle);
|
||||
}
|
||||
|
||||
HostCounter::~HostCounter() {
|
||||
cache.Reserve(type, std::move(query));
|
||||
}
|
||||
|
||||
void HostCounter::EndQuery() {
|
||||
if (!cache.AnyCommandQueued()) {
|
||||
// There are chances a query waited on without commands (glDraw, glClear, glDispatch). Not
|
||||
// having any of these causes a lock. glFlush is considered a command, so we can safely wait
|
||||
// for this. Insert to the OpenGL command stream a flush.
|
||||
glFlush();
|
||||
}
|
||||
glEndQuery(GetTarget(type));
|
||||
}
|
||||
|
||||
u64 HostCounter::BlockingQuery() const {
|
||||
GLint64 value;
|
||||
glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value);
|
||||
return static_cast<u64>(value);
|
||||
}
|
||||
|
||||
CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr)
|
||||
: VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {}
|
||||
|
||||
CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept
|
||||
: VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {}
|
||||
|
||||
CachedQuery& CachedQuery::operator=(CachedQuery&& rhs) noexcept {
|
||||
VideoCommon::CachedQueryBase<HostCounter>::operator=(std::move(rhs));
|
||||
cache = rhs.cache;
|
||||
type = rhs.type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CachedQuery::Flush() {
|
||||
// Waiting for a query while another query of the same target is enabled locks Nvidia's driver.
|
||||
// To avoid this disable and re-enable keeping the dependency stream.
|
||||
// But we only have to do this if we have pending waits to be done.
|
||||
auto& stream = cache->Stream(type);
|
||||
const bool slice_counter = WaitPending() && stream.IsEnabled();
|
||||
if (slice_counter) {
|
||||
stream.Update(false);
|
||||
}
|
||||
|
||||
VideoCommon::CachedQueryBase<HostCounter>::Flush();
|
||||
|
||||
if (slice_counter) {
|
||||
stream.Update(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/query_cache.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class CachedQuery;
|
||||
class HostCounter;
|
||||
class QueryCache;
|
||||
class RasterizerOpenGL;
|
||||
|
||||
using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
|
||||
|
||||
class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream,
|
||||
HostCounter, std::vector<OGLQuery>> {
|
||||
public:
|
||||
explicit QueryCache(Core::System& system, RasterizerOpenGL& rasterizer);
|
||||
~QueryCache();
|
||||
|
||||
OGLQuery AllocateQuery(VideoCore::QueryType type);
|
||||
|
||||
void Reserve(VideoCore::QueryType type, OGLQuery&& query);
|
||||
|
||||
bool AnyCommandQueued() const noexcept;
|
||||
|
||||
private:
|
||||
RasterizerOpenGL& gl_rasterizer;
|
||||
};
|
||||
|
||||
class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> {
|
||||
public:
|
||||
explicit HostCounter(QueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type);
|
||||
~HostCounter();
|
||||
|
||||
void EndQuery();
|
||||
|
||||
private:
|
||||
u64 BlockingQuery() const override;
|
||||
|
||||
QueryCache& cache;
|
||||
const VideoCore::QueryType type;
|
||||
OGLQuery query;
|
||||
};
|
||||
|
||||
class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> {
|
||||
public:
|
||||
explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr,
|
||||
u8* host_ptr);
|
||||
CachedQuery(CachedQuery&& rhs) noexcept;
|
||||
CachedQuery(const CachedQuery&) = delete;
|
||||
|
||||
CachedQuery& operator=(CachedQuery&& rhs) noexcept;
|
||||
CachedQuery& operator=(const CachedQuery&) = delete;
|
||||
|
||||
void Flush() override;
|
||||
|
||||
private:
|
||||
QueryCache* cache;
|
||||
VideoCore::QueryType type;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_type.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_query_cache.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"
|
||||
@@ -92,8 +93,8 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info)
|
||||
: RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device},
|
||||
shader_cache{*this, system, emu_window, device}, system{system}, screen_info{info},
|
||||
buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
|
||||
shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system},
|
||||
screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
|
||||
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
|
||||
state.draw.shader_program = 0;
|
||||
state.Apply();
|
||||
@@ -541,11 +542,16 @@ void RasterizerOpenGL::Clear() {
|
||||
} else if (use_stencil) {
|
||||
glClearBufferiv(GL_STENCIL, 0, ®s.clear_stencil);
|
||||
}
|
||||
|
||||
++num_queued_commands;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
auto& gpu = system.GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
SyncRasterizeEnable(state);
|
||||
SyncColorMask();
|
||||
@@ -611,7 +617,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
|
||||
// Setup shaders and their used resources.
|
||||
texture_cache.GuardSamplers(true);
|
||||
const auto primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology);
|
||||
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology);
|
||||
SetupShaders(primitive_mode);
|
||||
texture_cache.GuardSamplers(false);
|
||||
|
||||
@@ -638,35 +644,47 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
glTextureBarrier();
|
||||
}
|
||||
|
||||
++num_queued_commands;
|
||||
|
||||
const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
|
||||
const GLsizei num_instances =
|
||||
static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1);
|
||||
if (is_indexed) {
|
||||
const GLenum index_format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format);
|
||||
const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base);
|
||||
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count);
|
||||
glDrawElementsInstancedBaseVertexBaseInstance(
|
||||
primitive_mode, num_vertices, index_format,
|
||||
reinterpret_cast<const void*>(index_buffer_offset), num_instances, base_vertex,
|
||||
base_instance);
|
||||
const GLvoid* offset = reinterpret_cast<const GLvoid*>(index_buffer_offset);
|
||||
const GLenum format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format);
|
||||
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
|
||||
glDrawElements(primitive_mode, num_vertices, format, offset);
|
||||
} else if (num_instances == 1 && base_instance == 0) {
|
||||
glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex);
|
||||
} else if (base_vertex == 0 && base_instance == 0) {
|
||||
glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, num_instances);
|
||||
} else if (base_vertex == 0) {
|
||||
glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset,
|
||||
num_instances, base_instance);
|
||||
} else if (base_instance == 0) {
|
||||
glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset,
|
||||
num_instances, base_vertex);
|
||||
} else {
|
||||
glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format,
|
||||
offset, num_instances, base_vertex,
|
||||
base_instance);
|
||||
}
|
||||
} else {
|
||||
const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first);
|
||||
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count);
|
||||
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, num_instances,
|
||||
base_instance);
|
||||
if (num_instances == 1 && base_instance == 0) {
|
||||
glDrawArrays(primitive_mode, base_vertex, num_vertices);
|
||||
} else if (base_instance == 0) {
|
||||
glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances);
|
||||
} else {
|
||||
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices,
|
||||
num_instances, base_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
|
||||
Draw(is_indexed, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) {
|
||||
Draw(is_indexed, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
|
||||
if (device.HasBrokenCompute()) {
|
||||
return;
|
||||
@@ -707,6 +725,16 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
|
||||
state.ApplyProgramPipeline();
|
||||
|
||||
glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
|
||||
++num_queued_commands;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::ResetCounter(VideoCore::QueryType type) {
|
||||
query_cache.ResetCounter(type);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCore::QueryType type,
|
||||
std::optional<u64> timestamp) {
|
||||
query_cache.Query(gpu_addr, type, timestamp);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAll() {}
|
||||
@@ -718,6 +746,7 @@ void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) {
|
||||
}
|
||||
texture_cache.FlushRegion(addr, size);
|
||||
buffer_cache.FlushRegion(addr, size);
|
||||
query_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
|
||||
@@ -728,6 +757,7 @@ void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
|
||||
texture_cache.InvalidateRegion(addr, size);
|
||||
shader_cache.InvalidateRegion(addr, size);
|
||||
buffer_cache.InvalidateRegion(addr, size);
|
||||
query_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
|
||||
@@ -738,10 +768,18 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushCommands() {
|
||||
// Only flush when we have commands queued to OpenGL.
|
||||
if (num_queued_commands == 0) {
|
||||
return;
|
||||
}
|
||||
num_queued_commands = 0;
|
||||
glFlush();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::TickFrame() {
|
||||
// Ticking a frame means that buffers will be swapped, calling glFlush implicitly.
|
||||
num_queued_commands = 0;
|
||||
|
||||
buffer_cache.TickFrame();
|
||||
}
|
||||
|
||||
@@ -1220,6 +1258,7 @@ void RasterizerOpenGL::SyncPointState() {
|
||||
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
|
||||
// in OpenGL).
|
||||
state.point.program_control = regs.vp_point_size.enable != 0;
|
||||
state.point.sprite = regs.point_sprite_enable != 0;
|
||||
state.point.size = std::max(1.0f, regs.point_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_query_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_sampler_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
@@ -57,10 +58,11 @@ public:
|
||||
ScreenInfo& info);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
bool DrawBatch(bool is_indexed) override;
|
||||
bool DrawMultiBatch(bool is_indexed) override;
|
||||
void Draw(bool is_indexed, bool is_instanced) override;
|
||||
void Clear() override;
|
||||
void DispatchCompute(GPUVAddr code_addr) override;
|
||||
void ResetCounter(VideoCore::QueryType type) override;
|
||||
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(CacheAddr addr, u64 size) override;
|
||||
void InvalidateRegion(CacheAddr addr, u64 size) override;
|
||||
@@ -75,6 +77,11 @@ public:
|
||||
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) override;
|
||||
|
||||
/// Returns true when there are commands queued to the OpenGL server.
|
||||
bool AnyCommandQueued() const {
|
||||
return num_queued_commands > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Configures the color and depth framebuffer states.
|
||||
void ConfigureFramebuffers();
|
||||
@@ -102,9 +109,6 @@ private:
|
||||
void SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
|
||||
std::size_t size);
|
||||
|
||||
/// Syncs all the state, shaders, render targets and textures setting before a draw call.
|
||||
void Draw(bool is_indexed, bool is_instanced);
|
||||
|
||||
/// Configures the current textures to use for the draw command.
|
||||
void SetupDrawTextures(std::size_t stage_index, const Shader& shader);
|
||||
|
||||
@@ -180,10 +184,23 @@ private:
|
||||
/// Syncs the alpha test state to match the guest state
|
||||
void SyncAlphaTest();
|
||||
|
||||
/// Check for extension that are not strictly required
|
||||
/// but are needed for correct emulation
|
||||
/// Check for extension that are not strictly required but are needed for correct emulation
|
||||
void CheckExtensions();
|
||||
|
||||
std::size_t CalculateVertexArraysSize() const;
|
||||
|
||||
std::size_t CalculateIndexBufferSize() const;
|
||||
|
||||
/// Updates and returns a vertex array object representing current vertex format
|
||||
GLuint SetupVertexFormat();
|
||||
|
||||
void SetupVertexBuffer(GLuint vao);
|
||||
void SetupVertexInstances(GLuint vao);
|
||||
|
||||
GLintptr SetupIndexBuffer();
|
||||
|
||||
void SetupShaders(GLenum primitive_mode);
|
||||
|
||||
const Device device;
|
||||
OpenGLState state;
|
||||
|
||||
@@ -191,6 +208,7 @@ private:
|
||||
ShaderCacheOpenGL shader_cache;
|
||||
SamplerCacheOpenGL sampler_cache;
|
||||
FramebufferCacheOpenGL framebuffer_cache;
|
||||
QueryCache query_cache;
|
||||
|
||||
Core::System& system;
|
||||
ScreenInfo& screen_info;
|
||||
@@ -208,19 +226,8 @@ private:
|
||||
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
|
||||
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
|
||||
|
||||
std::size_t CalculateVertexArraysSize() const;
|
||||
|
||||
std::size_t CalculateIndexBufferSize() const;
|
||||
|
||||
/// Updates and returns a vertex array object representing current vertex format
|
||||
GLuint SetupVertexFormat();
|
||||
|
||||
void SetupVertexBuffer(GLuint vao);
|
||||
void SetupVertexInstances(GLuint vao);
|
||||
|
||||
GLintptr SetupIndexBuffer();
|
||||
|
||||
void SetupShaders(GLenum primitive_mode);
|
||||
/// Number of commands queued to the OpenGL driver. Reseted on flush.
|
||||
std::size_t num_queued_commands = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -207,4 +207,21 @@ void OGLFramebuffer::Release() {
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
void OGLQuery::Create(GLenum target) {
|
||||
if (handle != 0)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
|
||||
glCreateQueries(target, 1, &handle);
|
||||
}
|
||||
|
||||
void OGLQuery::Release() {
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteQueries(1, &handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -266,4 +266,29 @@ public:
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
class OGLQuery : private NonCopyable {
|
||||
public:
|
||||
OGLQuery() = default;
|
||||
|
||||
OGLQuery(OGLQuery&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
|
||||
|
||||
~OGLQuery() {
|
||||
Release();
|
||||
}
|
||||
|
||||
OGLQuery& operator=(OGLQuery&& o) noexcept {
|
||||
Release();
|
||||
handle = std::exchange(o.handle, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Creates a new internal OpenGL resource and stores the handle
|
||||
void Create(GLenum target);
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
void Release();
|
||||
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -128,6 +128,7 @@ void OpenGLState::ApplyClipDistances() {
|
||||
|
||||
void OpenGLState::ApplyPointSize() {
|
||||
Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control);
|
||||
Enable(GL_POINT_SPRITE, cur_state.point.sprite, point.sprite);
|
||||
if (UpdateValue(cur_state.point.size, point.size)) {
|
||||
glPointSize(point.size);
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ public:
|
||||
|
||||
struct {
|
||||
bool program_control = false; // GL_PROGRAM_POINT_SIZE
|
||||
bool sprite = false; // GL_POINT_SPRITE
|
||||
GLfloat size = 1.0f; // GL_POINT_SIZE
|
||||
} point;
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ struct FormatTuple {
|
||||
{vk::Format::eUndefined, {}}, // ASTC_2D_5X4
|
||||
{vk::Format::eUndefined, {}}, // BGRA8_SRGB
|
||||
{vk::Format::eBc1RgbaSrgbBlock, {}}, // DXT1_SRGB
|
||||
{vk::Format::eUndefined, {}}, // DXT23_SRGB
|
||||
{vk::Format::eBc2SrgbBlock, {}}, // DXT23_SRGB
|
||||
{vk::Format::eBc3SrgbBlock, {}}, // DXT45_SRGB
|
||||
{vk::Format::eBc7SrgbBlock, {}}, // BC7U_SRGB
|
||||
{vk::Format::eR4G4B4A4UnormPack16, Attachable}, // R4G4B4A4U
|
||||
@@ -363,6 +363,8 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr
|
||||
return vk::Format::eR8G8B8A8Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return vk::Format::eR32Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return vk::Format::eR32G32B32A32Uint;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -104,8 +104,11 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan
|
||||
features.depthBiasClamp = true;
|
||||
features.geometryShader = true;
|
||||
features.tessellationShader = true;
|
||||
features.occlusionQueryPrecise = true;
|
||||
features.fragmentStoresAndAtomics = true;
|
||||
features.shaderImageGatherExtended = true;
|
||||
features.shaderStorageImageReadWithoutFormat =
|
||||
is_shader_storage_img_read_without_format_supported;
|
||||
features.shaderStorageImageWriteWithoutFormat = true;
|
||||
features.textureCompressionASTC_LDR = is_optimal_astc_supported;
|
||||
|
||||
@@ -117,6 +120,10 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan
|
||||
bit8_storage.uniformAndStorageBuffer8BitAccess = true;
|
||||
SetNext(next, bit8_storage);
|
||||
|
||||
vk::PhysicalDeviceHostQueryResetFeaturesEXT host_query_reset;
|
||||
host_query_reset.hostQueryReset = true;
|
||||
SetNext(next, host_query_reset);
|
||||
|
||||
vk::PhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
|
||||
if (is_float16_supported) {
|
||||
float16_int8.shaderFloat16 = true;
|
||||
@@ -273,6 +280,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
|
||||
VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
|
||||
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
|
||||
VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
|
||||
};
|
||||
std::bitset<required_extensions.size()> available_extensions{};
|
||||
|
||||
@@ -340,6 +348,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||
std::make_pair(features.depthBiasClamp, "depthBiasClamp"),
|
||||
std::make_pair(features.geometryShader, "geometryShader"),
|
||||
std::make_pair(features.tessellationShader, "tessellationShader"),
|
||||
std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
|
||||
std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"),
|
||||
std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"),
|
||||
std::make_pair(features.shaderStorageImageWriteWithoutFormat,
|
||||
@@ -376,7 +385,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||
}
|
||||
};
|
||||
|
||||
extensions.reserve(13);
|
||||
extensions.reserve(14);
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
|
||||
@@ -384,6 +393,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||
extensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
|
||||
extensions.push_back(VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME);
|
||||
extensions.push_back(VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME);
|
||||
extensions.push_back(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME);
|
||||
|
||||
[[maybe_unused]] const bool nsight =
|
||||
std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
|
||||
@@ -457,6 +467,8 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK
|
||||
|
||||
void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) {
|
||||
const auto supported_features{physical.getFeatures(dldi)};
|
||||
is_shader_storage_img_read_without_format_supported =
|
||||
supported_features.shaderStorageImageReadWithoutFormat;
|
||||
is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi);
|
||||
}
|
||||
|
||||
@@ -530,6 +542,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti
|
||||
vk::Format::eBc6HUfloatBlock,
|
||||
vk::Format::eBc6HSfloatBlock,
|
||||
vk::Format::eBc1RgbaSrgbBlock,
|
||||
vk::Format::eBc2SrgbBlock,
|
||||
vk::Format::eBc3SrgbBlock,
|
||||
vk::Format::eBc7SrgbBlock,
|
||||
vk::Format::eAstc4x4SrgbBlock,
|
||||
|
||||
@@ -122,6 +122,11 @@ public:
|
||||
return properties.limits.maxPushConstantsSize;
|
||||
}
|
||||
|
||||
/// Returns true if Shader storage Image Read Without Format supported.
|
||||
bool IsShaderStorageImageReadWithoutFormatSupported() const {
|
||||
return is_shader_storage_img_read_without_format_supported;
|
||||
}
|
||||
|
||||
/// Returns true if ASTC is natively supported.
|
||||
bool IsOptimalAstcSupported() const {
|
||||
return is_optimal_astc_supported;
|
||||
@@ -227,6 +232,8 @@ private:
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
|
||||
bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints.
|
||||
bool is_shader_storage_img_read_without_format_supported{}; ///< Support for shader storage
|
||||
///< image read without format
|
||||
|
||||
// Telemetry parameters
|
||||
std::string vendor_name; ///< Device's driver name.
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_query_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array QUERY_TARGETS = {vk::QueryType::eOcclusion};
|
||||
|
||||
constexpr vk::QueryType GetTarget(VideoCore::QueryType type) {
|
||||
return QUERY_TARGETS[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
QueryPool::QueryPool() : VKFencedPool{GROW_STEP} {}
|
||||
|
||||
QueryPool::~QueryPool() = default;
|
||||
|
||||
void QueryPool::Initialize(const VKDevice& device_, VideoCore::QueryType type_) {
|
||||
device = &device_;
|
||||
type = type_;
|
||||
}
|
||||
|
||||
std::pair<vk::QueryPool, std::uint32_t> QueryPool::Commit(VKFence& fence) {
|
||||
std::size_t index;
|
||||
do {
|
||||
index = CommitResource(fence);
|
||||
} while (usage[index]);
|
||||
usage[index] = true;
|
||||
|
||||
return {*pools[index / GROW_STEP], static_cast<std::uint32_t>(index % GROW_STEP)};
|
||||
}
|
||||
|
||||
void QueryPool::Allocate(std::size_t begin, std::size_t end) {
|
||||
usage.resize(end);
|
||||
|
||||
const auto dev = device->GetLogical();
|
||||
const u32 size = static_cast<u32>(end - begin);
|
||||
const vk::QueryPoolCreateInfo query_pool_ci({}, GetTarget(type), size, {});
|
||||
pools.push_back(dev.createQueryPoolUnique(query_pool_ci, nullptr, device->GetDispatchLoader()));
|
||||
}
|
||||
|
||||
void QueryPool::Reserve(std::pair<vk::QueryPool, std::uint32_t> query) {
|
||||
const auto it =
|
||||
std::find_if(std::begin(pools), std::end(pools),
|
||||
[query_pool = query.first](auto& pool) { return query_pool == *pool; });
|
||||
ASSERT(it != std::end(pools));
|
||||
|
||||
const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it);
|
||||
usage[pool_index * GROW_STEP + static_cast<std::ptrdiff_t>(query.second)] = false;
|
||||
}
|
||||
|
||||
VKQueryCache::VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||
const VKDevice& device, VKScheduler& scheduler)
|
||||
: VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
|
||||
QueryPool>{system, rasterizer},
|
||||
device{device}, scheduler{scheduler} {
|
||||
for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) {
|
||||
query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i));
|
||||
}
|
||||
}
|
||||
|
||||
VKQueryCache::~VKQueryCache() = default;
|
||||
|
||||
std::pair<vk::QueryPool, std::uint32_t> VKQueryCache::AllocateQuery(VideoCore::QueryType type) {
|
||||
return query_pools[static_cast<std::size_t>(type)].Commit(scheduler.GetFence());
|
||||
}
|
||||
|
||||
void VKQueryCache::Reserve(VideoCore::QueryType type,
|
||||
std::pair<vk::QueryPool, std::uint32_t> query) {
|
||||
query_pools[static_cast<std::size_t>(type)].Reserve(query);
|
||||
}
|
||||
|
||||
HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type)
|
||||
: VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache},
|
||||
type{type}, query{cache.AllocateQuery(type)}, ticks{cache.Scheduler().Ticks()} {
|
||||
const auto dev = cache.Device().GetLogical();
|
||||
cache.Scheduler().Record([dev, query = query](vk::CommandBuffer cmdbuf, auto& dld) {
|
||||
dev.resetQueryPoolEXT(query.first, query.second, 1, dld);
|
||||
cmdbuf.beginQuery(query.first, query.second, vk::QueryControlFlagBits::ePrecise, dld);
|
||||
});
|
||||
}
|
||||
|
||||
HostCounter::~HostCounter() {
|
||||
cache.Reserve(type, query);
|
||||
}
|
||||
|
||||
void HostCounter::EndQuery() {
|
||||
cache.Scheduler().Record([query = query](auto cmdbuf, auto& dld) {
|
||||
cmdbuf.endQuery(query.first, query.second, dld);
|
||||
});
|
||||
}
|
||||
|
||||
u64 HostCounter::BlockingQuery() const {
|
||||
if (ticks >= cache.Scheduler().Ticks()) {
|
||||
cache.Scheduler().Flush();
|
||||
}
|
||||
|
||||
const auto dev = cache.Device().GetLogical();
|
||||
const auto& dld = cache.Device().GetDispatchLoader();
|
||||
u64 value;
|
||||
dev.getQueryPoolResults(query.first, query.second, 1, sizeof(value), &value, sizeof(value),
|
||||
vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait, dld);
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/query_cache.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RasterizerInterface;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class CachedQuery;
|
||||
class HostCounter;
|
||||
class VKDevice;
|
||||
class VKQueryCache;
|
||||
class VKScheduler;
|
||||
|
||||
using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>;
|
||||
|
||||
class QueryPool final : public VKFencedPool {
|
||||
public:
|
||||
explicit QueryPool();
|
||||
~QueryPool() override;
|
||||
|
||||
void Initialize(const VKDevice& device, VideoCore::QueryType type);
|
||||
|
||||
std::pair<vk::QueryPool, std::uint32_t> Commit(VKFence& fence);
|
||||
|
||||
void Reserve(std::pair<vk::QueryPool, std::uint32_t> query);
|
||||
|
||||
protected:
|
||||
void Allocate(std::size_t begin, std::size_t end) override;
|
||||
|
||||
private:
|
||||
static constexpr std::size_t GROW_STEP = 512;
|
||||
|
||||
const VKDevice* device = nullptr;
|
||||
VideoCore::QueryType type = {};
|
||||
|
||||
std::vector<UniqueQueryPool> pools;
|
||||
std::vector<bool> usage;
|
||||
};
|
||||
|
||||
class VKQueryCache final
|
||||
: public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
|
||||
QueryPool> {
|
||||
public:
|
||||
explicit VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||
const VKDevice& device, VKScheduler& scheduler);
|
||||
~VKQueryCache();
|
||||
|
||||
std::pair<vk::QueryPool, std::uint32_t> AllocateQuery(VideoCore::QueryType type);
|
||||
|
||||
void Reserve(VideoCore::QueryType type, std::pair<vk::QueryPool, std::uint32_t> query);
|
||||
|
||||
const VKDevice& Device() const noexcept {
|
||||
return device;
|
||||
}
|
||||
|
||||
VKScheduler& Scheduler() const noexcept {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
private:
|
||||
const VKDevice& device;
|
||||
VKScheduler& scheduler;
|
||||
};
|
||||
|
||||
class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> {
|
||||
public:
|
||||
explicit HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type);
|
||||
~HostCounter();
|
||||
|
||||
void EndQuery();
|
||||
|
||||
private:
|
||||
u64 BlockingQuery() const override;
|
||||
|
||||
VKQueryCache& cache;
|
||||
const VideoCore::QueryType type;
|
||||
const std::pair<vk::QueryPool, std::uint32_t> query;
|
||||
const u64 ticks;
|
||||
};
|
||||
|
||||
class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> {
|
||||
public:
|
||||
explicit CachedQuery(VKQueryCache&, VideoCore::QueryType, VAddr cpu_addr, u8* host_ptr)
|
||||
: VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr} {}
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -289,25 +289,19 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
|
||||
staging_pool),
|
||||
pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue),
|
||||
buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
|
||||
sampler_cache(device) {}
|
||||
sampler_cache(device), query_cache(system, *this, device, scheduler) {
|
||||
scheduler.SetQueryCache(query_cache);
|
||||
}
|
||||
|
||||
RasterizerVulkan::~RasterizerVulkan() = default;
|
||||
|
||||
bool RasterizerVulkan::DrawBatch(bool is_indexed) {
|
||||
Draw(is_indexed, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerVulkan::DrawMultiBatch(bool is_indexed) {
|
||||
Draw(is_indexed, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
MICROPROFILE_SCOPE(Vulkan_Drawing);
|
||||
|
||||
FlushWork();
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
GraphicsPipelineCacheKey key{GetFixedPipelineState(gpu.regs)};
|
||||
|
||||
@@ -362,6 +356,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
void RasterizerVulkan::Clear() {
|
||||
MICROPROFILE_SCOPE(Vulkan_Clearing);
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
if (!system.GPU().Maxwell3D().ShouldExecute()) {
|
||||
return;
|
||||
@@ -429,6 +425,8 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
|
||||
sampled_views.clear();
|
||||
image_views.clear();
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
|
||||
const ComputePipelineCacheKey key{
|
||||
code_addr,
|
||||
@@ -471,17 +469,28 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::ResetCounter(VideoCore::QueryType type) {
|
||||
query_cache.ResetCounter(type);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCore::QueryType type,
|
||||
std::optional<u64> timestamp) {
|
||||
query_cache.Query(gpu_addr, type, timestamp);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::FlushAll() {}
|
||||
|
||||
void RasterizerVulkan::FlushRegion(CacheAddr addr, u64 size) {
|
||||
texture_cache.FlushRegion(addr, size);
|
||||
buffer_cache.FlushRegion(addr, size);
|
||||
query_cache.FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::InvalidateRegion(CacheAddr addr, u64 size) {
|
||||
texture_cache.InvalidateRegion(addr, size);
|
||||
pipeline_cache.InvalidateRegion(addr, size);
|
||||
buffer_cache.InvalidateRegion(addr, size);
|
||||
query_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_memory_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_query_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_sampler_cache.h"
|
||||
@@ -96,7 +97,7 @@ struct ImageView {
|
||||
vk::ImageLayout* layout = nullptr;
|
||||
};
|
||||
|
||||
class RasterizerVulkan : public VideoCore::RasterizerAccelerated {
|
||||
class RasterizerVulkan final : public VideoCore::RasterizerAccelerated {
|
||||
public:
|
||||
explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window,
|
||||
VKScreenInfo& screen_info, const VKDevice& device,
|
||||
@@ -104,10 +105,11 @@ public:
|
||||
VKScheduler& scheduler);
|
||||
~RasterizerVulkan() override;
|
||||
|
||||
bool DrawBatch(bool is_indexed) override;
|
||||
bool DrawMultiBatch(bool is_indexed) override;
|
||||
void Draw(bool is_indexed, bool is_instanced) override;
|
||||
void Clear() override;
|
||||
void DispatchCompute(GPUVAddr code_addr) override;
|
||||
void ResetCounter(VideoCore::QueryType type) override;
|
||||
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(CacheAddr addr, u64 size) override;
|
||||
void InvalidateRegion(CacheAddr addr, u64 size) override;
|
||||
@@ -140,8 +142,6 @@ private:
|
||||
|
||||
static constexpr std::size_t ZETA_TEXCEPTION_INDEX = 8;
|
||||
|
||||
void Draw(bool is_indexed, bool is_instanced);
|
||||
|
||||
void FlushWork();
|
||||
|
||||
Texceptions UpdateAttachments();
|
||||
@@ -247,6 +247,7 @@ private:
|
||||
VKPipelineCache pipeline_cache;
|
||||
VKBufferCache buffer_cache;
|
||||
VKSamplerCache sampler_cache;
|
||||
VKQueryCache query_cache;
|
||||
|
||||
std::array<View, Maxwell::NumRenderTargets> color_attachments;
|
||||
View zeta_attachment;
|
||||
|
||||
@@ -23,7 +23,14 @@ static std::optional<vk::BorderColor> TryConvertBorderColor(std::array<float, 4>
|
||||
} else if (color == std::array<float, 4>{1, 1, 1, 1}) {
|
||||
return vk::BorderColor::eFloatOpaqueWhite;
|
||||
} else {
|
||||
return {};
|
||||
if (color[0] + color[1] + color[2] > 1.35f) {
|
||||
// If color elements are brighter than roughly 0.5 average, use white border
|
||||
return vk::BorderColor::eFloatOpaqueWhite;
|
||||
}
|
||||
if (color[3] > 0.5f) {
|
||||
return vk::BorderColor::eFloatOpaqueBlack;
|
||||
}
|
||||
return vk::BorderColor::eFloatTransparentBlack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +44,6 @@ UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc)
|
||||
|
||||
const auto border_color{tsc.GetBorderColor()};
|
||||
const auto vk_border_color{TryConvertBorderColor(border_color)};
|
||||
UNIMPLEMENTED_IF_MSG(!vk_border_color, "Unimplemented border color {} {} {} {}",
|
||||
border_color[0], border_color[1], border_color[2], border_color[3]);
|
||||
|
||||
constexpr bool unnormalized_coords{false};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_query_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
|
||||
@@ -139,6 +140,8 @@ void VKScheduler::SubmitExecution(vk::Semaphore semaphore) {
|
||||
}
|
||||
|
||||
void VKScheduler::AllocateNewContext() {
|
||||
++ticks;
|
||||
|
||||
std::unique_lock lock{mutex};
|
||||
current_fence = next_fence;
|
||||
next_fence = &resource_manager.CommitFence();
|
||||
@@ -146,6 +149,10 @@ void VKScheduler::AllocateNewContext() {
|
||||
current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence);
|
||||
current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit},
|
||||
device.GetDispatchLoader());
|
||||
// Enable counters once again. These are disabled when a command buffer is finished.
|
||||
if (query_cache) {
|
||||
query_cache->UpdateCounters();
|
||||
}
|
||||
}
|
||||
|
||||
void VKScheduler::InvalidateState() {
|
||||
@@ -159,6 +166,7 @@ void VKScheduler::InvalidateState() {
|
||||
}
|
||||
|
||||
void VKScheduler::EndPendingOperations() {
|
||||
query_cache->DisableStreams();
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -18,6 +19,7 @@ namespace Vulkan {
|
||||
|
||||
class VKDevice;
|
||||
class VKFence;
|
||||
class VKQueryCache;
|
||||
class VKResourceManager;
|
||||
|
||||
class VKFenceView {
|
||||
@@ -67,6 +69,11 @@ public:
|
||||
/// Binds a pipeline to the current execution context.
|
||||
void BindGraphicsPipeline(vk::Pipeline pipeline);
|
||||
|
||||
/// Assigns the query cache.
|
||||
void SetQueryCache(VKQueryCache& query_cache_) {
|
||||
query_cache = &query_cache_;
|
||||
}
|
||||
|
||||
/// Returns true when viewports have been set in the current command buffer.
|
||||
bool TouchViewports() {
|
||||
return std::exchange(state.viewports, true);
|
||||
@@ -112,6 +119,11 @@ public:
|
||||
return current_fence;
|
||||
}
|
||||
|
||||
/// Returns the current command buffer tick.
|
||||
u64 Ticks() const {
|
||||
return ticks;
|
||||
}
|
||||
|
||||
private:
|
||||
class Command {
|
||||
public:
|
||||
@@ -205,6 +217,8 @@ private:
|
||||
|
||||
const VKDevice& device;
|
||||
VKResourceManager& resource_manager;
|
||||
VKQueryCache* query_cache = nullptr;
|
||||
|
||||
vk::CommandBuffer current_cmdbuf;
|
||||
VKFence* current_fence = nullptr;
|
||||
VKFence* next_fence = nullptr;
|
||||
@@ -227,6 +241,7 @@ private:
|
||||
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::atomic<u64> ticks = 0;
|
||||
bool quit = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -275,12 +275,14 @@ public:
|
||||
AddCapability(spv::Capability::ImageGatherExtended);
|
||||
AddCapability(spv::Capability::SampledBuffer);
|
||||
AddCapability(spv::Capability::StorageImageWriteWithoutFormat);
|
||||
AddCapability(spv::Capability::DrawParameters);
|
||||
AddCapability(spv::Capability::SubgroupBallotKHR);
|
||||
AddCapability(spv::Capability::SubgroupVoteKHR);
|
||||
AddExtension("SPV_KHR_shader_ballot");
|
||||
AddExtension("SPV_KHR_subgroup_vote");
|
||||
AddExtension("SPV_KHR_storage_buffer_storage_class");
|
||||
AddExtension("SPV_KHR_variable_pointers");
|
||||
AddExtension("SPV_KHR_shader_draw_parameters");
|
||||
|
||||
if (ir.UsesViewportIndex()) {
|
||||
AddCapability(spv::Capability::MultiViewport);
|
||||
@@ -290,6 +292,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (device.IsShaderStorageImageReadWithoutFormatSupported()) {
|
||||
AddCapability(spv::Capability::StorageImageReadWithoutFormat);
|
||||
}
|
||||
|
||||
if (device.IsFloat16Supported()) {
|
||||
AddCapability(spv::Capability::Float16);
|
||||
}
|
||||
@@ -492,9 +498,11 @@ private:
|
||||
interfaces.push_back(AddGlobalVariable(Name(out_vertex, "out_vertex")));
|
||||
|
||||
// Declare input attributes
|
||||
vertex_index = DeclareInputBuiltIn(spv::BuiltIn::VertexIndex, t_in_uint, "vertex_index");
|
||||
vertex_index = DeclareInputBuiltIn(spv::BuiltIn::VertexIndex, t_in_int, "vertex_index");
|
||||
instance_index =
|
||||
DeclareInputBuiltIn(spv::BuiltIn::InstanceIndex, t_in_uint, "instance_index");
|
||||
DeclareInputBuiltIn(spv::BuiltIn::InstanceIndex, t_in_int, "instance_index");
|
||||
base_vertex = DeclareInputBuiltIn(spv::BuiltIn::BaseVertex, t_in_int, "base_vertex");
|
||||
base_instance = DeclareInputBuiltIn(spv::BuiltIn::BaseInstance, t_in_int, "base_instance");
|
||||
}
|
||||
|
||||
void DeclareTessControl() {
|
||||
@@ -1068,9 +1076,12 @@ private:
|
||||
return {OpLoad(t_float, AccessElement(t_in_float, tess_coord, element)),
|
||||
Type::Float};
|
||||
case 2:
|
||||
return {OpLoad(t_uint, instance_index), Type::Uint};
|
||||
return {
|
||||
OpISub(t_int, OpLoad(t_int, instance_index), OpLoad(t_int, base_instance)),
|
||||
Type::Int};
|
||||
case 3:
|
||||
return {OpLoad(t_uint, vertex_index), Type::Uint};
|
||||
return {OpISub(t_int, OpLoad(t_int, vertex_index), OpLoad(t_int, base_vertex)),
|
||||
Type::Int};
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
|
||||
return {Constant(t_uint, 0U), Type::Uint};
|
||||
@@ -1748,8 +1759,16 @@ private:
|
||||
}
|
||||
|
||||
Expression ImageLoad(Operation operation) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
if (!device.IsShaderStorageImageReadWithoutFormatSupported()) {
|
||||
return {v_float_zero, Type::Float};
|
||||
}
|
||||
|
||||
const auto& meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
|
||||
const Id coords = GetCoordinates(operation, Type::Int);
|
||||
const Id texel = OpImageRead(t_uint4, GetImage(operation), coords);
|
||||
|
||||
return {OpCompositeExtract(t_uint, texel, meta.element), Type::Uint};
|
||||
}
|
||||
|
||||
Expression ImageStore(Operation operation) {
|
||||
@@ -2542,6 +2561,8 @@ private:
|
||||
|
||||
Id instance_index{};
|
||||
Id vertex_index{};
|
||||
Id base_instance{};
|
||||
Id base_vertex{};
|
||||
std::array<Id, Maxwell::NumRenderTargets> frag_colors{};
|
||||
Id frag_depth{};
|
||||
Id frag_coord{};
|
||||
|
||||
@@ -166,13 +166,13 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
|
||||
const auto [op_rhs, test] = [&]() -> std::pair<Node, Node> {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::ICMP_CR:
|
||||
return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset),
|
||||
return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()),
|
||||
GetRegister(instr.gpr39)};
|
||||
case OpCode::Id::ICMP_R:
|
||||
return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
|
||||
case OpCode::Id::ICMP_RC:
|
||||
return {GetRegister(instr.gpr39),
|
||||
GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
|
||||
GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
|
||||
case OpCode::Id::ICMP_IMM:
|
||||
return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
|
||||
default:
|
||||
|
||||
@@ -21,7 +21,7 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::BFI_RC:
|
||||
return {GetRegister(instr.gpr39),
|
||||
GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
|
||||
GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
|
||||
case OpCode::Id::BFI_IMM_R:
|
||||
return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
|
||||
default:
|
||||
|
||||
@@ -83,14 +83,14 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
|
||||
const bool input_signed = instr.conversion.is_input_signed;
|
||||
|
||||
if (instr.conversion.src_size == Register::Size::Byte) {
|
||||
const u32 offset = static_cast<u32>(instr.conversion.int_src.selector) * 8;
|
||||
if (offset > 0) {
|
||||
value = SignedOperation(OperationCode::ILogicalShiftRight, input_signed,
|
||||
std::move(value), Immediate(offset));
|
||||
if (const u32 offset = static_cast<u32>(instr.conversion.int_src.selector); offset > 0) {
|
||||
ASSERT(instr.conversion.src_size == Register::Size::Byte ||
|
||||
instr.conversion.src_size == Register::Size::Short);
|
||||
if (instr.conversion.src_size == Register::Size::Short) {
|
||||
ASSERT(offset == 0 || offset == 2);
|
||||
}
|
||||
} else {
|
||||
UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0);
|
||||
value = SignedOperation(OperationCode::ILogicalShiftRight, input_signed,
|
||||
std::move(value), Immediate(offset * 8));
|
||||
}
|
||||
|
||||
value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed);
|
||||
|
||||
@@ -522,68 +522,53 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||
Node array, Node depth_compare, u32 bias_offset,
|
||||
std::vector<Node> aoffi,
|
||||
std::optional<Tegra::Shader::Register> bindless_reg) {
|
||||
const auto is_array = static_cast<bool>(array);
|
||||
const auto is_shadow = static_cast<bool>(depth_compare);
|
||||
const bool is_array = array != nullptr;
|
||||
const bool is_shadow = depth_compare != nullptr;
|
||||
const bool is_bindless = bindless_reg.has_value();
|
||||
|
||||
UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) ||
|
||||
(texture_type == TextureType::TextureCube && is_array && is_shadow),
|
||||
"This method is not supported.");
|
||||
UNIMPLEMENTED_IF(texture_type == TextureType::TextureCube && is_array && is_shadow);
|
||||
ASSERT_MSG(texture_type != TextureType::Texture3D || !is_array || !is_shadow,
|
||||
"Illegal texture type");
|
||||
|
||||
const SamplerInfo info{texture_type, is_array, is_shadow, false};
|
||||
Node index_var{};
|
||||
Node index_var;
|
||||
const Sampler* sampler = is_bindless ? GetBindlessSampler(*bindless_reg, index_var, info)
|
||||
: GetSampler(instr.sampler, info);
|
||||
Node4 values;
|
||||
if (sampler == nullptr) {
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
values[element] = Immediate(0);
|
||||
}
|
||||
return values;
|
||||
if (!sampler) {
|
||||
return {Immediate(0), Immediate(0), Immediate(0), Immediate(0)};
|
||||
}
|
||||
|
||||
const bool lod_needed = process_mode == TextureProcessMode::LZ ||
|
||||
process_mode == TextureProcessMode::LL ||
|
||||
process_mode == TextureProcessMode::LLA;
|
||||
|
||||
// LOD selection (either via bias or explicit textureLod) not supported in GL for
|
||||
// sampler2DArrayShadow and samplerCubeArrayShadow.
|
||||
const bool gl_lod_supported =
|
||||
!((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
|
||||
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
|
||||
|
||||
const OperationCode read_method =
|
||||
(lod_needed && gl_lod_supported) ? OperationCode::TextureLod : OperationCode::Texture;
|
||||
|
||||
UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
|
||||
const OperationCode opcode = lod_needed ? OperationCode::TextureLod : OperationCode::Texture;
|
||||
|
||||
Node bias;
|
||||
Node lod;
|
||||
if (process_mode != TextureProcessMode::None && gl_lod_supported) {
|
||||
switch (process_mode) {
|
||||
case TextureProcessMode::LZ:
|
||||
lod = Immediate(0.0f);
|
||||
break;
|
||||
case TextureProcessMode::LB:
|
||||
// If present, lod or bias are always stored in the register
|
||||
// indexed by the gpr20 field with an offset depending on the
|
||||
// usage of the other registers
|
||||
bias = GetRegister(instr.gpr20.Value() + bias_offset);
|
||||
break;
|
||||
case TextureProcessMode::LL:
|
||||
lod = GetRegister(instr.gpr20.Value() + bias_offset);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented process mode={}", static_cast<u32>(process_mode));
|
||||
break;
|
||||
}
|
||||
switch (process_mode) {
|
||||
case TextureProcessMode::None:
|
||||
break;
|
||||
case TextureProcessMode::LZ:
|
||||
lod = Immediate(0.0f);
|
||||
break;
|
||||
case TextureProcessMode::LB:
|
||||
// If present, lod or bias are always stored in the register indexed by the gpr20 field with
|
||||
// an offset depending on the usage of the other registers.
|
||||
bias = GetRegister(instr.gpr20.Value() + bias_offset);
|
||||
break;
|
||||
case TextureProcessMode::LL:
|
||||
lod = GetRegister(instr.gpr20.Value() + bias_offset);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented process mode={}", static_cast<u32>(process_mode));
|
||||
break;
|
||||
}
|
||||
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto copy_coords = coords;
|
||||
MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias,
|
||||
lod, {}, element, index_var};
|
||||
values[element] = Operation(read_method, meta, std::move(copy_coords));
|
||||
values[element] = Operation(opcode, meta, coords);
|
||||
}
|
||||
|
||||
return values;
|
||||
|
||||
@@ -721,7 +721,6 @@ private:
|
||||
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const CacheAddr cache_addr,
|
||||
const SurfaceParams& params, bool preserve_contents,
|
||||
bool is_render) {
|
||||
|
||||
// Step 1
|
||||
// Check Level 1 Cache for a fast structural match. If candidate surface
|
||||
// matches at certain level we are pretty much done.
|
||||
@@ -733,14 +732,18 @@ private:
|
||||
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents,
|
||||
topological_result);
|
||||
}
|
||||
|
||||
const auto struct_result = current_surface->MatchesStructure(params);
|
||||
if (struct_result != MatchStructureResult::None &&
|
||||
(params.target != SurfaceTarget::Texture3D ||
|
||||
current_surface->MatchTarget(params.target))) {
|
||||
if (struct_result == MatchStructureResult::FullMatch) {
|
||||
return ManageStructuralMatch(current_surface, params, is_render);
|
||||
} else {
|
||||
return RebuildSurface(current_surface, params, is_render);
|
||||
if (struct_result != MatchStructureResult::None) {
|
||||
const auto& old_params = current_surface->GetSurfaceParams();
|
||||
const bool not_3d = params.target != SurfaceTarget::Texture3D &&
|
||||
old_params.target != SurfaceTarget::Texture3D;
|
||||
if (not_3d || current_surface->MatchTarget(params.target)) {
|
||||
if (struct_result == MatchStructureResult::FullMatch) {
|
||||
return ManageStructuralMatch(current_surface, params, is_render);
|
||||
} else {
|
||||
return RebuildSurface(current_surface, params, is_render);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,14 +73,12 @@ struct Client::Impl {
|
||||
if (!parsedUrl.GetPort(&port)) {
|
||||
port = HTTP_PORT;
|
||||
}
|
||||
cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port,
|
||||
TIMEOUT_SECONDS);
|
||||
cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port);
|
||||
} else if (parsedUrl.m_Scheme == "https") {
|
||||
if (!parsedUrl.GetPort(&port)) {
|
||||
port = HTTPS_PORT;
|
||||
}
|
||||
cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
|
||||
TIMEOUT_SECONDS);
|
||||
cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port);
|
||||
} else {
|
||||
LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
|
||||
return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
|
||||
@@ -90,6 +88,7 @@ struct Client::Impl {
|
||||
LOG_ERROR(WebService, "Invalid URL {}", host + path);
|
||||
return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
|
||||
}
|
||||
cli->set_timeout_sec(TIMEOUT_SECONDS);
|
||||
|
||||
httplib::Headers params;
|
||||
if (!jwt.empty()) {
|
||||
|
||||
@@ -630,6 +630,7 @@ void Config::ReadRendererValues() {
|
||||
Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
|
||||
Settings::values.resolution_factor =
|
||||
ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat();
|
||||
Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt();
|
||||
Settings::values.use_frame_limit =
|
||||
ReadSetting(QStringLiteral("use_frame_limit"), true).toBool();
|
||||
Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
|
||||
@@ -1064,6 +1065,7 @@ void Config::SaveRendererValues() {
|
||||
WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
|
||||
WriteSetting(QStringLiteral("resolution_factor"),
|
||||
static_cast<double>(Settings::values.resolution_factor), 1.0);
|
||||
WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
|
||||
WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
|
||||
WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
|
||||
WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
|
||||
|
||||
@@ -97,6 +97,7 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
|
||||
ui->resolution_factor_combobox->setCurrentIndex(
|
||||
static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
|
||||
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
|
||||
ui->use_disk_shader_cache->setEnabled(runtime_lock);
|
||||
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
|
||||
ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
|
||||
@@ -114,6 +115,7 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.vulkan_device = vulkan_device;
|
||||
Settings::values.resolution_factor =
|
||||
ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
|
||||
Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex();
|
||||
Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
|
||||
Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
|
||||
Settings::values.use_asynchronous_gpu_emulation =
|
||||
|
||||
@@ -138,6 +138,41 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="ar_label">
|
||||
<property name="text">
|
||||
<string>Aspect Ratio:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="aspect_ratio_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Default (16:9)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Force 4:3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Force 21:9</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stretch to Window</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
|
||||
@@ -379,6 +379,8 @@ void Config::ReadValues() {
|
||||
|
||||
Settings::values.resolution_factor =
|
||||
static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
|
||||
Settings::values.aspect_ratio =
|
||||
static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0));
|
||||
Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
|
||||
Settings::values.frame_limit =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
|
||||
|
||||
@@ -122,6 +122,10 @@ use_shader_jit =
|
||||
# factor for the Switch resolution
|
||||
resolution_factor =
|
||||
|
||||
# Aspect ratio
|
||||
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
|
||||
aspect_ratio =
|
||||
|
||||
# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
|
||||
# 0 (default): Off, 1: On
|
||||
use_vsync =
|
||||
|
||||
@@ -118,6 +118,8 @@ void Config::ReadValues() {
|
||||
// Renderer
|
||||
Settings::values.resolution_factor =
|
||||
static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
|
||||
Settings::values.aspect_ratio =
|
||||
static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0));
|
||||
Settings::values.use_frame_limit = false;
|
||||
Settings::values.frame_limit = 100;
|
||||
Settings::values.use_disk_shader_cache =
|
||||
|
||||
@@ -26,6 +26,10 @@ use_shader_jit =
|
||||
# factor for the Switch resolution
|
||||
resolution_factor =
|
||||
|
||||
# Aspect ratio
|
||||
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
|
||||
aspect_ratio =
|
||||
|
||||
# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
|
||||
# 0 (default): Off, 1: On
|
||||
use_vsync =
|
||||
|
||||
Reference in New Issue
Block a user